xref: /csrg-svn/usr.bin/tail/reverse.c (revision 62299)
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