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