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