| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <draw.h> |
| #include <thread.h> |
| #include <cursor.h> |
| #include <mouse.h> |
| #include <keyboard.h> |
| #include <frame.h> |
| #include <fcall.h> |
| #include <plumb.h> |
| #include <9pclient.h> |
| #include "dat.h" |
| #include "fns.h" |
| |
| Buffer snarfbuf; |
| |
| /* |
| * These functions get called as: |
| * |
| * fn(et, t, argt, flag1, flag1, flag2, s, n); |
| * |
| * Where the arguments are: |
| * |
| * et: the Text* in which the executing event (click) occurred |
| * t: the Text* containing the current selection (Edit, Cut, Snarf, Paste) |
| * argt: the Text* containing the argument for a 2-1 click. |
| * e->flag1: from Exectab entry |
| * e->flag2: from Exectab entry |
| * s: the command line remainder (e.g., "x" if executing "Dump x") |
| * n: length of s (s is *not* NUL-terminated) |
| */ |
| |
| void doabort(Text*, Text*, Text*, int, int, Rune*, int); |
| void del(Text*, Text*, Text*, int, int, Rune*, int); |
| void delcol(Text*, Text*, Text*, int, int, Rune*, int); |
| void dotfiles(Text*, Text*, Text*, int, int, Rune*, int); |
| void dump(Text*, Text*, Text*, int, int, Rune*, int); |
| void edit(Text*, Text*, Text*, int, int, Rune*, int); |
| void xexit(Text*, Text*, Text*, int, int, Rune*, int); |
| void fontx(Text*, Text*, Text*, int, int, Rune*, int); |
| void get(Text*, Text*, Text*, int, int, Rune*, int); |
| void id(Text*, Text*, Text*, int, int, Rune*, int); |
| void incl(Text*, Text*, Text*, int, int, Rune*, int); |
| void indent(Text*, Text*, Text*, int, int, Rune*, int); |
| void xkill(Text*, Text*, Text*, int, int, Rune*, int); |
| void local(Text*, Text*, Text*, int, int, Rune*, int); |
| void look(Text*, Text*, Text*, int, int, Rune*, int); |
| void newcol(Text*, Text*, Text*, int, int, Rune*, int); |
| void paste(Text*, Text*, Text*, int, int, Rune*, int); |
| void put(Text*, Text*, Text*, int, int, Rune*, int); |
| void putall(Text*, Text*, Text*, int, int, Rune*, int); |
| void sendx(Text*, Text*, Text*, int, int, Rune*, int); |
| void sort(Text*, Text*, Text*, int, int, Rune*, int); |
| void tab(Text*, Text*, Text*, int, int, Rune*, int); |
| void zeroxx(Text*, Text*, Text*, int, int, Rune*, int); |
| |
| typedef struct Exectab Exectab; |
| struct Exectab |
| { |
| Rune *name; |
| void (*fn)(Text*, Text*, Text*, int, int, Rune*, int); |
| int mark; |
| int flag1; |
| int flag2; |
| }; |
| |
| static Rune LAbort[] = { 'A', 'b', 'o', 'r', 't', 0 }; |
| static Rune LCut[] = { 'C', 'u', 't', 0 }; |
| static Rune LDel[] = { 'D', 'e', 'l', 0 }; |
| static Rune LDelcol[] = { 'D', 'e', 'l', 'c', 'o', 'l', 0 }; |
| static Rune LDelete[] = { 'D', 'e', 'l', 'e', 't', 'e', 0 }; |
| static Rune LDump[] = { 'D', 'u', 'm', 'p', 0 }; |
| static Rune LEdit[] = { 'E', 'd', 'i', 't', 0 }; |
| static Rune LExit[] = { 'E', 'x', 'i', 't', 0 }; |
| static Rune LFont[] = { 'F', 'o', 'n', 't', 0 }; |
| static Rune LGet[] = { 'G', 'e', 't', 0 }; |
| static Rune LID[] = { 'I', 'D', 0 }; |
| static Rune LIncl[] = { 'I', 'n', 'c', 'l', 0 }; |
| static Rune LIndent[] = { 'I', 'n', 'd', 'e', 'n', 't', 0 }; |
| static Rune LKill[] = { 'K', 'i', 'l', 'l', 0 }; |
| static Rune LLoad[] = { 'L', 'o', 'a', 'd', 0 }; |
| static Rune LLocal[] = { 'L', 'o', 'c', 'a', 'l', 0 }; |
| static Rune LLook[] = { 'L', 'o', 'o', 'k', 0 }; |
| static Rune LNew[] = { 'N', 'e', 'w', 0 }; |
| static Rune LNewcol[] = { 'N', 'e', 'w', 'c', 'o', 'l', 0 }; |
| static Rune LPaste[] = { 'P', 'a', 's', 't', 'e', 0 }; |
| static Rune LPut[] = { 'P', 'u', 't', 0 }; |
| static Rune LPutall[] = { 'P', 'u', 't', 'a', 'l', 'l', 0 }; |
| static Rune LRedo[] = { 'R', 'e', 'd', 'o', 0 }; |
| static Rune LSend[] = { 'S', 'e', 'n', 'd', 0 }; |
| static Rune LSnarf[] = { 'S', 'n', 'a', 'r', 'f', 0 }; |
| static Rune LSort[] = { 'S', 'o', 'r', 't', 0 }; |
| static Rune LTab[] = { 'T', 'a', 'b', 0 }; |
| static Rune LUndo[] = { 'U', 'n', 'd', 'o', 0 }; |
| static Rune LZerox[] = { 'Z', 'e', 'r', 'o', 'x', 0 }; |
| |
| Exectab exectab[] = { |
| { LAbort, doabort, FALSE, XXX, XXX, }, |
| { LCut, cut, TRUE, TRUE, TRUE }, |
| { LDel, del, FALSE, FALSE, XXX }, |
| { LDelcol, delcol, FALSE, XXX, XXX }, |
| { LDelete, del, FALSE, TRUE, XXX }, |
| { LDump, dump, FALSE, TRUE, XXX }, |
| { LEdit, edit, FALSE, XXX, XXX }, |
| { LExit, xexit, FALSE, XXX, XXX }, |
| { LFont, fontx, FALSE, XXX, XXX }, |
| { LGet, get, FALSE, TRUE, XXX }, |
| { LID, id, FALSE, XXX, XXX }, |
| { LIncl, incl, FALSE, XXX, XXX }, |
| { LIndent, indent, FALSE, XXX, XXX }, |
| { LKill, xkill, FALSE, XXX, XXX }, |
| { LLoad, dump, FALSE, FALSE, XXX }, |
| { LLocal, local, FALSE, XXX, XXX }, |
| { LLook, look, FALSE, XXX, XXX }, |
| { LNew, new, FALSE, XXX, XXX }, |
| { LNewcol, newcol, FALSE, XXX, XXX }, |
| { LPaste, paste, TRUE, TRUE, XXX }, |
| { LPut, put, FALSE, XXX, XXX }, |
| { LPutall, putall, FALSE, XXX, XXX }, |
| { LRedo, undo, FALSE, FALSE, XXX }, |
| { LSend, sendx, TRUE, XXX, XXX }, |
| { LSnarf, cut, FALSE, TRUE, FALSE }, |
| { LSort, sort, FALSE, XXX, XXX }, |
| { LTab, tab, FALSE, XXX, XXX }, |
| { LUndo, undo, FALSE, TRUE, XXX }, |
| { LZerox, zeroxx, FALSE, XXX, XXX }, |
| { nil, 0, 0, 0, 0 } |
| }; |
| |
| Exectab* |
| lookup(Rune *r, int n) |
| { |
| Exectab *e; |
| int nr; |
| |
| r = skipbl(r, n, &n); |
| if(n == 0) |
| return nil; |
| findbl(r, n, &nr); |
| nr = n-nr; |
| for(e=exectab; e->name; e++) |
| if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE) |
| return e; |
| return nil; |
| } |
| |
| int |
| isexecc(int c) |
| { |
| if(isfilec(c)) |
| return 1; |
| return c=='<' || c=='|' || c=='>'; |
| } |
| |
| void |
| execute(Text *t, uint aq0, uint aq1, int external, Text *argt) |
| { |
| uint q0, q1; |
| Rune *r, *s; |
| char *b, *a, *aa; |
| Exectab *e; |
| int c, n, f; |
| Runestr dir; |
| |
| q0 = aq0; |
| q1 = aq1; |
| if(q1 == q0){ /* expand to find word (actually file name) */ |
| /* if in selection, choose selection */ |
| if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ |
| q0 = t->q0; |
| q1 = t->q1; |
| }else{ |
| while(q1<t->file->b.nc && isexecc(c=textreadc(t, q1)) && c!=':') |
| q1++; |
| while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':') |
| q0--; |
| if(q1 == q0) |
| return; |
| } |
| } |
| r = runemalloc(q1-q0); |
| bufread(&t->file->b, q0, r, q1-q0); |
| e = lookup(r, q1-q0); |
| if(!external && t->w!=nil && t->w->nopen[QWevent]>0){ |
| f = 0; |
| if(e) |
| f |= 1; |
| if(q0!=aq0 || q1!=aq1){ |
| bufread(&t->file->b, aq0, r, aq1-aq0); |
| f |= 2; |
| } |
| aa = getbytearg(argt, TRUE, TRUE, &a); |
| if(a){ |
| if(strlen(a) > EVENTSIZE){ /* too big; too bad */ |
| free(aa); |
| free(a); |
| warning(nil, "argument string too long\n"); |
| return; |
| } |
| f |= 8; |
| } |
| c = 'x'; |
| if(t->what == Body) |
| c = 'X'; |
| n = aq1-aq0; |
| if(n <= EVENTSIZE) |
| winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r); |
| else |
| winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n); |
| if(q0!=aq0 || q1!=aq1){ |
| n = q1-q0; |
| bufread(&t->file->b, q0, r, n); |
| if(n <= EVENTSIZE) |
| winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r); |
| else |
| winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n); |
| } |
| if(a){ |
| winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a); |
| if(aa) |
| winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa); |
| else |
| winevent(t->w, "%c0 0 0 0 \n", c); |
| } |
| free(r); |
| free(aa); |
| free(a); |
| return; |
| } |
| if(e){ |
| if(e->mark && seltext!=nil) |
| if(seltext->what == Body){ |
| seq++; |
| filemark(seltext->w->body.file); |
| } |
| s = skipbl(r, q1-q0, &n); |
| s = findbl(s, n, &n); |
| s = skipbl(s, n, &n); |
| (*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n); |
| free(r); |
| return; |
| } |
| |
| b = runetobyte(r, q1-q0); |
| free(r); |
| dir = dirname(t, nil, 0); |
| if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ |
| free(dir.r); |
| dir.r = nil; |
| dir.nr = 0; |
| } |
| aa = getbytearg(argt, TRUE, TRUE, &a); |
| if(t->w) |
| incref(&t->w->ref); |
| run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE); |
| } |
| |
| char* |
| printarg(Text *argt, uint q0, uint q1) |
| { |
| char *buf; |
| |
| if(argt->what!=Body || argt->file->name==nil) |
| return nil; |
| buf = emalloc(argt->file->nname+32); |
| if(q0 == q1) |
| sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0); |
| else |
| sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1); |
| return buf; |
| } |
| |
| char* |
| getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp) |
| { |
| int n; |
| Expand e; |
| char *a; |
| |
| *rp = nil; |
| *nrp = 0; |
| if(argt == nil) |
| return nil; |
| a = nil; |
| textcommit(argt, TRUE); |
| if(expand(argt, argt->q0, argt->q1, &e)){ |
| free(e.bname); |
| if(e.nname && dofile){ |
| e.name = runerealloc(e.name, e.nname+1); |
| if(doaddr) |
| a = printarg(argt, e.q0, e.q1); |
| *rp = e.name; |
| *nrp = e.nname; |
| return a; |
| } |
| free(e.name); |
| }else{ |
| e.q0 = argt->q0; |
| e.q1 = argt->q1; |
| } |
| n = e.q1 - e.q0; |
| *rp = runemalloc(n+1); |
| bufread(&argt->file->b, e.q0, *rp, n); |
| if(doaddr) |
| a = printarg(argt, e.q0, e.q1); |
| *nrp = n; |
| return a; |
| } |
| |
| char* |
| getbytearg(Text *argt, int doaddr, int dofile, char **bp) |
| { |
| Rune *r; |
| int n; |
| char *aa; |
| |
| *bp = nil; |
| aa = getarg(argt, doaddr, dofile, &r, &n); |
| if(r == nil) |
| return nil; |
| *bp = runetobyte(r, n); |
| free(r); |
| return aa; |
| } |
| |
| void |
| doabort(Text *__0, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| static int n; |
| |
| USED(__0); |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| if(n++ == 0) |
| warning(nil, "executing Abort again will call abort()\n"); |
| else |
| abort(); |
| } |
| |
| void |
| newcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| Column *c; |
| Window *w; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| c = rowadd(et->row, nil, -1); |
| if(c) { |
| w = coladd(c, nil, nil, -1); |
| winsettag(w); |
| xfidlog(w, "new"); |
| } |
| } |
| |
| void |
| delcol(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| int i; |
| Column *c; |
| Window *w; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| c = et->col; |
| if(c==nil || colclean(c)==0) |
| return; |
| for(i=0; i<c->nw; i++){ |
| w = c->w[i]; |
| if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){ |
| warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name); |
| return; |
| } |
| } |
| rowclose(et->col->row, et->col, TRUE); |
| } |
| |
| void |
| del(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4) |
| { |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| |
| if(et->col==nil || et->w == nil) |
| return; |
| if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)) |
| colclose(et->col, et->w, TRUE); |
| } |
| |
| void |
| sort(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| if(et->col) |
| colsort(et->col); |
| } |
| |
| uint |
| seqof(Window *w, int isundo) |
| { |
| /* if it's undo, see who changed with us */ |
| if(isundo) |
| return w->body.file->seq; |
| /* if it's redo, see who we'll be sync'ed up with */ |
| return fileredoseq(w->body.file); |
| } |
| |
| void |
| undo(Text *et, Text *_0, Text *_1, int flag1, int _2, Rune *_3, int _4) |
| { |
| int i, j; |
| Column *c; |
| Window *w; |
| uint seq; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| |
| if(et==nil || et->w== nil) |
| return; |
| seq = seqof(et->w, flag1); |
| if(seq == 0){ |
| /* nothing to undo */ |
| return; |
| } |
| /* |
| * Undo the executing window first. Its display will update. other windows |
| * in the same file will not call show() and jump to a different location in the file. |
| * Simultaneous changes to other files will be chaotic, however. |
| */ |
| winundo(et->w, flag1); |
| for(i=0; i<row.ncol; i++){ |
| c = row.col[i]; |
| for(j=0; j<c->nw; j++){ |
| w = c->w[j]; |
| if(w == et->w) |
| continue; |
| if(seqof(w, flag1) == seq) |
| winundo(w, flag1); |
| } |
| } |
| } |
| |
| char* |
| getname(Text *t, Text *argt, Rune *arg, int narg, int isput) |
| { |
| char *s; |
| Rune *r; |
| int i, n, promote; |
| Runestr dir; |
| |
| getarg(argt, FALSE, TRUE, &r, &n); |
| promote = FALSE; |
| if(r == nil) |
| promote = TRUE; |
| else if(isput){ |
| /* if are doing a Put, want to synthesize name even for non-existent file */ |
| /* best guess is that file name doesn't contain a slash */ |
| promote = TRUE; |
| for(i=0; i<n; i++) |
| if(r[i] == '/'){ |
| promote = FALSE; |
| break; |
| } |
| if(promote){ |
| t = argt; |
| arg = r; |
| narg = n; |
| } |
| } |
| if(promote){ |
| n = narg; |
| if(n <= 0){ |
| s = runetobyte(t->file->name, t->file->nname); |
| return s; |
| } |
| /* prefix with directory name if necessary */ |
| dir.r = nil; |
| dir.nr = 0; |
| if(n>0 && arg[0]!='/'){ |
| dir = dirname(t, nil, 0); |
| if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ |
| free(dir.r); |
| dir.r = nil; |
| dir.nr = 0; |
| } |
| } |
| if(dir.r){ |
| r = runemalloc(dir.nr+n+1); |
| runemove(r, dir.r, dir.nr); |
| free(dir.r); |
| if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/') |
| r[dir.nr++] = '/'; |
| runemove(r+dir.nr, arg, n); |
| n += dir.nr; |
| }else{ |
| r = runemalloc(n+1); |
| runemove(r, arg, n); |
| } |
| } |
| s = runetobyte(r, n); |
| free(r); |
| if(strlen(s) == 0){ |
| free(s); |
| s = nil; |
| } |
| return s; |
| } |
| |
| void |
| zeroxx(Text *et, Text *t, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| Window *nw; |
| int c, locked; |
| |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| locked = FALSE; |
| if(t!=nil && t->w!=nil && t->w!=et->w){ |
| locked = TRUE; |
| c = 'M'; |
| if(et->w) |
| c = et->w->owner; |
| winlock(t->w, c); |
| } |
| if(t == nil) |
| t = et; |
| if(t==nil || t->w==nil) |
| return; |
| t = &t->w->body; |
| if(t->w->isdir) |
| warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name); |
| else{ |
| nw = coladd(t->w->col, nil, t->w, -1); |
| /* ugly: fix locks so w->unlock works */ |
| winlock1(nw, t->w->owner); |
| xfidlog(nw, "zerox"); |
| } |
| if(locked) |
| winunlock(t->w); |
| } |
| |
| void |
| get(Text *et, Text *t, Text *argt, int flag1, int _0, Rune *arg, int narg) |
| { |
| char *name; |
| Rune *r; |
| int i, n, dirty, samename, isdir; |
| Window *w; |
| Text *u; |
| Dir *d; |
| |
| USED(_0); |
| |
| if(flag1) |
| if(et==nil || et->w==nil) |
| return; |
| if(!et->w->isdir && (et->w->body.file->b.nc>0 && !winclean(et->w, TRUE))) |
| return; |
| w = et->w; |
| t = &w->body; |
| name = getname(t, argt, arg, narg, FALSE); |
| if(name == nil){ |
| warning(nil, "no file name\n"); |
| return; |
| } |
| if(t->file->ntext>1){ |
| d = dirstat(name); |
| isdir = (d!=nil && (d->qid.type & QTDIR)); |
| free(d); |
| if(isdir){ |
| warning(nil, "%s is a directory; can't read with multiple windows on it\n", name); |
| return; |
| } |
| } |
| r = bytetorune(name, &n); |
| for(i=0; i<t->file->ntext; i++){ |
| u = t->file->text[i]; |
| /* second and subsequent calls with zero an already empty buffer, but OK */ |
| textreset(u); |
| windirfree(u->w); |
| } |
| samename = runeeq(r, n, t->file->name, t->file->nname); |
| textload(t, 0, name, samename); |
| if(samename){ |
| t->file->mod = FALSE; |
| dirty = FALSE; |
| }else{ |
| t->file->mod = TRUE; |
| dirty = TRUE; |
| } |
| for(i=0; i<t->file->ntext; i++) |
| t->file->text[i]->w->dirty = dirty; |
| free(name); |
| free(r); |
| winsettag(w); |
| t->file->unread = FALSE; |
| for(i=0; i<t->file->ntext; i++){ |
| u = t->file->text[i]; |
| textsetselect(&u->w->tag, u->w->tag.file->b.nc, u->w->tag.file->b.nc); |
| textscrdraw(u); |
| } |
| xfidlog(w, "get"); |
| } |
| |
| void |
| putfile(File *f, int q0, int q1, Rune *namer, int nname) |
| { |
| uint n, m; |
| Rune *r; |
| Biobuf *b; |
| char *s, *name; |
| int i, fd, q; |
| Dir *d, *d1; |
| Window *w; |
| int isapp; |
| |
| w = f->curtext->w; |
| name = runetobyte(namer, nname); |
| d = dirstat(name); |
| if(d!=nil && runeeq(namer, nname, f->name, f->nname)){ |
| /* f->mtime+1 because when talking over NFS it's often off by a second */ |
| if(f->dev!=d->dev || f->qidpath!=d->qid.path || abs(f->mtime-d->mtime) > 1){ |
| if(f->unread) |
| warning(nil, "%s not written; file already exists\n", name); |
| else |
| 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); |
| f->dev = d->dev; |
| f->qidpath = d->qid.path; |
| f->mtime = d->mtime; |
| goto Rescue1; |
| } |
| } |
| fd = create(name, OWRITE, 0666); |
| if(fd < 0){ |
| warning(nil, "can't create file %s: %r\n", name); |
| goto Rescue1; |
| } |
| // Use bio in order to force the writes to be large and |
| // block-aligned (bio's default is 8K). This is not strictly |
| // necessary; it works around some buggy underlying |
| // file systems that mishandle unaligned writes. |
| // https://codereview.appspot.com/89550043/ |
| b = emalloc(sizeof *b); |
| Binit(b, fd, OWRITE); |
| r = fbufalloc(); |
| s = fbufalloc(); |
| free(d); |
| d = dirfstat(fd); |
| isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND)); |
| if(isapp){ |
| warning(nil, "%s not written; file is append only\n", name); |
| goto Rescue2; |
| } |
| |
| for(q=q0; q<q1; q+=n){ |
| n = q1 - q; |
| if(n > BUFSIZE/UTFmax) |
| n = BUFSIZE/UTFmax; |
| bufread(&f->b, q, r, n); |
| m = snprint(s, BUFSIZE+1, "%.*S", n, r); |
| if(Bwrite(b, s, m) != m){ |
| warning(nil, "can't write file %s: %r\n", name); |
| goto Rescue2; |
| } |
| } |
| if(Bflush(b) < 0) { |
| warning(nil, "can't write file %s: %r\n", name); |
| goto Rescue2; |
| } |
| Bterm(b); |
| free(b); |
| b = nil; |
| if(runeeq(namer, nname, f->name, f->nname)){ |
| if(q0!=0 || q1!=f->b.nc){ |
| f->mod = TRUE; |
| w->dirty = TRUE; |
| f->unread = TRUE; |
| }else{ |
| // In case the file is on NFS, reopen the fd |
| // before dirfstat to cause the attribute cache |
| // to be updated (otherwise the mtime in the |
| // dirfstat below will be stale and not match |
| // what NFS sees). The file is already written, |
| // so this should be a no-op when not on NFS. |
| // Opening for OWRITE (but no truncation) |
| // in case we don't have read permission. |
| // (The create above worked, so we probably |
| // still have write permission.) |
| close(fd); |
| fd = open(name, OWRITE); |
| |
| d1 = dirfstat(fd); |
| if(d1 != nil){ |
| free(d); |
| d = d1; |
| } |
| f->qidpath = d->qid.path; |
| f->dev = d->dev; |
| f->mtime = d->mtime; |
| f->mod = FALSE; |
| w->dirty = FALSE; |
| f->unread = FALSE; |
| } |
| for(i=0; i<f->ntext; i++){ |
| f->text[i]->w->putseq = f->seq; |
| f->text[i]->w->dirty = w->dirty; |
| } |
| } |
| fbuffree(s); |
| fbuffree(r); |
| free(d); |
| free(namer); |
| free(name); |
| close(fd); |
| winsettag(w); |
| return; |
| |
| Rescue2: |
| if(b != nil) { |
| Bterm(b); |
| free(b); |
| } |
| fbuffree(s); |
| fbuffree(r); |
| close(fd); |
| /* fall through */ |
| |
| Rescue1: |
| free(d); |
| free(namer); |
| free(name); |
| } |
| |
| void |
| put(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) |
| { |
| int nname; |
| Rune *namer; |
| Window *w; |
| File *f; |
| char *name; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| if(et==nil || et->w==nil || et->w->isdir) |
| return; |
| w = et->w; |
| f = w->body.file; |
| name = getname(&w->body, argt, arg, narg, TRUE); |
| if(name == nil){ |
| warning(nil, "no file name\n"); |
| return; |
| } |
| namer = bytetorune(name, &nname); |
| putfile(f, 0, f->b.nc, namer, nname); |
| xfidlog(w, "put"); |
| free(name); |
| } |
| |
| void |
| dump(Text *_0, Text *_1, Text *argt, int isdump, int _2, Rune *arg, int narg) |
| { |
| char *name; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| if(narg) |
| name = runetobyte(arg, narg); |
| else |
| getbytearg(argt, FALSE, TRUE, &name); |
| if(isdump) |
| rowdump(&row, name); |
| else |
| rowload(&row, name, FALSE); |
| free(name); |
| } |
| |
| void |
| cut(Text *et, Text *t, Text *_0, int dosnarf, int docut, Rune *_2, int _3) |
| { |
| uint q0, q1, n, locked, c; |
| Rune *r; |
| |
| USED(_0); |
| USED(_2); |
| USED(_3); |
| |
| /* |
| * if not executing a mouse chord (et != t) and snarfing (dosnarf) |
| * and executed Cut or Snarf in window tag (et->w != nil), |
| * then use the window body selection or the tag selection |
| * or do nothing at all. |
| */ |
| if(et!=t && dosnarf && et->w!=nil){ |
| if(et->w->body.q1>et->w->body.q0){ |
| t = &et->w->body; |
| if(docut) |
| filemark(t->file); /* seq has been incremented by execute */ |
| }else if(et->w->tag.q1>et->w->tag.q0) |
| t = &et->w->tag; |
| else |
| t = nil; |
| } |
| if(t == nil) /* no selection */ |
| return; |
| |
| locked = FALSE; |
| if(t->w!=nil && et->w!=t->w){ |
| locked = TRUE; |
| c = 'M'; |
| if(et->w) |
| c = et->w->owner; |
| winlock(t->w, c); |
| } |
| if(t->q0 == t->q1){ |
| if(locked) |
| winunlock(t->w); |
| return; |
| } |
| if(dosnarf){ |
| q0 = t->q0; |
| q1 = t->q1; |
| bufdelete(&snarfbuf, 0, snarfbuf.nc); |
| r = fbufalloc(); |
| while(q0 < q1){ |
| n = q1 - q0; |
| if(n > RBUFSIZE) |
| n = RBUFSIZE; |
| bufread(&t->file->b, q0, r, n); |
| bufinsert(&snarfbuf, snarfbuf.nc, r, n); |
| q0 += n; |
| } |
| fbuffree(r); |
| acmeputsnarf(); |
| } |
| if(docut){ |
| textdelete(t, t->q0, t->q1, TRUE); |
| textsetselect(t, t->q0, t->q0); |
| if(t->w){ |
| textscrdraw(t); |
| winsettag(t->w); |
| } |
| }else if(dosnarf) /* Snarf command */ |
| argtext = t; |
| if(locked) |
| winunlock(t->w); |
| } |
| |
| void |
| paste(Text *et, Text *t, Text *_0, int selectall, int tobody, Rune *_1, int _2) |
| { |
| int c; |
| uint q, q0, q1, n; |
| Rune *r; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| /* if(tobody), use body of executing window (Paste or Send command) */ |
| if(tobody && et!=nil && et->w!=nil){ |
| t = &et->w->body; |
| filemark(t->file); /* seq has been incremented by execute */ |
| } |
| if(t == nil) |
| return; |
| |
| acmegetsnarf(); |
| if(t==nil || snarfbuf.nc==0) |
| return; |
| if(t->w!=nil && et->w!=t->w){ |
| c = 'M'; |
| if(et->w) |
| c = et->w->owner; |
| winlock(t->w, c); |
| } |
| cut(t, t, nil, FALSE, TRUE, nil, 0); |
| q = 0; |
| q0 = t->q0; |
| q1 = t->q0+snarfbuf.nc; |
| r = fbufalloc(); |
| while(q0 < q1){ |
| n = q1 - q0; |
| if(n > RBUFSIZE) |
| n = RBUFSIZE; |
| if(r == nil) |
| r = runemalloc(n); |
| bufread(&snarfbuf, q, r, n); |
| textinsert(t, q0, r, n, TRUE); |
| q += n; |
| q0 += n; |
| } |
| fbuffree(r); |
| if(selectall) |
| textsetselect(t, t->q0, q1); |
| else |
| textsetselect(t, q1, q1); |
| if(t->w){ |
| textscrdraw(t); |
| winsettag(t->w); |
| } |
| if(t->w!=nil && et->w!=t->w) |
| winunlock(t->w); |
| } |
| |
| void |
| look(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg) |
| { |
| Rune *r; |
| int n; |
| |
| USED(_0); |
| USED(_1); |
| |
| if(et && et->w){ |
| t = &et->w->body; |
| if(narg > 0){ |
| search(t, arg, narg); |
| return; |
| } |
| getarg(argt, FALSE, FALSE, &r, &n); |
| if(r == nil){ |
| n = t->q1-t->q0; |
| r = runemalloc(n); |
| bufread(&t->file->b, t->q0, r, n); |
| } |
| search(t, r, n); |
| free(r); |
| } |
| } |
| |
| static Rune Lnl[] = { '\n', 0 }; |
| |
| void |
| sendx(Text *et, Text *t, Text *_0, int _1, int _2, Rune *_3, int _4) |
| { |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| |
| if(et->w==nil) |
| return; |
| t = &et->w->body; |
| if(t->q0 != t->q1) |
| cut(t, t, nil, TRUE, FALSE, nil, 0); |
| textsetselect(t, t->file->b.nc, t->file->b.nc); |
| paste(t, t, nil, TRUE, TRUE, nil, 0); |
| if(textreadc(t, t->file->b.nc-1) != '\n'){ |
| textinsert(t, t->file->b.nc, Lnl, 1, TRUE); |
| textsetselect(t, t->file->b.nc, t->file->b.nc); |
| } |
| t->iq1 = t->q1; |
| textshow(t, t->q1, t->q1, 1); |
| } |
| |
| void |
| edit(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) |
| { |
| Rune *r; |
| int len; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| if(et == nil) |
| return; |
| getarg(argt, FALSE, TRUE, &r, &len); |
| seq++; |
| if(r != nil){ |
| editcmd(et, r, len); |
| free(r); |
| }else |
| editcmd(et, arg, narg); |
| } |
| |
| void |
| xexit(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| USED(et); |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| if(rowclean(&row)){ |
| sendul(cexit, 0); |
| threadexits(nil); |
| } |
| } |
| |
| void |
| putall(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| int i, j, e; |
| Window *w; |
| Column *c; |
| char *a; |
| |
| USED(et); |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| for(i=0; i<row.ncol; i++){ |
| c = row.col[i]; |
| for(j=0; j<c->nw; j++){ |
| w = c->w[j]; |
| if(w->isscratch || w->isdir || w->body.file->nname==0) |
| continue; |
| if(w->nopen[QWevent] > 0) |
| continue; |
| a = runetobyte(w->body.file->name, w->body.file->nname); |
| e = access(a, 0); |
| if(w->body.file->mod || w->body.ncache) |
| if(e < 0) |
| warning(nil, "no auto-Put of %s: %r\n", a); |
| else{ |
| wincommit(w, &w->body); |
| put(&w->body, nil, nil, XXX, XXX, nil, 0); |
| } |
| free(a); |
| } |
| } |
| } |
| |
| |
| void |
| id(Text *et, Text *_0, Text *_1, int _2, int _3, Rune *_4, int _5) |
| { |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| USED(_4); |
| USED(_5); |
| |
| if(et && et->w) |
| warning(nil, "/mnt/acme/%d/\n", et->w->id); |
| } |
| |
| void |
| local(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) |
| { |
| char *a, *aa; |
| Runestr dir; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| aa = getbytearg(argt, TRUE, TRUE, &a); |
| |
| dir = dirname(et, nil, 0); |
| if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */ |
| free(dir.r); |
| dir.r = nil; |
| dir.nr = 0; |
| } |
| run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE); |
| } |
| |
| void |
| xkill(Text *_0, Text *_1, Text *argt, int _2, int _3, Rune *arg, int narg) |
| { |
| Rune *a, *cmd, *r; |
| int na; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| USED(_3); |
| |
| getarg(argt, FALSE, FALSE, &r, &na); |
| if(r) |
| xkill(nil, nil, nil, 0, 0, r, na); |
| /* loop condition: *arg is not a blank */ |
| for(;;){ |
| a = findbl(arg, narg, &na); |
| if(a == arg) |
| break; |
| cmd = runemalloc(narg-na+1); |
| runemove(cmd, arg, narg-na); |
| sendp(ckill, cmd); |
| arg = skipbl(a, na, &narg); |
| } |
| } |
| |
| static Rune Lfix[] = { 'f', 'i', 'x', 0 }; |
| static Rune Lvar[] = { 'v', 'a', 'r', 0 }; |
| |
| void |
| fontx(Text *et, Text *t, Text *argt, int _0, int _1, Rune *arg, int narg) |
| { |
| Rune *a, *r, *flag, *file; |
| int na, nf; |
| char *aa; |
| Reffont *newfont; |
| Dirlist *dp; |
| int i, fix; |
| |
| USED(_0); |
| USED(_1); |
| |
| if(et==nil || et->w==nil) |
| return; |
| t = &et->w->body; |
| flag = nil; |
| file = nil; |
| /* loop condition: *arg is not a blank */ |
| nf = 0; |
| for(;;){ |
| a = findbl(arg, narg, &na); |
| if(a == arg) |
| break; |
| r = runemalloc(narg-na+1); |
| runemove(r, arg, narg-na); |
| if(runeeq(r, narg-na, Lfix, 3) || runeeq(r, narg-na, Lvar, 3)){ |
| free(flag); |
| flag = r; |
| }else{ |
| free(file); |
| file = r; |
| nf = narg-na; |
| } |
| arg = skipbl(a, na, &narg); |
| } |
| getarg(argt, FALSE, TRUE, &r, &na); |
| if(r) |
| if(runeeq(r, na, Lfix, 3) || runeeq(r, na, Lvar, 3)){ |
| free(flag); |
| flag = r; |
| }else{ |
| free(file); |
| file = r; |
| nf = na; |
| } |
| fix = 1; |
| if(flag) |
| fix = runeeq(flag, runestrlen(flag), Lfix, 3); |
| else if(file == nil){ |
| newfont = rfget(FALSE, FALSE, FALSE, nil); |
| if(newfont) |
| fix = strcmp(newfont->f->name, t->fr.font->name)==0; |
| } |
| if(file){ |
| aa = runetobyte(file, nf); |
| newfont = rfget(fix, flag!=nil, FALSE, aa); |
| free(aa); |
| }else |
| newfont = rfget(fix, FALSE, FALSE, nil); |
| if(newfont){ |
| draw(screen, t->w->r, textcols[BACK], nil, ZP); |
| rfclose(t->reffont); |
| t->reffont = newfont; |
| t->fr.font = newfont->f; |
| frinittick(&t->fr); |
| if(t->w->isdir){ |
| t->all.min.x++; /* force recolumnation; disgusting! */ |
| for(i=0; i<t->w->ndl; i++){ |
| dp = t->w->dlp[i]; |
| aa = runetobyte(dp->r, dp->nr); |
| dp->wid = stringwidth(newfont->f, aa); |
| free(aa); |
| } |
| } |
| /* avoid shrinking of window due to quantization */ |
| colgrow(t->w->col, t->w, -1); |
| } |
| free(file); |
| free(flag); |
| } |
| |
| void |
| incl(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) |
| { |
| Rune *a, *r; |
| Window *w; |
| int na, n, len; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| if(et==nil || et->w==nil) |
| return; |
| w = et->w; |
| n = 0; |
| getarg(argt, FALSE, TRUE, &r, &len); |
| if(r){ |
| n++; |
| winaddincl(w, r, len); |
| } |
| /* loop condition: *arg is not a blank */ |
| for(;;){ |
| a = findbl(arg, narg, &na); |
| if(a == arg) |
| break; |
| r = runemalloc(narg-na+1); |
| runemove(r, arg, narg-na); |
| n++; |
| winaddincl(w, r, narg-na); |
| arg = skipbl(a, na, &narg); |
| } |
| if(n==0 && w->nincl){ |
| for(n=w->nincl; --n>=0; ) |
| warning(nil, "%S ", w->incl[n]); |
| warning(nil, "\n"); |
| } |
| } |
| |
| static Rune LON[] = { 'O', 'N', 0 }; |
| static Rune LOFF[] = { 'O', 'F', 'F', 0 }; |
| static Rune Lon[] = { 'o', 'n', 0 }; |
| |
| enum { |
| IGlobal = -2, |
| IError = -1, |
| Ion = 0, |
| Ioff = 1 |
| }; |
| |
| static int |
| indentval(Rune *s, int n) |
| { |
| if(n < 2) |
| return IError; |
| if(runestrncmp(s, LON, n) == 0){ |
| globalautoindent = TRUE; |
| warning(nil, "Indent ON\n"); |
| return IGlobal; |
| } |
| if(runestrncmp(s, LOFF, n) == 0){ |
| globalautoindent = FALSE; |
| warning(nil, "Indent OFF\n"); |
| return IGlobal; |
| } |
| return runestrncmp(s, Lon, n) == 0; |
| } |
| |
| static void |
| fixindent(Window *w, void *arg) |
| { |
| USED(arg); |
| w->autoindent = globalautoindent; |
| } |
| |
| void |
| indent(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) |
| { |
| Rune *a, *r; |
| Window *w; |
| int na, len, autoindent; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| w = nil; |
| if(et!=nil && et->w!=nil) |
| w = et->w; |
| autoindent = IError; |
| getarg(argt, FALSE, TRUE, &r, &len); |
| if(r!=nil && len>0) |
| autoindent = indentval(r, len); |
| else{ |
| a = findbl(arg, narg, &na); |
| if(a != arg) |
| autoindent = indentval(arg, narg-na); |
| } |
| if(autoindent == IGlobal) |
| allwindows(fixindent, nil); |
| else if(w != nil && autoindent >= 0) |
| w->autoindent = autoindent; |
| } |
| |
| void |
| tab(Text *et, Text *_0, Text *argt, int _1, int _2, Rune *arg, int narg) |
| { |
| Rune *a, *r; |
| Window *w; |
| int na, len, tab; |
| char *p; |
| |
| USED(_0); |
| USED(_1); |
| USED(_2); |
| |
| if(et==nil || et->w==nil) |
| return; |
| w = et->w; |
| getarg(argt, FALSE, TRUE, &r, &len); |
| tab = 0; |
| if(r!=nil && len>0){ |
| p = runetobyte(r, len); |
| if('0'<=p[0] && p[0]<='9') |
| tab = atoi(p); |
| free(p); |
| }else{ |
| a = findbl(arg, narg, &na); |
| if(a != arg){ |
| p = runetobyte(arg, narg-na); |
| if('0'<=p[0] && p[0]<='9') |
| tab = atoi(p); |
| free(p); |
| } |
| } |
| if(tab > 0){ |
| if(w->body.tabstop != tab){ |
| w->body.tabstop = tab; |
| winresize(w, w->r, FALSE, TRUE); |
| } |
| }else |
| warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop); |
| } |
| |
| void |
| runproc(void *argvp) |
| { |
| /* args: */ |
| Window *win; |
| char *s; |
| Rune *rdir; |
| int ndir; |
| int newns; |
| char *argaddr; |
| char *arg; |
| Command *c; |
| Channel *cpid; |
| int iseditcmd; |
| /* end of args */ |
| char *e, *t, *name, *filename, *dir, **av, *news; |
| Rune r, **incl; |
| int ac, w, inarg, i, n, fd, nincl, winid; |
| int sfd[3]; |
| int pipechar; |
| char buf[512]; |
| int ret; |
| /*static void *parg[2]; */ |
| char *rcarg[4]; |
| void **argv; |
| CFsys *fs; |
| char *shell; |
| |
| threadsetname("runproc"); |
| |
| argv = argvp; |
| win = argv[0]; |
| s = argv[1]; |
| rdir = argv[2]; |
| ndir = (uintptr)argv[3]; |
| newns = (uintptr)argv[4]; |
| argaddr = argv[5]; |
| arg = argv[6]; |
| c = argv[7]; |
| cpid = argv[8]; |
| iseditcmd = (uintptr)argv[9]; |
| free(argv); |
| |
| t = s; |
| while(*t==' ' || *t=='\n' || *t=='\t') |
| t++; |
| for(e=t; *e; e++) |
| if(*e==' ' || *e=='\n' || *e=='\t' ) |
| break; |
| name = emalloc((e-t)+2); |
| memmove(name, t, e-t); |
| name[e-t] = 0; |
| e = utfrrune(name, '/'); |
| if(e) |
| memmove(name, e+1, strlen(e+1)+1); /* strcpy but overlaps */ |
| strcat(name, " "); /* add blank here for ease in waittask */ |
| c->name = bytetorune(name, &c->nname); |
| free(name); |
| pipechar = 0; |
| if(*t=='<' || *t=='|' || *t=='>') |
| pipechar = *t++; |
| c->iseditcmd = iseditcmd; |
| c->text = s; |
| if(newns){ |
| nincl = 0; |
| incl = nil; |
| if(win){ |
| filename = smprint("%.*S", win->body.file->nname, win->body.file->name); |
| nincl = win->nincl; |
| if(nincl > 0){ |
| incl = emalloc(nincl*sizeof(Rune*)); |
| for(i=0; i<nincl; i++){ |
| n = runestrlen(win->incl[i]); |
| incl[i] = runemalloc(n+1); |
| runemove(incl[i], win->incl[i], n); |
| } |
| } |
| winid = win->id; |
| }else{ |
| filename = nil; |
| winid = 0; |
| if(activewin) |
| winid = activewin->id; |
| } |
| rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG); |
| sprint(buf, "%d", winid); |
| putenv("winid", buf); |
| |
| if(filename){ |
| putenv("%", filename); |
| putenv("samfile", filename); |
| free(filename); |
| } |
| c->md = fsysmount(rdir, ndir, incl, nincl); |
| if(c->md == nil){ |
| fprint(2, "child: can't allocate mntdir: %r\n"); |
| threadexits("fsysmount"); |
| } |
| sprint(buf, "%d", c->md->id); |
| if((fs = nsmount("acme", buf)) == nil){ |
| fprint(2, "child: can't mount acme: %r\n"); |
| fsysdelid(c->md); |
| c->md = nil; |
| threadexits("nsmount"); |
| } |
| if(winid>0 && (pipechar=='|' || pipechar=='>')){ |
| sprint(buf, "%d/rdsel", winid); |
| sfd[0] = fsopenfd(fs, buf, OREAD); |
| }else |
| sfd[0] = open("/dev/null", OREAD); |
| if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){ |
| if(iseditcmd){ |
| if(winid > 0) |
| sprint(buf, "%d/editout", winid); |
| else |
| sprint(buf, "editout"); |
| }else |
| sprint(buf, "%d/wrsel", winid); |
| sfd[1] = fsopenfd(fs, buf, OWRITE); |
| sfd[2] = fsopenfd(fs, "cons", OWRITE); |
| }else{ |
| sfd[1] = fsopenfd(fs, "cons", OWRITE); |
| sfd[2] = sfd[1]; |
| } |
| fsunmount(fs); |
| }else{ |
| rfork(RFFDG|RFNOTEG); |
| fsysclose(); |
| sfd[0] = open("/dev/null", OREAD); |
| sfd[1] = open("/dev/null", OWRITE); |
| sfd[2] = dup(erroutfd, -1); |
| } |
| if(win) |
| winclose(win); |
| |
| if(argaddr) |
| putenv("acmeaddr", argaddr); |
| if(acmeshell != nil) |
| goto Hard; |
| if(strlen(t) > sizeof buf-10) /* may need to print into stack */ |
| goto Hard; |
| inarg = FALSE; |
| for(e=t; *e; e+=w){ |
| w = chartorune(&r, e); |
| if(r==' ' || r=='\t') |
| continue; |
| if(r < ' ') |
| goto Hard; |
| if(utfrune("#;&|^$=`'{}()<>[]*?^~`/", r)) |
| goto Hard; |
| inarg = TRUE; |
| } |
| if(!inarg) |
| goto Fail; |
| |
| ac = 0; |
| av = nil; |
| inarg = FALSE; |
| for(e=t; *e; e+=w){ |
| w = chartorune(&r, e); |
| if(r==' ' || r=='\t'){ |
| inarg = FALSE; |
| *e = 0; |
| continue; |
| } |
| if(!inarg){ |
| inarg = TRUE; |
| av = realloc(av, (ac+1)*sizeof(char**)); |
| av[ac++] = e; |
| } |
| } |
| av = realloc(av, (ac+2)*sizeof(char**)); |
| av[ac++] = arg; |
| av[ac] = nil; |
| c->av = av; |
| |
| dir = nil; |
| if(rdir != nil) |
| dir = runetobyte(rdir, ndir); |
| ret = threadspawnd(sfd, av[0], av, dir); |
| free(dir); |
| if(ret >= 0){ |
| if(cpid) |
| sendul(cpid, ret); |
| threadexits(""); |
| } |
| /* libthread uses execvp so no need to do this */ |
| #if 0 |
| e = av[0]; |
| if(e[0]=='/' || (e[0]=='.' && e[1]=='/')) |
| goto Fail; |
| if(cputype){ |
| sprint(buf, "%s/%s", cputype, av[0]); |
| procexec(cpid, sfd, buf, av); |
| } |
| sprint(buf, "/bin/%s", av[0]); |
| procexec(cpid, sfd, buf, av); |
| #endif |
| goto Fail; |
| |
| Hard: |
| /* |
| * ugly: set path = (. $cputype /bin) |
| * should honor $path if unusual. |
| */ |
| if(cputype){ |
| n = 0; |
| memmove(buf+n, ".", 2); |
| n += 2; |
| i = strlen(cputype)+1; |
| memmove(buf+n, cputype, i); |
| n += i; |
| memmove(buf+n, "/bin", 5); |
| n += 5; |
| fd = create("/env/path", OWRITE, 0666); |
| write(fd, buf, n); |
| close(fd); |
| } |
| |
| if(arg){ |
| news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1); |
| if(news){ |
| sprint(news, "%s '%s'", t, arg); /* BUG: what if quote in arg? */ |
| free(s); |
| t = news; |
| c->text = news; |
| } |
| } |
| dir = nil; |
| if(rdir != nil) |
| dir = runetobyte(rdir, ndir); |
| shell = acmeshell; |
| if(shell == nil) |
| shell = "rc"; |
| rcarg[0] = shell; |
| rcarg[1] = "-c"; |
| rcarg[2] = t; |
| rcarg[3] = nil; |
| ret = threadspawnd(sfd, rcarg[0], rcarg, dir); |
| free(dir); |
| if(ret >= 0){ |
| if(cpid) |
| sendul(cpid, ret); |
| threadexits(nil); |
| } |
| warning(nil, "exec %s: %r\n", shell); |
| |
| Fail: |
| /* threadexec hasn't happened, so send a zero */ |
| close(sfd[0]); |
| close(sfd[1]); |
| if(sfd[2] != sfd[1]) |
| close(sfd[2]); |
| sendul(cpid, 0); |
| threadexits(nil); |
| } |
| |
| void |
| runwaittask(void *v) |
| { |
| Command *c; |
| Channel *cpid; |
| void **a; |
| |
| threadsetname("runwaittask"); |
| a = v; |
| c = a[0]; |
| cpid = a[1]; |
| free(a); |
| do |
| c->pid = recvul(cpid); |
| while(c->pid == ~0); |
| free(c->av); |
| if(c->pid != 0) /* successful exec */ |
| sendp(ccommand, c); |
| else{ |
| if(c->iseditcmd) |
| sendul(cedit, 0); |
| free(c->name); |
| free(c->text); |
| free(c); |
| } |
| chanfree(cpid); |
| } |
| |
| void |
| run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd) |
| { |
| void **arg; |
| Command *c; |
| Channel *cpid; |
| |
| if(s == nil) |
| return; |
| |
| arg = emalloc(10*sizeof(void*)); |
| c = emalloc(sizeof *c); |
| cpid = chancreate(sizeof(ulong), 0); |
| chansetname(cpid, "cpid %s", s); |
| arg[0] = win; |
| arg[1] = s; |
| arg[2] = rdir; |
| arg[3] = (void*)(uintptr)ndir; |
| arg[4] = (void*)(uintptr)newns; |
| arg[5] = argaddr; |
| arg[6] = xarg; |
| arg[7] = c; |
| arg[8] = cpid; |
| arg[9] = (void*)(uintptr)iseditcmd; |
| threadcreate(runproc, arg, STACK); |
| /* mustn't block here because must be ready to answer mount() call in run() */ |
| arg = emalloc(2*sizeof(void*)); |
| arg[0] = c; |
| arg[1] = cpid; |
| threadcreate(runwaittask, arg, STACK); |
| } |