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