Commit Diff


commit - f80f53a9424c26f28c8819558541d19cd690a440
commit + 53db2d0f6bfc971f44183f360f3db5a72edd948a
blob - /dev/null
blob + 63a9db3f3edbe28f4ad432068c899d356a9a74cd (mode 644)
--- /dev/null
+++ src/cmd/p.c
@@ -0,0 +1,90 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define	DEF	22	/* lines in chunk: 3*DEF == 66, #lines per nroff page */
+
+Biobuf *cons;
+Biobuf bout;
+
+int pglen = DEF;
+
+void printfile(int);
+
+void
+main(int argc, char *argv[])
+{
+	int n;
+	int f;
+
+	if((cons = Bopen("/dev/cons", OREAD)) == 0) {
+		fprint(2, "p: can't open /dev/cons\n");
+		exits("missing /dev/cons");
+	}
+	Binit(&bout, 1, OWRITE);
+	n = 0;
+	while(argc > 1) {
+		--argc; argv++;
+		if(*argv[0] == '-'){
+			pglen = atoi(&argv[0][1]);
+			if(pglen <= 0)
+				pglen = DEF;
+		} else {
+			n++;
+			f = open(argv[0], OREAD);
+			if(f < 0){
+				fprint(2, "p: can't open %s\n", argv[0]);
+				continue;
+			}
+			printfile(f);
+			close(f);
+		}
+	}
+	if(n == 0)
+		printfile(0);
+	exits(0);
+}
+
+void
+printfile(int f)
+{
+	int i, j, n;
+	char *s, *cmd;
+	Biobuf *b;
+
+	b = malloc(sizeof(Biobuf));
+	Binit(b, f, OREAD);
+	for(;;){
+		for(i=1; i <= pglen; i++) {
+			s = Brdline(b, '\n');
+			if(s == 0){
+				n = Blinelen(b);
+				if(n > 0)	/* line too long for Brdline */
+					for(j=0; j<n; j++)
+						Bputc(&bout, Bgetc(b));
+				else{		/* true EOF */
+					free(b);
+					return;
+				}
+			}else{
+				Bwrite(&bout, s, Blinelen(b)-1);
+				if(i < pglen)
+					Bwrite(&bout, "\n", 1);
+			}
+		}
+		Bflush(&bout);
+	    getcmd:
+		cmd = Brdline(cons, '\n');
+		if(cmd == 0 || *cmd == 'q')
+			exits(0);
+		cmd[Blinelen(cons)-1] = 0;
+		if(*cmd == '!'){
+			if(fork() == 0){
+				dup(Bfildes(cons), 0);
+				execl("/bin/rc", "rc", "-c", cmd+1, 0);
+			}
+			waitpid();
+			goto getcmd;
+		}
+	}
+}
blob - /dev/null
blob + 934cfa5e7ffe05aed7643f847ceab10b72346c70 (mode 644)
--- /dev/null
+++ src/cmd/pr.c
@@ -0,0 +1,663 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+/*
+ *	PR command (print files in pages and columns, with headings)
+ *	2+head+2+page[56]+5
+ */
+
+#define	ISPRINT(c)	((c) >= ' ')
+#define ESC		'\033'
+#define LENGTH		66
+#define LINEW		72
+#define NUMW		5
+#define MARGIN		10
+#define DEFTAB		8
+#define NFILES		10
+#define HEAD		"%12.12s %4.4s  %s Page %d\n\n\n", date+4, date+24, head, Page
+#define TOLOWER(c)	(isupper(c) ? tolower(c) : c)	/* ouch! */
+#define cerror(S)	fprint(2, "pr: %s", S)
+#define STDINNAME()	nulls
+#define TTY		"/dev/cons", 0
+#define PROMPT()	fprint(2, "\a") /* BEL */
+#define TABS(N,C)	if((N = intopt(argv, &C)) < 0) N = DEFTAB
+#define ETABS		(Inpos % Etabn)
+#define ITABS		(Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
+#define NSEPC		'\t'
+#define EMPTY		14	/* length of " -- empty file" */
+
+typedef	struct	Fils	Fils;
+typedef	struct	Colp*	Colp;
+typedef	struct	Err	Err;
+
+struct	Fils
+{
+	Biobuf*	f_f;
+	char*	f_name;
+	long	f_nextc;
+};
+struct	Colp
+{
+	Rune*	c_ptr;
+	Rune*	c_ptr0;
+	long	c_lno;
+};
+struct	Err
+{
+	Err*	e_nextp;
+	char*	e_mess;
+};
+
+int	Balance = 0;
+Biobuf	bout;
+Rune*	Bufend;
+Rune*	Buffer = 0;
+int	C = '\0';
+Colp	Colpts;
+int	Colw;
+int	Dblspace = 1;
+Err*	err = 0;
+int	error = 0;
+int	Etabc = '\t';
+int	Etabn = 0;
+Fils*	Files;
+int	Formfeed = 0;
+int	Fpage = 1;
+char*	Head = 0;
+int	Inpos;
+int	Itabc = '\t';
+int	Itabn = 0;
+Err*	Lasterr = (Err*)&err;
+int	Lcolpos;
+int	Len = LENGTH;
+int	Line;
+int	Linew = 0;
+long	Lnumb = 0;
+int	Margin = MARGIN;
+int	Multi = 0;
+int	Ncols = 1;
+int	Nfiles = 0;
+int	Nsepc = NSEPC;
+int	Nspace;
+char	nulls[] = "";
+int	Numw;
+int	Offset = 0;
+int	Outpos;
+int	Padodd;
+int	Page;
+int	Pcolpos;
+int	Plength;
+int	Sepc = 0;
+
+extern	int	atoix(char**);
+extern	void	balance(int);
+extern	void	die(char*);
+extern	void	errprint(void);
+extern	char*	ffiler(char*);
+extern	int	findopt(int, char**);
+extern	int	get(int);
+extern	void*	getspace(ulong);
+extern	int	intopt(char**, int*);
+extern	void	main(int, char**);
+extern	Biobuf*	mustopen(char*, Fils*);
+extern	void	nexbuf(void);
+extern	int	pr(char*);
+extern	void	put(long);
+extern	void	putpage(void);
+extern	void	putspace(void);
+
+/*
+ * return date file was last modified
+ */
+char*
+getdate(void)
+{
+	static char *now = 0;
+	static Dir *sbuf;
+	ulong mtime;
+
+	if(Nfiles > 1 || Files->f_name == nulls) {
+		if(now == 0) {
+			mtime = time(0);
+			now = ctime(mtime);
+		}
+		return now;
+	}
+	mtime = 0;
+	sbuf = dirstat(Files->f_name);
+	if(sbuf){
+		mtime = sbuf->mtime;
+		free(sbuf);
+	}
+	return ctime(mtime);
+}
+
+char*
+ffiler(char *s)
+{
+	return smprint("can't open %s\n", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+	Fils fstr[NFILES];
+	int nfdone = 0;
+
+	Binit(&bout, 1, OWRITE);
+	Files = fstr;
+	for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
+		if(Multi == 'm') {
+			if(Nfiles >= NFILES - 1)
+				die("too many files");
+			if(mustopen(*argv, &Files[Nfiles++]) == 0)
+				nfdone++; /* suppress printing */
+		} else {
+			if(pr(*argv))
+				Bterm(Files->f_f);
+			nfdone++;
+		}
+	if(!nfdone)			/* no files named, use stdin */
+		pr(nulls);		/* on GCOS, use current file, if any */
+	errprint();			/* print accumulated error reports */
+	exits(error? "error": 0);
+}
+
+int
+findopt(int argc, char *argv[])
+{
+	char **eargv = argv;
+	int eargc = 0, c;
+
+	while(--argc > 0) {
+		switch(c = **++argv) {
+		case '-':
+			if((c = *++*argv) == '\0')
+				break;
+		case '+':
+			do {
+				if(isdigit(c)) {
+					--*argv;
+					Ncols = atoix(argv);
+				} else
+				switch(c = TOLOWER(c)) {
+				case '+':
+					if((Fpage = atoix(argv)) < 1)
+						Fpage = 1;
+					continue;
+				case 'd':
+					Dblspace = 2;
+					continue;
+				case 'e':
+					TABS(Etabn, Etabc);
+					continue;
+				case 'f':
+					Formfeed++;
+					continue;
+				case 'h':
+					if(--argc > 0)
+						Head = argv[1];
+					continue;
+				case 'i':
+					TABS(Itabn, Itabc);
+					continue;
+				case 'l':
+					Len = atoix(argv);
+					continue;
+				case 'a':
+				case 'm':
+					Multi = c;
+					continue;
+				case 'o':
+					Offset = atoix(argv);
+					continue;
+				case 's':
+					if((Sepc = (*argv)[1]) != '\0')
+						++*argv;
+					else
+						Sepc = '\t';
+					continue;
+				case 't':
+					Margin = 0;
+					continue;
+				case 'w':
+					Linew = atoix(argv);
+					continue;
+				case 'n':
+					Lnumb++;
+					if((Numw = intopt(argv, &Nsepc)) <= 0)
+						Numw = NUMW;
+				case 'b':
+					Balance = 1;
+					continue;
+				case 'p':
+					Padodd = 1;
+					continue;
+				default:
+					die("bad option");
+				}
+			} while((c = *++*argv) != '\0');
+			if(Head == argv[1])
+				argv++;
+			continue;
+		}
+		*eargv++ = *argv;
+		eargc++;
+	}
+	if(Len == 0)
+		Len = LENGTH;
+	if(Len <= Margin)
+		Margin = 0;
+	Plength = Len - Margin/2;
+	if(Multi == 'm')
+		Ncols = eargc;
+	switch(Ncols) {
+	case 0:
+		Ncols = 1;
+	case 1:
+		break;
+	default:
+		if(Etabn == 0)		/* respect explicit tab specification */
+			Etabn = DEFTAB;
+	}
+	if(Linew == 0)
+		Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
+	if(Lnumb)
+		Linew -= Multi == 'm'? Numw: Numw*Ncols;
+	if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
+		die("width too small");
+	if(Ncols != 1 && Multi == 0) {
+		ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
+		Buffer = getspace(buflen*sizeof(*Buffer));
+		Bufend = &Buffer[buflen];
+		Colpts = getspace((Ncols+1)*sizeof(*Colpts));
+	}
+	return eargc;
+}
+
+int
+intopt(char *argv[], int *optp)
+{
+	int c;
+
+	if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
+		*optp = c;
+		(*argv)++;
+	}
+	c = atoix(argv);
+	return c != 0? c: -1;
+}
+
+int
+pr(char *name)
+{
+	char *date = 0, *head = 0;
+
+	if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
+		return 0;
+	if(Buffer)
+		Bungetc(Files->f_f);
+	if(Lnumb)
+		Lnumb = 1;
+	for(Page = 0;; putpage()) {
+		if(C == -1)
+			break;
+		if(Buffer)
+			nexbuf();
+		Inpos = 0;
+		if(get(0) == -1)
+			break;
+		Bflush(&bout);
+		Page++;
+		if(Page >= Fpage) {
+			if(Margin == 0)
+				continue;
+			if(date == 0)
+				date = getdate();
+			if(head == 0)
+				head = Head != 0 ? Head :
+					Nfiles < 2? Files->f_name: nulls;
+			Bprint(&bout, "\n\n");
+			Nspace = Offset;
+			putspace();
+			Bprint(&bout, HEAD);
+		}
+	}
+	if(Padodd && (Page&1) == 1) {
+		Line = 0;
+		if(Formfeed)
+			put('\f');
+		else
+			while(Line < Len)
+				put('\n');
+	}
+	C = '\0';
+	return 1;
+}
+
+void
+putpage(void)
+{
+	int colno;
+
+	for(Line = Margin/2;; get(0)) {
+		for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
+			if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
+				if(Page >= Fpage) {
+					putspace();
+					Bprint(&bout, "%*ld", Numw, Buffer?
+						Colpts[colno].c_lno++: Lnumb);
+					Outpos += Numw;
+					put(Nsepc);
+				}
+				Lnumb++;
+			}
+			for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
+					put(C);
+			if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
+				break;
+			if(Sepc)
+				put(Sepc);
+			else
+				if((Nspace += Colw - Lcolpos + 1) < 1)
+					Nspace = 1;
+		}
+	/*
+		if(C == -1) {
+			if(Margin != 0)
+				break;
+			if(colno != 0)
+				put('\n');
+			return;
+		}
+	*/
+		if(C == -1 && colno == 0) {
+			if(Margin != 0)
+				break;
+			return;
+		}
+		if(C == '\f')
+			break;
+		put('\n');
+		if(Dblspace == 2 && Line < Plength)
+			put('\n');
+		if(Line >= Plength)
+			break;
+	}
+	if(Formfeed)
+		put('\f');
+	else
+		while(Line < Len)
+			put('\n');
+}
+
+void
+nexbuf(void)
+{
+	Rune *s = Buffer;
+	Colp p = Colpts;
+	int j, c, bline = 0;
+
+	for(;;) {
+		p->c_ptr0 = p->c_ptr = s;
+		if(p == &Colpts[Ncols])
+			return;
+		(p++)->c_lno = Lnumb + bline;
+		for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
+			for(Inpos = 0;;) {
+				if((c = Bgetrune(Files->f_f)) == -1) {
+					for(*s = -1; p <= &Colpts[Ncols]; p++)
+						p->c_ptr0 = p->c_ptr = s;
+					if(Balance)
+						balance(bline);
+					return;
+				}
+				if(ISPRINT(c))
+					Inpos++;
+				if(Inpos <= Colw || c == '\n') {
+					*s = c;
+					if(++s >= Bufend)
+						die("page-buffer overflow");
+				}
+				if(c == '\n')
+					break;
+				switch(c) {
+				case '\b':
+					if(Inpos == 0)
+						s--;
+				case ESC:
+					if(Inpos > 0)
+						Inpos--;
+				}
+			}
+	}
+}
+
+/*
+ * line balancing for last page
+ */
+void
+balance(int bline)
+{
+	Rune *s = Buffer;
+	Colp p = Colpts;
+	int colno = 0, j, c, l;
+
+	c = bline % Ncols;
+	l = (bline + Ncols - 1)/Ncols;
+	bline = 0;
+	do {
+		for(j = 0; j < l; ++j)
+			while(*s++ != '\n')
+				;
+		(++p)->c_lno = Lnumb + (bline += l);
+		p->c_ptr0 = p->c_ptr = s;
+		if(++colno == c)
+			l--;
+	} while(colno < Ncols - 1);
+}
+
+int
+get(int colno)
+{
+	static int peekc = 0;
+	Colp p;
+	Fils *q;
+	long c;
+
+	if(peekc) {
+		peekc = 0;
+		c = Etabc;
+	} else
+	if(Buffer) {
+		p = &Colpts[colno];
+		if(p->c_ptr >= (p+1)->c_ptr0)
+			c = -1;
+		else
+			if((c = *p->c_ptr) != -1)
+				p->c_ptr++;
+	} else
+	if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
+		for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
+			;
+		if(q >= Files)
+			c = '\n';
+	} else
+		q->f_nextc = Bgetrune(q->f_f);
+	if(Etabn != 0 && c == Etabc) {
+		Inpos++;
+		peekc = ETABS;
+		c = ' ';
+	} else
+	if(ISPRINT(c))
+		Inpos++;
+	else
+		switch(c) {
+		case '\b':
+		case ESC:
+			if(Inpos > 0)
+				Inpos--;
+			break;
+		case '\f':
+			if(Ncols == 1)
+				break;
+			c = '\n';
+		case '\n':
+		case '\r':
+			Inpos = 0;
+		}
+	return C = c;
+}
+
+void
+put(long c)
+{
+	int move;
+
+	switch(c) {
+	case ' ':
+		Nspace++;
+		Lcolpos++;
+		return;
+	case '\b':
+		if(Lcolpos == 0)
+			return;
+		if(Nspace > 0) {
+			Nspace--;
+			Lcolpos--;
+			return;
+		}
+		if(Lcolpos > Pcolpos) {
+			Lcolpos--;
+			return;
+		}
+	case ESC:
+		move = -1;
+		break;
+	case '\n':
+		Line++;
+	case '\r':
+	case '\f':
+		Pcolpos = 0;
+		Lcolpos = 0;
+		Nspace = 0;
+		Outpos = 0;
+	default:
+		move = (ISPRINT(c) != 0);
+	}
+	if(Page < Fpage)
+		return;
+	if(Lcolpos > 0 || move > 0)
+		Lcolpos += move;
+	if(Lcolpos <= Colw) {
+		putspace();
+		Bputrune(&bout, c);
+		Pcolpos = Lcolpos;
+		Outpos += move;
+	}
+}
+
+void
+putspace(void)
+{
+	int nc;
+
+	for(; Nspace > 0; Outpos += nc, Nspace -= nc)
+		if(ITABS)
+			Bputc(&bout, Itabc);
+		else {
+			nc = 1;
+			Bputc(&bout, ' ');
+		}
+}
+
+int
+atoix(char **p)
+{
+	int n = 0, c;
+
+	while(isdigit(c = *++*p))
+		n = 10*n + c - '0';
+	(*p)--;
+	return n;
+}
+
+/*
+ * Defer message about failure to open file to prevent messing up
+ * alignment of page with tear perforations or form markers.
+ * Treat empty file as special case and report as diagnostic.
+ */
+Biobuf*
+mustopen(char *s, Fils *f)
+{
+	char *tmp;
+
+	if(*s == '\0') {
+		f->f_name = STDINNAME();
+		f->f_f = malloc(sizeof(Biobuf));
+		if(f->f_f == 0)
+			cerror("no memory");
+		Binit(f->f_f, 0, OREAD);
+	} else
+	if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
+		tmp = ffiler(f->f_name);
+		s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
+		free(tmp);
+	}
+	if(f->f_f != 0) {
+		if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
+			return f->f_f;
+		sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
+			"%s -- empty file\n", f->f_name);
+		Bterm(f->f_f);
+	}
+	error = 1;
+	cerror(s);
+	fprint(2, "\n");
+	return 0;
+}
+
+void*
+getspace(ulong n)
+{
+	void *t;
+
+	if((t = malloc(n)) == 0)
+		die("out of space");
+	return t;
+}
+
+void
+die(char *s)
+{
+	error++;
+	errprint();
+	cerror(s);
+	Bputc(&bout, '\n');
+	exits("error");
+}
+
+/*
+void
+onintr(void)
+{
+	error++;
+	errprint();
+	exits("error");
+}
+/**/
+
+/*
+ * print accumulated error reports
+ */
+void
+errprint(void)
+{
+	Bflush(&bout);
+	for(; err != 0; err = err->e_nextp) {
+		cerror(err->e_mess);
+		fprint(2, "\n");
+	}
+}