exec.c (37635B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <draw.h> 5 #include <thread.h> 6 #include <cursor.h> 7 #include <mouse.h> 8 #include <keyboard.h> 9 #include <frame.h> 10 #include <fcall.h> 11 #include <plumb.h> 12 #include <libsec.h> 13 #include <9pclient.h> 14 #include "dat.h" 15 #include "fns.h" 16 17 Buffer snarfbuf; 18 19 /* 20 * These functions get called as: 21 * 22 * fn(et, t, argt, flag1, flag1, flag2, s, n); 23 * 24 * Where the arguments are: 25 * 26 * et: the Text* in which the executing event (click) occurred 27 * t: the Text* containing the current selection (Edit, Cut, Snarf, Paste) 28 * argt: the Text* containing the argument for a 2-1 click. 29 * e->flag1: from Exectab entry 30 * e->flag2: from Exectab entry 31 * s: the command line remainder (e.g., "x" if executing "Dump x") 32 * n: length of s (s is *not* NUL-terminated) 33 */ 34 35 void doabort(Text*, Text*, Text*, int, int, Rune*, int); 36 void del(Text*, Text*, Text*, int, int, Rune*, int); 37 void delcol(Text*, Text*, Text*, int, int, Rune*, int); 38 void dotfiles(Text*, Text*, Text*, int, int, Rune*, int); 39 void dump(Text*, Text*, Text*, int, int, Rune*, int); 40 void edit(Text*, Text*, Text*, int, int, Rune*, int); 41 void xexit(Text*, Text*, Text*, int, int, Rune*, int); 42 void fontx(Text*, Text*, Text*, int, int, Rune*, int); 43 void get(Text*, Text*, Text*, int, int, Rune*, int); 44 void id(Text*, Text*, Text*, int, int, Rune*, int); 45 void incl(Text*, Text*, Text*, int, int, Rune*, int); 46 void indent(Text*, Text*, Text*, int, int, Rune*, int); 47 void xkill(Text*, Text*, Text*, int, int, Rune*, int); 48 void local(Text*, Text*, Text*, int, int, Rune*, int); 49 void look(Text*, Text*, Text*, int, int, Rune*, int); 50 void newcol(Text*, Text*, Text*, int, int, Rune*, int); 51 void paste(Text*, Text*, Text*, int, int, Rune*, int); 52 void put(Text*, Text*, Text*, int, int, Rune*, int); 53 void putall(Text*, Text*, Text*, int, int, Rune*, int); 54 void sendx(Text*, Text*, Text*, int, int, Rune*, int); 55 void sort(Text*, Text*, Text*, int, int, Rune*, int); 56 void tab(Text*, Text*, Text*, int, int, Rune*, int); 57 void zeroxx(Text*, Text*, Text*, int, int, Rune*, int); 58 59 typedef struct Exectab Exectab; 60 struct Exectab 61 { 62 Rune *name; 63 void (*fn)(Text*, Text*, Text*, int, int, Rune*, int); 64 int mark; 65 int flag1; 66 int flag2; 67 }; 68 69 static Rune LAbort[] = { 'A', 'b', 'o', 'r', 't', 0 }; 70 static Rune LCut[] = { 'C', 'u', 't', 0 }; 71 static Rune LDel[] = { 'D', 'e', 'l', 0 }; 72 static Rune LDelcol[] = { 'D', 'e', 'l', 'c', 'o', 'l', 0 }; 73 static Rune LDelete[] = { 'D', 'e', 'l', 'e', 't', 'e', 0 }; 74 static Rune LDump[] = { 'D', 'u', 'm', 'p', 0 }; 75 static Rune LEdit[] = { 'E', 'd', 'i', 't', 0 }; 76 static Rune LExit[] = { 'E', 'x', 'i', 't', 0 }; 77 static Rune LFont[] = { 'F', 'o', 'n', 't', 0 }; 78 static Rune LGet[] = { 'G', 'e', 't', 0 }; 79 static Rune LID[] = { 'I', 'D', 0 }; 80 static Rune LIncl[] = { 'I', 'n', 'c', 'l', 0 }; 81 static Rune LIndent[] = { 'I', 'n', 'd', 'e', 'n', 't', 0 }; 82 static Rune LKill[] = { 'K', 'i', 'l', 'l', 0 }; 83 static Rune LLoad[] = { 'L', 'o', 'a', 'd', 0 }; 84 static Rune LLocal[] = { 'L', 'o', 'c', 'a', 'l', 0 }; 85 static Rune LLook[] = { 'L', 'o', 'o', 'k', 0 }; 86 static Rune LNew[] = { 'N', 'e', 'w', 0 }; 87 static Rune LNewcol[] = { 'N', 'e', 'w', 'c', 'o', 'l', 0 }; 88 static Rune LPaste[] = { 'P', 'a', 's', 't', 'e', 0 }; 89 static Rune LPut[] = { 'P', 'u', 't', 0 }; 90 static Rune LPutall[] = { 'P', 'u', 't', 'a', 'l', 'l', 0 }; 91 static Rune LRedo[] = { 'R', 'e', 'd', 'o', 0 }; 92 static Rune LSend[] = { 'S', 'e', 'n', 'd', 0 }; 93 static Rune LSnarf[] = { 'S', 'n', 'a', 'r', 'f', 0 }; 94 static Rune LSort[] = { 'S', 'o', 'r', 't', 0 }; 95 static Rune LTab[] = { 'T', 'a', 'b', 0 }; 96 static Rune LUndo[] = { 'U', 'n', 'd', 'o', 0 }; 97 static Rune LZerox[] = { 'Z', 'e', 'r', 'o', 'x', 0 }; 98 99 Exectab exectab[] = { 100 { LAbort, doabort, FALSE, XXX, XXX, }, 101 { LCut, cut, TRUE, TRUE, TRUE }, 102 { LDel, del, FALSE, FALSE, XXX }, 103 { LDelcol, delcol, FALSE, XXX, XXX }, 104 { LDelete, del, FALSE, TRUE, XXX }, 105 { LDump, dump, FALSE, TRUE, XXX }, 106 { LEdit, edit, FALSE, XXX, XXX }, 107 { LExit, xexit, FALSE, XXX, XXX }, 108 { LFont, fontx, FALSE, XXX, XXX }, 109 { LGet, get, FALSE, TRUE, XXX }, 110 { LID, id, FALSE, XXX, XXX }, 111 { LIncl, incl, FALSE, XXX, XXX }, 112 { LIndent, indent, FALSE, XXX, XXX }, 113 { LKill, xkill, FALSE, XXX, XXX }, 114 { LLoad, dump, FALSE, FALSE, XXX }, 115 { LLocal, local, FALSE, XXX, XXX }, 116 { LLook, look, FALSE, XXX, XXX }, 117 { LNew, new, FALSE, XXX, XXX }, 118 { LNewcol, newcol, FALSE, XXX, XXX }, 119 { LPaste, paste, TRUE, TRUE, XXX }, 120 { LPut, put, FALSE, XXX, XXX }, 121 { LPutall, putall, FALSE, XXX, XXX }, 122 { LRedo, undo, FALSE, FALSE, XXX }, 123 { LSend, sendx, TRUE, XXX, XXX }, 124 { LSnarf, cut, FALSE, TRUE, FALSE }, 125 { LSort, sort, FALSE, XXX, XXX }, 126 { LTab, tab, FALSE, XXX, XXX }, 127 { LUndo, undo, FALSE, TRUE, XXX }, 128 { LZerox, zeroxx, FALSE, XXX, XXX }, 129 { nil, 0, 0, 0, 0 } 130 }; 131 132 Exectab* 133 lookup(Rune *r, int n) 134 { 135 Exectab *e; 136 int nr; 137 138 r = skipbl(r, n, &n); 139 if(n == 0) 140 return nil; 141 findbl(r, n, &nr); 142 nr = n-nr; 143 for(e=exectab; e->name; e++) 144 if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE) 145 return e; 146 return nil; 147 } 148 149 int 150 isexecc(int c) 151 { 152 if(isfilec(c)) 153 return 1; 154 return c=='<' || c=='|' || c=='>'; 155 } 156 157 void 158 execute(Text *t, uint aq0, uint aq1, int external, Text *argt) 159 { 160 uint q0, q1; 161 Rune *r, *s; 162 char *b, *a, *aa; 163 Exectab *e; 164 int c, n, f; 165 Runestr dir; 166 167 q0 = aq0; 168 q1 = aq1; 169 if(q1 == q0){ /* expand to find word (actually file name) */ 170 /* if in selection, choose selection */ 171 if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 172 q0 = t->q0; 173 q1 = t->q1; 174 }else{ 175 while(q1<t->file->b.nc && isexecc(c=textreadc(t, q1)) && c!=':') 176 q1++; 177 while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':') 178 q0--; 179 if(q1 == q0) 180 return; 181 } 182 } 183 r = runemalloc(q1-q0); 184 bufread(&t->file->b, q0, r, q1-q0); 185 e = lookup(r, q1-q0); 186 if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ 187 f = 0; 188 if(e) 189 f |= 1; 190 if(q0!=aq0 || q1!=aq1){ 191 bufread(&t->file->b, aq0, r, aq1-aq0); 192 f |= 2; 193 } 194 aa = getbytearg(argt, TRUE, TRUE, &a); 195 if(a){ 196 if(strlen(a) > EVENTSIZE){ /* too big; too bad */ 197 free(r); 198 free(aa); 199 free(a); 200 warning(nil, "argument string too long\n"); 201 return; 202 } 203 f |= 8; 204 } 205 c = 'x'; 206 if(t->what == Body) 207 c = 'X'; 208 n = aq1-aq0; 209 if(n <= EVENTSIZE) 210 winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r); 211 else 212 winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f); 213 if(q0!=aq0 || q1!=aq1){ 214 n = q1-q0; 215 bufread(&t->file->b, q0, r, n); 216 if(n <= EVENTSIZE) 217 winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r); 218 else 219 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1); 220 } 221 if(a){ 222 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a); 223 if(aa) 224 winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa); 225 else 226 winevent(t->w, "%c0 0 0 0 \n", c); 227 } 228 free(r); 229 free(aa); 230 free(a); 231 return; 232 } 233 if(e){ 234 if(e->mark && seltext!=nil) 235 if(seltext->what == Body){ 236 seq++; 237 filemark(seltext->w->body.file); 238 } 239 s = skipbl(r, q1-q0, &n); 240 s = findbl(s, n, &n); 241 s = skipbl(s, n, &n); 242 (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n); 243 free(r); 244 return; 245 } 246 247 b = runetobyte(r, q1-q0); 248 free(r); 249 dir = dirname(t, nil, 0); 250 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 251 free(dir.r); 252 dir.r = nil; 253 dir.nr = 0; 254 } 255 aa = getbytearg(argt, TRUE, TRUE, &a); 256 if(t->w) 257 incref(&t->w->ref); 258 run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE); 259 } 260 261 char* 262 printarg(Text *argt, uint q0, uint q1) 263 { 264 char *buf; 265 266 if(argt->what!=Body || argt->file->name==nil) 267 return nil; 268 buf = emalloc(argt->file->nname+32); 269 if(q0 == q1) 270 sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0); 271 else 272 sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1); 273 return buf; 274 } 275 276 char* 277 getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp) 278 { 279 int n; 280 Expand e; 281 char *a; 282 283 memset(&e, 0, sizeof e); 284 *rp = nil; 285 *nrp = 0; 286 if(argt == nil) 287 return nil; 288 a = nil; 289 textcommit(argt, TRUE); 290 if(expand(argt, argt->q0, argt->q1, &e, FALSE)){ 291 free(e.bname); 292 if(e.nname && dofile){ 293 e.name = runerealloc(e.name, e.nname+1); 294 if(doaddr) 295 a = printarg(argt, e.q0, e.q1); 296 *rp = e.name; 297 *nrp = e.nname; 298 return a; 299 } 300 free(e.name); 301 }else{ 302 e.q0 = argt->q0; 303 e.q1 = argt->q1; 304 } 305 n = e.q1 - e.q0; 306 *rp = runemalloc(n+1); 307 bufread(&argt->file->b, e.q0, *rp, n); 308 if(doaddr) 309 a = printarg(argt, e.q0, e.q1); 310 *nrp = n; 311 return a; 312 } 313 314 char* 315 getbytearg(Text *argt, int doaddr, int dofile, char **bp) 316 { 317 Rune *r; 318 int n; 319 char *aa; 320 321 *bp = nil; 322 aa = getarg(argt, doaddr, dofile, &r, &n); 323 if(r == nil) 324 return nil; 325 *bp = runetobyte(r, n); 326 free(r); 327 return aa; 328 } 329 330 void 331 doabort(Text *__0, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 332 { 333 static int n; 334 335 USED(__0); 336 USED(_0); 337 USED(_1); 338 USED(_2); 339 USED(_3); 340 USED(_4); 341 USED(_5); 342 343 if(n++ == 0) 344 warning(nil, "executing Abort again will call abort()\n"); 345 else 346 abort(); 347 } 348 349 void 350 newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 351 { 352 Column *c; 353 Window *w; 354 355 USED(_0); 356 USED(_1); 357 USED(_2); 358 USED(_3); 359 USED(_4); 360 USED(_5); 361 362 c = rowadd(et->row, nil, -1); 363 if(c) { 364 w = coladd(c, nil, nil, -1); 365 winsettag(w); 366 xfidlog(w, "new"); 367 } 368 } 369 370 void 371 delcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 372 { 373 int i; 374 Column *c; 375 Window *w; 376 377 USED(_0); 378 USED(_1); 379 USED(_2); 380 USED(_3); 381 USED(_4); 382 USED(_5); 383 384 c = et->col; 385 if(c==nil || colclean(c)==0) 386 return; 387 for(i=0; i<c->nw; i++){ 388 w = c->w[i]; 389 if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){ 390 warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name); 391 return; 392 } 393 } 394 rowclose(et->col->row, et->col, TRUE); 395 } 396 397 void 398 del(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4) 399 { 400 USED(_0); 401 USED(_1); 402 USED(_2); 403 USED(_3); 404 USED(_4); 405 406 if(et->col==nil || et->w == nil) 407 return; 408 if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)) 409 colclose(et->col, et->w, TRUE); 410 } 411 412 void 413 sort(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 414 { 415 USED(_0); 416 USED(_1); 417 USED(_2); 418 USED(_3); 419 USED(_4); 420 USED(_5); 421 422 if(et->col) 423 colsort(et->col); 424 } 425 426 uint 427 seqof(Window *w, int isundo) 428 { 429 /* if it's undo, see who changed with us */ 430 if(isundo) 431 return w->body.file->seq; 432 /* if it's redo, see who we'll be sync'ed up with */ 433 return fileredoseq(w->body.file); 434 } 435 436 void 437 undo(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4) 438 { 439 int i, j; 440 Column *c; 441 Window *w; 442 uint seq; 443 444 USED(_0); 445 USED(_1); 446 USED(_2); 447 USED(_3); 448 USED(_4); 449 450 if(et==nil || et->w== nil) 451 return; 452 seq = seqof(et->w, flag1); 453 if(seq == 0){ 454 /* nothing to undo */ 455 return; 456 } 457 /* 458 * Undo the executing window first. Its display will update. other windows 459 * in the same file will not call show() and jump to a different location in the file. 460 * Simultaneous changes to other files will be chaotic, however. 461 */ 462 winundo(et->w, flag1); 463 for(i=0; i<row.ncol; i++){ 464 c = row.col[i]; 465 for(j=0; j<c->nw; j++){ 466 w = c->w[j]; 467 if(w == et->w) 468 continue; 469 if(seqof(w, flag1) == seq) 470 winundo(w, flag1); 471 } 472 } 473 } 474 475 char* 476 getname(Text *t, Text *argt, Rune *arg, int narg, int isput) 477 { 478 char *s; 479 Rune *r; 480 int i, n, promote; 481 Runestr dir; 482 483 getarg(argt, FALSE, TRUE, &r, &n); 484 promote = FALSE; 485 if(r == nil) 486 promote = TRUE; 487 else if(isput){ 488 /* if are doing a Put, want to synthesize name even for non-existent file */ 489 /* best guess is that file name doesn't contain a slash */ 490 promote = TRUE; 491 for(i=0; i<n; i++) 492 if(r[i] == '/'){ 493 promote = FALSE; 494 break; 495 } 496 if(promote){ 497 t = argt; 498 arg = r; 499 narg = n; 500 } 501 } 502 if(promote){ 503 n = narg; 504 if(n <= 0){ 505 s = runetobyte(t->file->name, t->file->nname); 506 return s; 507 } 508 /* prefix with directory name if necessary */ 509 dir.r = nil; 510 dir.nr = 0; 511 if(n>0 && arg[0]!='/'){ 512 dir = dirname(t, nil, 0); 513 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 514 free(dir.r); 515 dir.r = nil; 516 dir.nr = 0; 517 } 518 } 519 if(dir.r){ 520 r = runemalloc(dir.nr+n+1); 521 runemove(r, dir.r, dir.nr); 522 free(dir.r); 523 if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/') 524 r[dir.nr++] = '/'; 525 runemove(r+dir.nr, arg, n); 526 n += dir.nr; 527 }else{ 528 r = runemalloc(n+1); 529 runemove(r, arg, n); 530 } 531 } 532 s = runetobyte(r, n); 533 free(r); 534 if(strlen(s) == 0){ 535 free(s); 536 s = nil; 537 } 538 return s; 539 } 540 541 void 542 zeroxx(Text *et, Text *t, Text *_1, int _2, int _3, Rune *_4, int _5) 543 { 544 Window *nw; 545 int c, locked; 546 547 USED(_1); 548 USED(_2); 549 USED(_3); 550 USED(_4); 551 USED(_5); 552 553 locked = FALSE; 554 if(t!=nil && t->w!=nil && t->w!=et->w){ 555 locked = TRUE; 556 c = 'M'; 557 if(et->w) 558 c = et->w->owner; 559 winlock(t->w, c); 560 } 561 if(t == nil) 562 t = et; 563 if(t==nil || t->w==nil) 564 return; 565 t = &t->w->body; 566 if(t->w->isdir) 567 warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name); 568 else{ 569 nw = coladd(t->w->col, nil, t->w, -1); 570 /* ugly: fix locks so w->unlock works */ 571 winlock1(nw, t->w->owner); 572 xfidlog(nw, "zerox"); 573 } 574 if(locked) 575 winunlock(t->w); 576 } 577 578 typedef struct TextAddr TextAddr; 579 struct TextAddr { 580 long lorigin; // line+rune for origin 581 long rorigin; 582 long lq0; // line+rune for q0 583 long rq0; 584 long lq1; // line+rune for q1 585 long rq1; 586 }; 587 588 void 589 get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg) 590 { 591 char *name; 592 Rune *r; 593 int i, n, dirty, samename, isdir; 594 TextAddr *addr, *a; 595 Window *w; 596 Text *u; 597 Dir *d; 598 long q0, q1; 599 600 USED(_0); 601 602 if(flag1) 603 if(et==nil || et->w==nil) 604 return; 605 if(!et->w->isdir && (et->w->body.file->b.nc>0 && !winclean(et->w, TRUE))) 606 return; 607 w = et->w; 608 t = &w->body; 609 name = getname(t, argt, arg, narg, FALSE); 610 if(name == nil){ 611 warning(nil, "no file name\n"); 612 return; 613 } 614 if(t->file->ntext>1){ 615 d = dirstat(name); 616 isdir = (d!=nil && (d->qid.type & QTDIR)); 617 free(d); 618 if(isdir){ 619 warning(nil, "%s is a directory; can't read with multiple windows on it\n", name); 620 return; 621 } 622 } 623 addr = emalloc((t->file->ntext)*sizeof(TextAddr)); 624 for(i=0; i<t->file->ntext; i++) { 625 a = &addr[i]; 626 u = t->file->text[i]; 627 a->lorigin = nlcount(u, 0, u->org, &a->rorigin); 628 a->lq0 = nlcount(u, 0, u->q0, &a->rq0); 629 a->lq1 = nlcount(u, u->q0, u->q1, &a->rq1); 630 } 631 r = bytetorune(name, &n); 632 for(i=0; i<t->file->ntext; i++){ 633 u = t->file->text[i]; 634 /* second and subsequent calls with zero an already empty buffer, but OK */ 635 textreset(u); 636 windirfree(u->w); 637 } 638 samename = runeeq(r, n, t->file->name, t->file->nname); 639 textload(t, 0, name, samename); 640 if(samename){ 641 t->file->mod = FALSE; 642 dirty = FALSE; 643 }else{ 644 t->file->mod = TRUE; 645 dirty = TRUE; 646 } 647 for(i=0; i<t->file->ntext; i++) 648 t->file->text[i]->w->dirty = dirty; 649 free(name); 650 free(r); 651 winsettag(w); 652 t->file->unread = FALSE; 653 for(i=0; i<t->file->ntext; i++){ 654 u = t->file->text[i]; 655 textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc); 656 if(samename) { 657 a = &addr[i]; 658 // warning(nil, "%d %d %d %d %d %d\n", a->lorigin, a->rorigin, a->lq0, a->rq0, a->lq1, a->rq1); 659 q0 = nlcounttopos(u, 0, a->lq0, a->rq0); 660 q1 = nlcounttopos(u, q0, a->lq1, a->rq1); 661 textsetselect(u, q0, q1); 662 q0 = nlcounttopos(u, 0, a->lorigin, a->rorigin); 663 textsetorigin(u, q0, FALSE); 664 } 665 textscrdraw(u); 666 } 667 free(addr); 668 xfidlog(w, "get"); 669 } 670 671 static void 672 checksha1(char *name, File *f, Dir *d) 673 { 674 int fd, n; 675 DigestState *h; 676 uchar out[20]; 677 uchar *buf; 678 679 fd = open(name, OREAD); 680 if(fd < 0) 681 return; 682 h = sha1(nil, 0, nil, nil); 683 buf = emalloc(8192); 684 while((n = read(fd, buf, 8192)) > 0) 685 sha1(buf, n, nil, h); 686 free(buf); 687 close(fd); 688 sha1(nil, 0, out, h); 689 if(memcmp(out, f->sha1, sizeof out) == 0) { 690 f->dev = d->dev; 691 f->qidpath = d->qid.path; 692 f->mtime = d->mtime; 693 } 694 } 695 696 void 697 putfile(File *f, int q0, int q1, Rune *namer, int nname) 698 { 699 uint n, m; 700 Rune *r; 701 Biobuf *b; 702 char *s, *name; 703 int i, fd, q, ret, retc; 704 Dir *d, *d1; 705 Window *w; 706 int isapp; 707 DigestState *h; 708 709 w = f->curtext->w; 710 name = runetobyte(namer, nname); 711 d = dirstat(name); 712 if(d!=nil && runeeq(namer, nname, f->name, f->nname)){ 713 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime) 714 checksha1(name, f, d); 715 if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime) { 716 if(f->unread) 717 warning(nil, "%s not written; file already exists\n", name); 718 else 719 warning(nil, "%s modified%s%s since last read\n\twas %t; now %t\n", name, d->muid[0]?" by ":"", d->muid, f->mtime, d->mtime); 720 f->dev = d->dev; 721 f->qidpath = d->qid.path; 722 f->mtime = d->mtime; 723 goto Rescue1; 724 } 725 } 726 727 fd = create(name, OWRITE, 0666); 728 if(fd < 0){ 729 warning(nil, "can't create file %s: %r\n", name); 730 goto Rescue1; 731 } 732 // Use bio in order to force the writes to be large and 733 // block-aligned (bio's default is 8K). This is not strictly 734 // necessary; it works around some buggy underlying 735 // file systems that mishandle unaligned writes. 736 // https://codereview.appspot.com/89550043/ 737 b = emalloc(sizeof *b); 738 Binit(b, fd, OWRITE); 739 r = fbufalloc(); 740 s = fbufalloc(); 741 free(d); 742 d = dirfstat(fd); 743 h = sha1(nil, 0, nil, nil); 744 isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND)); 745 if(isapp){ 746 warning(nil, "%s not written; file is append only\n", name); 747 goto Rescue2; 748 } 749 750 for(q=q0; q<q1; q+=n){ 751 n = q1 - q; 752 if(n > BUFSIZE/UTFmax) 753 n = BUFSIZE/UTFmax; 754 bufread(&f->b, q, r, n); 755 m = snprint(s, BUFSIZE+1, "%.*S", n, r); 756 sha1((uchar*)s, m, nil, h); 757 if(Bwrite(b, s, m) != m){ 758 warning(nil, "can't write file %s: %r\n", name); 759 goto Rescue2; 760 } 761 } 762 if(Bflush(b) < 0) { 763 warning(nil, "can't write file %s: %r\n", name); 764 goto Rescue2; 765 } 766 ret = Bterm(b); 767 retc = close(fd); 768 free(b); 769 b = nil; 770 if(ret < 0 || retc < 0) { 771 warning(nil, "can't write file %s: %r\n", name); 772 goto Rescue2; // flush or close failed 773 } 774 if(runeeq(namer, nname, f->name, f->nname)){ 775 if(q0!=0 || q1!=f->b.nc){ 776 f->mod = TRUE; 777 w->dirty = TRUE; 778 f->unread = TRUE; 779 }else{ 780 // In case the file is on NFS, reopen the fd 781 // before dirfstat to cause the attribute cache 782 // to be updated (otherwise the mtime in the 783 // dirfstat below will be stale and not match 784 // what NFS sees). The file is already written, 785 // so this should be a no-op when not on NFS. 786 // Opening for OWRITE (but no truncation) 787 // in case we don't have read permission. 788 // (The create above worked, so we probably 789 // still have write permission.) 790 fd = open(name, OWRITE); 791 d1 = dirfstat(fd); 792 close(fd); 793 if(d1 != nil){ 794 free(d); 795 d = d1; 796 } 797 f->qidpath = d->qid.path; 798 f->dev = d->dev; 799 f->mtime = d->mtime; 800 sha1(nil, 0, f->sha1, h); 801 h = nil; 802 f->mod = FALSE; 803 w->dirty = FALSE; 804 f->unread = FALSE; 805 } 806 for(i=0; i<f->ntext; i++){ 807 f->text[i]->w->putseq = f->seq; 808 f->text[i]->w->dirty = w->dirty; 809 } 810 } 811 fbuffree(s); 812 fbuffree(r); 813 free(h); 814 free(d); 815 free(namer); 816 free(name); 817 close(fd); 818 winsettag(w); 819 return; 820 821 Rescue2: 822 if(b != nil) { 823 Bterm(b); 824 free(b); 825 close(fd); 826 } 827 free(h); 828 fbuffree(s); 829 fbuffree(r); 830 /* fall through */ 831 832 Rescue1: 833 free(d); 834 free(namer); 835 free(name); 836 } 837 838 static void 839 trimspaces(Text *et) 840 { 841 File *f; 842 Rune *r; 843 Text *t; 844 uint q0, n, delstart; 845 int c, i, marked; 846 847 t = &et->w->body; 848 f = t->file; 849 marked = 0; 850 851 if(t->w!=nil && et->w!=t->w){ 852 /* can this happen when t == &et->w->body? */ 853 c = 'M'; 854 if(et->w) 855 c = et->w->owner; 856 winlock(t->w, c); 857 } 858 859 r = fbufalloc(); 860 q0 = f->b.nc; 861 delstart = q0; /* end of current space run, or 0 if no active run; = q0 to delete spaces before EOF */ 862 while(q0 > 0) { 863 n = RBUFSIZE; 864 if(n > q0) 865 n = q0; 866 q0 -= n; 867 bufread(&f->b, q0, r, n); 868 for(i=n; ; i--) { 869 if(i == 0 || (r[i-1] != ' ' && r[i-1] != '\t')) { 870 // Found non-space or start of buffer. Delete active space run. 871 if(q0+i < delstart) { 872 if(!marked) { 873 marked = 1; 874 seq++; 875 filemark(f); 876 } 877 textdelete(t, q0+i, delstart, TRUE); 878 } 879 if(i == 0) { 880 /* keep run active into tail of next buffer */ 881 if(delstart > 0) 882 delstart = q0; 883 break; 884 } 885 delstart = 0; 886 if(r[i-1] == '\n') 887 delstart = q0+i-1; /* delete spaces before this newline */ 888 } 889 } 890 } 891 fbuffree(r); 892 893 if(t->w!=nil && et->w!=t->w) 894 winunlock(t->w); 895 } 896 897 void 898 put(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) 899 { 900 int nname; 901 Rune *namer; 902 Window *w; 903 File *f; 904 char *name; 905 906 USED(_0); 907 USED(_1); 908 USED(_2); 909 910 if(et==nil || et->w==nil || et->w->isdir) 911 return; 912 w = et->w; 913 f = w->body.file; 914 name = getname(&w->body, argt, arg, narg, TRUE); 915 if(name == nil){ 916 warning(nil, "no file name\n"); 917 return; 918 } 919 if(w->autoindent) 920 trimspaces(et); 921 namer = bytetorune(name, &nname); 922 putfile(f, 0, f->b.nc, namer, nname); 923 xfidlog(w, "put"); 924 free(name); 925 } 926 927 void 928 dump(Text *_0, Text *_1, Text *argt, int isdump, int _2, Rune *arg, int narg) 929 { 930 char *name; 931 932 USED(_0); 933 USED(_1); 934 USED(_2); 935 936 if(narg) 937 name = runetobyte(arg, narg); 938 else 939 getbytearg(argt, FALSE, TRUE, &name); 940 if(isdump) 941 rowdump(&row, name); 942 else 943 rowload(&row, name, FALSE); 944 free(name); 945 } 946 947 void 948 cut(Text *et, Text *t, Text *_0, int dosnarf, int docut, Rune *_2, int _3) 949 { 950 uint q0, q1, n, locked, c; 951 Rune *r; 952 953 USED(_0); 954 USED(_2); 955 USED(_3); 956 957 /* 958 * if not executing a mouse chord (et != t) and snarfing (dosnarf) 959 * and executed Cut or Snarf in window tag (et->w != nil), 960 * then use the window body selection or the tag selection 961 * or do nothing at all. 962 */ 963 if(et!=t && dosnarf && et->w!=nil){ 964 if(et->w->body.q1>et->w->body.q0){ 965 t = &et->w->body; 966 if(docut) 967 filemark(t->file); /* seq has been incremented by execute */ 968 }else if(et->w->tag.q1>et->w->tag.q0) 969 t = &et->w->tag; 970 else 971 t = nil; 972 } 973 if(t == nil) /* no selection */ 974 return; 975 976 locked = FALSE; 977 if(t->w!=nil && et->w!=t->w){ 978 locked = TRUE; 979 c = 'M'; 980 if(et->w) 981 c = et->w->owner; 982 winlock(t->w, c); 983 } 984 if(t->q0 == t->q1){ 985 if(locked) 986 winunlock(t->w); 987 return; 988 } 989 if(dosnarf){ 990 q0 = t->q0; 991 q1 = t->q1; 992 bufdelete(&snarfbuf, 0, snarfbuf.nc); 993 r = fbufalloc(); 994 while(q0 < q1){ 995 n = q1 - q0; 996 if(n > RBUFSIZE) 997 n = RBUFSIZE; 998 bufread(&t->file->b, q0, r, n); 999 bufinsert(&snarfbuf, snarfbuf.nc, r, n); 1000 q0 += n; 1001 } 1002 fbuffree(r); 1003 acmeputsnarf(); 1004 } 1005 if(docut){ 1006 textdelete(t, t->q0, t->q1, TRUE); 1007 textsetselect(t, t->q0, t->q0); 1008 if(t->w){ 1009 textscrdraw(t); 1010 winsettag(t->w); 1011 } 1012 }else if(dosnarf) /* Snarf command */ 1013 argtext = t; 1014 if(locked) 1015 winunlock(t->w); 1016 } 1017 1018 void 1019 paste(Text *et, Text *t, Text *_0, int selectall, int tobody, Rune *_1, int _2) 1020 { 1021 int c; 1022 uint q, q0, q1, n; 1023 Rune *r; 1024 1025 USED(_0); 1026 USED(_1); 1027 USED(_2); 1028 1029 /* if(tobody), use body of executing window (Paste or Send command) */ 1030 if(tobody && et!=nil && et->w!=nil){ 1031 t = &et->w->body; 1032 filemark(t->file); /* seq has been incremented by execute */ 1033 } 1034 if(t == nil) 1035 return; 1036 1037 acmegetsnarf(); 1038 if(t==nil || snarfbuf.nc==0) 1039 return; 1040 if(t->w!=nil && et->w!=t->w){ 1041 c = 'M'; 1042 if(et->w) 1043 c = et->w->owner; 1044 winlock(t->w, c); 1045 } 1046 cut(t, t, nil, FALSE, TRUE, nil, 0); 1047 q = 0; 1048 q0 = t->q0; 1049 q1 = t->q0+snarfbuf.nc; 1050 r = fbufalloc(); 1051 while(q0 < q1){ 1052 n = q1 - q0; 1053 if(n > RBUFSIZE) 1054 n = RBUFSIZE; 1055 if(r == nil) 1056 r = runemalloc(n); 1057 bufread(&snarfbuf, q, r, n); 1058 textinsert(t, q0, r, n, TRUE); 1059 q += n; 1060 q0 += n; 1061 } 1062 fbuffree(r); 1063 if(selectall) 1064 textsetselect(t, t->q0, q1); 1065 else 1066 textsetselect(t, q1, q1); 1067 if(t->w){ 1068 textscrdraw(t); 1069 winsettag(t->w); 1070 } 1071 if(t->w!=nil && et->w!=t->w) 1072 winunlock(t->w); 1073 } 1074 1075 void 1076 look(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg) 1077 { 1078 Rune *r; 1079 int n; 1080 1081 USED(_0); 1082 USED(_1); 1083 1084 if(et && et->w){ 1085 t = &et->w->body; 1086 if(narg > 0){ 1087 search(t, arg, narg, FALSE); 1088 return; 1089 } 1090 getarg(argt, FALSE, FALSE, &r, &n); 1091 if(r == nil){ 1092 n = t->q1-t->q0; 1093 r = runemalloc(n); 1094 bufread(&t->file->b, t->q0, r, n); 1095 } 1096 search(t, r, n, FALSE); 1097 free(r); 1098 } 1099 } 1100 1101 static Rune Lnl[] = { '\n', 0 }; 1102 1103 void 1104 sendx(Text *et, Text *t, Text *_0, int _1, int _2, Rune *_3, int _4) 1105 { 1106 USED(_0); 1107 USED(_1); 1108 USED(_2); 1109 USED(_3); 1110 USED(_4); 1111 1112 if(et->w==nil) 1113 return; 1114 t = &et->w->body; 1115 if(t->q0 != t->q1) 1116 cut(t, t, nil, TRUE, FALSE, nil, 0); 1117 textsetselect(t, t->file->b.nc, t->file->b.nc); 1118 paste(t, t, nil, TRUE, TRUE, nil, 0); 1119 if(textreadc(t, t->file->b.nc-1) != '\n'){ 1120 textinsert(t, t->file->b.nc, Lnl, 1, TRUE); 1121 textsetselect(t, t->file->b.nc, t->file->b.nc); 1122 } 1123 t->iq1 = t->q1; 1124 textshow(t, t->q1, t->q1, 1); 1125 } 1126 1127 void 1128 edit(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) 1129 { 1130 Rune *r; 1131 int len; 1132 1133 USED(_0); 1134 USED(_1); 1135 USED(_2); 1136 1137 if(et == nil) 1138 return; 1139 getarg(argt, FALSE, TRUE, &r, &len); 1140 seq++; 1141 if(r != nil){ 1142 editcmd(et, r, len); 1143 free(r); 1144 }else 1145 editcmd(et, arg, narg); 1146 } 1147 1148 void 1149 xexit(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 1150 { 1151 USED(et); 1152 USED(_0); 1153 USED(_1); 1154 USED(_2); 1155 USED(_3); 1156 USED(_4); 1157 USED(_5); 1158 1159 if(rowclean(&row)){ 1160 sendul(cexit, 0); 1161 threadexits(nil); 1162 } 1163 } 1164 1165 void 1166 putall(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 1167 { 1168 int i, j, e; 1169 Window *w; 1170 Column *c; 1171 char *a; 1172 1173 USED(et); 1174 USED(_0); 1175 USED(_1); 1176 USED(_2); 1177 USED(_3); 1178 USED(_4); 1179 USED(_5); 1180 1181 for(i=0; i<row.ncol; i++){ 1182 c = row.col[i]; 1183 for(j=0; j<c->nw; j++){ 1184 w = c->w[j]; 1185 if(w->isscratch || w->isdir || w->body.file->nname==0) 1186 continue; 1187 if(w->nopen[QWevent] > 0) 1188 continue; 1189 a = runetobyte(w->body.file->name, w->body.file->nname); 1190 e = access(a, 0); 1191 if(w->body.file->mod || w->body.ncache) 1192 if(e < 0) 1193 warning(nil, "no auto-Put of %s: %r\n", a); 1194 else{ 1195 wincommit(w, &w->body); 1196 put(&w->body, nil, nil, XXX, XXX, nil, 0); 1197 } 1198 free(a); 1199 } 1200 } 1201 } 1202 1203 1204 void 1205 id(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) 1206 { 1207 USED(_0); 1208 USED(_1); 1209 USED(_2); 1210 USED(_3); 1211 USED(_4); 1212 USED(_5); 1213 1214 if(et && et->w) 1215 warning(nil, "/mnt/acme/%d/\n", et->w->id); 1216 } 1217 1218 void 1219 local(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) 1220 { 1221 char *a, *aa; 1222 Runestr dir; 1223 1224 USED(_0); 1225 USED(_1); 1226 USED(_2); 1227 1228 aa = getbytearg(argt, TRUE, TRUE, &a); 1229 1230 dir = dirname(et, nil, 0); 1231 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ 1232 free(dir.r); 1233 dir.r = nil; 1234 dir.nr = 0; 1235 } 1236 run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE); 1237 } 1238 1239 void 1240 xkill(Text *_0, Text *_1, Text *argt, int _2, int _3, Rune *arg, int narg) 1241 { 1242 Rune *a, *cmd, *r; 1243 int na; 1244 1245 USED(_0); 1246 USED(_1); 1247 USED(_2); 1248 USED(_3); 1249 1250 getarg(argt, FALSE, FALSE, &r, &na); 1251 if(r) 1252 xkill(nil, nil, nil, 0, 0, r, na); 1253 /* loop condition: *arg is not a blank */ 1254 for(;;){ 1255 a = findbl(arg, narg, &na); 1256 if(a == arg) 1257 break; 1258 cmd = runemalloc(narg-na+1); 1259 runemove(cmd, arg, narg-na); 1260 sendp(ckill, cmd); 1261 arg = skipbl(a, na, &narg); 1262 } 1263 } 1264 1265 static Rune Lfix[] = { 'f', 'i', 'x', 0 }; 1266 static Rune Lvar[] = { 'v', 'a', 'r', 0 }; 1267 1268 void 1269 fontx(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg) 1270 { 1271 Rune *a, *r, *flag, *file; 1272 int na, nf; 1273 char *aa; 1274 Reffont *newfont; 1275 Dirlist *dp; 1276 int i, fix; 1277 1278 USED(_0); 1279 USED(_1); 1280 1281 if(et==nil || et->w==nil) 1282 return; 1283 t = &et->w->body; 1284 flag = nil; 1285 file = nil; 1286 /* loop condition: *arg is not a blank */ 1287 nf = 0; 1288 for(;;){ 1289 a = findbl(arg, narg, &na); 1290 if(a == arg) 1291 break; 1292 r = runemalloc(narg-na+1); 1293 runemove(r, arg, narg-na); 1294 if(runeeq(r, narg-na, Lfix, 3) || runeeq(r, narg-na, Lvar, 3)){ 1295 free(flag); 1296 flag = r; 1297 }else{ 1298 free(file); 1299 file = r; 1300 nf = narg-na; 1301 } 1302 arg = skipbl(a, na, &narg); 1303 } 1304 getarg(argt, FALSE, TRUE, &r, &na); 1305 if(r) 1306 if(runeeq(r, na, Lfix, 3) || runeeq(r, na, Lvar, 3)){ 1307 free(flag); 1308 flag = r; 1309 }else{ 1310 free(file); 1311 file = r; 1312 nf = na; 1313 } 1314 fix = 1; 1315 if(flag) 1316 fix = runeeq(flag, runestrlen(flag), Lfix, 3); 1317 else if(file == nil){ 1318 newfont = rfget(FALSE, FALSE, FALSE, nil); 1319 if(newfont) 1320 fix = strcmp(newfont->f->name, t->fr.font->name)==0; 1321 } 1322 if(file){ 1323 aa = runetobyte(file, nf); 1324 newfont = rfget(fix, flag!=nil, FALSE, aa); 1325 free(aa); 1326 }else 1327 newfont = rfget(fix, FALSE, FALSE, nil); 1328 if(newfont){ 1329 draw(screen, t->w->r, textcols[BACK], nil, ZP); 1330 rfclose(t->reffont); 1331 t->reffont = newfont; 1332 t->fr.font = newfont->f; 1333 frinittick(&t->fr); 1334 if(t->w->isdir){ 1335 t->all.min.x++; /* force recolumnation; disgusting! */ 1336 for(i=0; i<t->w->ndl; i++){ 1337 dp = t->w->dlp[i]; 1338 aa = runetobyte(dp->r, dp->nr); 1339 dp->wid = stringwidth(newfont->f, aa); 1340 free(aa); 1341 } 1342 } 1343 /* avoid shrinking of window due to quantization */ 1344 colgrow(t->w->col, t->w, -1); 1345 } 1346 free(file); 1347 free(flag); 1348 } 1349 1350 void 1351 incl(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) 1352 { 1353 Rune *a, *r; 1354 Window *w; 1355 int na, n, len; 1356 1357 USED(_0); 1358 USED(_1); 1359 USED(_2); 1360 1361 if(et==nil || et->w==nil) 1362 return; 1363 w = et->w; 1364 n = 0; 1365 getarg(argt, FALSE, TRUE, &r, &len); 1366 if(r){ 1367 n++; 1368 winaddincl(w, r, len); 1369 } 1370 /* loop condition: *arg is not a blank */ 1371 for(;;){ 1372 a = findbl(arg, narg, &na); 1373 if(a == arg) 1374 break; 1375 r = runemalloc(narg-na+1); 1376 runemove(r, arg, narg-na); 1377 n++; 1378 winaddincl(w, r, narg-na); 1379 arg = skipbl(a, na, &narg); 1380 } 1381 if(n==0 && w->nincl){ 1382 for(n=w->nincl; --n>=0; ) 1383 warning(nil, "%S ", w->incl[n]); 1384 warning(nil, "\n"); 1385 } 1386 } 1387 1388 static Rune LON[] = { 'O', 'N', 0 }; 1389 static Rune LOFF[] = { 'O', 'F', 'F', 0 }; 1390 static Rune Lon[] = { 'o', 'n', 0 }; 1391 1392 enum { 1393 IGlobal = -2, 1394 IError = -1, 1395 Ion = 0, 1396 Ioff = 1 1397 }; 1398 1399 static int 1400 indentval(Rune *s, int n) 1401 { 1402 if(n < 2) 1403 return IError; 1404 if(runestrncmp(s, LON, n) == 0){ 1405 globalautoindent = TRUE; 1406 warning(nil, "Indent ON\n"); 1407 return IGlobal; 1408 } 1409 if(runestrncmp(s, LOFF, n) == 0){ 1410 globalautoindent = FALSE; 1411 warning(nil, "Indent OFF\n"); 1412 return IGlobal; 1413 } 1414 return runestrncmp(s, Lon, n) == 0; 1415 } 1416 1417 static void 1418 fixindent(Window *w, void *arg) 1419 { 1420 USED(arg); 1421 w->autoindent = globalautoindent; 1422 } 1423 1424 void 1425 indent(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) 1426 { 1427 Rune *a, *r; 1428 Window *w; 1429 int na, len, autoindent; 1430 1431 USED(_0); 1432 USED(_1); 1433 USED(_2); 1434 1435 w = nil; 1436 if(et!=nil && et->w!=nil) 1437 w = et->w; 1438 autoindent = IError; 1439 getarg(argt, FALSE, TRUE, &r, &len); 1440 if(r!=nil && len>0) 1441 autoindent = indentval(r, len); 1442 else{ 1443 a = findbl(arg, narg, &na); 1444 if(a != arg) 1445 autoindent = indentval(arg, narg-na); 1446 } 1447 if(autoindent == IGlobal) 1448 allwindows(fixindent, nil); 1449 else if(w != nil && autoindent >= 0) 1450 w->autoindent = autoindent; 1451 } 1452 1453 void 1454 tab(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) 1455 { 1456 Rune *a, *r; 1457 Window *w; 1458 int na, len, tab; 1459 char *p; 1460 1461 USED(_0); 1462 USED(_1); 1463 USED(_2); 1464 1465 if(et==nil || et->w==nil) 1466 return; 1467 w = et->w; 1468 getarg(argt, FALSE, TRUE, &r, &len); 1469 tab = 0; 1470 if(r!=nil && len>0){ 1471 p = runetobyte(r, len); 1472 if('0'<=p[0] && p[0]<='9') 1473 tab = atoi(p); 1474 free(p); 1475 }else{ 1476 a = findbl(arg, narg, &na); 1477 if(a != arg){ 1478 p = runetobyte(arg, narg-na); 1479 if('0'<=p[0] && p[0]<='9') 1480 tab = atoi(p); 1481 free(p); 1482 } 1483 } 1484 if(tab > 0){ 1485 if(w->body.tabstop != tab){ 1486 w->body.tabstop = tab; 1487 winresize(w, w->r, FALSE, TRUE); 1488 } 1489 }else 1490 warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); 1491 } 1492 1493 void 1494 runproc(void *argvp) 1495 { 1496 /* args: */ 1497 Window *win; 1498 char *s; 1499 Rune *rdir; 1500 int ndir; 1501 int newns; 1502 char *argaddr; 1503 char *arg; 1504 Command *c; 1505 Channel *cpid; 1506 int iseditcmd; 1507 /* end of args */ 1508 char *e, *t, *name, *filename, *dir, **av, *news; 1509 Rune r, **incl; 1510 int ac, w, inarg, i, n, fd, nincl, winid; 1511 int sfd[3]; 1512 int pipechar; 1513 char buf[512]; 1514 int ret; 1515 /*static void *parg[2]; */ 1516 char *rcarg[4]; 1517 void **argv; 1518 CFsys *fs; 1519 char *shell; 1520 1521 threadsetname("runproc"); 1522 1523 argv = argvp; 1524 win = argv[0]; 1525 s = argv[1]; 1526 rdir = argv[2]; 1527 ndir = (uintptr)argv[3]; 1528 newns = (uintptr)argv[4]; 1529 argaddr = argv[5]; 1530 arg = argv[6]; 1531 c = argv[7]; 1532 cpid = argv[8]; 1533 iseditcmd = (uintptr)argv[9]; 1534 free(argv); 1535 1536 unsetenv("acmeaddr"); 1537 unsetenv("winid"); 1538 unsetenv("%"); 1539 unsetenv("samfile"); 1540 1541 t = s; 1542 while(*t==' ' || *t=='\n' || *t=='\t') 1543 t++; 1544 for(e=t; *e; e++) 1545 if(*e==' ' || *e=='\n' || *e=='\t' ) 1546 break; 1547 name = emalloc((e-t)+2); 1548 memmove(name, t, e-t); 1549 name[e-t] = 0; 1550 e = utfrrune(name, '/'); 1551 if(e) 1552 memmove(name, e+1, strlen(e+1)+1); /* strcpy but overlaps */ 1553 strcat(name, " "); /* add blank here for ease in waittask */ 1554 c->name = bytetorune(name, &c->nname); 1555 free(name); 1556 pipechar = 0; 1557 if(*t=='<' || *t=='|' || *t=='>') 1558 pipechar = *t++; 1559 c->iseditcmd = iseditcmd; 1560 c->text = s; 1561 if(newns){ 1562 nincl = 0; 1563 incl = nil; 1564 if(win){ 1565 filename = smprint("%.*S", win->body.file->nname, win->body.file->name); 1566 nincl = win->nincl; 1567 if(nincl > 0){ 1568 incl = emalloc(nincl*sizeof(Rune*)); 1569 for(i=0; i<nincl; i++){ 1570 n = runestrlen(win->incl[i]); 1571 incl[i] = runemalloc(n+1); 1572 runemove(incl[i], win->incl[i], n); 1573 } 1574 } 1575 winid = win->id; 1576 }else{ 1577 filename = nil; 1578 winid = 0; 1579 if(activewin) 1580 winid = activewin->id; 1581 } 1582 rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); 1583 sprint(buf, "%d", winid); 1584 putenv("winid", buf); 1585 1586 if(filename){ 1587 putenv("%", filename); 1588 putenv("samfile", filename); 1589 free(filename); 1590 } 1591 c->md = fsysmount(rdir, ndir, incl, nincl); 1592 if(c->md == nil){ 1593 fprint(2, "child: can't allocate mntdir: %r\n"); 1594 threadexits("fsysmount"); 1595 } 1596 sprint(buf, "%d", c->md->id); 1597 if((fs = nsmount("acme", buf)) == nil){ 1598 fprint(2, "child: can't mount acme: %r\n"); 1599 fsysdelid(c->md); 1600 c->md = nil; 1601 threadexits("nsmount"); 1602 } 1603 if(winid>0 && (pipechar=='|' || pipechar=='>')){ 1604 sprint(buf, "%d/rdsel", winid); 1605 sfd[0] = fsopenfd(fs, buf, OREAD); 1606 }else 1607 sfd[0] = open("/dev/null", OREAD); 1608 if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ 1609 if(iseditcmd){ 1610 if(winid > 0) 1611 sprint(buf, "%d/editout", winid); 1612 else 1613 sprint(buf, "editout"); 1614 }else 1615 sprint(buf, "%d/wrsel", winid); 1616 sfd[1] = fsopenfd(fs, buf, OWRITE); 1617 sfd[2] = fsopenfd(fs, "cons", OWRITE); 1618 }else{ 1619 sfd[1] = fsopenfd(fs, "cons", OWRITE); 1620 sfd[2] = sfd[1]; 1621 } 1622 fsunmount(fs); 1623 }else{ 1624 rfork(RFFDG|RFNOTEG); 1625 fsysclose(); 1626 sfd[0] = open("/dev/null", OREAD); 1627 sfd[1] = open("/dev/null", OWRITE); 1628 sfd[2] = dup(erroutfd, -1); 1629 } 1630 if(win) 1631 winclose(win); 1632 1633 if(argaddr) 1634 putenv("acmeaddr", argaddr); 1635 if(acmeshell != nil) 1636 goto Hard; 1637 if(strlen(t) > sizeof buf-10) /* may need to print into stack */ 1638 goto Hard; 1639 inarg = FALSE; 1640 for(e=t; *e; e+=w){ 1641 w = chartorune(&r, e); 1642 if(r==' ' || r=='\t') 1643 continue; 1644 if(r < ' ') 1645 goto Hard; 1646 if(utfrune("#;&|^$=`'{}()<>[]*?^~`/", r)) 1647 goto Hard; 1648 inarg = TRUE; 1649 } 1650 if(!inarg) 1651 goto Fail; 1652 1653 ac = 0; 1654 av = nil; 1655 inarg = FALSE; 1656 for(e=t; *e; e+=w){ 1657 w = chartorune(&r, e); 1658 if(r==' ' || r=='\t'){ 1659 inarg = FALSE; 1660 *e = 0; 1661 continue; 1662 } 1663 if(!inarg){ 1664 inarg = TRUE; 1665 av = realloc(av, (ac+1)*sizeof(char**)); 1666 av[ac++] = e; 1667 } 1668 } 1669 av = realloc(av, (ac+2)*sizeof(char**)); 1670 av[ac++] = arg; 1671 av[ac] = nil; 1672 c->av = av; 1673 1674 dir = nil; 1675 if(rdir != nil) 1676 dir = runetobyte(rdir, ndir); 1677 ret = threadspawnd(sfd, av[0], av, dir); 1678 free(dir); 1679 if(ret >= 0){ 1680 if(cpid) 1681 sendul(cpid, ret); 1682 threadexits(""); 1683 } 1684 /* libthread uses execvp so no need to do this */ 1685 #if 0 1686 e = av[0]; 1687 if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) 1688 goto Fail; 1689 if(cputype){ 1690 sprint(buf, "%s/%s", cputype, av[0]); 1691 procexec(cpid, sfd, buf, av); 1692 } 1693 sprint(buf, "/bin/%s", av[0]); 1694 procexec(cpid, sfd, buf, av); 1695 #endif 1696 goto Fail; 1697 1698 Hard: 1699 /* 1700 * ugly: set path = (. $cputype /bin) 1701 * should honor $path if unusual. 1702 */ 1703 if(cputype){ 1704 n = 0; 1705 memmove(buf+n, ".", 2); 1706 n += 2; 1707 i = strlen(cputype)+1; 1708 memmove(buf+n, cputype, i); 1709 n += i; 1710 memmove(buf+n, "/bin", 5); 1711 n += 5; 1712 fd = create("/env/path", OWRITE, 0666); 1713 write(fd, buf, n); 1714 close(fd); 1715 } 1716 1717 if(arg){ 1718 news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); 1719 if(news){ 1720 sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ 1721 free(s); 1722 t = news; 1723 c->text = news; 1724 } 1725 } 1726 dir = nil; 1727 if(rdir != nil) 1728 dir = runetobyte(rdir, ndir); 1729 shell = acmeshell; 1730 if(shell == nil) 1731 shell = "rc"; 1732 rcarg[0] = shell; 1733 rcarg[1] = "-c"; 1734 rcarg[2] = t; 1735 rcarg[3] = nil; 1736 ret = threadspawnd(sfd, rcarg[0], rcarg, dir); 1737 unsetenv("acmeaddr"); 1738 unsetenv("winid"); 1739 unsetenv("%"); 1740 unsetenv("samfile"); 1741 free(dir); 1742 if(ret >= 0){ 1743 if(cpid) 1744 sendul(cpid, ret); 1745 threadexits(nil); 1746 } 1747 warning(nil, "exec %s: %r\n", shell); 1748 1749 Fail: 1750 /* threadexec hasn't happened, so send a zero */ 1751 close(sfd[0]); 1752 close(sfd[1]); 1753 if(sfd[2] != sfd[1]) 1754 close(sfd[2]); 1755 sendul(cpid, 0); 1756 threadexits(nil); 1757 } 1758 1759 void 1760 runwaittask(void *v) 1761 { 1762 Command *c; 1763 Channel *cpid; 1764 void **a; 1765 1766 threadsetname("runwaittask"); 1767 a = v; 1768 c = a[0]; 1769 cpid = a[1]; 1770 free(a); 1771 do 1772 c->pid = recvul(cpid); 1773 while(c->pid == ~0); 1774 free(c->av); 1775 if(c->pid != 0) /* successful exec */ 1776 sendp(ccommand, c); 1777 else{ 1778 if(c->iseditcmd) 1779 sendul(cedit, 0); 1780 free(c->name); 1781 free(c->text); 1782 free(c); 1783 } 1784 chanfree(cpid); 1785 } 1786 1787 void 1788 run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) 1789 { 1790 void **arg; 1791 Command *c; 1792 Channel *cpid; 1793 1794 if(s == nil) 1795 return; 1796 1797 arg = emalloc(10*sizeof(void*)); 1798 c = emalloc(sizeof *c); 1799 cpid = chancreate(sizeof(ulong), 0); 1800 chansetname(cpid, "cpid %s", s); 1801 arg[0] = win; 1802 arg[1] = s; 1803 arg[2] = rdir; 1804 arg[3] = (void*)(uintptr)ndir; 1805 arg[4] = (void*)(uintptr)newns; 1806 arg[5] = argaddr; 1807 arg[6] = xarg; 1808 arg[7] = c; 1809 arg[8] = cpid; 1810 arg[9] = (void*)(uintptr)iseditcmd; 1811 threadcreate(runproc, arg, STACK); 1812 /* mustn't block here because must be ready to answer mount() call in run() */ 1813 arg = emalloc(2*sizeof(void*)); 1814 arg[0] = c; 1815 arg[1] = cpid; 1816 threadcreate(runwaittask, arg, STACK); 1817 }