1*50452Sbostic /*- 2*50452Sbostic * Copyright (c) 1991 The Regents of the University of California. 3*50452Sbostic * All rights reserved. 4*50452Sbostic * 5*50452Sbostic * This code is derived from software contributed to Berkeley by 6*50452Sbostic * Edward Sze-Tyan Wang. 7*50452Sbostic * 8*50452Sbostic * %sccs.include.redist.c% 9*50452Sbostic */ 10*50452Sbostic 11*50452Sbostic #ifndef lint 12*50452Sbostic static char sccsid[] = "@(#)reverse.c 5.1 (Berkeley) 07/21/91"; 13*50452Sbostic #endif /* not lint */ 14*50452Sbostic 15*50452Sbostic #include <sys/param.h> 16*50452Sbostic #include <sys/stat.h> 17*50452Sbostic #include <sys/mman.h> 18*50452Sbostic #include <errno.h> 19*50452Sbostic #include <unistd.h> 20*50452Sbostic #include <stdio.h> 21*50452Sbostic #include <stdlib.h> 22*50452Sbostic #include <string.h> 23*50452Sbostic #include "extern.h" 24*50452Sbostic 25*50452Sbostic static void r_buf __P((FILE *)); 26*50452Sbostic static void r_reg __P((FILE *, enum STYLE, long, struct stat *)); 27*50452Sbostic 28*50452Sbostic /* 29*50452Sbostic * reverse -- display input in reverse order by line. 30*50452Sbostic * 31*50452Sbostic * There are six separate cases for this -- regular and non-regular 32*50452Sbostic * files by bytes, lines or the whole file. 33*50452Sbostic * 34*50452Sbostic * BYTES display N bytes 35*50452Sbostic * REG mmap the file and display the lines 36*50452Sbostic * NOREG cyclically read characters into a wrap-around buffer 37*50452Sbostic * 38*50452Sbostic * LINES display N lines 39*50452Sbostic * REG mmap the file and display the lines 40*50452Sbostic * NOREG cyclically read lines into a wrap-around array of buffers 41*50452Sbostic * 42*50452Sbostic * FILE display the entire file 43*50452Sbostic * REG mmap the file and display the lines 44*50452Sbostic * NOREG cyclically read input into a linked list of buffers 45*50452Sbostic */ 46*50452Sbostic void 47*50452Sbostic reverse(fp, style, off, sbp) 48*50452Sbostic FILE *fp; 49*50452Sbostic enum STYLE style; 50*50452Sbostic long off; 51*50452Sbostic struct stat *sbp; 52*50452Sbostic { 53*50452Sbostic if (style != REVERSE && off == 0) 54*50452Sbostic return; 55*50452Sbostic 56*50452Sbostic if (S_ISREG(sbp->st_mode)) 57*50452Sbostic r_reg(fp, style, off, sbp); 58*50452Sbostic else 59*50452Sbostic switch(style) { 60*50452Sbostic case FBYTES: 61*50452Sbostic bytes(fp, off); 62*50452Sbostic break; 63*50452Sbostic case FLINES: 64*50452Sbostic lines(fp, off); 65*50452Sbostic break; 66*50452Sbostic case REVERSE: 67*50452Sbostic r_buf(fp); 68*50452Sbostic break; 69*50452Sbostic } 70*50452Sbostic } 71*50452Sbostic 72*50452Sbostic /* 73*50452Sbostic * r_reg -- display a regular file in reverse order by line. 74*50452Sbostic */ 75*50452Sbostic static void 76*50452Sbostic r_reg(fp, style, off, sbp) 77*50452Sbostic FILE *fp; 78*50452Sbostic register enum STYLE style; 79*50452Sbostic long off; 80*50452Sbostic struct stat *sbp; 81*50452Sbostic { 82*50452Sbostic register off_t size; 83*50452Sbostic register int llen; 84*50452Sbostic register char *p; 85*50452Sbostic int fd; 86*50452Sbostic 87*50452Sbostic if (!(size = sbp->st_size)) 88*50452Sbostic return; 89*50452Sbostic 90*50452Sbostic fd = fileno(fp); 91*50452Sbostic if ((p = mmap(NULL, size, PROT_READ, MAP_FILE, fd, (off_t)0)) == NULL) 92*50452Sbostic err("%s", strerror(errno)); 93*50452Sbostic p += size - 1; 94*50452Sbostic 95*50452Sbostic if (style == RBYTES && off < size) 96*50452Sbostic size = off; 97*50452Sbostic 98*50452Sbostic /* Last char is special, ignore whether newline or not. */ 99*50452Sbostic for (llen = 1; --size; ++llen) 100*50452Sbostic if (*--p == '\n') { 101*50452Sbostic WR(p + 1, llen); 102*50452Sbostic llen = 0; 103*50452Sbostic if (style == RLINES && !--off) 104*50452Sbostic break; 105*50452Sbostic } 106*50452Sbostic if (llen) 107*50452Sbostic WR(p + 1, llen); 108*50452Sbostic } 109*50452Sbostic 110*50452Sbostic typedef struct bf { 111*50452Sbostic struct bf *next; 112*50452Sbostic struct bf *prev; 113*50452Sbostic int len; 114*50452Sbostic char *l; 115*50452Sbostic } BF; 116*50452Sbostic 117*50452Sbostic /* 118*50452Sbostic * r_buf -- display a non-regular file in reverse order by line. 119*50452Sbostic * 120*50452Sbostic * This is the function that saves the entire input, storing the data in a 121*50452Sbostic * doubly linked list of buffers and then displays them in reverse order. 122*50452Sbostic * It has the usual nastiness of trying to find the newlines, as there's no 123*50452Sbostic * guarantee that a newline occurs anywhere in the file, let alone in any 124*50452Sbostic * particular buffer. If we run out of memory, input is discarded (and the 125*50452Sbostic * user warned). 126*50452Sbostic */ 127*50452Sbostic static void 128*50452Sbostic r_buf(fp) 129*50452Sbostic FILE *fp; 130*50452Sbostic { 131*50452Sbostic register BF *mark, *tl, *tr; 132*50452Sbostic register int ch, len, llen; 133*50452Sbostic register char *p; 134*50452Sbostic off_t enomem; 135*50452Sbostic 136*50452Sbostic #define BSZ (128 * 1024) 137*50452Sbostic for (mark = NULL, enomem = 0;;) { 138*50452Sbostic /* 139*50452Sbostic * Allocate a new block and link it into place in a doubly 140*50452Sbostic * linked list. If out of memory, toss the LRU block and 141*50452Sbostic * keep going. 142*50452Sbostic */ 143*50452Sbostic if (enomem || (tl = malloc(sizeof(BF))) == NULL || 144*50452Sbostic (tl->l = malloc(BSZ)) == NULL) { 145*50452Sbostic if (!mark) 146*50452Sbostic err("%s", strerror(errno)); 147*50452Sbostic tl = enomem ? tl->next : mark; 148*50452Sbostic enomem += tl->len; 149*50452Sbostic } else if (mark) { 150*50452Sbostic tl->next = mark; 151*50452Sbostic tl->prev = mark->prev; 152*50452Sbostic mark->prev->next = tl; 153*50452Sbostic mark->prev = tl; 154*50452Sbostic } else 155*50452Sbostic mark->next = mark->prev = (mark = tl); 156*50452Sbostic 157*50452Sbostic /* Fill the block with input data. */ 158*50452Sbostic for (p = tl->l, len = 0; 159*50452Sbostic len < BSZ && (ch = getc(fp)) != EOF; ++len) 160*50452Sbostic *p++ = ch; 161*50452Sbostic 162*50452Sbostic /* 163*50452Sbostic * If no input data for this block and we tossed some data, 164*50452Sbostic * recover it. 165*50452Sbostic */ 166*50452Sbostic if (!len) { 167*50452Sbostic if (enomem) 168*50452Sbostic enomem -= tl->len; 169*50452Sbostic tl = tl->prev; 170*50452Sbostic break; 171*50452Sbostic } 172*50452Sbostic 173*50452Sbostic tl->len = len; 174*50452Sbostic if (ch == EOF) 175*50452Sbostic break; 176*50452Sbostic } 177*50452Sbostic 178*50452Sbostic if (enomem) { 179*50452Sbostic (void)fprintf(stderr, 180*50452Sbostic "tail: warning: %ld bytes discarded\n", enomem); 181*50452Sbostic rval = 1; 182*50452Sbostic } 183*50452Sbostic 184*50452Sbostic /* 185*50452Sbostic * Step through the blocks in the reverse order read. The last char 186*50452Sbostic * is special, ignore whether newline or not. 187*50452Sbostic */ 188*50452Sbostic for (mark = tl;;) { 189*50452Sbostic for (p = tl->l + (len = tl->len) - 1, llen = 0; len--; 190*50452Sbostic --p, ++llen) 191*50452Sbostic if (*p == '\n') { 192*50452Sbostic if (llen) { 193*50452Sbostic WR(p + 1, llen); 194*50452Sbostic llen = 0; 195*50452Sbostic } 196*50452Sbostic if (tl == mark) 197*50452Sbostic continue; 198*50452Sbostic for (tr = tl->next; tr->len; tr = tr->next) { 199*50452Sbostic WR(tr->l, tr->len); 200*50452Sbostic tr->len = 0; 201*50452Sbostic if (tr == mark) 202*50452Sbostic break; 203*50452Sbostic } 204*50452Sbostic } 205*50452Sbostic tl->len = llen; 206*50452Sbostic if ((tl = tl->prev) == mark) 207*50452Sbostic break; 208*50452Sbostic } 209*50452Sbostic tl = tl->next; 210*50452Sbostic if (tl->len) { 211*50452Sbostic WR(tl->l, tl->len); 212*50452Sbostic tl->len = 0; 213*50452Sbostic } 214*50452Sbostic while ((tl = tl->next)->len) { 215*50452Sbostic WR(tl->l, tl->len); 216*50452Sbostic tl->len = 0; 217*50452Sbostic } 218*50452Sbostic } 219