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