726 lines
12 KiB
C
726 lines
12 KiB
C
/*
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
* in the LICENSE file.
|
|
*/
|
|
|
|
#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";
|
|
break;
|
|
}ARGEND
|
|
*ap = nil;
|
|
|
|
Strinit(&cmdstr);
|
|
Strinit0(&lastpat);
|
|
Strinit0(&lastregexp);
|
|
Strinit0(&genstr);
|
|
Strinit0(&rhs);
|
|
Strinit0(&curwd);
|
|
Strinit0(&plan9cmd);
|
|
home = getenv(ENV_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->Buffer.nc==0 || !fileisdirty(f))
|
|
continue;
|
|
if(io == -1){
|
|
sprint(buf, "%s/sam.save", home);
|
|
io = ocreate(buf, OWRITE, 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->Buffer.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->Buffer.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->Buffer.nc);
|
|
addr.r.p2 = f->Buffer.nc;
|
|
}else if(f->Buffer.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->Buffer.nc;
|
|
retcode = plan9(flist, '<', s, FALSE);
|
|
fileupdate(flist, FALSE, FALSE);
|
|
flist->seq = 0;
|
|
if (flist->Buffer.nc > BLOCKSIZE)
|
|
error(Etoolong);
|
|
Strzero(&genstr);
|
|
Strinsure(&genstr, flist->Buffer.nc);
|
|
bufread(&flist->Buffer, (Posn)0, genbuf, flist->Buffer.nc);
|
|
memmove(genstr.s, genbuf, flist->Buffer.nc*RUNESIZE);
|
|
genstr.n = flist->Buffer.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->Buffer, 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 charsonly)
|
|
{
|
|
Posn l1, l2;
|
|
|
|
if(!charsonly){
|
|
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("; ");
|
|
}
|
|
dprint("#%lud", addr.r.p1);
|
|
if(addr.r.p2 != addr.r.p1)
|
|
dprint(",#%lud", addr.r.p2);
|
|
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;
|
|
}
|