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