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 = get9root();
156 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
157 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
158 writeio(f);
159 fprint(io, "\n---%s\n", (char *)buf);
163 void
164 panic(char *s)
166 int wasd;
168 if(!panicking++ && !setjmp(mainloop)){
169 wasd = downloaded;
170 downloaded = 0;
171 dprint("sam: panic: %s: %r\n", s);
172 if(wasd)
173 fprint(2, "sam: panic: %s: %r\n", s);
174 rescue();
175 abort();
179 void
180 hiccough(char *s)
182 File *f;
183 int i;
185 if(rescuing)
186 exits("rescue");
187 if(s)
188 dprint("%s\n", s);
189 resetcmd();
190 resetxec();
191 resetsys();
192 if(io > 0)
193 close(io);
195 /*
196 * back out any logged changes & restore old sequences
197 */
198 for(i=0; i<file.nused; i++){
199 f = file.filepptr[i];
200 if(f==cmd)
201 continue;
202 if(f->seq==seq){
203 bufdelete(&f->epsilon, 0, f->epsilon.nc);
204 f->seq = f->prevseq;
205 f->dot.r = f->prevdot;
206 f->mark = f->prevmark;
207 state(f, f->prevmod ? Dirty: Clean);
211 update();
212 if (curfile) {
213 if (curfile->unread)
214 curfile->unread = FALSE;
215 else if (downloaded)
216 outTs(Hcurrent, curfile->tag);
218 longjmp(mainloop, 1);
221 void
222 intr(void)
224 error(Eintr);
227 void
228 trytoclose(File *f)
230 char *t;
231 char buf[256];
233 if(f == cmd) /* possible? */
234 return;
235 if(f->deleted)
236 return;
237 if(fileisdirty(f) && !f->closeok){
238 f->closeok = TRUE;
239 if(f->name.s[0]){
240 t = Strtoc(&f->name);
241 strncpy(buf, t, sizeof buf-1);
242 free(t);
243 }else
244 strcpy(buf, "nameless file");
245 error_s(Emodified, buf);
247 f->deleted = TRUE;
250 void
251 trytoquit(void)
253 int c;
254 File *f;
256 if(!quitok){
257 for(c = 0; c<file.nused; c++){
258 f = file.filepptr[c];
259 if(f!=cmd && fileisdirty(f)){
260 quitok = TRUE;
261 eof = FALSE;
262 error(Echanges);
268 void
269 load(File *f)
271 Address saveaddr;
273 Strduplstr(&genstr, &f->name);
274 filename(f);
275 if(f->name.s[0]){
276 saveaddr = addr;
277 edit(f, 'I');
278 addr = saveaddr;
279 }else{
280 f->unread = 0;
281 f->cleanseq = f->seq;
284 fileupdate(f, TRUE, TRUE);
287 void
288 cmdupdate(void)
290 if(cmd && cmd->seq!=0){
291 fileupdate(cmd, FALSE, downloaded);
292 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
293 telldot(cmd);
297 void
298 delete(File *f)
300 if(downloaded && f->rasp)
301 outTs(Hclose, f->tag);
302 delfile(f);
303 if(f == curfile)
304 current(0);
307 void
308 update(void)
310 int i, anymod;
311 File *f;
313 settempfile();
314 for(anymod = i=0; i<tempfile.nused; i++){
315 f = tempfile.filepptr[i];
316 if(f==cmd) /* cmd gets done in main() */
317 continue;
318 if(f->deleted) {
319 delete(f);
320 continue;
322 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
323 anymod++;
324 if(f->rasp)
325 telldot(f);
327 if(anymod)
328 seq++;
331 File *
332 current(File *f)
334 return curfile = f;
337 void
338 edit(File *f, int cmd)
340 int empty = TRUE;
341 Posn p;
342 int nulls;
344 if(cmd == 'r')
345 logdelete(f, addr.r.p1, addr.r.p2);
346 if(cmd=='e' || cmd=='I'){
347 logdelete(f, (Posn)0, f->b.nc);
348 addr.r.p2 = f->b.nc;
349 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
350 empty = FALSE;
351 if((io = open(genc, OREAD))<0) {
352 if (curfile && curfile->unread)
353 curfile->unread = FALSE;
354 error_r(Eopen, genc);
356 p = readio(f, &nulls, empty, TRUE);
357 closeio((cmd=='e' || cmd=='I')? -1 : p);
358 if(cmd == 'r')
359 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
360 else
361 f->ndot.r.p1 = f->ndot.r.p2 = 0;
362 f->closeok = empty;
363 if (quitok)
364 quitok = empty;
365 else
366 quitok = FALSE;
367 state(f, empty && !nulls? Clean : Dirty);
368 if(empty && !nulls)
369 f->cleanseq = f->seq;
370 if(cmd == 'e')
371 filename(f);
374 int
375 getname(File *f, String *s, int save)
377 int c, i;
379 Strzero(&genstr);
380 if(genc){
381 free(genc);
382 genc = 0;
384 if(s==0 || (c = s->s[0])==0){ /* no name provided */
385 if(f)
386 Strduplstr(&genstr, &f->name);
387 goto Return;
389 if(c!=' ' && c!='\t')
390 error(Eblank);
391 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
393 while(s->s[i] > ' ')
394 Straddc(&genstr, s->s[i++]);
395 if(s->s[i])
396 error(Enewline);
397 fixname(&genstr);
398 if(f && (save || f->name.s[0]==0)){
399 logsetname(f, &genstr);
400 if(Strcmp(&f->name, &genstr)){
401 quitok = f->closeok = FALSE;
402 f->qidpath = 0;
403 f->mtime = 0;
404 state(f, Dirty); /* if it's 'e', fix later */
407 Return:
408 genc = Strtoc(&genstr);
409 i = genstr.n;
410 if(i && genstr.s[i-1]==0)
411 i--;
412 return i; /* strlen(name) */
415 void
416 filename(File *f)
418 if(genc)
419 free(genc);
420 genc = Strtoc(&genstr);
421 dprint("%c%c%c %s\n", " '"[f->mod],
422 "-+"[f->rasp!=0], " ."[f==curfile], genc);
425 void
426 undostep(File *f, int isundo)
428 uint p1, p2;
429 int mod;
431 mod = f->mod;
432 fileundo(f, isundo, 1, &p1, &p2, TRUE);
433 f->ndot = f->dot;
434 if(f->mod){
435 f->closeok = 0;
436 quitok = 0;
437 }else
438 f->closeok = 1;
440 if(f->mod != mod){
441 f->mod = mod;
442 if(mod)
443 mod = Clean;
444 else
445 mod = Dirty;
446 state(f, mod);
450 int
451 undo(int isundo)
453 File *f;
454 int i;
455 Mod max;
457 max = undoseq(curfile, isundo);
458 if(max == 0)
459 return 0;
460 settempfile();
461 for(i = 0; i<tempfile.nused; i++){
462 f = tempfile.filepptr[i];
463 if(f!=cmd && undoseq(f, isundo)==max)
464 undostep(f, isundo);
466 return 1;
469 int
470 readcmd(String *s)
472 int retcode;
474 if(flist != 0)
475 fileclose(flist);
476 flist = fileopen();
478 addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
479 retcode = plan9(flist, '<', s, FALSE);
480 fileupdate(flist, FALSE, FALSE);
481 flist->seq = 0;
482 if (flist->b.nc > BLOCKSIZE)
483 error(Etoolong);
484 Strzero(&genstr);
485 Strinsure(&genstr, flist->b.nc);
486 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
487 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
488 genstr.n = flist->b.nc;
489 Straddc(&genstr, '\0');
490 return retcode;
493 void
494 getcurwd(void)
496 String *t;
497 char buf[256];
499 buf[0] = 0;
500 getwd(buf, sizeof(buf));
501 t = tmpcstr(buf);
502 Strduplstr(&curwd, t);
503 freetmpstr(t);
504 if(curwd.n == 0)
505 warn(Wpwd);
506 else if(curwd.s[curwd.n-1] != '/')
507 Straddc(&curwd, '/');
510 void
511 cd(String *str)
513 int i, fd;
514 char *s;
515 File *f;
516 String owd;
518 getcurwd();
519 if(getname((File *)0, str, FALSE))
520 s = genc;
521 else
522 s = home;
523 if(chdir(s))
524 syserror("chdir");
525 fd = open("/dev/wdir", OWRITE);
526 if(fd > 0)
527 write(fd, s, strlen(s));
528 dprint("!\n");
529 Strinit(&owd);
530 Strduplstr(&owd, &curwd);
531 getcurwd();
532 settempfile();
533 /*
534 * Two passes so that if we have open
535 * /a/foo.c and /b/foo.c and cd from /b to /a,
536 * we don't ever have two foo.c simultaneously.
537 */
538 for(i=0; i<tempfile.nused; i++){
539 f = tempfile.filepptr[i];
540 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
541 Strinsert(&f->name, &owd, (Posn)0);
542 fixname(&f->name);
543 sortname(f);
546 for(i=0; i<tempfile.nused; i++){
547 f = tempfile.filepptr[i];
548 if(f != cmd && Strispre(&curwd, &f->name)){
549 fixname(&f->name);
550 sortname(f);
553 Strclose(&owd);
556 int
557 loadflist(String *s)
559 int c, i;
561 c = s->s[0];
562 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
564 if((c==' ' || c=='\t') && s->s[i]!='\n'){
565 if(s->s[i]=='<'){
566 Strdelete(s, 0L, (long)i+1);
567 readcmd(s);
568 }else{
569 Strzero(&genstr);
570 while((c = s->s[i++]) && c!='\n')
571 Straddc(&genstr, c);
572 Straddc(&genstr, '\0');
574 }else{
575 if(c != '\n')
576 error(Eblank);
577 Strdupl(&genstr, empty);
579 if(genc)
580 free(genc);
581 genc = Strtoc(&genstr);
582 return genstr.s[0];
585 File *
586 readflist(int readall, int delete)
588 Posn i;
589 int c;
590 File *f;
591 String t;
593 Strinit(&t);
594 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
595 Strdelete(&genstr, (Posn)0, i);
596 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
598 if(i >= genstr.n)
599 break;
600 Strdelete(&genstr, (Posn)0, i);
601 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
604 if(i == 0)
605 break;
606 genstr.s[i] = 0;
607 Strduplstr(&t, tmprstr(genstr.s, i+1));
608 fixname(&t);
609 f = lookfile(&t);
610 if(delete){
611 if(f == 0)
612 warn_S(Wfile, &t);
613 else
614 trytoclose(f);
615 }else if(f==0 && readall)
616 logsetname(f = newfile(), &t);
618 Strclose(&t);
619 return f;
622 File *
623 tofile(String *s)
625 File *f;
627 if(s->s[0] != ' ')
628 error(Eblank);
629 if(loadflist(s) == 0){
630 f = lookfile(&genstr); /* empty string ==> nameless file */
631 if(f == 0)
632 error_s(Emenu, genc);
633 }else if((f=readflist(FALSE, FALSE)) == 0)
634 error_s(Emenu, genc);
635 return current(f);
638 File *
639 getfile(String *s)
641 File *f;
643 if(loadflist(s) == 0)
644 logsetname(f = newfile(), &genstr);
645 else if((f=readflist(TRUE, FALSE)) == 0)
646 error(Eblank);
647 return current(f);
650 void
651 closefiles(File *f, String *s)
653 if(s->s[0] == 0){
654 if(f == 0)
655 error(Enofile);
656 trytoclose(f);
657 return;
659 if(s->s[0] != ' ')
660 error(Eblank);
661 if(loadflist(s) == 0)
662 error(Enewline);
663 readflist(FALSE, TRUE);
666 void
667 copy(File *f, Address addr2)
669 Posn p;
670 int ni;
671 for(p=addr.r.p1; p<addr.r.p2; p+=ni){
672 ni = addr.r.p2-p;
673 if(ni > BLOCKSIZE)
674 ni = BLOCKSIZE;
675 bufread(&f->b, p, genbuf, ni);
676 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
678 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
679 addr2.f->ndot.r.p1 = addr2.r.p2;
682 void
683 move(File *f, Address addr2)
685 if(addr.r.p2 <= addr2.r.p2){
686 logdelete(f, addr.r.p1, addr.r.p2);
687 copy(f, addr2);
688 }else if(addr.r.p1 >= addr2.r.p2){
689 copy(f, addr2);
690 logdelete(f, addr.r.p1, addr.r.p2);
691 }else
692 error(Eoverlap);
695 Posn
696 nlcount(File *f, Posn p0, Posn p1)
698 Posn nl = 0;
700 while(p0 < p1)
701 if(filereadc(f, p0++)=='\n')
702 nl++;
703 return nl;
706 void
707 printposn(File *f, int charsonly)
709 Posn l1, l2;
711 if(!charsonly){
712 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
713 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
714 /* check if addr ends with '\n' */
715 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
716 --l2;
717 dprint("%lud", l1);
718 if(l2 != l1)
719 dprint(",%lud", l2);
720 dprint("; ");
722 dprint("#%lud", addr.r.p1);
723 if(addr.r.p2 != addr.r.p1)
724 dprint(",#%lud", addr.r.p2);
725 dprint("\n");
728 void
729 settempfile(void)
731 if(tempfile.nalloc < file.nused){
732 if(tempfile.filepptr)
733 free(tempfile.filepptr);
734 tempfile.filepptr = emalloc(sizeof(File*)*file.nused);
735 tempfile.nalloc = file.nused;
737 memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused);
738 tempfile.nused = file.nused;