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 = { 'p' };
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 void
41 main(int _argc, char **_argv)
42 {
43 volatile int i, argc;
44 char **volatile argv;
45 String *t;
46 char *termargs[10], **ap;
48 argc = _argc;
49 argv = _argv;
50 ap = termargs;
51 *ap++ = "samterm";
52 ARGBEGIN{
53 case 'd':
54 dflag++;
55 break;
56 case 'r':
57 machine = EARGF(usage());
58 break;
59 case 'R':
60 Rflag++;
61 break;
62 case 't':
63 samterm = EARGF(usage());
64 break;
65 case 's':
66 rsamname = EARGF(usage());
67 break;
68 default:
69 dprint("sam: unknown flag %c\n", ARGC());
70 usage();
71 /* options for samterm */
72 case 'a':
73 *ap++ = "-a";
74 break;
75 case 'W':
76 *ap++ = "-W";
77 *ap++ = EARGF(usage());
78 break;
79 }ARGEND
80 *ap = nil;
82 Strinit(&cmdstr);
83 Strinit0(&lastpat);
84 Strinit0(&lastregexp);
85 Strinit0(&genstr);
86 Strinit0(&rhs);
87 Strinit0(&curwd);
88 Strinit0(&plan9cmd);
89 home = getenv(HOME);
90 disk = diskinit();
91 if(home == 0)
92 home = "/";
93 if(!dflag)
94 startup(machine, Rflag, termargs, (char**)argv);
95 notify(notifyf);
96 getcurwd();
97 if(argc>0){
98 for(i=0; i<argc; i++){
99 if(!setjmp(mainloop)){
100 t = tmpcstr(argv[i]);
101 Straddc(t, '\0');
102 Strduplstr(&genstr, t);
103 freetmpstr(t);
104 fixname(&genstr);
105 logsetname(newfile(), &genstr);
108 }else if(!downloaded)
109 newfile();
110 seq++;
111 if(file.nused)
112 current(file.filepptr[0]);
113 setjmp(mainloop);
114 cmdloop();
115 trytoquit(); /* if we already q'ed, quitok will be TRUE */
116 exits(0);
119 void
120 usage(void)
122 dprint("usage: sam [-d] [-t samterm] [-s sam name] [-r machine] [file ...]\n");
123 exits("usage");
126 void
127 rescue(void)
129 int i, nblank = 0;
130 File *f;
131 char *c;
132 char buf[256];
133 char *root;
135 if(rescuing++)
136 return;
137 io = -1;
138 for(i=0; i<file.nused; i++){
139 f = file.filepptr[i];
140 if(f==cmd || f->b.nc==0 || !fileisdirty(f))
141 continue;
142 if(io == -1){
143 sprint(buf, "%s/sam.save", home);
144 io = create(buf, 1, 0777);
145 if(io<0)
146 return;
148 if(f->name.s[0]){
149 c = Strtoc(&f->name);
150 strncpy(buf, c, sizeof buf-1);
151 buf[sizeof buf-1] = 0;
152 free(c);
153 }else
154 sprint(buf, "nameless.%d", nblank++);
155 root = getenv("PLAN9");
156 if(root == nil)
157 root = "/usr/local/plan9";
158 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
159 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
160 writeio(f);
161 fprint(io, "\n---%s\n", (char *)buf);
165 void
166 panic(char *s)
168 int wasd;
170 if(!panicking++ && !setjmp(mainloop)){
171 wasd = downloaded;
172 downloaded = 0;
173 dprint("sam: panic: %s: %r\n", s);
174 if(wasd)
175 fprint(2, "sam: panic: %s: %r\n", s);
176 rescue();
177 abort();
181 void
182 hiccough(char *s)
184 File *f;
185 int i;
187 if(rescuing)
188 exits("rescue");
189 if(s)
190 dprint("%s\n", s);
191 resetcmd();
192 resetxec();
193 resetsys();
194 if(io > 0)
195 close(io);
197 /*
198 * back out any logged changes & restore old sequences
199 */
200 for(i=0; i<file.nused; i++){
201 f = file.filepptr[i];
202 if(f==cmd)
203 continue;
204 if(f->seq==seq){
205 bufdelete(&f->epsilon, 0, f->epsilon.nc);
206 f->seq = f->prevseq;
207 f->dot.r = f->prevdot;
208 f->mark = f->prevmark;
209 state(f, f->prevmod ? Dirty: Clean);
213 update();
214 if (curfile) {
215 if (curfile->unread)
216 curfile->unread = FALSE;
217 else if (downloaded)
218 outTs(Hcurrent, curfile->tag);
220 longjmp(mainloop, 1);
223 void
224 intr(void)
226 error(Eintr);
229 void
230 trytoclose(File *f)
232 char *t;
233 char buf[256];
235 if(f == cmd) /* possible? */
236 return;
237 if(f->deleted)
238 return;
239 if(fileisdirty(f) && !f->closeok){
240 f->closeok = TRUE;
241 if(f->name.s[0]){
242 t = Strtoc(&f->name);
243 strncpy(buf, t, sizeof buf-1);
244 free(t);
245 }else
246 strcpy(buf, "nameless file");
247 error_s(Emodified, buf);
249 f->deleted = TRUE;
252 void
253 trytoquit(void)
255 int c;
256 File *f;
258 if(!quitok){
259 for(c = 0; c<file.nused; c++){
260 f = file.filepptr[c];
261 if(f!=cmd && fileisdirty(f)){
262 quitok = TRUE;
263 eof = FALSE;
264 error(Echanges);
270 void
271 load(File *f)
273 Address saveaddr;
275 Strduplstr(&genstr, &f->name);
276 filename(f);
277 if(f->name.s[0]){
278 saveaddr = addr;
279 edit(f, 'I');
280 addr = saveaddr;
281 }else{
282 f->unread = 0;
283 f->cleanseq = f->seq;
286 fileupdate(f, TRUE, TRUE);
289 void
290 cmdupdate(void)
292 if(cmd && cmd->seq!=0){
293 fileupdate(cmd, FALSE, downloaded);
294 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
295 telldot(cmd);
299 void
300 delete(File *f)
302 if(downloaded && f->rasp)
303 outTs(Hclose, f->tag);
304 delfile(f);
305 if(f == curfile)
306 current(0);
309 void
310 update(void)
312 int i, anymod;
313 File *f;
315 settempfile();
316 for(anymod = i=0; i<tempfile.nused; i++){
317 f = tempfile.filepptr[i];
318 if(f==cmd) /* cmd gets done in main() */
319 continue;
320 if(f->deleted) {
321 delete(f);
322 continue;
324 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
325 anymod++;
326 if(f->rasp)
327 telldot(f);
329 if(anymod)
330 seq++;
333 File *
334 current(File *f)
336 return curfile = f;
339 void
340 edit(File *f, int cmd)
342 int empty = TRUE;
343 Posn p;
344 int nulls;
346 if(cmd == 'r')
347 logdelete(f, addr.r.p1, addr.r.p2);
348 if(cmd=='e' || cmd=='I'){
349 logdelete(f, (Posn)0, f->b.nc);
350 addr.r.p2 = f->b.nc;
351 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
352 empty = FALSE;
353 if((io = open(genc, OREAD))<0) {
354 if (curfile && curfile->unread)
355 curfile->unread = FALSE;
356 error_r(Eopen, genc);
358 p = readio(f, &nulls, empty, TRUE);
359 closeio((cmd=='e' || cmd=='I')? -1 : p);
360 if(cmd == 'r')
361 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
362 else
363 f->ndot.r.p1 = f->ndot.r.p2 = 0;
364 f->closeok = empty;
365 if (quitok)
366 quitok = empty;
367 else
368 quitok = FALSE;
369 state(f, empty && !nulls? Clean : Dirty);
370 if(empty && !nulls)
371 f->cleanseq = f->seq;
372 if(cmd == 'e')
373 filename(f);
376 int
377 getname(File *f, String *s, int save)
379 int c, i;
381 Strzero(&genstr);
382 if(genc){
383 free(genc);
384 genc = 0;
386 if(s==0 || (c = s->s[0])==0){ /* no name provided */
387 if(f)
388 Strduplstr(&genstr, &f->name);
389 goto Return;
391 if(c!=' ' && c!='\t')
392 error(Eblank);
393 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
395 while(s->s[i] > ' ')
396 Straddc(&genstr, s->s[i++]);
397 if(s->s[i])
398 error(Enewline);
399 fixname(&genstr);
400 if(f && (save || f->name.s[0]==0)){
401 logsetname(f, &genstr);
402 if(Strcmp(&f->name, &genstr)){
403 quitok = f->closeok = FALSE;
404 f->qidpath = 0;
405 f->mtime = 0;
406 state(f, Dirty); /* if it's 'e', fix later */
409 Return:
410 genc = Strtoc(&genstr);
411 i = genstr.n;
412 if(i && genstr.s[i-1]==0)
413 i--;
414 return i; /* strlen(name) */
417 void
418 filename(File *f)
420 if(genc)
421 free(genc);
422 genc = Strtoc(&genstr);
423 dprint("%c%c%c %s\n", " '"[f->mod],
424 "-+"[f->rasp!=0], " ."[f==curfile], genc);
427 void
428 undostep(File *f, int isundo)
430 uint p1, p2;
431 int mod;
433 mod = f->mod;
434 fileundo(f, isundo, 1, &p1, &p2, TRUE);
435 f->ndot = f->dot;
436 if(f->mod){
437 f->closeok = 0;
438 quitok = 0;
439 }else
440 f->closeok = 1;
442 if(f->mod != mod){
443 f->mod = mod;
444 if(mod)
445 mod = Clean;
446 else
447 mod = Dirty;
448 state(f, mod);
452 int
453 undo(int isundo)
455 File *f;
456 int i;
457 Mod max;
459 max = undoseq(curfile, isundo);
460 if(max == 0)
461 return 0;
462 settempfile();
463 for(i = 0; i<tempfile.nused; i++){
464 f = tempfile.filepptr[i];
465 if(f!=cmd && undoseq(f, isundo)==max)
466 undostep(f, isundo);
468 return 1;
471 int
472 readcmd(String *s)
474 int retcode;
476 if(flist != 0)
477 fileclose(flist);
478 flist = fileopen();
480 addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
481 retcode = plan9(flist, '<', s, FALSE);
482 fileupdate(flist, FALSE, FALSE);
483 flist->seq = 0;
484 if (flist->b.nc > BLOCKSIZE)
485 error(Etoolong);
486 Strzero(&genstr);
487 Strinsure(&genstr, flist->b.nc);
488 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
489 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
490 genstr.n = flist->b.nc;
491 Straddc(&genstr, '\0');
492 return retcode;
495 void
496 getcurwd(void)
498 String *t;
499 char buf[256];
501 buf[0] = 0;
502 getwd(buf, sizeof(buf));
503 t = tmpcstr(buf);
504 Strduplstr(&curwd, t);
505 freetmpstr(t);
506 if(curwd.n == 0)
507 warn(Wpwd);
508 else if(curwd.s[curwd.n-1] != '/')
509 Straddc(&curwd, '/');
512 void
513 cd(String *str)
515 int i, fd;
516 char *s;
517 File *f;
518 String owd;
520 getcurwd();
521 if(getname((File *)0, str, FALSE))
522 s = genc;
523 else
524 s = home;
525 if(chdir(s))
526 syserror("chdir");
527 fd = open("/dev/wdir", OWRITE);
528 if(fd > 0)
529 write(fd, s, strlen(s));
530 dprint("!\n");
531 Strinit(&owd);
532 Strduplstr(&owd, &curwd);
533 getcurwd();
534 settempfile();
535 /*
536 * Two passes so that if we have open
537 * /a/foo.c and /b/foo.c and cd from /b to /a,
538 * we don't ever have two foo.c simultaneously.
539 */
540 for(i=0; i<tempfile.nused; i++){
541 f = tempfile.filepptr[i];
542 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
543 Strinsert(&f->name, &owd, (Posn)0);
544 fixname(&f->name);
545 sortname(f);
548 for(i=0; i<tempfile.nused; i++){
549 f = tempfile.filepptr[i];
550 if(f != cmd && Strispre(&curwd, &f->name)){
551 fixname(&f->name);
552 sortname(f);
555 Strclose(&owd);
558 int
559 loadflist(String *s)
561 int c, i;
563 c = s->s[0];
564 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
566 if((c==' ' || c=='\t') && s->s[i]!='\n'){
567 if(s->s[i]=='<'){
568 Strdelete(s, 0L, (long)i+1);
569 readcmd(s);
570 }else{
571 Strzero(&genstr);
572 while((c = s->s[i++]) && c!='\n')
573 Straddc(&genstr, c);
574 Straddc(&genstr, '\0');
576 }else{
577 if(c != '\n')
578 error(Eblank);
579 Strdupl(&genstr, empty);
581 if(genc)
582 free(genc);
583 genc = Strtoc(&genstr);
584 return genstr.s[0];
587 File *
588 readflist(int readall, int delete)
590 Posn i;
591 int c;
592 File *f;
593 String t;
595 Strinit(&t);
596 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
597 Strdelete(&genstr, (Posn)0, i);
598 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
600 if(i >= genstr.n)
601 break;
602 Strdelete(&genstr, (Posn)0, i);
603 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
606 if(i == 0)
607 break;
608 genstr.s[i] = 0;
609 Strduplstr(&t, tmprstr(genstr.s, i+1));
610 fixname(&t);
611 f = lookfile(&t);
612 if(delete){
613 if(f == 0)
614 warn_S(Wfile, &t);
615 else
616 trytoclose(f);
617 }else if(f==0 && readall)
618 logsetname(f = newfile(), &t);
620 Strclose(&t);
621 return f;
624 File *
625 tofile(String *s)
627 File *f;
629 if(s->s[0] != ' ')
630 error(Eblank);
631 if(loadflist(s) == 0){
632 f = lookfile(&genstr); /* empty string ==> nameless file */
633 if(f == 0)
634 error_s(Emenu, genc);
635 }else if((f=readflist(FALSE, FALSE)) == 0)
636 error_s(Emenu, genc);
637 return current(f);
640 File *
641 getfile(String *s)
643 File *f;
645 if(loadflist(s) == 0)
646 logsetname(f = newfile(), &genstr);
647 else if((f=readflist(TRUE, FALSE)) == 0)
648 error(Eblank);
649 return current(f);
652 void
653 closefiles(File *f, String *s)
655 if(s->s[0] == 0){
656 if(f == 0)
657 error(Enofile);
658 trytoclose(f);
659 return;
661 if(s->s[0] != ' ')
662 error(Eblank);
663 if(loadflist(s) == 0)
664 error(Enewline);
665 readflist(FALSE, TRUE);
668 void
669 copy(File *f, Address addr2)
671 Posn p;
672 int ni;
673 for(p=addr.r.p1; p<addr.r.p2; p+=ni){
674 ni = addr.r.p2-p;
675 if(ni > BLOCKSIZE)
676 ni = BLOCKSIZE;
677 bufread(&f->b, p, genbuf, ni);
678 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
680 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
681 addr2.f->ndot.r.p1 = addr2.r.p2;
684 void
685 move(File *f, Address addr2)
687 if(addr.r.p2 <= addr2.r.p2){
688 logdelete(f, addr.r.p1, addr.r.p2);
689 copy(f, addr2);
690 }else if(addr.r.p1 >= addr2.r.p2){
691 copy(f, addr2);
692 logdelete(f, addr.r.p1, addr.r.p2);
693 }else
694 error(Eoverlap);
697 Posn
698 nlcount(File *f, Posn p0, Posn p1)
700 Posn nl = 0;
702 while(p0 < p1)
703 if(filereadc(f, p0++)=='\n')
704 nl++;
705 return nl;
708 void
709 printposn(File *f, int charsonly)
711 Posn l1, l2;
713 if(!charsonly){
714 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
715 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
716 /* check if addr ends with '\n' */
717 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
718 --l2;
719 dprint("%lud", l1);
720 if(l2 != l1)
721 dprint(",%lud", l2);
722 dprint("; ");
724 dprint("#%lud", addr.r.p1);
725 if(addr.r.p2 != addr.r.p1)
726 dprint(",#%lud", addr.r.p2);
727 dprint("\n");
730 void
731 settempfile(void)
733 if(tempfile.nalloc < file.nused){
734 if(tempfile.filepptr)
735 free(tempfile.filepptr);
736 tempfile.filepptr = emalloc(sizeof(File*)*file.nused);
737 tempfile.nalloc = file.nused;
739 memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused);
740 tempfile.nused = file.nused;