summaryrefslogtreecommitdiff
path: root/coreutils/tee.c (plain)
blob: 602d06737a65652913f26d0fff2292b6f90410fa
1/* vi: set sw=4 ts=4: */
2/*
3 * tee implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9//config:config TEE
10//config: bool "tee"
11//config: default y
12//config: help
13//config: tee is used to read from standard input and write
14//config: to standard output and files.
15//config:
16//config:config FEATURE_TEE_USE_BLOCK_IO
17//config: bool "Enable block I/O (larger/faster) instead of byte I/O"
18//config: default y
19//config: depends on TEE
20//config: help
21//config: Enable this option for a faster tee, at expense of size.
22
23//applet:IF_TEE(APPLET(tee, BB_DIR_USR_BIN, BB_SUID_DROP))
24
25//kbuild:lib-$(CONFIG_TEE) += tee.o
26
27/* BB_AUDIT SUSv3 compliant */
28/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */
29
30//usage:#define tee_trivial_usage
31//usage: "[-ai] [FILE]..."
32//usage:#define tee_full_usage "\n\n"
33//usage: "Copy stdin to each FILE, and also to stdout\n"
34//usage: "\n -a Append to the given FILEs, don't overwrite"
35//usage: "\n -i Ignore interrupt signals (SIGINT)"
36//usage:
37//usage:#define tee_example_usage
38//usage: "$ echo \"Hello\" | tee /tmp/foo\n"
39//usage: "$ cat /tmp/foo\n"
40//usage: "Hello\n"
41
42#include "libbb.h"
43#include "common_bufsiz.h"
44
45int tee_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
46int tee_main(int argc, char **argv)
47{
48 const char *mode = "w\0a";
49 FILE **files;
50 FILE **fp;
51 char **names;
52 char **np;
53 char retval;
54//TODO: make unconditional
55#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
56 ssize_t c;
57# define buf bb_common_bufsiz1
58 setup_common_bufsiz();
59#else
60 int c;
61#endif
62 retval = getopt32(argv, "ia"); /* 'a' must be 2nd */
63 argc -= optind;
64 argv += optind;
65
66 mode += (retval & 2); /* Since 'a' is the 2nd option... */
67
68 if (retval & 1) {
69 signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */
70 }
71 retval = EXIT_SUCCESS;
72 /* gnu tee ignores SIGPIPE in case one of the output files is a pipe
73 * that doesn't consume all its input. Good idea... */
74 signal(SIGPIPE, SIG_IGN);
75
76 /* Allocate an array of FILE *'s, with one extra for a sentinel. */
77 fp = files = xzalloc(sizeof(FILE *) * (argc + 2));
78 np = names = argv - 1;
79
80 files[0] = stdout;
81 goto GOT_NEW_FILE;
82 do {
83 *fp = stdout;
84 if (NOT_LONE_DASH(*argv)) {
85 *fp = fopen_or_warn(*argv, mode);
86 if (*fp == NULL) {
87 retval = EXIT_FAILURE;
88 argv++;
89 continue;
90 }
91 }
92 *np = *argv++;
93 GOT_NEW_FILE:
94 setbuf(*fp, NULL); /* tee must not buffer output. */
95 fp++;
96 np++;
97 } while (*argv);
98 /* names[0] will be filled later */
99
100#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
101 while ((c = safe_read(STDIN_FILENO, buf, COMMON_BUFSIZE)) > 0) {
102 fp = files;
103 do
104 fwrite(buf, 1, c, *fp);
105 while (*++fp);
106 }
107 if (c < 0) { /* Make sure read errors are signaled. */
108 retval = EXIT_FAILURE;
109 }
110#else
111 setvbuf(stdout, NULL, _IONBF, 0);
112 while ((c = getchar()) != EOF) {
113 fp = files;
114 do
115 putc(c, *fp);
116 while (*++fp);
117 }
118#endif
119
120 /* Now we need to check for i/o errors on stdin and the various
121 * output files. Since we know that the first entry in the output
122 * file table is stdout, we can save one "if ferror" test by
123 * setting the first entry to stdin and checking stdout error
124 * status with fflush_stdout_and_exit()... although fflush()ing
125 * is unnecessary here. */
126 np = names;
127 fp = files;
128 names[0] = (char *) bb_msg_standard_input;
129 files[0] = stdin;
130 do { /* Now check for input and output errors. */
131 /* Checking ferror should be sufficient, but we may want to fclose.
132 * If we do, remember not to close stdin! */
133 die_if_ferror(*fp++, *np++);
134 } while (*fp);
135
136 fflush_stdout_and_exit(retval);
137}
138