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