1/* tstuu.c
2   Test the uucp package on a UNIX system.
3
4   Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor
5
6   This file is part of the Taylor UUCP package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21
22   The author of the program may be contacted at ian@airs.com.
23   */
24
25#include "uucp.h"
26
27#if USE_RCS_ID
28const char tstuu_rcsid[] = "$Id: tstuu.c,v 1.89 2002/03/05 19:10:41 ian Rel $";
29#endif
30
31#include "sysdep.h"
32#include "system.h"
33#include "getopt.h"
34
35#include <stdio.h>
36#include <ctype.h>
37#include <errno.h>
38
39#if HAVE_SYS_TIMES_H
40#include <sys/times.h>
41#endif
42
43#if HAVE_SYS_IOCTL_H
44#include <sys/ioctl.h>
45#endif
46
47#if HAVE_SELECT
48#if HAVE_SYS_TIME_H
49#include <sys/time.h>
50#endif
51#if HAVE_SYS_SELECT_H
52#include <sys/select.h>
53#endif
54#endif
55
56#if HAVE_POLL
57#if HAVE_STROPTS_H
58#include <stropts.h>
59#endif
60#if HAVE_POLL_H
61#include <poll.h>
62#endif
63#endif
64
65#if HAVE_FCNTL_H
66#include <fcntl.h>
67#else
68#if HAVE_SYS_FILE_H
69#include <sys/file.h>
70#endif
71#endif
72
73#ifndef O_RDONLY
74#define O_RDONLY 0
75#define O_WRONLY 1
76#define O_RDWR 2
77#endif
78
79#if HAVE_TIME_H
80#if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME
81#include <time.h>
82#endif
83#endif
84
85#if HAVE_SYS_WAIT_H
86#include <sys/wait.h>
87#endif
88
89#if HAVE_UNION_WAIT
90typedef union wait wait_status;
91#else
92typedef int wait_status;
93#endif
94
95#if HAVE_STREAMS_PTYS
96#include <termio.h>
97extern char *ptsname ();
98#endif
99
100/* Get definitions for both O_NONBLOCK and O_NDELAY.  */
101
102#ifndef O_NDELAY
103#ifdef FNDELAY
104#define O_NDELAY FNDELAY
105#else /* ! defined (FNDELAY) */
106#define O_NDELAY 0
107#endif /* ! defined (FNDELAY) */
108#endif /* ! defined (O_NDELAY) */
109
110#ifndef O_NONBLOCK
111#ifdef FNBLOCK
112#define O_NONBLOCK FNBLOCK
113#else /* ! defined (FNBLOCK) */
114#define O_NONBLOCK 0
115#endif /* ! defined (FNBLOCK) */
116#endif /* ! defined (O_NONBLOCK) */
117
118#if O_NDELAY == 0 && O_NONBLOCK == 0
119 #error No way to do nonblocking I/O
120#endif
121
122/* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA.  */
123#ifndef EAGAIN
124#ifndef EWOULDBLOCK
125#define EAGAIN (-1)
126#define EWOULDBLOCK (-1)
127#else /* defined (EWOULDBLOCK) */
128#define EAGAIN EWOULDBLOCK
129#endif /* defined (EWOULDBLOCK) */
130#else /* defined (EAGAIN) */
131#ifndef EWOULDBLOCK
132#define EWOULDBLOCK EAGAIN
133#endif /* ! defined (EWOULDBLOCK) */
134#endif /* defined (EAGAIN) */
135
136#ifndef ENODATA
137#define ENODATA EAGAIN
138#endif
139
140/* Make sure we have a CLK_TCK definition, even if it makes no sense.
141   This is in case TIMES_TICK is defined as CLK_TCK.  */
142#ifndef CLK_TCK
143#define CLK_TCK (60)
144#endif
145
146/* Don't try too hard to get a TIMES_TICK value; it doesn't matter
147   that much.  */
148#if TIMES_TICK == 0
149#undef TIMES_TICK
150#define TIMES_TICK CLK_TCK
151#endif
152
153#if TIMES_DECLARATION_OK
154extern long times ();
155#endif
156
157#ifndef SIGCHLD
158#define SIGCHLD SIGCLD
159#endif
160
161#if 1
162#define ZUUCICO_CMD "login uucp"
163#define UUCICO_EXECL "/bin/login", "login", "uucp"
164#else
165#define ZUUCICO_CMD "su - nuucp"
166#define UUCICO_EXECL "/bin/su", "su", "-", "nuucp"
167#endif
168
169#if ! HAVE_SELECT && ! HAVE_POLL
170 #error You need select or poll
171#endif
172
173#if ! HAVE_REMOVE
174#undef remove
175#define remove unlink
176#endif
177
178/* Buffer chain to hold data read from a uucico.  */
179
180#define BUFCHARS (512)
181
182struct sbuf
183{
184  struct sbuf *qnext;
185  int cstart;
186  int cend;
187  char ab[BUFCHARS];
188};
189
190/* Local functions.  */
191
192static void umake_file P((const char *zfile, int cextra));
193static void uprepare_test P((boolean fmake, int itest,
194			     boolean fcall_uucico,
195			     const char *zsys));
196static void ucheck_file P((const char *zfile, const char *zerr,
197			   int cextra));
198static void ucheck_test P((int itest, boolean fcall_uucico));
199static RETSIGTYPE uchild P((int isig));
200static int cpshow P((char *z, int bchar));
201static void uchoose P((int *po1, int *po2));
202static long cread P((int o, struct sbuf **));
203static boolean fsend P((int o, int oslave, struct sbuf **));
204static boolean fwritable P((int o));
205static void xsystem P((const char *zcmd));
206static FILE *xfopen P((const char *zname, const char *zmode));
207
208static char *zDebug;
209static int iTest;
210static boolean fCall_uucico;
211static int iPercent;
212static pid_t iPid1, iPid2;
213static int cFrom1, cFrom2;
214static char abLogout1[sizeof "tstout /dev/ptyp0"];
215static char abLogout2[sizeof "tstout /dev/ptyp0"];
216static char *zProtocols;
217
218int
219main (argc, argv)
220     int argc;
221     char **argv;
222{
223  int iopt;
224  const char *zcmd1, *zcmd2;
225  const char *zsys;
226  boolean fmake = TRUE;
227  int omaster1, oslave1, omaster2, oslave2;
228  char abpty1[sizeof "/dev/ptyp0"];
229  char abpty2[sizeof "/dev/ptyp0"];
230  struct sbuf *qbuf1, *qbuf2;
231
232#if ! HAVE_TAYLOR_CONFIG
233  fprintf (stderr, "%s: only works when compiled with HAVE_TAYLOR_CONFIG\n",
234	   argv[0]);
235  exit (1);
236#endif
237
238  zcmd1 = NULL;
239  zcmd2 = NULL;
240  zsys = "test2";
241
242  while ((iopt = getopt (argc, argv, "c:np:s:t:ux:1:2:")) != EOF)
243    {
244      switch (iopt)
245	{
246	case 'c':
247	  zProtocols = optarg;
248	  break;
249	case 'n':
250	  fmake = FALSE;
251	  break;
252	case 'p':
253	  iPercent = (int) strtol (optarg, (char **) NULL, 10);
254	  srand ((unsigned int) ixsysdep_time ((long *) NULL));
255	  break;
256	case 's':
257	  zsys = optarg;
258	  break;
259	case 't':
260	  iTest = (int) strtol (optarg, (char **) NULL, 10);
261	  break;
262	case 'u':
263	  fCall_uucico = TRUE;
264	  break;
265	case 'x':
266	  zDebug = optarg;
267	  break;
268	case '1':
269	  zcmd1 = optarg;
270	  break;
271	case '2':
272	  zcmd2 = optarg;
273	  break;
274	default:
275	  fprintf (stderr,
276		   "Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n",
277		   VERSION);
278	  fprintf (stderr,
279		   "Usage: tstuu [-xn] [-t #] [-u] [-1 cmd] [-2 cmd]\n");
280	  exit (EXIT_FAILURE);
281	}
282    }
283
284  if (fCall_uucico && zcmd2 == NULL)
285    zcmd2 = ZUUCICO_CMD;
286
287  uprepare_test (fmake, iTest, fCall_uucico, zsys);
288
289  (void) remove ("/usr/tmp/tstuu/spool1/core");
290  (void) remove ("/usr/tmp/tstuu/spool2/core");
291
292  omaster1 = -1;
293  oslave1 = -1;
294  omaster2 = -1;
295  oslave2 = -1;
296
297#if ! HAVE_STREAMS_PTYS
298
299  {
300    char *zptyname;
301    const char *zpty;
302
303    zptyname = abpty1;
304
305    for (zpty = "pqrs"; *zpty != '\0'; ++zpty)
306      {
307	int ipty;
308
309	for (ipty = 0; ipty < 16; ipty++)
310	  {
311	    int om, os;
312	    FILE *e;
313
314	    sprintf (zptyname, "/dev/pty%c%c", *zpty,
315		     "0123456789abcdef"[ipty]);
316	    om = open (zptyname, O_RDWR);
317	    if (om < 0)
318	      continue;
319	    zptyname[5] = 't';
320	    os = open (zptyname, O_RDWR);
321	    if (os < 0)
322	      {
323		(void) close (om);
324		continue;
325	      }
326
327	    if (omaster1 == -1)
328	      {
329		omaster1 = om;
330		oslave1 = os;
331
332		e = fopen ("/usr/tmp/tstuu/pty1", "w");
333		if (e == NULL)
334		  {
335		    perror ("fopen");
336		    exit (EXIT_FAILURE);
337		  }
338		fprintf (e, "%s", zptyname + 5);
339		if (fclose (e) != 0)
340		  {
341		    perror ("fclose");
342		    exit (EXIT_FAILURE);
343		  }
344
345		zptyname = abpty2;
346	      }
347	    else
348	      {
349		omaster2 = om;
350		oslave2 = os;
351
352		e = fopen ("/usr/tmp/tstuu/pty2", "w");
353		if (e == NULL)
354		  {
355		    perror ("fopen");
356		    exit (EXIT_FAILURE);
357		  }
358		fprintf (e, "%s", zptyname + 5);
359		if (fclose (e) != 0)
360		  {
361		    perror ("fclose");
362		    exit (EXIT_FAILURE);
363		  }
364		break;
365	      }
366	  }
367
368	if (omaster1 != -1 && omaster2 != -1)
369	  break;
370      }
371  }
372
373#else /* HAVE_STREAMS_PTYS */
374
375  {
376    int ipty;
377
378    for (ipty = 0; ipty < 2; ipty++)
379      {
380	int om, os;
381	FILE *e;
382	char *znam;
383	struct termio stio;
384
385	om = open ((char *) "/dev/ptmx", O_RDWR);
386	if (om < 0)
387	  break;
388	znam = ptsname (om);
389	if (znam == NULL)
390	  break;
391	if (unlockpt (om) != 0
392	    || grantpt (om) != 0)
393	  break;
394
395	os = open (znam, O_RDWR);
396	if (os < 0)
397	  {
398	    (void) close (om);
399	    om = -1;
400	    break;
401	  }
402
403	if (ioctl (os, I_PUSH, "ptem") < 0
404	    || ioctl(os, I_PUSH, "ldterm") < 0)
405	  {
406	    perror ("ioctl");
407	    exit (EXIT_FAILURE);
408	  }
409
410	/* Can this really be right? */
411	memset (&stio, 0, sizeof (stio));
412	stio.c_cflag = B9600 | CS8 | CREAD | HUPCL;
413
414	if (ioctl(os, TCSETA, &stio) < 0)
415	  {
416	    perror ("TCSETA");
417	    exit (EXIT_FAILURE);
418	  }
419
420	if (omaster1 == -1)
421	  {
422	    strcpy (abpty1, znam);
423	    omaster1 = om;
424	    oslave1 = os;
425	    e = fopen ("/usr/tmp/tstuu/pty1", "w");
426	    if (e == NULL)
427	      {
428		perror ("fopen");
429		exit (EXIT_FAILURE);
430	      }
431	    fprintf (e, "%s", znam + 5);
432	    if (fclose (e) != 0)
433	      {
434		perror ("fclose");
435		exit (EXIT_FAILURE);
436	      }
437	  }
438	else
439	  {
440	    strcpy (abpty2, znam);
441	    omaster2 = om;
442	    oslave2 = os;
443	    e = fopen ("/usr/tmp/tstuu/pty2", "w");
444	    if (e == NULL)
445	      {
446		perror ("fopen");
447		exit (EXIT_FAILURE);
448	      }
449	    fprintf (e, "%s", znam + 5);
450	    if (fclose (e) != 0)
451	      {
452		perror ("fclose");
453		exit (EXIT_FAILURE);
454	      }
455	  }
456      }
457  }
458
459#endif /* HAVE_STREAMS_PTYS */
460
461  if (omaster2 == -1)
462    {
463      fprintf (stderr, "No pseudo-terminals available\n");
464      exit (EXIT_FAILURE);
465    }
466
467  /* Make sure we can or these into an int for the select call.  Most
468     systems could use 31 instead of 15, but it should never be a
469     problem.  */
470  if (omaster1 > 15 || omaster2 > 15)
471    {
472      fprintf (stderr, "File descriptors are too large\n");
473      exit (EXIT_FAILURE);
474    }
475
476  /* Prepare to log out the command if it is a login command.  On
477     Ultrix 4.0 uucico can only be run from login for some reason.  */
478
479  if (zcmd1 == NULL
480      || strncmp (zcmd1, "login", sizeof "login" - 1) != 0)
481    abLogout1[0] = '\0';
482  else
483    sprintf (abLogout1, "tstout %s", abpty1);
484
485  if (zcmd2 == NULL
486      || strncmp (zcmd2, "login", sizeof "login" - 1) != 0)
487    abLogout2[0] = '\0';
488  else
489    sprintf (abLogout2, "tstout %s", abpty2);
490
491  iPid1 = fork ();
492  if (iPid1 < 0)
493    {
494      perror ("fork");
495      exit (EXIT_FAILURE);
496    }
497  else if (iPid1 == 0)
498    {
499      if (close (0) < 0
500	  || close (1) < 0
501	  || close (omaster1) < 0
502	  || close (omaster2) < 0
503	  || close (oslave2) < 0)
504	perror ("close");
505
506      if (dup2 (oslave1, 0) < 0
507	  || dup2 (oslave1, 1) < 0)
508	perror ("dup2");
509
510      if (close (oslave1) < 0)
511	perror ("close");
512
513      /* This is said to improve the tests on Linux.  */
514      sleep (3);
515
516      if (zDebug != NULL)
517	fprintf (stderr, "About to exec first process\n");
518
519      if (zcmd1 != NULL)
520	exit (system ((char *) zcmd1));
521      else
522	{
523	  (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config1",
524			"-q", "-S", zsys, "-pstdin", (const char *) NULL);
525	  perror ("execl failed");
526	  exit (EXIT_FAILURE);
527	}
528    }
529
530  iPid2 = fork ();
531  if (iPid2 < 0)
532    {
533      perror ("fork");
534      kill (iPid1, SIGTERM);
535      exit (EXIT_FAILURE);
536    }
537  else if (iPid2 == 0)
538    {
539      if (close (0) < 0
540	  || close (1) < 0
541	  || close (omaster1) < 0
542	  || close (oslave1) < 0
543	  || close (omaster2) < 0)
544	perror ("close");
545
546      if (dup2 (oslave2, 0) < 0
547	  || dup2 (oslave2, 1) < 0)
548	perror ("dup2");
549
550      if (close (oslave2) < 0)
551	perror ("close");
552
553      /* This is said to improve the tests on Linux.  */
554      sleep (5);
555
556      if (zDebug != NULL)
557	fprintf (stderr, "About to exec second process\n");
558
559      if (fCall_uucico)
560	{
561	  (void) execl (UUCICO_EXECL, (const char *) NULL);
562	  perror ("execl failed");
563	  exit (EXIT_FAILURE);
564	}
565      else if (zcmd2 != NULL)
566	exit (system ((char *) zcmd2));
567      else
568	{
569	  (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config2",
570			"-lq", (const char *)NULL);
571	  perror ("execl failed");
572	  exit (EXIT_FAILURE);
573	}
574    }
575
576  signal (SIGCHLD, uchild);
577
578  if (fcntl (omaster1, F_SETFL, O_NDELAY | O_NONBLOCK) < 0
579      && errno == EINVAL)
580    (void) fcntl (omaster1, F_SETFL, O_NONBLOCK);
581  if (fcntl (omaster2, F_SETFL, O_NDELAY | O_NONBLOCK) < 0
582      && errno == EINVAL)
583    (void) fcntl (omaster2, F_SETFL, O_NONBLOCK);
584
585  qbuf1 = NULL;
586  qbuf2 = NULL;
587
588  while (TRUE)
589    {
590      int o1, o2;
591      boolean fcont;
592
593      o1 = omaster1;
594      o2 = omaster2;
595      uchoose (&o1, &o2);
596
597      if (o1 == -1 && o2 == -1)
598	{
599	  if (zDebug != NULL)
600	    fprintf (stderr, "Five second pause\n");
601	  continue;
602	}
603
604      if (o1 != -1)
605	cFrom1 += cread (omaster1, &qbuf1);
606
607      if (o2 != -1)
608	cFrom2 += cread (omaster2, &qbuf2);
609
610      do
611	{
612	  fcont = FALSE;
613
614	  if (qbuf1 != NULL
615	      && fwritable (omaster2)
616	      && fsend (omaster2, oslave2, &qbuf1))
617	    fcont = TRUE;
618
619	  if (qbuf2 != NULL
620	      && fwritable (omaster1)
621	      && fsend (omaster1, oslave1, &qbuf2))
622	    fcont = TRUE;
623
624	  if (! fcont
625	      && (qbuf1 != NULL || qbuf2 != NULL))
626	    {
627	      long cgot1, cgot2;
628
629	      cgot1 = cread (omaster1, &qbuf1);
630	      cFrom1 += cgot1;
631	      cgot2 = cread (omaster2, &qbuf2);
632	      cFrom2 += cgot2;
633	      fcont = TRUE;
634	    }
635	}
636      while (fcont);
637    }
638
639  /*NOTREACHED*/
640}
641
642/* When a child dies, kill them both.  */
643
644static RETSIGTYPE
645uchild (isig)
646     int isig ATTRIBUTE_UNUSED;
647{
648  struct tms sbase, s1, s2;
649
650  signal (SIGCHLD, SIG_DFL);
651
652  /* Give the processes a chance to die on their own.  */
653  sleep (2);
654
655  (void) kill (iPid1, SIGTERM);
656  (void) kill (iPid2, SIGTERM);
657
658  (void) times (&sbase);
659
660#if HAVE_WAITPID
661  (void) waitpid (iPid1, (pointer) NULL, 0);
662#else /* ! HAVE_WAITPID */
663#if HAVE_WAIT4
664  (void) wait4 (iPid1, (pointer) NULL, 0, (struct rusage *) NULL);
665#else /* ! HAVE_WAIT4 */
666  (void) wait ((wait_status *) NULL);
667#endif /* ! HAVE_WAIT4 */
668#endif /* ! HAVE_WAITPID */
669
670  (void) times (&s1);
671
672#if HAVE_WAITPID
673  (void) waitpid (iPid2, (pointer) NULL, 0);
674#else /* ! HAVE_WAITPID */
675#if HAVE_WAIT4
676  (void) wait4 (iPid2, (wait_status *) NULL, 0, (struct rusage *) NULL);
677#else /* ! HAVE_WAIT4 */
678  (void) wait ((wait_status *) NULL);
679#endif /* ! HAVE_WAIT4 */
680#endif /* ! HAVE_WAITPID */
681
682  (void) times (&s2);
683
684  fprintf (stderr,
685	   " First child: user: %g; system: %g\n",
686	   (double) (s1.tms_cutime - sbase.tms_cutime) / (double) TIMES_TICK,
687	   (double) (s1.tms_cstime - sbase.tms_cstime) / (double) TIMES_TICK);
688  fprintf (stderr,
689	   "Second child: user: %g; system: %g\n",
690	   (double) (s2.tms_cutime - s1.tms_cutime) / (double) TIMES_TICK,
691	   (double) (s2.tms_cstime - s1.tms_cstime) / (double) TIMES_TICK);
692
693  ucheck_test (iTest, fCall_uucico);
694
695  if (abLogout1[0] != '\0')
696    {
697      if (zDebug != NULL)
698	fprintf (stderr, "Executing %s\n", abLogout1);
699      (void) system (abLogout1);
700    }
701  if (abLogout2[0] != '\0')
702    {
703      if (zDebug != NULL)
704	fprintf (stderr, "Executing %s\n", abLogout2);
705      (void) system (abLogout2);
706    }
707
708  fprintf (stderr, "Wrote %d bytes from 1 to 2\n", cFrom1);
709  fprintf (stderr, "Wrote %d bytes from 2 to 1\n", cFrom2);
710
711  if (access ("/usr/tmp/tstuu/spool1/core", R_OK) == 0)
712    fprintf (stderr, "core file 1 exists\n");
713  if (access ("/usr/tmp/tstuu/spool2/core", R_OK) == 0)
714    fprintf (stderr, "core file 2 exists\n");
715
716  exit (EXIT_SUCCESS);
717}
718
719/* Open a file without error.  */
720
721static FILE *
722xfopen (zname, zmode)
723     const char *zname;
724     const char *zmode;
725{
726  FILE *eret;
727
728  eret = fopen (zname, zmode);
729  if (eret == NULL)
730    {
731      perror (zname);
732      exit (EXIT_FAILURE);
733    }
734  return eret;
735}
736
737/* Close a file without error.  */
738
739static void xfclose P((FILE *e));
740
741static void
742xfclose (e)
743     FILE *e;
744{
745  if (fclose (e) != 0)
746    {
747      perror ("fclose");
748      exit (EXIT_FAILURE);
749    }
750}
751
752/* Create a test file.  */
753
754static void
755umake_file (z, c)
756     const char *z;
757     int c;
758{
759  int i;
760  FILE *e;
761
762  e = xfopen (z, "w");
763
764  for (i = 0; i < 256; i++)
765    {
766      int i2;
767
768      for (i2 = 0; i2 < 256; i2++)
769	putc (i, e);
770    }
771
772  for (i = 0; i < c; i++)
773    putc (i, e);
774
775  xfclose (e);
776}
777
778/* Check a test file.  */
779
780static void
781ucheck_file (z, zerr, c)
782     const char *z;
783     const char *zerr;
784     int c;
785{
786  int i;
787  FILE *e;
788
789  e = xfopen (z, "r");
790
791  for (i = 0; i < 256; i++)
792    {
793      int i2;
794
795      for (i2 = 0; i2 < 256; i2++)
796	{
797	  int bread;
798
799	  bread = getc (e);
800	  if (bread == EOF)
801	    {
802	      fprintf (stderr,
803		       "%s: Unexpected EOF at position %d,%d\n",
804		       zerr, i, i2);
805	      xfclose (e);
806	      return;
807	    }
808	  if (bread != i)
809	    fprintf (stderr,
810		     "%s: At position %d,%d got %d expected %d\n",
811		     zerr, i, i2, bread, i);
812	}
813    }
814
815  for (i = 0; i < c; i++)
816    {
817      int bread;
818
819      bread = getc (e);
820      if (bread == EOF)
821	{
822	  fprintf (stderr, "%s: Unexpected EOF at extra %d\n", zerr, i);
823	  xfclose (e);
824	  return;
825	}
826      if (bread != i)
827	fprintf (stderr, "%s: At extra %d got %d expected %d\n",
828		 zerr, i, bread, i);
829    }
830
831  if (getc (e) != EOF)
832    fprintf (stderr, "%s: File is too long", zerr);
833
834  xfclose (e);
835}
836
837/* Prepare all the configuration files for testing.  */
838
839static void
840uprepare_test (fmake, itest, fcall_uucico, zsys)
841     boolean fmake;
842     int itest;
843     boolean fcall_uucico;
844     const char *zsys;
845{
846  FILE *e;
847  const char *zuucp1, *zuucp2;
848  const char *zuux1, *zuux2;
849  char ab[1000];
850  const char *zfrom;
851  const char *zto;
852
853/* We must make /usr/tmp/tstuu world writeable or we won't be able to
854   receive files into it.  */
855  (void) umask (0);
856
857#ifndef S_IWOTH
858#define S_IWOTH 02
859#endif
860
861  if (mkdir ((char *) "/usr/tmp/tstuu",
862	     IPUBLIC_DIRECTORY_MODE | S_IWOTH) != 0
863      && errno != EEXIST)
864    {
865      perror ("mkdir");
866      exit (EXIT_FAILURE);
867    }
868
869  if (mkdir ((char *) "/usr/tmp/tstuu/spool1", IPUBLIC_DIRECTORY_MODE) != 0
870      && errno != EEXIST)
871    {
872      perror ("mkdir");
873      exit (EXIT_FAILURE);
874    }
875
876  if (mkdir ((char *) "/usr/tmp/tstuu/spool2", IPUBLIC_DIRECTORY_MODE) != 0
877      && errno != EEXIST)
878    {
879      perror ("mkdir");
880      exit (EXIT_FAILURE);
881    }
882
883  if (fmake)
884    {
885      e = xfopen ("/usr/tmp/tstuu/Config1", "w");
886
887      fprintf (e, "# First test configuration file\n");
888      fprintf (e, "nodename test1\n");
889      fprintf (e, "spool /usr/tmp/tstuu/spool1\n");
890      fprintf (e, "lockdir /usr/tmp/tstuu/spool1\n");
891      fprintf (e, "sysfile /usr/tmp/tstuu/System1\n");
892      fprintf (e, "sysfile /usr/tmp/tstuu/System1.2\n");
893      fprintf (e, "portfile /usr/tmp/tstuu/Port1\n");
894      (void) remove ("/usr/tmp/tstuu/Log1");
895#if ! HAVE_HDB_LOGGING
896      fprintf (e, "logfile /usr/tmp/tstuu/Log1\n");
897#else
898      fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log1/%s/%s");
899#endif
900      fprintf (e, "statfile /usr/tmp/tstuu/Stats1\n");
901      fprintf (e, "debugfile /usr/tmp/tstuu/Debug1\n");
902      fprintf (e, "callfile /usr/tmp/tstuu/Call1\n");
903      fprintf (e, "pubdir /usr/tmp/tstuu\n");
904#if HAVE_V2_CONFIG
905      fprintf (e, "v2-files no\n");
906#endif
907#if HAVE_HDB_CONFIG
908      fprintf (e, "hdb-files no\n");
909#endif
910      if (zDebug != NULL)
911	fprintf (e, "debug %s\n", zDebug);
912
913      xfclose (e);
914
915      e = xfopen ("/usr/tmp/tstuu/System1", "w");
916
917      fprintf (e, "# This file is ignored, to test multiple system files\n");
918      fprintf (e, "time never\n");
919
920      xfclose (e);
921
922      e = xfopen ("/usr/tmp/tstuu/System1.2", "w");
923
924      fprintf (e, "# First test system file\n");
925      fprintf (e, "time any\n");
926      fprintf (e, "port stdin\n");
927      fprintf (e, "# That was the defaults\n");
928      fprintf (e, "system %s\n", zsys);
929      if (! fcall_uucico)
930	{
931	  FILE *eprog;
932
933	  eprog = xfopen ("/usr/tmp/tstuu/Chat1", "w");
934
935	  /* Wait for the other side to open the port and flush input.  */
936	  fprintf (eprog, "sleep 2\n");
937	  fprintf (eprog,
938		   "echo password $1 speed $2 1>&2\n");
939	  fprintf (eprog, "echo test1\n");
940	  fprintf (eprog, "exit 0\n");
941
942	  xfclose (eprog);
943
944	  if (chmod ("/usr/tmp/tstuu/Chat1",
945		     S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
946	    {
947	      perror ("chmod (/usr/tmp/tstuu/Chat1)");
948	      exit (EXIT_FAILURE);
949	    }
950
951	  fprintf (e, "chat-program /usr/tmp/tstuu/Chat1 \\P \\S\n");
952
953	  fprintf (e, "chat word: \\P\n");
954	  fprintf (e, "chat-fail login;\n");
955	  fprintf (e, "call-login *\n");
956	  fprintf (e, "call-password *\n");
957	}
958      else
959	fprintf (e, "chat \"\"\n");
960      fprintf (e, "call-transfer yes\n");
961      fprintf (e, "commands cat\n");
962      if (! fcall_uucico && iPercent == 0)
963	{
964	  fprintf (e, "protocol-parameter g window 7\n");
965	  fprintf (e, "protocol-parameter g packet-size 4096\n");
966	  fprintf (e, "protocol-parameter j avoid \\377\n");
967	}
968      if (zProtocols != NULL)
969	fprintf (e, "protocol %s\n", zProtocols);
970
971      xfclose (e);
972
973      e = xfopen ("/usr/tmp/tstuu/Port1", "w");
974
975      fprintf (e, "port stdin\n");
976      fprintf (e, "type stdin\n");
977
978      xfclose (e);
979
980      e = xfopen ("/usr/tmp/tstuu/Call1", "w");
981
982      fprintf (e, "Call out password file\n");
983      fprintf (e, "%s test1 pass\\s1\n", zsys);
984
985      xfclose (e);
986
987      if (! fcall_uucico)
988	{
989	  FILE *eprog;
990
991	  e = xfopen ("/usr/tmp/tstuu/Config2", "w");
992
993	  fprintf (e, "# Second test configuration file\n");
994	  fprintf (e, "nodename test2\n");
995	  fprintf (e, "spool /usr/tmp/tstuu/spool2\n");
996	  fprintf (e, "lockdir /usr/tmp/tstuu/spool2\n");
997	  fprintf (e, "sysfile /usr/tmp/tstuu/System2\n");
998	  (void) remove ("/usr/tmp/tstuu/Log2");
999#if ! HAVE_HDB_LOGGING
1000	  fprintf (e, "logfile /usr/tmp/tstuu/Log2\n");
1001#else
1002	  fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log2/%s/%s");
1003#endif
1004	  fprintf (e, "statfile /usr/tmp/tstuu/Stats2\n");
1005	  fprintf (e, "debugfile /usr/tmp/tstuu/Debug2\n");
1006	  fprintf (e, "passwdfile /usr/tmp/tstuu/Pass2\n");
1007	  fprintf (e, "pubdir /usr/tmp/tstuu\n");
1008#if HAVE_V2_CONFIG
1009	  fprintf (e, "v2-files no\n");
1010#endif
1011#if HAVE_HDB_CONFIG
1012	  fprintf (e, "hdb-files no\n");
1013#endif
1014	  if (zDebug != NULL)
1015	    fprintf (e, "debug %s\n", zDebug);
1016
1017	  xfclose (e);
1018
1019	  e = xfopen ("/usr/tmp/tstuu/System2", "w");
1020
1021	  fprintf (e, "# Second test system file\n");
1022	  fprintf (e, "system test1\n");
1023	  fprintf (e, "called-login test1\n");
1024	  fprintf (e, "request true\n");
1025	  fprintf (e, "commands cat\n");
1026	  if (zProtocols != NULL)
1027	    fprintf (e, "protocol %s\n", zProtocols);
1028
1029	  eprog = xfopen ("/usr/tmp/tstuu/Chat2", "w");
1030
1031	  fprintf (eprog,
1032		   "echo port $1 1>&2\n");
1033	  fprintf (eprog, "exit 0\n");
1034
1035	  xfclose (eprog);
1036
1037	  if (chmod ("/usr/tmp/tstuu/Chat2",
1038		     S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
1039	    {
1040	      perror ("chmod (/usr/tmp/tstuu/Chat2");
1041	      exit (EXIT_FAILURE);
1042	    }
1043
1044	  fprintf (e, "called-chat-program /bin/sh /usr/tmp/tstuu/Chat2 \\Y\n");
1045	  fprintf (e, "time any\n");
1046
1047	  xfclose (e);
1048
1049	  e = xfopen ("/usr/tmp/tstuu/Pass2", "w");
1050
1051	  fprintf (e, "# Call in password file\n");
1052	  fprintf (e, "test1 pass\\s1\n");
1053
1054	  xfclose (e);
1055	}
1056    }
1057
1058  zuucp1 = "./uucp -I /usr/tmp/tstuu/Config1 -r";
1059  zuux1 = "./uux -I /usr/tmp/tstuu/Config1 -r";
1060
1061  if (fcall_uucico)
1062    {
1063      zuucp2 = "/usr/bin/uucp -r";
1064      zuux2 = "/usr/bin/uux -r";
1065    }
1066  else
1067    {
1068      zuucp2 = "./uucp -I /usr/tmp/tstuu/Config2 -r";
1069      zuux2 = "./uux -I /usr/tmp/tstuu/Config2 -r";
1070    }
1071
1072  /* Test transferring a file from the first system to the second.  */
1073  if (itest == 0 || itest == 1)
1074    {
1075      zfrom = "/usr/tmp/tstuu/from1";
1076      if (fcall_uucico)
1077	zto = "/usr/spool/uucppublic/to1";
1078      else
1079	zto = "/usr/tmp/tstuu/to1";
1080
1081      (void) remove (zto);
1082      umake_file (zfrom, 0);
1083
1084      sprintf (ab, "%s %s %s!%s", zuucp1, zfrom, zsys, zto);
1085      xsystem (ab);
1086    }
1087
1088  /* Test having the first system request a file from the second.  */
1089  if (itest == 0 || itest == 2)
1090    {
1091      if (fcall_uucico)
1092	zfrom = "/usr/spool/uucppublic/from2";
1093      else
1094	zfrom = "/usr/tmp/tstuu/from2";
1095      zto = "/usr/tmp/tstuu/to2";
1096
1097      (void) remove (zto);
1098      umake_file (zfrom, 3);
1099
1100      sprintf (ab, "%s %s!%s %s", zuucp1, zsys, zfrom, zto);
1101      xsystem (ab);
1102    }
1103
1104  /* Test having the second system send a file to the first.  */
1105  if (itest == 0 || itest == 3)
1106    {
1107      if (fcall_uucico)
1108	zfrom = "/usr/spool/uucppublic/from3";
1109      else
1110	zfrom = "/usr/tmp/tstuu/from3";
1111      zto = "/usr/tmp/tstuu/to3";
1112
1113      (void) remove (zto);
1114      umake_file (zfrom, 5);
1115
1116      sprintf (ab, "%s -c \\~/from3 test1!~/to3", zuucp2);
1117      xsystem (ab);
1118    }
1119
1120  /* Test having the second system request a file from the first.  */
1121  if (itest == 0 || itest == 4)
1122    {
1123      zfrom = "/usr/tmp/tstuu/from4";
1124      if (fcall_uucico)
1125	zto = "/usr/spool/uucppublic/to4";
1126      else
1127	zto = "/usr/tmp/tstuu/to4";
1128
1129      (void) remove (zto);
1130      umake_file (zfrom, 7);
1131
1132      sprintf (ab, "%s test1!%s %s", zuucp2, zfrom, zto);
1133      xsystem (ab);
1134    }
1135
1136  /* Test having the second system make an execution request.  */
1137  if (itest == 0 || itest == 5)
1138    {
1139      zfrom = "/usr/tmp/tstuu/from5";
1140      if (fcall_uucico)
1141	zto = "/usr/spool/uucppublic/to5";
1142      else
1143	zto = "/usr/tmp/tstuu/to5";
1144
1145      (void) remove (zto);
1146      umake_file (zfrom, 11);
1147
1148      sprintf (ab, "%s test1!cat '<%s' '>%s'", zuux2, zfrom, zto);
1149      xsystem (ab);
1150    }
1151
1152  /* Test having the first system request a wildcard.  */
1153  if (itest == 0 || itest == 6)
1154    {
1155      const char *zfrom1, *zfrom2;
1156
1157      if (fcall_uucico)
1158	{
1159	  zfrom = "/usr/spool/uucppublic/to6\\*";
1160	  zfrom1 = "/usr/spool/uucppublic/to6.1";
1161	  zfrom2 = "/usr/spool/uucppublic/to6.2";
1162	}
1163      else
1164	{
1165	  zfrom = "/usr/tmp/tstuu/spool2/to6\\*";
1166	  zfrom1 = "/usr/tmp/tstuu/spool2/to6.1";
1167	  zfrom2 = "/usr/tmp/tstuu/spool2/to6.2";
1168	}
1169
1170      umake_file (zfrom1, 100);
1171      umake_file (zfrom2, 101);
1172      (void) remove ("/usr/tmp/tstuu/to6.1");
1173      (void) remove ("/usr/tmp/tstuu/to6.2");
1174
1175      sprintf (ab, "%s %s!%s /usr/tmp/tstuu", zuucp1, zsys, zfrom);
1176      xsystem (ab);
1177    }
1178
1179  /* Test having the second system request a wildcard.  */
1180  if (itest == 0 || itest == 7)
1181    {
1182      const char *zto1, *zto2;
1183
1184      if (fcall_uucico)
1185	{
1186	  zto = "/usr/spool/uucppublic";
1187	  zto1 = "/usr/spool/uucppublic/to7.1";
1188	  zto2 = "/usr/spool/uucppublic/to7.2";
1189	}
1190      else
1191	{
1192	  zto = "/usr/tmp/tstuu";
1193	  zto1 = "/usr/tmp/tstuu/to7.1";
1194	  zto2 = "/usr/tmp/tstuu/to7.2";
1195	}
1196
1197      umake_file ("/usr/tmp/tstuu/spool1/to7.1", 150);
1198      umake_file ("/usr/tmp/tstuu/spool1/to7.2", 155);
1199      (void) remove (zto1);
1200      (void) remove (zto2);
1201
1202      sprintf (ab, "%s test1!/usr/tmp/tstuu/spool1/to7.\\* %s", zuucp2,
1203	       zto);
1204      xsystem (ab);
1205    }
1206
1207  /* Test an E command.  This runs cat, discarding the output.  */
1208  if ((itest == 0 || itest == 8) && ! fcall_uucico)
1209    {
1210      umake_file ("/usr/tmp/tstuu/from8", 30);
1211      sprintf (ab, "%s - test2!cat < /usr/tmp/tstuu/from8", zuux1);
1212      xsystem (ab);
1213    }
1214}
1215
1216/* Try to make sure the file transfers were successful.  */
1217
1218static void
1219ucheck_test (itest, fcall_uucico)
1220     int itest;
1221     boolean fcall_uucico;
1222{
1223  if (itest == 0 || itest == 1)
1224    {
1225      if (fcall_uucico)
1226	ucheck_file ("/usr/spool/uucppublic/to1", "test 1", 0);
1227      else
1228	ucheck_file ("/usr/tmp/tstuu/to1", "test 1", 0);
1229    }
1230
1231  if (itest == 0 || itest == 2)
1232    ucheck_file ("/usr/tmp/tstuu/to2", "test 2", 3);
1233
1234  if (itest == 0 || itest == 3)
1235    ucheck_file ("/usr/tmp/tstuu/to3", "test 3", 5);
1236
1237  if (itest == 0 || itest == 4)
1238    {
1239      if (fcall_uucico)
1240	ucheck_file ("/usr/spool/uucppublic/to4", "test 4", 7);
1241      else
1242	ucheck_file ("/usr/tmp/tstuu/to4", "test 4", 7);
1243    }
1244
1245  if (itest == 0 || itest == 6)
1246    {
1247      ucheck_file ("/usr/tmp/tstuu/to6.1", "test 6.1", 100);
1248      ucheck_file ("/usr/tmp/tstuu/to6.2", "test 6.2", 101);
1249    }
1250
1251  if (itest == 0 || itest == 7)
1252    {
1253      const char *zto1, *zto2;
1254
1255      if (fcall_uucico)
1256	{
1257	  zto1 = "/usr/spool/uucppublic/to7.1";
1258	  zto2 = "/usr/spool/uucppublic/to7.2";
1259	}
1260      else
1261	{
1262	  zto1 = "/usr/tmp/tstuu/to7.1";
1263	  zto2 = "/usr/tmp/tstuu/to7.2";
1264	}
1265
1266      ucheck_file (zto1, "test 7.1", 150);
1267      ucheck_file (zto2, "test 7.2", 155);
1268    }
1269}
1270
1271/* A debugging routine used when displaying buffers.  */
1272
1273static int
1274cpshow (z, ichar)
1275     char *z;
1276     int ichar;
1277{
1278  if (isprint (BUCHAR (ichar)) && ichar != '\"')
1279    {
1280      *z = (char) ichar;
1281      return 1;
1282    }
1283
1284  *z++ = '\\';
1285
1286  switch (ichar)
1287    {
1288    case '\n':
1289      *z = 'n';
1290      return 2;
1291    case '\r':
1292      *z = 'r';
1293      return 2;
1294    case '\"':
1295      *z = '\"';
1296      return 2;
1297    default:
1298      sprintf (z, "%03o", (unsigned int)(ichar & 0xff));
1299      return strlen (z) + 1;
1300    }
1301}
1302
1303/* Pick one of two file descriptors which is ready for reading, or
1304   return in five seconds.  If the argument is ready for reading,
1305   leave it alone; otherwise set it to -1.  */
1306
1307static void
1308uchoose (po1, po2)
1309     int *po1;
1310     int *po2;
1311{
1312#if HAVE_SELECT
1313
1314  int iread;
1315  struct timeval stime;
1316
1317  iread = (1 << *po1) | (1 << *po2);
1318  stime.tv_sec = 5;
1319  stime.tv_usec = 0;
1320
1321  if (select ((*po1 > *po2 ? *po1 : *po2) + 1, (pointer) &iread,
1322	      (pointer) NULL, (pointer) NULL, &stime) < 0)
1323    {
1324      perror ("select");
1325      uchild (SIGCHLD);
1326    }
1327
1328  if ((iread & (1 << *po1)) == 0)
1329    *po1 = -1;
1330
1331  if ((iread & (1 << *po2)) == 0)
1332    *po2 = -1;
1333
1334#else /* ! HAVE_SELECT */
1335
1336#if HAVE_POLL
1337
1338  struct pollfd as[2];
1339
1340  as[0].fd = *po1;
1341  as[0].events = POLLIN;
1342  as[1].fd = *po2;
1343  as[1].events = POLLIN;
1344
1345  if (poll (as, 2, 5 * 1000) < 0)
1346    {
1347      perror ("poll");
1348      uchild (SIGCHLD);
1349    }
1350
1351  if ((as[0].revents & POLLIN) == 0)
1352    *po1 = -1;
1353
1354  if ((as[1].revents & POLLIN) == 0)
1355    *po2 = -1;
1356
1357#endif /* HAVE_POLL */
1358#endif /* ! HAVE_SELECT */
1359}
1360
1361/* Read some data from a file descriptor.  This keeps reading until
1362   one of the reads gets no data.  */
1363
1364static long
1365cread (o, pqbuf)
1366     int o;
1367     struct sbuf **pqbuf;
1368{
1369  long ctotal;
1370
1371  while (*pqbuf != NULL && (*pqbuf)->qnext != NULL)
1372    pqbuf = &(*pqbuf)->qnext;
1373
1374  ctotal = 0;
1375
1376  while (TRUE)
1377    {
1378      int cgot;
1379
1380      if (*pqbuf != NULL
1381	  && (size_t) (*pqbuf)->cend >= sizeof (*pqbuf)->ab)
1382	pqbuf = &(*pqbuf)->qnext;
1383
1384      if (*pqbuf == NULL)
1385	{
1386	  *pqbuf = (struct sbuf *) malloc (sizeof (struct sbuf));
1387	  if (*pqbuf == NULL)
1388	    {
1389	      fprintf (stderr, "Out of memory\n");
1390	      uchild (SIGCHLD);
1391	    }
1392	  (*pqbuf)->qnext = NULL;
1393	  (*pqbuf)->cstart = 0;
1394	  (*pqbuf)->cend = 0;
1395	}
1396
1397      cgot = read (o, (*pqbuf)->ab + (*pqbuf)->cend,
1398		   (sizeof (*pqbuf)->ab) - (*pqbuf)->cend);
1399      if (cgot < 0)
1400	{
1401	  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)
1402	    cgot = 0;
1403	  else
1404	    {
1405	      perror ("read");
1406	      uchild (SIGCHLD);
1407	    }
1408	}
1409
1410      if (cgot == 0)
1411	return ctotal;
1412
1413      ctotal += cgot;
1414
1415      if (zDebug != NULL)
1416	{
1417	  char abshow[325];
1418	  char *zfrom;
1419	  char *zshow;
1420	  int i;
1421
1422	  zfrom = (*pqbuf)->ab + (*pqbuf)->cend;
1423	  zshow = abshow;
1424	  for (i = 0; i < cgot && i < 80; i++, zfrom++)
1425	    zshow += cpshow (zshow, *zfrom);
1426	  if (i < cgot)
1427	    {
1428	      *zshow++ = '.';
1429	      *zshow++ = '.';
1430	      *zshow++ = '.';
1431	    }
1432	  *zshow = '\0';
1433	  fprintf (stderr, "Read from %d: %d \"%s\"\n", o, cgot, abshow);
1434	  fflush (stderr);
1435	}
1436
1437      if (iPercent > 0)
1438	{
1439	  int i;
1440	  int c;
1441
1442	  c = 0;
1443	  for (i = 0; i < cgot; i++)
1444	    {
1445	      if (rand () % 1000 < iPercent)
1446		{
1447		  ++(*pqbuf)->ab[(*pqbuf)->cend + i];
1448		  ++c;
1449		}
1450	    }
1451	  if (zDebug != NULL && c > 0)
1452	    fprintf (stderr, "Clobbered %d bytes\n", c);
1453	}
1454
1455      (*pqbuf)->cend += cgot;
1456
1457      if (ctotal > 256)
1458	return ctotal;
1459    }
1460}
1461
1462/* Write data to a file descriptor until one of the writes gets no
1463   data.  */
1464
1465static boolean
1466fsend (o, oslave, pqbuf)
1467     int o;
1468     int oslave;
1469     struct sbuf **pqbuf;
1470{
1471  long ctotal;
1472
1473  ctotal = 0;
1474  while (*pqbuf != NULL)
1475    {
1476      int cwrite, cwrote;
1477
1478      if ((*pqbuf)->cstart >= (*pqbuf)->cend)
1479	{
1480	  struct sbuf *qfree;
1481
1482	  qfree = *pqbuf;
1483	  *pqbuf = (*pqbuf)->qnext;
1484	  free ((pointer) qfree);
1485	  continue;
1486	}
1487
1488#ifdef FIONREAD
1489      {
1490	long cunread;
1491
1492	if (ioctl (oslave, FIONREAD, &cunread) < 0)
1493	  {
1494	    perror ("FIONREAD");
1495	    uchild (SIGCHLD);
1496	  }
1497	if (zDebug != NULL)
1498	  fprintf (stderr, "%ld unread\n", cunread);
1499	cwrite = 256 - cunread;
1500	if (cwrite <= 0)
1501	  break;
1502      }
1503#else /* ! FIONREAD */
1504      if (! fwritable (o))
1505	break;
1506      cwrite = 1;
1507#endif /* ! FIONREAD */
1508
1509      if (cwrite > (*pqbuf)->cend - (*pqbuf)->cstart)
1510	cwrite = (*pqbuf)->cend - (*pqbuf)->cstart;
1511
1512      cwrote = write (o, (*pqbuf)->ab + (*pqbuf)->cstart, cwrite);
1513      if (cwrote < 0)
1514	{
1515	  if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA)
1516	    cwrote = 0;
1517	  else
1518	    {
1519	      perror ("write");
1520	      uchild (SIGCHLD);
1521	    }
1522	}
1523
1524      if (cwrote == 0)
1525	break;
1526
1527      ctotal += cwrote;
1528      (*pqbuf)->cstart += cwrote;
1529    }
1530
1531  if (zDebug != NULL && ctotal > 0)
1532    fprintf (stderr, "Wrote %ld to %d\n", ctotal, o);
1533
1534  return ctotal > 0;
1535}
1536
1537/* Check whether a file descriptor can be written to.  */
1538
1539static boolean
1540fwritable (o)
1541     int o;
1542{
1543#if HAVE_SELECT
1544
1545  int iwrite;
1546  struct timeval stime;
1547  int cfds;
1548
1549  iwrite = 1 << o;
1550
1551  stime.tv_sec = 0;
1552  stime.tv_usec = 0;
1553
1554  cfds = select (o + 1, (pointer) NULL, (pointer) &iwrite,
1555		 (pointer) NULL, &stime);
1556  if (cfds < 0)
1557    {
1558      perror ("select");
1559      uchild (SIGCHLD);
1560    }
1561
1562  return cfds > 0;
1563
1564#else /* ! HAVE_SELECT */
1565
1566#if HAVE_POLL
1567
1568  struct pollfd s;
1569  int cfds;
1570
1571  s.fd = o;
1572  s.events = POLLOUT;
1573
1574  cfds = poll (&s, 1, 0);
1575  if (cfds < 0)
1576    {
1577      perror ("poll");
1578      uchild (SIGCHLD);
1579    }
1580
1581  return cfds > 0;
1582
1583#endif /* HAVE_POLL */
1584#endif /* ! HAVE_SELECT */
1585}
1586
1587/* A version of the system command that checks for errors.  */
1588
1589static void
1590xsystem (zcmd)
1591     const char *zcmd;
1592{
1593  int istat;
1594
1595  istat = system ((char *) zcmd);
1596  if (istat != 0)
1597    {
1598      fprintf (stderr, "Command failed with status %d\n", istat);
1599      fprintf (stderr, "%s\n", zcmd);
1600      exit (EXIT_FAILURE);
1601    }
1602}
1603