|  | #include "sam.h" | 
|  | #include "parse.h" | 
|  |  | 
|  | int	Glooping; | 
|  | int	nest; | 
|  |  | 
|  | int	append(File*, Cmd*, Posn); | 
|  | int	display(File*); | 
|  | void	looper(File*, Cmd*, int); | 
|  | void	filelooper(Cmd*, int); | 
|  | void	linelooper(File*, Cmd*); | 
|  |  | 
|  | void | 
|  | resetxec(void) | 
|  | { | 
|  | Glooping = nest = 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | cmdexec(File *f, Cmd *cp) | 
|  | { | 
|  | int i; | 
|  | Addr *ap; | 
|  | Address a; | 
|  |  | 
|  | if(f && f->unread) | 
|  | load(f); | 
|  | if(f==0 && (cp->addr==0 || cp->addr->type!='"') && | 
|  | !utfrune("bBnqUXY!", cp->cmdc) && | 
|  | cp->cmdc!=('c'|0x100) && !(cp->cmdc=='D' && cp->ctext)) | 
|  | error(Enofile); | 
|  | i = lookup(cp->cmdc); | 
|  | if(i >= 0 && cmdtab[i].defaddr != aNo){ | 
|  | if((ap=cp->addr)==0 && cp->cmdc!='\n'){ | 
|  | cp->addr = ap = newaddr(); | 
|  | ap->type = '.'; | 
|  | if(cmdtab[i].defaddr == aAll) | 
|  | ap->type = '*'; | 
|  | }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){ | 
|  | ap->next = newaddr(); | 
|  | ap->next->type = '.'; | 
|  | if(cmdtab[i].defaddr == aAll) | 
|  | ap->next->type = '*'; | 
|  | } | 
|  | if(cp->addr){	/* may be false for '\n' (only) */ | 
|  | static Address none = {0,0,0}; | 
|  | if(f) | 
|  | addr = address(ap, f->dot, 0); | 
|  | else	/* a " */ | 
|  | addr = address(ap, none, 0); | 
|  | f = addr.f; | 
|  | } | 
|  | } | 
|  | current(f); | 
|  | switch(cp->cmdc){ | 
|  | case '{': | 
|  | a = cp->addr? address(cp->addr, f->dot, 0): f->dot; | 
|  | for(cp = cp->ccmd; cp; cp = cp->next){ | 
|  | a.f->dot = a; | 
|  | cmdexec(a.f, cp); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | i=(*cmdtab[i].fn)(f, cp); | 
|  | return i; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | a_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | return append(f, cp, addr.r.p2); | 
|  | } | 
|  |  | 
|  | int | 
|  | b_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(f); | 
|  | f = cp->cmdc=='b'? tofile(cp->ctext) : getfile(cp->ctext); | 
|  | if(f->unread) | 
|  | load(f); | 
|  | else if(nest == 0) | 
|  | filename(f); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | c_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | logdelete(f, addr.r.p1, addr.r.p2); | 
|  | f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p2; | 
|  | return append(f, cp, addr.r.p2); | 
|  | } | 
|  |  | 
|  | int | 
|  | d_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(cp); | 
|  | logdelete(f, addr.r.p1, addr.r.p2); | 
|  | f->ndot.r.p1 = f->ndot.r.p2 = addr.r.p1; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | D_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | closefiles(f, cp->ctext); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | e_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | if(getname(f, cp->ctext, cp->cmdc=='e')==0) | 
|  | error(Enoname); | 
|  | edit(f, cp->cmdc); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | f_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | getname(f, cp->ctext, TRUE); | 
|  | filename(f); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | g_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | if(f!=addr.f)panic("g_cmd f!=addr.f"); | 
|  | compile(cp->re); | 
|  | if(execute(f, addr.r.p1, addr.r.p2) ^ cp->cmdc=='v'){ | 
|  | f->dot = addr; | 
|  | return cmdexec(f, cp->ccmd); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | i_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | return append(f, cp, addr.r.p1); | 
|  | } | 
|  |  | 
|  | int | 
|  | k_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(cp); | 
|  | f->mark = addr.r; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | m_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | Address addr2; | 
|  |  | 
|  | addr2 = address(cp->caddr, f->dot, 0); | 
|  | if(cp->cmdc=='m') | 
|  | move(f, addr2); | 
|  | else | 
|  | copy(f, addr2); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | n_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | int i; | 
|  | USED(f); | 
|  | USED(cp); | 
|  | for(i = 0; i<file.nused; i++){ | 
|  | if(file.filepptr[i] == cmd) | 
|  | continue; | 
|  | f = file.filepptr[i]; | 
|  | Strduplstr(&genstr, &f->name); | 
|  | filename(f); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | p_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(cp); | 
|  | return display(f); | 
|  | } | 
|  |  | 
|  | int | 
|  | q_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(cp); | 
|  | USED(f); | 
|  | trytoquit(); | 
|  | if(downloaded){ | 
|  | outT0(Hexit); | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | int | 
|  | s_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | int i, j, c, n; | 
|  | Posn p1, op, didsub = 0, delta = 0; | 
|  |  | 
|  | n = cp->num; | 
|  | op= -1; | 
|  | compile(cp->re); | 
|  | for(p1 = addr.r.p1; p1<=addr.r.p2 && execute(f, p1, addr.r.p2); ){ | 
|  | if(sel.p[0].p1==sel.p[0].p2){	/* empty match? */ | 
|  | if(sel.p[0].p1==op){ | 
|  | p1++; | 
|  | continue; | 
|  | } | 
|  | p1 = sel.p[0].p2+1; | 
|  | }else | 
|  | p1 = sel.p[0].p2; | 
|  | op = sel.p[0].p2; | 
|  | if(--n>0) | 
|  | continue; | 
|  | Strzero(&genstr); | 
|  | for(i = 0; i<cp->ctext->n; i++) | 
|  | if((c = cp->ctext->s[i])=='\\' && i<cp->ctext->n-1){ | 
|  | c = cp->ctext->s[++i]; | 
|  | if('1'<=c && c<='9') { | 
|  | j = c-'0'; | 
|  | if(sel.p[j].p2-sel.p[j].p1>BLOCKSIZE) | 
|  | error(Elongtag); | 
|  | bufread(&f->b, sel.p[j].p1, genbuf, sel.p[j].p2-sel.p[j].p1); | 
|  | Strinsert(&genstr, tmprstr(genbuf, (sel.p[j].p2-sel.p[j].p1)), genstr.n); | 
|  | }else | 
|  | Straddc(&genstr, c); | 
|  | }else if(c!='&') | 
|  | Straddc(&genstr, c); | 
|  | else{ | 
|  | if(sel.p[0].p2-sel.p[0].p1>BLOCKSIZE) | 
|  | error(Elongrhs); | 
|  | bufread(&f->b, sel.p[0].p1, genbuf, sel.p[0].p2-sel.p[0].p1); | 
|  | Strinsert(&genstr, | 
|  | tmprstr(genbuf, (int)(sel.p[0].p2-sel.p[0].p1)), | 
|  | genstr.n); | 
|  | } | 
|  | if(sel.p[0].p1!=sel.p[0].p2){ | 
|  | logdelete(f, sel.p[0].p1, sel.p[0].p2); | 
|  | delta-=sel.p[0].p2-sel.p[0].p1; | 
|  | } | 
|  | if(genstr.n){ | 
|  | loginsert(f, sel.p[0].p2, genstr.s, genstr.n); | 
|  | delta+=genstr.n; | 
|  | } | 
|  | didsub = 1; | 
|  | if(!cp->flag) | 
|  | break; | 
|  | } | 
|  | if(!didsub && nest==0) | 
|  | error(Enosub); | 
|  | f->ndot.r.p1 = addr.r.p1, f->ndot.r.p2 = addr.r.p2+delta; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | u_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | USED(f); | 
|  | USED(cp); | 
|  | n = cp->num; | 
|  | if(n >= 0) | 
|  | while(n-- && undo(TRUE)) | 
|  | ; | 
|  | else | 
|  | while(n++ && undo(FALSE)) | 
|  | ; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | w_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | int fseq; | 
|  |  | 
|  | fseq = f->seq; | 
|  | if(getname(f, cp->ctext, FALSE)==0) | 
|  | error(Enoname); | 
|  | if(fseq == seq) | 
|  | error_s(Ewseq, genc); | 
|  | writef(f); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | x_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | if(cp->re) | 
|  | looper(f, cp, cp->cmdc=='x'); | 
|  | else | 
|  | linelooper(f, cp); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | X_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(f); | 
|  | filelooper(cp, cp->cmdc=='X'); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | plan9_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | plan9(f, cp->cmdc, cp->ctext, nest); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | eq_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | int charsonly; | 
|  |  | 
|  | switch(cp->ctext->n){ | 
|  | case 1: | 
|  | charsonly = FALSE; | 
|  | break; | 
|  | case 2: | 
|  | if(cp->ctext->s[0]=='#'){ | 
|  | charsonly = TRUE; | 
|  | break; | 
|  | } | 
|  | default: | 
|  | SET(charsonly); | 
|  | error(Enewline); | 
|  | } | 
|  | printposn(f, charsonly); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | nl_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | Address a; | 
|  |  | 
|  | if(cp->addr == 0){ | 
|  | /* First put it on newline boundaries */ | 
|  | addr = lineaddr((Posn)0, f->dot, -1); | 
|  | a = lineaddr((Posn)0, f->dot, 1); | 
|  | addr.r.p2 = a.r.p2; | 
|  | if(addr.r.p1==f->dot.r.p1 && addr.r.p2==f->dot.r.p2) | 
|  | addr = lineaddr((Posn)1, f->dot, 1); | 
|  | display(f); | 
|  | }else if(downloaded) | 
|  | moveto(f, addr.r); | 
|  | else | 
|  | display(f); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | cd_cmd(File *f, Cmd *cp) | 
|  | { | 
|  | USED(f); | 
|  | cd(cp->ctext); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | append(File *f, Cmd *cp, Posn p) | 
|  | { | 
|  | if(cp->ctext->n>0 && cp->ctext->s[cp->ctext->n-1]==0) | 
|  | --cp->ctext->n; | 
|  | if(cp->ctext->n>0) | 
|  | loginsert(f, p, cp->ctext->s, cp->ctext->n); | 
|  | f->ndot.r.p1 = p; | 
|  | f->ndot.r.p2 = p+cp->ctext->n; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int | 
|  | display(File *f) | 
|  | { | 
|  | Posn p1, p2; | 
|  | int np; | 
|  | char *c; | 
|  |  | 
|  | p1 = addr.r.p1; | 
|  | p2 = addr.r.p2; | 
|  | if(p2 > f->b.nc){ | 
|  | fprint(2, "bad display addr p1=%ld p2=%ld f->b.nc=%d\n", p1, p2, f->b.nc); /*ZZZ should never happen, can remove */ | 
|  | p2 = f->b.nc; | 
|  | } | 
|  | while(p1 < p2){ | 
|  | np = p2-p1; | 
|  | if(np>BLOCKSIZE-1) | 
|  | np = BLOCKSIZE-1; | 
|  | bufread(&f->b, p1, genbuf, np); | 
|  | genbuf[np] = 0; | 
|  | c = Strtoc(tmprstr(genbuf, np+1)); | 
|  | if(downloaded) | 
|  | termwrite(c); | 
|  | else | 
|  | Write(1, c, strlen(c)); | 
|  | free(c); | 
|  | p1 += np; | 
|  | } | 
|  | f->dot = addr; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | void | 
|  | looper(File *f, Cmd *cp, int xy) | 
|  | { | 
|  | Posn p, op; | 
|  | Range r; | 
|  |  | 
|  | r = addr.r; | 
|  | op= xy? -1 : r.p1; | 
|  | nest++; | 
|  | compile(cp->re); | 
|  | for(p = r.p1; p<=r.p2; ){ | 
|  | if(!execute(f, p, r.p2)){ /* no match, but y should still run */ | 
|  | if(xy || op>r.p2) | 
|  | break; | 
|  | f->dot.r.p1 = op, f->dot.r.p2 = r.p2; | 
|  | p = r.p2+1;	/* exit next loop */ | 
|  | }else{ | 
|  | if(sel.p[0].p1==sel.p[0].p2){	/* empty match? */ | 
|  | if(sel.p[0].p1==op){ | 
|  | p++; | 
|  | continue; | 
|  | } | 
|  | p = sel.p[0].p2+1; | 
|  | }else | 
|  | p = sel.p[0].p2; | 
|  | if(xy) | 
|  | f->dot.r = sel.p[0]; | 
|  | else | 
|  | f->dot.r.p1 = op, f->dot.r.p2 = sel.p[0].p1; | 
|  | } | 
|  | op = sel.p[0].p2; | 
|  | cmdexec(f, cp->ccmd); | 
|  | compile(cp->re); | 
|  | } | 
|  | --nest; | 
|  | } | 
|  |  | 
|  | void | 
|  | linelooper(File *f, Cmd *cp) | 
|  | { | 
|  | Posn p; | 
|  | Range r, linesel; | 
|  | Address a, a3; | 
|  |  | 
|  | nest++; | 
|  | r = addr.r; | 
|  | a3.f = f; | 
|  | a3.r.p1 = a3.r.p2 = r.p1; | 
|  | for(p = r.p1; p<r.p2; p = a3.r.p2){ | 
|  | a3.r.p1 = a3.r.p2; | 
|  | /*pjw		if(p!=r.p1 || (linesel = lineaddr((Posn)0, a3, 1)).r.p2==p)*/ | 
|  | if(p!=r.p1 || (a = lineaddr((Posn)0, a3, 1), linesel = a.r, linesel.p2==p)){ | 
|  | a = lineaddr((Posn)1, a3, 1); | 
|  | linesel = a.r; | 
|  | } | 
|  | if(linesel.p1 >= r.p2) | 
|  | break; | 
|  | if(linesel.p2 >= r.p2) | 
|  | linesel.p2 = r.p2; | 
|  | if(linesel.p2 > linesel.p1) | 
|  | if(linesel.p1>=a3.r.p2 && linesel.p2>a3.r.p2){ | 
|  | f->dot.r = linesel; | 
|  | cmdexec(f, cp->ccmd); | 
|  | a3.r = linesel; | 
|  | continue; | 
|  | } | 
|  | break; | 
|  | } | 
|  | --nest; | 
|  | } | 
|  |  | 
|  | void | 
|  | filelooper(Cmd *cp, int XY) | 
|  | { | 
|  | File *f, *cur; | 
|  | int i; | 
|  |  | 
|  | if(Glooping++) | 
|  | error(EnestXY); | 
|  | nest++; | 
|  | settempfile(); | 
|  | cur = curfile; | 
|  | for(i = 0; i<tempfile.nused; i++){ | 
|  | f = tempfile.filepptr[i]; | 
|  | if(f==cmd) | 
|  | continue; | 
|  | if(cp->re==0 || filematch(f, cp->re)==XY) | 
|  | cmdexec(f, cp->ccmd); | 
|  | } | 
|  | if(cur && whichmenu(cur)>=0)	/* check that cur is still a file */ | 
|  | current(cur); | 
|  | --Glooping; | 
|  | --nest; | 
|  | } |