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