Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
#include <ctype.h>
5
 
6
/*
7
 *	PR command (print files in pages and columns, with headings)
8
 *	2+head+2+page[56]+5
9
 */
10
 
11
#define	ISPRINT(c)	((c) >= ' ')
12
#define ESC		'\033'
13
#define LENGTH		66
14
#define LINEW		72
15
#define NUMW		5
16
#define MARGIN		10
17
#define DEFTAB		8
18
#define NFILES		20
19
#define HEAD		"%12.12s %4.4s  %s Page %d\n\n\n", date+4, date+24, head, Page
20
#define TOLOWER(c)	(isupper(c) ? tolower(c) : c)	/* ouch! */
21
#define cerror(S)	fprint(2, "pr: %s", S)
22
#define STDINNAME()	nulls
23
#define TTY		"/dev/cons", 0
24
#define PROMPT()	fprint(2, "\a") /* BEL */
25
#define TABS(N,C)	if((N = intopt(argv, &C)) < 0) N = DEFTAB
26
#define ETABS		(Inpos % Etabn)
27
#define ITABS		(Itabn > 0 && Nspace > 1 && Nspace >= (nc = Itabn - Outpos % Itabn))
28
#define NSEPC		'\t'
29
#define EMPTY		14	/* length of " -- empty file" */
30
 
31
typedef	struct	Fils	Fils;
32
typedef	struct	Colp*	Colp;
33
typedef	struct	Err	Err;
34
 
35
struct	Fils
36
{
37
	Biobuf*	f_f;
38
	char*	f_name;
39
	long	f_nextc;
40
};
41
struct	Colp
42
{
43
	Rune*	c_ptr;
44
	Rune*	c_ptr0;
45
	long	c_lno;
46
};
47
struct	Err
48
{
49
	Err*	e_nextp;
50
	char*	e_mess;
51
};
52
 
53
int	Balance = 0;
54
Biobuf	bout;
55
Rune*	Bufend;
56
Rune*	Buffer = 0;
57
int	C = '\0';
58
Colp	Colpts;
59
int	Colw;
60
int	Dblspace = 1;
61
Err*	err = 0;
62
int	error = 0;
63
int	Etabc = '\t';
64
int	Etabn = 0;
65
Fils*	Files;
66
int	Formfeed = 0;
67
int	Fpage = 1;
68
char*	Head = 0;
69
int	Inpos;
70
int	Itabc = '\t';
71
int	Itabn = 0;
72
Err*	Lasterr = (Err*)&err;
73
int	Lcolpos;
74
int	Len = LENGTH;
75
int	Line;
76
int	Linew = 0;
77
long	Lnumb = 0;
78
int	Margin = MARGIN;
79
int	Multi = 0;
80
int	Ncols = 1;
81
int	Nfiles = 0;
82
int	Nsepc = NSEPC;
83
int	Nspace;
84
char	nulls[] = "";
85
int	Numw;
86
int	Offset = 0;
87
int	Outpos;
88
int	Padodd;
89
int	Page;
90
int	Pcolpos;
91
int	Plength;
92
int	Sepc = 0;
93
 
94
extern	int	atoix(char**);
95
extern	void	balance(int);
96
extern	void	die(char*);
97
extern	void	errprint(void);
98
extern	char*	ffiler(char*);
99
extern	int	findopt(int, char**);
100
extern	int	get(int);
101
extern	void*	getspace(ulong);
102
extern	int	intopt(char**, int*);
103
extern	void	main(int, char**);
104
extern	Biobuf*	mustopen(char*, Fils*);
105
extern	void	nexbuf(void);
106
extern	int	pr(char*);
107
extern	void	put(long);
108
extern	void	putpage(void);
109
extern	void	putspace(void);
110
 
111
/*
112
 * return date file was last modified
113
 */
