Update menuconfig items with approximate applet sizes
[oweals/busybox.git] / coreutils / tac.c
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 (4.1 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;
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 }