summaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/tstuu.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /gnu/libexec/uucp/tstuu.c
initial import of NetBSD tree
Diffstat (limited to 'gnu/libexec/uucp/tstuu.c')
-rw-r--r--gnu/libexec/uucp/tstuu.c1603
1 files changed, 1603 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/tstuu.c b/gnu/libexec/uucp/tstuu.c
new file mode 100644
index 00000000000..4d19fe66a7e
--- /dev/null
+++ b/gnu/libexec/uucp/tstuu.c
@@ -0,0 +1,1603 @@
+/* tstuu.c
+ Test the uucp package on a UNIX system.
+
+ Copyright (C) 1991, 1992, 1993, 1994, 1995 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char tstuu_rcsid[] = "$Id: tstuu.c,v 1.1 1995/10/18 08:38:23 deraadt Exp $";
+#endif
+
+#include "sysdep.h"
+#include "system.h"
+#include "getopt.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#if HAVE_SYS_TIMES_H
+#include <sys/times.h>
+#endif
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_SELECT
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+
+#if HAVE_POLL
+#if HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#if HAVE_POLL_H
+#include <poll.h>
+#endif
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#endif
+
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+#endif
+
+#if HAVE_TIME_H
+#if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#if HAVE_UNION_WAIT
+typedef union wait wait_status;
+#else
+typedef int wait_status;
+#endif
+
+#if HAVE_STREAMS_PTYS
+#include <termio.h>
+extern char *ptsname ();
+#endif
+
+/* Get definitions for both O_NONBLOCK and O_NDELAY. */
+
+#ifndef O_NDELAY
+#ifdef FNDELAY
+#define O_NDELAY FNDELAY
+#else /* ! defined (FNDELAY) */
+#define O_NDELAY 0
+#endif /* ! defined (FNDELAY) */
+#endif /* ! defined (O_NDELAY) */
+
+#ifndef O_NONBLOCK
+#ifdef FNBLOCK
+#define O_NONBLOCK FNBLOCK
+#else /* ! defined (FNBLOCK) */
+#define O_NONBLOCK 0
+#endif /* ! defined (FNBLOCK) */
+#endif /* ! defined (O_NONBLOCK) */
+
+#if O_NDELAY == 0 && O_NONBLOCK == 0
+ #error No way to do nonblocking I/O
+#endif
+
+/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */
+#ifndef EAGAIN
+#ifndef EWOULDBLOCK
+#define EAGAIN (-1)
+#define EWOULDBLOCK (-1)
+#else /* defined (EWOULDBLOCK) */
+#define EAGAIN EWOULDBLOCK
+#endif /* defined (EWOULDBLOCK) */
+#else /* defined (EAGAIN) */
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif /* ! defined (EWOULDBLOCK) */
+#endif /* defined (EAGAIN) */
+
+#ifndef ENODATA
+#define ENODATA EAGAIN
+#endif
+
+/* Make sure we have a CLK_TCK definition, even if it makes no sense.
+ This is in case TIMES_TICK is defined as CLK_TCK. */
+#ifndef CLK_TCK
+#define CLK_TCK (60)
+#endif
+
+/* Don't try too hard to get a TIMES_TICK value; it doesn't matter
+ that much. */
+#if TIMES_TICK == 0
+#undef TIMES_TICK
+#define TIMES_TICK CLK_TCK
+#endif
+
+#if TIMES_DECLARATION_OK
+extern long times ();
+#endif
+
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+
+#if 1
+#define ZUUCICO_CMD "login uucp"
+#define UUCICO_EXECL "/bin/login", "login", "uucp"
+#else
+#define ZUUCICO_CMD "su - nuucp"
+#define UUCICO_EXECL "/bin/su", "su", "-", "nuucp"
+#endif
+
+#if ! HAVE_SELECT && ! HAVE_POLL
+ #error You need select or poll
+#endif
+
+#if ! HAVE_REMOVE
+#undef remove
+#define remove unlink
+#endif
+
+/* Buffer chain to hold data read from a uucico. */
+
+#define BUFCHARS (512)
+
+struct sbuf
+{
+ struct sbuf *qnext;
+ int cstart;
+ int cend;
+ char ab[BUFCHARS];
+};
+
+/* Local functions. */
+
+static void umake_file P((const char *zfile, int cextra));
+static void uprepare_test P((boolean fmake, int itest,
+ boolean fcall_uucico,
+ const char *zsys));
+static void ucheck_file P((const char *zfile, const char *zerr,
+ int cextra));
+static void ucheck_test P((int itest, boolean fcall_uucico));
+static RETSIGTYPE uchild P((int isig));
+static int cpshow P((char *z, int bchar));
+static void uchoose P((int *po1, int *po2));
+static long cread P((int o, struct sbuf **));
+static boolean fsend P((int o, int oslave, struct sbuf **));
+static boolean fwritable P((int o));
+static void xsystem P((const char *zcmd));
+static FILE *xfopen P((const char *zname, const char *zmode));
+
+static char *zDebug;
+static int iTest;
+static boolean fCall_uucico;
+static int iPercent;
+static pid_t iPid1, iPid2;
+static int cFrom1, cFrom2;
+static char abLogout1[sizeof "tstout /dev/ptyp0"];
+static char abLogout2[sizeof "tstout /dev/ptyp0"];
+static char *zProtocols;
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int iopt;
+ const char *zcmd1, *zcmd2;
+ const char *zsys;
+ boolean fmake = TRUE;
+ int omaster1, oslave1, omaster2, oslave2;
+ char abpty1[sizeof "/dev/ptyp0"];
+ char abpty2[sizeof "/dev/ptyp0"];
+ struct sbuf *qbuf1, *qbuf2;
+
+#if ! HAVE_TAYLOR_CONFIG
+ fprintf (stderr, "%s: only works when compiled with HAVE_TAYLOR_CONFIG\n",
+ argv[0]);
+ exit (1);
+#endif
+
+ zcmd1 = NULL;
+ zcmd2 = NULL;
+ zsys = "test2";
+
+ while ((iopt = getopt (argc, argv, "c:np:s:t:ux:1:2:")) != EOF)
+ {
+ switch (iopt)
+ {
+ case 'c':
+ zProtocols = optarg;
+ break;
+ case 'n':
+ fmake = FALSE;
+ break;
+ case 'p':
+ iPercent = (int) strtol (optarg, (char **) NULL, 10);
+ srand ((unsigned int) ixsysdep_time ((long *) NULL));
+ break;
+ case 's':
+ zsys = optarg;
+ break;
+ case 't':
+ iTest = (int) strtol (optarg, (char **) NULL, 10);
+ break;
+ case 'u':
+ fCall_uucico = TRUE;
+ break;
+ case 'x':
+ zDebug = optarg;
+ break;
+ case '1':
+ zcmd1 = optarg;
+ break;
+ case '2':
+ zcmd2 = optarg;
+ break;
+ default:
+ fprintf (stderr,
+ "Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n",
+ VERSION);
+ fprintf (stderr,
+ "Usage: tstuu [-xn] [-t #] [-u] [-1 cmd] [-2 cmd]\n");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ if (fCall_uucico && zcmd2 == NULL)
+ zcmd2 = ZUUCICO_CMD;
+
+ uprepare_test (fmake, iTest, fCall_uucico, zsys);
+
+ (void) remove ("/usr/tmp/tstuu/spool1/core");
+ (void) remove ("/usr/tmp/tstuu/spool2/core");
+
+ omaster1 = -1;
+ oslave1 = -1;
+ omaster2 = -1;
+ oslave2 = -1;
+
+#if ! HAVE_STREAMS_PTYS
+
+ {
+ char *zptyname;
+ const char *zpty;
+
+ zptyname = abpty1;
+
+ for (zpty = "pqrs"; *zpty != '\0'; ++zpty)
+ {
+ int ipty;
+
+ for (ipty = 0; ipty < 16; ipty++)
+ {
+ int om, os;
+ FILE *e;
+
+ sprintf (zptyname, "/dev/pty%c%c", *zpty,
+ "0123456789abcdef"[ipty]);
+ om = open (zptyname, O_RDWR);
+ if (om < 0)
+ continue;
+ zptyname[5] = 't';
+ os = open (zptyname, O_RDWR);
+ if (os < 0)
+ {
+ (void) close (om);
+ continue;
+ }
+
+ if (omaster1 == -1)
+ {
+ omaster1 = om;
+ oslave1 = os;
+
+ e = fopen ("/usr/tmp/tstuu/pty1", "w");
+ if (e == NULL)
+ {
+ perror ("fopen");
+ exit (EXIT_FAILURE);
+ }
+ fprintf (e, "%s", zptyname + 5);
+ if (fclose (e) != 0)
+ {
+ perror ("fclose");
+ exit (EXIT_FAILURE);
+ }
+
+ zptyname = abpty2;
+ }
+ else
+ {
+ omaster2 = om;
+ oslave2 = os;
+
+ e = fopen ("/usr/tmp/tstuu/pty2", "w");
+ if (e == NULL)
+ {
+ perror ("fopen");
+ exit (EXIT_FAILURE);
+ }
+ fprintf (e, "%s", zptyname + 5);
+ if (fclose (e) != 0)
+ {
+ perror ("fclose");
+ exit (EXIT_FAILURE);
+ }
+ break;
+ }
+ }
+
+ if (omaster1 != -1 && omaster2 != -1)
+ break;
+ }
+ }
+
+#else /* HAVE_STREAMS_PTYS */
+
+ {
+ int ipty;
+
+ for (ipty = 0; ipty < 2; ipty++)
+ {
+ int om, os;
+ FILE *e;
+ char *znam;
+ struct termio stio;
+
+ om = open ((char *) "/dev/ptmx", O_RDWR);
+ if (om < 0)
+ break;
+ znam = ptsname (om);
+ if (znam == NULL)
+ break;
+ if (unlockpt (om) != 0
+ || grantpt (om) != 0)
+ break;
+
+ os = open (znam, O_RDWR);
+ if (os < 0)
+ {
+ (void) close (om);
+ om = -1;
+ break;
+ }
+
+ if (ioctl (os, I_PUSH, "ptem") < 0
+ || ioctl(os, I_PUSH, "ldterm") < 0)
+ {
+ perror ("ioctl");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Can this really be right? */
+ memset (&stio, 0, sizeof (stio));
+ stio.c_cflag = B9600 | CS8 | CREAD | HUPCL;
+
+ if (ioctl(os, TCSETA, &stio) < 0)
+ {
+ perror ("TCSETA");
+ exit (EXIT_FAILURE);
+ }
+
+ if (omaster1 == -1)
+ {
+ strcpy (abpty1, znam);
+ omaster1 = om;
+ oslave1 = os;
+ e = fopen ("/usr/tmp/tstuu/pty1", "w");
+ if (e == NULL)
+ {
+ perror ("fopen");
+ exit (EXIT_FAILURE);
+ }
+ fprintf (e, "%s", znam + 5);
+ if (fclose (e) != 0)
+ {
+ perror ("fclose");
+ exit (EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ strcpy (abpty2, znam);
+ omaster2 = om;
+ oslave2 = os;
+ e = fopen ("/usr/tmp/tstuu/pty2", "w");
+ if (e == NULL)
+ {
+ perror ("fopen");
+ exit (EXIT_FAILURE);
+ }
+ fprintf (e, "%s", znam + 5);
+ if (fclose (e) != 0)
+ {
+ perror ("fclose");
+ exit (EXIT_FAILURE);
+ }
+ }
+ }
+ }
+
+#endif /* HAVE_STREAMS_PTYS */
+
+ if (omaster2 == -1)
+ {
+ fprintf (stderr, "No pseudo-terminals available\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Make sure we can or these into an int for the select call. Most
+ systems could use 31 instead of 15, but it should never be a
+ problem. */
+ if (omaster1 > 15 || omaster2 > 15)
+ {
+ fprintf (stderr, "File descriptors are too large\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Prepare to log out the command if it is a login command. On
+ Ultrix 4.0 uucico can only be run from login for some reason. */
+
+ if (zcmd1 == NULL
+ || strncmp (zcmd1, "login", sizeof "login" - 1) != 0)
+ abLogout1[0] = '\0';
+ else
+ sprintf (abLogout1, "tstout %s", abpty1);
+
+ if (zcmd2 == NULL
+ || strncmp (zcmd2, "login", sizeof "login" - 1) != 0)
+ abLogout2[0] = '\0';
+ else
+ sprintf (abLogout2, "tstout %s", abpty2);
+
+ iPid1 = fork ();
+ if (iPid1 < 0)
+ {
+ perror ("fork");
+ exit (EXIT_FAILURE);
+ }
+ else if (iPid1 == 0)
+ {
+ if (close (0) < 0
+ || close (1) < 0
+ || close (omaster1) < 0
+ || close (omaster2) < 0
+ || close (oslave2) < 0)
+ perror ("close");
+
+ if (dup2 (oslave1, 0) < 0
+ || dup2 (oslave1, 1) < 0)
+ perror ("dup2");
+
+ if (close (oslave1) < 0)
+ perror ("close");
+
+ /* This is said to improve the tests on Linux. */
+ sleep (3);
+
+ if (zDebug != NULL)
+ fprintf (stderr, "About to exec first process\n");
+
+ if (zcmd1 != NULL)
+ exit (system ((char *) zcmd1));
+ else
+ {
+ (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config1",
+ "-q", "-S", zsys, "-pstdin", (const char *) NULL);
+ perror ("execl failed");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ iPid2 = fork ();
+ if (iPid2 < 0)
+ {
+ perror ("fork");
+ kill (iPid1, SIGTERM);
+ exit (EXIT_FAILURE);
+ }
+ else if (iPid2 == 0)
+ {
+ if (close (0) < 0
+ || close (1) < 0
+ || close (omaster1) < 0
+ || close (oslave1) < 0
+ || close (omaster2) < 0)
+ perror ("close");
+
+ if (dup2 (oslave2, 0) < 0
+ || dup2 (oslave2, 1) < 0)
+ perror ("dup2");
+
+ if (close (oslave2) < 0)
+ perror ("close");
+
+ /* This is said to improve the tests on Linux. */
+ sleep (5);
+
+ if (zDebug != NULL)
+ fprintf (stderr, "About to exec second process\n");
+
+ if (fCall_uucico)
+ {
+ (void) execl (UUCICO_EXECL, (const char *) NULL);
+ perror ("execl failed");
+ exit (EXIT_FAILURE);
+ }
+ else if (zcmd2 != NULL)
+ exit (system ((char *) zcmd2));
+ else
+ {
+ (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config2",
+ "-lq", (const char *)NULL);
+ perror ("execl failed");
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ signal (SIGCHLD, uchild);
+
+ if (fcntl (omaster1, F_SETFL, O_NDELAY | O_NONBLOCK) < 0
+ && errno == EINVAL)
+ (void) fcntl (omaster1, F_SETFL, O_NONBLOCK);
+ if (fcntl (omaster2, F_SETFL, O_NDELAY | O_NONBLOCK) < 0
+ && errno == EINVAL)
+ (void) fcntl (omaster2, F_SETFL, O_NONBLOCK);
+
+ qbuf1 = NULL;
+ qbuf2 = NULL;
+
+ while (TRUE)
+ {
+ int o1, o2;
+ boolean fcont;
+
+ o1 = omaster1;
+ o2 = omaster2;
+ uchoose (&o1, &o2);
+
+ if (o1 == -1 && o2 == -1)
+ {
+ if (zDebug != NULL)
+ fprintf (stderr, "Five second pause\n");
+ continue;
+ }
+
+ if (o1 != -1)
+ cFrom1 += cread (omaster1, &qbuf1);
+
+ if (o2 != -1)
+ cFrom2 += cread (omaster2, &qbuf2);
+
+ do
+ {
+ fcont = FALSE;
+
+ if (qbuf1 != NULL
+ && fwritable (omaster2)
+ && fsend (omaster2, oslave2, &qbuf1))
+ fcont = TRUE;
+
+ if (qbuf2 != NULL
+ && fwritable (omaster1)
+ && fsend (omaster1, oslave1, &qbuf2))
+ fcont = TRUE;
+
+ if (! fcont
+ && (qbuf1 != NULL || qbuf2 != NULL))
+ {
+ long cgot1, cgot2;
+
+ cgot1 = cread (omaster1, &qbuf1);
+ cFrom1 += cgot1;
+ cgot2 = cread (omaster2, &qbuf2);
+ cFrom2 += cgot2;
+ fcont = TRUE;
+ }
+ }
+ while (fcont);
+ }
+
+ /*NOTREACHED*/
+}
+
+/* When a child dies, kill them both. */
+
+static RETSIGTYPE
+uchild (isig)
+ int isig;
+{
+ struct tms sbase, s1, s2;
+
+ signal (SIGCHLD, SIG_DFL);
+
+ /* Give the processes a chance to die on their own. */
+ sleep (2);
+
+ (void) kill (iPid1, SIGTERM);
+ (void) kill (iPid2, SIGTERM);
+
+ (void) times (&sbase);
+
+#if HAVE_WAITPID
+ (void) waitpid (iPid1, (pointer) NULL, 0);
+#else /* ! HAVE_WAITPID */
+#if HAVE_WAIT4
+ (void) wait4 (iPid1, (pointer) NULL, 0, (struct rusage *) NULL);
+#else /* ! HAVE_WAIT4 */
+ (void) wait ((wait_status *) NULL);
+#endif /* ! HAVE_WAIT4 */
+#endif /* ! HAVE_WAITPID */
+
+ (void) times (&s1);
+
+#if HAVE_WAITPID
+ (void) waitpid (iPid2, (pointer) NULL, 0);
+#else /* ! HAVE_WAITPID */
+#if HAVE_WAIT4
+ (void) wait4 (iPid2, (wait_status *) NULL, 0, (struct rusage *) NULL);
+#else /* ! HAVE_WAIT4 */
+ (void) wait ((wait_status *) NULL);
+#endif /* ! HAVE_WAIT4 */
+#endif /* ! HAVE_WAITPID */
+
+ (void) times (&s2);
+
+ fprintf (stderr,
+ " First child: user: %g; system: %g\n",
+ (double) (s1.tms_cutime - sbase.tms_cutime) / (double) TIMES_TICK,
+ (double) (s1.tms_cstime - sbase.tms_cstime) / (double) TIMES_TICK);
+ fprintf (stderr,
+ "Second child: user: %g; system: %g\n",
+ (double) (s2.tms_cutime - s1.tms_cutime) / (double) TIMES_TICK,
+ (double) (s2.tms_cstime - s1.tms_cstime) / (double) TIMES_TICK);
+
+ ucheck_test (iTest, fCall_uucico);
+
+ if (abLogout1[0] != '\0')
+ {
+ if (zDebug != NULL)
+ fprintf (stderr, "Executing %s\n", abLogout1);
+ (void) system (abLogout1);
+ }
+ if (abLogout2[0] != '\0')
+ {
+ if (zDebug != NULL)
+ fprintf (stderr, "Executing %s\n", abLogout2);
+ (void) system (abLogout2);
+ }
+
+ fprintf (stderr, "Wrote %d bytes from 1 to 2\n", cFrom1);
+ fprintf (stderr, "Wrote %d bytes from 2 to 1\n", cFrom2);
+
+ if (access ("/usr/tmp/tstuu/spool1/core", R_OK) == 0)
+ fprintf (stderr, "core file 1 exists\n");
+ if (access ("/usr/tmp/tstuu/spool2/core", R_OK) == 0)
+ fprintf (stderr, "core file 2 exists\n");
+
+ exit (EXIT_SUCCESS);
+}
+
+/* Open a file without error. */
+
+static FILE *
+xfopen (zname, zmode)
+ const char *zname;
+ const char *zmode;
+{
+ FILE *eret;
+
+ eret = fopen (zname, zmode);
+ if (eret == NULL)
+ {
+ perror (zname);
+ exit (EXIT_FAILURE);
+ }
+ return eret;
+}
+
+/* Close a file without error. */
+
+static void xfclose P((FILE *e));
+
+static void
+xfclose (e)
+ FILE *e;
+{
+ if (fclose (e) != 0)
+ {
+ perror ("fclose");
+ exit (EXIT_FAILURE);
+ }
+}
+
+/* Create a test file. */
+
+static void
+umake_file (z, c)
+ const char *z;
+ int c;
+{
+ int i;
+ FILE *e;
+
+ e = xfopen (z, "w");
+
+ for (i = 0; i < 256; i++)
+ {
+ int i2;
+
+ for (i2 = 0; i2 < 256; i2++)
+ putc (i, e);
+ }
+
+ for (i = 0; i < c; i++)
+ putc (i, e);
+
+ xfclose (e);
+}
+
+/* Check a test file. */
+
+static void
+ucheck_file (z, zerr, c)
+ const char *z;
+ const char *zerr;
+ int c;
+{
+ int i;
+ FILE *e;
+
+ e = xfopen (z, "r");
+
+ for (i = 0; i < 256; i++)
+ {
+ int i2;
+
+ for (i2 = 0; i2 < 256; i2++)
+ {
+ int bread;
+
+ bread = getc (e);
+ if (bread == EOF)
+ {
+ fprintf (stderr,
+ "%s: Unexpected EOF at position %d,%d\n",
+ zerr, i, i2);
+ xfclose (e);
+ return;
+ }
+ if (bread != i)
+ fprintf (stderr,
+ "%s: At position %d,%d got %d expected %d\n",
+ zerr, i, i2, bread, i);
+ }
+ }
+
+ for (i = 0; i < c; i++)
+ {
+ int bread;
+
+ bread = getc (e);
+ if (bread == EOF)
+ {
+ fprintf (stderr, "%s: Unexpected EOF at extra %d\n", zerr, i);
+ xfclose (e);
+ return;
+ }
+ if (bread != i)
+ fprintf (stderr, "%s: At extra %d got %d expected %d\n",
+ zerr, i, bread, i);
+ }
+
+ if (getc (e) != EOF)
+ fprintf (stderr, "%s: File is too long", zerr);
+
+ xfclose (e);
+}
+
+/* Prepare all the configuration files for testing. */
+
+static void
+uprepare_test (fmake, itest, fcall_uucico, zsys)
+ boolean fmake;
+ int itest;
+ boolean fcall_uucico;
+ const char *zsys;
+{
+ FILE *e;
+ const char *zuucp1, *zuucp2;
+ const char *zuux1, *zuux2;
+ char ab[1000];
+ const char *zfrom;
+ const char *zto;
+
+/* We must make /usr/tmp/tstuu world writeable or we won't be able to
+ receive files into it. */
+ (void) umask (0);
+
+#ifndef S_IWOTH
+#define S_IWOTH 02
+#endif
+
+ if (mkdir ((char *) "/usr/tmp/tstuu",
+ IPUBLIC_DIRECTORY_MODE | S_IWOTH) != 0
+ && errno != EEXIST)
+ {
+ perror ("mkdir");
+ exit (EXIT_FAILURE);
+ }
+
+ if (mkdir ((char *) "/usr/tmp/tstuu/spool1", IPUBLIC_DIRECTORY_MODE) != 0
+ && errno != EEXIST)
+ {
+ perror ("mkdir");
+ exit (EXIT_FAILURE);
+ }
+
+ if (mkdir ((char *) "/usr/tmp/tstuu/spool2", IPUBLIC_DIRECTORY_MODE) != 0
+ && errno != EEXIST)
+ {
+ perror ("mkdir");
+ exit (EXIT_FAILURE);
+ }
+
+ if (fmake)
+ {
+ e = xfopen ("/usr/tmp/tstuu/Config1", "w");
+
+ fprintf (e, "# First test configuration file\n");
+ fprintf (e, "nodename test1\n");
+ fprintf (e, "spool /usr/tmp/tstuu/spool1\n");
+ fprintf (e, "lockdir /usr/tmp/tstuu/spool1\n");
+ fprintf (e, "sysfile /usr/tmp/tstuu/System1\n");
+ fprintf (e, "sysfile /usr/tmp/tstuu/System1.2\n");
+ fprintf (e, "portfile /usr/tmp/tstuu/Port1\n");
+ (void) remove ("/usr/tmp/tstuu/Log1");
+#if ! HAVE_HDB_LOGGING
+ fprintf (e, "logfile /usr/tmp/tstuu/Log1\n");
+#else
+ fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log1/%s/%s");
+#endif
+ fprintf (e, "statfile /usr/tmp/tstuu/Stats1\n");
+ fprintf (e, "debugfile /usr/tmp/tstuu/Debug1\n");
+ fprintf (e, "callfile /usr/tmp/tstuu/Call1\n");
+ fprintf (e, "pubdir /usr/tmp/tstuu\n");
+#if HAVE_V2_CONFIG
+ fprintf (e, "v2-files no\n");
+#endif
+#if HAVE_HDB_CONFIG
+ fprintf (e, "hdb-files no\n");
+#endif
+ if (zDebug != NULL)
+ fprintf (e, "debug %s\n", zDebug);
+
+ xfclose (e);
+
+ e = xfopen ("/usr/tmp/tstuu/System1", "w");
+
+ fprintf (e, "# This file is ignored, to test multiple system files\n");
+ fprintf (e, "time never\n");
+
+ xfclose (e);
+
+ e = xfopen ("/usr/tmp/tstuu/System1.2", "w");
+
+ fprintf (e, "# First test system file\n");
+ fprintf (e, "time any\n");
+ fprintf (e, "port stdin\n");
+ fprintf (e, "# That was the defaults\n");
+ fprintf (e, "system %s\n", zsys);
+ if (! fcall_uucico)
+ {
+ FILE *eprog;
+
+ eprog = xfopen ("/usr/tmp/tstuu/Chat1", "w");
+
+ /* Wait for the other side to open the port and flush input. */
+ fprintf (eprog, "sleep 2\n");
+ fprintf (eprog,
+ "echo password $1 speed $2 1>&2\n");
+ fprintf (eprog, "echo test1\n");
+ fprintf (eprog, "exit 0\n");
+
+ xfclose (eprog);
+
+ if (chmod ("/usr/tmp/tstuu/Chat1",
+ S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
+ {
+ perror ("chmod (/usr/tmp/tstuu/Chat1)");
+ exit (EXIT_FAILURE);
+ }
+
+ fprintf (e, "chat-program /usr/tmp/tstuu/Chat1 \\P \\S\n");
+
+ fprintf (e, "chat word: \\P\n");
+ fprintf (e, "chat-fail login;\n");
+ fprintf (e, "call-login *\n");
+ fprintf (e, "call-password *\n");
+ }
+ else
+ fprintf (e, "chat \"\"\n");
+ fprintf (e, "call-transfer yes\n");
+ fprintf (e, "commands cat\n");
+ if (! fcall_uucico && iPercent == 0)
+ {
+ fprintf (e, "protocol-parameter g window 7\n");
+ fprintf (e, "protocol-parameter g packet-size 4096\n");
+ fprintf (e, "protocol-parameter j avoid \\377\n");
+ }
+ if (zProtocols != NULL)
+ fprintf (e, "protocol %s\n", zProtocols);
+
+ xfclose (e);
+
+ e = xfopen ("/usr/tmp/tstuu/Port1", "w");
+
+ fprintf (e, "port stdin\n");
+ fprintf (e, "type stdin\n");
+
+ xfclose (e);
+
+ e = xfopen ("/usr/tmp/tstuu/Call1", "w");
+
+ fprintf (e, "Call out password file\n");
+ fprintf (e, "%s test1 pass\\s1\n", zsys);
+
+ xfclose (e);
+
+ if (! fcall_uucico)
+ {
+ FILE *eprog;
+
+ e = xfopen ("/usr/tmp/tstuu/Config2", "w");
+
+ fprintf (e, "# Second test configuration file\n");
+ fprintf (e, "nodename test2\n");
+ fprintf (e, "spool /usr/tmp/tstuu/spool2\n");
+ fprintf (e, "lockdir /usr/tmp/tstuu/spool2\n");
+ fprintf (e, "sysfile /usr/tmp/tstuu/System2\n");
+ (void) remove ("/usr/tmp/tstuu/Log2");
+#if ! HAVE_HDB_LOGGING
+ fprintf (e, "logfile /usr/tmp/tstuu/Log2\n");
+#else
+ fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log2/%s/%s");
+#endif
+ fprintf (e, "statfile /usr/tmp/tstuu/Stats2\n");
+ fprintf (e, "debugfile /usr/tmp/tstuu/Debug2\n");
+ fprintf (e, "passwdfile /usr/tmp/tstuu/Pass2\n");
+ fprintf (e, "pubdir /usr/tmp/tstuu\n");
+#if HAVE_V2_CONFIG
+ fprintf (e, "v2-files no\n");
+#endif
+#if HAVE_HDB_CONFIG
+ fprintf (e, "hdb-files no\n");
+#endif
+ if (zDebug != NULL)
+ fprintf (e, "debug %s\n", zDebug);
+
+ xfclose (e);
+
+ e = xfopen ("/usr/tmp/tstuu/System2", "w");
+
+ fprintf (e, "# Second test system file\n");
+ fprintf (e, "system test1\n");
+ fprintf (e, "called-login test1\n");
+ fprintf (e, "request true\n");
+ fprintf (e, "commands cat\n");
+ if (zProtocols != NULL)
+ fprintf (e, "protocol %s\n", zProtocols);
+
+ eprog = xfopen ("/usr/tmp/tstuu/Chat2", "w");
+
+ fprintf (eprog,
+ "echo port $1 1>&2\n");
+ fprintf (eprog, "exit 0\n");
+
+ xfclose (eprog);
+
+ if (chmod ("/usr/tmp/tstuu/Chat2",
+ S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
+ {
+ perror ("chmod (/usr/tmp/tstuu/Chat2");
+ exit (EXIT_FAILURE);
+ }
+
+ fprintf (e, "called-chat-program /bin/sh /usr/tmp/tstuu/Chat2 \\Y\n");
+ fprintf (e, "time any\n");
+
+ xfclose (e);
+
+ e = xfopen ("/usr/tmp/tstuu/Pass2", "w");
+
+ fprintf (e, "# Call in password file\n");
+ fprintf (e, "test1 pass\\s1\n");
+
+ xfclose (e);
+ }
+ }
+
+ zuucp1 = "./uucp -I /usr/tmp/tstuu/Config1 -r";
+ zuux1 = "./uux -I /usr/tmp/tstuu/Config1 -r";
+
+ if (fcall_uucico)
+ {
+ zuucp2 = "/usr/bin/uucp -r";
+ zuux2 = "/usr/bin/uux -r";
+ }
+ else
+ {
+ zuucp2 = "./uucp -I /usr/tmp/tstuu/Config2 -r";
+ zuux2 = "./uux -I /usr/tmp/tstuu/Config2 -r";
+ }
+
+ /* Test transferring a file from the first system to the second. */
+ if (itest == 0 || itest == 1)
+ {
+ zfrom = "/usr/tmp/tstuu/from1";
+ if (fcall_uucico)
+ zto = "/usr/spool/uucppublic/to1";
+ else
+ zto = "/usr/tmp/tstuu/to1";
+
+ (void) remove (zto);
+ umake_file (zfrom, 0);
+
+ sprintf (ab, "%s %s %s!%s", zuucp1, zfrom, zsys, zto);
+ xsystem (ab);
+ }
+
+ /* Test having the first system request a file from the second. */
+ if (itest == 0 || itest == 2)
+ {
+ if (fcall_uucico)
+ zfrom = "/usr/spool/uucppublic/from2";
+ else
+ zfrom = "/usr/tmp/tstuu/from2";
+ zto = "/usr/tmp/tstuu/to2";
+
+ (void) remove (zto);
+ umake_file (zfrom, 3);
+
+ sprintf (ab, "%s %s!%s %s", zuucp1, zsys, zfrom, zto);
+ xsystem (ab);
+ }
+
+ /* Test having the second system send a file to the first. */
+ if (itest == 0 || itest == 3)
+ {
+ if (fcall_uucico)
+ zfrom = "/usr/spool/uucppublic/from3";
+ else
+ zfrom = "/usr/tmp/tstuu/from3";
+ zto = "/usr/tmp/tstuu/to3";
+
+ (void) remove (zto);
+ umake_file (zfrom, 5);
+
+ sprintf (ab, "%s -c \\~/from3 test1!~/to3", zuucp2);
+ xsystem (ab);
+ }
+
+ /* Test having the second system request a file from the first. */
+ if (itest == 0 || itest == 4)
+ {
+ zfrom = "/usr/tmp/tstuu/from4";
+ if (fcall_uucico)
+ zto = "/usr/spool/uucppublic/to4";
+ else
+ zto = "/usr/tmp/tstuu/to4";
+
+ (void) remove (zto);
+ umake_file (zfrom, 7);
+
+ sprintf (ab, "%s test1!%s %s", zuucp2, zfrom, zto);
+ xsystem (ab);
+ }
+
+ /* Test having the second system make an execution request. */
+ if (itest == 0 || itest == 5)
+ {
+ zfrom = "/usr/tmp/tstuu/from5";
+ if (fcall_uucico)
+ zto = "/usr/spool/uucppublic/to5";
+ else
+ zto = "/usr/tmp/tstuu/to5";
+
+ (void) remove (zto);
+ umake_file (zfrom, 11);
+
+ sprintf (ab, "%s test1!cat '<%s' '>%s'", zuux2, zfrom, zto);
+ xsystem (ab);
+ }
+
+ /* Test having the first system request a wildcard. */
+ if (itest == 0 || itest == 6)
+ {
+ const char *zfrom1, *zfrom2;
+
+ if (fcall_uucico)
+ {
+ zfrom = "/usr/spool/uucppublic/to6\\*";
+ zfrom1 = "/usr/spool/uucppublic/to6.1";
+ zfrom2 = "/usr/spool/uucppublic/to6.2";
+ }
+ else
+ {
+ zfrom = "/usr/tmp/tstuu/spool2/to6\\*";
+ zfrom1 = "/usr/tmp/tstuu/spool2/to6.1";
+ zfrom2 = "/usr/tmp/tstuu/spool2/to6.2";
+ }
+
+ umake_file (zfrom1, 100);
+ umake_file (zfrom2, 101);
+ (void) remove ("/usr/tmp/tstuu/to6.1");
+ (void) remove ("/usr/tmp/tstuu/to6.2");
+
+ sprintf (ab, "%s %s!%s /usr/tmp/tstuu", zuucp1, zsys, zfrom);
+ xsystem (ab);
+ }
+
+ /* Test having the second system request a wildcard. */
+ if (itest == 0 || itest == 7)
+ {
+ const char *zto1, *zto2;
+
+ if (fcall_uucico)
+ {
+ zto = "/usr/spool/uucppublic";
+ zto1 = "/usr/spool/uucppublic/to7.1";
+ zto2 = "/usr/spool/uucppublic/to7.2";
+ }
+ else
+ {
+ zto = "/usr/tmp/tstuu";
+ zto1 = "/usr/tmp/tstuu/to7.1";
+ zto2 = "/usr/tmp/tstuu/to7.2";
+ }
+
+ umake_file ("/usr/tmp/tstuu/spool1/to7.1", 150);
+ umake_file ("/usr/tmp/tstuu/spool1/to7.2", 155);
+ (void) remove (zto1);
+ (void) remove (zto2);
+
+ sprintf (ab, "%s test1!/usr/tmp/tstuu/spool1/to7.\\* %s", zuucp2,
+ zto);
+ xsystem (ab);
+ }
+
+ /* Test an E command. This runs cat, discarding the output. */
+ if ((itest == 0 || itest == 8) && ! fcall_uucico)
+ {
+ umake_file ("/usr/tmp/tstuu/from8", 30);
+ sprintf (ab, "%s - test2!cat < /usr/tmp/tstuu/from8", zuux1);
+ xsystem (ab);
+ }
+}
+
+/* Try to make sure the file transfers were successful. */
+
+static void
+ucheck_test (itest, fcall_uucico)
+ int itest;
+ boolean fcall_uucico;
+{
+ if (itest == 0 || itest == 1)
+ {
+ if (fcall_uucico)
+ ucheck_file ("/usr/spool/uucppublic/to1", "test 1", 0);
+ else
+ ucheck_file ("/usr/tmp/tstuu/to1", "test 1", 0);
+ }
+
+ if (itest == 0 || itest == 2)
+ ucheck_file ("/usr/tmp/tstuu/to2", "test 2", 3);
+
+ if (itest == 0 || itest == 3)
+ ucheck_file ("/usr/tmp/tstuu/to3", "test 3", 5);
+
+ if (itest == 0 || itest == 4)
+ {
+ if (fcall_uucico)
+ ucheck_file ("/usr/spool/uucppublic/to4", "test 4", 7);
+ else
+ ucheck_file ("/usr/tmp/tstuu/to4", "test 4", 7);
+ }
+
+ if (itest == 0 || itest == 6)
+ {
+ ucheck_file ("/usr/tmp/tstuu/to6.1", "test 6.1", 100);
+ ucheck_file ("/usr/tmp/tstuu/to6.2", "test 6.2", 101);
+ }
+
+ if (itest == 0 || itest == 7)
+ {
+ const char *zto1, *zto2;
+
+ if (fcall_uucico)
+ {
+ zto1 = "/usr/spool/uucppublic/to7.1";
+ zto2 = "/usr/spool/uucppublic/to7.2";
+ }
+ else
+ {
+ zto1 = "/usr/tmp/tstuu/to7.1";
+ zto2 = "/usr/tmp/tstuu/to7.2";
+ }
+
+ ucheck_file (zto1, "test 7.1", 150);
+ ucheck_file (zto2, "test 7.2", 155);
+ }
+}
+
+/* A debugging routine used when displaying buffers. */
+
+static int
+cpshow (z, ichar)
+ char *z;
+ int ichar;
+{
+ if (isprint (BUCHAR (ichar)) && ichar != '\"')
+ {
+ *z = (char) ichar;
+ return 1;
+ }
+
+ *z++ = '\\';
+
+ switch (ichar)
+ {
+ case '\n':
+ *z = 'n';
+ return 2;
+ case '\r':
+ *z = 'r';
+ return 2;
+ case '\"':
+ *z = '\"';
+ return 2;
+ default:
+ sprintf (z, "%03o", (unsigned int)(ichar & 0xff));
+ return strlen (z) + 1;
+ }
+}
+
+/* Pick one of two file descriptors which is ready for reading, or
+ return in five seconds. If the argument is ready for reading,
+ leave it alone; otherwise set it to -1. */
+
+static void
+uchoose (po1, po2)
+ int *po1;
+ int *po2;
+{
+#if HAVE_SELECT
+
+ int iread;
+ struct timeval stime;
+
+ iread = (1 << *po1) | (1 << *po2);
+ stime.tv_sec = 5;
+ stime.tv_usec = 0;
+
+ if (select ((*po1 > *po2 ? *po1 : *po2) + 1, (pointer) &iread,
+ (pointer) NULL, (pointer) NULL, &stime) < 0)
+ {
+ perror ("select");
+ uchild (SIGCHLD);
+ }
+
+ if ((iread & (1 << *po1)) == 0)
+ *po1 = -1;
+
+ if ((iread & (1 << *po2)) == 0)
+ *po2 = -1;
+
+#else /* ! HAVE_SELECT */
+
+#if HAVE_POLL
+
+ struct pollfd as[2];
+
+ as[0].fd = *po1;
+ as[0].events = POLLIN;
+ as[1].fd = *po2;
+ as[1].events = POLLIN;
+
+ if (poll (as, 2, 5 * 1000) < 0)
+ {
+ perror ("poll");
+ uchild (SIGCHLD);
+ }
+
+ if ((as[0].revents & POLLIN) == 0)
+ *po1 = -1;
+
+ if ((as[1].revents & POLLIN) == 0)
+ *po2 = -1;
+
+#endif /* HAVE_POLL */
+#endif /* ! HAVE_SELECT */
+}
+
+/* Read some data from a file descriptor. This keeps reading until
+ one of the reads gets no data. */
+
+static long
+cread (o, pqbuf)
+ int o;
+ struct sbuf **pqbuf;
+{
+ long ctotal;
+
+ while (*pqbuf != NULL && (*pqbuf)->qnext != NULL)
+ pqbuf = &(*pqbuf)->qnext;
+
+ ctotal = 0;
+
+ while (TRUE)
+ {
+ int cgot;
+
+ if (*pqbuf != NULL
+ && (*pqbuf)->cend >= sizeof (*pqbuf)->ab)
+ pqbuf = &(*pqbuf)->qnext;
+
+ if (*pqbuf == NULL)
+ {
+ *pqbuf = (struct sbuf *) malloc (sizeof (struct sbuf));
+ if (*pqbuf == NULL)
+ {
+ fprintf (stderr, "Out of memory\n");
+ uchild (SIGCHLD);
+ }
+ (*pqbuf)->qnext = NULL;
+ (*pqbuf)->cstart = 0;
+ (*pqbuf)->cend = 0;
+ }
+
+ cgot = read (o, (*pqbuf)->ab + (*pqbuf)->cend,
+ (sizeof (*pqbuf)->ab) - (*pqbuf)->cend);
+ if (cgot < 0)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)
+ cgot = 0;
+ else
+ {
+ perror ("read");
+ uchild (SIGCHLD);
+ }
+ }
+
+ if (cgot == 0)
+ return ctotal;
+
+ ctotal += cgot;
+
+ if (zDebug != NULL)
+ {
+ char abshow[325];
+ char *zfrom;
+ char *zshow;
+ int i;
+
+ zfrom = (*pqbuf)->ab + (*pqbuf)->cend;
+ zshow = abshow;
+ for (i = 0; i < cgot && i < 80; i++, zfrom++)
+ zshow += cpshow (zshow, *zfrom);
+ if (i < cgot)
+ {
+ *zshow++ = '.';
+ *zshow++ = '.';
+ *zshow++ = '.';
+ }
+ *zshow = '\0';
+ fprintf (stderr, "Read from %d: %d \"%s\"\n", o, cgot, abshow);
+ fflush (stderr);
+ }
+
+ if (iPercent > 0)
+ {
+ int i;
+ int c;
+
+ c = 0;
+ for (i = 0; i < cgot; i++)
+ {
+ if (rand () % 1000 < iPercent)
+ {
+ ++(*pqbuf)->ab[(*pqbuf)->cend + i];
+ ++c;
+ }
+ }
+ if (zDebug != NULL && c > 0)
+ fprintf (stderr, "Clobbered %d bytes\n", c);
+ }
+
+ (*pqbuf)->cend += cgot;
+
+ if (ctotal > 256)
+ return ctotal;
+ }
+}
+
+/* Write data to a file descriptor until one of the writes gets no
+ data. */
+
+static boolean
+fsend (o, oslave, pqbuf)
+ int o;
+ int oslave;
+ struct sbuf **pqbuf;
+{
+ long ctotal;
+
+ ctotal = 0;
+ while (*pqbuf != NULL)
+ {
+ int cwrite, cwrote;
+
+ if ((*pqbuf)->cstart >= (*pqbuf)->cend)
+ {
+ struct sbuf *qfree;
+
+ qfree = *pqbuf;
+ *pqbuf = (*pqbuf)->qnext;
+ free ((pointer) qfree);
+ continue;
+ }
+
+#ifdef FIONREAD
+ {
+ long cunread;
+
+ if (ioctl (oslave, FIONREAD, &cunread) < 0)
+ {
+ perror ("FIONREAD");
+ uchild (SIGCHLD);
+ }
+ if (zDebug != NULL)
+ fprintf (stderr, "%ld unread\n", cunread);
+ cwrite = 256 - cunread;
+ if (cwrite <= 0)
+ break;
+ }
+#else /* ! FIONREAD */
+ if (! fwritable (o))
+ break;
+ cwrite = 1;
+#endif /* ! FIONREAD */
+
+ if (cwrite > (*pqbuf)->cend - (*pqbuf)->cstart)
+ cwrite = (*pqbuf)->cend - (*pqbuf)->cstart;
+
+ cwrote = write (o, (*pqbuf)->ab + (*pqbuf)->cstart, cwrite);
+ if (cwrote < 0)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)
+ cwrote = 0;
+ else
+ {
+ perror ("write");
+ uchild (SIGCHLD);
+ }
+ }
+
+ if (cwrote == 0)
+ break;
+
+ ctotal += cwrote;
+ (*pqbuf)->cstart += cwrote;
+ }
+
+ if (zDebug != NULL && ctotal > 0)
+ fprintf (stderr, "Wrote %ld to %d\n", ctotal, o);
+
+ return ctotal > 0;
+}
+
+/* Check whether a file descriptor can be written to. */
+
+static boolean
+fwritable (o)
+ int o;
+{
+#if HAVE_SELECT
+
+ int iwrite;
+ struct timeval stime;
+ int cfds;
+
+ iwrite = 1 << o;
+
+ stime.tv_sec = 0;
+ stime.tv_usec = 0;
+
+ cfds = select (o + 1, (pointer) NULL, (pointer) &iwrite,
+ (pointer) NULL, &stime);
+ if (cfds < 0)
+ {
+ perror ("select");
+ uchild (SIGCHLD);
+ }
+
+ return cfds > 0;
+
+#else /* ! HAVE_SELECT */
+
+#if HAVE_POLL
+
+ struct pollfd s;
+ int cfds;
+
+ s.fd = o;
+ s.events = POLLOUT;
+
+ cfds = poll (&s, 1, 0);
+ if (cfds < 0)
+ {
+ perror ("poll");
+ uchild (SIGCHLD);
+ }
+
+ return cfds > 0;
+
+#endif /* HAVE_POLL */
+#endif /* ! HAVE_SELECT */
+}
+
+/* A version of the system command that checks for errors. */
+
+static void
+xsystem (zcmd)
+ const char *zcmd;
+{
+ int istat;
+
+ istat = system ((char *) zcmd);
+ if (istat != 0)
+ {
+ fprintf (stderr, "Command failed with status %d\n", istat);
+ fprintf (stderr, "%s\n", zcmd);
+ exit (EXIT_FAILURE);
+ }
+}