busybox/coreutils/tac.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * tac implementation for busybox
   4 * tac - concatenate and print files in reverse
   5 *
   6 * Copyright (C) 2003  Yang Xiaopeng  <yxp at hanwang.com.cn>
   7 * Copyright (C) 2007  Natanael Copa  <natanael.copa@gmail.com>
   8 * Copyright (C) 2007  Tito Ragusa    <farmatito@tiscali.it>
   9 *
  10 * Licensed under GPLv2, see file LICENSE in this source tree.
  11 */
  12/* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch
  13 * http://www.uclibc.org/lists/busybox/2003-July/008813.html
  14 */
  15//config:config TAC
  16//config:       bool "tac (3.9 kb)"
  17//config:       default y
  18//config:       help
  19//config:       tac is used to concatenate and print files in reverse.
  20
  21//applet:IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac))
  22
  23//kbuild:lib-$(CONFIG_TAC) += tac.o
  24
  25//usage:#define tac_trivial_usage
  26//usage:        "[FILE]..."
  27//usage:#define tac_full_usage "\n\n"
  28//usage:        "Concatenate FILEs and print them in reverse"
  29
  30#include "libbb.h"
  31
  32/* This is a NOEXEC applet. Be very careful! */
  33
  34struct lstring {
  35        int size;
  36        char buf[1];
  37};
  38
  39int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  40int tac_main(int argc UNUSED_PARAM, char **argv)
  41{
  42        char **name;
  43        FILE *f;
  44        struct lstring *line = NULL;
  45        llist_t *list = NULL;
  46        int retval = EXIT_SUCCESS;
  47
  48#if ENABLE_DESKTOP
  49/* tac from coreutils 6.9 supports:
  50       -b, --before
  51              attach the separator before instead of after
  52       -r, --regex
  53              interpret the separator as a regular expression
  54       -s, --separator=STRING
  55              use STRING as the separator instead of newline
  56We support none, but at least we will complain or handle "--":
  57*/
  58        getopt32(argv, "");
  59        argv += optind;
  60#else
  61        argv++;
  62#endif
  63        if (!*argv)
  64                *--argv = (char *)"-";
  65        /* We will read from last file to first */
  66        name = argv;
  67        while (*name)
  68                name++;
  69
  70        do {
  71                int ch, i;
  72
  73                name--;
  74                f = fopen_or_warn_stdin(*name);
  75                if (f == NULL) {
  76                        /* error message is printed by fopen_or_warn_stdin */
  77                        retval = EXIT_FAILURE;
  78                        continue;
  79                }
  80
  81                errno = i = 0;
  82                do {
  83                        ch = fgetc(f);
  84                        if (ch != EOF) {
  85                                if (!(i & 0x7f))
  86                                        /* Grow on every 128th char */
  87                                        line = xrealloc(line, i + 0x7f + sizeof(int) + 1);
  88                                line->buf[i++] = ch;
  89                        }
  90                        if (ch == '\n' || (ch == EOF && i != 0)) {
  91                                line = xrealloc(line, i + sizeof(int));
  92                                line->size = i;
  93                                llist_add_to(&list, line);
  94                                line = NULL;
  95                                i = 0;
  96                        }
  97                } while (ch != EOF);
  98                /* fgetc sets errno to ENOENT on EOF, we don't want
  99                 * to warn on this non-error! */
 100                if (errno && errno != ENOENT) {
 101                        bb_simple_perror_msg(*name);
 102                        retval = EXIT_FAILURE;
 103                }
 104        } while (name != argv);
 105
 106        while (list) {
 107                line = (struct lstring *)list->data;
 108                xwrite(STDOUT_FILENO, line->buf, line->size);
 109                if (ENABLE_FEATURE_CLEAN_UP) {
 110                        free(llist_pop(&list));
 111                } else {
 112                        list = list->link;
 113                }
 114        }
 115
 116        return retval;
 117}
 118