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*52839Sbostic static char sccsid[] = "@(#)reverse.c 5.5 (Berkeley) 03/04/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: 61*52839Sbostic case RBYTES: 6250452Sbostic bytes(fp, off); 6350452Sbostic break; 6450452Sbostic case FLINES: 65*52839Sbostic case RLINES: 6650452Sbostic lines(fp, off); 6750452Sbostic break; 6850452Sbostic case REVERSE: 6950452Sbostic r_buf(fp); 7050452Sbostic break; 7150452Sbostic } 7250452Sbostic } 7350452Sbostic 7450452Sbostic /* 7550452Sbostic * r_reg -- display a regular file in reverse order by line. 7650452Sbostic */ 7750452Sbostic static void 7850452Sbostic r_reg(fp, style, off, sbp) 7950452Sbostic FILE *fp; 8050452Sbostic register enum STYLE style; 8150452Sbostic long off; 8250452Sbostic struct stat *sbp; 8350452Sbostic { 8450452Sbostic register off_t size; 8550452Sbostic register int llen; 8650452Sbostic register char *p; 8750452Sbostic 8850452Sbostic if (!(size = sbp->st_size)) 8950452Sbostic return; 9050452Sbostic 9152827Sbostic if ((p = mmap(NULL, 92*52839Sbostic size, PROT_READ, MAP_FILE, fileno(fp), (off_t)0)) == (caddr_t)-1) { 9352827Sbostic err(0, "%s", strerror(errno)); 9452827Sbostic return; 9552827Sbostic } 9650452Sbostic p += size - 1; 9750452Sbostic 9850452Sbostic if (style == RBYTES && off < size) 9950452Sbostic size = off; 10050452Sbostic 10150452Sbostic /* Last char is special, ignore whether newline or not. */ 10250452Sbostic for (llen = 1; --size; ++llen) 10350452Sbostic if (*--p == '\n') { 10450452Sbostic WR(p + 1, llen); 10550452Sbostic llen = 0; 10652471Sbostic if (style == RLINES && !--off) { 10752471Sbostic ++p; 10850452Sbostic break; 10952471Sbostic } 11050452Sbostic } 11150452Sbostic if (llen) 11252471Sbostic WR(p, llen); 11350452Sbostic } 11450452Sbostic 11550452Sbostic typedef struct bf { 11650452Sbostic struct bf *next; 11750452Sbostic struct bf *prev; 11850452Sbostic int len; 11950452Sbostic char *l; 12050452Sbostic } BF; 12150452Sbostic 12250452Sbostic /* 12350452Sbostic * r_buf -- display a non-regular file in reverse order by line. 12450452Sbostic * 12550452Sbostic * This is the function that saves the entire input, storing the data in a 12650452Sbostic * doubly linked list of buffers and then displays them in reverse order. 12750452Sbostic * It has the usual nastiness of trying to find the newlines, as there's no 12850452Sbostic * guarantee that a newline occurs anywhere in the file, let alone in any 12950452Sbostic * particular buffer. If we run out of memory, input is discarded (and the 13050452Sbostic * user warned). 13150452Sbostic */ 13250452Sbostic static void 13350452Sbostic r_buf(fp) 13450452Sbostic FILE *fp; 13550452Sbostic { 13650452Sbostic register BF *mark, *tl, *tr; 13750452Sbostic register int ch, len, llen; 13850452Sbostic register char *p; 13950452Sbostic off_t enomem; 14050452Sbostic 14150452Sbostic #define BSZ (128 * 1024) 14250452Sbostic for (mark = NULL, enomem = 0;;) { 14350452Sbostic /* 14450452Sbostic * Allocate a new block and link it into place in a doubly 14550452Sbostic * linked list. If out of memory, toss the LRU block and 14650452Sbostic * keep going. 14750452Sbostic */ 14850452Sbostic if (enomem || (tl = malloc(sizeof(BF))) == NULL || 14950452Sbostic (tl->l = malloc(BSZ)) == NULL) { 15050452Sbostic if (!mark) 15152827Sbostic err(1, "%s", strerror(errno)); 15250452Sbostic tl = enomem ? tl->next : mark; 15350452Sbostic enomem += tl->len; 15450452Sbostic } else if (mark) { 15550452Sbostic tl->next = mark; 15650452Sbostic tl->prev = mark->prev; 15750452Sbostic mark->prev->next = tl; 15850452Sbostic mark->prev = tl; 15950452Sbostic } else 16050452Sbostic mark->next = mark->prev = (mark = tl); 16150452Sbostic 16250452Sbostic /* Fill the block with input data. */ 16350452Sbostic for (p = tl->l, len = 0; 16450452Sbostic len < BSZ && (ch = getc(fp)) != EOF; ++len) 16550452Sbostic *p++ = ch; 16650452Sbostic 16750452Sbostic /* 16850452Sbostic * If no input data for this block and we tossed some data, 16950452Sbostic * recover it. 17050452Sbostic */ 17150452Sbostic if (!len) { 17250452Sbostic if (enomem) 17350452Sbostic enomem -= tl->len; 17450452Sbostic tl = tl->prev; 17550452Sbostic break; 17650452Sbostic } 17750452Sbostic 17850452Sbostic tl->len = len; 17950452Sbostic if (ch == EOF) 18050452Sbostic break; 18150452Sbostic } 18250452Sbostic 18350452Sbostic if (enomem) { 18450452Sbostic (void)fprintf(stderr, 18550452Sbostic "tail: warning: %ld bytes discarded\n", enomem); 18650452Sbostic rval = 1; 18750452Sbostic } 18850452Sbostic 18950452Sbostic /* 19050452Sbostic * Step through the blocks in the reverse order read. The last char 19150452Sbostic * is special, ignore whether newline or not. 19250452Sbostic */ 19350452Sbostic for (mark = tl;;) { 19450452Sbostic for (p = tl->l + (len = tl->len) - 1, llen = 0; len--; 19550452Sbostic --p, ++llen) 19650452Sbostic if (*p == '\n') { 19750452Sbostic if (llen) { 19850452Sbostic WR(p + 1, llen); 19950452Sbostic llen = 0; 20050452Sbostic } 20150452Sbostic if (tl == mark) 20250452Sbostic continue; 20350452Sbostic for (tr = tl->next; tr->len; tr = tr->next) { 20450452Sbostic WR(tr->l, tr->len); 20550452Sbostic tr->len = 0; 20650452Sbostic if (tr == mark) 20750452Sbostic break; 20850452Sbostic } 20950452Sbostic } 21050452Sbostic tl->len = llen; 21150452Sbostic if ((tl = tl->prev) == mark) 21250452Sbostic break; 21350452Sbostic } 21450452Sbostic tl = tl->next; 21550452Sbostic if (tl->len) { 21650452Sbostic WR(tl->l, tl->len); 21750452Sbostic tl->len = 0; 21850452Sbostic } 21950452Sbostic while ((tl = tl->next)->len) { 22050452Sbostic WR(tl->l, tl->len); 22150452Sbostic tl->len = 0; 22250452Sbostic } 22350452Sbostic } 224