Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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(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 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;
}