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