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