xref: /csrg-svn/usr.bin/tail/reverse.c (revision 52827)
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*52827Sbostic static char sccsid[] = "@(#)reverse.c	5.4 (Berkeley) 03/03/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);
91*52827Sbostic 	if ((p = mmap(NULL,
92*52827Sbostic 	    size, PROT_READ, MAP_FILE, fd, (off_t)0)) == (caddr_t)-1) {
93*52827Sbostic 		err(0, "%s", strerror(errno));
94*52827Sbostic 		return;
95*52827Sbostic 	}
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)
151*52827Sbostic 				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