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