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