1169695Skan/* Pexecute test program,
2169695Skan   Copyright (C) 2005 Free Software Foundation, Inc.
3169695Skan   Written by Ian Lance Taylor <ian@airs.com>.
4169695Skan
5169695Skan   This file is part of GNU libiberty.
6169695Skan
7169695Skan   This program is free software; you can redistribute it and/or modify
8169695Skan   it under the terms of the GNU General Public License as published by
9169695Skan   the Free Software Foundation; either version 2 of the License, or
10169695Skan   (at your option) any later version.
11169695Skan
12169695Skan   This program is distributed in the hope that it will be useful,
13169695Skan   but WITHOUT ANY WARRANTY; without even the implied warranty of
14169695Skan   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15169695Skan   GNU General Public License for more details.
16169695Skan
17169695Skan   You should have received a copy of the GNU General Public License
18169695Skan   along with this program; if not, write to the Free Software
19169695Skan   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20169695Skan*/
21169695Skan
22169695Skan#ifdef HAVE_CONFIG_H
23169695Skan#include "config.h"
24169695Skan#endif
25169695Skan#include "ansidecl.h"
26169695Skan#include "libiberty.h"
27169695Skan#include <stdio.h>
28169695Skan#include <signal.h>
29169695Skan#include <errno.h>
30169695Skan#ifdef HAVE_STRING_H
31169695Skan#include <string.h>
32169695Skan#endif
33169695Skan#include <sys/types.h>
34169695Skan#ifdef HAVE_STDLIB_H
35169695Skan#include <stdlib.h>
36169695Skan#endif
37169695Skan#ifdef HAVE_UNISTD_H
38169695Skan#include <unistd.h>
39169695Skan#endif
40169695Skan#ifdef HAVE_SYS_WAIT_H
41169695Skan#include <sys/wait.h>
42169695Skan#endif
43169695Skan#ifdef HAVE_SYS_TIME_H
44169695Skan#include <sys/time.h>
45169695Skan#endif
46169695Skan#ifdef HAVE_SYS_RESOURCE_H
47169695Skan#include <sys/resource.h>
48169695Skan#endif
49169695Skan
50169695Skan#ifndef WIFSIGNALED
51169695Skan#define WIFSIGNALED(S) (((S) & 0xff) != 0 && ((S) & 0xff) != 0x7f)
52169695Skan#endif
53169695Skan#ifndef WTERMSIG
54169695Skan#define WTERMSIG(S) ((S) & 0x7f)
55169695Skan#endif
56169695Skan#ifndef WIFEXITED
57169695Skan#define WIFEXITED(S) (((S) & 0xff) == 0)
58169695Skan#endif
59169695Skan#ifndef WEXITSTATUS
60169695Skan#define WEXITSTATUS(S) (((S) & 0xff00) >> 8)
61169695Skan#endif
62169695Skan#ifndef WSTOPSIG
63169695Skan#define WSTOPSIG WEXITSTATUS
64169695Skan#endif
65169695Skan#ifndef WCOREDUMP
66169695Skan#define WCOREDUMP(S) ((S) & WCOREFLG)
67169695Skan#endif
68169695Skan#ifndef WCOREFLG
69169695Skan#define WCOREFLG 0200
70169695Skan#endif
71169695Skan
72169695Skan#ifndef EXIT_SUCCESS
73169695Skan#define EXIT_SUCCESS 0
74169695Skan#endif
75169695Skan
76169695Skan#ifndef EXIT_FAILURE
77169695Skan#define EXIT_FAILURE 1
78169695Skan#endif
79169695Skan
80169695Skan/* When this program is run with no arguments, it runs some tests of
81169695Skan   the libiberty pexecute functions.  As a test program, it simply
82169695Skan   invokes itself with various arguments.
83169695Skan
84169695Skan   argv[1]:
85169695Skan     *empty string*      Run tests, exit with success status
86169695Skan     exit                Exit success
87169695Skan     error               Exit error
88169695Skan     abort               Abort
89169695Skan     echo                Echo remaining arguments, exit success
90169695Skan     echoerr             Echo next arg to stdout, next to stderr, repeat
91169695Skan     copy                Copy stdin to stdout
92169695Skan     write               Write stdin to file named in next argument
93169695Skan*/
94169695Skan
95169695Skanstatic void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN;
96169695Skanstatic void error (int, const char *);
97169695Skanstatic void check_line (int, FILE *, const char *);
98169695Skanstatic void do_cmd (int, char **) ATTRIBUTE_NORETURN;
99169695Skan
100169695Skan/* The number of errors we have seen.  */
101169695Skan
102169695Skanstatic int error_count;
103169695Skan
104169695Skan/* Print a fatal error and exit.  LINE is the line number where we
105169695Skan   detected the error, ERRMSG is the error message to print, and ERR
106169695Skan   is 0 or an errno value to print.  */
107169695Skan
108169695Skanstatic void
109169695Skanfatal_error (int line, const char *errmsg, int err)
110169695Skan{
111169695Skan  fprintf (stderr, "test-pexecute:%d: %s", line, errmsg);
112169695Skan  if (errno != 0)
113169695Skan    fprintf (stderr, ": %s", xstrerror (err));
114169695Skan  fprintf (stderr, "\n");
115169695Skan  exit (EXIT_FAILURE);
116169695Skan}
117169695Skan
118169695Skan#define FATAL_ERROR(ERRMSG, ERR) fatal_error (__LINE__, ERRMSG, ERR)
119169695Skan
120169695Skan/* Print an error message and bump the error count.  LINE is the line
121169695Skan   number where we detected the error, ERRMSG is the error to
122169695Skan   print.  */
123169695Skan
124169695Skanstatic void
125169695Skanerror (int line, const char *errmsg)
126169695Skan{
127169695Skan  fprintf (stderr, "test-pexecute:%d: %s\n", line, errmsg);
128169695Skan  ++error_count;
129169695Skan}
130169695Skan
131169695Skan#define ERROR(ERRMSG) error (__LINE__, ERRMSG)
132169695Skan
133169695Skan/* Check a line in a file.  */
134169695Skan
135169695Skanstatic void
136169695Skancheck_line (int line, FILE *e, const char *str)
137169695Skan{
138169695Skan  const char *p;
139169695Skan  int c;
140169695Skan  char buf[1000];
141169695Skan
142169695Skan  p = str;
143169695Skan  while (1)
144169695Skan    {
145169695Skan      c = getc (e);
146169695Skan
147169695Skan      if (*p == '\0')
148169695Skan	{
149169695Skan	  if (c != '\n')
150169695Skan	    {
151169695Skan	      snprintf (buf, sizeof buf, "got '%c' when expecting newline", c);
152169695Skan	      fatal_error (line, buf, 0);
153169695Skan	    }
154169695Skan	  c = getc (e);
155169695Skan	  if (c != EOF)
156169695Skan	    {
157169695Skan	      snprintf (buf, sizeof buf, "got '%c' when expecting EOF", c);
158169695Skan	      fatal_error (line, buf, 0);
159169695Skan	    }
160169695Skan	  return;
161169695Skan	}
162169695Skan
163169695Skan      if (c != *p)
164169695Skan	{
165169695Skan	  snprintf (buf, sizeof buf, "expected '%c', got '%c'", *p, c);
166169695Skan	  fatal_error (line, buf, 0);
167169695Skan	}
168169695Skan
169169695Skan      ++p;
170169695Skan    }
171169695Skan}
172169695Skan
173169695Skan#define CHECK_LINE(E, STR) check_line (__LINE__, E, STR)
174169695Skan
175169695Skan/* Main function for the pexecute tester.  Run the tests.  */
176169695Skan
177169695Skanint
178169695Skanmain (int argc, char **argv)
179169695Skan{
180169695Skan  int trace;
181169695Skan  struct pex_obj *test_pex_tmp;
182169695Skan  int test_pex_status;
183169695Skan  FILE *test_pex_file;
184169695Skan  struct pex_obj *pex1;
185169695Skan  char *subargv[10];
186169695Skan  int status;
187169695Skan  FILE *e;
188169695Skan  int statuses[10];
189169695Skan
190169695Skan  trace = 0;
191169695Skan  if (argc > 1 && strcmp (argv[1], "-t") == 0)
192169695Skan    {
193169695Skan      trace = 1;
194169695Skan      --argc;
195169695Skan      ++argv;
196169695Skan    }
197169695Skan
198169695Skan  if (argc > 1)
199169695Skan    do_cmd (argc, argv);
200169695Skan
201169695Skan#define TEST_PEX_INIT(FLAGS, TEMPBASE)					\
202169695Skan  (((test_pex_tmp = pex_init (FLAGS, "test-pexecute", TEMPBASE))	\
203169695Skan    != NULL)								\
204169695Skan   ? test_pex_tmp							\
205169695Skan   : (FATAL_ERROR ("pex_init failed", 0), NULL))
206169695Skan
207169695Skan#define TEST_PEX_RUN(PEXOBJ, FLAGS, EXECUTABLE, ARGV, OUTNAME, ERRNAME)	\
208169695Skan  do									\
209169695Skan    {									\
210169695Skan      int err;								\
211169695Skan      const char *pex_run_err;						\
212169695Skan      if (trace)							\
213169695Skan	fprintf (stderr, "Line %d: running %s %s\n",			\
214169695Skan		 __LINE__, EXECUTABLE, ARGV[0]);			\
215169695Skan      pex_run_err = pex_run (PEXOBJ, FLAGS, EXECUTABLE, ARGV, OUTNAME,	\
216169695Skan			     ERRNAME, &err);				\
217169695Skan      if (pex_run_err != NULL)						\
218169695Skan	FATAL_ERROR (pex_run_err, err);					\
219169695Skan    }									\
220169695Skan  while (0)
221169695Skan
222169695Skan#define TEST_PEX_GET_STATUS_1(PEXOBJ)					\
223169695Skan  (pex_get_status (PEXOBJ, 1, &test_pex_status)				\
224169695Skan   ? test_pex_status							\
225169695Skan   : (FATAL_ERROR ("pex_get_status failed", errno), 1))
226169695Skan
227169695Skan#define TEST_PEX_GET_STATUS(PEXOBJ, COUNT, VECTOR)			\
228169695Skan  do									\
229169695Skan    {									\
230169695Skan      if (!pex_get_status (PEXOBJ, COUNT, VECTOR))			\
231169695Skan	FATAL_ERROR ("pex_get_status failed", errno);			\
232169695Skan    }									\
233169695Skan  while (0)
234169695Skan
235169695Skan#define TEST_PEX_READ_OUTPUT(PEXOBJ)					\
236169695Skan  ((test_pex_file = pex_read_output (PEXOBJ, 0)) != NULL		\
237169695Skan   ? test_pex_file							\
238169695Skan   : (FATAL_ERROR ("pex_read_output failed", errno), NULL))
239169695Skan
240169695Skan  remove ("temp.x");
241169695Skan  remove ("temp.y");
242169695Skan
243169695Skan  memset (subargv, 0, sizeof subargv);
244169695Skan
245169695Skan  subargv[0] = "./test-pexecute";
246169695Skan
247169695Skan  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
248169695Skan  subargv[1] = "exit";
249169695Skan  subargv[2] = NULL;
250169695Skan  TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, NULL);
251169695Skan  status = TEST_PEX_GET_STATUS_1 (pex1);
252169695Skan  if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
253169695Skan    ERROR ("exit failed");
254169695Skan  pex_free (pex1);
255169695Skan
256169695Skan  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
257169695Skan  subargv[1] = "error";
258169695Skan  subargv[2] = NULL;
259169695Skan  TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, NULL);
260169695Skan  status = TEST_PEX_GET_STATUS_1 (pex1);
261169695Skan  if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_FAILURE)
262169695Skan    ERROR ("error test failed");
263169695Skan  pex_free (pex1);
264169695Skan
265169695Skan  /* We redirect stderr to a file to avoid an error message which is
266169695Skan     printed on mingw32 when the child calls abort.  */
267169695Skan  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
268169695Skan  subargv[1] = "abort";
269169695Skan  subargv[2] = NULL;
270169695Skan  TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, "temp.z");
271169695Skan  status = TEST_PEX_GET_STATUS_1 (pex1);
272169695Skan  if (!WIFSIGNALED (status) || WTERMSIG (status) != SIGABRT)
273169695Skan    ERROR ("abort failed");
274169695Skan  pex_free (pex1);
275169695Skan  remove ("temp.z");
276169695Skan
277169695Skan  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
278169695Skan  subargv[1] = "echo";
279169695Skan  subargv[2] = "foo";
280169695Skan  subargv[3] = NULL;
281169695Skan  TEST_PEX_RUN (pex1, 0, "./test-pexecute", subargv, NULL, NULL);
282169695Skan  e = TEST_PEX_READ_OUTPUT (pex1);
283169695Skan  CHECK_LINE (e, "foo");
284169695Skan  if (TEST_PEX_GET_STATUS_1 (pex1) != 0)
285169695Skan    ERROR ("echo exit status failed");
286169695Skan  pex_free (pex1);
287169695Skan
288169695Skan  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
289169695Skan  subargv[1] = "echo";
290169695Skan  subargv[2] = "bar";
291169695Skan  subargv[3] = NULL;
292169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
293169695Skan  subargv[1] = "copy";
294169695Skan  subargv[2] = NULL;
295169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
296169695Skan  e = TEST_PEX_READ_OUTPUT (pex1);
297169695Skan  CHECK_LINE (e, "bar");
298169695Skan  TEST_PEX_GET_STATUS (pex1, 2, statuses);
299169695Skan  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
300169695Skan      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
301169695Skan    ERROR ("copy exit status failed");
302169695Skan  pex_free (pex1);
303169695Skan  if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
304169695Skan    ERROR ("temporary files exist");
305169695Skan
306169695Skan  pex1 = TEST_PEX_INIT (0, "temp");
307169695Skan  subargv[1] = "echo";
308169695Skan  subargv[2] = "bar";
309169695Skan  subargv[3] = NULL;
310169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
311169695Skan  subargv[1] = "copy";
312169695Skan  subargv[2] = NULL;
313169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
314169695Skan  e = TEST_PEX_READ_OUTPUT (pex1);
315169695Skan  CHECK_LINE (e, "bar");
316169695Skan  TEST_PEX_GET_STATUS (pex1, 2, statuses);
317169695Skan  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
318169695Skan      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
319169695Skan    ERROR ("copy exit status failed");
320169695Skan  pex_free (pex1);
321169695Skan  if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
322169695Skan    ERROR ("temporary files exist");
323169695Skan
324169695Skan  pex1 = TEST_PEX_INIT (PEX_SAVE_TEMPS, "temp");
325169695Skan  subargv[1] = "echo";
326169695Skan  subargv[2] = "quux";
327169695Skan  subargv[3] = NULL;
328169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
329169695Skan  subargv[1] = "copy";
330169695Skan  subargv[2] = NULL;
331169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
332169695Skan  e = TEST_PEX_READ_OUTPUT (pex1);
333169695Skan  CHECK_LINE (e, "quux");
334169695Skan  TEST_PEX_GET_STATUS (pex1, 2, statuses);
335169695Skan  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
336169695Skan      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
337169695Skan    ERROR ("copy temp exit status failed");
338169695Skan  e = fopen ("temp.x", "r");
339169695Skan  if (e == NULL)
340169695Skan    FATAL_ERROR ("fopen temp.x failed in copy temp", errno);
341169695Skan  CHECK_LINE (e, "quux");
342169695Skan  fclose (e);
343169695Skan  e = fopen ("temp.y", "r");
344169695Skan  if (e == NULL)
345169695Skan    FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
346169695Skan  CHECK_LINE (e, "quux");
347169695Skan  fclose (e);
348169695Skan  pex_free (pex1);
349169695Skan  remove ("temp.x");
350169695Skan  remove ("temp.y");
351169695Skan
352169695Skan  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
353169695Skan  subargv[1] = "echoerr";
354169695Skan  subargv[2] = "one";
355169695Skan  subargv[3] = "two";
356169695Skan  subargv[4] = NULL;
357169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", "temp2.x");
358169695Skan  subargv[1] = "write";
359169695Skan  subargv[2] = "temp2.y";
360169695Skan  subargv[3] = NULL;
361169695Skan  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
362169695Skan  TEST_PEX_GET_STATUS (pex1, 2, statuses);
363169695Skan  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
364169695Skan      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
365169695Skan    ERROR ("echoerr exit status failed");
366169695Skan  pex_free (pex1);
367169695Skan  if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
368169695Skan    ERROR ("temporary files exist");
369169695Skan  e = fopen ("temp2.x", "r");
370169695Skan  if (e == NULL)
371169695Skan    FATAL_ERROR ("fopen temp2.x failed in echoerr", errno);
372169695Skan  CHECK_LINE (e, "two");
373169695Skan  fclose (e);
374169695Skan  e = fopen ("temp2.y", "r");
375169695Skan  if (e == NULL)
376169695Skan    FATAL_ERROR ("fopen temp2.y failed in echoerr", errno);
377169695Skan  CHECK_LINE (e, "one");
378169695Skan  fclose (e);
379169695Skan  remove ("temp2.x");
380169695Skan  remove ("temp2.y");
381169695Skan
382169695Skan  /* Test the old pexecute interface.  */
383169695Skan  {
384169695Skan    int pid1, pid2;
385169695Skan    char *errmsg_fmt;
386169695Skan    char *errmsg_arg;
387169695Skan    char errbuf1[1000];
388169695Skan    char errbuf2[1000];
389169695Skan
390169695Skan    subargv[1] = "echo";
391169695Skan    subargv[2] = "oldpexecute";
392169695Skan    subargv[3] = NULL;
393169695Skan    pid1 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
394169695Skan		     &errmsg_fmt, &errmsg_arg, PEXECUTE_FIRST);
395169695Skan    if (pid1 < 0)
396169695Skan      {
397169695Skan	snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
398169695Skan	snprintf (errbuf2, sizeof errbuf2, "pexecute 1 failed: %s", errbuf1);
399169695Skan	FATAL_ERROR (errbuf2, 0);
400169695Skan      }
401169695Skan
402169695Skan    subargv[1] = "write";
403169695Skan    subargv[2] = "temp.y";
404169695Skan    subargv[3] = NULL;
405169695Skan    pid2 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
406169695Skan		     &errmsg_fmt, &errmsg_arg, PEXECUTE_LAST);
407169695Skan    if (pid2 < 0)
408169695Skan      {
409169695Skan	snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
410169695Skan	snprintf (errbuf2, sizeof errbuf2, "pexecute 2 failed: %s", errbuf1);
411169695Skan	FATAL_ERROR (errbuf2, 0);
412169695Skan      }
413169695Skan
414169695Skan    if (pwait (pid1, &status, 0) < 0)
415169695Skan      FATAL_ERROR ("write pwait 1 failed", errno);
416169695Skan    if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
417169695Skan      ERROR ("write exit status 1 failed");
418169695Skan
419169695Skan    if (pwait (pid2, &status, 0) < 0)
420169695Skan      FATAL_ERROR ("write pwait 1 failed", errno);
421169695Skan    if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
422169695Skan      ERROR ("write exit status 2 failed");
423169695Skan
424169695Skan    e = fopen ("temp.y", "r");
425169695Skan    if (e == NULL)
426169695Skan      FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
427169695Skan    CHECK_LINE (e, "oldpexecute");
428169695Skan    fclose (e);
429169695Skan
430169695Skan    remove ("temp.y");
431169695Skan  }
432169695Skan
433169695Skan  if (trace)
434169695Skan    fprintf (stderr, "Exiting with status %d\n", error_count);
435169695Skan
436169695Skan  return error_count;
437169695Skan}
438169695Skan
439169695Skan/* Execute one of the special testing commands.  */
440169695Skan
441169695Skanstatic void
442169695Skando_cmd (int argc, char **argv)
443169695Skan{
444169695Skan  const char *s;
445169695Skan
446169695Skan  /* Try to prevent generating a core dump.  */
447169695Skan#ifdef RLIMIT_CORE
448169695Skan {
449169695Skan   struct rlimit r;
450169695Skan
451169695Skan   r.rlim_cur = 0;
452169695Skan   r.rlim_max = 0;
453169695Skan   setrlimit (RLIMIT_CORE, &r);
454169695Skan }
455169695Skan#endif
456169695Skan
457169695Skan  s = argv[1];
458169695Skan  if (strcmp (s, "exit") == 0)
459169695Skan    exit (EXIT_SUCCESS);
460169695Skan  else if (strcmp (s, "echo") == 0)
461169695Skan    {
462169695Skan      int i;
463169695Skan
464169695Skan      for (i = 2; i < argc; ++i)
465169695Skan	{
466169695Skan	  if (i > 2)
467169695Skan	    putchar (' ');
468169695Skan	  fputs (argv[i], stdout);
469169695Skan	}
470169695Skan      putchar ('\n');
471169695Skan      exit (EXIT_SUCCESS);
472169695Skan    }
473169695Skan  else if (strcmp (s, "echoerr") == 0)
474169695Skan    {
475169695Skan      int i;
476169695Skan
477169695Skan      for (i = 2; i < argc; ++i)
478169695Skan	{
479169695Skan	  if (i > 3)
480169695Skan	    putc (' ', (i & 1) == 0 ? stdout : stderr);
481169695Skan	  fputs (argv[i], (i & 1) == 0 ? stdout : stderr);
482169695Skan	}
483169695Skan      putc ('\n', stdout);
484169695Skan      putc ('\n', stderr);
485169695Skan      exit (EXIT_SUCCESS);
486169695Skan    }
487169695Skan  else if (strcmp (s, "error") == 0)
488169695Skan    exit (EXIT_FAILURE);
489169695Skan  else if (strcmp (s, "abort") == 0)
490169695Skan    abort ();
491169695Skan  else if (strcmp (s, "copy") == 0)
492169695Skan    {
493169695Skan      int c;
494169695Skan
495169695Skan      while ((c = getchar ()) != EOF)
496169695Skan	putchar (c);
497169695Skan      exit (EXIT_SUCCESS);
498169695Skan    }
499169695Skan  else if (strcmp (s, "write") == 0)
500169695Skan    {
501169695Skan      FILE *e;
502169695Skan      int c;
503169695Skan
504169695Skan      e = fopen (argv[2], "w");
505169695Skan      if (e == NULL)
506169695Skan	FATAL_ERROR ("fopen for write failed", errno);
507169695Skan      while ((c = getchar ()) != EOF)
508169695Skan	putc (c, e);
509169695Skan      if (fclose (e) != 0)
510169695Skan	FATAL_ERROR ("fclose for write failed", errno);
511169695Skan      exit (EXIT_SUCCESS);
512169695Skan    }
513169695Skan  else
514169695Skan    {
515169695Skan      char buf[1000];
516169695Skan
517169695Skan      snprintf (buf, sizeof buf, "unrecognized command %s", argv[1]);
518169695Skan      FATAL_ERROR (buf, 0);
519169695Skan    }
520169695Skan
521169695Skan  exit (EXIT_FAILURE);
522169695Skan}
523