114
char*
115
getdate(void)
116
{
117
	static char *now = 0;
118
	static Dir *sbuf;
119
	ulong mtime;
120
 
121
	if(Nfiles > 1 || Files->f_name == nulls) {
122
		if(now == 0) {
123
			mtime = time(0);
124
			now = ctime(mtime);
125
		}
126
		return now;
127
	}
128
	mtime = 0;
129
	sbuf = dirstat(Files->f_name);
130
	if(sbuf){
131
		mtime = sbuf->mtime;
132
		free(sbuf);
133
	}
134
	return ctime(mtime);
135
}
136
 
137
char*
138
ffiler(char *s)
139
{
140
	return smprint("can't open %s\n", s);
141
}
142
 
143
void
144
main(int argc, char *argv[])
145
{
146
	Fils fstr[NFILES];
147
	int nfdone = 0;
148
 
149
	Binit(&bout, 1, OWRITE);
150
	Files = fstr;
151
	for(argc = findopt(argc, argv); argc > 0; --argc, ++argv)
152
		if(Multi == 'm') {
153
			if(Nfiles >= NFILES - 1)
154
				die("too many files");
155
			if(mustopen(*argv, &Files[Nfiles++]) == 0)
156
				nfdone++; /* suppress printing */
157
		} else {
158
			if(pr(*argv))
159
				Bterm(Files->f_f);
160
			nfdone++;
161
		}
162
	if(!nfdone)			/* no files named, use stdin */
163
		pr(nulls);		/* on GCOS, use current file, if any */
164
	errprint();			/* print accumulated error reports */
165
	exits(error? "error": 0);
166
}
167
 
168
int
169
findopt(int argc, char *argv[])
170
{
171
	char **eargv = argv;
172
	int eargc = 0, c;
173
 
174
	while(--argc > 0) {
175
		switch(c = **++argv) {
176
		case '-':
177
			if((c = *++*argv) == '\0')
178
				break;
179
		case '+':
180
			do {
181
				if(isdigit(c)) {
182
					--*argv;
183
					Ncols = atoix(argv);
184
				} else
185
				switch(c = TOLOWER(c)) {
186
				case '+':
187
					if((Fpage = atoix(argv)) < 1)
188
						Fpage = 1;
189
					continue;
190
				case 'd':
191
					Dblspace = 2;
192
					continue;
193
				case 'e':
194
					TABS(Etabn, Etabc);
195
					continue;
196
				case 'f':
197
					Formfeed++;
198
					continue;
199
				case 'h':
200
					if(--argc > 0)
201
						Head = argv[1];
202
					continue;
203
				case 'i':
204
					TABS(Itabn, Itabc);
205
					continue;
206
				case 'l':
207
					Len = atoix(argv);
208
					continue;
209
				case 'a':
210
				case 'm':
211
					Multi = c;
212
					continue;
213
				case 'o':
214
					Offset = atoix(argv);
215
					continue;
216
				case 's':
217
					if((Sepc = (*argv)[1]) != '\0')
218
						++*argv;
219
					else
220
						Sepc = '\t';
221
					continue;
222
				case 't':
223
					Margin = 0;
224
					continue;
225
				case 'w':
226
					Linew = atoix(argv);
227
					continue;
228
				case 'n':
229
					Lnumb++;
230
					if((Numw = intopt(argv, &Nsepc)) <= 0)
231
						Numw = NUMW;
232
				case 'b':
233
					Balance = 1;
234
					continue;
235
				case 'p':
236
					Padodd = 1;
237
					continue;
238
				default:
239
					die("bad option");
240
				}
241
			} while((c = *++*argv) != '\0');
242
			if(Head == argv[1])
243
				argv++;
244
			continue;
245
		}
246
		*eargv++ = *argv;
247
		eargc++;
248
	}
249
	if(Len == 0)
250
		Len = LENGTH;
251
	if(Len <= Margin)
252
		Margin = 0;
253
	Plength = Len - Margin/2;
254
	if(Multi == 'm')
255
		Ncols = eargc;
256
	switch(Ncols) {
257
	case 0:
258
		Ncols = 1;
259
	case 1:
260
		break;
261
	default:
262
		if(Etabn == 0)		/* respect explicit tab specification */
263
			Etabn = DEFTAB;
264
	}
265
	if(Linew == 0)
266
		Linew = Ncols != 1 && Sepc == 0? LINEW: 512;
267
	if(Lnumb)
268
		Linew -= Multi == 'm'? Numw: Numw*Ncols;
269
	if((Colw = (Linew - Ncols + 1)/Ncols) < 1)
270
		die("width too small");
271
	if(Ncols != 1 && Multi == 0) {
272
		ulong buflen = ((ulong)(Plength/Dblspace + 1))*(Linew+1)*sizeof(char);
273
		Buffer = getspace(buflen*sizeof(*Buffer));
274
		Bufend = &Buffer[buflen];
275
		Colpts = getspace((Ncols+1)*sizeof(*Colpts));
276
	}
277
	return eargc;
278
}
279
 
