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