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 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime<d->mtime){ 532 f->dev = d->dev; 533 f->qidpath = d->qid.path; 534 f->mtime = d->mtime; 535 if(f->unread) 536 warning(nil, "%s not written; file already exists\n", name); 537 else 538 warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid); 539 goto Rescue1; 540 } 541 } 542 fd = create(name, OWRITE, 0666); 543 if(fd < 0){ 544 warning(nil, "can't create file %s: %r\n", name); 545 goto Rescue1; 546 } 547 r = fbufalloc(); 548 s = fbufalloc(); 549 free(d); 550 d = dirfstat(fd); 551 isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND)); 552 if(isapp){ 553 warning(nil, "%s not written; file is append only\n", name); 554 goto Rescue2; 555 } 556 557 for(q=q0; q<q1; q+=n){ 558 n = q1 - q; 559 if(n > BUFSIZE/UTFmax) 560 n = BUFSIZE/UTFmax; 561 bufread(f, q, r, n); 562 m = snprint(s, BUFSIZE+1, "%.*S", n, r); 563 if(write(fd, s, m) != m){ 564 warning(nil, "can't write file %s: %r\n", name); 565 goto Rescue2; 566 } 567 } 568 if(runeeq(namer, nname, f->name, f->nname)){ 569 if(q0!=0 || q1!=f->nc){ 570 f->mod = TRUE; 571 w->dirty = TRUE; 572 f->unread = TRUE; 573 }else{ 574 d1 = dirfstat(fd); 575 if(d1 != nil){ 576 free(d); 577 d = d1; 578 } 579 f->qidpath = d->qid.path; 580 f->dev = d->dev; 581 f->mtime = d->mtime; 582 f->mod = FALSE; 583 w->dirty = FALSE; 584 f->unread = FALSE; 585 } 586 for(i=0; i<f->ntext; i++){ 587 f->text[i]->w->putseq = f->seq; 588 f->text[i]->w->dirty = w->dirty; 589 } 590 } 591 fbuffree(s); 592 fbuffree(r); 593 free(d); 594 free(namer); 595 free(name); 596 close(fd); 597 winsettag(w); 598 return; 599 600 Rescue2: 601 fbuffree(s); 602 fbuffree(r); 603 close(fd); 604 /* fall through */ 605 606 Rescue1: 607 free(d); 608 free(namer); 609 free(name); 610 } 611 612 void 613 put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 614 { 615 int nname; 616 Rune *namer; 617 Window *w; 618 File *f; 619 char *name; 620 621 if(et==nil || et->w==nil || et->w->isdir) 622 return; 623 w = et->w; 624 f = w->body.file; 625 name = getname(&w->body, argt, arg, narg, TRUE); 626 if(name == nil){ 627 warning(nil, "no file name\n"); 628 return; 629 } 630 namer = bytetorune(name, &nname); 631 putfile(f, 0, f->nc, namer, nname); 632 free(name); 633 } 634 635 void 636 dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg) 637 { 638 char *name; 639 640 if(narg) 641 name = runetobyte(arg, narg); 642 else 643 getbytearg(argt, FALSE, TRUE, &name); 644 if(isdump) 645 rowdump(&row, name); 646 else 647 rowload(&row, name, FALSE); 648 free(name); 649 } 650 651 void 652 cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int) 653 { 654 uint q0, q1, n, locked, c; 655 Rune *r; 656 657 /* use current window if snarfing and its selection is non-null */ 658 if(et!=t && dosnarf && et->w!=nil){ 659 if(et->w->body.q1>et->w->body.q0){ 660 t = &et->w->body; 661 if(docut) 662 filemark(t->file); /* seq has been incremented by execute */ 663 }else if(et->w->tag.q1>et->w->tag.q0) 664 t = &et->w->tag; 665 } 666 if(t == nil){ 667 /* can only happen if seltext == nil */ 668 return; 669 } 670 locked = FALSE; 671 if(t->w!=nil && et->w!=t->w){ 672 locked = TRUE; 673 c = 'M'; 674 if(et->w) 675 c = et->w->owner; 676 winlock(t->w, c); 677 } 678 if(t->q0 == t->q1){ 679 if(locked) 680 winunlock(t->w); 681 return; 682 } 683 if(dosnarf){ 684 q0 = t->q0; 685 q1 = t->q1; 686 bufdelete(&snarfbuf, 0, snarfbuf.nc); 687 r = fbufalloc(); 688 while(q0 < q1){ 689 n = q1 - q0; 690 if(n > RBUFSIZE) 691 n = RBUFSIZE; 692 bufread(t->file, q0, r, n); 693 bufinsert(&snarfbuf, snarfbuf.nc, r, n); 694 q0 += n; 695 } 696 fbuffree(r); 697 putsnarf(); 698 } 699 if(docut){ 700 textdelete(t, t->q0, t->q1, TRUE); 701 textsetselect(t, t->q0, t->q0); 702 if(t->w){ 703 textscrdraw(t); 704 winsettag(t->w); 705 } 706 }else if(dosnarf) /* Snarf command */ 707 argtext = t; 708 if(locked) 709 winunlock(t->w); 710 } 711 712 void 713 paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int) 714 { 715 int c; 716 uint q, q0, q1, n; 717 Rune *r; 718 719 /* if(tobody), use body of executing window (Paste or Send command) */ 720 if(tobody && et!=nil && et->w!=nil){ 721 t = &et->w->body; 722 filemark(t->file); /* seq has been incremented by execute */ 723 } 724 if(t == nil) 725 return; 726 727 getsnarf(); 728 if(t==nil || snarfbuf.nc==0) 729 return; 730 if(t->w!=nil && et->w!=t->w){ 731 c = 'M'; 732 if(et->w) 733 c = et->w->owner; 734 winlock(t->w, c); 735 } 736 cut(t, t, nil, FALSE, TRUE, nil, 0); 737 q = 0; 738 q0 = t->q0; 739 q1 = t->q0+snarfbuf.nc; 740 r = fbufalloc(); 741 while(q0 < q1){ 742 n = q1 - q0; 743 if(n > RBUFSIZE) 744 n = RBUFSIZE; 745 if(r == nil) 746 r = runemalloc(n); 747 bufread(&snarfbuf, q, r, n); 748 textinsert(t, q0, r, n, TRUE); 749 q += n; 750 q0 += n; 751 } 752 fbuffree(r); 753 if(selectall) 754 textsetselect(t, t->q0, q1); 755 else 756 textsetselect(t, q1, q1); 757 if(t->w){ 758 textscrdraw(t); 759 winsettag(t->w); 760 } 761 if(t->w!=nil && et->w!=t->w) 762 winunlock(t->w); 763 } 764 765 void 766 look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) 767 { 768 Rune *r; 769 int n; 770 771 if(et && et->w){ 772 t = &et->w->body; 773 if(narg > 0){ 774 search(t, arg, narg); 775 return; 776 } 777 getarg(argt, FALSE, FALSE, &r, &n); 778 if(r == nil){ 779 n = t->q1-t->q0; 780 r = runemalloc(n); 781 bufread(t->file, t->q0, r, n); 782 } 783 search(t, r, n); 784 free(r); 785 } 786 } 787 788 void 789 sendx(Text *et, Text *t, Text*, int, int, Rune*, int) 790 { 791 if(et->w==nil) 792 return; 793 t = &et->w->body; 794 if(t->q0 != t->q1) 795 cut(t, t, nil, TRUE, FALSE, nil, 0); 796 textsetselect(t, t->file->nc, t->file->nc); 797 paste(t, t, nil, TRUE, TRUE, nil, 0); 798 if(textreadc(t, t->file->nc-1) != '\n'){ 799 textinsert(t, t->file->nc, L"\n", 1, TRUE); 800 textsetselect(t, t->file->nc, t->file->nc); 801 } 802 } 803 804 void 805 edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 806 { 807 Rune *r; 808 int len; 809 810 if(et == nil) 811 return; 812 getarg(argt, FALSE, TRUE, &r, &len); 813 seq++; 814 if(r != nil){ 815 editcmd(et, r, len); 816 free(r); 817 }else 818 editcmd(et, arg, narg); 819 } 820 821 void 822 exit(Text*, Text*, Text*, int, int, Rune*, int) 823 { 824 if(rowclean(&row)){ 825 sendul(cexit, 0); 826 threadexits(nil); 827 } 828 } 829 830 void 831 putall(Text*, Text*, Text*, int, int, Rune*, int) 832 { 833 int i, j, e; 834 Window *w; 835 Column *c; 836 char *a; 837 838 for(i=0; i<row.ncol; i++){ 839 c = row.col[i]; 840 for(j=0; j<c->nw; j++){ 841 w = c->w[j]; 842 if(w->isscratch || w->isdir || w->body.file->nname==0) 843 continue; 844 if(w->nopen[QWevent] > 0) 845 continue; 846 a = runetobyte(w->body.file->name, w->body.file->nname); 847 e = access(a, 0); 848 if(w->body.file->mod || w->body.ncache) 849 if(e < 0) 850 warning(nil, "no auto-Put of %s: %r\n", a); 851 else{ 852 wincommit(w, &w->body); 853 put(&w->body, nil, nil, XXX, XXX, nil, 0); 854 } 855 free(a); 856 } 857 } 858 } 859 860 861 void 862 id(Text *et, Text*, Text*, int, int, Rune*, int) 863 { 864 if(et && et->w) 865 warning(nil, "/mnt/acme/%d/\n", et->w->id); 866 } 867 868 void 869 local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 870 { 871 char *a, *aa; 872 Runestr dir; 873 874 aa = getbytearg(argt, TRUE, TRUE, &a); 875 876 dir = dirname(et, nil, 0); 877 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 878 free(dir.r); 879 dir.r = nil; 880 dir.nr = 0; 881 } 882 run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE); 883 } 884 885 void 886 kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg) 887 { 888 Rune *a, *cmd, *r; 889 int na; 890 891 getarg(argt, FALSE, FALSE, &r, &na); 892 if(r) 893 kill(nil, nil, nil, 0, 0, r, na); 894 /* loop condition: *arg is not a blank */ 895 for(;;){ 896 a = findbl(arg, narg, &na); 897 if(a == arg) 898 break; 899 cmd = runemalloc(narg-na+1); 900 runemove(cmd, arg, narg-na); 901 sendp(ckill, cmd); 902 arg = skipbl(a, na, &narg); 903 } 904 } 905 906 void 907 fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg) 908 { 909 Rune *a, *r, *flag, *file; 910 int na, nf; 911 char *aa; 912 Reffont *newfont; 913 Dirlist *dp; 914 int i, fix; 915 916 if(et==nil || et->w==nil) 917 return; 918 t = &et->w->body; 919 flag = nil; 920 file = nil; 921 /* loop condition: *arg is not a blank */ 922 nf = 0; 923 for(;;){ 924 a = findbl(arg, narg, &na); 925 if(a == arg) 926 break; 927 r = runemalloc(narg-na+1); 928 runemove(r, arg, narg-na); 929 if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){ 930 free(flag); 931 flag = r; 932 }else{ 933 free(file); 934 file = r; 935 nf = narg-na; 936 } 937 arg = skipbl(a, na, &narg); 938 } 939 getarg(argt, FALSE, TRUE, &r, &na); 940 if(r) 941 if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){ 942 free(flag); 943 flag = r; 944 }else{ 945 free(file); 946 file = r; 947 nf = na; 948 } 949 fix = 1; 950 if(flag) 951 fix = runeeq(flag, runestrlen(flag), L"fix", 3); 952 else if(file == nil){ 953 newfont = rfget(FALSE, FALSE, FALSE, nil); 954 if(newfont) 955 fix = strcmp(newfont->f->name, t->font->name)==0; 956 } 957 if(file){ 958 aa = runetobyte(file, nf); 959 newfont = rfget(fix, flag!=nil, FALSE, aa); 960 free(aa); 961 }else 962 newfont = rfget(fix, FALSE, FALSE, nil); 963 if(newfont){ 964 draw(screen, t->w->r, textcols[BACK], nil, ZP); 965 rfclose(t->reffont); 966 t->reffont = newfont; 967 t->font = newfont->f; 968 frinittick(t); 969 if(t->w->isdir){ 970 t->all.min.x++; /* force recolumnation; disgusting! */ 971 for(i=0; i<t->w->ndl; i++){ 972 dp = t->w->dlp[i]; 973 aa = runetobyte(dp->r, dp->nr); 974 dp->wid = stringwidth(newfont->f, aa); 975 free(aa); 976 } 977 } 978 /* avoid shrinking of window due to quantization */ 979 colgrow(t->w->col, t->w, -1); 980 } 981 free(file); 982 free(flag); 983 } 984 985 void 986 incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 987 { 988 Rune *a, *r; 989 Window *w; 990 int na, n, len; 991 992 if(et==nil || et->w==nil) 993 return; 994 w = et->w; 995 n = 0; 996 getarg(argt, FALSE, TRUE, &r, &len); 997 if(r){ 998 n++; 999 winaddincl(w, r, len); 1000 } 1001 /* loop condition: *arg is not a blank */ 1002 for(;;){ 1003 a = findbl(arg, narg, &na); 1004 if(a == arg) 1005 break; 1006 r = runemalloc(narg-na+1); 1007 runemove(r, arg, narg-na); 1008 n++; 1009 winaddincl(w, r, narg-na); 1010 arg = skipbl(a, na, &narg); 1011 } 1012 if(n==0 && w->nincl){ 1013 for(n=w->nincl; --n>=0; ) 1014 warning(nil, "%S ", w->incl[n]); 1015 warning(nil, "\n"); 1016 } 1017 } 1018 1019 void 1020 tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1021 { 1022 Rune *a, *r; 1023 Window *w; 1024 int na, len, tab; 1025 char *p; 1026 1027 if(et==nil || et->w==nil) 1028 return; 1029 w = et->w; 1030 getarg(argt, FALSE, TRUE, &r, &len); 1031 tab = 0; 1032 if(r!=nil && len>0){ 1033 p = runetobyte(r, len); 1034 if('0'<=p[0] && p[0]<='9') 1035 tab = atoi(p); 1036 free(p); 1037 }else{ 1038 a = findbl(arg, narg, &na); 1039 if(a != arg){ 1040 p = runetobyte(arg, narg-na); 1041 if('0'<=p[0] && p[0]<='9') 1042 tab = atoi(p); 1043 free(p); 1044 } 1045 } 1046 if(tab > 0){ 1047 if(w->body.tabstop != tab){ 1048 w->body.tabstop = tab; 1049 winresize(w, w->r, 1); 1050 } 1051 }else 1052 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); 1053 } 1054 1055 void 1056 runproc(void *argvp) 1057 { 1058 /* args: */ 1059 Window *win; 1060 char *s; 1061 Rune *rdir; 1062 int ndir; 1063 int newns; 1064 char *argaddr; 1065 char *arg; 1066 Command *c; 1067 Channel *cpid; 1068 int iseditcmd; 1069 /* end of args */ 1070 char *e, *t, *name, *filename, *dir, **av, *news; 1071 Rune r, **incl; 1072 int ac, w, inarg, i, n, fd, nincl, winid; 1073 int pipechar; 1074 char buf[512]; 1075 static void *parg[2]; 1076 void **argv; 1077 1078 argv = argvp; 1079 win = argv[0]; 1080 s = argv[1]; 1081 rdir = argv[2]; 1082 ndir = (int)argv[3]; 1083 newns = (int)argv[4]; 1084 argaddr = argv[5]; 1085 arg = argv[6]; 1086 c = argv[7]; 1087 cpid = argv[8]; 1088 iseditcmd = (int)argv[9]; 1089 free(argv); 1090 1091 t = s; 1092 while(*t==' ' || *t=='\n' || *t=='\t') 1093 t++; 1094 for(e=t; *e; e++) 1095 if(*e==' ' || *e=='\n' || *e=='\t' ) 1096 break; 1097 name = emalloc((e-t)+2); 1098 memmove(name, t, e-t); 1099 name[e-t] = 0; 1100 e = utfrrune(name, '/'); 1101 if(e) 1102 strcpy(name, e+1); 1103 strcat(name, " "); /* add blank here for ease in waittask */ 1104 c->name = bytetorune(name, &c->nname); 1105 free(name); 1106 pipechar = 0; 1107 if(*t=='<' || *t=='|' || *t=='>') 1108 pipechar = *t++; 1109 c->iseditcmd = iseditcmd; 1110 c->text = s; 1111 if(rdir != nil){ 1112 dir = runetobyte(rdir, ndir); 1113 chdir(dir); /* ignore error: probably app. window */ 1114 free(dir); 1115 } 1116 if(newns){ 1117 nincl = 0; 1118 incl = nil; 1119 if(win){ 1120 filename = smprint("%.*S", win->body.file->nname, win->body.file->name); 1121 nincl = win->nincl; 1122 if(nincl > 0){ 1123 incl = emalloc(nincl*sizeof(Rune*)); 1124 for(i=0; i<nincl; i++){ 1125 n = runestrlen(win->incl[i]); 1126 incl[i] = runemalloc(n+1); 1127 runemove(incl[i], win->incl[i], n); 1128 } 1129 } 1130 winid = win->id; 1131 winclose(win); 1132 }else{ 1133 filename = nil; 1134 winid = 0; 1135 if(activewin) 1136 winid = activewin->id; 1137 } 1138 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); 1139 if(filename){ 1140 putenv("%", filename); 1141 free(filename); 1142 } 1143 c->md = fsysmount(rdir, ndir, incl, nincl); 1144 if(c->md == nil){ 1145 fprint(2, "child: can't mount /dev/cons: %r\n"); 1146 threadexits("mount"); 1147 } 1148 close(0); 1149 if(winid>0 && (pipechar=='|' || pipechar=='>')){ 1150 sprint(buf, "/mnt/acme/%d/rdsel", winid); 1151 open(buf, OREAD); 1152 }else 1153 open("/dev/null", OREAD); 1154 close(1); 1155 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ 1156 if(iseditcmd){ 1157 if(winid > 0) 1158 sprint(buf, "/mnt/acme/%d/editout", winid); 1159 else 1160 sprint(buf, "/mnt/acme/editout"); 1161 }else 1162 sprint(buf, "/mnt/acme/%d/wrsel", winid); 1163 open(buf, OWRITE); 1164 close(2); 1165 open("/dev/cons", OWRITE); 1166 }else{ 1167 open("/dev/cons", OWRITE); 1168 dup(1, 2); 1169 } 1170 }else{ 1171 if(win) 1172 winclose(win); 1173 rfork(RFFDG|RFNOTEG); 1174 fsysclose(); 1175 close(0); 1176 open("/dev/null", OREAD); 1177 close(1); 1178 open(acmeerrorfile, OWRITE); 1179 dup(1, 2); 1180 } 1181 1182 if(argaddr) 1183 putenv("acmeaddr", argaddr); 1184 if(strlen(t) > sizeof buf-10) /* may need to print into stack */ 1185 goto Hard; 1186 inarg = FALSE; 1187 for(e=t; *e; e+=w){ 1188 w = chartorune(&r, e); 1189 if(r==' ' || r=='\t') 1190 continue; 1191 if(r < ' ') 1192 goto Hard; 1193 if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r)) 1194 goto Hard; 1195 inarg = TRUE; 1196 } 1197 if(!inarg) 1198 goto Fail; 1199 1200 ac = 0; 1201 av = nil; 1202 inarg = FALSE; 1203 for(e=t; *e; e+=w){ 1204 w = chartorune(&r, e); 1205 if(r==' ' || r=='\t'){ 1206 inarg = FALSE; 1207 *e = 0; 1208 continue; 1209 } 1210 if(!inarg){ 1211 inarg = TRUE; 1212 av = realloc(av, (ac+1)*sizeof(char**)); 1213 av[ac++] = e; 1214 } 1215 } 1216 av = realloc(av, (ac+2)*sizeof(char**)); 1217 av[ac++] = arg; 1218 av[ac] = nil; 1219 c->av = av; 1220 procexec(cpid, av[0], av); 1221 e = av[0]; 1222 if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) 1223 goto Fail; 1224 if(cputype){ 1225 sprint(buf, "%s/%s", cputype, av[0]); 1226 procexec(cpid, buf, av); 1227 } 1228 sprint(buf, "/bin/%s", av[0]); 1229 procexec(cpid, buf, av); 1230 goto Fail; 1231 1232 Hard: 1233 1234 /* 1235 * ugly: set path = (. $cputype /bin) 1236 * should honor $path if unusual. 1237 */ 1238 if(cputype){ 1239 n = 0; 1240 memmove(buf+n, ".", 2); 1241 n += 2; 1242 i = strlen(cputype)+1; 1243 memmove(buf+n, cputype, i); 1244 n += i; 1245 memmove(buf+n, "/bin", 5); 1246 n += 5; 1247 fd = create("/env/path", OWRITE, 0666); 1248 write(fd, buf, n); 1249 close(fd); 1250 } 1251 1252 if(arg){ 1253 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); 1254 if(news){ 1255 sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ 1256 free(s); 1257 t = news; 1258 c->text = news; 1259 } 1260 } 1261 procexecl(cpid, "/bin/rc", "rc", "-c", t, nil); 1262 1263 Fail: 1264 /* procexec hasn't happened, so send a zero */ 1265 sendul(cpid, 0); 1266 threadexits(nil); 1267 } 1268 1269 void 1270 runwaittask(void *v) 1271 { 1272 Command *c; 1273 Channel *cpid; 1274 void **a; 1275 1276 threadsetname("runwaittask"); 1277 a = v; 1278 c = a[0]; 1279 cpid = a[1]; 1280 free(a); 1281 do 1282 c->pid = recvul(cpid); 1283 while(c->pid == ~0); 1284 free(c->av); 1285 if(c->pid != 0) /* successful exec */ 1286 sendp(ccommand, c); 1287 else{ 1288 if(c->iseditcmd) 1289 sendul(cedit, 0); 1290 free(c->name); 1291 free(c->text); 1292 free(c); 1293 } 1294 chanfree(cpid); 1295 } 1296 1297 void 1298 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) 1299 { 1300 void **arg; 1301 Command *c; 1302 Channel *cpid; 1303 1304 if(s == nil) 1305 return; 1306 1307 arg = emalloc(10*sizeof(void*)); 1308 c = emalloc(sizeof *c); 1309 cpid = chancreate(sizeof(ulong), 0); 1310 arg[0] = win; 1311 arg[1] = s; 1312 arg[2] = rdir; 1313 arg[3] = (void*)ndir; 1314 arg[4] = (void*)newns; 1315 arg[5] = argaddr; 1316 arg[6] = xarg; 1317 arg[7] = c; 1318 arg[8] = cpid; 1319 arg[9] = (void*)iseditcmd; 1320 proccreate(runproc, arg, STACK); 1321 /* mustn't block here because must be ready to answer mount() call in run() */ 1322 arg = emalloc(2*sizeof(void*)); 1323 arg[0] = c; 1324 arg[1] = cpid; 1325 threadcreate(runwaittask, arg, STACK); 1326 } 1327