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