19b50d902SRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
49b50d902SRodney W. Grimes * Copyright (c) 1991, 1993
59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved.
69b50d902SRodney W. Grimes *
79b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by
89b50d902SRodney W. Grimes * Edward Sze-Tyan Wang.
99b50d902SRodney W. Grimes *
109b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without
119b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions
129b50d902SRodney W. Grimes * are met:
139b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer.
159b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
169b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
179b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
199b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software
209b50d902SRodney W. Grimes * without specific prior written permission.
219b50d902SRodney W. Grimes *
229b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
239b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
249b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
259b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
269b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
279b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
289b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
299b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
309b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
319b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329b50d902SRodney W. Grimes * SUCH DAMAGE.
339b50d902SRodney W. Grimes */
349b50d902SRodney W. Grimes
359b50d902SRodney W. Grimes #include <sys/param.h>
36cdb7a6fcSAlan Somers #include <sys/queue.h>
379b50d902SRodney W. Grimes #include <sys/stat.h>
389b50d902SRodney W. Grimes #include <sys/mman.h>
399b50d902SRodney W. Grimes
40814e3a92SMark Murray #include <err.h>
419b50d902SRodney W. Grimes #include <errno.h>
42814e3a92SMark Murray #include <limits.h>
43d0990ea9SDavid Malone #include <stdint.h>
449b50d902SRodney W. Grimes #include <stdio.h>
459b50d902SRodney W. Grimes #include <stdlib.h>
469b50d902SRodney W. Grimes #include <string.h>
47814e3a92SMark Murray #include <unistd.h>
48814e3a92SMark Murray
49c851fce6SMariusz Zaborski #include <libcasper.h>
50c851fce6SMariusz Zaborski #include <casper/cap_fileargs.h>
51c851fce6SMariusz Zaborski
529b50d902SRodney W. Grimes #include "extern.h"
539b50d902SRodney W. Grimes
5422da50cfSBrian Somers static void r_buf(FILE *, const char *);
5522da50cfSBrian Somers static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *);
569b50d902SRodney W. Grimes
579b50d902SRodney W. Grimes /*
589b50d902SRodney W. Grimes * reverse -- display input in reverse order by line.
599b50d902SRodney W. Grimes *
609b50d902SRodney W. Grimes * There are six separate cases for this -- regular and non-regular
619b50d902SRodney W. Grimes * files by bytes, lines or the whole file.
629b50d902SRodney W. Grimes *
639b50d902SRodney W. Grimes * BYTES display N bytes
649b50d902SRodney W. Grimes * REG mmap the file and display the lines
659b50d902SRodney W. Grimes * NOREG cyclically read characters into a wrap-around buffer
669b50d902SRodney W. Grimes *
679b50d902SRodney W. Grimes * LINES display N lines
689b50d902SRodney W. Grimes * REG mmap the file and display the lines
699b50d902SRodney W. Grimes * NOREG cyclically read lines into a wrap-around array of buffers
709b50d902SRodney W. Grimes *
719b50d902SRodney W. Grimes * FILE display the entire file
729b50d902SRodney W. Grimes * REG mmap the file and display the lines
739b50d902SRodney W. Grimes * NOREG cyclically read input into a linked list of buffers
749b50d902SRodney W. Grimes */
759b50d902SRodney W. Grimes void
reverse(FILE * fp,const char * fn,enum STYLE style,off_t off,struct stat * sbp)7622da50cfSBrian Somers reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
779b50d902SRodney W. Grimes {
789b50d902SRodney W. Grimes if (style != REVERSE && off == 0)
799b50d902SRodney W. Grimes return;
809b50d902SRodney W. Grimes
819b50d902SRodney W. Grimes if (S_ISREG(sbp->st_mode))
8222da50cfSBrian Somers r_reg(fp, fn, style, off, sbp);
839b50d902SRodney W. Grimes else
849b50d902SRodney W. Grimes switch(style) {
859b50d902SRodney W. Grimes case FBYTES:
869b50d902SRodney W. Grimes case RBYTES:
8722da50cfSBrian Somers bytes(fp, fn, off);
889b50d902SRodney W. Grimes break;
899b50d902SRodney W. Grimes case FLINES:
909b50d902SRodney W. Grimes case RLINES:
9122da50cfSBrian Somers lines(fp, fn, off);
929b50d902SRodney W. Grimes break;
939b50d902SRodney W. Grimes case REVERSE:
9422da50cfSBrian Somers r_buf(fp, fn);
959b50d902SRodney W. Grimes break;
96814e3a92SMark Murray default:
97b77b9b9aSMurray Stokely break;
989b50d902SRodney W. Grimes }
999b50d902SRodney W. Grimes }
1009b50d902SRodney W. Grimes
1019b50d902SRodney W. Grimes /*
1029b50d902SRodney W. Grimes * r_reg -- display a regular file in reverse order by line.
1039b50d902SRodney W. Grimes */
1049b50d902SRodney W. Grimes static void
r_reg(FILE * fp,const char * fn,enum STYLE style,off_t off,struct stat * sbp)10522da50cfSBrian Somers r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
1069b50d902SRodney W. Grimes {
107726098d3SDavid Malone struct mapinfo map;
108726098d3SDavid Malone off_t curoff, size, lineend;
109726098d3SDavid Malone int i;
1109b50d902SRodney W. Grimes
1119b50d902SRodney W. Grimes if (!(size = sbp->st_size))
1129b50d902SRodney W. Grimes return;
1139b50d902SRodney W. Grimes
114726098d3SDavid Malone map.start = NULL;
115726098d3SDavid Malone map.mapoff = map.maxoff = size;
116726098d3SDavid Malone map.fd = fileno(fp);
1172277edc8SAlan Somers map.maplen = 0;
118726098d3SDavid Malone
119726098d3SDavid Malone /*
120726098d3SDavid Malone * Last char is special, ignore whether newline or not. Note that
121726098d3SDavid Malone * size == 0 is dealt with above, and size == 1 sets curoff to -1.
122726098d3SDavid Malone */
123726098d3SDavid Malone curoff = size - 2;
124726098d3SDavid Malone lineend = size;
125726098d3SDavid Malone while (curoff >= 0) {
126d0990ea9SDavid Malone if (curoff < map.mapoff ||
127d0990ea9SDavid Malone curoff >= map.mapoff + (off_t)map.maplen) {
128726098d3SDavid Malone if (maparound(&map, curoff) != 0) {
12922da50cfSBrian Somers ierr(fn);
1309b50d902SRodney W. Grimes return;
1319b50d902SRodney W. Grimes }
132726098d3SDavid Malone }
133726098d3SDavid Malone for (i = curoff - map.mapoff; i >= 0; i--) {
134726098d3SDavid Malone if (style == RBYTES && --off == 0)
135726098d3SDavid Malone break;
136726098d3SDavid Malone if (map.start[i] == '\n')
137726098d3SDavid Malone break;
138726098d3SDavid Malone }
139726098d3SDavid Malone /* `i' is either the map offset of a '\n', or -1. */
140726098d3SDavid Malone curoff = map.mapoff + i;
141726098d3SDavid Malone if (i < 0)
142726098d3SDavid Malone continue;
1439b50d902SRodney W. Grimes
144726098d3SDavid Malone /* Print the line and update offsets. */
145726098d3SDavid Malone if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) {
14622da50cfSBrian Somers ierr(fn);
1479b50d902SRodney W. Grimes return;
1489b50d902SRodney W. Grimes }
149726098d3SDavid Malone lineend = curoff + 1;
150726098d3SDavid Malone curoff--;
1519b50d902SRodney W. Grimes
152726098d3SDavid Malone if (style == RLINES)
153726098d3SDavid Malone off--;
1549b50d902SRodney W. Grimes
155726098d3SDavid Malone if (off == 0 && style != REVERSE) {
156726098d3SDavid Malone /* Avoid printing anything below. */
157726098d3SDavid Malone curoff = 0;
1589b50d902SRodney W. Grimes break;
1599b50d902SRodney W. Grimes }
1609b50d902SRodney W. Grimes }
161726098d3SDavid Malone if (curoff < 0 && mapprint(&map, 0, lineend) != 0) {
16222da50cfSBrian Somers ierr(fn);
163726098d3SDavid Malone return;
164726098d3SDavid Malone }
165726098d3SDavid Malone if (map.start != NULL && munmap(map.start, map.maplen))
16622da50cfSBrian Somers ierr(fn);
1679b50d902SRodney W. Grimes }
1689b50d902SRodney W. Grimes
16943e9ad02SAlan Somers #define BSZ (128 * 1024)
170cdb7a6fcSAlan Somers typedef struct bfelem {
171cdb7a6fcSAlan Somers TAILQ_ENTRY(bfelem) entries;
172cdb7a6fcSAlan Somers size_t len;
17343e9ad02SAlan Somers char l[BSZ];
174cdb7a6fcSAlan Somers } bfelem_t;
1759b50d902SRodney W. Grimes
1769b50d902SRodney W. Grimes /*
1779b50d902SRodney W. Grimes * r_buf -- display a non-regular file in reverse order by line.
1789b50d902SRodney W. Grimes *
1799b50d902SRodney W. Grimes * This is the function that saves the entire input, storing the data in a
1809b50d902SRodney W. Grimes * doubly linked list of buffers and then displays them in reverse order.
1819b50d902SRodney W. Grimes * It has the usual nastiness of trying to find the newlines, as there's no
1829b50d902SRodney W. Grimes * guarantee that a newline occurs anywhere in the file, let alone in any
1839b50d902SRodney W. Grimes * particular buffer. If we run out of memory, input is discarded (and the
1849b50d902SRodney W. Grimes * user warned).
1859b50d902SRodney W. Grimes */
1869b50d902SRodney W. Grimes static void
r_buf(FILE * fp,const char * fn)18722da50cfSBrian Somers r_buf(FILE *fp, const char *fn)
1889b50d902SRodney W. Grimes {
18943e9ad02SAlan Somers struct bfelem *tl, *first = NULL;
19043e9ad02SAlan Somers size_t llen;
19148a1ef22SJeroen Ruigrok van der Werven char *p;
192cdb7a6fcSAlan Somers off_t enomem = 0;
193cdb7a6fcSAlan Somers TAILQ_HEAD(bfhead, bfelem) head;
1949b50d902SRodney W. Grimes
195cdb7a6fcSAlan Somers TAILQ_INIT(&head);
196cdb7a6fcSAlan Somers
197cdb7a6fcSAlan Somers while (!feof(fp)) {
19843e9ad02SAlan Somers size_t len;
19943e9ad02SAlan Somers
2009b50d902SRodney W. Grimes /*
2019b50d902SRodney W. Grimes * Allocate a new block and link it into place in a doubly
2029b50d902SRodney W. Grimes * linked list. If out of memory, toss the LRU block and
2039b50d902SRodney W. Grimes * keep going.
2049b50d902SRodney W. Grimes */
205cdb7a6fcSAlan Somers while ((tl = malloc(sizeof(bfelem_t))) == NULL) {
206cdb7a6fcSAlan Somers first = TAILQ_FIRST(&head);
207cdb7a6fcSAlan Somers if (TAILQ_EMPTY(&head))
208*b70e57beSDag-Erling Smørgrav err(1, "failed to allocate memory");
209cdb7a6fcSAlan Somers enomem += first->len;
210cdb7a6fcSAlan Somers TAILQ_REMOVE(&head, first, entries);
211cdb7a6fcSAlan Somers free(first);
2122277edc8SAlan Somers }
213cdb7a6fcSAlan Somers TAILQ_INSERT_TAIL(&head, tl, entries);
2149b50d902SRodney W. Grimes
2159b50d902SRodney W. Grimes /* Fill the block with input data. */
216cdb7a6fcSAlan Somers len = 0;
21743e9ad02SAlan Somers while ((!feof(fp)) && len < BSZ) {
218cdb7a6fcSAlan Somers p = tl->l + len;
21943e9ad02SAlan Somers len += fread(p, 1, BSZ - len, fp);
22049a598abSAdam David if (ferror(fp)) {
22122da50cfSBrian Somers ierr(fn);
22249a598abSAdam David return;
22349a598abSAdam David }
2249b50d902SRodney W. Grimes }
2259b50d902SRodney W. Grimes
2269b50d902SRodney W. Grimes tl->len = len;
2279b50d902SRodney W. Grimes }
2289b50d902SRodney W. Grimes
2299b50d902SRodney W. Grimes if (enomem) {
230d0990ea9SDavid Malone warnx("warning: %jd bytes discarded", (intmax_t)enomem);
2319b50d902SRodney W. Grimes rval = 1;
2329b50d902SRodney W. Grimes }
2339b50d902SRodney W. Grimes
2349b50d902SRodney W. Grimes /*
235cdb7a6fcSAlan Somers * Now print the lines in reverse order
236cdb7a6fcSAlan Somers * Outline:
237cdb7a6fcSAlan Somers * Scan backward for "\n",
238cdb7a6fcSAlan Somers * print forward to the end of the buffers
239cdb7a6fcSAlan Somers * free any buffers that start after the "\n" just found
240cdb7a6fcSAlan Somers * Loop
2419b50d902SRodney W. Grimes */
242cdb7a6fcSAlan Somers tl = TAILQ_LAST(&head, bfhead);
243cdb7a6fcSAlan Somers first = TAILQ_FIRST(&head);
244cdb7a6fcSAlan Somers while (tl != NULL) {
24543e9ad02SAlan Somers struct bfelem *temp;
24643e9ad02SAlan Somers
247cdb7a6fcSAlan Somers for (p = tl->l + tl->len - 1, llen = 0; p >= tl->l;
248cdb7a6fcSAlan Somers --p, ++llen) {
249cdb7a6fcSAlan Somers int start = (tl == first && p == tl->l);
250cdb7a6fcSAlan Somers
251cdb7a6fcSAlan Somers if ((*p == '\n') || start) {
252cdb7a6fcSAlan Somers struct bfelem *tr;
253cdb7a6fcSAlan Somers
254d23662ecSAlan Somers if (llen && start && *p != '\n')
255cdb7a6fcSAlan Somers WR(p, llen + 1);
256d23662ecSAlan Somers else if (llen) {
2579b50d902SRodney W. Grimes WR(p + 1, llen);
258d23662ecSAlan Somers if (start && *p == '\n')
259d23662ecSAlan Somers WR(p, 1);
260d23662ecSAlan Somers }
261cdb7a6fcSAlan Somers tr = TAILQ_NEXT(tl, entries);
2629b50d902SRodney W. Grimes llen = 0;
263cdb7a6fcSAlan Somers if (tr != NULL) {
264cdb7a6fcSAlan Somers TAILQ_FOREACH_FROM_SAFE(tr, &head,
265cdb7a6fcSAlan Somers entries, temp) {
266cdb7a6fcSAlan Somers if (tr->len)
267cdb7a6fcSAlan Somers WR(&tr->l, tr->len);
268cdb7a6fcSAlan Somers TAILQ_REMOVE(&head, tr,
269cdb7a6fcSAlan Somers entries);
270cdb7a6fcSAlan Somers free(tr);
2719b50d902SRodney W. Grimes }
272cdb7a6fcSAlan Somers }
2739b50d902SRodney W. Grimes }
2749b50d902SRodney W. Grimes }
2759b50d902SRodney W. Grimes tl->len = llen;
276cdb7a6fcSAlan Somers tl = TAILQ_PREV(tl, bfhead, entries);
2779b50d902SRodney W. Grimes }
278cdb7a6fcSAlan Somers TAILQ_REMOVE(&head, first, entries);
279cdb7a6fcSAlan Somers free(first);
2809b50d902SRodney W. Grimes }
281