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