1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23 * David Korn
24 * AT&T Bell Laboratories
25 *
26 * tee
27 */
28
29 static const char usage[] =
30 "[-?\n@(#)$Id: tee (AT&T Research) 2012-05-31 $\n]"
31 USAGE_LICENSE
32 "[+NAME?tee - duplicate standard input]"
33 "[+DESCRIPTION?\btee\b copies standard input to standard output "
34 "and to zero or more files. The options determine whether "
35 "the specified files are overwritten or appended to. The "
36 "\btee\b utility does not buffer output. If writes to any "
37 "\afile\a fail, writes to other files continue although \btee\b "
38 "will exit with a non-zero exit status.]"
39 "[+?The number of \afile\a operands that can be specified is limited "
40 "by the underlying operating system.]"
41 "[a:append?Append the standard input to the given files rather "
42 "than overwriting them.]"
43 "[i:ignore-interrupts?Ignore SIGINT signal.]"
44 "[l:linebuffer?Set the standard output to be line buffered.]"
45 "\n"
46 "\n[file ...]\n"
47 "\n"
48 "[+EXIT STATUS?]{"
49 "[+0?All files copies successfully.]"
50 "[+>0?An error occurred.]"
51 "}"
52 "[+SEE ALSO?\bcat\b(1), \bsignal\b(3)]"
53 ;
54
55 #include <cmd.h>
56 #include <ls.h>
57 #include <sig.h>
58
59 typedef struct Tee_s
60 {
61 Sfdisc_t disc;
62 int line;
63 int fd[1];
64 } Tee_t;
65
66 /*
67 * This discipline writes to each file in the list given in handle
68 */
69
70 static ssize_t
tee_write(Sfio_t * fp,const void * buf,size_t n,Sfdisc_t * handle)71 tee_write(Sfio_t* fp, const void* buf, size_t n, Sfdisc_t* handle)
72 {
73 register const char* bp;
74 register const char* ep;
75 register int* hp = ((Tee_t*)handle)->fd;
76 register int fd = sffileno(fp);
77 register ssize_t r;
78
79 do
80 {
81 bp = (const char*)buf;
82 ep = bp + n;
83 while (bp < ep)
84 {
85 if ((r = write(fd, bp, ep - bp)) <= 0)
86 return -1;
87 bp += r;
88 }
89 } while ((fd = *hp++) >= 0);
90 return n;
91 }
92
93 static void
tee_cleanup(register Tee_t * tp)94 tee_cleanup(register Tee_t* tp)
95 {
96 register int* hp;
97 register int n;
98
99 if (tp)
100 {
101 sfdisc(sfstdout, NiL);
102 if (tp->line >= 0)
103 sfset(sfstdout, SF_LINE, tp->line);
104 for (hp = tp->fd; (n = *hp) >= 0; hp++)
105 close(n);
106 }
107 }
108
109 int
b_tee(int argc,register char ** argv,Shbltin_t * context)110 b_tee(int argc, register char** argv, Shbltin_t* context)
111 {
112 register Tee_t* tp = 0;
113 register int oflag = O_WRONLY|O_TRUNC|O_CREAT|O_BINARY|O_cloexec;
114 register int* hp;
115 register char* cp;
116 int line;
117
118 if (argc <= 0)
119 {
120 if (context && (tp = (Tee_t*)sh_context(context)->data))
121 {
122 sh_context(context)->data = 0;
123 tee_cleanup(tp);
124 }
125 return 0;
126 }
127 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_CALLBACK);
128 line = -1;
129 for (;;)
130 {
131 switch (optget(argv, usage))
132 {
133 case 'a':
134 oflag &= ~O_TRUNC;
135 oflag |= O_APPEND;
136 continue;
137 case 'i':
138 signal(SIGINT, SIG_IGN);
139 continue;
140 case 'l':
141 line = sfset(sfstdout, 0, 0) & SF_LINE;
142 if ((line == 0) == (opt_info.num == 0))
143 line = -1;
144 else
145 sfset(sfstdout, SF_LINE, !!opt_info.num);
146 continue;
147 case ':':
148 error(2, "%s", opt_info.arg);
149 break;
150 case '?':
151 error(ERROR_usage(2), "%s", opt_info.arg);
152 break;
153 }
154 break;
155 }
156 if (error_info.errors)
157 error(ERROR_usage(2), "%s", optusage(NiL));
158 argv += opt_info.index;
159 argc -= opt_info.index;
160 #if _ANCIENT_BSD_COMPATIBILITY
161 if (*argv && streq(*argv, "-"))
162 {
163 signal(SIGINT, SIG_IGN);
164 argv++;
165 argc--;
166 }
167 #endif
168 if (argc > 0)
169 {
170 if (tp = (Tee_t*)stakalloc(sizeof(Tee_t) + argc * sizeof(int)))
171 {
172 memset(&tp->disc, 0, sizeof(tp->disc));
173 tp->disc.writef = tee_write;
174 if (context)
175 sh_context(context)->data = (void*)tp;
176 tp->line = line;
177 hp = tp->fd;
178 while (cp = *argv++)
179 {
180 while ((*hp = open(cp, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0 && errno == EINTR)
181 errno = 0;
182 if (*hp < 0)
183 error(ERROR_system(0), "%s: cannot create", cp);
184 else
185 hp++;
186 }
187 if (hp == tp->fd)
188 tp = 0;
189 else
190 {
191 *hp = -1;
192 sfdisc(sfstdout, &tp->disc);
193 }
194 }
195 else
196 error(ERROR_exit(0), "out of space");
197 }
198 if ((sfmove(sfstdin, sfstdout, SF_UNBOUND, -1) < 0 || !sfeof(sfstdin)) && !ERROR_PIPE(errno) && errno != EINTR)
199 error(ERROR_system(0), "read error");
200 if (sfsync(sfstdout))
201 error(ERROR_system(0), "write error");
202 tee_cleanup(tp);
203 return error_info.errors;
204 }
205