280
int
281
intopt(char *argv[], int *optp)
282
{
283
	int c;
284
 
285
	if((c = (*argv)[1]) != '\0' && !isdigit(c)) {
286
		*optp = c;
287
		(*argv)++;
288
	}
289
	c = atoix(argv);
290
	return c != 0? c: -1;
291
}
292
 
293
int
294
pr(char *name)
295
{
296
	char *date = 0, *head = 0;
297
 
298
	if(Multi != 'm' && mustopen(name, &Files[0]) == 0)
299
		return 0;
300
	if(Buffer)
301
		Bungetc(Files->f_f);
302
	if(Lnumb)
303
		Lnumb = 1;
304
	for(Page = 0;; putpage()) {
305
		if(C == -1)
306
			break;
307
		if(Buffer)
308
			nexbuf();
309
		Inpos = 0;
310
		if(get(0) == -1)
311
			break;
312
		Bflush(&bout);
313
		Page++;
314
		if(Page >= Fpage) {
315
			if(Margin == 0)
316
				continue;
317
			if(date == 0)
318
				date = getdate();
319
			if(head == 0)
320
				head = Head != 0 ? Head :
321
					Nfiles < 2? Files->f_name: nulls;
322
			Bprint(&bout, "\n\n");
323
			Nspace = Offset;
324
			putspace();
325
			Bprint(&bout, HEAD);
326
		}
327
	}
328
	if(Padodd && (Page&1) == 1) {
329
		Line = 0;
330
		if(Formfeed)
331
			put('\f');
332
		else
333
			while(Line < Len)
334
				put('\n');
335
	}
336
	C = '\0';
337
	return 1;
338
}
339
 
340
void
341
putpage(void)
342
{
343
	int colno;
344
 
345
	for(Line = Margin/2;; get(0)) {
346
		for(Nspace = Offset, colno = 0, Outpos = 0; C != '\f';) {
347
			if(Lnumb && C != -1 && (colno == 0 || Multi == 'a')) {
348
				if(Page >= Fpage) {
349
					putspace();
350
					Bprint(&bout, "%*ld", Numw, Buffer?
351
						Colpts[colno].c_lno++: Lnumb);
352
					Outpos += Numw;
353
					put(Nsepc);
354
				}
355
				Lnumb++;
356
			}
357
			for(Lcolpos=0, Pcolpos=0; C!='\n' && C!='\f' && C!=-1; get(colno))
358
					put(C);
359
			if(C==-1 || ++colno==Ncols || C=='\n' && get(colno)==-1)
360
				break;
361
			if(Sepc)
362
				put(Sepc);
363
			else
364
				if((Nspace += Colw - Lcolpos + 1) < 1)
365
					Nspace = 1;
366
		}
367
	/*
368
		if(C == -1) {
369
			if(Margin != 0)
370
				break;
371
			if(colno != 0)
372
				put('\n');
373
			return;
374
		}
375
	*/
376
		if(C == -1 && colno == 0) {
377
			if(Margin != 0)
378
				break;
379
			return;
380
		}
381
		if(C == '\f')
382
			break;
383
		put('\n');
384
		if(Dblspace == 2 && Line < Plength)
385
			put('\n');
386
		if(Line >= Plength)
387
			break;
388
	}
389
	if(Formfeed)
390
		put('\f');
391
	else
392
		while(Line < Len)
393
			put('\n');
394
}
395
 
