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