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