code: purgatorio

ref: 82b046f36f8084a22bbb5d71edd0edd9179561eb
dir: /appl/cmd/p.b/

View raw version
implement P;
# Original by Steve Arons, based on Plan 9 p

include "sys.m"; 
	sys: Sys;
	FD:	import Sys;
include "draw.m";
include "string.m";
	str: String;
include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;
include "sh.m";

stderr: ref FD;
outb, cons: ref Iobuf;
drawctxt: ref Draw->Context;

nlines := 22;	# 1/3rd 66-line nroff page (!)
progname := "p";

P: module
{
	init:  fn(ctxt:  ref Draw->Context, argv:  list of string);
};

usage()
{
	sys->fprint(stderr, "Usage: p [-number] [file...]\n");
	raise "fail:usage";
}

init(ctxt: ref Draw->Context, argv:  list of string)
{
	sys = load Sys Sys->PATH;
	bufio = load Bufio Bufio->PATH;
	if(bufio == nil)
		nomod(Bufio->PATH);
	str = load String String->PATH;
	if(str == nil)
		nomod(String->PATH);
	sys->pctl(Sys->FORKFD, nil);
	drawctxt = ctxt;

	stderr = sys->fildes(2);

	if((stdout := sys->fildes(1)) != nil)
		outb = bufio->fopen(stdout, bufio->OWRITE);
	if(outb == nil){
		sys->fprint(stderr, "p: can't open stdout: %r\n");
		raise "fail:stdout";
	}
	cons = bufio->open("/dev/cons", bufio->OREAD);
	if(cons == nil){
		sys->fprint(stderr, "p: can't open /dev/cons: %r\n");
		raise "fail:cons";
	}

	if(argv != nil){
		progname = hd argv;
		argv = tl argv;
		if(argv != nil){
			s := hd argv;
			if(len s > 1 && s[0] == '-'){
				(x, y) := str->toint(s[1:],10);
				if(y == "" && x > 0)
					nlines = x;
				else
					usage();
				argv = tl argv;
			}
		}
	}
	if(argv == nil)
		argv = "-" :: nil;
	for(; argv != nil; argv = tl argv){
		file := hd argv;
		fd: ref Sys->FD;
		if(file == "-"){
			file = "stdin";
			fd = sys->fildes(0);
		}else
			fd = sys->open(file, Sys->OREAD);
		if(fd == nil){
			sys->fprint(stderr, "%s: can't open %s: %r\n", progname, file);
			continue;
		}
		page(fd);
		fd = nil;
	}
}

nomod(m: string)
{
	sys->fprint(sys->fildes(2), "%s: can't load %s: %r\n", progname, m);
	raise "fail:load";
}

page(fd: ref Sys->FD)
{
	inb := bufio->fopen(fd, bufio->OREAD);
	nl := nlines;
	while((line := inb.gets('\n')) != nil){
		outb.puts(line);        
		if(--nl == 0){
			outb.flush();
			nl = nlines;
			pause();
		}
	}
	outb.flush();   
}

pause()
{
	for(;;){
		cmdline := cons.gets('\n');
		if(cmdline == nil || cmdline[0] == 'q') # catch ^d
			exit;
		else if(cmdline[0] == '!') {
			done := chan of int;
			spawn command(cmdline[1:], done);
			<-done;
		}else
			break;
	}
}

command(cmdline: string, done: chan of int)
{
	sh := load Sh Sh->PATH;
	if(sh == nil) {
		sys->fprint(stderr, "%s: can't load %s: %r\n", progname, Sh->PATH);
		done <-= 0;
		return;
	}
	sys->pctl(Sys->FORKFD, nil);
	sys->dup(cons.fd.fd, 0);
	sh->system(drawctxt, cmdline);
	done <-= 1;
}