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