9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

sam.c (12567B)


      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 = getenv("PLAN9");
    156 		if(root == nil)
    157 			root = "/usr/local/plan9";
    158 		fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
    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)!=0))
    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 blank */
    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'; i++)
    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 ==> nameless file */
    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, ni);
    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, addr.r.p2-1)=='\n')
    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.nused);
    740 	tempfile.nused = file.nused;
    741 }