Blob


1 #include "sam.h"
3 Rune genbuf[BLOCKSIZE];
4 int io;
5 int panicking;
6 int rescuing;
7 String genstr;
8 String rhs;
9 String curwd;
10 String cmdstr;
11 Rune empty[] = { 0 };
12 char *genc;
13 File *curfile;
14 File *flist;
15 File *cmd;
16 jmp_buf mainloop;
17 List tempfile;
18 int quitok = TRUE;
19 int downloaded;
20 int dflag;
21 int Rflag;
22 char *machine;
23 char *home;
24 int bpipeok;
25 int termlocked;
26 char *samterm = SAMTERM;
27 char *rsamname = RSAM;
28 File *lastfile;
29 Disk *disk;
30 long seq;
32 char *winsize;
34 Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
36 void usage(void);
38 extern int notify(void(*)(void*,char*));
40 int
41 main(int volatile argc, char **volatile argv)
42 {
43 int volatile i;
44 String *t;
45 char **ap, **arg;
47 arg = argv++;
48 ap = argv;
49 while(argc>1 && argv[0] && argv[0][0]=='-'){
50 switch(argv[0][1]){
51 case 'd':
52 dflag++;
53 break;
55 case 'r':
56 --argc, argv++;
57 if(argc == 1)
58 usage();
59 machine = *argv;
60 break;
62 case 'R':
63 Rflag++;
64 break;
66 case 't':
67 --argc, argv++;
68 if(argc == 1)
69 usage();
70 samterm = *argv;
71 break;
73 case 's':
74 --argc, argv++;
75 if(argc == 1)
76 usage();
77 rsamname = *argv;
78 break;
80 case 'x': /* x11 option - strip the x */
81 strcpy(*argv+1, *argv+2);
82 *ap++ = *argv++;
83 *ap++ = *argv;
84 argc--;
85 break;
87 case 'W':
88 --argc, argv++;
89 break;
91 default:
92 dprint("sam: unknown flag %c\n", argv[0][1]);
93 exits("usage");
94 }
95 --argc, argv++;
96 }
97 Strinit(&cmdstr);
98 Strinit0(&lastpat);
99 Strinit0(&lastregexp);
100 Strinit0(&genstr);
101 Strinit0(&rhs);
102 Strinit0(&curwd);
103 tempfile.listptr = emalloc(1); /* so it can be freed later */
104 Strinit0(&plan9cmd);
105 home = getenv(HOME);
106 disk = diskinit();
107 if(home == 0)
108 home = "/";
109 if(!dflag)
110 startup(machine, Rflag, arg, ap, argv);
111 notify(notifyf);
112 getcurwd();
113 if(argc>1){
114 for(i=0; i<argc-1; i++){
115 if(!setjmp(mainloop)){
116 t = tmpcstr(argv[i]);
117 Straddc(t, '\0');
118 Strduplstr(&genstr, t);
119 freetmpstr(t);
120 fixname(&genstr);
121 logsetname(newfile(), &genstr);
124 }else if(!downloaded)
125 newfile();
126 seq++;
127 if(file.nused)
128 current(file.filepptr[0]);
129 setjmp(mainloop);
130 cmdloop();
131 trytoquit(); /* if we already q'ed, quitok will be TRUE */
132 exits(0);
133 return 0;
136 void
137 usage(void)
139 dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
140 exits("usage");
143 void
144 rescue(void)
146 int i, nblank = 0;
147 File *f;
148 char *c;
149 char buf[256];
150 char *root;
152 if(rescuing++)
153 return;
154 io = -1;
155 for(i=0; i<file.nused; i++){
156 f = file.filepptr[i];
157 if(f==cmd || f->b.nc==0 || !fileisdirty(f))
158 continue;
159 if(io == -1){
160 sprint(buf, "%s/sam.save", home);
161 io = create(buf, 1, 0777);
162 if(io<0)
163 return;
165 if(f->name.s[0]){
166 c = Strtoc(&f->name);
167 strncpy(buf, c, sizeof buf-1);
168 buf[sizeof buf-1] = 0;
169 free(c);
170 }else
171 sprint(buf, "nameless.%d", nblank++);
172 root = getenv("PLAN9");
173 if(root == nil)
174 root = "/usr/local/plan9";
175 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
176 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
177 writeio(f);
178 fprint(io, "\n---%s\n", (char *)buf);
182 void
183 panic(char *s)
185 int wasd;
187 if(!panicking++ && !setjmp(mainloop)){
188 wasd = downloaded;
189 downloaded = 0;
190 dprint("sam: panic: %s: %r\n", s);
191 if(wasd)
192 fprint(2, "sam: panic: %s: %r\n", s);
193 rescue();
194 abort();
198 void
199 hiccough(char *s)
201 File *f;
202 int i;
204 if(rescuing)
205 exits("rescue");
206 if(s)
207 dprint("%s\n", s);
208 resetcmd();
209 resetxec();
210 resetsys();
211 if(io > 0)
212 close(io);
214 /*
215 * back out any logged changes & restore old sequences
216 */
217 for(i=0; i<file.nused; i++){
218 f = file.filepptr[i];
219 if(f==cmd)
220 continue;
221 if(f->seq==seq){
222 bufdelete(&f->epsilon, 0, f->epsilon.nc);
223 f->seq = f->prevseq;
224 f->dot.r = f->prevdot;
225 f->mark = f->prevmark;
226 state(f, f->prevmod ? Dirty: Clean);
230 update();
231 if (curfile) {
232 if (curfile->unread)
233 curfile->unread = FALSE;
234 else if (downloaded)
235 outTs(Hcurrent, curfile->tag);
237 longjmp(mainloop, 1);
240 void
241 intr(void)
243 error(Eintr);
246 void
247 trytoclose(File *f)
249 char *t;
250 char buf[256];
252 if(f == cmd) /* possible? */
253 return;
254 if(f->deleted)
255 return;
256 if(fileisdirty(f) && !f->closeok){
257 f->closeok = TRUE;
258 if(f->name.s[0]){
259 t = Strtoc(&f->name);
260 strncpy(buf, t, sizeof buf-1);
261 free(t);
262 }else
263 strcpy(buf, "nameless file");
264 error_s(Emodified, buf);
266 f->deleted = TRUE;
269 void
270 trytoquit(void)
272 int c;
273 File *f;
275 if(!quitok){
276 for(c = 0; c<file.nused; c++){
277 f = file.filepptr[c];
278 if(f!=cmd && fileisdirty(f)){
279 quitok = TRUE;
280 eof = FALSE;
281 error(Echanges);
287 void
288 load(File *f)
290 Address saveaddr;
292 Strduplstr(&genstr, &f->name);
293 filename(f);
294 if(f->name.s[0]){
295 saveaddr = addr;
296 edit(f, 'I');
297 addr = saveaddr;
298 }else{
299 f->unread = 0;
300 f->cleanseq = f->seq;
303 fileupdate(f, TRUE, TRUE);
306 void
307 cmdupdate(void)
309 if(cmd && cmd->seq!=0){
310 fileupdate(cmd, FALSE, downloaded);
311 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
312 telldot(cmd);
316 void
317 delete(File *f)
319 if(downloaded && f->rasp)
320 outTs(Hclose, f->tag);
321 delfile(f);
322 if(f == curfile)
323 current(0);
326 void
327 update(void)
329 int i, anymod;
330 File *f;
332 settempfile();
333 for(anymod = i=0; i<tempfile.nused; i++){
334 f = tempfile.filepptr[i];
335 if(f==cmd) /* cmd gets done in main() */
336 continue;
337 if(f->deleted) {
338 delete(f);
339 continue;
341 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
342 anymod++;
343 if(f->rasp)
344 telldot(f);
346 if(anymod)
347 seq++;
350 File *
351 current(File *f)
353 return curfile = f;
356 void
357 edit(File *f, int cmd)
359 int empty = TRUE;
360 Posn p;
361 int nulls;
363 if(cmd == 'r')
364 logdelete(f, addr.r.p1, addr.r.p2);
365 if(cmd=='e' || cmd=='I'){
366 logdelete(f, (Posn)0, f->b.nc);
367 addr.r.p2 = f->b.nc;
368 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
369 empty = FALSE;
370 if((io = open(genc, OREAD))<0) {
371 if (curfile && curfile->unread)
372 curfile->unread = FALSE;
373 error_r(Eopen, genc);
375 p = readio(f, &nulls, empty, TRUE);
376 closeio((cmd=='e' || cmd=='I')? -1 : p);
377 if(cmd == 'r')
378 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
379 else
380 f->ndot.r.p1 = f->ndot.r.p2 = 0;
381 f->closeok = empty;
382 if (quitok)
383 quitok = empty;
384 else
385 quitok = FALSE;
386 state(f, empty && !nulls? Clean : Dirty);
387 if(empty && !nulls)
388 f->cleanseq = f->seq;
389 if(cmd == 'e')
390 filename(f);
393 int
394 getname(File *f, String *s, int save)
396 int c, i;
398 Strzero(&genstr);
399 if(genc){
400 free(genc);
401 genc = 0;
403 if(s==0 || (c = s->s[0])==0){ /* no name provided */
404 if(f)
405 Strduplstr(&genstr, &f->name);
406 goto Return;
408 if(c!=' ' && c!='\t')
409 error(Eblank);
410 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
412 while(s->s[i] > ' ')
413 Straddc(&genstr, s->s[i++]);
414 if(s->s[i])
415 error(Enewline);
416 fixname(&genstr);
417 if(f && (save || f->name.s[0]==0)){
418 logsetname(f, &genstr);
419 if(Strcmp(&f->name, &genstr)){
420 quitok = f->closeok = FALSE;
421 f->qidpath = 0;
422 f->mtime = 0;
423 state(f, Dirty); /* if it's 'e', fix later */
426 Return:
427 genc = Strtoc(&genstr);
428 i = genstr.n;
429 if(i && genstr.s[i-1]==0)
430 i--;
431 return i; /* strlen(name) */
434 void
435 filename(File *f)
437 if(genc)
438 free(genc);
439 genc = Strtoc(&genstr);
440 dprint("%c%c%c %s\n", " '"[f->mod],
441 "-+"[f->rasp!=0], " ."[f==curfile], genc);
444 void
445 undostep(File *f, int isundo)
447 uint p1, p2;
448 int mod;
450 mod = f->mod;
451 fileundo(f, isundo, 1, &p1, &p2, TRUE);
452 f->ndot = f->dot;
453 if(f->mod){
454 f->closeok = 0;
455 quitok = 0;
456 }else
457 f->closeok = 1;
459 if(f->mod != mod){
460 f->mod = mod;
461 if(mod)
462 mod = Clean;
463 else
464 mod = Dirty;
465 state(f, mod);
469 int
470 undo(int isundo)
472 File *f;
473 int i;
474 Mod max;
476 max = undoseq(curfile, isundo);
477 if(max == 0)
478 return 0;
479 settempfile();
480 for(i = 0; i<tempfile.nused; i++){
481 f = tempfile.filepptr[i];
482 if(f!=cmd && undoseq(f, isundo)==max)
483 undostep(f, isundo);
485 return 1;
488 int
489 readcmd(String *s)
491 int retcode;
493 if(flist != 0)
494 fileclose(flist);
495 flist = fileopen();
497 addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
498 retcode = plan9(flist, '<', s, FALSE);
499 fileupdate(flist, FALSE, FALSE);
500 flist->seq = 0;
501 if (flist->b.nc > BLOCKSIZE)
502 error(Etoolong);
503 Strzero(&genstr);
504 Strinsure(&genstr, flist->b.nc);
505 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
506 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
507 genstr.n = flist->b.nc;
508 Straddc(&genstr, '\0');
509 return retcode;
512 void
513 getcurwd(void)
515 String *t;
516 char buf[256];
518 buf[0] = 0;
519 getwd(buf, sizeof(buf));
520 t = tmpcstr(buf);
521 Strduplstr(&curwd, t);
522 freetmpstr(t);
523 if(curwd.n == 0)
524 warn(Wpwd);
525 else if(curwd.s[curwd.n-1] != '/')
526 Straddc(&curwd, '/');
529 void
530 cd(String *str)
532 int i, fd;
533 char *s;
534 File *f;
535 String owd;
537 getcurwd();
538 if(getname((File *)0, str, FALSE))
539 s = genc;
540 else
541 s = home;
542 if(chdir(s))
543 syserror("chdir");
544 fd = open("/dev/wdir", OWRITE);
545 if(fd > 0)
546 write(fd, s, strlen(s));
547 dprint("!\n");
548 Strinit(&owd);
549 Strduplstr(&owd, &curwd);
550 getcurwd();
551 settempfile();
552 for(i=0; i<tempfile.nused; i++){
553 f = tempfile.filepptr[i];
554 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
555 Strinsert(&f->name, &owd, (Posn)0);
556 fixname(&f->name);
557 sortname(f);
558 }else if(f != cmd && Strispre(&curwd, &f->name)){
559 fixname(&f->name);
560 sortname(f);
563 Strclose(&owd);
566 int
567 loadflist(String *s)
569 int c, i;
571 c = s->s[0];
572 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
574 if((c==' ' || c=='\t') && s->s[i]!='\n'){
575 if(s->s[i]=='<'){
576 Strdelete(s, 0L, (long)i+1);
577 readcmd(s);
578 }else{
579 Strzero(&genstr);
580 while((c = s->s[i++]) && c!='\n')
581 Straddc(&genstr, c);
582 Straddc(&genstr, '\0');
584 }else{
585 if(c != '\n')
586 error(Eblank);
587 Strdupl(&genstr, empty);
589 if(genc)
590 free(genc);
591 genc = Strtoc(&genstr);
592 return genstr.s[0];
595 File *
596 readflist(int readall, int delete)
598 Posn i;
599 int c;
600 File *f;
601 String t;
603 Strinit(&t);
604 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
605 Strdelete(&genstr, (Posn)0, i);
606 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
608 if(i >= genstr.n)
609 break;
610 Strdelete(&genstr, (Posn)0, i);
611 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
614 if(i == 0)
615 break;
616 genstr.s[i] = 0;
617 Strduplstr(&t, tmprstr(genstr.s, i+1));
618 fixname(&t);
619 f = lookfile(&t);
620 if(delete){
621 if(f == 0)
622 warn_S(Wfile, &t);
623 else
624 trytoclose(f);
625 }else if(f==0 && readall)
626 logsetname(f = newfile(), &t);
628 Strclose(&t);
629 return f;
632 File *
633 tofile(String *s)
635 File *f;
637 if(s->s[0] != ' ')
638 error(Eblank);
639 if(loadflist(s) == 0){
640 f = lookfile(&genstr); /* empty string ==> nameless file */
641 if(f == 0)
642 error_s(Emenu, genc);
643 }else if((f=readflist(FALSE, FALSE)) == 0)
644 error_s(Emenu, genc);
645 return current(f);
648 File *
649 getfile(String *s)
651 File *f;
653 if(loadflist(s) == 0)
654 logsetname(f = newfile(), &genstr);
655 else if((f=readflist(TRUE, FALSE)) == 0)
656 error(Eblank);
657 return current(f);
660 void
661 closefiles(File *f, String *s)
663 if(s->s[0] == 0){
664 if(f == 0)
665 error(Enofile);
666 trytoclose(f);
667 return;
669 if(s->s[0] != ' ')
670 error(Eblank);
671 if(loadflist(s) == 0)
672 error(Enewline);
673 readflist(FALSE, TRUE);
676 void
677 copy(File *f, Address addr2)
679 Posn p;
680 int ni;
681 for(p=addr.r.p1; p<addr.r.p2; p+=ni){
682 ni = addr.r.p2-p;
683 if(ni > BLOCKSIZE)
684 ni = BLOCKSIZE;
685 bufread(&f->b, p, genbuf, ni);
686 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
688 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
689 addr2.f->ndot.r.p1 = addr2.r.p2;
692 void
693 move(File *f, Address addr2)
695 if(addr.r.p2 <= addr2.r.p2){
696 logdelete(f, addr.r.p1, addr.r.p2);
697 copy(f, addr2);
698 }else if(addr.r.p1 >= addr2.r.p2){
699 copy(f, addr2);
700 logdelete(f, addr.r.p1, addr.r.p2);
701 }else
702 error(Eoverlap);
705 Posn
706 nlcount(File *f, Posn p0, Posn p1)
708 Posn nl = 0;
710 while(p0 < p1)
711 if(filereadc(f, p0++)=='\n')
712 nl++;
713 return nl;
716 void
717 printposn(File *f, int charsonly)
719 Posn l1, l2;
721 if(!charsonly){
722 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
723 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
724 /* check if addr ends with '\n' */
725 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
726 --l2;
727 dprint("%lud", l1);
728 if(l2 != l1)
729 dprint(",%lud", l2);
730 dprint("; ");
732 dprint("#%lud", addr.r.p1);
733 if(addr.r.p2 != addr.r.p1)
734 dprint(",#%lud", addr.r.p2);
735 dprint("\n");
738 void
739 settempfile(void)
741 if(tempfile.nalloc < file.nused){
742 free(tempfile.listptr);
743 tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nused);
744 tempfile.nalloc = file.nused;
746 tempfile.nused = file.nused;
747 memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*));