1/* vim:set ts=4 sw=4: 2 * 3 * Copyright (c) 1996, Paul Slootman 4 * 5 * Author: Paul Slootman 6 * (paul@wurtel.hobby.nl, paul@murphy.nl, paulS@toecompst.nl) 7 * 8 * This source code is released into the public domain. It is provided on an 9 * as-is basis and no responsibility is accepted for its failure to perform 10 * as expected. It is worth at least as much as you paid for it! 11 * 12 * tee.c - pipe fitting 13 * 14 * tee reads stdin, and writes what it reads to each of the specified 15 * files. The primary reason of existence for this version is a quick 16 * and dirty implementation to distribute with Vim, to make one of the 17 * most useful features of Vim possible on OS/2: quickfix. 18 * 19 * Of course, not using tee but instead redirecting make's output directly 20 * into a temp file and then processing that is possible, but if we have a 21 * system capable of correctly piping (unlike DOS, for example), why not 22 * use it as well as possible? This tee should also work on other systems, 23 * but it's not been tested there, only on OS/2. 24 * 25 * tee is also available in the GNU shellutils package, which is available 26 * precompiled for OS/2. That one probably works better. 27 */ 28 29#include <unistd.h> 30#include <malloc.h> 31#include <stdio.h> 32 33void usage(void) 34{ 35 fprintf(stderr, 36"tee usage:\n\ 37\ttee [-a] file ... file_n\n\ 38\n\ 39\t-a\tappend to files instead of truncating\n\ 40\nTee reads its input, and writes to each of the specified files,\n\ 41as well as to the standard output.\n\ 42\n\ 43This version supplied with Vim 4.2 to make ':make' possible.\n\ 44For a more complete and stable version, consider getting\n\ 45[a port of] the GNU shellutils package.\n\ 46"); 47} 48 49/* 50 * fread only returns when count is read or at EOF. 51 * We could use fgets, but I want to be able to handle binary blubber. 52 */ 53 54int 55myfread(char *buf, int elsize /*ignored*/, int max, FILE *fp) 56{ 57 int c; 58 int n = 0; 59 60 while ((n < max) && ((c = getchar()) != EOF)) 61 { 62 *(buf++) = c; 63 n++; 64 if (c == '\n' || c == '\r') 65 break; 66 } 67 return n; 68} 69 70 71void 72main(int argc, char *argv[]) 73{ 74 int append = 0; 75 int numfiles; 76 int opt; 77 int maxfiles; 78 FILE **filepointers; 79 int i; 80 char buf[BUFSIZ]; 81 int n; 82 extern int optind; 83 84 while ((opt = getopt(argc, argv, "a")) != EOF) 85 { 86 switch (opt) 87 { 88 case 'a': append++; 89 break; 90 default: usage(); 91 exit(2); 92 } 93 } 94 95 numfiles = argc - optind; 96 97 if (numfiles == 0) 98 { 99 fprintf(stderr, "doesn't make much sense using tee without any file name arguments...\n"); 100 usage(); 101 exit(2); 102 } 103 104 maxfiles = sysconf(_SC_OPEN_MAX); /* or fill in 10 or so */ 105 if (maxfiles < 0) 106 maxfiles = 10; 107 if (numfiles + 3 > maxfiles) /* +3 accounts for stdin, out, err */ 108 { 109 fprintf(stderr, "Sorry, there is a limit of max %d files.\n", maxfiles - 3); 110 exit(1); 111 } 112 filepointers = calloc(numfiles, sizeof(FILE *)); 113 if (filepointers == NULL) 114 { 115 fprintf(stderr, "Error allocating memory for %d files\n", numfiles); 116 exit(1); 117 } 118 for (i = 0; i < numfiles; i++) 119 { 120 filepointers[i] = fopen(argv[i+optind], append ? "ab" : "wb"); 121 if (filepointers[i] == NULL) 122 { 123 fprintf(stderr, "Can't open \"%s\"\n", argv[i+optind]); 124 exit(1); 125 } 126 } 127 _fsetmode(stdin, "b"); 128 fflush(stdout); /* needed for _fsetmode(stdout) */ 129 _fsetmode(stdout, "b"); 130 131 while ((n = myfread(buf, sizeof(char), sizeof(buf), stdin)) > 0) 132 { 133 fwrite(buf, sizeof(char), n, stdout); 134 fflush(stdout); 135 for (i = 0; i < numfiles; i++) 136 { 137 if (filepointers[i] && 138 fwrite(buf, sizeof(char), n, filepointers[i]) != n) 139 { 140 fprintf(stderr, "Error writing to file \"%s\"\n", argv[i+optind]); 141 fclose(filepointers[i]); 142 filepointers[i] = NULL; 143 } 144 } 145 } 146 for (i = 0; i < numfiles; i++) 147 { 148 if (filepointers[i]) 149 fclose(filepointers[i]); 150 } 151 152 exit(0); 153} 154