Before, executing Get in a file rewound the window offset and selection to the start of the file. After this CL, Get preserves the window offset and selection, where preserve is defined as "the same line number and rune offset within the line". So if the window started at line 10 before and the selection was line 13 chars 5-7, then that will still be true after Get, provided the new content is large enough. This should help the common situation of plumbing a compiler error, realizing the window is out of date, clicking Get, and then losing the positioning from the plumb operation.
1741 lines
35 KiB
C
1741 lines
35 KiB
C
#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 <libsec.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);
|
|
}
|
|
|
|
typedef struct TextAddr TextAddr;
|
|
struct TextAddr {
|
|
long lorigin; // line+rune for origin
|
|
long rorigin;
|
|
long lq0; // line+rune for q0
|
|
long rq0;
|
|
long lq1; // line+rune for q1
|
|
long rq1;
|
|
};
|
|
|
|
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;
|
|
TextAddr *addr, *a;
|
|
Window *w;
|
|
Text *u;
|
|
Dir *d;
|
|
long q0, q1;
|
|
|
|
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;
|
|
}
|
|
}
|
|
addr = emalloc((t->file->ntext)*sizeof(TextAddr));
|
|
for(i=0; i<t->file->ntext; i++) {
|
|
a = &addr[i];
|
|
u = t->file->text[i];
|
|
a->lorigin = nlcount(u, 0, u->org, &a->rorigin);
|
|
a->lq0 = nlcount(u, 0, u->q0, &a->rq0);
|
|
a->lq1 = nlcount(u, u->q0, u->q1, &a->rq1);
|
|
}
|
|
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);
|
|
if(samename) {
|
|
a = &addr[i];
|
|
// warning(nil, "%d %d %d %d %d %d\n", a->lorigin, a->rorigin, a->lq0, a->rq0, a->lq1, a->rq1);
|
|
q0 = nlcounttopos(u, 0, a->lq0, a->rq0);
|
|
q1 = nlcounttopos(u, q0, a->lq1, a->rq1);
|
|
textsetselect(u, q0, q1);
|
|
q0 = nlcounttopos(u, 0, a->lorigin, a->rorigin);
|
|
textsetorigin(u, q0, FALSE);
|
|
}
|
|
textscrdraw(u);
|
|
}
|
|
free(addr);
|
|
xfidlog(w, "get");
|
|
}
|
|
|
|
static void
|
|
checksha1(char *name, File *f, Dir *d)
|
|
{
|
|
int fd, n;
|
|
DigestState *h;
|
|
uchar out[20];
|
|
uchar *buf;
|
|
|
|
fd = open(name, OREAD);
|
|
if(fd < 0)
|
|
return;
|
|
h = sha1(nil, 0, nil, nil);
|
|
buf = emalloc(8192);
|
|
while((n = read(fd, buf, 8192)) > 0)
|
|
sha1(buf, n, nil, h);
|
|
free(buf);
|
|
close(fd);
|
|
sha1(nil, 0, out, h);
|
|
if(memcmp(out, f->sha1, sizeof out) == 0) {
|
|
f->dev = d->dev;
|
|
f->qidpath = d->qid.path;
|
|
f->mtime = d->mtime;
|
|
}
|
|
}
|
|
|
|
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;
|
|
DigestState *h;
|
|
|
|
w = f->curtext->w;
|
|
name = runetobyte(namer, nname);
|
|
d = dirstat(name);
|
|
if(d!=nil && runeeq(namer, nname, f->name, f->nname)){
|
|
if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime)
|
|
checksha1(name, f, d);
|
|
if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime != d->mtime) {
|
|
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);
|
|
h = sha1(nil, 0, nil, nil);
|
|
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);
|
|
sha1((uchar*)s, m, nil, h);
|
|
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;
|
|
sha1(nil, 0, f->sha1, h);
|
|
h = nil;
|
|
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(h);
|
|
free(d);
|
|
free(namer);
|
|
free(name);
|
|
close(fd);
|
|
winsettag(w);
|
|
return;
|
|
|
|
Rescue2:
|
|
if(b != nil) {
|
|
Bterm(b);
|
|
free(b);
|
|
}
|
|
free(h);
|
|
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);
|
|
}
|