1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2008 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) 2007-11-11 $\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 56 #include <cmd.h> 57 #include <ls.h> 58 #include <sig.h> 59 60 typedef struct Tee_s 61 { 62 Sfdisc_t disc; 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 { 72 register const char* bp; 73 register const char* ep; 74 register int* hp = ((Tee_t*)handle)->fd; 75 register int fd = sffileno(fp); 76 register ssize_t r; 77 78 do 79 { 80 bp = (const char*)buf; 81 ep = bp + n; 82 while (bp < ep) 83 { 84 if ((r = write(fd, bp, ep - bp)) <= 0) 85 return(-1); 86 bp += r; 87 } 88 } while ((fd = *hp++) >= 0); 89 return(n); 90 } 91 92 int 93 b_tee(int argc, register char** argv, void* context) 94 { 95 register Tee_t* tp = 0; 96 register int oflag = O_WRONLY|O_TRUNC|O_CREAT|O_BINARY; 97 register int n; 98 register int* hp; 99 register char* cp; 100 int line; 101 Sfdisc_t tee_disc; 102 103 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 104 line = -1; 105 while (n = optget(argv, usage)) switch (n) 106 { 107 case 'a': 108 oflag &= ~O_TRUNC; 109 oflag |= O_APPEND; 110 break; 111 case 'i': 112 signal(SIGINT, SIG_IGN); 113 break; 114 case 'l': 115 line = sfset(sfstdout, 0, 0) & SF_LINE; 116 if ((line == 0) == (opt_info.num == 0)) 117 line = -1; 118 else 119 sfset(sfstdout, SF_LINE, !!opt_info.num); 120 break; 121 case ':': 122 error(2, "%s", opt_info.arg); 123 break; 124 case '?': 125 error(ERROR_usage(2), "%s", opt_info.arg); 126 break; 127 } 128 if(error_info.errors) 129 error(ERROR_usage(2), "%s", optusage(NiL)); 130 argv += opt_info.index; 131 argc -= opt_info.index; 132 #if _ANCIENT_BSD_COMPATIBILITY 133 if (*argv && streq(*argv, "-")) 134 { 135 signal(SIGINT, SIG_IGN); 136 argv++; 137 argc--; 138 } 139 #endif 140 if (argc > 0) 141 { 142 if (!(tp = (Tee_t*)stakalloc(sizeof(Tee_t) + argc * sizeof(int)))) 143 error(ERROR_exit(1), "no space"); 144 memset(&tee_disc, 0, sizeof(tee_disc)); 145 tee_disc.writef = tee_write; 146 tp->disc = tee_disc; 147 hp = tp->fd; 148 while (cp = *argv++) 149 { 150 if ((*hp = open(cp, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) 151 error(ERROR_system(0), "%s: cannot create", cp); 152 else hp++; 153 } 154 if (hp == tp->fd) 155 tp = 0; 156 else 157 { 158 *hp = -1; 159 sfdisc(sfstdout, &tp->disc); 160 } 161 } 162 if (sfmove(sfstdin, sfstdout, SF_UNBOUND, -1) < 0 || !sfeof(sfstdin) || sfsync(sfstdout)) 163 error(ERROR_system(1), "cannot copy"); 164 165 /* 166 * close files and free resources 167 */ 168 169 if (tp) 170 { 171 sfdisc(sfstdout, NiL); 172 if (line >= 0) 173 sfset(sfstdout, SF_LINE, line); 174 for(hp = tp->fd; (n = *hp) >= 0; hp++) 175 close(n); 176 } 177 return(error_info.errors); 178 } 179