plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

sam.c (12513B)


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