blob: f180290bbe650cff628a8f8f4104d514e9568a87 [file] [log] [blame]
#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;
char *winsize;
Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
void usage(void);
extern int notify(void(*)(void*,char*));
void
main(int _argc, char **_argv)
{
volatile int i, argc;
char **volatile argv;
String *t;
char *termargs[10], **ap;
argc = _argc;
argv = _argv;
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;
case 'W':
*ap++ = "-W";
*ap++ = EARGF(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, (char**)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] [file ...]\n");
exits("usage");
}
void
rescue(void)
{
int i, nblank = 0;
File *f;
char *c;
char buf[256];
char *root;
if(rescuing++)
return;
io = -1;
for(i=0; i<file.nused; i++){
f = file.filepptr[i];
if(f==cmd || f->b.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++);
root = getenv("PLAN9");
if(root == nil)
root = "/usr/local/plan9";
fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
addr.r.p1 = 0, addr.r.p2 = f->b.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->b.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->b.nc);
addr.r.p2 = f->b.nc;
}else if(f->b.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->b.nc;
retcode = plan9(flist, '<', s, FALSE);
fileupdate(flist, FALSE, FALSE);
flist->seq = 0;
if (flist->b.nc > BLOCKSIZE)
error(Etoolong);
Strzero(&genstr);
Strinsure(&genstr, flist->b.nc);
bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
genstr.n = flist->b.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();
/*
* Two passes so that if we have open
* /a/foo.c and /b/foo.c and cd from /b to /a,
* we don't ever have two foo.c simultaneously.
*/
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);
}
}
for(i=0; i<tempfile.nused; i++){
f = tempfile.filepptr[i];
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->b, 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;
}