xref: /minix3/usr.bin/tail/reverse.c (revision a967d739abf6b9e18d75e99925030c41b4f075ca)
1*a967d739SClaudio Martella /*	$NetBSD: reverse.c,v 1.23 2011/09/03 10:59:11 christos Exp $	*/
2*a967d739SClaudio Martella 
3*a967d739SClaudio Martella /*-
4*a967d739SClaudio Martella  * Copyright (c) 1991, 1993
5*a967d739SClaudio Martella  *	The Regents of the University of California.  All rights reserved.
6*a967d739SClaudio Martella  *
7*a967d739SClaudio Martella  * This code is derived from software contributed to Berkeley by
8*a967d739SClaudio Martella  * Edward Sze-Tyan Wang.
9*a967d739SClaudio Martella  *
10*a967d739SClaudio Martella  * Redistribution and use in source and binary forms, with or without
11*a967d739SClaudio Martella  * modification, are permitted provided that the following conditions
12*a967d739SClaudio Martella  * are met:
13*a967d739SClaudio Martella  * 1. Redistributions of source code must retain the above copyright
14*a967d739SClaudio Martella  *    notice, this list of conditions and the following disclaimer.
15*a967d739SClaudio Martella  * 2. Redistributions in binary form must reproduce the above copyright
16*a967d739SClaudio Martella  *    notice, this list of conditions and the following disclaimer in the
17*a967d739SClaudio Martella  *    documentation and/or other materials provided with the distribution.
18*a967d739SClaudio Martella  * 3. Neither the name of the University nor the names of its contributors
19*a967d739SClaudio Martella  *    may be used to endorse or promote products derived from this software
20*a967d739SClaudio Martella  *    without specific prior written permission.
21*a967d739SClaudio Martella  *
22*a967d739SClaudio Martella  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23*a967d739SClaudio Martella  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*a967d739SClaudio Martella  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*a967d739SClaudio Martella  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26*a967d739SClaudio Martella  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*a967d739SClaudio Martella  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28*a967d739SClaudio Martella  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29*a967d739SClaudio Martella  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30*a967d739SClaudio Martella  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31*a967d739SClaudio Martella  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*a967d739SClaudio Martella  * SUCH DAMAGE.
33*a967d739SClaudio Martella  */
34*a967d739SClaudio Martella 
35*a967d739SClaudio Martella #include <sys/cdefs.h>
36*a967d739SClaudio Martella #ifndef lint
37*a967d739SClaudio Martella #if 0
38*a967d739SClaudio Martella static char sccsid[] = "@(#)reverse.c	8.1 (Berkeley) 6/6/93";
39*a967d739SClaudio Martella #endif
40*a967d739SClaudio Martella __RCSID("$NetBSD: reverse.c,v 1.23 2011/09/03 10:59:11 christos Exp $");
41*a967d739SClaudio Martella #endif /* not lint */
42*a967d739SClaudio Martella 
43*a967d739SClaudio Martella #include <sys/param.h>
44*a967d739SClaudio Martella #include <sys/stat.h>
45*a967d739SClaudio Martella #include <sys/mman.h>
46*a967d739SClaudio Martella 
47*a967d739SClaudio Martella #include <limits.h>
48*a967d739SClaudio Martella #include <errno.h>
49*a967d739SClaudio Martella #include <unistd.h>
50*a967d739SClaudio Martella #include <stdio.h>
51*a967d739SClaudio Martella #include <stdlib.h>
52*a967d739SClaudio Martella #include <string.h>
53*a967d739SClaudio Martella #include "extern.h"
54*a967d739SClaudio Martella 
55*a967d739SClaudio Martella static void r_buf(FILE *);
56*a967d739SClaudio Martella static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
57*a967d739SClaudio Martella 
58*a967d739SClaudio Martella /*
59*a967d739SClaudio Martella  * reverse -- display input in reverse order by line.
60*a967d739SClaudio Martella  *
61*a967d739SClaudio Martella  * There are six separate cases for this -- regular and non-regular
62*a967d739SClaudio Martella  * files by bytes, lines or the whole file.
63*a967d739SClaudio Martella  *
64*a967d739SClaudio Martella  * BYTES	display N bytes
65*a967d739SClaudio Martella  *	REG	mmap the file and display the lines
66*a967d739SClaudio Martella  *	NOREG	cyclically read characters into a wrap-around buffer
67*a967d739SClaudio Martella  *
68*a967d739SClaudio Martella  * LINES	display N lines
69*a967d739SClaudio Martella  *	REG	mmap the file and display the lines
70*a967d739SClaudio Martella  *	NOREG	cyclically read lines into a wrap-around array of buffers
71*a967d739SClaudio Martella  *
72*a967d739SClaudio Martella  * FILE		display the entire file
73*a967d739SClaudio Martella  *	REG	mmap the file and display the lines
74*a967d739SClaudio Martella  *	NOREG	cyclically read input into a linked list of buffers
75*a967d739SClaudio Martella  */
76*a967d739SClaudio Martella void
reverse(FILE * fp,enum STYLE style,off_t off,struct stat * sbp)77*a967d739SClaudio Martella reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
78*a967d739SClaudio Martella {
79*a967d739SClaudio Martella 	if (style != REVERSE && off == 0)
80*a967d739SClaudio Martella 		return;
81*a967d739SClaudio Martella 
82*a967d739SClaudio Martella 	if (S_ISREG(sbp->st_mode))
83*a967d739SClaudio Martella 		r_reg(fp, style, off, sbp);
84*a967d739SClaudio Martella 	else
85*a967d739SClaudio Martella 		switch(style) {
86*a967d739SClaudio Martella 		case FBYTES:
87*a967d739SClaudio Martella 		case RBYTES:
88*a967d739SClaudio Martella 			(void)displaybytes(fp, off);
89*a967d739SClaudio Martella 			break;
90*a967d739SClaudio Martella 		case FLINES:
91*a967d739SClaudio Martella 		case RLINES:
92*a967d739SClaudio Martella 			(void)displaylines(fp, off);
93*a967d739SClaudio Martella 			break;
94*a967d739SClaudio Martella 		case REVERSE:
95*a967d739SClaudio Martella 			r_buf(fp);
96*a967d739SClaudio Martella 			break;
97*a967d739SClaudio Martella 		default:
98*a967d739SClaudio Martella 			break;
99*a967d739SClaudio Martella 		}
100*a967d739SClaudio Martella }
101*a967d739SClaudio Martella 
102*a967d739SClaudio Martella /*
103*a967d739SClaudio Martella  * r_reg -- display a regular file in reverse order by line.
104*a967d739SClaudio Martella  */
105*a967d739SClaudio Martella static void
r_reg(FILE * fp,enum STYLE style,off_t off,struct stat * sbp)106*a967d739SClaudio Martella r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
107*a967d739SClaudio Martella {
108*a967d739SClaudio Martella 	off_t size;
109*a967d739SClaudio Martella 	int llen;
110*a967d739SClaudio Martella 	char *p;
111*a967d739SClaudio Martella 	char *start;
112*a967d739SClaudio Martella 
113*a967d739SClaudio Martella 	if (!(size = sbp->st_size))
114*a967d739SClaudio Martella 		return;
115*a967d739SClaudio Martella 
116*a967d739SClaudio Martella 	if ((uint64_t)size > SIZE_T_MAX) {
117*a967d739SClaudio Martella 			/* XXX: need a cleaner way to check this on amd64 */
118*a967d739SClaudio Martella 		errno = EFBIG;
119*a967d739SClaudio Martella 		xerr(0, "%s", fname);
120*a967d739SClaudio Martella 		return;
121*a967d739SClaudio Martella 	}
122*a967d739SClaudio Martella 
123*a967d739SClaudio Martella 	if ((start = mmap(NULL, (size_t)size, PROT_READ,
124*a967d739SClaudio Martella 	    MAP_FILE|MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
125*a967d739SClaudio Martella 		xerr(0, "%s", fname);
126*a967d739SClaudio Martella 		return;
127*a967d739SClaudio Martella 	}
128*a967d739SClaudio Martella 	p = start + size - 1;
129*a967d739SClaudio Martella 
130*a967d739SClaudio Martella 	if (style == RBYTES && off < size)
131*a967d739SClaudio Martella 		size = off;
132*a967d739SClaudio Martella 
133*a967d739SClaudio Martella 	/* Last char is special, ignore whether newline or not. */
134*a967d739SClaudio Martella 	for (llen = 1; --size; ++llen)
135*a967d739SClaudio Martella 		if (*--p == '\n') {
136*a967d739SClaudio Martella 			WR(p + 1, llen);
137*a967d739SClaudio Martella 			llen = 0;
138*a967d739SClaudio Martella 			if (style == RLINES && !--off) {
139*a967d739SClaudio Martella 				++p;
140*a967d739SClaudio Martella 				break;
141*a967d739SClaudio Martella 			}
142*a967d739SClaudio Martella 		}
143*a967d739SClaudio Martella 	if (llen)
144*a967d739SClaudio Martella 		WR(p, llen);
145*a967d739SClaudio Martella 	if (munmap(start, (size_t)sbp->st_size))
146*a967d739SClaudio Martella 		xerr(0, "%s", fname);
147*a967d739SClaudio Martella }
148*a967d739SClaudio Martella 
149*a967d739SClaudio Martella typedef struct bf {
150*a967d739SClaudio Martella 	struct bf *next;
151*a967d739SClaudio Martella 	struct bf *prev;
152*a967d739SClaudio Martella 	int len;
153*a967d739SClaudio Martella 	char *l;
154*a967d739SClaudio Martella } BF;
155*a967d739SClaudio Martella 
156*a967d739SClaudio Martella /*
157*a967d739SClaudio Martella  * r_buf -- display a non-regular file in reverse order by line.
158*a967d739SClaudio Martella  *
159*a967d739SClaudio Martella  * This is the function that saves the entire input, storing the data in a
160*a967d739SClaudio Martella  * doubly linked list of buffers and then displays them in reverse order.
161*a967d739SClaudio Martella  * It has the usual nastiness of trying to find the newlines, as there's no
162*a967d739SClaudio Martella  * guarantee that a newline occurs anywhere in the file, let alone in any
163*a967d739SClaudio Martella  * particular buffer.  If we run out of memory, input is discarded (and the
164*a967d739SClaudio Martella  * user warned).
165*a967d739SClaudio Martella  */
166*a967d739SClaudio Martella static void
r_buf(FILE * fp)167*a967d739SClaudio Martella r_buf(FILE *fp)
168*a967d739SClaudio Martella {
169*a967d739SClaudio Martella 	BF *mark, *tl, *tr;
170*a967d739SClaudio Martella 	int ch, len, llen;
171*a967d739SClaudio Martella 	char *p;
172*a967d739SClaudio Martella 	off_t enomem;
173*a967d739SClaudio Martella 
174*a967d739SClaudio Martella #define	BSZ	(128 * 1024)
175*a967d739SClaudio Martella 	tl =  NULL;
176*a967d739SClaudio Martella 	for (mark = NULL, enomem = 0;;) {
177*a967d739SClaudio Martella 		/*
178*a967d739SClaudio Martella 		 * Allocate a new block and link it into place in a doubly
179*a967d739SClaudio Martella 		 * linked list.  If out of memory, toss the LRU block and
180*a967d739SClaudio Martella 		 * keep going.
181*a967d739SClaudio Martella 		 */
182*a967d739SClaudio Martella 		if (enomem) {
183*a967d739SClaudio Martella 			if (!mark) {
184*a967d739SClaudio Martella 				errno = ENOMEM;
185*a967d739SClaudio Martella 				xerr(1, NULL);
186*a967d739SClaudio Martella 			}
187*a967d739SClaudio Martella 			tl = tl->next;
188*a967d739SClaudio Martella 			enomem += tl->len;
189*a967d739SClaudio Martella 		} else if ((tl = malloc(sizeof(*tl))) == NULL ||
190*a967d739SClaudio Martella 		    (tl->l = malloc(BSZ)) == NULL) {
191*a967d739SClaudio Martella 			if (tl)
192*a967d739SClaudio Martella 				free(tl);
193*a967d739SClaudio Martella 			if (!mark) {
194*a967d739SClaudio Martella 				errno = ENOMEM;
195*a967d739SClaudio Martella 				xerr(1, NULL);
196*a967d739SClaudio Martella 			}
197*a967d739SClaudio Martella 			tl = mark;
198*a967d739SClaudio Martella 			enomem += tl->len;
199*a967d739SClaudio Martella 		} else if (mark) {
200*a967d739SClaudio Martella 			tl->next = mark;
201*a967d739SClaudio Martella 			tl->prev = mark->prev;
202*a967d739SClaudio Martella 			mark->prev->next = tl;
203*a967d739SClaudio Martella 			mark->prev = tl;
204*a967d739SClaudio Martella 		} else {
205*a967d739SClaudio Martella 			mark = tl;
206*a967d739SClaudio Martella 			mark->next = mark->prev = mark;
207*a967d739SClaudio Martella 		}
208*a967d739SClaudio Martella 
209*a967d739SClaudio Martella 		/* Fill the block with input data. */
210*a967d739SClaudio Martella 		ch = 0;
211*a967d739SClaudio Martella 		for (p = tl->l, len = 0;
212*a967d739SClaudio Martella 		    len < BSZ && (ch = getc(fp)) != EOF; ++len)
213*a967d739SClaudio Martella 			*p++ = ch;
214*a967d739SClaudio Martella 
215*a967d739SClaudio Martella 		/*
216*a967d739SClaudio Martella 		 * If no input data for this block and we tossed some data,
217*a967d739SClaudio Martella 		 * recover it.
218*a967d739SClaudio Martella 		 */
219*a967d739SClaudio Martella 		if (!len) {
220*a967d739SClaudio Martella 			if (enomem)
221*a967d739SClaudio Martella 				enomem -= tl->len;
222*a967d739SClaudio Martella 			tl = tl->prev;
223*a967d739SClaudio Martella 			break;
224*a967d739SClaudio Martella 		}
225*a967d739SClaudio Martella 
226*a967d739SClaudio Martella 		tl->len = len;
227*a967d739SClaudio Martella 		if (ch == EOF)
228*a967d739SClaudio Martella 			break;
229*a967d739SClaudio Martella 	}
230*a967d739SClaudio Martella 
231*a967d739SClaudio Martella 	if (enomem) {
232*a967d739SClaudio Martella 		xerrx(0, "Warning: %lld bytes discarded", (long long)enomem);
233*a967d739SClaudio Martella 	}
234*a967d739SClaudio Martella 
235*a967d739SClaudio Martella 	/*
236*a967d739SClaudio Martella 	 * Step through the blocks in the reverse order read.  The last char
237*a967d739SClaudio Martella 	 * is special, ignore whether newline or not.
238*a967d739SClaudio Martella 	 */
239*a967d739SClaudio Martella 	for (mark = tl;;) {
240*a967d739SClaudio Martella 		for (p = tl->l + (len = tl->len) - 1, llen = 0; len--;
241*a967d739SClaudio Martella 		    --p, ++llen)
242*a967d739SClaudio Martella 			if (*p == '\n') {
243*a967d739SClaudio Martella 				if (llen) {
244*a967d739SClaudio Martella 					WR(p + 1, llen);
245*a967d739SClaudio Martella 					llen = 0;
246*a967d739SClaudio Martella 				}
247*a967d739SClaudio Martella 				if (tl == mark)
248*a967d739SClaudio Martella 					continue;
249*a967d739SClaudio Martella 				for (tr = tl->next; tr->len; tr = tr->next) {
250*a967d739SClaudio Martella 					WR(tr->l, tr->len);
251*a967d739SClaudio Martella 					tr->len = 0;
252*a967d739SClaudio Martella 					if (tr == mark)
253*a967d739SClaudio Martella 						break;
254*a967d739SClaudio Martella 				}
255*a967d739SClaudio Martella 			}
256*a967d739SClaudio Martella 		tl->len = llen;
257*a967d739SClaudio Martella 		if ((tl = tl->prev) == mark)
258*a967d739SClaudio Martella 			break;
259*a967d739SClaudio Martella 	}
260*a967d739SClaudio Martella 	tl = tl->next;
261*a967d739SClaudio Martella 	if (tl->len) {
262*a967d739SClaudio Martella 		WR(tl->l, tl->len);
263*a967d739SClaudio Martella 		tl->len = 0;
264*a967d739SClaudio Martella 	}
265*a967d739SClaudio Martella 	while ((tl = tl->next)->len) {
266*a967d739SClaudio Martella 		WR(tl->l, tl->len);
267*a967d739SClaudio Martella 		tl->len = 0;
268*a967d739SClaudio Martella 	}
269*a967d739SClaudio Martella }
270