]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/tail/reverse.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / usr.bin / tail / reverse.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #if 0
36 #ifndef lint
37 static char sccsid[] = "@(#)reverse.c   8.1 (Berkeley) 6/6/93";
38 #endif /* not lint */
39 #endif
40
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43
44 #include <sys/param.h>
45 #include <sys/queue.h>
46 #include <sys/stat.h>
47 #include <sys/mman.h>
48
49 #include <err.h>
50 #include <errno.h>
51 #include <limits.h>
52 #include <stdint.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 #include <libcasper.h>
59 #include <casper/cap_fileargs.h>
60
61 #include "extern.h"
62
63 static void r_buf(FILE *, const char *);
64 static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *);
65
66 /*
67  * reverse -- display input in reverse order by line.
68  *
69  * There are six separate cases for this -- regular and non-regular
70  * files by bytes, lines or the whole file.
71  *
72  * BYTES        display N bytes
73  *      REG     mmap the file and display the lines
74  *      NOREG   cyclically read characters into a wrap-around buffer
75  *
76  * LINES        display N lines
77  *      REG     mmap the file and display the lines
78  *      NOREG   cyclically read lines into a wrap-around array of buffers
79  *
80  * FILE         display the entire file
81  *      REG     mmap the file and display the lines
82  *      NOREG   cyclically read input into a linked list of buffers
83  */
84 void
85 reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
86 {
87         if (style != REVERSE && off == 0)
88                 return;
89
90         if (S_ISREG(sbp->st_mode))
91                 r_reg(fp, fn, style, off, sbp);
92         else
93                 switch(style) {
94                 case FBYTES:
95                 case RBYTES:
96                         bytes(fp, fn, off);
97                         break;
98                 case FLINES:
99                 case RLINES:
100                         lines(fp, fn, off);
101                         break;
102                 case REVERSE:
103                         r_buf(fp, fn);
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(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
115 {
116         struct mapinfo map;
117         off_t curoff, size, lineend;
118         int i;
119
120         if (!(size = sbp->st_size))
121                 return;
122
123         map.start = NULL;
124         map.mapoff = map.maxoff = size;
125         map.fd = fileno(fp);
126         map.maplen = 0;
127
128         /*
129          * Last char is special, ignore whether newline or not. Note that
130          * size == 0 is dealt with above, and size == 1 sets curoff to -1.
131          */
132         curoff = size - 2;
133         lineend = size;
134         while (curoff >= 0) {
135                 if (curoff < map.mapoff ||
136                     curoff >= map.mapoff + (off_t)map.maplen) {
137                         if (maparound(&map, curoff) != 0) {
138                                 ierr(fn);
139                                 return;
140                         }
141                 }
142                 for (i = curoff - map.mapoff; i >= 0; i--) {
143                         if (style == RBYTES && --off == 0)
144                                 break;
145                         if (map.start[i] == '\n')
146                                 break;
147                 }
148                 /* `i' is either the map offset of a '\n', or -1. */
149                 curoff = map.mapoff + i;
150                 if (i < 0)
151                         continue;
152
153                 /* Print the line and update offsets. */
154                 if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) {
155                         ierr(fn);
156                         return;
157                 }
158                 lineend = curoff + 1;
159                 curoff--;
160
161                 if (style == RLINES)
162                         off--;
163
164                 if (off == 0 && style != REVERSE) {
165                         /* Avoid printing anything below. */
166                         curoff = 0;
167                         break;
168                 }
169         }
170         if (curoff < 0 && mapprint(&map, 0, lineend) != 0) {
171                 ierr(fn);
172                 return;
173         }
174         if (map.start != NULL && munmap(map.start, map.maplen))
175                 ierr(fn);
176 }
177
178 #define BSZ     (128 * 1024)
179 typedef struct bfelem {
180         TAILQ_ENTRY(bfelem) entries;
181         size_t len;
182         char l[BSZ];
183 } bfelem_t;
184
185 /*
186  * r_buf -- display a non-regular file in reverse order by line.
187  *
188  * This is the function that saves the entire input, storing the data in a
189  * doubly linked list of buffers and then displays them in reverse order.
190  * It has the usual nastiness of trying to find the newlines, as there's no
191  * guarantee that a newline occurs anywhere in the file, let alone in any
192  * particular buffer.  If we run out of memory, input is discarded (and the
193  * user warned).
194  */
195 static void
196 r_buf(FILE *fp, const char *fn)
197 {
198         struct bfelem *tl, *first = NULL;
199         size_t llen;
200         char *p;
201         off_t enomem = 0;
202         TAILQ_HEAD(bfhead, bfelem) head;
203
204         TAILQ_INIT(&head);
205
206         while (!feof(fp)) {
207                 size_t len;
208
209                 /*
210                  * Allocate a new block and link it into place in a doubly
211                  * linked list.  If out of memory, toss the LRU block and
212                  * keep going.
213                  */
214                 while ((tl = malloc(sizeof(bfelem_t))) == NULL) {
215                         first = TAILQ_FIRST(&head);
216                         if (TAILQ_EMPTY(&head))
217                                 err(1, "malloc");
218                         enomem += first->len;
219                         TAILQ_REMOVE(&head, first, entries);
220                         free(first);
221                 }
222                 TAILQ_INSERT_TAIL(&head, tl, entries);
223
224                 /* Fill the block with input data. */
225                 len = 0;
226                 while ((!feof(fp)) && len < BSZ) {
227                         p = tl->l + len;
228                         len += fread(p, 1, BSZ - len, fp);
229                         if (ferror(fp)) {
230                                 ierr(fn);
231                                 return;
232                         }
233                 }
234
235                 tl->len = len;
236         }
237
238         if (enomem) {
239                 warnx("warning: %jd bytes discarded", (intmax_t)enomem);
240                 rval = 1;
241         }
242
243         /*
244          * Now print the lines in reverse order
245          * Outline:
246          *    Scan backward for "\n",
247          *    print forward to the end of the buffers
248          *    free any buffers that start after the "\n" just found
249          *    Loop
250          */
251         tl = TAILQ_LAST(&head, bfhead);
252         first = TAILQ_FIRST(&head);
253         while (tl != NULL) {
254                 struct bfelem *temp;
255
256                 for (p = tl->l + tl->len - 1, llen = 0; p >= tl->l;
257                     --p, ++llen) {
258                         int start = (tl == first && p == tl->l);
259
260                         if ((*p == '\n') || start) {
261                                 struct bfelem *tr;
262
263                                 if (llen && start && *p != '\n')
264                                         WR(p, llen + 1);
265                                 else if (llen) {
266                                         WR(p + 1, llen);
267                                         if (start && *p == '\n')
268                                                 WR(p, 1);
269                                 }
270                                 tr = TAILQ_NEXT(tl, entries);
271                                 llen = 0;
272                                 if (tr != NULL) {
273                                         TAILQ_FOREACH_FROM_SAFE(tr, &head,
274                                             entries, temp) {
275                                                 if (tr->len)
276                                                         WR(&tr->l, tr->len);
277                                                 TAILQ_REMOVE(&head, tr,
278                                                     entries);
279                                                 free(tr);
280                                         }
281                                 }
282                         }
283                 }
284                 tl->len = llen;
285                 tl = TAILQ_PREV(tl, bfhead, entries);
286         }
287         TAILQ_REMOVE(&head, first, entries);
288         free(first);
289 }