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 static int indentval(Rune *s, int n){ 1024 if(n < 2) 1025 return -1; 1026 if(runestrncmp(s, L"ON", n) == 0){ 1027 globalautoindent = TRUE; 1028 warning(nil, "Indent ON\n"); 1029 return -2; 1030 } 1031 if(runestrncmp(s, L"OFF", n) == 0){ 1032 globalautoindent = FALSE; 1033 warning(nil, "Indent OFF\n"); 1034 return -2; 1035 } 1036 return runestrncmp(s, L"on", n) == 0; 1037 } 1038 1039 void 1040 indent(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1041 { 1042 Rune *a, *r; 1043 Window *w; 1044 int na, len, autoindent; 1045 char *result; 1046 1047 if(et==nil || et->w==nil) 1048 return; 1049 w = et->w; 1050 autoindent = -1; 1051 getarg(argt, FALSE, TRUE, &r, &len); 1052 if(r!=nil && len>0) 1053 autoindent = indentval(r, len); 1054 else{ 1055 a = findbl(arg, narg, &na); 1056 if(a != arg) 1057 autoindent = indentval(arg, narg-na); 1058 } 1059 if(autoindent >= 0) 1060 w->autoindent = autoindent; 1061 if(autoindent != -2){ 1062 result = "off"; 1063 if(w->autoindent) 1064 result = "on"; 1065 if(w->body.file->nname) 1066 warning(nil, "%.*S: Indent %s\n", w->body.file->nname, w->body.file->name, result); 1067 else 1068 warning(nil, "Indent %s\n", result); 1069 } 1070 } 1071 1072 void 1073 tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg) 1074 { 1075 Rune *a, *r; 1076 Window *w; 1077 int na, len, tab; 1078 char *p; 1079 1080 if(et==nil || et->w==nil) 1081 return; 1082 w = et->w; 1083 getarg(argt, FALSE, TRUE, &r, &len); 1084 tab = 0; 1085 if(r!=nil && len>0){ 1086 p = runetobyte(r, len); 1087 if('0'<=p[0] && p[0]<='9') 1088 tab = atoi(p); 1089 free(p); 1090 }else{ 1091 a = findbl(arg, narg, &na); 1092 if(a != arg){ 1093 p = runetobyte(arg, narg-na); 1094 if('0'<=p[0] && p[0]<='9') 1095 tab = atoi(p); 1096 free(p); 1097 } 1098 } 1099 if(tab > 0){ 1100 if(w->body.tabstop != tab){ 1101 w->body.tabstop = tab; 1102 winresize(w, w->r, 1); 1103 } 1104 }else 1105 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); 1106 } 1107 1108 void 1109 runproc(void *argvp) 1110 { 1111 /* args: */ 1112 Window *win; 1113 char *s; 1114 Rune *rdir; 1115 int ndir; 1116 int newns; 1117 char *argaddr; 1118 char *arg; 1119 Command *c; 1120 Channel *cpid; 1121 int iseditcmd; 1122 /* end of args */ 1123 char *e, *t, *name, *filename, *dir, **av, *news; 1124 Rune r, **incl; 1125 int ac, w, inarg, i, n, fd, nincl, winid; 1126 int pipechar; 1127 char buf[512]; 1128 static void *parg[2]; 1129 void **argv; 1130 1131 argv = argvp; 1132 win = argv[0]; 1133 s = argv[1]; 1134 rdir = argv[2]; 1135 ndir = (int)argv[3]; 1136 newns = (int)argv[4]; 1137 argaddr = argv[5]; 1138 arg = argv[6]; 1139 c = argv[7]; 1140 cpid = argv[8]; 1141 iseditcmd = (int)argv[9]; 1142 free(argv); 1143 1144 t = s; 1145 while(*t==' ' || *t=='\n' || *t=='\t') 1146 t++; 1147 for(e=t; *e; e++) 1148 if(*e==' ' || *e=='\n' || *e=='\t' ) 1149 break; 1150 name = emalloc((e-t)+2); 1151 memmove(name, t, e-t); 1152 name[e-t] = 0; 1153 e = utfrrune(name, '/'); 1154 if(e) 1155 strcpy(name, e+1); 1156 strcat(name, " "); /* add blank here for ease in waittask */ 1157 c->name = bytetorune(name, &c->nname); 1158 free(name); 1159 pipechar = 0; 1160 if(*t=='<' || *t=='|' || *t=='>') 1161 pipechar = *t++; 1162 c->iseditcmd = iseditcmd; 1163 c->text = s; 1164 if(rdir != nil){ 1165 dir = runetobyte(rdir, ndir); 1166 chdir(dir); /* ignore error: probably app. window */ 1167 free(dir); 1168 } 1169 if(newns){ 1170 nincl = 0; 1171 incl = nil; 1172 if(win){ 1173 filename = smprint("%.*S", win->body.file->nname, win->body.file->name); 1174 nincl = win->nincl; 1175 if(nincl > 0){ 1176 incl = emalloc(nincl*sizeof(Rune*)); 1177 for(i=0; i<nincl; i++){ 1178 n = runestrlen(win->incl[i]); 1179 incl[i] = runemalloc(n+1); 1180 runemove(incl[i], win->incl[i], n); 1181 } 1182 } 1183 winid = win->id; 1184 }else{ 1185 filename = nil; 1186 winid = 0; 1187 if(activewin) 1188 winid = activewin->id; 1189 } 1190 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); 1191 sprint(buf, "%d", winid); 1192 putenv("winid", buf); 1193 1194 if(filename){ 1195 putenv("%", filename); 1196 free(filename); 1197 } 1198 c->md = fsysmount(rdir, ndir, incl, nincl); 1199 if(c->md == nil){ 1200 fprint(2, "child: can't mount /dev/cons: %r\n"); 1201 threadexits("mount"); 1202 } 1203 close(0); 1204 if(winid>0 && (pipechar=='|' || pipechar=='>')){ 1205 sprint(buf, "/mnt/acme/%d/rdsel", winid); 1206 open(buf, OREAD); 1207 }else 1208 open("/dev/null", OREAD); 1209 close(1); 1210 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ 1211 if(iseditcmd){ 1212 if(winid > 0) 1213 sprint(buf, "/mnt/acme/%d/editout", winid); 1214 else 1215 sprint(buf, "/mnt/acme/editout"); 1216 }else 1217 sprint(buf, "/mnt/acme/%d/wrsel", winid); 1218 open(buf, OWRITE); 1219 close(2); 1220 open("/dev/cons", OWRITE); 1221 }else{ 1222 open("/dev/cons", OWRITE); 1223 dup(1, 2); 1224 } 1225 }else{ 1226 rfork(RFFDG|RFNOTEG); 1227 fsysclose(); 1228 close(0); 1229 open("/dev/null", OREAD); 1230 close(1); 1231 open(acmeerrorfile, OWRITE); 1232 dup(1, 2); 1233 } 1234 1235 if(win) 1236 winclose(win); 1237 1238 if(argaddr) 1239 putenv("acmeaddr", argaddr); 1240 if(strlen(t) > sizeof buf-10) /* may need to print into stack */ 1241 goto Hard; 1242 inarg = FALSE; 1243 for(e=t; *e; e+=w){ 1244 w = chartorune(&r, e); 1245 if(r==' ' || r=='\t') 1246 continue; 1247 if(r < ' ') 1248 goto Hard; 1249 if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r)) 1250 goto Hard; 1251 inarg = TRUE; 1252 } 1253 if(!inarg) 1254 goto Fail; 1255 1256 ac = 0; 1257 av = nil; 1258 inarg = FALSE; 1259 for(e=t; *e; e+=w){ 1260 w = chartorune(&r, e); 1261 if(r==' ' || r=='\t'){ 1262 inarg = FALSE; 1263 *e = 0; 1264 continue; 1265 } 1266 if(!inarg){ 1267 inarg = TRUE; 1268 av = realloc(av, (ac+1)*sizeof(char**)); 1269 av[ac++] = e; 1270 } 1271 } 1272 av = realloc(av, (ac+2)*sizeof(char**)); 1273 av[ac++] = arg; 1274 av[ac] = nil; 1275 c->av = av; 1276 procexec(cpid, av[0], av); 1277 e = av[0]; 1278 if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) 1279 goto Fail; 1280 if(cputype){ 1281 sprint(buf, "%s/%s", cputype, av[0]); 1282 procexec(cpid, buf, av); 1283 } 1284 sprint(buf, "/bin/%s", av[0]); 1285 procexec(cpid, buf, av); 1286 goto Fail; 1287 1288 Hard: 1289 1290 /* 1291 * ugly: set path = (. $cputype /bin) 1292 * should honor $path if unusual. 1293 */ 1294 if(cputype){ 1295 n = 0; 1296 memmove(buf+n, ".", 2); 1297 n += 2; 1298 i = strlen(cputype)+1; 1299 memmove(buf+n, cputype, i); 1300 n += i; 1301 memmove(buf+n, "/bin", 5); 1302 n += 5; 1303 fd = create("/env/path", OWRITE, 0666); 1304 write(fd, buf, n); 1305 close(fd); 1306 } 1307 1308 if(arg){ 1309 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); 1310 if(news){ 1311 sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ 1312 free(s); 1313 t = news; 1314 c->text = news; 1315 } 1316 } 1317 procexecl(cpid, "/bin/rc", "rc", "-c", t, nil); 1318 1319 Fail: 1320 /* procexec hasn't happened, so send a zero */ 1321 sendul(cpid, 0); 1322 threadexits(nil); 1323 } 1324 1325 void 1326 runwaittask(void *v) 1327 { 1328 Command *c; 1329 Channel *cpid; 1330 void **a; 1331 1332 threadsetname("runwaittask"); 1333 a = v; 1334 c = a[0]; 1335 cpid = a[1]; 1336 free(a); 1337 do 1338 c->pid = recvul(cpid); 1339 while(c->pid == ~0); 1340 free(c->av); 1341 if(c->pid != 0) /* successful exec */ 1342 sendp(ccommand, c); 1343 else{ 1344 if(c->iseditcmd) 1345 sendul(cedit, 0); 1346 free(c->name); 1347 free(c->text); 1348 free(c); 1349 } 1350 chanfree(cpid); 1351 } 1352 1353 void 1354 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) 1355 { 1356 void **arg; 1357 Command *c; 1358 Channel *cpid; 1359 1360 if(s == nil) 1361 return; 1362 1363 arg = emalloc(10*sizeof(void*)); 1364 c = emalloc(sizeof *c); 1365 cpid = chancreate(sizeof(ulong), 0); 1366 arg[0] = win; 1367 arg[1] = s; 1368 arg[2] = rdir; 1369 arg[3] = (void*)ndir; 1370 arg[4] = (void*)newns; 1371 arg[5] = argaddr; 1372 arg[6] = xarg; 1373 arg[7] = c; 1374 arg[8] = cpid; 1375 arg[9] = (void*)iseditcmd; 1376 proccreate(runproc, arg, STACK); 1377 /* mustn't block here because must be ready to answer mount() call in run() */ 1378 arg = emalloc(2*sizeof(void*)); 1379 arg[0] = c; 1380 arg[1] = cpid; 1381 threadcreate(runwaittask, arg, STACK); 1382 } 1383