19b50d902SRodney W. Grimes /*- 2*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3*8a16b7a1SPedro 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 3513685eeeSDavid Malone #if 0 369b50d902SRodney W. Grimes #ifndef lint 3713685eeeSDavid Malone static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93"; 3813685eeeSDavid Malone #endif /* not lint */ 39ea7cc495SPhilippe Charnier #endif 409b50d902SRodney W. Grimes 4113685eeeSDavid Malone #include <sys/cdefs.h> 4213685eeeSDavid Malone __FBSDID("$FreeBSD$"); 4313685eeeSDavid Malone 449b50d902SRodney W. Grimes #include <sys/param.h> 45cdb7a6fcSAlan Somers #include <sys/queue.h> 469b50d902SRodney W. Grimes #include <sys/stat.h> 479b50d902SRodney W. Grimes #include <sys/mman.h> 489b50d902SRodney W. Grimes 49814e3a92SMark Murray #include <err.h> 509b50d902SRodney W. Grimes #include <errno.h> 51814e3a92SMark Murray #include <limits.h> 52d0990ea9SDavid Malone #include <stdint.h> 539b50d902SRodney W. Grimes #include <stdio.h> 549b50d902SRodney W. Grimes #include <stdlib.h> 559b50d902SRodney W. Grimes #include <string.h> 56814e3a92SMark Murray #include <unistd.h> 57814e3a92SMark Murray 589b50d902SRodney W. Grimes #include "extern.h" 599b50d902SRodney W. Grimes 6022da50cfSBrian Somers static void r_buf(FILE *, const char *); 6122da50cfSBrian Somers static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *); 629b50d902SRodney W. Grimes 639b50d902SRodney W. Grimes /* 649b50d902SRodney W. Grimes * reverse -- display input in reverse order by line. 659b50d902SRodney W. Grimes * 669b50d902SRodney W. Grimes * There are six separate cases for this -- regular and non-regular 679b50d902SRodney W. Grimes * files by bytes, lines or the whole file. 689b50d902SRodney W. Grimes * 699b50d902SRodney W. Grimes * BYTES display N bytes 709b50d902SRodney W. Grimes * REG mmap the file and display the lines 719b50d902SRodney W. Grimes * NOREG cyclically read characters into a wrap-around buffer 729b50d902SRodney W. Grimes * 739b50d902SRodney W. Grimes * LINES display N lines 749b50d902SRodney W. Grimes * REG mmap the file and display the lines 759b50d902SRodney W. Grimes * NOREG cyclically read lines into a wrap-around array of buffers 769b50d902SRodney W. Grimes * 779b50d902SRodney W. Grimes * FILE display the entire file 789b50d902SRodney W. Grimes * REG mmap the file and display the lines 799b50d902SRodney W. Grimes * NOREG cyclically read input into a linked list of buffers 809b50d902SRodney W. Grimes */ 819b50d902SRodney W. Grimes void 8222da50cfSBrian Somers reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 839b50d902SRodney W. Grimes { 849b50d902SRodney W. Grimes if (style != REVERSE && off == 0) 859b50d902SRodney W. Grimes return; 869b50d902SRodney W. Grimes 879b50d902SRodney W. Grimes if (S_ISREG(sbp->st_mode)) 8822da50cfSBrian Somers r_reg(fp, fn, style, off, sbp); 899b50d902SRodney W. Grimes else 909b50d902SRodney W. Grimes switch(style) { 919b50d902SRodney W. Grimes case FBYTES: 929b50d902SRodney W. Grimes case RBYTES: 9322da50cfSBrian Somers bytes(fp, fn, off); 949b50d902SRodney W. Grimes break; 959b50d902SRodney W. Grimes case FLINES: 969b50d902SRodney W. Grimes case RLINES: 9722da50cfSBrian Somers lines(fp, fn, off); 989b50d902SRodney W. Grimes break; 999b50d902SRodney W. Grimes case REVERSE: 10022da50cfSBrian Somers r_buf(fp, fn); 1019b50d902SRodney W. Grimes break; 102814e3a92SMark Murray default: 103b77b9b9aSMurray Stokely break; 1049b50d902SRodney W. Grimes } 1059b50d902SRodney W. Grimes } 1069b50d902SRodney W. Grimes 1079b50d902SRodney W. Grimes /* 1089b50d902SRodney W. Grimes * r_reg -- display a regular file in reverse order by line. 1099b50d902SRodney W. Grimes */ 1109b50d902SRodney W. Grimes static void 11122da50cfSBrian Somers r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp) 1129b50d902SRodney W. Grimes { 113726098d3SDavid Malone struct mapinfo map; 114726098d3SDavid Malone off_t curoff, size, lineend; 115726098d3SDavid Malone int i; 1169b50d902SRodney W. Grimes 1179b50d902SRodney W. Grimes if (!(size = sbp->st_size)) 1189b50d902SRodney W. Grimes return; 1199b50d902SRodney W. Grimes 120726098d3SDavid Malone map.start = NULL; 121726098d3SDavid Malone map.mapoff = map.maxoff = size; 122726098d3SDavid Malone map.fd = fileno(fp); 1232277edc8SAlan Somers map.maplen = 0; 124726098d3SDavid Malone 125726098d3SDavid Malone /* 126726098d3SDavid Malone * Last char is special, ignore whether newline or not. Note that 127726098d3SDavid Malone * size == 0 is dealt with above, and size == 1 sets curoff to -1. 128726098d3SDavid Malone */ 129726098d3SDavid Malone curoff = size - 2; 130726098d3SDavid Malone lineend = size; 131726098d3SDavid Malone while (curoff >= 0) { 132d0990ea9SDavid Malone if (curoff < map.mapoff || 133d0990ea9SDavid Malone curoff >= map.mapoff + (off_t)map.maplen) { 134726098d3SDavid Malone if (maparound(&map, curoff) != 0) { 13522da50cfSBrian Somers ierr(fn); 1369b50d902SRodney W. Grimes return; 1379b50d902SRodney W. Grimes } 138726098d3SDavid Malone } 139726098d3SDavid Malone for (i = curoff - map.mapoff; i >= 0; i--) { 140726098d3SDavid Malone if (style == RBYTES && --off == 0) 141726098d3SDavid Malone break; 142726098d3SDavid Malone if (map.start[i] == '\n') 143726098d3SDavid Malone break; 144726098d3SDavid Malone } 145726098d3SDavid Malone /* `i' is either the map offset of a '\n', or -1. */ 146726098d3SDavid Malone curoff = map.mapoff + i; 147726098d3SDavid Malone if (i < 0) 148726098d3SDavid Malone continue; 1499b50d902SRodney W. Grimes 150726098d3SDavid Malone /* Print the line and update offsets. */ 151726098d3SDavid Malone if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) { 15222da50cfSBrian Somers ierr(fn); 1539b50d902SRodney W. Grimes return; 1549b50d902SRodney W. Grimes } 155726098d3SDavid Malone lineend = curoff + 1; 156726098d3SDavid Malone curoff--; 1579b50d902SRodney W. Grimes 158726098d3SDavid Malone if (style == RLINES) 159726098d3SDavid Malone off--; 1609b50d902SRodney W. Grimes 161726098d3SDavid Malone if (off == 0 && style != REVERSE) { 162726098d3SDavid Malone /* Avoid printing anything below. */ 163726098d3SDavid Malone curoff = 0; 1649b50d902SRodney W. Grimes break; 1659b50d902SRodney W. Grimes } 1669b50d902SRodney W. Grimes } 167726098d3SDavid Malone if (curoff < 0 && mapprint(&map, 0, lineend) != 0) { 16822da50cfSBrian Somers ierr(fn); 169726098d3SDavid Malone return; 170726098d3SDavid Malone } 171726098d3SDavid Malone if (map.start != NULL && munmap(map.start, map.maplen)) 17222da50cfSBrian Somers ierr(fn); 1739b50d902SRodney W. Grimes } 1749b50d902SRodney W. Grimes 17543e9ad02SAlan Somers #define BSZ (128 * 1024) 176cdb7a6fcSAlan Somers typedef struct bfelem { 177cdb7a6fcSAlan Somers TAILQ_ENTRY(bfelem) entries; 178cdb7a6fcSAlan Somers size_t len; 17943e9ad02SAlan Somers char l[BSZ]; 180cdb7a6fcSAlan Somers } bfelem_t; 1819b50d902SRodney W. Grimes 1829b50d902SRodney W. Grimes /* 1839b50d902SRodney W. Grimes * r_buf -- display a non-regular file in reverse order by line. 1849b50d902SRodney W. Grimes * 1859b50d902SRodney W. Grimes * This is the function that saves the entire input, storing the data in a 1869b50d902SRodney W. Grimes * doubly linked list of buffers and then displays them in reverse order. 1879b50d902SRodney W. Grimes * It has the usual nastiness of trying to find the newlines, as there's no 1889b50d902SRodney W. Grimes * guarantee that a newline occurs anywhere in the file, let alone in any 1899b50d902SRodney W. Grimes * particular buffer. If we run out of memory, input is discarded (and the 1909b50d902SRodney W. Grimes * user warned). 1919b50d902SRodney W. Grimes */ 1929b50d902SRodney W. Grimes static void 19322da50cfSBrian Somers r_buf(FILE *fp, const char *fn) 1949b50d902SRodney W. Grimes { 19543e9ad02SAlan Somers struct bfelem *tl, *first = NULL; 19643e9ad02SAlan Somers size_t llen; 19748a1ef22SJeroen Ruigrok van der Werven char *p; 198cdb7a6fcSAlan Somers off_t enomem = 0; 199cdb7a6fcSAlan Somers TAILQ_HEAD(bfhead, bfelem) head; 2009b50d902SRodney W. Grimes 201cdb7a6fcSAlan Somers TAILQ_INIT(&head); 202cdb7a6fcSAlan Somers 203cdb7a6fcSAlan Somers while (!feof(fp)) { 20443e9ad02SAlan Somers size_t len; 20543e9ad02SAlan Somers 2069b50d902SRodney W. Grimes /* 2079b50d902SRodney W. Grimes * Allocate a new block and link it into place in a doubly 2089b50d902SRodney W. Grimes * linked list. If out of memory, toss the LRU block and 2099b50d902SRodney W. Grimes * keep going. 2109b50d902SRodney W. Grimes */ 211cdb7a6fcSAlan Somers while ((tl = malloc(sizeof(bfelem_t))) == NULL) { 212cdb7a6fcSAlan Somers first = TAILQ_FIRST(&head); 213cdb7a6fcSAlan Somers if (TAILQ_EMPTY(&head)) 214ac551270SPeter Wemm err(1, "malloc"); 215cdb7a6fcSAlan Somers enomem += first->len; 216cdb7a6fcSAlan Somers TAILQ_REMOVE(&head, first, entries); 217cdb7a6fcSAlan Somers free(first); 2182277edc8SAlan Somers } 219cdb7a6fcSAlan Somers TAILQ_INSERT_TAIL(&head, tl, entries); 2209b50d902SRodney W. Grimes 2219b50d902SRodney W. Grimes /* Fill the block with input data. */ 222cdb7a6fcSAlan Somers len = 0; 22343e9ad02SAlan Somers while ((!feof(fp)) && len < BSZ) { 224cdb7a6fcSAlan Somers p = tl->l + len; 22543e9ad02SAlan Somers len += fread(p, 1, BSZ - len, fp); 22649a598abSAdam David if (ferror(fp)) { 22722da50cfSBrian Somers ierr(fn); 22849a598abSAdam David return; 22949a598abSAdam David } 2309b50d902SRodney W. Grimes } 2319b50d902SRodney W. Grimes 2329b50d902SRodney W. Grimes tl->len = len; 2339b50d902SRodney W. Grimes } 2349b50d902SRodney W. Grimes 2359b50d902SRodney W. Grimes if (enomem) { 236d0990ea9SDavid Malone warnx("warning: %jd bytes discarded", (intmax_t)enomem); 2379b50d902SRodney W. Grimes rval = 1; 2389b50d902SRodney W. Grimes } 2399b50d902SRodney W. Grimes 2409b50d902SRodney W. Grimes /* 241cdb7a6fcSAlan Somers * Now print the lines in reverse order 242cdb7a6fcSAlan Somers * Outline: 243cdb7a6fcSAlan Somers * Scan backward for "\n", 244cdb7a6fcSAlan Somers * print forward to the end of the buffers 245cdb7a6fcSAlan Somers * free any buffers that start after the "\n" just found 246cdb7a6fcSAlan Somers * Loop 2479b50d902SRodney W. Grimes */ 248cdb7a6fcSAlan Somers tl = TAILQ_LAST(&head, bfhead); 249cdb7a6fcSAlan Somers first = TAILQ_FIRST(&head); 250cdb7a6fcSAlan Somers while (tl != NULL) { 25143e9ad02SAlan Somers struct bfelem *temp; 25243e9ad02SAlan Somers 253cdb7a6fcSAlan Somers for (p = tl->l + tl->len - 1, llen = 0; p >= tl->l; 254cdb7a6fcSAlan Somers --p, ++llen) { 255cdb7a6fcSAlan Somers int start = (tl == first && p == tl->l); 256cdb7a6fcSAlan Somers 257cdb7a6fcSAlan Somers if ((*p == '\n') || start) { 258cdb7a6fcSAlan Somers struct bfelem *tr; 259cdb7a6fcSAlan Somers 26043e9ad02SAlan Somers if (start && llen) 261cdb7a6fcSAlan Somers WR(p, llen + 1); 262cdb7a6fcSAlan Somers else if (llen) 2639b50d902SRodney W. Grimes WR(p + 1, llen); 264cdb7a6fcSAlan Somers tr = TAILQ_NEXT(tl, entries); 2659b50d902SRodney W. Grimes llen = 0; 266cdb7a6fcSAlan Somers if (tr != NULL) { 267cdb7a6fcSAlan Somers TAILQ_FOREACH_FROM_SAFE(tr, &head, 268cdb7a6fcSAlan Somers entries, temp) { 269cdb7a6fcSAlan Somers if (tr->len) 270cdb7a6fcSAlan Somers WR(&tr->l, tr->len); 271cdb7a6fcSAlan Somers TAILQ_REMOVE(&head, tr, 272cdb7a6fcSAlan Somers entries); 273cdb7a6fcSAlan Somers free(tr); 2749b50d902SRodney W. Grimes } 275cdb7a6fcSAlan Somers } 2769b50d902SRodney W. Grimes } 2779b50d902SRodney W. Grimes } 2789b50d902SRodney W. Grimes tl->len = llen; 279cdb7a6fcSAlan Somers tl = TAILQ_PREV(tl, bfhead, entries); 2809b50d902SRodney W. Grimes } 281cdb7a6fcSAlan Somers TAILQ_REMOVE(&head, first, entries); 282cdb7a6fcSAlan Somers free(first); 2839b50d902SRodney W. Grimes } 284