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