1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2010 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 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) 2009-06-19 $\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 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 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 110 b_tee(int argc, register char** argv, void* context) 111 { 112 register Tee_t* tp = 0; 113 register int oflag = O_WRONLY|O_TRUNC|O_CREAT|O_BINARY; 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 if ((*hp = open(cp, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) 181 error(ERROR_system(0), "%s: cannot create", cp); 182 else 183 hp++; 184 } 185 if (hp == tp->fd) 186 tp = 0; 187 else 188 { 189 *hp = -1; 190 sfdisc(sfstdout, &tp->disc); 191 } 192 } 193 else 194 error(ERROR_exit(0), "out of space"); 195 } 196 if ((sfmove(sfstdin, sfstdout, SF_UNBOUND, -1) < 0 || !sfeof(sfstdin)) && errno != EPIPE) 197 error(ERROR_system(0), "read error"); 198 if (sfsync(sfstdout)) 199 error(ERROR_system(0), "write error"); 200 tee_cleanup(tp); 201 return error_info.errors; 202 } 203