1/* tee - duplicate standard input */
2
3/* See Makefile for compilation details. */
4
5#include "config.h"
6
7#include "bashtypes.h"
8#include "posixstat.h"
9#include "filecntl.h"
10
11#include <signal.h>
12
13#if defined (HAVE_UNISTD_H)
14#  include <unistd.h>
15#endif
16
17#include "bashansi.h"
18
19#include <stdio.h>
20#include <errno.h>
21
22#include "builtins.h"
23#include "shell.h"
24#include "bashgetopt.h"
25
26#if !defined (errno)
27extern int errno;
28#endif
29
30typedef struct flist {
31  struct flist *next;
32  int fd;
33  char *fname;
34} FLIST;
35
36static FLIST *tee_flist;
37
38#define TEE_BUFSIZE	8192
39
40extern int interrupt_immediately;
41
42extern char *strerror ();
43
44tee_builtin (list)
45     WORD_LIST *list;
46{
47  int opt, append, nointr, rval, fd, fflags;
48  int n, nr, nw;
49  FLIST *fl;
50  char *buf, *bp;
51
52  char *t;
53
54  reset_internal_getopt ();
55  append = nointr = 0;
56  tee_flist = (FLIST *)NULL;
57  while ((opt = internal_getopt (list, "ai")) != -1)
58    {
59      switch (opt)
60	{
61	case 'a':
62	  append = 1;
63	  break;
64	case 'i':
65	  nointr = 1;
66	  break;
67	default:
68	  builtin_usage ();
69	  return (EX_USAGE);
70	}
71    }
72  list = loptend;
73
74  if (nointr == 0)
75    interrupt_immediately++;
76
77  buf = xmalloc (TEE_BUFSIZE);
78
79  /* Initialize output file list. */
80  fl = tee_flist = (FLIST *)xmalloc (sizeof(FLIST));
81  tee_flist->fd = 1;
82  tee_flist->fname = "stdout";
83  tee_flist->next = (FLIST *)NULL;
84
85  /* Add file arguments to list of output files. */
86  fflags = append ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC;
87  for (rval = EXECUTION_SUCCESS; list; list = list->next)
88    {
89      fd = open (list->word->word, fflags, 0666);
90      if (fd < 0)
91        {
92          builtin_error ("%s: cannot open: %s", list->word->word, strerror (errno));
93          rval = EXECUTION_FAILURE;
94        }
95      else
96        {
97          fl->next = (FLIST *)xmalloc (sizeof(FLIST));
98          fl->next->fd = fd;
99          fl->next->fname = list->word->word;
100          fl = fl->next;
101          fl->next = (FLIST *)NULL;
102        }
103    }
104
105  while ((nr = read(0, buf, TEE_BUFSIZE)) > 0)
106    for (fl = tee_flist; fl; fl = fl->next)
107      {
108	n = nr;
109	bp = buf;
110	do
111	  {
112	    if ((nw = write (fl->fd, bp, n)) == -1)
113	      {
114		builtin_error ("%s: write error: %s", fl->fname, strerror (errno));
115		rval = EXECUTION_FAILURE;
116		break;
117	      }
118            bp += nw;
119	  }
120	while (n -= nw);
121      }
122  if (nr < 0)
123    builtin_error ("read error: %s", strerror (errno));
124
125  /* Deallocate resources -- this is a builtin command. */
126  tee_flist = tee_flist->next;		/* skip bogus close of stdout */
127  while (tee_flist)
128    {
129      fl = tee_flist;
130      if (close (fl->fd) < 0)
131	{
132	  builtin_error ("%s: close_error: %s", fl->fname, strerror (errno));
133	  rval = EXECUTION_FAILURE;
134	}
135      tee_flist = tee_flist->next;
136      free (fl);
137    }
138
139  return (rval);
140}
141
142char *tee_doc[] = {
143	"Copy standard input to standard output, making a copy in each",
144	"filename argument.  If the `-a' option is gived, the specified",
145	"files are appended to, otherwise they are overwritten.  If the",
146	"`-i' option is supplied, tee ignores interrupts.",
147	(char *)NULL
148};
149
150struct builtin tee_struct = {
151	"tee",			/* builtin name */
152	tee_builtin,		/* function implementing the builtin */
153	BUILTIN_ENABLED,	/* initial flags for builtin */
154	tee_doc,		/* array of long documentation strings. */
155	"tee [-ai] [file ...]",	/* usage synopsis; becomes short_doc */
156	0			/* reserved for internal use */
157};
158