V9/jtools/src/sam/sam.c
#include "sam.h"
#include <setjmp.h>
#include <signal.h>
typedef struct Patlist{
int nalloc;
int nused;
String **ptr;
}Patlist;
uchar genbuf[BLOCKSIZE];
char *home;
int io;
int panicking;
int rescuing;
Mod modnum;
String genstr;
String rhs;
String wd;
String cmdstr;
int intr();
int rescue();
char *getenv();
File *current();
File *tofile();
File *getfile();
File *curfile;
File *flist;
File *cmd;
jmp_buf mainloop;
Filelist tempfile;
int quitok=TRUE;
Patlist globstr;
Patlist pattern;
int downloaded;
int dflag;
int Rflag;
int zflag;
char *machine;
int dounlock;
main(argc, argv)
uchar *argv[];
{
register i;
int (*onintr)();
while(argc>1 && argv[1][0]=='-'){
switch(argv[1][1]){
case 'd':
dflag++;
break;
case 'r':
--argc, argv++;
if(argc==1){
dprint("usage: sam [-d] -r machine\n");
return 1;
}
machine=(char *)argv[1];
break;
case 'z':
zflag++;
break;
case 'R':
Rflag++;
break;
#ifdef SUN
case 't':
case 'x':
case '=':
case 'W':
sunarg(&argv, &argc);
break;
#endif
default:
dprint("sam: unknown flag %c\n", argv[1][1]);
return 1;
}
--argc, argv++;
}
allocinit();
Fstart();
strinit(&cmdstr);
strinit(&lastpat);
strinit(&lastregexp);
strinit(&genstr);
strinit(&rhs);
strinit(&wd);
gcnew(tempfile.ptr, 0);
strinit(&unixcmd);
straddc(&unixcmd, '\0');
home=getenv("HOME");
if(home==0)
home="/tmp";
if(!dflag){
if(machine)
#ifdef SUN
connectboot(machine,zflag); /* doesn't return */
#else
connectto(machine);
#endif
if(!Rflag){
if(!bootterm(zflag))
return 1;
}
rawmode(1);
if(machine)
join();
downloaded=1;
}
if(argc>1){
for(i=1; i<argc; i++)
if(!setjmp(mainloop)){
strdup(&genstr, argv[i]);
Fsetname(newfile());
}
}else if(!downloaded)
newfile()->state=Clean;
modnum++;
onintr=signal(SIGINT, intr);
if(onintr)
signal(SIGINT, onintr);
onintr=signal(SIGHUP, rescue);
if(onintr)
signal(SIGHUP, onintr);
if(file.nused)
current(file.ptr[0]);
(void)setjmp(mainloop);
cmdloop();
trytoquit(); /* if we already q'ed, quitok will be TRUE */
if(downloaded)
rawmode(0);
return 0;
}
panic(s)
char *s;
{
if(!panicking++ && !setjmp(mainloop)){
dprint("sam: panic: %s\n", s);
rescue();
}
abort();
}
rescue(){
register i, nblank=0;
register File *f;
uchar buf[128];
signal(SIGHUP, SIG_IGN);
if(rescuing++)
exit(1);
io= -1;
for(i=0; i<file.nused; i++){
f=file.ptr[i];
if(f==cmd || f->nbytes==0 || f->state!=Dirty)
continue;
if(io==-1){
strcpy(buf, (uchar *)home);
strcpy(buf+strlen(buf), (uchar *)"/sam.save");
io=creat((char *)buf, 0777);
if(io<0)
return;
}
strcpy(buf, f->name.s);
if(buf[0]==0)
sprint(buf, "nameless.%d", nblank++);
fprint(io, "/usr/jerq/lib/samsave '%s' $* <<'---%s'\n",
(char *)buf, (char *)buf);
addr.r.p1=0, addr.r.p2=f->nbytes;
writeio(f);
fprint(io, "\n---%s\n", (char *)buf);
}
if(panicking)
abort();
exit(0);
}
hiccough(s)
char *s;
{
if(rescuing)
exit(1);
if(s)
dprint("%s\n", s);
resetcmd();
resetxec();
compactok();
if(io>0)
close(io);
if(undobuf->nbytes)
Bdelete(undobuf, (Posn)0, undobuf->nbytes);
update();
if(curfile && curfile->state==Unread)
curfile->state=Clean;
dounlock=TRUE;
if(downloaded && curfile && curfile->state!=Unread)
outTs(Hcurrent, curfile->tag);
longjmp(mainloop, 1);
}
intr(){
signal(SIGINT, intr);
error(Eintr);
}
trytoclose(f)
register File *f;
{
if(f==cmd) /* possible? */
return;
if(f->state==Dirty && !f->closeok){
f->closeok=TRUE;
error_s(Emodified,
f->name.s[0]?(char *)f->name.s : "nameless file");
}
if(downloaded && f->rasp)
outTs(Hclose, f->tag);
delfile(f);
if(f==curfile)
current((File *)0);
}
trytoquit(){
register c;
register File *f;
extern int eof;
if(!quitok)
for(c=0; c<file.nused; c++){
f=file.ptr[c];
if(f!=cmd && f->state==Dirty){
quitok=TRUE;
eof=FALSE;
error(Echanges);
}
}
}
load(f)
register File *f;
{
Address saveaddr;
strdupstr(&genstr, &f->name);
filename(f);
if(f->name.s[0]){
saveaddr=addr;
edit(f, 'I');
addr=saveaddr;
}else
f->state=Clean;
Fupdate(f, FALSE, TRUE);
}
update(){
register i, anymod;
register File *f;
settempfile();
for(anymod=i=0; i<tempfile.nused; i++){
f=tempfile.ptr[i];
if(f==cmd) /* cmd gets done in main() */
continue;
if(f->mod==modnum && Fupdate(f, FALSE, downloaded))
anymod++;
if(f->rasp)
telldot(f);
}
if(anymod)
modnum++;
}
File *
current(f)
register File *f;
{
return curfile=f;
}
edit(f, cmd)
register File *f;
{
register empty=TRUE;
Posn p;
int nonascii;
if(cmd=='r')
Fdelete(f, addr.r.p1, addr.r.p2);
if(cmd=='e' || cmd=='I'){
Fdelete(f, (Posn)0, f->nbytes);
addr.r.p2=f->nbytes;
}else if(f->nbytes!=0 || (f->name.s[0] && strcmp(genstr.s, f->name.s)!=0))
empty=FALSE;
if((io=open((char *)genstr.s, 0))<0)
error_s(Eopen, (char *)genstr.s);
p=readio(f, &nonascii, empty);
closeio((cmd=='e' || cmd=='I')? -1 : p);
if(cmd=='r')
f->dot.r.p1=addr.r.p2, f->dot.r.p2=addr.r.p2+p;
else
f->dot.r.p1=f->dot.r.p2=0;
quitok=f->closeok=empty;
state(f, empty && !nonascii? Clean : Dirty);
if(cmd=='e')
filename(f);
}
getname(f, s, save)
register File *f;
register String *s;
{
register c, i;
strzero(&genstr);
if(s==0 || (c=s->s[0])==0){ /* no name provided */
if(f)
strdupstr(&genstr, &f->name);
else
straddc(&genstr, '\0');
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);
straddc(&genstr, '\0');
if(f && (save || f->name.s[0]==0)){
Fsetname(f);
if(strcmp(f->name.s, genstr.s)){
quitok=f->closeok=FALSE;
f->inumber=0;
f->date=0;
state(f, Dirty); /* if it's 'e', fix later */
}
}
Return:
return genstr.n-1; /* strlen(name) */
}
filename(f)
register File *f;
{
dprint("%c%c%c %s\n", " '"[f->state==Dirty],
"-+"[f->rasp!=0], " ."[f==curfile], genstr.s);
}
undo()
{
register File *f;
register i;
Mod max;
if((max=curfile->mod)==0)
return;
settempfile();
for(i=0; i<tempfile.nused; i++){
f=tempfile.ptr[i];
if(f!=cmd && f->mod==max)
undostep(f);
}
}
undostep(f)
register File *f;
{
register Buffer *t;
register changes;
Mark mark;
t=f->transcript;
changes=Fupdate(f, TRUE, TRUE);
Bread(t, (uchar *)&mark, sizeof mark, f->markp);
Bdelete(t, f->markp, t->nbytes);
f->markp=mark.p;
f->dot.r=mark.dot;
f->mark=mark.mark;
f->mod=mark.m;
f->closeok=mark.s1!=Dirty;
if(mark.s1==Dirty)
quitok=FALSE;
if(f->state==Clean && mark.s1==Clean && changes)
state(f, Dirty);
else
state(f, mark.s1);
}
cd(str)
String *str;
{
register i;
register File *f;
readcmd(tempstr((uchar *)"/bin/pwd", 9));
strdupstr(&wd, &genstr);
if(wd.s[0]==0){
wd.n=0;
warn(Wpwd);
}else if(wd.s[wd.n-2]=='\n'){
--wd.n;
wd.s[wd.n-1]='/';
}
if(chdir(getname((File *)0, str, FALSE)? (char *)genstr.s : home))
syserror("chdir");
settempfile();
for(i=0; i<tempfile.nused; i++){
f=tempfile.ptr[i];
if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
strinsert(&f->name, &wd, (Posn)0);
sortname(f);
}
}
}
readcmd(s)
String *s;
{
if(flist==0)
(flist=Fopen())->state=Clean;
addr.r.p1=0, addr.r.p2=flist->nbytes;
Unix(flist, '<', s, FALSE);
Fupdate(flist, FALSE, FALSE);
flist->mod=0;
strzero(&genstr);
strinsure(&genstr, flist->nbytes);
Fchars(flist, genstr.s, (Posn)0, flist->nbytes);
genstr.n=flist->nbytes;
straddc(&genstr, '\0');
}
loadflist(s)
register String *s;
{
register 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);
strdup(&genstr, (uchar *)"");
}
return genstr.s[0];
}
File *
readflist(readall, delete)
{
register Posn i;
register c;
register File *f;
for(i=0,f=0; f==0 || readall || delete; ){
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;
f=lookfile();
if(delete){
if(f==0)
warn_s(Wfile, (char *)genstr.s);
else
trytoclose(f);
}else if(f==0 && readall)
Fsetname(f=newfile());
}
return f;
}
File *
tofile(s)
String *s;
{
register File *f;
if(s->s[0]!=' ')
error(Eblank);
if(loadflist(s)==0)
f=lookfile(); /* empty string ==> nameless file */
else if((f=readflist(FALSE, FALSE))==0)
error_s(Emenu, (char *)genstr.s);
return current(f);
}
File *
getfile(s)
String *s;
{
register File *f;
if(loadflist(s)==0)
Fsetname(f=newfile());
else if((f=readflist(TRUE, FALSE))==0)
error(Eblank);
return current(f);
}
closefiles(f, s)
File *f;
register 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);
}
move(f, addr2)
register File *f;
Address addr2;
{
if(addr.r.p2<=addr2.r.p2){
Fdelete(f, addr.r.p1, addr.r.p2);
copy(f, addr2);
}else if(addr.r.p1>=addr2.r.p2){
copy(f, addr2);
Fdelete(f, addr.r.p1, addr.r.p2);
}else
error(Eoverlap);
}
copy(f, addr2)
register File *f;
Address addr2;
{
register Posn p;
register ni;
for(p=addr.r.p1; p<addr.r.p2; p+=ni){
ni=addr.r.p2-p;
if(ni>BLOCKSIZE)
ni=BLOCKSIZE;
Fchars(f, genbuf, p, p+ni);
Finsert(addr2.f, tempstr(genbuf, ni), addr2.r.p2);
}
addr2.f->dot.r.p2=addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
addr2.f->dot.r.p1=addr2.r.p2;
}
Posn
nlcount(f, p0, p1)
register File *f;
register Posn p0, p1;
{
register Posn nl=0;
Fgetcset(f, p0);
while(p0++<p1)
if(Fgetc(f)=='\n')
nl++;
return nl;
}
printposn(f, charsonly)
register File *f;
{
register 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 && (Fgetcset(f, addr.r.p2-1),Fgetc(f)=='\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");
}
settempfile(){
if(tempfile.nalloc<file.nused){
gcfree((uchar *)tempfile.ptr);
gcnew(tempfile.ptr, file.nused);
tempfile.nalloc=file.nused;
}
tempfile.nused=file.nused;
bcopy((uchar *)&file.ptr[0], (uchar *)&file.ptr[file.nused],
(uchar *)&tempfile.ptr[0], 1);
}