xref: /vim-8.2.3635/src/tee/tee.c (revision cf2d8dee)
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 <fcntl.h>
36 
37 #ifdef _WIN32
38 # define sysconf(x) -1
39 #endif
40 
41 void usage(void)
42 {
43 	fprintf(stderr,
44 "tee usage:\n\
45 \ttee [-a] file ... file_n\n\
46 \n\
47 \t-a\tappend to files instead of truncating\n\
48 \nTee reads its input, and writes to each of the specified files,\n\
49 as well as to the standard output.\n\
50 \n\
51 This version supplied with Vim 4.2 to make ':make' possible.\n\
52 For a more complete and stable version, consider getting\n\
53 [a port of] the GNU shellutils package.\n\
54 ");
55 }
56 
57 /*
58  * fread only returns when count is read or at EOF.
59  * We could use fgets, but I want to be able to handle binary blubber.
60  */
61 
62 int
63 myfread(char *buf, int elsize /*ignored*/, int max, FILE *fp)
64 {
65 	int	c;
66 	int	n = 0;
67 
68 	while ((n < max) && ((c = getchar()) != EOF))
69 	{
70 		*(buf++) = c;
71 		n++;
72 		if (c == '\n' || c == '\r')
73 			break;
74 	}
75 	return n;
76 }
77 
78 
79 void
80 main(int argc, char *argv[])
81 {
82 	int	append = 0;
83 	int	numfiles;
84 	int	opt;
85 	int	maxfiles;
86 	FILE	**filepointers;
87 	int	i;
88 	char	buf[BUFSIZ];
89 	int	n;
90 	int	optind = 1;
91 
92 	for (i = 1; i < argc; i++)
93 	{
94 		if (argv[i][0] != '-')
95 			break;
96 		if (!strcmp(argv[i], "-a"))
97 			append++;
98 		else
99 			usage();
100 		optind++;
101 	}
102 
103 	numfiles = argc - optind;
104 
105 	if (numfiles == 0)
106 	{
107 		fprintf(stderr, "doesn't make much sense using tee without any file name arguments...\n");
108 		usage();
109 		exit(2);
110 	}
111 
112 	maxfiles = sysconf(_SC_OPEN_MAX);	/* or fill in 10 or so */
113 	if (maxfiles < 0)
114 		maxfiles = 10;
115 	if (numfiles + 3 > maxfiles)	/* +3 accounts for stdin, out, err */
116 	{
117 		fprintf(stderr, "Sorry, there is a limit of max %d files.\n", maxfiles - 3);
118 		exit(1);
119 	}
120 	filepointers = calloc(numfiles, sizeof(FILE *));
121 	if (filepointers == NULL)
122 	{
123 		fprintf(stderr, "Error allocating memory for %d files\n", numfiles);
124 		exit(1);
125 	}
126 	for (i = 0; i < numfiles; i++)
127 	{
128 		filepointers[i] = fopen(argv[i+optind], append ? "ab" : "wb");
129 		if (filepointers[i] == NULL)
130 		{
131 			fprintf(stderr, "Can't open \"%s\"\n", argv[i+optind]);
132 			exit(1);
133 		}
134 	}
135 	setmode(fileno(stdin),  O_BINARY);
136 	fflush(stdout);	/* needed for _fsetmode(stdout) */
137 	setmode(fileno(stdout),  O_BINARY);
138 
139 	while ((n = myfread(buf, sizeof(char), sizeof(buf), stdin)) > 0)
140 	{
141 		fwrite(buf, sizeof(char), n, stdout);
142 		fflush(stdout);
143 		for (i = 0; i < numfiles; i++)
144 		{
145 			if (filepointers[i] &&
146 			     fwrite(buf, sizeof(char), n, filepointers[i]) != n)
147 			{
148 				fprintf(stderr, "Error writing to file \"%s\"\n", argv[i+optind]);
149 				fclose(filepointers[i]);
150 				filepointers[i] = NULL;
151 			}
152 		}
153 	}
154 	for (i = 0; i < numfiles; i++)
155 	{
156 		if (filepointers[i])
157 			fclose(filepointers[i]);
158 	}
159 
160 	exit(0);
161 }
162