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