150452Sbostic /*- 250452Sbostic * Copyright (c) 1991 The Regents of the University of California. 350452Sbostic * All rights reserved. 450452Sbostic * 550452Sbostic * This code is derived from software contributed to Berkeley by 650452Sbostic * Edward Sze-Tyan Wang. 750452Sbostic * 850452Sbostic * %sccs.include.redist.c% 950452Sbostic */ 1050452Sbostic 1150452Sbostic #ifndef lint 12*52471Sbostic static char sccsid[] = "@(#)reverse.c 5.3 (Berkeley) 02/12/92"; 1350452Sbostic #endif /* not lint */ 1450452Sbostic 1550452Sbostic #include <sys/param.h> 1650452Sbostic #include <sys/stat.h> 1750452Sbostic #include <sys/mman.h> 1850452Sbostic #include <errno.h> 1950452Sbostic #include <unistd.h> 2050452Sbostic #include <stdio.h> 2150452Sbostic #include <stdlib.h> 2250452Sbostic #include <string.h> 2350452Sbostic #include "extern.h" 2450452Sbostic 2550452Sbostic static void r_buf __P((FILE *)); 2650452Sbostic static void r_reg __P((FILE *, enum STYLE, long, struct stat *)); 2750452Sbostic 2850452Sbostic /* 2950452Sbostic * reverse -- display input in reverse order by line. 3050452Sbostic * 3150452Sbostic * There are six separate cases for this -- regular and non-regular 3250452Sbostic * files by bytes, lines or the whole file. 3350452Sbostic * 3450452Sbostic * BYTES display N bytes 3550452Sbostic * REG mmap the file and display the lines 3650452Sbostic * NOREG cyclically read characters into a wrap-around buffer 3750452Sbostic * 3850452Sbostic * LINES display N lines 3950452Sbostic * REG mmap the file and display the lines 4050452Sbostic * NOREG cyclically read lines into a wrap-around array of buffers 4150452Sbostic * 4250452Sbostic * FILE display the entire file 4350452Sbostic * REG mmap the file and display the lines 4450452Sbostic * NOREG cyclically read input into a linked list of buffers 4550452Sbostic */ 4650452Sbostic void 4750452Sbostic reverse(fp, style, off, sbp) 4850452Sbostic FILE *fp; 4950452Sbostic enum STYLE style; 5050452Sbostic long off; 5150452Sbostic struct stat *sbp; 5250452Sbostic { 5350452Sbostic if (style != REVERSE && off == 0) 5450452Sbostic return; 5550452Sbostic 5650452Sbostic if (S_ISREG(sbp->st_mode)) 5750452Sbostic r_reg(fp, style, off, sbp); 5850452Sbostic else 5950452Sbostic switch(style) { 6050452Sbostic case FBYTES: 6150452Sbostic bytes(fp, off); 6250452Sbostic break; 6350452Sbostic case FLINES: 6450452Sbostic lines(fp, off); 6550452Sbostic break; 6650452Sbostic case REVERSE: 6750452Sbostic r_buf(fp); 6850452Sbostic break; 6950452Sbostic } 7050452Sbostic } 7150452Sbostic 7250452Sbostic /* 7350452Sbostic * r_reg -- display a regular file in reverse order by line. 7450452Sbostic */ 7550452Sbostic static void 7650452Sbostic r_reg(fp, style, off, sbp) 7750452Sbostic FILE *fp; 7850452Sbostic register enum STYLE style; 7950452Sbostic long off; 8050452Sbostic struct stat *sbp; 8150452Sbostic { 8250452Sbostic register off_t size; 8350452Sbostic register int llen; 8450452Sbostic register char *p; 8550452Sbostic int fd; 8650452Sbostic 8750452Sbostic if (!(size = sbp->st_size)) 8850452Sbostic return; 8950452Sbostic 9050452Sbostic fd = fileno(fp); 9151382Sbostic if ((p = 9251382Sbostic mmap(NULL, size, PROT_READ, MAP_FILE, fd, (off_t)0)) == (caddr_t)-1) 9350452Sbostic err("%s", strerror(errno)); 9450452Sbostic p += size - 1; 9550452Sbostic 9650452Sbostic if (style == RBYTES && off < size) 9750452Sbostic size = off; 9850452Sbostic 9950452Sbostic /* Last char is special, ignore whether newline or not. */ 10050452Sbostic for (llen = 1; --size; ++llen) 10150452Sbostic if (*--p == '\n') { 10250452Sbostic WR(p + 1, llen); 10350452Sbostic llen = 0; 104*52471Sbostic if (style == RLINES && !--off) { 105*52471Sbostic ++p; 10650452Sbostic break; 107*52471Sbostic } 10850452Sbostic } 10950452Sbostic if (llen) 110*52471Sbostic WR(p, llen); 11150452Sbostic } 11250452Sbostic 11350452Sbostic typedef struct bf { 11450452Sbostic struct bf *next; 11550452Sbostic struct bf *prev; 11650452Sbostic int len; 11750452Sbostic char *l; 11850452Sbostic } BF; 11950452Sbostic 12050452Sbostic /* 12150452Sbostic * r_buf -- display a non-regular file in reverse order by line. 12250452Sbostic * 12350452Sbostic * This is the function that saves the entire input, storing the data in a 12450452Sbostic * doubly linked list of buffers and then displays them in reverse order. 12550452Sbostic * It has the usual nastiness of trying to find the newlines, as there's no 12650452Sbostic * guarantee that a newline occurs anywhere in the file, let alone in any 12750452Sbostic * particular buffer. If we run out of memory, input is discarded (and the 12850452Sbostic * user warned). 12950452Sbostic */ 13050452Sbostic static void 13150452Sbostic r_buf(fp) 13250452Sbostic FILE *fp; 13350452Sbostic { 13450452Sbostic register BF *mark, *tl, *tr; 13550452Sbostic register int ch, len, llen; 13650452Sbostic register char *p; 13750452Sbostic off_t enomem; 13850452Sbostic 13950452Sbostic #define BSZ (128 * 1024) 14050452Sbostic for (mark = NULL, enomem = 0;;) { 14150452Sbostic /* 14250452Sbostic * Allocate a new block and link it into place in a doubly 14350452Sbostic * linked list. If out of memory, toss the LRU block and 14450452Sbostic * keep going. 14550452Sbostic */ 14650452Sbostic if (enomem || (tl = malloc(sizeof(BF))) == NULL || 14750452Sbostic (tl->l = malloc(BSZ)) == NULL) { 14850452Sbostic if (!mark) 14950452Sbostic err("%s", strerror(errno)); 15050452Sbostic tl = enomem ? tl->next : mark; 15150452Sbostic enomem += tl->len; 15250452Sbostic } else if (mark) { 15350452Sbostic tl->next = mark; 15450452Sbostic tl->prev = mark->prev; 15550452Sbostic mark->prev->next = tl; 15650452Sbostic mark->prev = tl; 15750452Sbostic } else 15850452Sbostic mark->next = mark->prev = (mark = tl); 15950452Sbostic 16050452Sbostic /* Fill the block with input data. */ 16150452Sbostic for (p = tl->l, len = 0; 16250452Sbostic len < BSZ && (ch = getc(fp)) != EOF; ++len) 16350452Sbostic *p++ = ch; 16450452Sbostic 16550452Sbostic /* 16650452Sbostic * If no input data for this block and we tossed some data, 16750452Sbostic * recover it. 16850452Sbostic */ 16950452Sbostic if (!len) { 17050452Sbostic if (enomem) 17150452Sbostic enomem -= tl->len; 17250452Sbostic tl = tl->prev; 17350452Sbostic break; 17450452Sbostic } 17550452Sbostic 17650452Sbostic tl->len = len; 17750452Sbostic if (ch == EOF) 17850452Sbostic break; 17950452Sbostic } 18050452Sbostic 18150452Sbostic if (enomem) { 18250452Sbostic (void)fprintf(stderr, 18350452Sbostic "tail: warning: %ld bytes discarded\n", enomem); 18450452Sbostic rval = 1; 18550452Sbostic } 18650452Sbostic 18750452Sbostic /* 18850452Sbostic * Step through the blocks in the reverse order read. The last char 18950452Sbostic * is special, ignore whether newline or not. 19050452Sbostic */ 19150452Sbostic for (mark = tl;;) { 19250452Sbostic for (p = tl->l + (len = tl->len) - 1, llen = 0; len--; 19350452Sbostic --p, ++llen) 19450452Sbostic if (*p == '\n') { 19550452Sbostic if (llen) { 19650452Sbostic WR(p + 1, llen); 19750452Sbostic llen = 0; 19850452Sbostic } 19950452Sbostic if (tl == mark) 20050452Sbostic continue; 20150452Sbostic for (tr = tl->next; tr->len; tr = tr->next) { 20250452Sbostic WR(tr->l, tr->len); 20350452Sbostic tr->len = 0; 20450452Sbostic if (tr == mark) 20550452Sbostic break; 20650452Sbostic } 20750452Sbostic } 20850452Sbostic tl->len = llen; 20950452Sbostic if ((tl = tl->prev) == mark) 21050452Sbostic break; 21150452Sbostic } 21250452Sbostic tl = tl->next; 21350452Sbostic if (tl->len) { 21450452Sbostic WR(tl->l, tl->len); 21550452Sbostic tl->len = 0; 21650452Sbostic } 21750452Sbostic while ((tl = tl->next)->len) { 21850452Sbostic WR(tl->l, tl->len); 21950452Sbostic tl->len = 0; 22050452Sbostic } 22150452Sbostic } 222