xref: /csrg-svn/usr.bin/tail/reverse.c (revision 50452)
1*50452Sbostic /*-
2*50452Sbostic  * Copyright (c) 1991 The Regents of the University of California.
3*50452Sbostic  * All rights reserved.
4*50452Sbostic  *
5*50452Sbostic  * This code is derived from software contributed to Berkeley by
6*50452Sbostic  * Edward Sze-Tyan Wang.
7*50452Sbostic  *
8*50452Sbostic  * %sccs.include.redist.c%
9*50452Sbostic  */
10*50452Sbostic 
11*50452Sbostic #ifndef lint
12*50452Sbostic static char sccsid[] = "@(#)reverse.c	5.1 (Berkeley) 07/21/91";
13*50452Sbostic #endif /* not lint */
14*50452Sbostic 
15*50452Sbostic #include <sys/param.h>
16*50452Sbostic #include <sys/stat.h>
17*50452Sbostic #include <sys/mman.h>
18*50452Sbostic #include <errno.h>
19*50452Sbostic #include <unistd.h>
20*50452Sbostic #include <stdio.h>
21*50452Sbostic #include <stdlib.h>
22*50452Sbostic #include <string.h>
23*50452Sbostic #include "extern.h"
24*50452Sbostic 
25*50452Sbostic static void r_buf __P((FILE *));
26*50452Sbostic static void r_reg __P((FILE *, enum STYLE, long, struct stat *));
27*50452Sbostic 
28*50452Sbostic /*
29*50452Sbostic  * reverse -- display input in reverse order by line.
30*50452Sbostic  *
31*50452Sbostic  * There are six separate cases for this -- regular and non-regular
32*50452Sbostic  * files by bytes, lines or the whole file.
33*50452Sbostic  *
34*50452Sbostic  * BYTES	display N bytes
35*50452Sbostic  *	REG	mmap the file and display the lines
36*50452Sbostic  *	NOREG	cyclically read characters into a wrap-around buffer
37*50452Sbostic  *
38*50452Sbostic  * LINES	display N lines
39*50452Sbostic  *	REG	mmap the file and display the lines
40*50452Sbostic  *	NOREG	cyclically read lines into a wrap-around array of buffers
41*50452Sbostic  *
42*50452Sbostic  * FILE		display the entire file
43*50452Sbostic  *	REG	mmap the file and display the lines
44*50452Sbostic  *	NOREG	cyclically read input into a linked list of buffers
45*50452Sbostic  */
46*50452Sbostic void
47*50452Sbostic reverse(fp, style, off, sbp)
48*50452Sbostic 	FILE *fp;
49*50452Sbostic 	enum STYLE style;
50*50452Sbostic 	long off;
51*50452Sbostic 	struct stat *sbp;
52*50452Sbostic {
53*50452Sbostic 	if (style != REVERSE && off == 0)
54*50452Sbostic 		return;
55*50452Sbostic 
56*50452Sbostic 	if (S_ISREG(sbp->st_mode))
57*50452Sbostic 		r_reg(fp, style, off, sbp);
58*50452Sbostic 	else
59*50452Sbostic 		switch(style) {
60*50452Sbostic 		case FBYTES:
61*50452Sbostic 			bytes(fp, off);
62*50452Sbostic 			break;
63*50452Sbostic 		case FLINES:
64*50452Sbostic 			lines(fp, off);
65*50452Sbostic 			break;
66*50452Sbostic 		case REVERSE:
67*50452Sbostic 			r_buf(fp);
68*50452Sbostic 			break;
69*50452Sbostic 		}
70*50452Sbostic }
71*50452Sbostic 
72*50452Sbostic /*
73*50452Sbostic  * r_reg -- display a regular file in reverse order by line.
74*50452Sbostic  */
75*50452Sbostic static void
76*50452Sbostic r_reg(fp, style, off, sbp)
77*50452Sbostic 	FILE *fp;
78*50452Sbostic 	register enum STYLE style;
79*50452Sbostic 	long off;
80*50452Sbostic 	struct stat *sbp;
81*50452Sbostic {
82*50452Sbostic 	register off_t size;
83*50452Sbostic 	register int llen;
84*50452Sbostic 	register char *p;
85*50452Sbostic 	int fd;
86*50452Sbostic 
87*50452Sbostic 	if (!(size = sbp->st_size))
88*50452Sbostic 		return;
89*50452Sbostic 
90*50452Sbostic 	fd = fileno(fp);
91*50452Sbostic 	if ((p = mmap(NULL, size, PROT_READ, MAP_FILE, fd, (off_t)0)) == NULL)
92*50452Sbostic 		err("%s", strerror(errno));
93*50452Sbostic 	p += size - 1;
94*50452Sbostic 
95*50452Sbostic 	if (style == RBYTES && off < size)
96*50452Sbostic 		size = off;
97*50452Sbostic 
98*50452Sbostic 	/* Last char is special, ignore whether newline or not. */
99*50452Sbostic 	for (llen = 1; --size; ++llen)
100*50452Sbostic 		if (*--p == '\n') {
101*50452Sbostic 			WR(p + 1, llen);
102*50452Sbostic 			llen = 0;
103*50452Sbostic 			if (style == RLINES && !--off)
104*50452Sbostic 				break;
105*50452Sbostic 		}
106*50452Sbostic 	if (llen)
107*50452Sbostic 		WR(p + 1, llen);
108*50452Sbostic }
109*50452Sbostic 
110*50452Sbostic typedef struct bf {
111*50452Sbostic 	struct bf *next;
112*50452Sbostic 	struct bf *prev;
113*50452Sbostic 	int len;
114*50452Sbostic 	char *l;
115*50452Sbostic } BF;
116*50452Sbostic 
117*50452Sbostic /*
118*50452Sbostic  * r_buf -- display a non-regular file in reverse order by line.
119*50452Sbostic  *
120*50452Sbostic  * This is the function that saves the entire input, storing the data in a
121*50452Sbostic  * doubly linked list of buffers and then displays them in reverse order.
122*50452Sbostic  * It has the usual nastiness of trying to find the newlines, as there's no
123*50452Sbostic  * guarantee that a newline occurs anywhere in the file, let alone in any
124*50452Sbostic  * particular buffer.  If we run out of memory, input is discarded (and the
125*50452Sbostic  * user warned).
126*50452Sbostic  */
127*50452Sbostic static void
128*50452Sbostic r_buf(fp)
129*50452Sbostic 	FILE *fp;
130*50452Sbostic {
131*50452Sbostic 	register BF *mark, *tl, *tr;
132*50452Sbostic 	register int ch, len, llen;
133*50452Sbostic 	register char *p;
134*50452Sbostic 	off_t enomem;
135*50452Sbostic 
136*50452Sbostic #define	BSZ	(128 * 1024)
137*50452Sbostic 	for (mark = NULL, enomem = 0;;) {
138*50452Sbostic 		/*
139*50452Sbostic 		 * Allocate a new block and link it into place in a doubly
140*50452Sbostic 		 * linked list.  If out of memory, toss the LRU block and
141*50452Sbostic 		 * keep going.
142*50452Sbostic 		 */
143*50452Sbostic 		if (enomem || (tl = malloc(sizeof(BF))) == NULL ||
144*50452Sbostic 		    (tl->l = malloc(BSZ)) == NULL) {
145*50452Sbostic 			if (!mark)
146*50452Sbostic 				err("%s", strerror(errno));
147*50452Sbostic 			tl = enomem ? tl->next : mark;
148*50452Sbostic 			enomem += tl->len;
149*50452Sbostic 		} else if (mark) {
150*50452Sbostic 			tl->next = mark;
151*50452Sbostic 			tl->prev = mark->prev;
152*50452Sbostic 			mark->prev->next = tl;
153*50452Sbostic 			mark->prev = tl;
154*50452Sbostic 		} else
155*50452Sbostic 			mark->next = mark->prev = (mark = tl);
156*50452Sbostic 
157*50452Sbostic 		/* Fill the block with input data. */
158*50452Sbostic 		for (p = tl->l, len = 0;
159*50452Sbostic 		    len < BSZ && (ch = getc(fp)) != EOF; ++len)
160*50452Sbostic 			*p++ = ch;
161*50452Sbostic 
162*50452Sbostic 		/*
163*50452Sbostic 		 * If no input data for this block and we tossed some data,
164*50452Sbostic 		 * recover it.
165*50452Sbostic 		 */
166*50452Sbostic 		if (!len) {
167*50452Sbostic 			if (enomem)
168*50452Sbostic 				enomem -= tl->len;
169*50452Sbostic 			tl = tl->prev;
170*50452Sbostic 			break;
171*50452Sbostic 		}
172*50452Sbostic 
173*50452Sbostic 		tl->len = len;
174*50452Sbostic 		if (ch == EOF)
175*50452Sbostic 			break;
176*50452Sbostic 	}
177*50452Sbostic 
178*50452Sbostic 	if (enomem) {
179*50452Sbostic 		(void)fprintf(stderr,
180*50452Sbostic 		    "tail: warning: %ld bytes discarded\n", enomem);
181*50452Sbostic 		rval = 1;
182*50452Sbostic 	}
183*50452Sbostic 
184*50452Sbostic 	/*
185*50452Sbostic 	 * Step through the blocks in the reverse order read.  The last char
186*50452Sbostic 	 * is special, ignore whether newline or not.
187*50452Sbostic 	 */
188*50452Sbostic 	for (mark = tl;;) {
189*50452Sbostic 		for (p = tl->l + (len = tl->len) - 1, llen = 0; len--;
190*50452Sbostic 		    --p, ++llen)
191*50452Sbostic 			if (*p == '\n') {
192*50452Sbostic 				if (llen) {
193*50452Sbostic 					WR(p + 1, llen);
194*50452Sbostic 					llen = 0;
195*50452Sbostic 				}
196*50452Sbostic 				if (tl == mark)
197*50452Sbostic 					continue;
198*50452Sbostic 				for (tr = tl->next; tr->len; tr = tr->next) {
199*50452Sbostic 					WR(tr->l, tr->len);
200*50452Sbostic 					tr->len = 0;
201*50452Sbostic 					if (tr == mark)
202*50452Sbostic 						break;
203*50452Sbostic 				}
204*50452Sbostic 			}
205*50452Sbostic 		tl->len = llen;
206*50452Sbostic 		if ((tl = tl->prev) == mark)
207*50452Sbostic 			break;
208*50452Sbostic 	}
209*50452Sbostic 	tl = tl->next;
210*50452Sbostic 	if (tl->len) {
211*50452Sbostic 		WR(tl->l, tl->len);
212*50452Sbostic 		tl->len = 0;
213*50452Sbostic 	}
214*50452Sbostic 	while ((tl = tl->next)->len) {
215*50452Sbostic 		WR(tl->l, tl->len);
216*50452Sbostic 		tl->len = 0;
217*50452Sbostic 	}
218*50452Sbostic }
219