396
void
397
nexbuf(void)
398
{
399
	Rune *s = Buffer;
400
	Colp p = Colpts;
401
	int j, c, bline = 0;
402
 
403
	for(;;) {
404
		p->c_ptr0 = p->c_ptr = s;
405
		if(p == &Colpts[Ncols])
406
			return;
407
		(p++)->c_lno = Lnumb + bline;
408
		for(j = (Len - Margin)/Dblspace; --j >= 0; bline++)
409
			for(Inpos = 0;;) {
410
				if((c = Bgetrune(Files->f_f)) == -1) {
411
					for(*s = -1; p <= &Colpts[Ncols]; p++)
412
						p->c_ptr0 = p->c_ptr = s;
413
					if(Balance)
414
						balance(bline);
415
					return;
416
				}
417
				if(ISPRINT(c))
418
					Inpos++;
419
				if(Inpos <= Colw || c == '\n') {
420
					*s = c;
421
					if(++s >= Bufend)
422
						die("page-buffer overflow");
423
				}
424
				if(c == '\n')
425
					break;
426
				switch(c) {
427
				case '\b':
428
					if(Inpos == 0)
429
						s--;
430
				case ESC:
431
					if(Inpos > 0)
432
						Inpos--;
433
				}
434
			}
435
	}
436
}
437
 
438
/*
439
 * line balancing for last page
440
 */
441
void
442
balance(int bline)
443
{
444
	Rune *s = Buffer;
445
	Colp p = Colpts;
446
	int colno = 0, j, c, l;
447
 
448
	c = bline % Ncols;
449
	l = (bline + Ncols - 1)/Ncols;
450
	bline = 0;
451
	do {
452
		for(j = 0; j < l; ++j)
453
			while(*s++ != '\n')
454
				;
455
		(++p)->c_lno = Lnumb + (bline += l);
456
		p->c_ptr0 = p->c_ptr = s;
457
		if(++colno == c)
458
			l--;
459
	} while(colno < Ncols - 1);
460
}
461
 
462
int
463
get(int colno)
464
{
465
	static int peekc = 0;
466
	Colp p;
467
	Fils *q;
468
	long c;
469
 
470
	if(peekc) {
471
		peekc = 0;
472
		c = Etabc;
473
	} else
474
	if(Buffer) {
475
		p = &Colpts[colno];
476
		if(p->c_ptr >= (p+1)->c_ptr0)
477
			c = -1;
478
		else
479
			if((c = *p->c_ptr) != -1)
480
				p->c_ptr++;
481
	} else
482
	if((c = (q = &Files[Multi == 'a'? 0: colno])->f_nextc) == -1) {
483
		for(q = &Files[Nfiles]; --q >= Files && q->f_nextc == -1;)
484
			;
485
		if(q >= Files)
486
			c = '\n';
487
	} else
488
		q->f_nextc = Bgetrune(q->f_f);
489
	if(Etabn != 0 && c == Etabc) {
490
		Inpos++;
491
		peekc = ETABS;
492
		c = ' ';
493
	} else
494
	if(ISPRINT(c))
495
		Inpos++;
496
	else
497
		switch(c) {
498
		case '\b':
499
		case ESC:
500
			if(Inpos > 0)
501
				Inpos--;
502
			break;
503
		case '\f':
504
			if(Ncols == 1)
505
				break;
506
			c = '\n';
507
		case '\n':
508
		case '\r':
509
			Inpos = 0;
510
		}
511
	return C = c;
512
}
513
 
