150452Sbostic /*-
2*62299Sbostic * Copyright (c) 1991, 1993
3*62299Sbostic * The Regents of the University of California. 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*62299Sbostic static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 06/06/93";
1350452Sbostic #endif /* not lint */
1450452Sbostic
1550452Sbostic #include <sys/param.h>
1650452Sbostic #include <sys/stat.h>
1750452Sbostic #include <sys/mman.h>
1854363Sbostic
1954363Sbostic #include <limits.h>
2050452Sbostic #include <errno.h>
2150452Sbostic #include <unistd.h>
2250452Sbostic #include <stdio.h>
2350452Sbostic #include <stdlib.h>
2450452Sbostic #include <string.h>
2550452Sbostic #include "extern.h"
2650452Sbostic
2750452Sbostic static void r_buf __P((FILE *));
2850452Sbostic static void r_reg __P((FILE *, enum STYLE, long, struct stat *));
2950452Sbostic
3050452Sbostic /*
3150452Sbostic * reverse -- display input in reverse order by line.
3250452Sbostic *
3350452Sbostic * There are six separate cases for this -- regular and non-regular
3450452Sbostic * files by bytes, lines or the whole file.
3550452Sbostic *
3650452Sbostic * BYTES display N bytes
3750452Sbostic * REG mmap the file and display the lines
3850452Sbostic * NOREG cyclically read characters into a wrap-around buffer
3950452Sbostic *
4050452Sbostic * LINES display N lines
4150452Sbostic * REG mmap the file and display the lines
4250452Sbostic * NOREG cyclically read lines into a wrap-around array of buffers
4350452Sbostic *
4450452Sbostic * FILE display the entire file
4550452Sbostic * REG mmap the file and display the lines
4650452Sbostic * NOREG cyclically read input into a linked list of buffers
4750452Sbostic */
4850452Sbostic void
reverse(fp,style,off,sbp)4950452Sbostic reverse(fp, style, off, sbp)
5050452Sbostic FILE *fp;
5150452Sbostic enum STYLE style;
5250452Sbostic long off;
5350452Sbostic struct stat *sbp;
5450452Sbostic {
5550452Sbostic if (style != REVERSE && off == 0)
5650452Sbostic return;
5750452Sbostic
5850452Sbostic if (S_ISREG(sbp->st_mode))
5950452Sbostic r_reg(fp, style, off, sbp);
6050452Sbostic else
6150452Sbostic switch(style) {
6250452Sbostic case FBYTES:
6352839Sbostic case RBYTES:
6450452Sbostic bytes(fp, off);
6550452Sbostic break;
6650452Sbostic case FLINES:
6752839Sbostic case RLINES:
6850452Sbostic lines(fp, off);
6950452Sbostic break;
7050452Sbostic case REVERSE:
7150452Sbostic r_buf(fp);
7250452Sbostic break;
7350452Sbostic }
7450452Sbostic }
7550452Sbostic
7650452Sbostic /*
7750452Sbostic * r_reg -- display a regular file in reverse order by line.
7850452Sbostic */
7950452Sbostic static void
r_reg(fp,style,off,sbp)8050452Sbostic r_reg(fp, style, off, sbp)
8150452Sbostic FILE *fp;
8250452Sbostic register enum STYLE style;
8350452Sbostic long off;
8450452Sbostic struct stat *sbp;
8550452Sbostic {
8650452Sbostic register off_t size;
8750452Sbostic register int llen;
8850452Sbostic register char *p;
8958753Sbostic char *start;
9050452Sbostic
9150452Sbostic if (!(size = sbp->st_size))
9250452Sbostic return;
9350452Sbostic
9454363Sbostic if (size > SIZE_T_MAX) {
9554363Sbostic err(0, "%s: %s", fname, strerror(EFBIG));
9654363Sbostic return;
9754363Sbostic }
9854363Sbostic
9958753Sbostic if ((start = mmap(NULL, (size_t)size,
10054363Sbostic PROT_READ, 0, fileno(fp), (off_t)0)) == (caddr_t)-1) {
10154363Sbostic err(0, "%s: %s", fname, strerror(EFBIG));
10252827Sbostic return;
10352827Sbostic }
10458753Sbostic p = start + size - 1;
10550452Sbostic
10650452Sbostic if (style == RBYTES && off < size)
10750452Sbostic size = off;
10850452Sbostic
10950452Sbostic /* Last char is special, ignore whether newline or not. */
11050452Sbostic for (llen = 1; --size; ++llen)
11150452Sbostic if (*--p == '\n') {
11250452Sbostic WR(p + 1, llen);
11350452Sbostic llen = 0;
11452471Sbostic if (style == RLINES && !--off) {
11552471Sbostic ++p;
11650452Sbostic break;
11752471Sbostic }
11850452Sbostic }
11950452Sbostic if (llen)
12052471Sbostic WR(p, llen);
12158753Sbostic if (munmap(start, (size_t)sbp->st_size))
12258753Sbostic err(0, "%s: %s", fname, strerror(errno));
12350452Sbostic }
12450452Sbostic
12550452Sbostic typedef struct bf {
12650452Sbostic struct bf *next;
12750452Sbostic struct bf *prev;
12850452Sbostic int len;
12950452Sbostic char *l;
13050452Sbostic } BF;
13150452Sbostic
13250452Sbostic /*
13350452Sbostic * r_buf -- display a non-regular file in reverse order by line.
13450452Sbostic *
13550452Sbostic * This is the function that saves the entire input, storing the data in a
13650452Sbostic * doubly linked list of buffers and then displays them in reverse order.
13750452Sbostic * It has the usual nastiness of trying to find the newlines, as there's no
13850452Sbostic * guarantee that a newline occurs anywhere in the file, let alone in any
13950452Sbostic * particular buffer. If we run out of memory, input is discarded (and the
14050452Sbostic * user warned).
14150452Sbostic */
14250452Sbostic static void
r_buf(fp)14350452Sbostic r_buf(fp)
14450452Sbostic FILE *fp;
14550452Sbostic {
14650452Sbostic register BF *mark, *tl, *tr;
14750452Sbostic register int ch, len, llen;
14850452Sbostic register char *p;
14950452Sbostic off_t enomem;
15050452Sbostic
15150452Sbostic #define BSZ (128 * 1024)
15250452Sbostic for (mark = NULL, enomem = 0;;) {
15350452Sbostic /*
15450452Sbostic * Allocate a new block and link it into place in a doubly
15550452Sbostic * linked list. If out of memory, toss the LRU block and
15650452Sbostic * keep going.
15750452Sbostic */
15850452Sbostic if (enomem || (tl = malloc(sizeof(BF))) == NULL ||
15950452Sbostic (tl->l = malloc(BSZ)) == NULL) {
16050452Sbostic if (!mark)
16152827Sbostic err(1, "%s", strerror(errno));
16250452Sbostic tl = enomem ? tl->next : mark;
16350452Sbostic enomem += tl->len;
16450452Sbostic } else if (mark) {
16550452Sbostic tl->next = mark;
16650452Sbostic tl->prev = mark->prev;
16750452Sbostic mark->prev->next = tl;
16850452Sbostic mark->prev = tl;
16950452Sbostic } else
17050452Sbostic mark->next = mark->prev = (mark = tl);
17150452Sbostic
17250452Sbostic /* Fill the block with input data. */
17350452Sbostic for (p = tl->l, len = 0;
17450452Sbostic len < BSZ && (ch = getc(fp)) != EOF; ++len)
17550452Sbostic *p++ = ch;
17650452Sbostic
17750452Sbostic /*
17850452Sbostic * If no input data for this block and we tossed some data,
17950452Sbostic * recover it.
18050452Sbostic */
18150452Sbostic if (!len) {
18250452Sbostic if (enomem)
18350452Sbostic enomem -= tl->len;
18450452Sbostic tl = tl->prev;
18550452Sbostic break;
18650452Sbostic }
18750452Sbostic
18850452Sbostic tl->len = len;
18950452Sbostic if (ch == EOF)
19050452Sbostic break;
19150452Sbostic }
19250452Sbostic
19350452Sbostic if (enomem) {
19450452Sbostic (void)fprintf(stderr,
19550452Sbostic "tail: warning: %ld bytes discarded\n", enomem);
19650452Sbostic rval = 1;
19750452Sbostic }
19850452Sbostic
19950452Sbostic /*
20050452Sbostic * Step through the blocks in the reverse order read. The last char
20150452Sbostic * is special, ignore whether newline or not.
20250452Sbostic */
20350452Sbostic for (mark = tl;;) {
20450452Sbostic for (p = tl->l + (len = tl->len) - 1, llen = 0; len--;
20550452Sbostic --p, ++llen)
20650452Sbostic if (*p == '\n') {
20750452Sbostic if (llen) {
20850452Sbostic WR(p + 1, llen);
20950452Sbostic llen = 0;
21050452Sbostic }
21150452Sbostic if (tl == mark)
21250452Sbostic continue;
21350452Sbostic for (tr = tl->next; tr->len; tr = tr->next) {
21450452Sbostic WR(tr->l, tr->len);
21550452Sbostic tr->len = 0;
21650452Sbostic if (tr == mark)
21750452Sbostic break;
21850452Sbostic }
21950452Sbostic }
22050452Sbostic tl->len = llen;
22150452Sbostic if ((tl = tl->prev) == mark)
22250452Sbostic break;
22350452Sbostic }
22450452Sbostic tl = tl->next;
22550452Sbostic if (tl->len) {
22650452Sbostic WR(tl->l, tl->len);
22750452Sbostic tl->len = 0;
22850452Sbostic }
22950452Sbostic while ((tl = tl->next)->len) {
23050452Sbostic WR(tl->l, tl->len);
23150452Sbostic tl->len = 0;
23250452Sbostic }
23350452Sbostic }
234