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