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 Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
34 void usage(void);
36 extern int notify(void(*)(void*,char*));
38 int
39 main(int argc, char *argv[])
40 {
41 int i;
42 String *t;
43 char **ap, **arg;
45 arg = argv++;
46 ap = argv;
47 while(argc>1 && argv[0] && argv[0][0]=='-'){
48 switch(argv[0][1]){
49 case 'd':
50 dflag++;
51 break;
53 case 'r':
54 --argc, argv++;
55 if(argc == 1)
56 usage();
57 machine = *argv;
58 break;
60 case 'R':
61 Rflag++;
62 break;
64 case 't':
65 --argc, argv++;
66 if(argc == 1)
67 usage();
68 samterm = *argv;
69 break;
71 case 's':
72 --argc, argv++;
73 if(argc == 1)
74 usage();
75 rsamname = *argv;
76 break;
78 case 'x': /* x11 option - strip the x */
79 strcpy(*argv+1, *argv+2);
80 *ap++ = *argv++;
81 *ap++ = *argv;
82 argc--;
83 break;
85 default:
86 dprint("sam: unknown flag %c\n", argv[0][1]);
87 exits("usage");
88 }
89 --argc, argv++;
90 }
91 Strinit(&cmdstr);
92 Strinit0(&lastpat);
93 Strinit0(&lastregexp);
94 Strinit0(&genstr);
95 Strinit0(&rhs);
96 Strinit0(&curwd);
97 tempfile.listptr = emalloc(1); /* so it can be freed later */
98 Strinit0(&plan9cmd);
99 home = getenv(HOME);
100 disk = diskinit();
101 if(home == 0)
102 home = "/";
103 if(!dflag)
104 startup(machine, Rflag, arg, ap);
105 notify(notifyf);
106 getcurwd();
107 if(argc>1){
108 for(i=0; i<argc-1; i++){
109 if(!setjmp(mainloop)){
110 t = tmpcstr(argv[i]);
111 Straddc(t, '\0');
112 Strduplstr(&genstr, t);
113 freetmpstr(t);
114 fixname(&genstr);
115 logsetname(newfile(), &genstr);
118 }else if(!downloaded)
119 newfile();
120 seq++;
121 if(file.nused)
122 current(file.filepptr[0]);
123 setjmp(mainloop);
124 cmdloop();
125 trytoquit(); /* if we already q'ed, quitok will be TRUE */
126 exits(0);
127 return 0;
130 void
131 usage(void)
133 dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
134 exits("usage");
137 void
138 rescue(void)
140 int i, nblank = 0;
141 File *f;
142 char *c;
143 char buf[256];
144 char *root;
146 if(rescuing++)
147 return;
148 io = -1;
149 for(i=0; i<file.nused; i++){
150 f = file.filepptr[i];
151 if(f==cmd || f->b.nc==0 || !fileisdirty(f))
152 continue;
153 if(io == -1){
154 sprint(buf, "%s/sam.save", home);
155 io = create(buf, 1, 0777);
156 if(io<0)
157 return;
159 if(f->name.s[0]){
160 c = Strtoc(&f->name);
161 strncpy(buf, c, sizeof buf-1);
162 buf[sizeof buf-1] = 0;
163 free(c);
164 }else
165 sprint(buf, "nameless.%d", nblank++);
166 root = getenv("PLAN9");
167 if(root == nil)
168 root = "/usr/local/plan9";
169 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
170 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
171 writeio(f);
172 fprint(io, "\n---%s\n", (char *)buf);
176 void
177 panic(char *s)
179 int wasd;
181 if(!panicking++ && !setjmp(mainloop)){
182 wasd = downloaded;
183 downloaded = 0;
184 dprint("sam: panic: %s: %r\n", s);
185 if(wasd)
186 fprint(2, "sam: panic: %s: %r\n", s);
187 rescue();
188 abort();
192 void
193 hiccough(char *s)
195 File *f;
196 int i;
198 if(rescuing)
199 exits("rescue");
200 if(s)
201 dprint("%s\n", s);
202 resetcmd();
203 resetxec();
204 resetsys();
205 if(io > 0)
206 close(io);
208 /*
209 * back out any logged changes & restore old sequences
210 */
211 for(i=0; i<file.nused; i++){
212 f = file.filepptr[i];
213 if(f==cmd)
214 continue;
215 if(f->seq==seq){
216 bufdelete(&f->epsilon, 0, f->epsilon.nc);
217 f->seq = f->prevseq;
218 f->dot.r = f->prevdot;
219 f->mark = f->prevmark;
220 state(f, f->prevmod ? Dirty: Clean);
224 update();
225 if (curfile) {
226 if (curfile->unread)
227 curfile->unread = FALSE;
228 else if (downloaded)
229 outTs(Hcurrent, curfile->tag);
231 longjmp(mainloop, 1);
234 void
235 intr(void)
237 error(Eintr);
240 void
241 trytoclose(File *f)
243 char *t;
244 char buf[256];
246 if(f == cmd) /* possible? */
247 return;
248 if(f->deleted)
249 return;
250 if(fileisdirty(f) && !f->closeok){
251 f->closeok = TRUE;
252 if(f->name.s[0]){
253 t = Strtoc(&f->name);
254 strncpy(buf, t, sizeof buf-1);
255 free(t);
256 }else
257 strcpy(buf, "nameless file");
258 error_s(Emodified, buf);
260 f->deleted = TRUE;
263 void
264 trytoquit(void)
266 int c;
267 File *f;
269 if(!quitok){
270 for(c = 0; c<file.nused; c++){
271 f = file.filepptr[c];
272 if(f!=cmd && fileisdirty(f)){
273 quitok = TRUE;
274 eof = FALSE;
275 error(Echanges);
281 void
282 load(File *f)
284 Address saveaddr;
286 Strduplstr(&genstr, &f->name);
287 filename(f);
288 if(f->name.s[0]){
289 saveaddr = addr;
290 edit(f, 'I');
291 addr = saveaddr;
292 }else{
293 f->unread = 0;
294 f->cleanseq = f->seq;
297 fileupdate(f, TRUE, TRUE);
300 void
301 cmdupdate(void)
303 if(cmd && cmd->seq!=0){
304 fileupdate(cmd, FALSE, downloaded);
305 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
306 telldot(cmd);
310 void
311 delete(File *f)
313 if(downloaded && f->rasp)
314 outTs(Hclose, f->tag);
315 delfile(f);
316 if(f == curfile)
317 current(0);
320 void
321 update(void)
323 int i, anymod;
324 File *f;
326 settempfile();
327 for(anymod = i=0; i<tempfile.nused; i++){
328 f = tempfile.filepptr[i];
329 if(f==cmd) /* cmd gets done in main() */
330 continue;
331 if(f->deleted) {
332 delete(f);
333 continue;
335 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
336 anymod++;
337 if(f->rasp)
338 telldot(f);
340 if(anymod)
341 seq++;
344 File *
345 current(File *f)
347 return curfile = f;
350 void
351 edit(File *f, int cmd)
353 int empty = TRUE;
354 Posn p;
355 int nulls;
357 if(cmd == 'r')
358 logdelete(f, addr.r.p1, addr.r.p2);
359 if(cmd=='e' || cmd=='I'){
360 logdelete(f, (Posn)0, f->b.nc);
361 addr.r.p2 = f->b.nc;
362 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
363 empty = FALSE;
364 if((io = open(genc, OREAD))<0) {
365 if (curfile && curfile->unread)
366 curfile->unread = FALSE;
367 error_r(Eopen, genc);
369 p = readio(f, &nulls, empty, TRUE);
370 closeio((cmd=='e' || cmd=='I')? -1 : p);
371 if(cmd == 'r')
372 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
373 else
374 f->ndot.r.p1 = f->ndot.r.p2 = 0;
375 f->closeok = empty;
376 if (quitok)
377 quitok = empty;
378 else
379 quitok = FALSE;
380 state(f, empty && !nulls? Clean : Dirty);
381 if(empty && !nulls)
382 f->cleanseq = f->seq;
383 if(cmd == 'e')
384 filename(f);
387 int
388 getname(File *f, String *s, int save)
390 int c, i;
392 Strzero(&genstr);
393 if(genc){
394 free(genc);
395 genc = 0;
397 if(s==0 || (c = s->s[0])==0){ /* no name provided */
398 if(f)
399 Strduplstr(&genstr, &f->name);
400 goto Return;
402 if(c!=' ' && c!='\t')
403 error(Eblank);
404 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
406 while(s->s[i] > ' ')
407 Straddc(&genstr, s->s[i++]);
408 if(s->s[i])
409 error(Enewline);
410 fixname(&genstr);
411 if(f && (save || f->name.s[0]==0)){
412 logsetname(f, &genstr);
413 if(Strcmp(&f->name, &genstr)){
414 quitok = f->closeok = FALSE;
415 f->qidpath = 0;
416 f->mtime = 0;
417 state(f, Dirty); /* if it's 'e', fix later */
420 Return:
421 genc = Strtoc(&genstr);
422 i = genstr.n;
423 if(i && genstr.s[i-1]==0)
424 i--;
425 return i; /* strlen(name) */
428 void
429 filename(File *f)
431 if(genc)
432 free(genc);
433 genc = Strtoc(&genstr);
434 dprint("%c%c%c %s\n", " '"[f->mod],
435 "-+"[f->rasp!=0], " ."[f==curfile], genc);
438 void
439 undostep(File *f, int isundo)
441 uint p1, p2;
442 int mod;
444 mod = f->mod;
445 fileundo(f, isundo, 1, &p1, &p2, TRUE);
446 f->ndot = f->dot;
447 if(f->mod){
448 f->closeok = 0;
449 quitok = 0;
450 }else
451 f->closeok = 1;
453 if(f->mod != mod){
454 f->mod = mod;
455 if(mod)
456 mod = Clean;
457 else
458 mod = Dirty;
459 state(f, mod);
463 int
464 undo(int isundo)
466 File *f;
467 int i;
468 Mod max;
470 max = undoseq(curfile, isundo);
471 if(max == 0)
472 return 0;
473 settempfile();
474 for(i = 0; i<tempfile.nused; i++){
475 f = tempfile.filepptr[i];
476 if(f!=cmd && undoseq(f, isundo)==max)
477 undostep(f, isundo);
479 return 1;
482 int
483 readcmd(String *s)
485 int retcode;
487 if(flist != 0)
488 fileclose(flist);
489 flist = fileopen();
491 addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
492 retcode = plan9(flist, '<', s, FALSE);
493 fileupdate(flist, FALSE, FALSE);
494 flist->seq = 0;
495 if (flist->b.nc > BLOCKSIZE)
496 error(Etoolong);
497 Strzero(&genstr);
498 Strinsure(&genstr, flist->b.nc);
499 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
500 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
501 genstr.n = flist->b.nc;
502 Straddc(&genstr, '\0');
503 return retcode;
506 void
507 getcurwd(void)
509 String *t;
510 char buf[256];
512 buf[0] = 0;
513 getwd(buf, sizeof(buf));
514 t = tmpcstr(buf);
515 Strduplstr(&curwd, t);
516 freetmpstr(t);
517 if(curwd.n == 0)
518 warn(Wpwd);
519 else if(curwd.s[curwd.n-1] != '/')
520 Straddc(&curwd, '/');
523 void
524 cd(String *str)
526 int i, fd;
527 char *s;
528 File *f;
529 String owd;
531 getcurwd();
532 if(getname((File *)0, str, FALSE))
533 s = genc;
534 else
535 s = home;
536 if(chdir(s))
537 syserror("chdir");
538 fd = open("/dev/wdir", OWRITE);
539 if(fd > 0)
540 write(fd, s, strlen(s));
541 dprint("!\n");
542 Strinit(&owd);
543 Strduplstr(&owd, &curwd);
544 getcurwd();
545 settempfile();
546 for(i=0; i<tempfile.nused; i++){
547 f = tempfile.filepptr[i];
548 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
549 Strinsert(&f->name, &owd, (Posn)0);
550 fixname(&f->name);
551 sortname(f);
552 }else if(f != cmd && Strispre(&curwd, &f->name)){
553 fixname(&f->name);
554 sortname(f);
557 Strclose(&owd);
560 int
561 loadflist(String *s)
563 int c, i;
565 c = s->s[0];
566 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
568 if((c==' ' || c=='\t') && s->s[i]!='\n'){
569 if(s->s[i]=='<'){
570 Strdelete(s, 0L, (long)i+1);
571 readcmd(s);
572 }else{
573 Strzero(&genstr);
574 while((c = s->s[i++]) && c!='\n')
575 Straddc(&genstr, c);
576 Straddc(&genstr, '\0');
578 }else{
579 if(c != '\n')
580 error(Eblank);
581 Strdupl(&genstr, empty);
583 if(genc)
584 free(genc);
585 genc = Strtoc(&genstr);
586 return genstr.s[0];
589 File *
590 readflist(int readall, int delete)
592 Posn i;
593 int c;
594 File *f;
595 String t;
597 Strinit(&t);
598 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
599 Strdelete(&genstr, (Posn)0, i);
600 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
602 if(i >= genstr.n)
603 break;
604 Strdelete(&genstr, (Posn)0, i);
605 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
608 if(i == 0)
609 break;
610 genstr.s[i] = 0;
611 Strduplstr(&t, tmprstr(genstr.s, i+1));
612 fixname(&t);
613 f = lookfile(&t);
614 if(delete){
615 if(f == 0)
616 warn_S(Wfile, &t);
617 else
618 trytoclose(f);
619 }else if(f==0 && readall)
620 logsetname(f = newfile(), &t);
622 Strclose(&t);
623 return f;
626 File *
627 tofile(String *s)
629 File *f;
631 if(s->s[0] != ' ')
632 error(Eblank);
633 if(loadflist(s) == 0){
634 f = lookfile(&genstr); /* empty string ==> nameless file */
635 if(f == 0)
636 error_s(Emenu, genc);
637 }else if((f=readflist(FALSE, FALSE)) == 0)
638 error_s(Emenu, genc);
639 return current(f);
642 File *
643 getfile(String *s)
645 File *f;
647 if(loadflist(s) == 0)
648 logsetname(f = newfile(), &genstr);
649 else if((f=readflist(TRUE, FALSE)) == 0)
650 error(Eblank);
651 return current(f);
654 void
655 closefiles(File *f, String *s)
657 if(s->s[0] == 0){
658 if(f == 0)
659 error(Enofile);
660 trytoclose(f);
661 return;
663 if(s->s[0] != ' ')
664 error(Eblank);
665 if(loadflist(s) == 0)
666 error(Enewline);
667 readflist(FALSE, TRUE);
670 void
671 copy(File *f, Address addr2)
673 Posn p;
674 int ni;
675 for(p=addr.r.p1; p<addr.r.p2; p+=ni){
676 ni = addr.r.p2-p;
677 if(ni > BLOCKSIZE)
678 ni = BLOCKSIZE;
679 bufread(&f->b, p, genbuf, ni);
680 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
682 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
683 addr2.f->ndot.r.p1 = addr2.r.p2;
686 void
687 move(File *f, Address addr2)
689 if(addr.r.p2 <= addr2.r.p2){
690 logdelete(f, addr.r.p1, addr.r.p2);
691 copy(f, addr2);
692 }else if(addr.r.p1 >= addr2.r.p2){
693 copy(f, addr2);
694 logdelete(f, addr.r.p1, addr.r.p2);
695 }else
696 error(Eoverlap);
699 Posn
700 nlcount(File *f, Posn p0, Posn p1)
702 Posn nl = 0;
704 while(p0 < p1)
705 if(filereadc(f, p0++)=='\n')
706 nl++;
707 return nl;
710 void
711 printposn(File *f, int charsonly)
713 Posn l1, l2;
715 if(!charsonly){
716 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
717 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
718 /* check if addr ends with '\n' */
719 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
720 --l2;
721 dprint("%lud", l1);
722 if(l2 != l1)
723 dprint(",%lud", l2);
724 dprint("; ");
726 dprint("#%lud", addr.r.p1);
727 if(addr.r.p2 != addr.r.p1)
728 dprint(",#%lud", addr.r.p2);
729 dprint("\n");
732 void
733 settempfile(void)
735 if(tempfile.nalloc < file.nused){
736 free(tempfile.listptr);
737 tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nused);
738 tempfile.nalloc = file.nused;
740 tempfile.nused = file.nused;
741 memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*));