1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2009 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 n; 115 register int* hp; 116 register char* cp; 117 int line; 118 Sfdisc_t tee_disc; 119 120 if (argc <= 0) 121 { 122 if (context && (tp = (Tee_t*)sh_context(context)->data)) 123 { 124 sh_context(context)->data = 0; 125 tee_cleanup(tp); 126 } 127 return 0; 128 } 129 cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_CALLBACK); 130 line = -1; 131 for (;;) 132 { 133 switch (optget(argv, usage)) 134 { 135 case 'a': 136 oflag &= ~O_TRUNC; 137 oflag |= O_APPEND; 138 continue; 139 case 'i': 140 signal(SIGINT, SIG_IGN); 141 continue; 142 case 'l': 143 line = sfset(sfstdout, 0, 0) & SF_LINE; 144 if ((line == 0) == (opt_info.num == 0)) 145 line = -1; 146 else 147 sfset(sfstdout, SF_LINE, !!opt_info.num); 148 continue; 149 case ':': 150 error(2, "%s", opt_info.arg); 151 break; 152 case '?': 153 error(ERROR_usage(2), "%s", opt_info.arg); 154 break; 155 } 156 break; 157 } 158 if (error_info.errors) 159 error(ERROR_usage(2), "%s", optusage(NiL)); 160 argv += opt_info.index; 161 argc -= opt_info.index; 162 #if _ANCIENT_BSD_COMPATIBILITY 163 if (*argv && streq(*argv, "-")) 164 { 165 signal(SIGINT, SIG_IGN); 166 argv++; 167 argc--; 168 } 169 #endif 170 if (argc > 0) 171 { 172 if (tp = (Tee_t*)stakalloc(sizeof(Tee_t) + argc * sizeof(int))) 173 { 174 memset(&tp->disc, 0, sizeof(tp->disc)); 175 tp->disc.writef = tee_write; 176 if (context) 177 sh_context(context)->data = (void*)tp; 178 tp->line = line; 179 hp = tp->fd; 180 while (cp = *argv++) 181 { 182 if ((*hp = open(cp, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 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)) && errno != EPIPE) 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