xref: /plan9/sys/src/cmd/pr.c (revision 208510e168b9c00c3c6969f56b807dc03575167d)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <bio.h>
43e12c5d1SDavid du Colombier #include <ctype.h>
53e12c5d1SDavid du Colombier 
63e12c5d1SDavid du Colombier /*
73e12c5d1SDavid du Colombier  *	PR command (print files in pages and columns, with headings)
83e12c5d1SDavid du Colombier  *	2+head+2+page[56]+5
93e12c5d1SDavid du Colombier  */
103e12c5d1SDavid du Colombier 
11bd389b36SDavid du Colombier #define	ISPRINT(c)	((c) >= ' ')
123e12c5d1SDavid du Colombier #define ESC		'\033'
133e12c5d1SDavid du Colombier #define LENGTH		66
143e12c5d1SDavid du Colombier #define LINEW		72
153e12c5d1SDavid du Colombier #define NUMW		5
163e12c5d1SDavid du Colombier #define MARGIN		10
173e12c5d1SDavid du Colombier #define DEFTAB		8
18*208510e1SDavid du Colombier #define NFILES		20
193e12c5d1SDavid du Colombier #define HEAD		"%12.12s %4.4s  %s Page %d\n\n\n", date+4, date+24, head, Page
203e12c5d1SDavid du Colombier #define TOLOWER(c)	(isupper(c) ? tolower(c) : c)	/* ouch! */
213e12c5d1SDavid du Colombier #define cerror(S)	fprint(2, "pr: %s", S)
223e12c5d1SDavid du Colombier #define STDINNAME()	nulls
233e12c5d1SDavid du Colombier #define TTY		"/dev/cons", 0
243e12c5d1SDavid du Colombier #define PROMPT()	fprint(2, "\a") /* BEL */
253e12c5d1SDavid du Colombier #define TABS(N,C)	if((N = intopt(argv, &C)) < 0) N = DEFTAB
263e12c5d1SDavid du Colombier #define ETABS		(Inpos % Etabn)
27d9dc5dd1SDavid du Colombier #define ITABS		(Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
283e12c5d1SDavid du Colombier #define NSEPC		'\t'
293e12c5d1SDavid du Colombier #define EMPTY		14	/* length of " -- empty file" */
303e12c5d1SDavid du Colombier 
313e12c5d1SDavid du Colombier typedef	struct	Fils	Fils;
323e12c5d1SDavid du Colombier typedef	struct	Colp*	Colp;
333e12c5d1SDavid du Colombier typedef	struct	Err	Err;
343e12c5d1SDavid du Colombier 
353e12c5d1SDavid du Colombier struct	Fils
363e12c5d1SDavid du Colombier {
373e12c5d1SDavid du Colombier 	Biobuf*	f_f;
383e12c5d1SDavid du Colombier 	char*	f_name;
39bd389b36SDavid du Colombier 	long	f_nextc;
403e12c5d1SDavid du Colombier };
413e12c5d1SDavid du Colombier struct	Colp
423e12c5d1SDavid du Colombier {
43bd389b36SDavid du Colombier 	Rune*	c_ptr;
44bd389b36SDavid du Colombier 	Rune*	c_ptr0;
453e12c5d1SDavid du Colombier 	long	c_lno;
463e12c5d1SDavid du Colombier };
473e12c5d1SDavid du Colombier struct	Err
483e12c5d1SDavid du Colombier {
493e12c5d1SDavid du Colombier 	Err*	e_nextp;
503e12c5d1SDavid du Colombier 	char*	e_mess;
513e12c5d1SDavid du Colombier };
523e12c5d1SDavid du Colombier 
533e12c5d1SDavid du Colombier int	Balance = 0;
543e12c5d1SDavid du Colombier Biobuf	bout;
55bd389b36SDavid du Colombier Rune*	Bufend;
56bd389b36SDavid du Colombier Rune*	Buffer = 0;
57bd389b36SDavid du Colombier int	C = '\0';
58bd389b36SDavid du Colombier Colp	Colpts;
59bd389b36SDavid du Colombier int	Colw;
60bd389b36SDavid du Colombier int	Dblspace = 1;
61bd389b36SDavid du Colombier Err*	err = 0;
62bd389b36SDavid du Colombier int	error = 0;
63bd389b36SDavid du Colombier int	Etabc = '\t';
64bd389b36SDavid du Colombier int	Etabn = 0;
65bd389b36SDavid du Colombier Fils*	Files;
66bd389b36SDavid du Colombier int	Formfeed = 0;
67bd389b36SDavid du Colombier int	Fpage = 1;
68bd389b36SDavid du Colombier char*	Head = 0;
69bd389b36SDavid du Colombier int	Inpos;
70bd389b36SDavid du Colombier int	Itabc = '\t';
71bd389b36SDavid du Colombier int	Itabn = 0;
72bd389b36SDavid du Colombier Err*	Lasterr = (Err*)&err;
73bd389b36SDavid du Colombier int	Lcolpos;
74bd389b36SDavid du Colombier int	Len = LENGTH;
75bd389b36SDavid du Colombier int	Line;
76bd389b36SDavid du Colombier int	Linew = 0;
77bd389b36SDavid du Colombier long	Lnumb = 0;
78bd389b36SDavid du Colombier int	Margin = MARGIN;
79bd389b36SDavid du Colombier int	Multi = 0;
80bd389b36SDavid du Colombier int	Ncols = 1;
81bd389b36SDavid du Colombier int	Nfiles = 0;
82bd389b36SDavid du Colombier int	Nsepc = NSEPC;
83bd389b36SDavid du Colombier int	Nspace;
84bd389b36SDavid du Colombier char	nulls[] = "";
85bd389b36SDavid du Colombier int	Numw;
86bd389b36SDavid du Colombier int	Offset = 0;
87bd389b36SDavid du Colombier int	Outpos;
887dd7cddfSDavid du Colombier int	Padodd;
89bd389b36SDavid du Colombier int	Page;
90bd389b36SDavid du Colombier int	Pcolpos;
91bd389b36SDavid du Colombier int	Plength;
92bd389b36SDavid du Colombier int	Sepc = 0;
933e12c5d1SDavid du Colombier 
943e12c5d1SDavid du Colombier extern	int	atoix(char**);
95bd389b36SDavid du Colombier extern	void	balance(int);
963e12c5d1SDavid du Colombier extern	void	die(char*);
973e12c5d1SDavid du Colombier extern	void	errprint(void);
98bd389b36SDavid du Colombier extern	char*	ffiler(char*);
99bd389b36SDavid du Colombier extern	int	findopt(int, char**);
100bd389b36SDavid du Colombier extern	int	get(int);
101bd389b36SDavid du Colombier extern	void*	getspace(ulong);
102bd389b36SDavid du Colombier extern	int	intopt(char**, int*);
103bd389b36SDavid du Colombier extern	void	main(int, char**);
104bd389b36SDavid du Colombier extern	Biobuf*	mustopen(char*, Fils*);
105bd389b36SDavid du Colombier extern	void	nexbuf(void);
106bd389b36SDavid du Colombier extern	int	pr(char*);
107bd389b36SDavid du Colombier extern	void	put(long);
108bd389b36SDavid du Colombier extern	void	putpage(void);
109bd389b36SDavid du Colombier extern	void	putspace(void);
1103e12c5d1SDavid du Colombier 
1113e12c5d1SDavid du Colombier /*
1123e12c5d1SDavid du Colombier  * return date file was last modified
1133e12c5d1SDavid du Colombier  */
1143e12c5d1SDavid du Colombier char*
getdate(void)1153e12c5d1SDavid du Colombier getdate(void)
1163e12c5d1SDavid du Colombier {
1173e12c5d1SDavid du Colombier 	static char *now = 0;
1189a747e4fSDavid du Colombier 	static Dir *sbuf;
1199a747e4fSDavid du Colombier 	ulong mtime;
1203e12c5d1SDavid du Colombier 
1213e12c5d1SDavid du Colombier 	if(Nfiles > 1 || Files->f_name == nulls) {
1223e12c5d1SDavid du Colombier 		if(now == 0) {
1239a747e4fSDavid du Colombier 			mtime = time(0);
1249a747e4fSDavid du Colombier 			now = ctime(mtime);
1253e12c5d1SDavid du Colombier 		}
1263e12c5d1SDavid du Colombier 		return now;
1273e12c5d1SDavid du Colombier 	}
1289a747e4fSDavid du Colombier 	mtime = 0;
1299a747e4fSDavid du Colombier 	sbuf = dirstat(Files->f_name);
1309a747e4fSDavid du Colombier 	if(sbuf){
1319a747e4fSDavid du Colombier 		mtime = sbuf->mtime;
1329a747e4fSDavid du Colombier 		free(sbuf);
1339a747e4fSDavid du Colombier 	}
1349a747e4fSDavid du Colombier 	return ctime(mtime);
1353e12c5d1SDavid du Colombier }
1363e12c5d1SDavid du Colombier 
1373e12c5d1SDavid du Colombier char*
ffiler(char * s)1383e12c5d1SDavid du Colombier ffiler(char *s)
1393e12c5d1SDavid du Colombier {
1409a747e4fSDavid du Colombier 	return smprint("can't open %s\n", s);
1413e12c5d1SDavid du Colombier }
1423e12c5d1SDavid du Colombier 
1433e12c5d1SDavid du Colombier void
main(int argc,char * argv[])1443e12c5d1SDavid du Colombier main(int argc, char *argv[])
1453e12c5d1SDavid du Colombier {
1463e12c5d1SDavid du Colombier 	Fils fstr[NFILES];
1473e12c5d1SDavid du Colombier 	int nfdone = 0;
1483e12c5d1SDavid du Colombier 
1493e12c5d1SDavid du Colombier 	Binit(&bout, 1, OWRITE);
1503e12c5d1SDavid du Colombier 	Files = fstr;
1513e12c5d1SDavid du Colombier 	for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
1523e12c5d1SDavid du Colombier 		if(Multi == 'm') {
1533e12c5d1SDavid du Colombier 			if(Nfiles >= NFILES - 1)
1543e12c5d1SDavid du Colombier 				die("too many files");
1553e12c5d1SDavid du Colombier 			if(mustopen(*argv, &Files[Nfiles++]) == 0)
1563e12c5d1SDavid du Colombier 				nfdone++; /* suppress printing */
1573e12c5d1SDavid du Colombier 		} else {
1583e12c5d1SDavid du Colombier 			if(pr(*argv))
159219b2ee8SDavid du Colombier 				Bterm(Files->f_f);
1603e12c5d1SDavid du Colombier 			nfdone++;
1613e12c5d1SDavid du Colombier 		}
1623e12c5d1SDavid du Colombier 	if(!nfdone)			/* no files named, use stdin */
1633e12c5d1SDavid du Colombier 		pr(nulls);		/* on GCOS, use current file, if any */
1643e12c5d1SDavid du Colombier 	errprint();			/* print accumulated error reports */
1653e12c5d1SDavid du Colombier 	exits(error? "error": 0);
1663e12c5d1SDavid du Colombier }
1673e12c5d1SDavid du Colombier 
1683e12c5d1SDavid du Colombier int
findopt(int argc,char * argv[])1693e12c5d1SDavid du Colombier findopt(int argc, char *argv[])
1703e12c5d1SDavid du Colombier {
1713e12c5d1SDavid du Colombier 	char **eargv = argv;
1723e12c5d1SDavid du Colombier 	int eargc = 0, c;
1733e12c5d1SDavid du Colombier 
1743e12c5d1SDavid du Colombier 	while(--argc > 0) {
1753e12c5d1SDavid du Colombier 		switch(c = **++argv) {
1763e12c5d1SDavid du Colombier 		case '-':
177bd389b36SDavid du Colombier 			if((c = *++*argv) == '\0')
178bd389b36SDavid du Colombier 				break;
1793e12c5d1SDavid du Colombier 		case '+':
1803e12c5d1SDavid du Colombier 			do {
181bd389b36SDavid du Colombier 				if(isdigit(c)) {
182bd389b36SDavid du Colombier 					--*argv;
183bd389b36SDavid du Colombier 					Ncols = atoix(argv);
184bd389b36SDavid du Colombier 				} else
185bd389b36SDavid du Colombier 				switch(c = TOLOWER(c)) {
186bd389b36SDavid du Colombier 				case '+':
187bd389b36SDavid du Colombier 					if((Fpage = atoix(argv)) < 1)
1883e12c5d1SDavid du Colombier 						Fpage = 1;
1893e12c5d1SDavid du Colombier 					continue;
190bd389b36SDavid du Colombier 				case 'd':
191bd389b36SDavid du Colombier 					Dblspace = 2;
1923e12c5d1SDavid du Colombier 					continue;
193bd389b36SDavid du Colombier 				case 'e':
194bd389b36SDavid du Colombier 					TABS(Etabn, Etabc);
195bd389b36SDavid du Colombier 					continue;
196bd389b36SDavid du Colombier 				case 'f':
197bd389b36SDavid du Colombier 					Formfeed++;
198bd389b36SDavid du Colombier 					continue;
199bd389b36SDavid du Colombier 				case 'h':
200bd389b36SDavid du Colombier 					if(--argc > 0)
201bd389b36SDavid du Colombier 						Head = argv[1];
202bd389b36SDavid du Colombier 					continue;
203bd389b36SDavid du Colombier 				case 'i':
204bd389b36SDavid du Colombier 					TABS(Itabn, Itabc);
205bd389b36SDavid du Colombier 					continue;
206bd389b36SDavid du Colombier 				case 'l':
207bd389b36SDavid du Colombier 					Len = atoix(argv);
208bd389b36SDavid du Colombier 					continue;
2093e12c5d1SDavid du Colombier 				case 'a':
210bd389b36SDavid du Colombier 				case 'm':
211bd389b36SDavid du Colombier 					Multi = c;
212bd389b36SDavid du Colombier 					continue;
213bd389b36SDavid du Colombier 				case 'o':
214bd389b36SDavid du Colombier 					Offset = atoix(argv);
215bd389b36SDavid du Colombier 					continue;
2163e12c5d1SDavid du Colombier 				case 's':
2173e12c5d1SDavid du Colombier 					if((Sepc = (*argv)[1]) != '\0')
2183e12c5d1SDavid du Colombier 						++*argv;
2193e12c5d1SDavid du Colombier 					else
2203e12c5d1SDavid du Colombier 						Sepc = '\t';
2213e12c5d1SDavid du Colombier 					continue;
222bd389b36SDavid du Colombier 				case 't':
223bd389b36SDavid du Colombier 					Margin = 0;
2243e12c5d1SDavid du Colombier 					continue;
225bd389b36SDavid du Colombier 				case 'w':
226bd389b36SDavid du Colombier 					Linew = atoix(argv);
227bd389b36SDavid du Colombier 					continue;
2283e12c5d1SDavid du Colombier 				case 'n':
229bd389b36SDavid du Colombier 					Lnumb++;
2303e12c5d1SDavid du Colombier 					if((Numw = intopt(argv, &Nsepc)) <= 0)
2313e12c5d1SDavid du Colombier 						Numw = NUMW;
232bd389b36SDavid du Colombier 				case 'b':
233bd389b36SDavid du Colombier 					Balance = 1;
234bd389b36SDavid du Colombier 					continue;
2357dd7cddfSDavid du Colombier 				case 'p':
2367dd7cddfSDavid du Colombier 					Padodd = 1;
2377dd7cddfSDavid du Colombier 					continue;
238bd389b36SDavid du Colombier 				default:
239bd389b36SDavid du Colombier 					die("bad option");
2403e12c5d1SDavid du Colombier 				}
2413e12c5d1SDavid du Colombier 			} while((c = *++*argv) != '\0');
2423e12c5d1SDavid du Colombier 			if(Head == argv[1])
2433e12c5d1SDavid du Colombier 				argv++;
2443e12c5d1SDavid du Colombier 			continue;
2453e12c5d1SDavid du Colombier 		}
2463e12c5d1SDavid du Colombier 		*eargv++ = *argv;
247bd389b36SDavid du Colombier 		eargc++;
2483e12c5d1SDavid du Colombier 	}
2493e12c5d1SDavid du Colombier 	if(Len == 0)
2503e12c5d1SDavid du Colombier 		Len = LENGTH;
2513e12c5d1SDavid du Colombier 	if(Len <= Margin)
2523e12c5d1SDavid du Colombier 		Margin = 0;
2533e12c5d1SDavid du Colombier 	Plength = Len - Margin/2;
2543e12c5d1SDavid du Colombier 	if(Multi == 'm')
2553e12c5d1SDavid du Colombier 		Ncols = eargc;
2563e12c5d1SDavid du Colombier 	switch(Ncols) {
2573e12c5d1SDavid du Colombier 	case 0:
2583e12c5d1SDavid du Colombier 		Ncols = 1;
2593e12c5d1SDavid du Colombier 	case 1:
2603e12c5d1SDavid du Colombier 		break;
2613e12c5d1SDavid du Colombier 	default:
2623e12c5d1SDavid du Colombier 		if(Etabn == 0)		/* respect explicit tab specification */
2633e12c5d1SDavid du Colombier 			Etabn = DEFTAB;
2643e12c5d1SDavid du Colombier 	}
2653e12c5d1SDavid du Colombier 	if(Linew == 0)
2663e12c5d1SDavid du Colombier 		Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
2673e12c5d1SDavid du Colombier 	if(Lnumb)
2683e12c5d1SDavid du Colombier 		Linew -= Multi == 'm'? Numw: Numw*Ncols;
2693e12c5d1SDavid du Colombier 	if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
2703e12c5d1SDavid du Colombier 		die("width too small");
2713e12c5d1SDavid du Colombier 	if(Ncols != 1 && Multi == 0) {
2723e12c5d1SDavid du Colombier 		ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
273bd389b36SDavid du Colombier 		Buffer = getspace(buflen*sizeof(*Buffer));
2743e12c5d1SDavid du Colombier 		Bufend = &Buffer[buflen];
275bd389b36SDavid du Colombier 		Colpts = getspace((Ncols+1)*sizeof(*Colpts));
2763e12c5d1SDavid du Colombier 	}
2773e12c5d1SDavid du Colombier 	return eargc;
2783e12c5d1SDavid du Colombier }
2793e12c5d1SDavid du Colombier 
2803e12c5d1SDavid du Colombier int
intopt(char * argv[],int * optp)2813e12c5d1SDavid du Colombier intopt(char *argv[], int *optp)
2823e12c5d1SDavid du Colombier {
2833e12c5d1SDavid du Colombier 	int c;
2843e12c5d1SDavid du Colombier 
2853e12c5d1SDavid du Colombier 	if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
2863e12c5d1SDavid du Colombier 		*optp = c;
287bd389b36SDavid du Colombier 		(*argv)++;
2883e12c5d1SDavid du Colombier 	}
2893e12c5d1SDavid du Colombier 	c = atoix(argv);
2903e12c5d1SDavid du Colombier 	return c != 0? c: -1;
2913e12c5d1SDavid du Colombier }
2923e12c5d1SDavid du Colombier 
2933e12c5d1SDavid du Colombier int
pr(char * name)2943e12c5d1SDavid du Colombier pr(char *name)
2953e12c5d1SDavid du Colombier {
2963e12c5d1SDavid du Colombier 	char *date = 0, *head = 0;
2973e12c5d1SDavid du Colombier 
2983e12c5d1SDavid du Colombier 	if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
2993e12c5d1SDavid du Colombier 		return 0;
3003e12c5d1SDavid du Colombier 	if(Buffer)
3013e12c5d1SDavid du Colombier 		Bungetc(Files->f_f);
3023e12c5d1SDavid du Colombier 	if(Lnumb)
3033e12c5d1SDavid du Colombier 		Lnumb = 1;
3043e12c5d1SDavid du Colombier 	for(Page = 0;; putpage()) {
3053e12c5d1SDavid du Colombier 		if(C == -1)
3063e12c5d1SDavid du Colombier 			break;
3073e12c5d1SDavid du Colombier 		if(Buffer)
3083e12c5d1SDavid du Colombier 			nexbuf();
3093e12c5d1SDavid du Colombier 		Inpos = 0;
3103e12c5d1SDavid du Colombier 		if(get(0) == -1)
3113e12c5d1SDavid du Colombier 			break;
3123e12c5d1SDavid du Colombier 		Bflush(&bout);
313bd389b36SDavid du Colombier 		Page++;
314bd389b36SDavid du Colombier 		if(Page >= Fpage) {
3153e12c5d1SDavid du Colombier 			if(Margin == 0)
3163e12c5d1SDavid du Colombier 				continue;
3173e12c5d1SDavid du Colombier 			if(date == 0)
3183e12c5d1SDavid du Colombier 				date = getdate();
3193e12c5d1SDavid du Colombier 			if(head == 0)
3203e12c5d1SDavid du Colombier 				head = Head != 0 ? Head :
3213e12c5d1SDavid du Colombier 					Nfiles < 2? Files->f_name: nulls;
3223e12c5d1SDavid du Colombier 			Bprint(&bout, "\n\n");
3233e12c5d1SDavid du Colombier 			Nspace = Offset;
3243e12c5d1SDavid du Colombier 			putspace();
3253e12c5d1SDavid du Colombier 			Bprint(&bout, HEAD);
3263e12c5d1SDavid du Colombier 		}
3273e12c5d1SDavid du Colombier 	}
3287dd7cddfSDavid du Colombier 	if(Padodd && (Page&1) == 1) {
3297dd7cddfSDavid du Colombier 		Line = 0;
3307dd7cddfSDavid du Colombier 		if(Formfeed)
3317dd7cddfSDavid du Colombier 			put('\f');
3327dd7cddfSDavid du Colombier 		else
3337dd7cddfSDavid du Colombier 			while(Line < Len)
3347dd7cddfSDavid du Colombier 				put('\n');
3357dd7cddfSDavid du Colombier 	}
3363e12c5d1SDavid du Colombier 	C = '\0';
3373e12c5d1SDavid du Colombier 	return 1;
3383e12c5d1SDavid du Colombier }
3393e12c5d1SDavid du Colombier 
3403e12c5d1SDavid du Colombier void
putpage(void)3413e12c5d1SDavid du Colombier putpage(void)
3423e12c5d1SDavid du Colombier {
3433e12c5d1SDavid du Colombier 	int colno;
3443e12c5d1SDavid du Colombier 
3453e12c5d1SDavid du Colombier 	for(Line = Margin/2;; get(0)) {
3463e12c5d1SDavid du Colombier 		for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
3473e12c5d1SDavid du Colombier 			if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
3483e12c5d1SDavid du Colombier 				if(Page >= Fpage) {
3493e12c5d1SDavid du Colombier 					putspace();
3503e12c5d1SDavid du Colombier 					Bprint(&bout, "%*ld", Numw, Buffer?
3513e12c5d1SDavid du Colombier 						Colpts[colno].c_lno++: Lnumb);
3523e12c5d1SDavid du Colombier 					Outpos += Numw;
3533e12c5d1SDavid du Colombier 					put(Nsepc);
3543e12c5d1SDavid du Colombier 				}
355bd389b36SDavid du Colombier 				Lnumb++;
3563e12c5d1SDavid du Colombier 			}
357bd389b36SDavid du Colombier 			for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
3583e12c5d1SDavid du Colombier 					put(C);
359bd389b36SDavid du Colombier 			if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
360bd389b36SDavid du Colombier 				break;
3613e12c5d1SDavid du Colombier 			if(Sepc)
3623e12c5d1SDavid du Colombier 				put(Sepc);
3633e12c5d1SDavid du Colombier 			else
3643e12c5d1SDavid du Colombier 				if((Nspace += Colw - Lcolpos + 1) < 1)
3653e12c5d1SDavid du Colombier 					Nspace = 1;
3663e12c5d1SDavid du Colombier 		}
3673e12c5d1SDavid du Colombier 	/*
3683e12c5d1SDavid du Colombier 		if(C == -1) {
3693e12c5d1SDavid du Colombier 			if(Margin != 0)
3703e12c5d1SDavid du Colombier 				break;
3713e12c5d1SDavid du Colombier 			if(colno != 0)
3723e12c5d1SDavid du Colombier 				put('\n');
3733e12c5d1SDavid du Colombier 			return;
3743e12c5d1SDavid du Colombier 		}
3753e12c5d1SDavid du Colombier 	*/
3763e12c5d1SDavid du Colombier 		if(C == -1 && colno == 0) {
3773e12c5d1SDavid du Colombier 			if(Margin != 0)
3783e12c5d1SDavid du Colombier 				break;
3793e12c5d1SDavid du Colombier 			return;
3803e12c5d1SDavid du Colombier 		}
3813e12c5d1SDavid du Colombier 		if(C == '\f')
3823e12c5d1SDavid du Colombier 			break;
3833e12c5d1SDavid du Colombier 		put('\n');
3843e12c5d1SDavid du Colombier 		if(Dblspace == 2 && Line < Plength)
3853e12c5d1SDavid du Colombier 			put('\n');
3863e12c5d1SDavid du Colombier 		if(Line >= Plength)
3873e12c5d1SDavid du Colombier 			break;
3883e12c5d1SDavid du Colombier 	}
3893e12c5d1SDavid du Colombier 	if(Formfeed)
3903e12c5d1SDavid du Colombier 		put('\f');
3913e12c5d1SDavid du Colombier 	else
3923e12c5d1SDavid du Colombier 		while(Line < Len)
3933e12c5d1SDavid du Colombier 			put('\n');
3943e12c5d1SDavid du Colombier }
3953e12c5d1SDavid du Colombier 
3963e12c5d1SDavid du Colombier void
nexbuf(void)3973e12c5d1SDavid du Colombier nexbuf(void)
3983e12c5d1SDavid du Colombier {
399bd389b36SDavid du Colombier 	Rune *s = Buffer;
4003e12c5d1SDavid du Colombier 	Colp p = Colpts;
4013e12c5d1SDavid du Colombier 	int j, c, bline = 0;
4023e12c5d1SDavid du Colombier 
4033e12c5d1SDavid du Colombier 	for(;;) {
4043e12c5d1SDavid du Colombier 		p->c_ptr0 = p->c_ptr = s;
4053e12c5d1SDavid du Colombier 		if(p == &Colpts[Ncols])
4063e12c5d1SDavid du Colombier 			return;
4073e12c5d1SDavid du Colombier 		(p++)->c_lno = Lnumb + bline;
408bd389b36SDavid du Colombier 		for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
4093e12c5d1SDavid du Colombier 			for(Inpos = 0;;) {
410bd389b36SDavid du Colombier 				if((c = Bgetrune(Files->f_f)) == -1) {
411bd389b36SDavid du Colombier 					for(*s = -1; p <= &Colpts[Ncols]; p++)
4123e12c5d1SDavid du Colombier 						p->c_ptr0 = p->c_ptr = s;
4133e12c5d1SDavid du Colombier 					if(Balance)
4143e12c5d1SDavid du Colombier 						balance(bline);
4153e12c5d1SDavid du Colombier 					return;
4163e12c5d1SDavid du Colombier 				}
417bd389b36SDavid du Colombier 				if(ISPRINT(c))
418bd389b36SDavid du Colombier 					Inpos++;
4193e12c5d1SDavid du Colombier 				if(Inpos <= Colw || c == '\n') {
4203e12c5d1SDavid du Colombier 					*s = c;
4213e12c5d1SDavid du Colombier 					if(++s >= Bufend)
4223e12c5d1SDavid du Colombier 						die("page-buffer overflow");
4233e12c5d1SDavid du Colombier 				}
4243e12c5d1SDavid du Colombier 				if(c == '\n')
4253e12c5d1SDavid du Colombier 					break;
4263e12c5d1SDavid du Colombier 				switch(c) {
427bd389b36SDavid du Colombier 				case '\b':
428bd389b36SDavid du Colombier 					if(Inpos == 0)
429bd389b36SDavid du Colombier 						s--;
430bd389b36SDavid du Colombier 				case ESC:
431bd389b36SDavid du Colombier 					if(Inpos > 0)
432bd389b36SDavid du Colombier 						Inpos--;
4333e12c5d1SDavid du Colombier 				}
4343e12c5d1SDavid du Colombier 			}
4353e12c5d1SDavid du Colombier 	}
4363e12c5d1SDavid du Colombier }
4373e12c5d1SDavid du Colombier 
4383e12c5d1SDavid du Colombier /*
4393e12c5d1SDavid du Colombier  * line balancing for last page
4403e12c5d1SDavid du Colombier  */
4413e12c5d1SDavid du Colombier void
balance(int bline)4423e12c5d1SDavid du Colombier balance(int bline)
4433e12c5d1SDavid du Colombier {
444bd389b36SDavid du Colombier 	Rune *s = Buffer;
4453e12c5d1SDavid du Colombier 	Colp p = Colpts;
4463e12c5d1SDavid du Colombier 	int colno = 0, j, c, l;
4473e12c5d1SDavid du Colombier 
4483e12c5d1SDavid du Colombier 	c = bline % Ncols;
4493e12c5d1SDavid du Colombier 	l = (bline + Ncols - 1)/Ncols;
4503e12c5d1SDavid du Colombier 	bline = 0;
4513e12c5d1SDavid du Colombier 	do {
4523e12c5d1SDavid du Colombier 		for(j = 0; j < l; ++j)
453bd389b36SDavid du Colombier 			while(*s++ != '\n')
454bd389b36SDavid du Colombier 				;
4553e12c5d1SDavid du Colombier 		(++p)->c_lno = Lnumb + (bline += l);
4563e12c5d1SDavid du Colombier 		p->c_ptr0 = p->c_ptr = s;
4573e12c5d1SDavid du Colombier 		if(++colno == c)
458bd389b36SDavid du Colombier 			l--;
4593e12c5d1SDavid du Colombier 	} while(colno < Ncols - 1);
4603e12c5d1SDavid du Colombier }
4613e12c5d1SDavid du Colombier 
4623e12c5d1SDavid du Colombier int
get(int colno)4633e12c5d1SDavid du Colombier get(int colno)
4643e12c5d1SDavid du Colombier {
4653e12c5d1SDavid du Colombier 	static int peekc = 0;
4663e12c5d1SDavid du Colombier 	Colp p;
4673e12c5d1SDavid du Colombier 	Fils *q;
468bd389b36SDavid du Colombier 	long c;
4693e12c5d1SDavid du Colombier 
4703e12c5d1SDavid du Colombier 	if(peekc) {
4713e12c5d1SDavid du Colombier 		peekc = 0;
4723e12c5d1SDavid du Colombier 		c = Etabc;
473bd389b36SDavid du Colombier 	} else
4743e12c5d1SDavid du Colombier 	if(Buffer) {
4753e12c5d1SDavid du Colombier 		p = &Colpts[colno];
4763e12c5d1SDavid du Colombier 		if(p->c_ptr >= (p+1)->c_ptr0)
4773e12c5d1SDavid du Colombier 			c = -1;
4783e12c5d1SDavid du Colombier 		else
4793e12c5d1SDavid du Colombier 			if((c = *p->c_ptr) != -1)
480bd389b36SDavid du Colombier 				p->c_ptr++;
4813e12c5d1SDavid du Colombier 	} else
4823e12c5d1SDavid du Colombier 	if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
4833e12c5d1SDavid du Colombier 		for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
4843e12c5d1SDavid du Colombier 			;
4853e12c5d1SDavid du Colombier 		if(q >= Files)
4863e12c5d1SDavid du Colombier 			c = '\n';
4873e12c5d1SDavid du Colombier 	} else
488bd389b36SDavid du Colombier 		q->f_nextc = Bgetrune(q->f_f);
4893e12c5d1SDavid du Colombier 	if(Etabn != 0 && c == Etabc) {
490bd389b36SDavid du Colombier 		Inpos++;
4913e12c5d1SDavid du Colombier 		peekc = ETABS;
4923e12c5d1SDavid du Colombier 		c = ' ';
4933e12c5d1SDavid du Colombier 	} else
494bd389b36SDavid du Colombier 	if(ISPRINT(c))
495bd389b36SDavid du Colombier 		Inpos++;
4963e12c5d1SDavid du Colombier 	else
4973e12c5d1SDavid du Colombier 		switch(c) {
4983e12c5d1SDavid du Colombier 		case '\b':
4993e12c5d1SDavid du Colombier 		case ESC:
500bd389b36SDavid du Colombier 			if(Inpos > 0)
501bd389b36SDavid du Colombier 				Inpos--;
5023e12c5d1SDavid du Colombier 			break;
5033e12c5d1SDavid du Colombier 		case '\f':
504bd389b36SDavid du Colombier 			if(Ncols == 1)
505bd389b36SDavid du Colombier 				break;
5063e12c5d1SDavid du Colombier 			c = '\n';
5073e12c5d1SDavid du Colombier 		case '\n':
5083e12c5d1SDavid du Colombier 		case '\r':
5093e12c5d1SDavid du Colombier 			Inpos = 0;
5103e12c5d1SDavid du Colombier 		}
5113e12c5d1SDavid du Colombier 	return C = c;
5123e12c5d1SDavid du Colombier }
5133e12c5d1SDavid du Colombier 
5143e12c5d1SDavid du Colombier void
put(long c)515bd389b36SDavid du Colombier put(long c)
5163e12c5d1SDavid du Colombier {
5173e12c5d1SDavid du Colombier 	int move;
5183e12c5d1SDavid du Colombier 
5193e12c5d1SDavid du Colombier 	switch(c) {
5203e12c5d1SDavid du Colombier 	case ' ':
521bd389b36SDavid du Colombier 		Nspace++;
522bd389b36SDavid du Colombier 		Lcolpos++;
5233e12c5d1SDavid du Colombier 		return;
5243e12c5d1SDavid du Colombier 	case '\b':
5253e12c5d1SDavid du Colombier 		if(Lcolpos == 0)
5263e12c5d1SDavid du Colombier 			return;
5273e12c5d1SDavid du Colombier 		if(Nspace > 0) {
528bd389b36SDavid du Colombier 			Nspace--;
529bd389b36SDavid du Colombier 			Lcolpos--;
5303e12c5d1SDavid du Colombier 			return;
5313e12c5d1SDavid du Colombier 		}
5323e12c5d1SDavid du Colombier 		if(Lcolpos > Pcolpos) {
533bd389b36SDavid du Colombier 			Lcolpos--;
5343e12c5d1SDavid du Colombier 			return;
5353e12c5d1SDavid du Colombier 		}
5363e12c5d1SDavid du Colombier 	case ESC:
5373e12c5d1SDavid du Colombier 		move = -1;
5383e12c5d1SDavid du Colombier 		break;
5393e12c5d1SDavid du Colombier 	case '\n':
540bd389b36SDavid du Colombier 		Line++;
5413e12c5d1SDavid du Colombier 	case '\r':
5423e12c5d1SDavid du Colombier 	case '\f':
543bd389b36SDavid du Colombier 		Pcolpos = 0;
544bd389b36SDavid du Colombier 		Lcolpos = 0;
545bd389b36SDavid du Colombier 		Nspace = 0;
546bd389b36SDavid du Colombier 		Outpos = 0;
5473e12c5d1SDavid du Colombier 	default:
548bd389b36SDavid du Colombier 		move = (ISPRINT(c) != 0);
5493e12c5d1SDavid du Colombier 	}
5503e12c5d1SDavid du Colombier 	if(Page < Fpage)
5513e12c5d1SDavid du Colombier 		return;
5523e12c5d1SDavid du Colombier 	if(Lcolpos > 0 || move > 0)
5533e12c5d1SDavid du Colombier 		Lcolpos += move;
5543e12c5d1SDavid du Colombier 	if(Lcolpos <= Colw) {
5553e12c5d1SDavid du Colombier 		putspace();
556bd389b36SDavid du Colombier 		Bputrune(&bout, c);
5573e12c5d1SDavid du Colombier 		Pcolpos = Lcolpos;
5583e12c5d1SDavid du Colombier 		Outpos += move;
5593e12c5d1SDavid du Colombier 	}
5603e12c5d1SDavid du Colombier }
5613e12c5d1SDavid du Colombier 
5623e12c5d1SDavid du Colombier void
putspace(void)5633e12c5d1SDavid du Colombier putspace(void)
5643e12c5d1SDavid du Colombier {
5653e12c5d1SDavid du Colombier 	int nc;
5663e12c5d1SDavid du Colombier 
5673e12c5d1SDavid du Colombier 	for(; Nspace > 0; Outpos += nc, Nspace -= nc)
5683e12c5d1SDavid du Colombier 		if(ITABS)
5693e12c5d1SDavid du Colombier 			Bputc(&bout, Itabc);
5703e12c5d1SDavid du Colombier 		else {
5713e12c5d1SDavid du Colombier 			nc = 1;
5723e12c5d1SDavid du Colombier 			Bputc(&bout, ' ');
5733e12c5d1SDavid du Colombier 		}
5743e12c5d1SDavid du Colombier }
5753e12c5d1SDavid du Colombier 
5763e12c5d1SDavid du Colombier int
atoix(char ** p)5773e12c5d1SDavid du Colombier atoix(char **p)
5783e12c5d1SDavid du Colombier {
5793e12c5d1SDavid du Colombier 	int n = 0, c;
5803e12c5d1SDavid du Colombier 
5813e12c5d1SDavid du Colombier 	while(isdigit(c = *++*p))
5823e12c5d1SDavid du Colombier 		n = 10*n + c - '0';
583bd389b36SDavid du Colombier 	(*p)--;
5843e12c5d1SDavid du Colombier 	return n;
5853e12c5d1SDavid du Colombier }
5863e12c5d1SDavid du Colombier 
5873e12c5d1SDavid du Colombier /*
5883e12c5d1SDavid du Colombier  * Defer message about failure to open file to prevent messing up
5893e12c5d1SDavid du Colombier  * alignment of page with tear perforations or form markers.
5903e12c5d1SDavid du Colombier  * Treat empty file as special case and report as diagnostic.
5913e12c5d1SDavid du Colombier  */
5923e12c5d1SDavid du Colombier Biobuf*
mustopen(char * s,Fils * f)5933e12c5d1SDavid du Colombier mustopen(char *s, Fils *f)
5943e12c5d1SDavid du Colombier {
5959a747e4fSDavid du Colombier 	char *tmp;
5969a747e4fSDavid du Colombier 
5973e12c5d1SDavid du Colombier 	if(*s == '\0') {
5983e12c5d1SDavid du Colombier 		f->f_name = STDINNAME();
5993e12c5d1SDavid du Colombier 		f->f_f = malloc(sizeof(Biobuf));
6003e12c5d1SDavid du Colombier 		if(f->f_f == 0)
6013e12c5d1SDavid du Colombier 			cerror("no memory");
6023e12c5d1SDavid du Colombier 		Binit(f->f_f, 0, OREAD);
6033e12c5d1SDavid du Colombier 	} else
6043e12c5d1SDavid du Colombier 	if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
6059a747e4fSDavid du Colombier 		tmp = ffiler(f->f_name);
6069a747e4fSDavid du Colombier 		s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
6079a747e4fSDavid du Colombier 		free(tmp);
6083e12c5d1SDavid du Colombier 	}
6093e12c5d1SDavid du Colombier 	if(f->f_f != 0) {
610bd389b36SDavid du Colombier 		if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
6113e12c5d1SDavid du Colombier 			return f->f_f;
612bd389b36SDavid du Colombier 		sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
6133e12c5d1SDavid du Colombier 			"%s -- empty file\n", f->f_name);
614219b2ee8SDavid du Colombier 		Bterm(f->f_f);
6153e12c5d1SDavid du Colombier 	}
6163e12c5d1SDavid du Colombier 	error = 1;
6173e12c5d1SDavid du Colombier 	cerror(s);
618219b2ee8SDavid du Colombier 	fprint(2, "\n");
6193e12c5d1SDavid du Colombier 	return 0;
6203e12c5d1SDavid du Colombier }
6213e12c5d1SDavid du Colombier 
6223e12c5d1SDavid du Colombier void*
getspace(ulong n)6233e12c5d1SDavid du Colombier getspace(ulong n)
6243e12c5d1SDavid du Colombier {
6253e12c5d1SDavid du Colombier 	void *t;
6263e12c5d1SDavid du Colombier 
6273e12c5d1SDavid du Colombier 	if((t = malloc(n)) == 0)
6283e12c5d1SDavid du Colombier 		die("out of space");
6293e12c5d1SDavid du Colombier 	return t;
6303e12c5d1SDavid du Colombier }
6313e12c5d1SDavid du Colombier 
6323e12c5d1SDavid du Colombier void
die(char * s)6333e12c5d1SDavid du Colombier die(char *s)
6343e12c5d1SDavid du Colombier {
635bd389b36SDavid du Colombier 	error++;
6363e12c5d1SDavid du Colombier 	errprint();
6373e12c5d1SDavid du Colombier 	cerror(s);
6383e12c5d1SDavid du Colombier 	Bputc(&bout, '\n');
6393e12c5d1SDavid du Colombier 	exits("error");
6403e12c5d1SDavid du Colombier }
6413e12c5d1SDavid du Colombier 
6423e12c5d1SDavid du Colombier /*
6433e12c5d1SDavid du Colombier void
6443e12c5d1SDavid du Colombier onintr(void)
6453e12c5d1SDavid du Colombier {
646bd389b36SDavid du Colombier 	error++;
6473e12c5d1SDavid du Colombier 	errprint();
6483e12c5d1SDavid du Colombier 	exits("error");
6493e12c5d1SDavid du Colombier }
6503e12c5d1SDavid du Colombier /**/
6513e12c5d1SDavid du Colombier 
6523e12c5d1SDavid du Colombier /*
6533e12c5d1SDavid du Colombier  * print accumulated error reports
6543e12c5d1SDavid du Colombier  */
6553e12c5d1SDavid du Colombier void
errprint(void)6563e12c5d1SDavid du Colombier errprint(void)
6573e12c5d1SDavid du Colombier {
6583e12c5d1SDavid du Colombier 	Bflush(&bout);
6593e12c5d1SDavid du Colombier 	for(; err != 0; err = err->e_nextp) {
6603e12c5d1SDavid du Colombier 		cerror(err->e_mess);
6613e12c5d1SDavid du Colombier 		fprint(2, "\n");
6623e12c5d1SDavid du Colombier 	}
6633e12c5d1SDavid du Colombier }
664