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 *termargs[10], **ap;
47 ap = termargs;
48 *ap++ = "samterm";
49 ARGBEGIN{
50 case 'd':
51 dflag++;
52 break;
53 case 'r':
54 machine = EARGF(usage());
55 break;
56 case 'R':
57 Rflag++;
58 break;
59 case 't':
60 samterm = EARGF(usage());
61 break;
62 case 's':
63 rsamname = EARGF(usage());
64 break;
65 default:
66 dprint("sam: unknown flag %c\n", ARGC());
67 usage();
68 /* options for samterm */
69 case 'a':
70 *ap++ = "-a";
71 break;
72 case 'W':
73 *ap++ = "-W";
74 *ap++ = EARGF(usage());
75 break;
76 }ARGEND
77 *ap = nil;
79 Strinit(&cmdstr);
80 Strinit0(&lastpat);
81 Strinit0(&lastregexp);
82 Strinit0(&genstr);
83 Strinit0(&rhs);
84 Strinit0(&curwd);
85 tempfile.listptr = emalloc(1); /* so it can be freed later */
86 Strinit0(&plan9cmd);
87 home = getenv(HOME);
88 disk = diskinit();
89 if(home == 0)
90 home = "/";
91 if(!dflag)
92 startup(machine, Rflag, termargs, argv);
93 notify(notifyf);
94 getcurwd();
95 if(argc>0){
96 for(i=0; i<argc; i++){
97 if(!setjmp(mainloop)){
98 t = tmpcstr(argv[i]);
99 Straddc(t, '\0');
100 Strduplstr(&genstr, t);
101 freetmpstr(t);
102 fixname(&genstr);
103 logsetname(newfile(), &genstr);
106 }else if(!downloaded)
107 newfile();
108 seq++;
109 if(file.nused)
110 current(file.filepptr[0]);
111 setjmp(mainloop);
112 cmdloop();
113 trytoquit(); /* if we already q'ed, quitok will be TRUE */
114 exits(0);
115 return 0;
118 void
119 usage(void)
121 dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
122 exits("usage");
125 void
126 rescue(void)
128 int i, nblank = 0;
129 File *f;
130 char *c;
131 char buf[256];
132 char *root;
134 if(rescuing++)
135 return;
136 io = -1;
137 for(i=0; i<file.nused; i++){
138 f = file.filepptr[i];
139 if(f==cmd || f->b.nc==0 || !fileisdirty(f))
140 continue;
141 if(io == -1){
142 sprint(buf, "%s/sam.save", home);
143 io = create(buf, 1, 0777);
144 if(io<0)
145 return;
147 if(f->name.s[0]){
148 c = Strtoc(&f->name);
149 strncpy(buf, c, sizeof buf-1);
150 buf[sizeof buf-1] = 0;
151 free(c);
152 }else
153 sprint(buf, "nameless.%d", nblank++);
154 root = getenv("PLAN9");
155 if(root == nil)
156 root = "/usr/local/plan9";
157 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
158 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
159 writeio(f);
160 fprint(io, "\n---%s\n", (char *)buf);
164 void
165 panic(char *s)
167 int wasd;
169 if(!panicking++ && !setjmp(mainloop)){
170 wasd = downloaded;
171 downloaded = 0;
172 dprint("sam: panic: %s: %r\n", s);
173 if(wasd)
174 fprint(2, "sam: panic: %s: %r\n", s);
175 rescue();
176 abort();
180 void
181 hiccough(char *s)
183 File *f;
184 int i;
186 if(rescuing)
187 exits("rescue");
188 if(s)
189 dprint("%s\n", s);
190 resetcmd();
191 resetxec();
192 resetsys();
193 if(io > 0)
194 close(io);
196 /*
197 * back out any logged changes & restore old sequences
198 */
199 for(i=0; i<file.nused; i++){
200 f = file.filepptr[i];
201 if(f==cmd)
202 continue;
203 if(f->seq==seq){
204 bufdelete(&f->epsilon, 0, f->epsilon.nc);
205 f->seq = f->prevseq;
206 f->dot.r = f->prevdot;
207 f->mark = f->prevmark;
208 state(f, f->prevmod ? Dirty: Clean);
212 update();
213 if (curfile) {
214 if (curfile->unread)
215 curfile->unread = FALSE;
216 else if (downloaded)
217 outTs(Hcurrent, curfile->tag);
219 longjmp(mainloop, 1);
222 void
223 intr(void)
225 error(Eintr);
228 void
229 trytoclose(File *f)
231 char *t;
232 char buf[256];
234 if(f == cmd) /* possible? */
235 return;
236 if(f->deleted)
237 return;
238 if(fileisdirty(f) && !f->closeok){
239 f->closeok = TRUE;
240 if(f->name.s[0]){
241 t = Strtoc(&f->name);
242 strncpy(buf, t, sizeof buf-1);
243 free(t);
244 }else
245 strcpy(buf, "nameless file");
246 error_s(Emodified, buf);
248 f->deleted = TRUE;
251 void
252 trytoquit(void)
254 int c;
255 File *f;
257 if(!quitok){
258 for(c = 0; c<file.nused; c++){
259 f = file.filepptr[c];
260 if(f!=cmd && fileisdirty(f)){
261 quitok = TRUE;
262 eof = FALSE;
263 error(Echanges);
269 void
270 load(File *f)
272 Address saveaddr;
274 Strduplstr(&genstr, &f->name);
275 filename(f);
276 if(f->name.s[0]){
277 saveaddr = addr;
278 edit(f, 'I');
279 addr = saveaddr;
280 }else{
281 f->unread = 0;
282 f->cleanseq = f->seq;
285 fileupdate(f, TRUE, TRUE);
288 void
289 cmdupdate(void)
291 if(cmd && cmd->seq!=0){
292 fileupdate(cmd, FALSE, downloaded);
293 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
294 telldot(cmd);
298 void
299 delete(File *f)
301 if(downloaded && f->rasp)
302 outTs(Hclose, f->tag);
303 delfile(f);
304 if(f == curfile)
305 current(0);
308 void
309 update(void)
311 int i, anymod;
312 File *f;
314 settempfile();
315 for(anymod = i=0; i<tempfile.nused; i++){
316 f = tempfile.filepptr[i];
317 if(f==cmd) /* cmd gets done in main() */
318 continue;
319 if(f->deleted) {
320 delete(f);
321 continue;
323 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
324 anymod++;
325 if(f->rasp)
326 telldot(f);
328 if(anymod)
329 seq++;
332 File *
333 current(File *f)
335 return curfile = f;
338 void
339 edit(File *f, int cmd)
341 int empty = TRUE;
342 Posn p;
343 int nulls;
345 if(cmd == 'r')
346 logdelete(f, addr.r.p1, addr.r.p2);
347 if(cmd=='e' || cmd=='I'){
348 logdelete(f, (Posn)0, f->b.nc);
349 addr.r.p2 = f->b.nc;
350 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
351 empty = FALSE;
352 if((io = open(genc, OREAD))<0) {
353 if (curfile && curfile->unread)
354 curfile->unread = FALSE;
355 error_r(Eopen, genc);
357 p = readio(f, &nulls, empty, TRUE);
358 closeio((cmd=='e' || cmd=='I')? -1 : p);
359 if(cmd == 'r')
360 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
361 else
362 f->ndot.r.p1 = f->ndot.r.p2 = 0;
363 f->closeok = empty;
364 if (quitok)
365 quitok = empty;
366 else
367 quitok = FALSE;
368 state(f, empty && !nulls? Clean : Dirty);
369 if(empty && !nulls)
370 f->cleanseq = f->seq;
371 if(cmd == 'e')
372 filename(f);
375 int
376 getname(File *f, String *s, int save)
378 int c, i;
380 Strzero(&genstr);
381 if(genc){
382 free(genc);
383 genc = 0;
385 if(s==0 || (c = s->s[0])==0){ /* no name provided */
386 if(f)
387 Strduplstr(&genstr, &f->name);
388 goto Return;
390 if(c!=' ' && c!='\t')
391 error(Eblank);
392 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
394 while(s->s[i] > ' ')
395 Straddc(&genstr, s->s[i++]);
396 if(s->s[i])
397 error(Enewline);
398 fixname(&genstr);
399 if(f && (save || f->name.s[0]==0)){
400 logsetname(f, &genstr);
401 if(Strcmp(&f->name, &genstr)){
402 quitok = f->closeok = FALSE;
403 f->qidpath = 0;
404 f->mtime = 0;
405 state(f, Dirty); /* if it's 'e', fix later */
408 Return:
409 genc = Strtoc(&genstr);
410 i = genstr.n;
411 if(i && genstr.s[i-1]==0)
412 i--;
413 return i; /* strlen(name) */
416 void
417 filename(File *f)
419 if(genc)
420 free(genc);
421 genc = Strtoc(&genstr);
422 dprint("%c%c%c %s\n", " '"[f->mod],
423 "-+"[f->rasp!=0], " ."[f==curfile], genc);
426 void
427 undostep(File *f, int isundo)
429 uint p1, p2;
430 int mod;
432 mod = f->mod;
433 fileundo(f, isundo, 1, &p1, &p2, TRUE);
434 f->ndot = f->dot;
435 if(f->mod){
436 f->closeok = 0;
437 quitok = 0;
438 }else
439 f->closeok = 1;
441 if(f->mod != mod){
442 f->mod = mod;
443 if(mod)
444 mod = Clean;
445 else
446 mod = Dirty;
447 state(f, mod);
451 int
452 undo(int isundo)
454 File *f;
455 int i;
456 Mod max;
458 max = undoseq(curfile, isundo);
459 if(max == 0)
460 return 0;
461 settempfile();
462 for(i = 0; i<tempfile.nused; i++){
463 f = tempfile.filepptr[i];
464 if(f!=cmd && undoseq(f, isundo)==max)
465 undostep(f, isundo);
467 return 1;
470 int
471 readcmd(String *s)
473 int retcode;
475 if(flist != 0)
476 fileclose(flist);
477 flist = fileopen();
479 addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
480 retcode = plan9(flist, '<', s, FALSE);
481 fileupdate(flist, FALSE, FALSE);
482 flist->seq = 0;
483 if (flist->b.nc > BLOCKSIZE)
484 error(Etoolong);
485 Strzero(&genstr);
486 Strinsure(&genstr, flist->b.nc);
487 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
488 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
489 genstr.n = flist->b.nc;
490 Straddc(&genstr, '\0');
491 return retcode;
494 void
495 getcurwd(void)
497 String *t;
498 char buf[256];
500 buf[0] = 0;
501 getwd(buf, sizeof(buf));
502 t = tmpcstr(buf);
503 Strduplstr(&curwd, t);
504 freetmpstr(t);
505 if(curwd.n == 0)
506 warn(Wpwd);
507 else if(curwd.s[curwd.n-1] != '/')
508 Straddc(&curwd, '/');
511 void
512 cd(String *str)
514 int i, fd;
515 char *s;
516 File *f;
517 String owd;
519 getcurwd();
520 if(getname((File *)0, str, FALSE))
521 s = genc;
522 else
523 s = home;
524 if(chdir(s))
525 syserror("chdir");
526 fd = open("/dev/wdir", OWRITE);
527 if(fd > 0)
528 write(fd, s, strlen(s));
529 dprint("!\n");
530 Strinit(&owd);
531 Strduplstr(&owd, &curwd);
532 getcurwd();
533 settempfile();
534 for(i=0; i<tempfile.nused; i++){
535 f = tempfile.filepptr[i];
536 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
537 Strinsert(&f->name, &owd, (Posn)0);
538 fixname(&f->name);
539 sortname(f);
540 }else if(f != cmd && Strispre(&curwd, &f->name)){
541 fixname(&f->name);
542 sortname(f);
545 Strclose(&owd);
548 int
549 loadflist(String *s)
551 int c, i;
553 c = s->s[0];
554 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
556 if((c==' ' || c=='\t') && s->s[i]!='\n'){
557 if(s->s[i]=='<'){
558 Strdelete(s, 0L, (long)i+1);
559 readcmd(s);
560 }else{
561 Strzero(&genstr);
562 while((c = s->s[i++]) && c!='\n')
563 Straddc(&genstr, c);
564 Straddc(&genstr, '\0');
566 }else{
567 if(c != '\n')
568 error(Eblank);
569 Strdupl(&genstr, empty);
571 if(genc)
572 free(genc);
573 genc = Strtoc(&genstr);
574 return genstr.s[0];
577 File *
578 readflist(int readall, int delete)
580 Posn i;
581 int c;
582 File *f;
583 String t;
585 Strinit(&t);
586 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
587 Strdelete(&genstr, (Posn)0, i);
588 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
590 if(i >= genstr.n)
591 break;
592 Strdelete(&genstr, (Posn)0, i);
593 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
596 if(i == 0)
597 break;
598 genstr.s[i] = 0;
599 Strduplstr(&t, tmprstr(genstr.s, i+1));
600 fixname(&t);
601 f = lookfile(&t);
602 if(delete){
603 if(f == 0)
604 warn_S(Wfile, &t);
605 else
606 trytoclose(f);
607 }else if(f==0 && readall)
608 logsetname(f = newfile(), &t);
610 Strclose(&t);
611 return f;
614 File *
615 tofile(String *s)
617 File *f;
619 if(s->s[0] != ' ')
620 error(Eblank);
621 if(loadflist(s) == 0){
622 f = lookfile(&genstr); /* empty string ==> nameless file */
623 if(f == 0)
624 error_s(Emenu, genc);
625 }else if((f=readflist(FALSE, FALSE)) == 0)
626 error_s(Emenu, genc);
627 return current(f);
630 File *
631 getfile(String *s)
633 File *f;
635 if(loadflist(s) == 0)
636 logsetname(f = newfile(), &genstr);
637 else if((f=readflist(TRUE, FALSE)) == 0)
638 error(Eblank);
639 return current(f);
642 void
643 closefiles(File *f, String *s)
645 if(s->s[0] == 0){
646 if(f == 0)
647 error(Enofile);
648 trytoclose(f);
649 return;
651 if(s->s[0] != ' ')
652 error(Eblank);
653 if(loadflist(s) == 0)
654 error(Enewline);
655 readflist(FALSE, TRUE);
658 void
659 copy(File *f, Address addr2)
661 Posn p;
662 int ni;
663 for(p=addr.r.p1; p<addr.r.p2; p+=ni){
664 ni = addr.r.p2-p;
665 if(ni > BLOCKSIZE)
666 ni = BLOCKSIZE;
667 bufread(&f->b, p, genbuf, ni);
668 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
670 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
671 addr2.f->ndot.r.p1 = addr2.r.p2;
674 void
675 move(File *f, Address addr2)
677 if(addr.r.p2 <= addr2.r.p2){
678 logdelete(f, addr.r.p1, addr.r.p2);
679 copy(f, addr2);
680 }else if(addr.r.p1 >= addr2.r.p2){
681 copy(f, addr2);
682 logdelete(f, addr.r.p1, addr.r.p2);
683 }else
684 error(Eoverlap);
687 Posn
688 nlcount(File *f, Posn p0, Posn p1)
690 Posn nl = 0;
692 while(p0 < p1)
693 if(filereadc(f, p0++)=='\n')
694 nl++;
695 return nl;
698 void
699 printposn(File *f, int charsonly)
701 Posn l1, l2;
703 if(!charsonly){
704 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
705 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
706 /* check if addr ends with '\n' */
707 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
708 --l2;
709 dprint("%lud", l1);
710 if(l2 != l1)
711 dprint(",%lud", l2);
712 dprint("; ");
714 dprint("#%lud", addr.r.p1);
715 if(addr.r.p2 != addr.r.p1)
716 dprint(",#%lud", addr.r.p2);
717 dprint("\n");
720 void
721 settempfile(void)
723 if(tempfile.nalloc < file.nused){
724 free(tempfile.listptr);
725 tempfile.listptr = emalloc(sizeof(*tempfile.filepptr)*file.nused);
726 tempfile.nalloc = file.nused;
728 tempfile.nused = file.nused;
729 memmove(&tempfile.filepptr[0], &file.filepptr[0], file.nused*sizeof(File*));