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 *argt, int flag1, int, Rune *arg, int narg) 324 { 325 Window *w; 326 char *name, *p; 327 Plumbmsg *pm; 328 329 if(et->col==nil || et->w == nil) 330 return; 331 if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)){ 332 w = et->w; 333 name = getname(&w->body, argt, arg, narg, TRUE); 334 if(name && plumbsendfd >= 0){ 335 pm = emalloc(sizeof(Plumbmsg)); 336 pm->src = estrdup("acme"); 337 pm->dst = estrdup("close"); 338 pm->wdir = estrdup(name); 339 if(p = strrchr(pm->wdir, '/')) 340 *p = '\0'; 341 pm->type = estrdup("text"); 342 pm->attr = nil; 343 pm->data = estrdup(name); 344 pm->ndata = strlen(pm->data); 345 if(pm->ndata < messagesize-1024) 346 plumbsend(plumbsendfd, pm); 347 else 348 plumbfree(pm); 349 } 350 colclose(et->col, et->w, TRUE); 351 } 352 } 353 354 void 355 sort(Text *et, Text*, Text*, int, int, Rune*, int) 356 { 357 if(et->col) 358 colsort(et->col); 359 } 360 361 uint 362 seqof(Window *w, int isundo) 363 { 364 /* if it's undo, see who changed with us */ 365 if(isundo) 366 return w->body.file->seq; 367 /* if it's redo, see who we'll be sync'ed up with */ 368 return fileredoseq(w->body.file); 369 } 370 371 void 372 undo(Text *et, Text*, Text*, int flag1, int, Rune*, int) 373 { 374 int i, j; 375 Column *c; 376 Window *w; 377 uint seq; 378 379 if(et==nil || et->w== nil) 380 return; 381 seq = seqof(et->w, flag1); 382 if(seq == 0){ 383 /* nothing to undo */ 384 return; 385 } 386 /* 387 * Undo the executing window first. Its display will update. other windows 388 * in the same file will not call show() and jump to a different location in the file. 389 * Simultaneous changes to other files will be chaotic, however. 390 */ 391 winundo(et->w, flag1); 392 for(i=0; i<row.ncol; i++){ 393 c = row.col[i]; 394 for(j=0; j<c->nw; j++){ 395 w = c->w[j]; 396 if(w == et->w) 397 continue; 398 if(seqof(w, flag1) == seq) 399 winundo(w, flag1); 400 } 401 } 402 } 403 404 char* 405 getname(Text *t, Text *argt, Rune *arg, int narg, int isput) 406 { 407 char *s; 408 Rune *r; 409 int i, n, promote; 410 Runestr dir; 411 412 getarg(argt, FALSE, TRUE, &r, &n); 413 promote = FALSE; 414 if(r == nil) 415 promote = TRUE; 416 else if(isput){ 417 /* if are doing a Put, want to synthesize name even for non-existent file */ 418 /* best guess is that file name doesn't contain a slash */ 419 promote = TRUE; 420 for(i=0; i<n; i++) 421 if(r[i] == '/'){ 422 promote = FALSE; 423 break; 424 } 425 if(promote){ 426 t = argt; 427 arg = r; 428 narg = n; 429 } 430 } 431 if(promote){ 432 n = narg; 433 if(n <= 0){ 434 s = runetobyte(t->file->name, t->file->nname); 435 return s; 436 } 437 /* prefix with directory name if necessary */ 438 dir.r = nil; 439 dir.nr = 0; 440 if(n>0 && arg[0]!='/'){ 441 dir = dirname(t, nil, 0); 442 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 443 free(dir.r); 444 dir.r = nil; 445 dir.nr = 0; 446 } 447 } 448 if(dir.r){ 449 r = runemalloc(dir.nr+n+1); 450 runemove(r, dir.r, dir.nr); 451 free(dir.r); 452 if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/') 453 r[dir.nr++] = '/'; 454 runemove(r+dir.nr, arg, n); 455 n += dir.nr; 456 }else{ 457 r = runemalloc(n+1); 458 runemove(r, arg, n); 459 } 460 } 461 s = runetobyte(r, n); 462 free(r); 463 if(strlen(s) == 0){ 464 free(s); 465 s = nil; 466 } 467 return s; 468 } 469 470 void 471 zeroxx(Text *et, Text *t, Text*, int, int, Rune*, int) 472 { 473 Window *nw; 474 int c, locked; 475 476 locked = FALSE; 477 if(t!=nil && t->w!=nil && t->w!=et->w){ 478 locked = TRUE; 479 c = 'M'; 480 if(et->w) 481 c = et->w->owner; 482 winlock(t->w, c); 483 } 484 if(t == nil) 485 t = et; 486 if(t==nil || t->w==nil) 487 return; 488 t = &t->w->body; 489 if(t->w->isdir) 490 warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name); 491 else{ 492 nw = coladd(t->w->col, nil, t->w, -1); 493 /* ugly: fix locks so w->unlock works */ 494 winlock1(nw, t->w->owner); 495 } 496 if(locked) 497 winunlock(t->w); 498 } 499 500 void 501 get(Text *et, Text *t, Text *argt, int flag1, int, Rune *arg, int narg) 502 { 503 char *name; 504 Rune *r; 505 int i, n, dirty, samename, isdir; 506 Window *w; 507 Text *u; 508 Dir *d; 509 510 if(flag1) 511 if(et==nil || et->w==nil) 512 return; 513 if(!et->w->isdir && (et->w->body.file->nc>0 && !winclean(et->w, TRUE))) 514 return; 515 w = et->w; 516 t = &w->body; 517 name = getname(t, argt, arg, narg, FALSE); 518 if(name == nil){ 519 warning(nil, "no file name\n"); 520 return; 521 } 522 if(t->file->ntext>1){ 523 d = dirstat(name); 524 isdir = (d!=nil && (d->qid.type & QTDIR)); 525 free(d); 526 if(isdir){ 527 warning(nil, "%s is a directory; can't read with multiple windows on it\n", name); 528 return; 529 } 530 } 531 r = bytetorune(name, &n); 532 for(i=0; i<t->file->ntext; i++){ 533 u = t->file->text[i]; 534 /* second and subsequent calls with zero an already empty buffer, but OK */ 535 textreset(u); 536 windirfree(u->w); 537 } 538 samename = runeeq(r, n, t->file->name, t->file->nname); 539 textload(t, 0, name, samename); 540 if(samename){ 541 t->file->mod = FALSE; 542 dirty = FALSE; 543 }else{ 544 t->file->mod = TRUE; 545 dirty = TRUE; 546 } 547 for(i=0; i<t->file->ntext; i++) 548 t->file->text[i]->w->dirty = dirty; 549 free(name); 550 free(r); 551 winsettag(w); 552 t->file->unread = FALSE; 553 for(i=0; i<t->file->ntext; i++){ 554 u = t->file->text[i]; 555 textsetselect(&u->w->tag, u->w->tag.file->nc, u->w->tag.file->nc); 556 textscrdraw(u); 557 } 558 } 559 560 void 561 putfile(File *f, int q0, int q1, Rune *namer, int nname) 562 { 563 uint n, m; 564 Rune *r; 565 char *s, *name, *p; 566 int i, fd, q; 567 Dir *d, *d1; 568 Window *w; 569 Plumbmsg *pm; 570 int isapp; 571 572 w = f->curtext->w; 573 name = runetobyte(namer, nname); 574 d = dirstat(name); 575 if(d!=nil && runeeq(namer, nname, f->name, f->nname)){ 576 /* f->mtime+1 because when talking over NFS it's often off by a second */ 577 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime+1<d->mtime){ 578 f->dev = d->dev; 579 f->qidpath = d->qid.path; 580 f->mtime = d->mtime; 581 if(f->unread) 582 warning(nil, "%s not written; file already exists\n", name); 583 else 584 warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid); 585 goto Rescue1; 586 } 587 } 588 fd = create(name, OWRITE, 0666); 589 if(fd < 0){ 590 warning(nil, "can't create file %s: %r\n", name); 591 goto Rescue1; 592 } 593 r = fbufalloc(); 594 s = fbufalloc(); 595 free(d); 596 d = dirfstat(fd); 597 isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND)); 598 if(isapp){ 599 warning(nil, "%s not written; file is append only\n", name); 600 goto Rescue2; 601 } 602 603 for(q=q0; q<q1; q+=n){ 604 n = q1 - q; 605 if(n > BUFSIZE/UTFmax) 606 n = BUFSIZE/UTFmax; 607 bufread(f, q, r, n); 608 m = snprint(s, BUFSIZE+1, "%.*S", n, r); 609 if(write(fd, s, m) != m){ 610 warning(nil, "can't write file %s: %r\n", name); 611 goto Rescue2; 612 } 613 } 614 if(runeeq(namer, nname, f->name, f->nname)){ 615 if(q0!=0 || q1!=f->nc){ 616 f->mod = TRUE; 617 w->dirty = TRUE; 618 f->unread = TRUE; 619 }else{ 620 d1 = dirfstat(fd); 621 if(d1 != nil){ 622 free(d); 623 d = d1; 624 } 625 f->qidpath = d->qid.path; 626 f->dev = d->dev; 627 f->mtime = d->mtime; 628 f->mod = FALSE; 629 w->dirty = FALSE; 630 f->unread = FALSE; 631 } 632 for(i=0; i<f->ntext; i++){ 633 f->text[i]->w->putseq = f->seq; 634 f->text[i]->w->dirty = w->dirty; 635 } 636 } 637 if(plumbsendfd >= 0){ 638 pm = emalloc(sizeof(Plumbmsg)); 639 pm->src = estrdup("acme"); 640 pm->dst = estrdup("put"); 641 pm->wdir = estrdup(name); 642 if(p = strrchr(pm->wdir, '/')) 643 *p = '\0'; 644 pm->type = estrdup("text"); 645 pm->attr = nil; 646 pm->data = estrdup(name); 647 pm->ndata = strlen(pm->data); 648 if(pm->ndata < messagesize-1024) 649 plumbsend(plumbsendfd, pm); 650 else 651 plumbfree(pm); 652 } 653 fbuffree(s); 654 fbuffree(r); 655 free(d); 656 free(namer); 657 free(name); 658 close(fd); 659 winsettag(w); 660 return; 661 662 Rescue2: 663 fbuffree(s); 664 fbuffree(r); 665 close(fd); 666 /* fall through */ 667 668 Rescue1: 669 free(d); 670 free(namer); 671 free(name); 672 } 673 674 void 675 put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 676 { 677 int nname; 678 Rune *namer; 679 Window *w; 680 File *f; 681 char *name; 682 683 if(et==nil || et->w==nil || et->w->isdir) 684 return; 685 w = et->w; 686 f = w->body.file; 687 name = getname(&w->body, argt, arg, narg, TRUE); 688 if(name == nil){ 689 warning(nil, "no file name\n"); 690 return; 691 } 692 namer = bytetorune(name, &nname); 693 putfile(f, 0, f->nc, namer, nname); 694 free(name); 695 } 696 697 void 698 dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg) 699 { 700 char *name; 701 702 if(narg) 703 name = runetobyte(arg, narg); 704 else 705 getbytearg(argt, FALSE, TRUE, &name); 706 if(isdump) 707 rowdump(&row, name); 708 else 709 rowload(&row, name, FALSE); 710 free(name); 711 } 712 713 void 714 cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int) 715 { 716 uint q0, q1, n, locked, c; 717 Rune *r; 718 719 /* 720 * if not executing a mouse chord (et != t) and snarfing (dosnarf) 721 * and executed Cut or Snarf in window tag (et->w != nil), 722 * then use the window body selection or the tag selection 723 * or do nothing at all. 724 */ 725 if(et!=t && dosnarf && et->w!=nil){ 726 if(et->w->body.q1>et->w->body.q0){ 727 t = &et->w->body; 728 if(docut) 729 filemark(t->file); /* seq has been incremented by execute */ 730 }else if(et->w->tag.q1>et->w->tag.q0) 731 t = &et->w->tag; 732 else 733 t = nil; 734 } 735 if(t == nil) /* no selection */ 736 return; 737 738 locked = FALSE; 739 if(t->w!=nil && et->w!=t->w){ 740 locked = TRUE; 741 c = 'M'; 742 if(et->w) 743 c = et->w->owner; 744 winlock(t->w, c); 745 } 746 if(t->q0 == t->q1){ 747 if(locked) 748 winunlock(t->w); 749 return; 750 } 751 if(dosnarf){ 752 q0 = t->q0; 753 q1 = t->q1; 754 bufdelete(&snarfbuf, 0, snarfbuf.nc); 755 r = fbufalloc(); 756 while(q0 < q1){ 757 n = q1 - q0; 758 if(n > RBUFSIZE) 759 n = RBUFSIZE; 760 bufread(t->file, q0, r, n); 761 bufinsert(&snarfbuf, snarfbuf.nc, r, n); 762 q0 += n; 763 } 764 fbuffree(r); 765 putsnarf(); 766 } 767 if(docut){ 768 textdelete(t, t->q0, t->q1, TRUE); 769 textsetselect(t, t->q0, t->q0); 770 if(t->w){ 771 textscrdraw(t); 772 winsettag(t->w); 773 } 774 }else if(dosnarf) /* Snarf command */ 775 argtext = t; 776 if(locked) 777 winunlock(t->w); 778 } 779 780 void 781 paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int) 782 { 783 int c; 784 uint q, q0, q1, n; 785 Rune *r; 786 787 /* if(tobody), use body of executing window (Paste or Send command) */ 788 if(tobody && et!=nil && et->w!=nil){ 789 t = &et->w->body; 790 filemark(t->file); /* seq has been incremented by execute */ 791 } 792 if(t == nil) 793 return; 794 795 getsnarf(); 796 if(t==nil || snarfbuf.nc==0) 797 return; 798 if(t->w!=nil && et->w!=t->w){ 799 c = 'M'; 800 if(et->w) 801 c = et->w->owner; 802 winlock(t->w, c); 803 } 804 cut(t, t, nil, FALSE, TRUE, nil, 0); 805 q = 0; 806 q0 = t->q0; 807 q1 = t->q0+snarfbuf.nc; 808 r = fbufalloc(); 809 while(q0 < q1){ 810 n = q1 - q0; 811 if(n > RBUFSIZE) 812 n = RBUFSIZE; 813 if(r == nil) 814 r = runemalloc(n); 815 bufread(&snarfbuf, q, r, n); 816 textinsert(t, q0, r, n, TRUE); 817 q += n; 818 q0 += n; 819 } 820 fbuffree(r); 821 if(selectall) 822 textsetselect(t, t->q0, q1); 823 else 824 textsetselect(t, q1, q1); 825 if(t->w){ 826 textscrdraw(t); 827 winsettag(t->w); 828 } 829 if(t->w!=nil && et->w!=t->w) 830 winunlock(t->w); 831 } 832 833 void 834 look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) 835 { 836 Rune *r; 837 int n; 838 839 if(et && et->w){ 840 t = &et->w->body; 841 if(narg > 0){ 842 search(t, arg, narg); 843 return; 844 } 845 getarg(argt, FALSE, FALSE, &r, &n); 846 if(r == nil){ 847 n = t->q1-t->q0; 848 r = runemalloc(n); 849 bufread(t->file, t->q0, r, n); 850 } 851 search(t, r, n); 852 free(r); 853 } 854 } 855 856 void 857 sendx(Text *et, Text *t, Text*, int, int, Rune*, int) 858 { 859 if(et->w==nil) 860 return; 861 t = &et->w->body; 862 if(t->q0 != t->q1) 863 cut(t, t, nil, TRUE, FALSE, nil, 0); 864 textsetselect(t, t->file->nc, t->file->nc); 865 paste(t, t, nil, TRUE, TRUE, nil, 0); 866 if(textreadc(t, t->file->nc-1) != '\n'){ 867 textinsert(t, t->file->nc, L"\n", 1, TRUE); 868 textsetselect(t, t->file->nc, t->file->nc); 869 } 870 } 871 872 void 873 edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 874 { 875 Rune *r; 876 int len; 877 878 if(et == nil) 879 return; 880 getarg(argt, FALSE, TRUE, &r, &len); 881 seq++; 882 if(r != nil){ 883 editcmd(et, r, len); 884 free(r); 885 }else 886 editcmd(et, arg, narg); 887 } 888 889 void 890 exit(Text*, Text*, Text*, int, int, Rune*, int) 891 { 892 if(rowclean(&row)){ 893 sendul(cexit, 0); 894 threadexits(nil); 895 } 896 } 897 898 void 899 putall(Text*, Text*, Text*, int, int, Rune*, int) 900 { 901 int i, j, e; 902 Window *w; 903 Column *c; 904 char *a; 905 906 for(i=0; i<row.ncol; i++){ 907 c = row.col[i]; 908 for(j=0; j<c->nw; j++){ 909 w = c->w[j]; 910 if(w->isscratch || w->isdir || w->body.file->nname==0) 911 continue; 912 if(w->nopen[QWevent] > 0) 913 continue; 914 a = runetobyte(w->body.file->name, w->body.file->nname); 915 e = access(a, 0); 916 if(w->body.file->mod || w->body.ncache) 917 if(e < 0) 918 warning(nil, "no auto-Put of %s: %r\n", a); 919 else{ 920 wincommit(w, &w->body); 921 put(&w->body, nil, nil, XXX, XXX, nil, 0); 922 } 923 free(a); 924 } 925 } 926 } 927 928 929 void 930 id(Text *et, Text*, Text*, int, int, Rune*, int) 931 { 932 if(et && et->w) 933 warning(nil, "/mnt/acme/%d/\n", et->w->id); 934 } 935 936 void 937 local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 938 { 939 char *a, *aa; 940 Runestr dir; 941 942 aa = getbytearg(argt, TRUE, TRUE, &a); 943 944 dir = dirname(et, nil, 0); 945 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 946 free(dir.r); 947 dir.r = nil; 948 dir.nr = 0; 949 } 950 run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE); 951 } 952 953 void 954 kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg) 955 { 956 Rune *a, *cmd, *r; 957 int na; 958 959 getarg(argt, FALSE, FALSE, &r, &na); 960 if(r) 961 kill(nil, nil, nil, 0, 0, r, na); 962 /* loop condition: *arg is not a blank */ 963 for(;;){ 964 a = findbl(arg, narg, &na); 965 if(a == arg) 966 break; 967 cmd = runemalloc(narg-na+1); 968 runemove(cmd, arg, narg-na); 969 sendp(ckill, cmd); 970 arg = skipbl(a, na, &narg); 971 } 972 } 973 974 void 975 fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) 976 { 977 Rune *a, *r, *flag, *file; 978 int na, nf; 979 char *aa; 980 Reffont *newfont; 981 Dirlist *dp; 982 int i, fix; 983 984 if(et==nil || et->w==nil) 985 return; 986 t = &et->w->body; 987 flag = nil; 988 file = nil; 989 /* loop condition: *arg is not a blank */ 990 nf = 0; 991 for(;;){ 992 a = findbl(arg, narg, &na); 993 if(a == arg) 994 break; 995 r = runemalloc(narg-na+1); 996 runemove(r, arg, narg-na); 997 if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){ 998 free(flag); 999 flag = r; 1000 }else{ 1001 free(file); 1002 file = r; 1003 nf = narg-na; 1004 } 1005 arg = skipbl(a, na, &narg); 1006 } 1007 getarg(argt, FALSE, TRUE, &r, &na); 1008 if(r) 1009 if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){ 1010 free(flag); 1011 flag = r; 1012 }else{ 1013 free(file); 1014 file = r; 1015 nf = na; 1016 } 1017 fix = 1; 1018 if(flag) 1019 fix = runeeq(flag, runestrlen(flag), L"fix", 3); 1020 else if(file == nil){ 1021 newfont = rfget(FALSE, FALSE, FALSE, nil); 1022 if(newfont) 1023 fix = strcmp(newfont->f->name, t->font->name)==0; 1024 } 1025 if(file){ 1026 aa = runetobyte(file, nf); 1027 newfont = rfget(fix, flag!=nil, FALSE, aa); 1028 free(aa); 1029 }else 1030 newfont = rfget(fix, FALSE, FALSE, nil); 1031 if(newfont){ 1032 draw(screen, t->w->r, textcols[BACK], nil, ZP); 1033 rfclose(t->reffont); 1034 t->reffont = newfont; 1035 t->font = newfont->f; 1036 frinittick(t); 1037 if(t->w->isdir){ 1038 t->all.min.x++; /* force recolumnation; disgusting! */ 1039 for(i=0; i<t->w->ndl; i++){ 1040 dp = t->w->dlp[i]; 1041 aa = runetobyte(dp->r, dp->nr); 1042 dp->wid = stringwidth(newfont->f, aa); 1043 free(aa); 1044 } 1045 } 1046 /* avoid shrinking of window due to quantization */ 1047 colgrow(t->w->col, t->w, -1); 1048 } 1049 free(file); 1050 free(flag); 1051 } 1052 1053 void 1054 incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1055 { 1056 Rune *a, *r; 1057 Window *w; 1058 int na, n, len; 1059 1060 if(et==nil || et->w==nil) 1061 return; 1062 w = et->w; 1063 n = 0; 1064 getarg(argt, FALSE, TRUE, &r, &len); 1065 if(r){ 1066 n++; 1067 winaddincl(w, r, len); 1068 } 1069 /* loop condition: *arg is not a blank */ 1070 for(;;){ 1071 a = findbl(arg, narg, &na); 1072 if(a == arg) 1073 break; 1074 r = runemalloc(narg-na+1); 1075 runemove(r, arg, narg-na); 1076 n++; 1077 winaddincl(w, r, narg-na); 1078 arg = skipbl(a, na, &narg); 1079 } 1080 if(n==0 && w->nincl){ 1081 for(n=w->nincl; --n>=0; ) 1082 warning(nil, "%S ", w->incl[n]); 1083 warning(nil, "\n"); 1084 } 1085 } 1086 1087 enum { 1088 IGlobal = -2, 1089 IError = -1, 1090 Ion = 0, 1091 Ioff = 1, 1092 }; 1093 1094 static int 1095 indentval(Rune *s, int n) 1096 { 1097 if(n < 2) 1098 return IError; 1099 if(runestrncmp(s, L"ON", n) == 0){ 1100 globalautoindent = TRUE; 1101 warning(nil, "Indent ON\n"); 1102 return IGlobal; 1103 } 1104 if(runestrncmp(s, L"OFF", n) == 0){ 1105 globalautoindent = FALSE; 1106 warning(nil, "Indent OFF\n"); 1107 return IGlobal; 1108 } 1109 return runestrncmp(s, L"on", n) == 0; 1110 } 1111 1112 static void 1113 fixindent(Window *w, void*) 1114 { 1115 w->autoindent = globalautoindent; 1116 } 1117 1118 void 1119 indent(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1120 { 1121 Rune *a, *r; 1122 Window *w; 1123 int na, len, autoindent; 1124 1125 w = nil; 1126 if(et!=nil && et->w!=nil) 1127 w = et->w; 1128 autoindent = IError; 1129 getarg(argt, FALSE, TRUE, &r, &len); 1130 if(r!=nil && len>0) 1131 autoindent = indentval(r, len); 1132 else{ 1133 a = findbl(arg, narg, &na); 1134 if(a != arg) 1135 autoindent = indentval(arg, narg-na); 1136 } 1137 if(autoindent == IGlobal) 1138 allwindows(fixindent, nil); 1139 else if(w != nil && autoindent >= 0) 1140 w->autoindent = autoindent; 1141 } 1142 1143 void 1144 tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1145 { 1146 Rune *a, *r; 1147 Window *w; 1148 int na, len, tab; 1149 char *p; 1150 1151 if(et==nil || et->w==nil) 1152 return; 1153 w = et->w; 1154 getarg(argt, FALSE, TRUE, &r, &len); 1155 tab = 0; 1156 if(r!=nil && len>0){ 1157 p = runetobyte(r, len); 1158 if('0'<=p[0] && p[0]<='9') 1159 tab = atoi(p); 1160 free(p); 1161 }else{ 1162 a = findbl(arg, narg, &na); 1163 if(a != arg){ 1164 p = runetobyte(arg, narg-na); 1165 if('0'<=p[0] && p[0]<='9') 1166 tab = atoi(p); 1167 free(p); 1168 } 1169 } 1170 if(tab > 0){ 1171 if(w->body.tabstop != tab){ 1172 w->body.tabstop = tab; 1173 winresize(w, w->r, 1); 1174 } 1175 }else 1176 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); 1177 } 1178 1179 void 1180 runproc(void *argvp) 1181 { 1182 /* args: */ 1183 Window *win; 1184 char *s; 1185 Rune *rdir; 1186 int ndir; 1187 int newns; 1188 char *argaddr; 1189 char *arg; 1190 Command *c; 1191 Channel *cpid; 1192 int iseditcmd; 1193 /* end of args */ 1194 char *e, *t, *name, *filename, *dir, **av, *news; 1195 Rune r, **incl; 1196 int ac, w, inarg, i, n, fd, nincl, winid; 1197 int pipechar; 1198 char buf[512]; 1199 static void *parg[2]; 1200 void **argv; 1201 1202 argv = argvp; 1203 win = argv[0]; 1204 s = argv[1]; 1205 rdir = argv[2]; 1206 ndir = (uintptr)argv[3]; 1207 newns = (uintptr)argv[4]; 1208 argaddr = argv[5]; 1209 arg = argv[6]; 1210 c = argv[7]; 1211 cpid = argv[8]; 1212 iseditcmd = (uintptr)argv[9]; 1213 free(argv); 1214 1215 t = s; 1216 while(*t==' ' || *t=='\n' || *t=='\t') 1217 t++; 1218 for(e=t; *e; e++) 1219 if(*e==' ' || *e=='\n' || *e=='\t' ) 1220 break; 1221 name = emalloc((e-t)+2); 1222 memmove(name, t, e-t); 1223 name[e-t] = 0; 1224 e = utfrrune(name, '/'); 1225 if(e) 1226 memmove(name, e+1, strlen(e+1)+1); /* strcpy but overlaps */ 1227 strcat(name, " "); /* add blank here for ease in waittask */ 1228 c->name = bytetorune(name, &c->nname); 1229 free(name); 1230 pipechar = 0; 1231 if(*t=='<' || *t=='|' || *t=='>') 1232 pipechar = *t++; 1233 c->iseditcmd = iseditcmd; 1234 c->text = s; 1235 if(rdir != nil){ 1236 dir = runetobyte(rdir, ndir); 1237 chdir(dir); /* ignore error: probably app. window */ 1238 free(dir); 1239 } 1240 if(newns){ 1241 nincl = 0; 1242 incl = nil; 1243 if(win){ 1244 filename = smprint("%.*S", win->body.file->nname, win->body.file->name); 1245 nincl = win->nincl; 1246 if(nincl > 0){ 1247 incl = emalloc(nincl*sizeof(Rune*)); 1248 for(i=0; i<nincl; i++){ 1249 n = runestrlen(win->incl[i]); 1250 incl[i] = runemalloc(n+1); 1251 runemove(incl[i], win->incl[i], n); 1252 } 1253 } 1254 winid = win->id; 1255 }else{ 1256 filename = nil; 1257 winid = 0; 1258 if(activewin) 1259 winid = activewin->id; 1260 } 1261 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); 1262 sprint(buf, "%d", winid); 1263 putenv("winid", buf); 1264 1265 if(filename){ 1266 putenv("%", filename); 1267 free(filename); 1268 } 1269 c->md = fsysmount(rdir, ndir, incl, nincl); 1270 if(c->md == nil){ 1271 fprint(2, "child: can't mount /dev/cons: %r\n"); 1272 threadexits("mount"); 1273 } 1274 close(0); 1275 if(winid>0 && (pipechar=='|' || pipechar=='>')){ 1276 sprint(buf, "/mnt/acme/%d/rdsel", winid); 1277 open(buf, OREAD); 1278 }else 1279 open("/dev/null", OREAD); 1280 close(1); 1281 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ 1282 if(iseditcmd){ 1283 if(winid > 0) 1284 sprint(buf, "/mnt/acme/%d/editout", winid); 1285 else 1286 sprint(buf, "/mnt/acme/editout"); 1287 }else 1288 sprint(buf, "/mnt/acme/%d/wrsel", winid); 1289 open(buf, OWRITE); 1290 close(2); 1291 open("/dev/cons", OWRITE); 1292 }else{ 1293 open("/dev/cons", OWRITE); 1294 dup(1, 2); 1295 } 1296 }else{ 1297 rfork(RFFDG|RFNOTEG); 1298 fsysclose(); 1299 close(0); 1300 open("/dev/null", OREAD); 1301 close(1); 1302 open(acmeerrorfile, OWRITE); 1303 dup(1, 2); 1304 } 1305 1306 if(win) 1307 winclose(win); 1308 1309 if(argaddr) 1310 putenv("acmeaddr", argaddr); 1311 if(strlen(t) > sizeof buf-10) /* may need to print into stack */ 1312 goto Hard; 1313 inarg = FALSE; 1314 for(e=t; *e; e+=w){ 1315 w = chartorune(&r, e); 1316 if(r==' ' || r=='\t') 1317 continue; 1318 if(r < ' ') 1319 goto Hard; 1320 if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r)) 1321 goto Hard; 1322 inarg = TRUE; 1323 } 1324 if(!inarg) 1325 goto Fail; 1326 1327 ac = 0; 1328 av = nil; 1329 inarg = FALSE; 1330 for(e=t; *e; e+=w){ 1331 w = chartorune(&r, e); 1332 if(r==' ' || r=='\t'){ 1333 inarg = FALSE; 1334 *e = 0; 1335 continue; 1336 } 1337 if(!inarg){ 1338 inarg = TRUE; 1339 av = realloc(av, (ac+1)*sizeof(char**)); 1340 av[ac++] = e; 1341 } 1342 } 1343 av = realloc(av, (ac+2)*sizeof(char**)); 1344 av[ac++] = arg; 1345 av[ac] = nil; 1346 c->av = av; 1347 procexec(cpid, av[0], av); 1348 e = av[0]; 1349 if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) 1350 goto Fail; 1351 if(cputype){ 1352 sprint(buf, "%s/%s", cputype, av[0]); 1353 procexec(cpid, buf, av); 1354 } 1355 sprint(buf, "/bin/%s", av[0]); 1356 procexec(cpid, buf, av); 1357 goto Fail; 1358 1359 Hard: 1360 1361 /* 1362 * ugly: set path = (. $cputype /bin) 1363 * should honor $path if unusual. 1364 */ 1365 if(cputype){ 1366 n = 0; 1367 memmove(buf+n, ".", 2); 1368 n += 2; 1369 i = strlen(cputype)+1; 1370 memmove(buf+n, cputype, i); 1371 n += i; 1372 memmove(buf+n, "/bin", 5); 1373 n += 5; 1374 fd = create("/env/path", OWRITE, 0666); 1375 write(fd, buf, n); 1376 close(fd); 1377 } 1378 1379 if(arg){ 1380 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); 1381 if(news){ 1382 sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ 1383 free(s); 1384 t = news; 1385 c->text = news; 1386 } 1387 } 1388 procexecl(cpid, "/bin/rc", "rc", "-c", t, nil); 1389 1390 Fail: 1391 /* procexec hasn't happened, so send a zero */ 1392 sendul(cpid, 0); 1393 threadexits(nil); 1394 } 1395 1396 void 1397 runwaittask(void *v) 1398 { 1399 Command *c; 1400 Channel *cpid; 1401 void **a; 1402 1403 threadsetname("runwaittask"); 1404 a = v; 1405 c = a[0]; 1406 cpid = a[1]; 1407 free(a); 1408 do 1409 c->pid = recvul(cpid); 1410 while(c->pid == ~0); 1411 free(c->av); 1412 if(c->pid != 0) /* successful exec */ 1413 sendp(ccommand, c); 1414 else{ 1415 if(c->iseditcmd) 1416 sendul(cedit, 0); 1417 free(c->name); 1418 free(c->text); 1419 free(c); 1420 } 1421 chanfree(cpid); 1422 } 1423 1424 void 1425 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) 1426 { 1427 void **arg; 1428 Command *c; 1429 Channel *cpid; 1430 1431 if(s == nil) 1432 return; 1433 1434 arg = emalloc(10*sizeof(void*)); 1435 c = emalloc(sizeof *c); 1436 cpid = chancreate(sizeof(ulong), 0); 1437 arg[0] = win; 1438 arg[1] = s; 1439 arg[2] = rdir; 1440 arg[3] = (void*)ndir; 1441 arg[4] = (void*)newns; 1442 arg[5] = argaddr; 1443 arg[6] = xarg; 1444 arg[7] = c; 1445 arg[8] = cpid; 1446 arg[9] = (void*)iseditcmd; 1447 proccreate(runproc, arg, STACK); 1448 /* mustn't block here because must be ready to answer mount() call in run() */ 1449 arg = emalloc(2*sizeof(void*)); 1450 arg[0] = c; 1451 arg[1] = cpid; 1452 threadcreate(runwaittask, arg, STACK); 1453 } 1454