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