xref: /plan9/sys/src/cmd/sam/sam.c (revision 73e742d79f6b0cfc24f3b01d7ade790955db63c2)
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