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 33 #if 0 34 #ifndef lint 35 static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93"; 36 #endif /* not lint */ 37 #endif 38 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 41 42 #include <sys/param.h> 43 #include <sys/queue.h> 44 #include <sys/stat.h> 45 #include <sys/mman.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <limits.h> 50 #include <stdint.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #include "extern.h" 57 58 static void r_buf(FILE *, const char *); 59 static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *); 60 61 /* 62 * reverse -- display input in reverse order by line. 63 * 64 * There are six separate cases for this -- regular and non-regular 65 * files by bytes, lines or the whole file. 66 * 67 * BYTES display N bytes 68 * REG mmap the file and display the lines 69 * NOREG cyclically read characters into a wrap-around buffer 70 * 71 * LINES display N lines 72 * REG mmap the file and display the lines 73 * NOREG cyclically read lines into a wrap-around array of buffers 74 * 75 * FILE display the entire file 76 * REG mmap the file and display the lines 77 * NOREG cyclically read input into a linked list of buffers 78 */ 79 void 80 reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 81 { 82 if (style != REVERSE && off == 0) 83 return; 84 85 if (S_ISREG(sbp->st_mode)) 86 r_reg(fp, fn, style, off, sbp); 87 else 88 switch(style) { 89 case FBYTES: 90 case RBYTES: 91 bytes(fp, fn, off); 92 break; 93 case FLINES: 94 case RLINES: 95 lines(fp, fn, off); 96 break; 97 case REVERSE: 98 r_buf(fp, fn); 99 break; 100 default: 101 break; 102 } 103 } 104 105 /* 106 * r_reg -- display a regular file in reverse order by line. 107 */ 108 static void 109 r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 110 { 111 struct mapinfo map; 112 off_t curoff, size, lineend; 113 int i; 114 115 if (!(size = sbp->st_size)) 116 return; 117 118 map.start = NULL; 119 map.mapoff = map.maxoff = size; 120 map.fd = fileno(fp); 121 map.maplen = 0; 122 123 /* 124 * Last char is special, ignore whether newline or not. Note that 125 * size == 0 is dealt with above, and size == 1 sets curoff to -1. 126 */ 127 curoff = size - 2; 128 lineend = size; 129 while (curoff >= 0) { 130 if (curoff < map.mapoff || 131 curoff >= map.mapoff + (off_t)map.maplen) { 132 if (maparound(&map, curoff) != 0) { 133 ierr(fn); 134 return; 135 } 136 } 137 for (i = curoff - map.mapoff; i >= 0; i--) { 138 if (style == RBYTES && --off == 0) 139 break; 140 if (map.start[i] == '\n') 141 break; 142 } 143 /* `i' is either the map offset of a '\n', or -1. */ 144 curoff = map.mapoff + i; 145 if (i < 0) 146 continue; 147 148 /* Print the line and update offsets. */ 149 if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) { 150 ierr(fn); 151 return; 152 } 153 lineend = curoff + 1; 154 curoff--; 155 156 if (style == RLINES) 157 off--; 158 159 if (off == 0 && style != REVERSE) { 160 /* Avoid printing anything below. */ 161 curoff = 0; 162 break; 163 } 164 } 165 if (curoff < 0 && mapprint(&map, 0, lineend) != 0) { 166 ierr(fn); 167 return; 168 } 169 if (map.start != NULL && munmap(map.start, map.maplen)) 170 ierr(fn); 171 } 172 173 #define BSZ (128 * 1024) 174 typedef struct bfelem { 175 TAILQ_ENTRY(bfelem) entries; 176 size_t len; 177 char l[BSZ]; 178 } bfelem_t; 179 180 /* 181 * r_buf -- display a non-regular file in reverse order by line. 182 * 183 * This is the function that saves the entire input, storing the data in a 184 * doubly linked list of buffers and then displays them in reverse order. 185 * It has the usual nastiness of trying to find the newlines, as there's no 186 * guarantee that a newline occurs anywhere in the file, let alone in any 187 * particular buffer. If we run out of memory, input is discarded (and the 188 * user warned). 189 */ 190 static void 191 r_buf(FILE *fp, const char *fn) 192 { 193 struct bfelem *tl, *first = NULL; 194 size_t llen; 195 char *p; 196 off_t enomem = 0; 197 TAILQ_HEAD(bfhead, bfelem) head; 198 199 TAILQ_INIT(&head); 200 201 while (!feof(fp)) { 202 size_t len; 203 204 /* 205 * Allocate a new block and link it into place in a doubly 206 * linked list. If out of memory, toss the LRU block and 207 * keep going. 208 */ 209 while ((tl = malloc(sizeof(bfelem_t))) == NULL) { 210 first = TAILQ_FIRST(&head); 211 if (TAILQ_EMPTY(&head)) 212 err(1, "malloc"); 213 enomem += first->len; 214 TAILQ_REMOVE(&head, first, entries); 215 free(first); 216 } 217 TAILQ_INSERT_TAIL(&head, tl, entries); 218 219 /* Fill the block with input data. */ 220 len = 0; 221 while ((!feof(fp)) && len < BSZ) { 222 p = tl->l + len; 223 len += fread(p, 1, BSZ - len, fp); 224 if (ferror(fp)) { 225 ierr(fn); 226 return; 227 } 228 } 229 230 tl->len = len; 231 } 232 233 if (enomem) { 234 warnx("warning: %jd bytes discarded", (intmax_t)enomem); 235 rval = 1; 236 } 237 238 /* 239 * Now print the lines in reverse order 240 * Outline: 241 * Scan backward for "\n", 242 * print forward to the end of the buffers 243 * free any buffers that start after the "\n" just found 244 * Loop 245 */ 246 tl = TAILQ_LAST(&head, bfhead); 247 first = TAILQ_FIRST(&head); 248 while (tl != NULL) { 249 struct bfelem *temp; 250 251 for (p = tl->l + tl->len - 1, llen = 0; p >= tl->l; 252 --p, ++llen) { 253 int start = (tl == first && p == tl->l); 254 255 if ((*p == '\n') || start) { 256 struct bfelem *tr; 257 258 if (start && llen) 259 WR(p, llen + 1); 260 else if (llen) 261 WR(p + 1, llen); 262 tr = TAILQ_NEXT(tl, entries); 263 llen = 0; 264 if (tr != NULL) { 265 TAILQ_FOREACH_FROM_SAFE(tr, &head, 266 entries, temp) { 267 if (tr->len) 268 WR(&tr->l, tr->len); 269 TAILQ_REMOVE(&head, tr, 270 entries); 271 free(tr); 272 } 273 } 274 } 275 } 276 tl->len = llen; 277 tl = TAILQ_PREV(tl, bfhead, entries); 278 } 279 TAILQ_REMOVE(&head, first, entries); 280 free(first); 281 } 282