blob: 25e85f6f33c5cd45860045a0bf0247bcaa9977f8 [file] [log] [blame]
Denis Vlasenko482f2b32008-01-07 16:14:14 +00001/* vi: set sw=4 ts=4: */
2/*
3 * tac implementation for busybox
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +01004 * tac - concatenate and print files in reverse
Denis Vlasenko482f2b32008-01-07 16:14:14 +00005 *
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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020010 * Licensed under GPLv2, see file LICENSE in this source tree.
Denis Vlasenko482f2b32008-01-07 16:14:14 +000011 */
Denis Vlasenko482f2b32008-01-07 16:14:14 +000012/* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch
13 * http://www.uclibc.org/lists/busybox/2003-July/008813.html
14 */
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010015//config:config TAC
Denys Vlasenkob097a842018-12-28 03:20:17 +010016//config: bool "tac (3.9 kb)"
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010017//config: default y
18//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020019//config: tac is used to concatenate and print files in reverse.
Denys Vlasenkoaf3f4202016-11-23 14:46:56 +010020
21//applet:IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac))
22
23//kbuild:lib-$(CONFIG_TAC) += tac.o
Denis Vlasenko482f2b32008-01-07 16:14:14 +000024
Pere Orga34425382011-03-31 14:43:25 +020025//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
Denis Vlasenko482f2b32008-01-07 16:14:14 +000030#include "libbb.h"
31
32/* This is a NOEXEC applet. Be very careful! */
33
Denis Vlasenkode24bd92008-01-09 23:00:00 +000034struct lstring {
35 int size;
Denis Vlasenko62a90cd2008-03-17 09:07:36 +000036 char buf[1];
Denis Vlasenkode24bd92008-01-09 23:00:00 +000037};
38
Denis Vlasenko482f2b32008-01-07 16:14:14 +000039int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000040int tac_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko482f2b32008-01-07 16:14:14 +000041{
42 char **name;
43 FILE *f;
Denis Vlasenkode24bd92008-01-09 23:00:00 +000044 struct lstring *line = NULL;
Denis Vlasenko482f2b32008-01-07 16:14:14 +000045 llist_t *list = NULL;
46 int retval = EXIT_SUCCESS;
Denis Vlasenko474d1c52008-01-07 19:06:47 +000047
Denis Vlasenko62a90cd2008-03-17 09:07:36 +000048#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
Denis Vlasenko482f2b32008-01-07 16:14:14 +000061 argv++;
Denis Vlasenko62a90cd2008-03-17 09:07:36 +000062#endif
Denis Vlasenko482f2b32008-01-07 16:14:14 +000063 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 {
Denis Vlasenkode24bd92008-01-09 23:00:00 +000071 int ch, i;
72
Denis Vlasenko482f2b32008-01-07 16:14:14 +000073 name--;
74 f = fopen_or_warn_stdin(*name);
75 if (f == NULL) {
Denis Vlasenko62a90cd2008-03-17 09:07:36 +000076 /* error message is printed by fopen_or_warn_stdin */
Denis Vlasenko482f2b32008-01-07 16:14:14 +000077 retval = EXIT_FAILURE;
78 continue;
79 }
80
Denis Vlasenkode24bd92008-01-09 23:00:00 +000081 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 }
Denis Vlasenko62a90cd2008-03-17 09:07:36 +000090 if (ch == '\n' || (ch == EOF && i != 0)) {
Denis Vlasenkode24bd92008-01-09 23:00:00 +000091 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);
Denis Vlasenko62a90cd2008-03-17 09:07:36 +000098 /* fgetc sets errno to ENOENT on EOF, we don't want
99 * to warn on this non-error! */
Denis Vlasenko482f2b32008-01-07 16:14:14 +0000100 if (errno && errno != ENOENT) {
101 bb_simple_perror_msg(*name);
102 retval = EXIT_FAILURE;
103 }
104 } while (name != argv);
105
106 while (list) {
Denis Vlasenkode24bd92008-01-09 23:00:00 +0000107 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 }
Denis Vlasenko482f2b32008-01-07 16:14:14 +0000114 }
Denis Vlasenko474d1c52008-01-07 19:06:47 +0000115
Denis Vlasenko482f2b32008-01-07 16:14:14 +0000116 return retval;
117}