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