ref: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
dir: /sys/src/cmd/sam/sam.c/
#include "sam.h" Rune genbuf[BLOCKSIZE]; int io; int panicking; int rescuing; String genstr; String rhs; String curwd; String cmdstr; Rune empty[] = { 0 }; char *genc; File *curfile; File *flist; File *cmd; jmp_buf mainloop; List tempfile = { 'p' }; int quitok = TRUE; int downloaded; int dflag; int Rflag; char *machine; char *home; int bpipeok; int termlocked; char *samterm = SAMTERM; char *rsamname = RSAM; File *lastfile; Disk *disk; long seq; Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'}; void usage(void); void main(int argc, char *argv[]) { int i; String *t; char *termargs[10], **ap; ap = termargs; *ap++ = "samterm"; ARGBEGIN{ case 'd': dflag++; break; case 'r': machine = EARGF(usage()); break; case 'R': Rflag++; break; case 't': samterm = EARGF(usage()); break; case 's': rsamname = EARGF(usage()); break; default: dprint("sam: unknown flag %c\n", ARGC()); usage(); /* options for samterm */ case 'a': *ap++ = "-a"; if(ap >= termargs+nelem(termargs)) usage(); break; case 'i': *ap++ = "-i"; if(ap >= termargs+nelem(termargs)) usage(); break; }ARGEND *ap = nil; Strinit(&cmdstr); Strinit0(&lastpat); Strinit0(&lastregexp); Strinit0(&genstr); Strinit0(&rhs); Strinit0(&curwd); Strinit0(&plan9cmd); home = getenv(HOME); disk = diskinit(); if(home == 0) home = "/"; if(!dflag) startup(machine, Rflag, termargs, argv); notify(notifyf); getcurwd(); if(argc>0){ for(i=0; i<argc; i++){ if(!setjmp(mainloop)){ t = tmpcstr(argv[i]); Straddc(t, '\0'); Strduplstr(&genstr, t); freetmpstr(t); fixname(&genstr); logsetname(newfile(), &genstr); } } }else if(!downloaded) newfile(); seq++; if(file.nused) current(file.filepptr[0]); setjmp(mainloop); cmdloop(); trytoquit(); /* if we already q'ed, quitok will be TRUE */ exits(0); } void usage(void) { dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n"); exits("usage"); } void rescue(void) { int i, nblank = 0; File *f; char *c; char buf[256]; if(rescuing++) return; io = -1; for(i=0; i<file.nused; i++){ f = file.filepptr[i]; if(f==cmd || f->nc==0 || !fileisdirty(f)) continue; if(io == -1){ sprint(buf, "%s/sam.save", home); io = create(buf, 1, 0777); if(io<0) return; } if(f->name.s[0]){ c = Strtoc(&f->name); strncpy(buf, c, sizeof buf-1); buf[sizeof buf-1] = 0; free(c); }else sprint(buf, "nameless.%d", nblank++); fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf); addr.r.p1 = 0, addr.r.p2 = f->nc; writeio(f); fprint(io, "\n---%s\n", (char *)buf); } } void panic(char *s) { int wasd; if(!panicking++ && !setjmp(mainloop)){ wasd = downloaded; downloaded = 0; dprint("sam: panic: %s: %r\n", s); if(wasd) fprint(2, "sam: panic: %s: %r\n", s); rescue(); abort(); } } void hiccough(char *s) { File *f; int i; if(rescuing) exits("rescue"); if(s) dprint("%s\n", s); resetcmd(); resetxec(); resetsys(); if(io > 0) close(io); /* * back out any logged changes & restore old sequences */ for(i=0; i<file.nused; i++){ f = file.filepptr[i]; if(f==cmd) continue; if(f->seq==seq){ bufdelete(&f->epsilon, 0, f->epsilon.nc); f->seq = f->prevseq; f->dot.r = f->prevdot; f->mark = f->prevmark; state(f, f->prevmod ? Dirty: Clean); } } update(); if (curfile) { if (curfile->unread) curfile->unread = FALSE; else if (downloaded) outTs(Hcurrent, curfile->tag); } longjmp(mainloop, 1); } void intr(void) { error(Eintr); } void trytoclose(File *f) { char *t; char buf[256]; if(f == cmd) /* possible? */ return; if(f->deleted) return; if(fileisdirty(f) && !f->closeok){ f->closeok = TRUE; if(f->name.s[0]){ t = Strtoc(&f->name); strncpy(buf, t, sizeof buf-1); free(t); }else strcpy(buf, "nameless file"); error_s(Emodified, buf); } f->deleted = TRUE; } void trytoquit(void) { int c; File *f; if(!quitok){ for(c = 0; c<file.nused; c++){ f = file.filepptr[c]; if(f!=cmd && fileisdirty(f)){ quitok = TRUE; eof = FALSE; error(Echanges); } } } } void load(File *f) { Address saveaddr; Strduplstr(&genstr, &f->name); filename(f); if(f->name.s[0]){ saveaddr = addr; edit(f, 'I'); addr = saveaddr; }else{ f->unread = 0; f->cleanseq = f->seq; } fileupdate(f, TRUE, TRUE); } void cmdupdate(void) { if(cmd && cmd->seq!=0){ fileupdate(cmd, FALSE, downloaded); cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc; telldot(cmd); } } void delete(File *f) { if(downloaded && f->rasp) outTs(Hclose, f->tag); delfile(f); if(f == curfile) current(0); } void update(void) { int i, anymod; File *f; settempfile(); for(anymod = i=0; i<tempfile.nused; i++){ f = tempfile.filepptr[i]; if(f==cmd) /* cmd gets done in main() */ continue; if(f->deleted) { delete(f); continue; } if(f->seq==seq && fileupdate(f, FALSE, downloaded)) anymod++; if(f->rasp) telldot(f); } if(anymod) seq++; } File * current(File *f) { return curfile = f; } void edit(File *f, int cmd) { int empty = TRUE; Posn p; int nulls; if(cmd == 'r') logdelete(f, addr.r.p1, addr.r.p2); if(cmd=='e' || cmd=='I'){ logdelete(f, (Posn)0, f->nc); addr.r.p2 = f->nc; }else if(f->nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0)) empty = FALSE; if((io = open(genc, OREAD))<0) { if (curfile && curfile->unread) curfile->unread = FALSE; error_r(Eopen, genc); } p = readio(f, &nulls, empty, TRUE); closeio((cmd=='e' || cmd=='I')? -1 : p); if(cmd == 'r') f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p; else f->ndot.r.p1 = f->ndot.r.p2 = 0; f->closeok = empty; if (quitok) quitok = empty; else quitok = FALSE; state(f, empty && !nulls? Clean : Dirty); if(empty && !nulls) f->cleanseq = f->seq; if(cmd == 'e') filename(f); } int getname(File *f, String *s, int save) { int c, i; Strzero(&genstr); if(genc){ free(genc); genc = 0; } if(s==0 || (c = s->s[0])==0){ /* no name provided */ if(f) Strduplstr(&genstr, &f->name); goto Return; } if(c!=' ' && c!='\t') error(Eblank); for(i=0; (c=s->s[i])==' ' || c=='\t'; i++) ; while(s->s[i] > ' ') Straddc(&genstr, s->s[i++]); if(s->s[i]) error(Enewline); fixname(&genstr); if(f && (save || f->name.s[0]==0)){ logsetname(f, &genstr); if(Strcmp(&f->name, &genstr)){ quitok = f->closeok = FALSE; f->qidpath = 0; f->mtime = 0; state(f, Dirty); /* if it's 'e', fix later */ } } Return: genc = Strtoc(&genstr); i = genstr.n; if(i && genstr.s[i-1]==0) i--; return i; /* strlen(name) */ } void filename(File *f) { if(genc) free(genc); genc = Strtoc(&genstr); dprint("%c%c%c %s\n", " '"[f->mod], "-+"[f->rasp!=0], " ."[f==curfile], genc); } void undostep(File *f, int isundo) { uint p1, p2; int mod; mod = f->mod; fileundo(f, isundo, 1, &p1, &p2, TRUE); f->ndot = f->dot; if(f->mod){ f->closeok = 0; quitok = 0; }else f->closeok = 1; if(f->mod != mod){ f->mod = mod; if(mod) mod = Clean; else mod = Dirty; state(f, mod); } } int undo(int isundo) { File *f; int i; Mod max; max = undoseq(curfile, isundo); if(max == 0) return 0; settempfile(); for(i = 0; i<tempfile.nused; i++){ f = tempfile.filepptr[i]; if(f!=cmd && undoseq(f, isundo)==max) undostep(f, isundo); } return 1; } int readcmd(String *s) { int retcode; if(flist != 0) fileclose(flist); flist = fileopen(); addr.r.p1 = 0, addr.r.p2 = flist->nc; retcode = plan9(flist, '<', s, FALSE); fileupdate(flist, FALSE, FALSE); flist->seq = 0; if (flist->nc > BLOCKSIZE) error(Etoolong); Strzero(&genstr); Strinsure(&genstr, flist->nc); bufread(flist, (Posn)0, genbuf, flist->nc); memmove(genstr.s, genbuf, flist->nc*RUNESIZE); genstr.n = flist->nc; Straddc(&genstr, '\0'); return retcode; } void getcurwd(void) { String *t; char buf[256]; buf[0] = 0; getwd(buf, sizeof(buf)); t = tmpcstr(buf); Strduplstr(&curwd, t); freetmpstr(t); if(curwd.n == 0) warn(Wpwd); else if(curwd.s[curwd.n-1] != '/') Straddc(&curwd, '/'); } void cd(String *str) { int i, fd; char *s; File *f; String owd; getcurwd(); if(getname((File *)0, str, FALSE)) s = genc; else s = home; if(chdir(s)) syserror("chdir"); fd = open("/dev/wdir", OWRITE); if(fd >= 0) write(fd, s, strlen(s)); dprint("!\n"); Strinit(&owd); Strduplstr(&owd, &curwd); getcurwd(); settempfile(); for(i=0; i<tempfile.nused; i++){ f = tempfile.filepptr[i]; if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){ Strinsert(&f->name, &owd, (Posn)0); fixname(&f->name); sortname(f); }else if(f != cmd && Strispre(&curwd, &f->name)){ fixname(&f->name); sortname(f); } } Strclose(&owd); } int loadflist(String *s) { int c, i; c = s->s[0]; for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++) ; if((c==' ' || c=='\t') && s->s[i]!='\n'){ if(s->s[i]=='<'){ Strdelete(s, 0L, (long)i+1); readcmd(s); }else{ Strzero(&genstr); while((c = s->s[i++]) && c!='\n') Straddc(&genstr, c); Straddc(&genstr, '\0'); } }else{ if(c != '\n') error(Eblank); Strdupl(&genstr, empty); } if(genc) free(genc); genc = Strtoc(&genstr); return genstr.s[0]; } File * readflist(int readall, int delete) { Posn i; int c; File *f; String t; Strinit(&t); for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */ Strdelete(&genstr, (Posn)0, i); for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++) ; if(i >= genstr.n) break; Strdelete(&genstr, (Posn)0, i); for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++) ; if(i == 0) break; genstr.s[i] = 0; Strduplstr(&t, tmprstr(genstr.s, i+1)); fixname(&t); f = lookfile(&t); if(delete){ if(f == 0) warn_S(Wfile, &t); else trytoclose(f); }else if(f==0 && readall) logsetname(f = newfile(), &t); } Strclose(&t); return f; } File * tofile(String *s) { File *f; if(s->s[0] != ' ') error(Eblank); if(loadflist(s) == 0){ f = lookfile(&genstr); /* empty string ==> nameless file */ if(f == 0) error_s(Emenu, genc); }else if((f=readflist(FALSE, FALSE)) == 0) error_s(Emenu, genc); return current(f); } File * getfile(String *s) { File *f; if(loadflist(s) == 0) logsetname(f = newfile(), &genstr); else if((f=readflist(TRUE, FALSE)) == 0) error(Eblank); return current(f); } void closefiles(File *f, String *s) { if(s->s[0] == 0){ if(f == 0) error(Enofile); trytoclose(f); return; } if(s->s[0] != ' ') error(Eblank); if(loadflist(s) == 0) error(Enewline); readflist(FALSE, TRUE); } void copy(File *f, Address addr2) { Posn p; int ni; for(p=addr.r.p1; p<addr.r.p2; p+=ni){ ni = addr.r.p2-p; if(ni > BLOCKSIZE) ni = BLOCKSIZE; bufread(f, p, genbuf, ni); loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni); } addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1); addr2.f->ndot.r.p1 = addr2.r.p2; } void move(File *f, Address addr2) { if(addr.r.p2 <= addr2.r.p2){ logdelete(f, addr.r.p1, addr.r.p2); copy(f, addr2); }else if(addr.r.p1 >= addr2.r.p2){ copy(f, addr2); logdelete(f, addr.r.p1, addr.r.p2); }else error(Eoverlap); } Posn nlcount(File *f, Posn p0, Posn p1) { Posn nl = 0; while(p0 < p1) if(filereadc(f, p0++)=='\n') nl++; return nl; } void printposn(File *f, int chars) { Posn l1, l2; char *s; if(f->name.s[0]){ if(f->name.s[0]!='/'){ getcurwd(); s = Strtoc(&curwd); dprint("%s", s); free(s); } s = Strtoc(&f->name); dprint("%s:", s); free(s); } if(chars){ dprint("#%lud", addr.r.p1); if(addr.r.p2 != addr.r.p1) dprint(",#%lud", addr.r.p2); }else{ l1 = 1+nlcount(f, (Posn)0, addr.r.p1); l2 = l1+nlcount(f, addr.r.p1, addr.r.p2); /* check if addr ends with '\n' */ if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n') --l2; dprint("%lud", l1); if(l2 != l1) dprint(",%lud", l2); } dprint("\n"); } void settempfile(void) { if(tempfile.nalloc < file.nused){ if(tempfile.filepptr) free(tempfile.filepptr); tempfile.filepptr = emalloc(sizeof(File*)*file.nused); tempfile.nalloc = file.nused; } memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused); tempfile.nused = file.nused; }