1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <thread.h> 5 #include <cursor.h> 6 #include <mouse.h> 7 #include <keyboard.h> 8 #include <frame.h> 9 #include <fcall.h> 10 #include <plumb.h> 11 #include "dat.h" 12 #include "fns.h" 13 14 Buffer snarfbuf; 15 16 void del(Text*, Text*, Text*, int, int, Rune*, int); 17 void delcol(Text*, Text*, Text*, int, int, Rune*, int); 18 void dump(Text*, Text*, Text*, int, int, Rune*, int); 19 void edit(Text*, Text*, Text*, int, int, Rune*, int); 20 void exit(Text*, Text*, Text*, int, int, Rune*, int); 21 void fontx(Text*, Text*, Text*, int, int, Rune*, int); 22 void get(Text*, Text*, Text*, int, int, Rune*, int); 23 void id(Text*, Text*, Text*, int, int, Rune*, int); 24 void incl(Text*, Text*, Text*, int, int, Rune*, int); 25 void indent(Text*, Text*, Text*, int, int, Rune*, int); 26 void kill(Text*, Text*, Text*, int, int, Rune*, int); 27 void local(Text*, Text*, Text*, int, int, Rune*, int); 28 void look(Text*, Text*, Text*, int, int, Rune*, int); 29 void newcol(Text*, Text*, Text*, int, int, Rune*, int); 30 void paste(Text*, Text*, Text*, int, int, Rune*, int); 31 void put(Text*, Text*, Text*, int, int, Rune*, int); 32 void putall(Text*, Text*, Text*, int, int, Rune*, int); 33 void sendx(Text*, Text*, Text*, int, int, Rune*, int); 34 void sort(Text*, Text*, Text*, int, int, Rune*, int); 35 void tab(Text*, Text*, Text*, int, int, Rune*, int); 36 void zeroxx(Text*, Text*, Text*, int, int, Rune*, int); 37 38 typedef struct Exectab Exectab; 39 struct Exectab 40 { 41 Rune *name; 42 void (*fn)(Text*, Text*, Text*, int, int, Rune*, int); 43 int mark; 44 int flag1; 45 int flag2; 46 }; 47 48 Exectab exectab[] = { 49 { L"Cut", cut, TRUE, TRUE, TRUE }, 50 { L"Del", del, FALSE, FALSE, XXX }, 51 { L"Delcol", delcol, FALSE, XXX, XXX }, 52 { L"Delete", del, FALSE, TRUE, XXX }, 53 { L"Dump", dump, FALSE, TRUE, XXX }, 54 { L"Edit", edit, FALSE, XXX, XXX }, 55 { L"Exit", exit, FALSE, XXX, XXX }, 56 { L"Font", fontx, FALSE, XXX, XXX }, 57 { L"Get", get, FALSE, TRUE, XXX }, 58 { L"ID", id, FALSE, XXX, XXX }, 59 { L"Incl", incl, FALSE, XXX, XXX }, 60 { L"Indent", indent, FALSE, XXX, XXX }, 61 { L"Kill", kill, FALSE, XXX, XXX }, 62 { L"Load", dump, FALSE, FALSE, XXX }, 63 { L"Local", local, FALSE, XXX, XXX }, 64 { L"Look", look, FALSE, XXX, XXX }, 65 { L"New", new, FALSE, XXX, XXX }, 66 { L"Newcol", newcol, FALSE, XXX, XXX }, 67 { L"Paste", paste, TRUE, TRUE, XXX }, 68 { L"Put", put, FALSE, XXX, XXX }, 69 { L"Putall", putall, FALSE, XXX, XXX }, 70 { L"Redo", undo, FALSE, FALSE, XXX }, 71 { L"Send", sendx, TRUE, XXX, XXX }, 72 { L"Snarf", cut, FALSE, TRUE, FALSE }, 73 { L"Sort", sort, FALSE, XXX, XXX }, 74 { L"Tab", tab, FALSE, XXX, XXX }, 75 { L"Undo", undo, FALSE, TRUE, XXX }, 76 { L"Zerox", zeroxx, FALSE, XXX, XXX }, 77 { nil, nil, 0, 0, 0 }, 78 }; 79 80 Exectab* 81 lookup(Rune *r, int n) 82 { 83 Exectab *e; 84 int nr; 85 86 r = skipbl(r, n, &n); 87 if(n == 0) 88 return nil; 89 findbl(r, n, &nr); 90 nr = n-nr; 91 for(e=exectab; e->name; e++) 92 if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE) 93 return e; 94 return nil; 95 } 96 97 int 98 isexecc(int c) 99 { 100 if(isfilec(c)) 101 return 1; 102 return c=='<' || c=='|' || c=='>'; 103 } 104 105 void 106 execute(Text *t, uint aq0, uint aq1, int external, Text *argt) 107 { 108 uint q0, q1; 109 Rune *r, *s; 110 char *b, *a, *aa; 111 Exectab *e; 112 int c, n, f; 113 Runestr dir; 114 115 q0 = aq0; 116 q1 = aq1; 117 if(q1 == q0){ /* expand to find word (actually file name) */ 118 /* if in selection, choose selection */ 119 if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 120 q0 = t->q0; 121 q1 = t->q1; 122 }else{ 123 while(q1<t->file->nc && isexecc(c=textreadc(t, q1)) && c!=':') 124 q1++; 125 while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':') 126 q0--; 127 if(q1 == q0) 128 return; 129 } 130 } 131 r = runemalloc(q1-q0); 132 bufread(t->file, q0, r, q1-q0); 133 e = lookup(r, q1-q0); 134 if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ 135 f = 0; 136 if(e) 137 f |= 1; 138 if(q0!=aq0 || q1!=aq1){ 139 bufread(t->file, aq0, r, aq1-aq0); 140 f |= 2; 141 } 142 aa = getbytearg(argt, TRUE, TRUE, &a); 143 if(a){ 144 if(strlen(a) > EVENTSIZE){ /* too big; too bad */ 145 free(aa); 146 free(a); 147 warning(nil, "`argument string too long\n"); 148 return; 149 } 150 f |= 8; 151 } 152 c = 'x'; 153 if(t->what == Body) 154 c = 'X'; 155 n = aq1-aq0; 156 if(n <= EVENTSIZE) 157 winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r); 158 else 159 winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n); 160 if(q0!=aq0 || q1!=aq1){ 161 n = q1-q0; 162 bufread(t->file, q0, r, n); 163 if(n <= EVENTSIZE) 164 winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r); 165 else 166 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n); 167 } 168 if(a){ 169 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a); 170 if(aa) 171 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa); 172 else 173 winevent(t->w, "%c0 0 0 0 \n", c); 174 } 175 free(r); 176 free(aa); 177 free(a); 178 return; 179 } 180 if(e){ 181 if(e->mark && seltext!=nil) 182 if(seltext->what == Body){ 183 seq++; 184 filemark(seltext->w->body.file); 185 } 186 s = skipbl(r, q1-q0, &n); 187 s = findbl(s, n, &n); 188 s = skipbl(s, n, &n); 189 (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n); 190 free(r); 191 return; 192 } 193 194 b = runetobyte(r, q1-q0); 195 free(r); 196 dir = dirname(t, nil, 0); 197 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 198 free(dir.r); 199 dir.r = nil; 200 dir.nr = 0; 201 } 202 aa = getbytearg(argt, TRUE, TRUE, &a); 203 if(t->w) 204 incref(t->w); 205 run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE); 206 } 207 208 char* 209 printarg(Text *argt, uint q0, uint q1) 210 { 211 char *buf; 212 213 if(argt->what!=Body || argt->file->name==nil) 214 return nil; 215 buf = emalloc(argt->file->nname+32); 216 if(q0 == q1) 217 sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0); 218 else 219 sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1); 220 return buf; 221 } 222 223 char* 224 getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp) 225 { 226 int n; 227 Expand e; 228 char *a; 229 230 *rp = nil; 231 *nrp = 0; 232 if(argt == nil) 233 return nil; 234 a = nil; 235 textcommit(argt, TRUE); 236 if(expand(argt, argt->q0, argt->q1, &e)){ 237 free(e.bname); 238 if(e.nname && dofile){ 239 e.name = runerealloc(e.name, e.nname+1); 240 if(doaddr) 241 a = printarg(argt, e.q0, e.q1); 242 *rp = e.name; 243 *nrp = e.nname; 244 return a; 245 } 246 free(e.name); 247 }else{ 248 e.q0 = argt->q0; 249 e.q1 = argt->q1; 250 } 251 n = e.q1 - e.q0; 252 *rp = runemalloc(n+1); 253 bufread(argt->file, e.q0, *rp, n); 254 if(doaddr) 255 a = printarg(argt, e.q0, e.q1); 256 *nrp = n; 257 return a; 258 } 259 260 char* 261 getbytearg(Text *argt, int doaddr, int dofile, char **bp) 262 { 263 Rune *r; 264 int n; 265 char *aa; 266 267 *bp = nil; 268 aa = getarg(argt, doaddr, dofile, &r, &n); 269 if(r == nil) 270 return nil; 271 *bp = runetobyte(r, n); 272 free(r); 273 return aa; 274 } 275 276 void 277 newcol(Text *et, Text*, Text*, int, int, Rune*, int) 278 { 279 Column *c; 280 281 c = rowadd(et->row, nil, -1); 282 if(c) 283 winsettag(coladd(c, nil, nil, -1)); 284 } 285 286 void 287 delcol(Text *et, Text*, Text*, int, int, Rune*, int) 288 { 289 int i; 290 Column *c; 291 Window *w; 292 293 c = et->col; 294 if(c==nil || colclean(c)==0) 295 return; 296 for(i=0; i<c->nw; i++){ 297 w = c->w[i]; 298 if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){ 299 warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name); 300 return; 301 } 302 } 303 rowclose(et->col->row, et->col, TRUE); 304 } 305 306 void 307 del(Text *et, Text*, Text*, int flag1, int, Rune*, int) 308 { 309 if(et->col==nil || et->w == nil) 310 return; 311 if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)) 312 colclose(et->col, et->w, TRUE); 313 } 314 315 void 316 sort(Text *et, Text*, Text*, int, int, Rune*, int) 317 { 318 if(et->col) 319 colsort(et->col); 320 } 321 322 uint 323 seqof(Window *w, int isundo) 324 { 325 /* if it's undo, see who changed with us */ 326 if(isundo) 327 return w->body.file->seq; 328 /* if it's redo, see who we'll be sync'ed up with */ 329 return fileredoseq(w->body.file); 330 } 331 332 void 333 undo(Text *et, Text*, Text*, int flag1, int, Rune*, int) 334 { 335 int i, j; 336 Column *c; 337 Window *w; 338 uint seq; 339 340 if(et==nil || et->w== nil) 341 return; 342 seq = seqof(et->w, flag1); 343 if(seq == 0){ 344 /* nothing to undo */ 345 return; 346 } 347 /* 348 * Undo the executing window first. Its display will update. other windows 349 * in the same file will not call show() and jump to a different location in the file. 350 * Simultaneous changes to other files will be chaotic, however. 351 */ 352 winundo(et->w, flag1); 353 for(i=0; i<row.ncol; i++){ 354 c = row.col[i]; 355 for(j=0; j<c->nw; j++){ 356 w = c->w[j]; 357 if(w == et->w) 358 continue; 359 if(seqof(w, flag1) == seq) 360 winundo(w, flag1); 361 } 362 } 363 } 364 365 char* 366 getname(Text *t, Text *argt, Rune *arg, int narg, int isput) 367 { 368 char *s; 369 Rune *r; 370 int i, n, promote; 371 Runestr dir; 372 373 getarg(argt, FALSE, TRUE, &r, &n); 374 promote = FALSE; 375 if(r == nil) 376 promote = TRUE; 377 else if(isput){ 378 /* if are doing a Put, want to synthesize name even for non-existent file */ 379 /* best guess is that file name doesn't contain a slash */ 380 promote = TRUE; 381 for(i=0; i<n; i++) 382 if(r[i] == '/'){ 383 promote = FALSE; 384 break; 385 } 386 if(promote){ 387 t = argt; 388 arg = r; 389 narg = n; 390 } 391 } 392 if(promote){ 393 n = narg; 394 if(n <= 0){ 395 s = runetobyte(t->file->name, t->file->nname); 396 return s; 397 } 398 /* prefix with directory name if necessary */ 399 dir.r = nil; 400 dir.nr = 0; 401 if(n>0 && arg[0]!='/'){ 402 dir = dirname(t, nil, 0); 403 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 404 free(dir.r); 405 dir.r = nil; 406 dir.nr = 0; 407 } 408 } 409 if(dir.r){ 410 r = runemalloc(dir.nr+n+1); 411 runemove(r, dir.r, dir.nr); 412 free(dir.r); 413 if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/') 414 r[dir.nr++] = '/'; 415 runemove(r+dir.nr, arg, n); 416 n += dir.nr; 417 }else{ 418 r = runemalloc(n+1); 419 runemove(r, arg, n); 420 } 421 } 422 s = runetobyte(r, n); 423 free(r); 424 if(strlen(s) == 0){ 425 free(s); 426 s = nil; 427 } 428 return s; 429 } 430 431 void 432 zeroxx(Text *et, Text *t, Text*, int, int, Rune*, int) 433 { 434 Window *nw; 435 int c, locked; 436 437 locked = FALSE; 438 if(t!=nil && t->w!=nil && t->w!=et->w){ 439 locked = TRUE; 440 c = 'M'; 441 if(et->w) 442 c = et->w->owner; 443 winlock(t->w, c); 444 } 445 if(t == nil) 446 t = et; 447 if(t==nil || t->w==nil) 448 return; 449 t = &t->w->body; 450 if(t->w->isdir) 451 warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name); 452 else{ 453 nw = coladd(t->w->col, nil, t->w, -1); 454 /* ugly: fix locks so w->unlock works */ 455 winlock1(nw, t->w->owner); 456 } 457 if(locked) 458 winunlock(t->w); 459 } 460 461 void 462 get(Text *et, Text *t, Text *argt, int flag1, int, Rune *arg, int narg) 463 { 464 char *name; 465 Rune *r; 466 int i, n, dirty, samename, isdir; 467 Window *w; 468 Text *u; 469 Dir *d; 470 471 if(flag1) 472 if(et==nil || et->w==nil) 473 return; 474 if(!et->w->isdir && (et->w->body.file->nc>0 && !winclean(et->w, TRUE))) 475 return; 476 w = et->w; 477 t = &w->body; 478 name = getname(t, argt, arg, narg, FALSE); 479 if(name == nil){ 480 warning(nil, "no file name\n"); 481 return; 482 } 483 if(t->file->ntext>1){ 484 d = dirstat(name); 485 isdir = (d!=nil && (d->qid.type & QTDIR)); 486 free(d); 487 if(isdir){ 488 warning(nil, "%s is a directory; can't read with multiple windows on it\n", name); 489 return; 490 } 491 } 492 r = bytetorune(name, &n); 493 for(i=0; i<t->file->ntext; i++){ 494 u = t->file->text[i]; 495 /* second and subsequent calls with zero an already empty buffer, but OK */ 496 textreset(u); 497 windirfree(u->w); 498 } 499 samename = runeeq(r, n, t->file->name, t->file->nname); 500 textload(t, 0, name, samename); 501 if(samename){ 502 t->file->mod = FALSE; 503 dirty = FALSE; 504 }else{ 505 t->file->mod = TRUE; 506 dirty = TRUE; 507 } 508 for(i=0; i<t->file->ntext; i++) 509 t->file->text[i]->w->dirty = dirty; 510 free(name); 511 free(r); 512 winsettag(w); 513 t->file->unread = FALSE; 514 for(i=0; i<t->file->ntext; i++){ 515 u = t->file->text[i]; 516 textsetselect(&u->w->tag, u->w->tag.file->nc, u->w->tag.file->nc); 517 textscrdraw(u); 518 } 519 } 520 521 void 522 putfile(File *f, int q0, int q1, Rune *namer, int nname) 523 { 524 uint n, m; 525 Rune *r; 526 char *s, *name; 527 int i, fd, q; 528 Dir *d, *d1; 529 Window *w; 530 int isapp; 531 532 w = f->curtext->w; 533 name = runetobyte(namer, nname); 534 d = dirstat(name); 535 if(d!=nil && runeeq(namer, nname, f->name, f->nname)){ 536 /* f->mtime+1 because when talking over NFS it's often off by a second */ 537 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime+1<d->mtime){ 538 f->dev = d->dev; 539 f->qidpath = d->qid.path; 540 f->mtime = d->mtime; 541 if(f->unread) 542 warning(nil, "%s not written; file already exists\n", name); 543 else 544 warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid); 545 goto Rescue1; 546 } 547 } 548 fd = create(name, OWRITE, 0666); 549 if(fd < 0){ 550 warning(nil, "can't create file %s: %r\n", name); 551 goto Rescue1; 552 } 553 r = fbufalloc(); 554 s = fbufalloc(); 555 free(d); 556 d = dirfstat(fd); 557 isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND)); 558 if(isapp){ 559 warning(nil, "%s not written; file is append only\n", name); 560 goto Rescue2; 561 } 562 563 for(q=q0; q<q1; q+=n){ 564 n = q1 - q; 565 if(n > BUFSIZE/UTFmax) 566 n = BUFSIZE/UTFmax; 567 bufread(f, q, r, n); 568 m = snprint(s, BUFSIZE+1, "%.*S", n, r); 569 if(write(fd, s, m) != m){ 570 warning(nil, "can't write file %s: %r\n", name); 571 goto Rescue2; 572 } 573 } 574 if(runeeq(namer, nname, f->name, f->nname)){ 575 if(q0!=0 || q1!=f->nc){ 576 f->mod = TRUE; 577 w->dirty = TRUE; 578 f->unread = TRUE; 579 }else{ 580 d1 = dirfstat(fd); 581 if(d1 != nil){ 582 free(d); 583 d = d1; 584 } 585 f->qidpath = d->qid.path; 586 f->dev = d->dev; 587 f->mtime = d->mtime; 588 f->mod = FALSE; 589 w->dirty = FALSE; 590 f->unread = FALSE; 591 } 592 for(i=0; i<f->ntext; i++){ 593 f->text[i]->w->putseq = f->seq; 594 f->text[i]->w->dirty = w->dirty; 595 } 596 } 597 fbuffree(s); 598 fbuffree(r); 599 free(d); 600 free(namer); 601 free(name); 602 close(fd); 603 winsettag(w); 604 return; 605 606 Rescue2: 607 fbuffree(s); 608 fbuffree(r); 609 close(fd); 610 /* fall through */ 611 612 Rescue1: 613 free(d); 614 free(namer); 615 free(name); 616 } 617 618 void 619 put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 620 { 621 int nname; 622 Rune *namer; 623 Window *w; 624 File *f; 625 char *name; 626 627 if(et==nil || et->w==nil || et->w->isdir) 628 return; 629 w = et->w; 630 f = w->body.file; 631 name = getname(&w->body, argt, arg, narg, TRUE); 632 if(name == nil){ 633 warning(nil, "no file name\n"); 634 return; 635 } 636 namer = bytetorune(name, &nname); 637 putfile(f, 0, f->nc, namer, nname); 638 free(name); 639 } 640 641 void 642 dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg) 643 { 644 char *name; 645 646 if(narg) 647 name = runetobyte(arg, narg); 648 else 649 getbytearg(argt, FALSE, TRUE, &name); 650 if(isdump) 651 rowdump(&row, name); 652 else 653 rowload(&row, name, FALSE); 654 free(name); 655 } 656 657 void 658 cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int) 659 { 660 uint q0, q1, n, locked, c; 661 Rune *r; 662 663 /* use current window if snarfing and its selection is non-null */ 664 if(et!=nil && dosnarf && et->w!=nil && t==nil){ 665 if(et->w->body.q1>et->w->body.q0){ 666 t = &et->w->body; 667 if(docut) 668 filemark(t->file); /* seq has been incremented by execute */ 669 }else if(et->w->tag.q1>et->w->tag.q0) 670 t = &et->w->tag; 671 } 672 if(t == nil){ 673 /* can only happen if seltext == nil */ 674 return; 675 } 676 locked = FALSE; 677 if(t->w!=nil && et->w!=t->w){ 678 locked = TRUE; 679 c = 'M'; 680 if(et->w) 681 c = et->w->owner; 682 winlock(t->w, c); 683 } 684 if(t->q0 == t->q1){ 685 if(locked) 686 winunlock(t->w); 687 return; 688 } 689 if(dosnarf){ 690 q0 = t->q0; 691 q1 = t->q1; 692 bufdelete(&snarfbuf, 0, snarfbuf.nc); 693 r = fbufalloc(); 694 while(q0 < q1){ 695 n = q1 - q0; 696 if(n > RBUFSIZE) 697 n = RBUFSIZE; 698 bufread(t->file, q0, r, n); 699 bufinsert(&snarfbuf, snarfbuf.nc, r, n); 700 q0 += n; 701 } 702 fbuffree(r); 703 putsnarf(); 704 } 705 if(docut){ 706 textdelete(t, t->q0, t->q1, TRUE); 707 textsetselect(t, t->q0, t->q0); 708 if(t->w){ 709 textscrdraw(t); 710 winsettag(t->w); 711 } 712 }else if(dosnarf) /* Snarf command */ 713 argtext = t; 714 if(locked) 715 winunlock(t->w); 716 } 717 718 void 719 paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int) 720 { 721 int c; 722 uint q, q0, q1, n; 723 Rune *r; 724 725 /* if(tobody), use body of executing window (Paste or Send command) */ 726 if(tobody && et!=nil && et->w!=nil){ 727 t = &et->w->body; 728 filemark(t->file); /* seq has been incremented by execute */ 729 } 730 if(t == nil) 731 return; 732 733 getsnarf(); 734 if(t==nil || snarfbuf.nc==0) 735 return; 736 if(t->w!=nil && et->w!=t->w){ 737 c = 'M'; 738 if(et->w) 739 c = et->w->owner; 740 winlock(t->w, c); 741 } 742 cut(t, t, nil, FALSE, TRUE, nil, 0); 743 q = 0; 744 q0 = t->q0; 745 q1 = t->q0+snarfbuf.nc; 746 r = fbufalloc(); 747 while(q0 < q1){ 748 n = q1 - q0; 749 if(n > RBUFSIZE) 750 n = RBUFSIZE; 751 if(r == nil) 752 r = runemalloc(n); 753 bufread(&snarfbuf, q, r, n); 754 textinsert(t, q0, r, n, TRUE); 755 q += n; 756 q0 += n; 757 } 758 fbuffree(r); 759 if(selectall) 760 textsetselect(t, t->q0, q1); 761 else 762 textsetselect(t, q1, q1); 763 if(t->w){ 764 textscrdraw(t); 765 winsettag(t->w); 766 } 767 if(t->w!=nil && et->w!=t->w) 768 winunlock(t->w); 769 } 770 771 void 772 look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) 773 { 774 Rune *r; 775 int n; 776 777 if(et && et->w){ 778 t = &et->w->body; 779 if(narg > 0){ 780 search(t, arg, narg); 781 return; 782 } 783 getarg(argt, FALSE, FALSE, &r, &n); 784 if(r == nil){ 785 n = t->q1-t->q0; 786 r = runemalloc(n); 787 bufread(t->file, t->q0, r, n); 788 } 789 search(t, r, n); 790 free(r); 791 } 792 } 793 794 void 795 sendx(Text *et, Text *t, Text*, int, int, Rune*, int) 796 { 797 if(et->w==nil) 798 return; 799 t = &et->w->body; 800 if(t->q0 != t->q1) 801 cut(t, t, nil, TRUE, FALSE, nil, 0); 802 textsetselect(t, t->file->nc, t->file->nc); 803 paste(t, t, nil, TRUE, TRUE, nil, 0); 804 if(textreadc(t, t->file->nc-1) != '\n'){ 805 textinsert(t, t->file->nc, L"\n", 1, TRUE); 806 textsetselect(t, t->file->nc, t->file->nc); 807 } 808 } 809 810 void 811 edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 812 { 813 Rune *r; 814 int len; 815 816 if(et == nil) 817 return; 818 getarg(argt, FALSE, TRUE, &r, &len); 819 seq++; 820 if(r != nil){ 821 editcmd(et, r, len); 822 free(r); 823 }else 824 editcmd(et, arg, narg); 825 } 826 827 void 828 exit(Text*, Text*, Text*, int, int, Rune*, int) 829 { 830 if(rowclean(&row)){ 831 sendul(cexit, 0); 832 threadexits(nil); 833 } 834 } 835 836 void 837 putall(Text*, Text*, Text*, int, int, Rune*, int) 838 { 839 int i, j, e; 840 Window *w; 841 Column *c; 842 char *a; 843 844 for(i=0; i<row.ncol; i++){ 845 c = row.col[i]; 846 for(j=0; j<c->nw; j++){ 847 w = c->w[j]; 848 if(w->isscratch || w->isdir || w->body.file->nname==0) 849 continue; 850 if(w->nopen[QWevent] > 0) 851 continue; 852 a = runetobyte(w->body.file->name, w->body.file->nname); 853 e = access(a, 0); 854 if(w->body.file->mod || w->body.ncache) 855 if(e < 0) 856 warning(nil, "no auto-Put of %s: %r\n", a); 857 else{ 858 wincommit(w, &w->body); 859 put(&w->body, nil, nil, XXX, XXX, nil, 0); 860 } 861 free(a); 862 } 863 } 864 } 865 866 867 void 868 id(Text *et, Text*, Text*, int, int, Rune*, int) 869 { 870 if(et && et->w) 871 warning(nil, "/mnt/acme/%d/\n", et->w->id); 872 } 873 874 void 875 local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 876 { 877 char *a, *aa; 878 Runestr dir; 879 880 aa = getbytearg(argt, TRUE, TRUE, &a); 881 882 dir = dirname(et, nil, 0); 883 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 884 free(dir.r); 885 dir.r = nil; 886 dir.nr = 0; 887 } 888 run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE); 889 } 890 891 void 892 kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg) 893 { 894 Rune *a, *cmd, *r; 895 int na; 896 897 getarg(argt, FALSE, FALSE, &r, &na); 898 if(r) 899 kill(nil, nil, nil, 0, 0, r, na); 900 /* loop condition: *arg is not a blank */ 901 for(;;){ 902 a = findbl(arg, narg, &na); 903 if(a == arg) 904 break; 905 cmd = runemalloc(narg-na+1); 906 runemove(cmd, arg, narg-na); 907 sendp(ckill, cmd); 908 arg = skipbl(a, na, &narg); 909 } 910 } 911 912 void 913 fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) 914 { 915 Rune *a, *r, *flag, *file; 916 int na, nf; 917 char *aa; 918 Reffont *newfont; 919 Dirlist *dp; 920 int i, fix; 921 922 if(et==nil || et->w==nil) 923 return; 924 t = &et->w->body; 925 flag = nil; 926 file = nil; 927 /* loop condition: *arg is not a blank */ 928 nf = 0; 929 for(;;){ 930 a = findbl(arg, narg, &na); 931 if(a == arg) 932 break; 933 r = runemalloc(narg-na+1); 934 runemove(r, arg, narg-na); 935 if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){ 936 free(flag); 937 flag = r; 938 }else{ 939 free(file); 940 file = r; 941 nf = narg-na; 942 } 943 arg = skipbl(a, na, &narg); 944 } 945 getarg(argt, FALSE, TRUE, &r, &na); 946 if(r) 947 if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){ 948 free(flag); 949 flag = r; 950 }else{ 951 free(file); 952 file = r; 953 nf = na; 954 } 955 fix = 1; 956 if(flag) 957 fix = runeeq(flag, runestrlen(flag), L"fix", 3); 958 else if(file == nil){ 959 newfont = rfget(FALSE, FALSE, FALSE, nil); 960 if(newfont) 961 fix = strcmp(newfont->f->name, t->font->name)==0; 962 } 963 if(file){ 964 aa = runetobyte(file, nf); 965 newfont = rfget(fix, flag!=nil, FALSE, aa); 966 free(aa); 967 }else 968 newfont = rfget(fix, FALSE, FALSE, nil); 969 if(newfont){ 970 draw(screen, t->w->r, textcols[BACK], nil, ZP); 971 rfclose(t->reffont); 972 t->reffont = newfont; 973 t->font = newfont->f; 974 frinittick(t); 975 if(t->w->isdir){ 976 t->all.min.x++; /* force recolumnation; disgusting! */ 977 for(i=0; i<t->w->ndl; i++){ 978 dp = t->w->dlp[i]; 979 aa = runetobyte(dp->r, dp->nr); 980 dp->wid = stringwidth(newfont->f, aa); 981 free(aa); 982 } 983 } 984 /* avoid shrinking of window due to quantization */ 985 colgrow(t->w->col, t->w, -1); 986 } 987 free(file); 988 free(flag); 989 } 990 991 void 992 incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 993 { 994 Rune *a, *r; 995 Window *w; 996 int na, n, len; 997 998 if(et==nil || et->w==nil) 999 return; 1000 w = et->w; 1001 n = 0; 1002 getarg(argt, FALSE, TRUE, &r, &len); 1003 if(r){ 1004 n++; 1005 winaddincl(w, r, len); 1006 } 1007 /* loop condition: *arg is not a blank */ 1008 for(;;){ 1009 a = findbl(arg, narg, &na); 1010 if(a == arg) 1011 break; 1012 r = runemalloc(narg-na+1); 1013 runemove(r, arg, narg-na); 1014 n++; 1015 winaddincl(w, r, narg-na); 1016 arg = skipbl(a, na, &narg); 1017 } 1018 if(n==0 && w->nincl){ 1019 for(n=w->nincl; --n>=0; ) 1020 warning(nil, "%S ", w->incl[n]); 1021 warning(nil, "\n"); 1022 } 1023 } 1024 1025 enum { 1026 IGlobal = -2, 1027 IError = -1, 1028 Ion = 0, 1029 Ioff = 1, 1030 }; 1031 1032 static int 1033 indentval(Rune *s, int n) 1034 { 1035 if(n < 2) 1036 return IError; 1037 if(runestrncmp(s, L"ON", n) == 0){ 1038 globalautoindent = TRUE; 1039 warning(nil, "Indent ON\n"); 1040 return IGlobal; 1041 } 1042 if(runestrncmp(s, L"OFF", n) == 0){ 1043 globalautoindent = FALSE; 1044 warning(nil, "Indent OFF\n"); 1045 return IGlobal; 1046 } 1047 return runestrncmp(s, L"on", n) == 0; 1048 } 1049 1050 void 1051 indent(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1052 { 1053 Rune *a, *r; 1054 Window *w; 1055 int na, len, autoindent; 1056 1057 w = nil; 1058 if(et!=nil && et->w!=nil) 1059 w = et->w; 1060 autoindent = IError; 1061 getarg(argt, FALSE, TRUE, &r, &len); 1062 if(r!=nil && len>0) 1063 autoindent = indentval(r, len); 1064 else{ 1065 a = findbl(arg, narg, &na); 1066 if(a != arg) 1067 autoindent = indentval(arg, narg-na); 1068 } 1069 if(w != nil){ 1070 switch(autoindent){ 1071 case Ion: 1072 case Ioff: 1073 w->autoindent = autoindent; 1074 break; 1075 case IGlobal: 1076 w->autoindent = globalautoindent; 1077 break; 1078 } 1079 } 1080 } 1081 1082 void 1083 tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1084 { 1085 Rune *a, *r; 1086 Window *w; 1087 int na, len, tab; 1088 char *p; 1089 1090 if(et==nil || et->w==nil) 1091 return; 1092 w = et->w; 1093 getarg(argt, FALSE, TRUE, &r, &len); 1094 tab = 0; 1095 if(r!=nil && len>0){ 1096 p = runetobyte(r, len); 1097 if('0'<=p[0] && p[0]<='9') 1098 tab = atoi(p); 1099 free(p); 1100 }else{ 1101 a = findbl(arg, narg, &na); 1102 if(a != arg){ 1103 p = runetobyte(arg, narg-na); 1104 if('0'<=p[0] && p[0]<='9') 1105 tab = atoi(p); 1106 free(p); 1107 } 1108 } 1109 if(tab > 0){ 1110 if(w->body.tabstop != tab){ 1111 w->body.tabstop = tab; 1112 winresize(w, w->r, 1); 1113 } 1114 }else 1115 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); 1116 } 1117 1118 void 1119 runproc(void *argvp) 1120 { 1121 /* args: */ 1122 Window *win; 1123 char *s; 1124 Rune *rdir; 1125 int ndir; 1126 int newns; 1127 char *argaddr; 1128 char *arg; 1129 Command *c; 1130 Channel *cpid; 1131 int iseditcmd; 1132 /* end of args */ 1133 char *e, *t, *name, *filename, *dir, **av, *news; 1134 Rune r, **incl; 1135 int ac, w, inarg, i, n, fd, nincl, winid; 1136 int pipechar; 1137 char buf[512]; 1138 static void *parg[2]; 1139 void **argv; 1140 1141 argv = argvp; 1142 win = argv[0]; 1143 s = argv[1]; 1144 rdir = argv[2]; 1145 ndir = (uintptr)argv[3]; 1146 newns = (uintptr)argv[4]; 1147 argaddr = argv[5]; 1148 arg = argv[6]; 1149 c = argv[7]; 1150 cpid = argv[8]; 1151 iseditcmd = (uintptr)argv[9]; 1152 free(argv); 1153 1154 t = s; 1155 while(*t==' ' || *t=='\n' || *t=='\t') 1156 t++; 1157 for(e=t; *e; e++) 1158 if(*e==' ' || *e=='\n' || *e=='\t' ) 1159 break; 1160 name = emalloc((e-t)+2); 1161 memmove(name, t, e-t); 1162 name[e-t] = 0; 1163 e = utfrrune(name, '/'); 1164 if(e) 1165 memmove(name, e+1, strlen(e+1)+1); /* strcpy but overlaps */ 1166 strcat(name, " "); /* add blank here for ease in waittask */ 1167 c->name = bytetorune(name, &c->nname); 1168 free(name); 1169 pipechar = 0; 1170 if(*t=='<' || *t=='|' || *t=='>') 1171 pipechar = *t++; 1172 c->iseditcmd = iseditcmd; 1173 c->text = s; 1174 if(rdir != nil){ 1175 dir = runetobyte(rdir, ndir); 1176 chdir(dir); /* ignore error: probably app. window */ 1177 free(dir); 1178 } 1179 if(newns){ 1180 nincl = 0; 1181 incl = nil; 1182 if(win){ 1183 filename = smprint("%.*S", win->body.file->nname, win->body.file->name); 1184 nincl = win->nincl; 1185 if(nincl > 0){ 1186 incl = emalloc(nincl*sizeof(Rune*)); 1187 for(i=0; i<nincl; i++){ 1188 n = runestrlen(win->incl[i]); 1189 incl[i] = runemalloc(n+1); 1190 runemove(incl[i], win->incl[i], n); 1191 } 1192 } 1193 winid = win->id; 1194 }else{ 1195 filename = nil; 1196 winid = 0; 1197 if(activewin) 1198 winid = activewin->id; 1199 } 1200 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); 1201 sprint(buf, "%d", winid); 1202 putenv("winid", buf); 1203 1204 if(filename){ 1205 putenv("%", filename); 1206 free(filename); 1207 } 1208 c->md = fsysmount(rdir, ndir, incl, nincl); 1209 if(c->md == nil){ 1210 fprint(2, "child: can't mount /dev/cons: %r\n"); 1211 threadexits("mount"); 1212 } 1213 close(0); 1214 if(winid>0 && (pipechar=='|' || pipechar=='>')){ 1215 sprint(buf, "/mnt/acme/%d/rdsel", winid); 1216 open(buf, OREAD); 1217 }else 1218 open("/dev/null", OREAD); 1219 close(1); 1220 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ 1221 if(iseditcmd){ 1222 if(winid > 0) 1223 sprint(buf, "/mnt/acme/%d/editout", winid); 1224 else 1225 sprint(buf, "/mnt/acme/editout"); 1226 }else 1227 sprint(buf, "/mnt/acme/%d/wrsel", winid); 1228 open(buf, OWRITE); 1229 close(2); 1230 open("/dev/cons", OWRITE); 1231 }else{ 1232 open("/dev/cons", OWRITE); 1233 dup(1, 2); 1234 } 1235 }else{ 1236 rfork(RFFDG|RFNOTEG); 1237 fsysclose(); 1238 close(0); 1239 open("/dev/null", OREAD); 1240 close(1); 1241 open(acmeerrorfile, OWRITE); 1242 dup(1, 2); 1243 } 1244 1245 if(win) 1246 winclose(win); 1247 1248 if(argaddr) 1249 putenv("acmeaddr", argaddr); 1250 if(strlen(t) > sizeof buf-10) /* may need to print into stack */ 1251 goto Hard; 1252 inarg = FALSE; 1253 for(e=t; *e; e+=w){ 1254 w = chartorune(&r, e); 1255 if(r==' ' || r=='\t') 1256 continue; 1257 if(r < ' ') 1258 goto Hard; 1259 if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r)) 1260 goto Hard; 1261 inarg = TRUE; 1262 } 1263 if(!inarg) 1264 goto Fail; 1265 1266 ac = 0; 1267 av = nil; 1268 inarg = FALSE; 1269 for(e=t; *e; e+=w){ 1270 w = chartorune(&r, e); 1271 if(r==' ' || r=='\t'){ 1272 inarg = FALSE; 1273 *e = 0; 1274 continue; 1275 } 1276 if(!inarg){ 1277 inarg = TRUE; 1278 av = realloc(av, (ac+1)*sizeof(char**)); 1279 av[ac++] = e; 1280 } 1281 } 1282 av = realloc(av, (ac+2)*sizeof(char**)); 1283 av[ac++] = arg; 1284 av[ac] = nil; 1285 c->av = av; 1286 procexec(cpid, av[0], av); 1287 e = av[0]; 1288 if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) 1289 goto Fail; 1290 if(cputype){ 1291 sprint(buf, "%s/%s", cputype, av[0]); 1292 procexec(cpid, buf, av); 1293 } 1294 sprint(buf, "/bin/%s", av[0]); 1295 procexec(cpid, buf, av); 1296 goto Fail; 1297 1298 Hard: 1299 1300 /* 1301 * ugly: set path = (. $cputype /bin) 1302 * should honor $path if unusual. 1303 */ 1304 if(cputype){ 1305 n = 0; 1306 memmove(buf+n, ".", 2); 1307 n += 2; 1308 i = strlen(cputype)+1; 1309 memmove(buf+n, cputype, i); 1310 n += i; 1311 memmove(buf+n, "/bin", 5); 1312 n += 5; 1313 fd = create("/env/path", OWRITE, 0666); 1314 write(fd, buf, n); 1315 close(fd); 1316 } 1317 1318 if(arg){ 1319 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); 1320 if(news){ 1321 sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ 1322 free(s); 1323 t = news; 1324 c->text = news; 1325 } 1326 } 1327 procexecl(cpid, "/bin/rc", "rc", "-c", t, nil); 1328 1329 Fail: 1330 /* procexec hasn't happened, so send a zero */ 1331 sendul(cpid, 0); 1332 threadexits(nil); 1333 } 1334 1335 void 1336 runwaittask(void *v) 1337 { 1338 Command *c; 1339 Channel *cpid; 1340 void **a; 1341 1342 threadsetname("runwaittask"); 1343 a = v; 1344 c = a[0]; 1345 cpid = a[1]; 1346 free(a); 1347 do 1348 c->pid = recvul(cpid); 1349 while(c->pid == ~0); 1350 free(c->av); 1351 if(c->pid != 0) /* successful exec */ 1352 sendp(ccommand, c); 1353 else{ 1354 if(c->iseditcmd) 1355 sendul(cedit, 0); 1356 free(c->name); 1357 free(c->text); 1358 free(c); 1359 } 1360 chanfree(cpid); 1361 } 1362 1363 void 1364 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) 1365 { 1366 void **arg; 1367 Command *c; 1368 Channel *cpid; 1369 1370 if(s == nil) 1371 return; 1372 1373 arg = emalloc(10*sizeof(void*)); 1374 c = emalloc(sizeof *c); 1375 cpid = chancreate(sizeof(ulong), 0); 1376 arg[0] = win; 1377 arg[1] = s; 1378 arg[2] = rdir; 1379 arg[3] = (void*)ndir; 1380 arg[4] = (void*)newns; 1381 arg[5] = argaddr; 1382 arg[6] = xarg; 1383 arg[7] = c; 1384 arg[8] = cpid; 1385 arg[9] = (void*)iseditcmd; 1386 proccreate(runproc, arg, STACK); 1387 /* mustn't block here because must be ready to answer mount() call in run() */ 1388 arg = emalloc(2*sizeof(void*)); 1389 arg[0] = c; 1390 arg[1] = cpid; 1391 threadcreate(runwaittask, arg, STACK); 1392 } 1393