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
34 struct lstring {
35 int size;
36 char buf[1];
37 };
38
39 int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
tac_main(int argc UNUSED_PARAM,char ** argv)40 int 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
56 We 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