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