514
void
515
put(long c)
516
{
517
	int move;
518
 
519
	switch(c) {
520
	case ' ':
521
		Nspace++;
522
		Lcolpos++;
523
		return;
524
	case '\b':
525
		if(Lcolpos == 0)
526
			return;
527
		if(Nspace > 0) {
528
			Nspace--;
529
			Lcolpos--;
530
			return;
531
		}
532
		if(Lcolpos > Pcolpos) {
533
			Lcolpos--;
534
			return;
535
		}
536
	case ESC:
537
		move = -1;
538
		break;
539
	case '\n':
540
		Line++;
541
	case '\r':
542
	case '\f':
543
		Pcolpos = 0;
544
		Lcolpos = 0;
545
		Nspace = 0;
546
		Outpos = 0;
547
	default:
548
		move = (ISPRINT(c) != 0);
549
	}
550
	if(Page < Fpage)
551
		return;
552
	if(Lcolpos > 0 || move > 0)
553
		Lcolpos += move;
554
	if(Lcolpos <= Colw) {
555
		putspace();
556
		Bputrune(&bout, c);
557
		Pcolpos = Lcolpos;
558
		Outpos += move;
559
	}
560
}
561
 
562
void
563
putspace(void)
564
{
565
	int nc;
566
 
567
	for(; Nspace > 0; Outpos += nc, Nspace -= nc)
568
		if(ITABS)
569
			Bputc(&bout, Itabc);
570
		else {
571
			nc = 1;
572
			Bputc(&bout, ' ');
573
		}
574
}
575
 
576
int
577
atoix(char **p)
578
{
579
	int n = 0, c;
580
 
581
	while(isdigit(c = *++*p))
582
		n = 10*n + c - '0';
583
	(*p)--;
584
	return n;
585
}
586
 
587
/*
588
 * Defer message about failure to open file to prevent messing up
589
 * alignment of page with tear perforations or form markers.
590
 * Treat empty file as special case and report as diagnostic.
591
 */
592
Biobuf*
593
mustopen(char *s, Fils *f)
594
{
595
	char *tmp;
596
 
597
	if(*s == '\0') {
598
		f->f_name = STDINNAME();
599
		f->f_f = malloc(sizeof(Biobuf));
600
		if(f->f_f == 0)
601
			cerror("no memory");
602
		Binit(f->f_f, 0, OREAD);
603
	} else
604
	if((f->f_f = Bopen(f->f_name = s, OREAD)) == 0) {
605
		tmp = ffiler(f->f_name);
606
		s = strcpy((char*)getspace(strlen(tmp) + 1), tmp);
607
		free(tmp);
608
	}
609
	if(f->f_f != 0) {
610
		if((f->f_nextc = Bgetrune(f->f_f)) >= 0 || Multi == 'm')
611
			return f->f_f;
612
		sprint(s = (char*)getspace(strlen(f->f_name) + 1 + EMPTY),
613
			"%s -- empty file\n", f->f_name);
614
		Bterm(f->f_f);
615
	}
616
	error = 1;
617
	cerror(s);
618
	fprint(2, "\n");
619
	return 0;
620
}
621
 
622
void*
623
getspace(ulong n)
624
{
625
	void *t;
626
 
627
	if((t = malloc(n)) == 0)
628
		die("out of space");
629
	return t;
630
}
631
 
632
void
633
die(char *s)
634
{
635
	error++;
636
	errprint();
637
	cerror(s);
638
	Bputc(&bout, '\n');
639
	exits("error");
640
}
641
 
642
/*
643
void
644
onintr(void)
645
{
646
	error++;
647
	errprint();
648
	exits("error");
649
}
650
/**/
651
 
652
/*
653
 * print accumulated error reports
654
 */
655
void
656
errprint(void)
657
{
658
	Bflush(&bout);
659
	for(; err != 0; err = err->e_nextp) {
660
		cerror(err->e_mess);
661
		fprint(2, "\n");
662
	}
663
}