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