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 int main(int argc, char *argv[])
37 {
38 int i;
39 String *t;
40 char **ap, **arg;
42 { // libfmt-2.0 uses %lu where we need %lud
43 extern int __flagfmt(Fmt*);
44 fmtinstall('u', __flagfmt);
45 }
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 default:
88 dprint("sam: unknown flag %c\n", argv[0][1]);
89 exits("usage");
90 }
91 --argc, argv++;
92 }
93 Strinit(&cmdstr);
94 Strinit0(&lastpat);
95 Strinit0(&lastregexp);
96 Strinit0(&genstr);
97 Strinit0(&rhs);
98 Strinit0(&curwd);
99 tempfile.listptr = emalloc(1); /* so it can be freed later */
100 Strinit0(&plan9cmd);
101 home = getenv(HOME);
102 disk = diskinit();
103 if(home == 0)
104 home = "/";
105 if(!dflag)
106 startup(machine, Rflag, arg, ap);
107 notify(notifyf);
108 getcurwd();
109 if(argc>1){
110 for(i=0; i<argc-1; i++){
111 if(!setjmp(mainloop)){
112 t = tmpcstr(argv[i]);
113 Straddc(t, '\0');
114 Strduplstr(&genstr, t);
115 freetmpstr(t);
116 fixname(&genstr);
117 logsetname(newfile(), &genstr);
120 }else if(!downloaded)
121 newfile();
122 seq++;
123 if(file.nused)
124 current(file.filepptr[0]);
125 setjmp(mainloop);
126 cmdloop();
127 trytoquit(); /* if we already q'ed, quitok will be TRUE */
128 exits(0);
131 void
132 usage(void)
134 dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
135 exits("usage");
138 void
139 rescue(void)
141 int i, nblank = 0;
142 File *f;
143 char *c;
144 char buf[256];
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 fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf);
167 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
168 writeio(f);
169 fprint(io, "\n---%s\n", (char *)buf);
173 void
174 panic(char *s)
176 int wasd;
178 if(!panicking++ && !setjmp(mainloop)){
179 wasd = downloaded;
180 downloaded = 0;
181 dprint("sam: panic: %s: %r\n", s);
182 if(wasd)
183 fprint(2, "sam: panic: %s: %r\n", s);
184 rescue();
185 abort();
189 void
190 hiccough(char *s)
192 File *f;
193 int i;
195 if(rescuing)
196 exits("rescue");
197 if(s)
198 dprint("%s\n", s);
199 resetcmd();
200 resetxec();
201 resetsys();
202 if(io > 0)
203 close(io);
205 /*
206 * back out any logged changes & restore old sequences
207 */
208 for(i=0; i<file.nused; i++){
209 f = file.filepptr[i];
210 if(f==cmd)
211 continue;
212 if(f->seq==seq){
213 bufdelete(&f->epsilon, 0, f->epsilon.nc);
214 f->seq = f->prevseq;
215 f->dot.r = f->prevdot;
216 f->mark = f->prevmark;
217 state(f, f->prevmod ? Dirty: Clean);
221 update();
222 if (curfile) {
223 if (curfile->unread)
224 curfile->unread = FALSE;
225 else if (downloaded)
226 outTs(Hcurrent, curfile->tag);
228 longjmp(mainloop, 1);
231 void
232 intr(void)
234 error(Eintr);
237 void
238 trytoclose(File *f)
240 char *t;
241 char buf[256];
243 if(f == cmd) /* possible? */
244 return;
245 if(f->deleted)
246 return;
247 if(fileisdirty(f) && !f->closeok){
248 f->closeok = TRUE;
249 if(f->name.s[0]){
250 t = Strtoc(&f->name);
251 strncpy(buf, t, sizeof buf-1);
252 free(t);
253 }else
254 strcpy(buf, "nameless file");
255 error_s(Emodified, buf);
257 f->deleted = TRUE;
260 void
261 trytoquit(void)
263 int c;
264 File *f;
266 if(!quitok){
267 for(c = 0; c<file.nused; c++){
268 f = file.filepptr[c];
269 if(f!=cmd && fileisdirty(f)){
270 quitok = TRUE;
271 eof = FALSE;
272 error(Echanges);
278 void
279 load(File *f)
281 Address saveaddr;
283 Strduplstr(&genstr, &f->name);
284 filename(f);
285 if(f->name.s[0]){
286 saveaddr = addr;
287 edit(f, 'I');
288 addr = saveaddr;
289 }else{
290 f->unread = 0;
291 f->cleanseq = f->seq;
294 fileupdate(f, TRUE, TRUE);
297 void
298 cmdupdate(void)
300 if(cmd && cmd->seq!=0){
301 fileupdate(cmd, FALSE, downloaded);
302 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
303 telldot(cmd);
307 void
308 delete(File *f)
310 if(downloaded && f->rasp)
311 outTs(Hclose, f->tag);
312 delfile(f);
313 if(f == curfile)
314 current(0);
317 void
318 update(void)
320 int i, anymod;
321 File *f;
323 settempfile();
324 for(anymod = i=0; i<tempfile.nused; i++){
325 f = tempfile.filepptr[i];
326 if(f==cmd) /* cmd gets done in main() */
327 continue;
328 if(f->deleted) {
329 delete(f);
330 continue;
332 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
333 anymod++;
334 if(f->rasp)
335 telldot(f);
337 if(anymod)
338 seq++;
341 File *
342 current(File *f)
344 return curfile = f;
347 void
348 edit(File *f, int cmd)
350 int empty = TRUE;
351 Posn p;
352 int nulls;
354 if(cmd == 'r')
355 logdelete(f, addr.r.p1, addr.r.p2);
356 if(cmd=='e' || cmd=='I'){
357 logdelete(f, (Posn)0, f->b.nc);
358 addr.r.p2 = f->b.nc;
359 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
360 empty = FALSE;
361 if((io = open(genc, OREAD))<0) {
362 if (curfile && curfile->unread)
363 curfile->unread = FALSE;
364 error_r(Eopen, genc);
366 p = readio(f, &nulls, empty, TRUE);
367 closeio((cmd=='e' || cmd=='I')? -1 : p);
368 if(cmd == 'r')
369 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
370 else
371 f->ndot.r.p1 = f->ndot.r.p2 = 0;
372 f->closeok = empty;
373 if (quitok)
374 quitok = empty;
375 else
376 quitok = FALSE;
377 state(f, empty && !nulls? Clean : Dirty);
378 if(empty && !nulls)
379 f->cleanseq = f->seq;
380 if(cmd == 'e')
381 filename(f);
384 int
385 getname(File *f, String *s, int save)
387 int c, i;
389 Strzero(&genstr);
390 if(genc){
391 free(genc);
392 genc = 0;
394 if(s==0 || (c = s->s[0])==0){ /* no name provided */
395 if(f)
396 Strduplstr(&genstr, &f->name);
397 goto Return;
399 if(c!=' ' && c!='\t')
400 error(Eblank);
401 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
403 while(s->s[i] > ' ')
404 Straddc(&genstr, s->s[i++]);
405 if(s->s[i])
406 error(Enewline);
407 fixname(&genstr);
408 if(f && (save || f->name.s[0]==0)){
409 logsetname(f, &genstr);
410 if(Strcmp(&f->name, &genstr)){
411 quitok = f->closeok = FALSE;
412 f->qidpath = 0;
413 f->mtime = 0;
414 state(f, Dirty); /* if it's 'e', fix later */
417 Return:
418 genc = Strtoc(&genstr);
419 i = genstr.n;
420 if(i && genstr.s[i-1]==0)
421 i--;
422 return i; /* strlen(name) */
425 void
426 filename(File *f)
428 if(genc)
429 free(genc);
430 genc = Strtoc(&genstr);
431 dprint("%c%c%c %s\n", " '"[f->mod],
432 "-+"[f->rasp!=0], " ."[f==curfile], genc);
435 void
436 undostep(File *f, int isundo)
438 uint p1, p2;
439 int mod;
441 mod = f->mod;
442 fileundo(f, isundo, 1, &p1, &p2, TRUE);
443 f->ndot = f->dot;
444 if(f->mod){
445 f->closeok = 0;
446 quitok = 0;
447 }else
448 f->closeok = 1;
450 if(f->mod != mod){
451 f->mod = mod;
452 if(mod)
453 mod = Clean;
454 else
455 mod = Dirty;
456 state(f, mod);
460 int
461 undo(int isundo)
463 File *f;
464 int i;
465 Mod max;
467 max = undoseq(curfile, isundo);
468 if(max == 0)
469 return 0;
470 settempfile();
471 for(i = 0; i<tempfile.nused; i++){
472 f = tempfile.filepptr[i];
473 if(f!=cmd && undoseq(f, isundo)==max)
474 undostep(f, isundo);
476 return 1;
479 int
480 readcmd(String *s)
482 int retcode;
484 if(flist != 0)
485 fileclose(flist);
486 flist = fileopen();
488 addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
489 retcode = plan9(flist, '<', s, FALSE);
490 fileupdate(flist, FALSE, FALSE);
491 flist->seq = 0;
492 if (flist->b.nc > BLOCKSIZE)
493 error(Etoolong);
494 Strzero(&genstr);
495 Strinsure(&genstr, flist->b.nc);
496 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
497 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
498 genstr.n = flist->b.nc;
499 Straddc(&genstr, '\0');
500 return retcode;
503 void
504 getcurwd(void)
506 String *t;
507 char buf[256];
509 buf[0] = 0;
510 getwd(buf, sizeof(buf));
511 t = tmpcstr(buf);
512 Strduplstr(&curwd, t);
513 freetmpstr(t);
514 if(curwd.n == 0)
515 warn(Wpwd);
516 else if(curwd.s[curwd.n-1] != '/')
517 Straddc(&curwd, '/');
520 void
521 cd(String *str)
523 int i, fd;
524 char *s;
525 File *f;
526 String owd;
528 getcurwd();
529 if(getname((File *)0, str, FALSE))
530 s = genc;
531 else
532 s = home;
533 if(chdir(s))
534 syserror("chdir");
535 fd = open("/dev/wdir", OWRITE);
536 if(fd > 0)
537 write(fd, s, strlen(s));
538 dprint("!\n");
539 Strinit(&owd);
540 Strduplstr(&owd, &curwd);
541 getcurwd();
542 settempfile();
543 for(i=0; i<tempfile.nused; i++){
544 f = tempfile.filepptr[i];
545 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
546 Strinsert(&f->name, &owd, (Posn)0);
547 fixname(&f->name);
548 sortname(f);
549 }else if(f != cmd && Strispre(&curwd, &f->name)){
550 fixname(&f->name);
551 sortname(f);
554 Strclose(&owd);
557 int
558 loadflist(String *s)
560 int c, i;
562 c = s->s[0];
563 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
565 if((c==' ' || c=='\t') && s->s[i]!='\n'){
566 if(s->s[i]=='<'){
567 Strdelete(s, 0L, (long)i+1);
568 readcmd(s);
569 }else{
570 Strzero(&genstr);
571 while((c = s->s[i++]) && c!='\n')
572 Straddc(&genstr, c);
573 Straddc(&genstr, '\0');
575 }else{
576 if(c != '\n')
577 error(Eblank);
578 Strdupl(&genstr, empty);
580 if(genc)
581 free(genc);
582 genc = Strtoc(&genstr);
583 return genstr.s[0];
586 File *
587 readflist(int readall, int delete)
589 Posn i;
590 int c;
591 File *f;
592 String t;
594 Strinit(&t);
595 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
596 Strdelete(&genstr, (Posn)0, i);
597 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
599 if(i >= genstr.n)
600 break;
601 Strdelete(&genstr, (Posn)0, i);
602 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
605 if(i == 0)
606 break;
607 genstr.s[i] = 0;
608 Strduplstr(&t, tmprstr(genstr.s, i+1));
609 fixname(&t);
610 f = lookfile(&t);
611 if(delete){
612 if(f == 0)
613 warn_S(Wfile, &t);
614 else
615 trytoclose(f);
616 }else if(f==0 && readall)
617 logsetname(f = newfile(), &t);
619 Strclose(&t);
620 return f;
623 File *
624 tofile(String *s)
626 File *f;
628 if(s->s[0] != ' ')
629 error(Eblank);
630 if(loadflist(s) == 0){
631 f = lookfile(&genstr); /* empty string ==> nameless file */
632 if(f == 0)
633 error_s(Emenu, genc);
634 }else if((f=readflist(FALSE, FALSE)) == 0)
635 error_s(Emenu, genc);
636 return current(f);
639 File *
640 getfile(String *s)
642 File *f;
644 if(loadflist(s) == 0)
645 logsetname(f = newfile(), &genstr);
646 else if((f=readflist(TRUE, FALSE)) == 0)
647 error(Eblank);
648 return current(f);
651 void
652 closefiles(File *f, String *s)
654 if(s->s[0] == 0){
655 if(f == 0)
656 error(Enofile);
657 trytoclose(f);
658 return;
660 if(s->s[0] != ' ')
661 error(Eblank);
662 if(loadflist(s) == 0)
663 error(Enewline);
664 readflist(FALSE, TRUE);
667 void
668 copy(File *f, Address addr2)
670 Posn p;
671 int ni;
672 for(p=addr.r.p1; p<addr.r.p2; p+=ni){
673 ni = addr.r.p2-p;
674 if(ni > BLOCKSIZE)
675 ni = BLOCKSIZE;
676 bufread(&f->b, p, genbuf, ni);
677 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
679 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
680 addr2.f->ndot.r.p1 = addr2.r.p2;
683 void
684 move(File *f, Address addr2)
686 if(addr.r.p2 <= addr2.r.p2){
687 logdelete(f, addr.r.p1, addr.r.p2);
688 copy(f, addr2);
689 }else if(addr.r.p1 >= addr2.r.p2){
690 copy(f, addr2);
691 logdelete(f, addr.r.p1, addr.r.p2);
692 }else
693 error(Eoverlap);
696 Posn
697 nlcount(File *f, Posn p0, Posn p1)
699 Posn nl = 0;
701 while(p0 < p1)
702 if(filereadc(f, p0++)=='\n')
703 nl++;
704 return nl;
707 void
708 printposn(File *f, int charsonly)
710 Posn l1, l2;
712 if(!charsonly){
713 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
714 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
715 /* check if addr ends with '\n' */
716 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
717 --l2;
718 dprint("%lud", l1);
719 if(l2 != l1)
720 dprint(",%lud", l2);
721 dprint("; ");
723 dprint("#%lud", addr.r.p1);
724 if(addr.r.p2 != addr.r.p1)
725 dprint(",#%lud", addr.r.p2);
726 dprint("\n");
729 void
730 settempfile(void)
732 if(tempfile.nalloc < file.nused){
733 free(tempfile.listptr);
734 tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nused);
735 tempfile.nalloc = file.nused;
737 tempfile.nused = file.nused;
738 memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*));