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