1/* uux.c
2   Prepare to execute a command on a remote 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 uux_rcsid[] = "$Id: uux.c,v 1.89 2002/03/05 19:10:42 ian Rel $";
29#endif
30
31#include "uudefs.h"
32#include "uuconf.h"
33#include "system.h"
34#include "sysdep.h"
35#include "getopt.h"
36
37#include <ctype.h>
38#include <errno.h>
39
40#if HAVE_SYSEXITS_H
41#include <sysexits.h>
42#endif
43
44#ifndef EX_OK
45#define EX_OK (0)
46#endif
47
48#ifndef EX_USAGE
49#define EX_USAGE (64)
50#endif
51
52#ifndef EX_DATAERR
53#define EX_DATAERR (65)
54#endif
55
56#ifndef EX_NOINPUT
57#define EX_NOINPUT (66)
58#endif
59
60#ifndef EX_UNAVAILABLE
61#define EX_UNAVAILABLE (69)
62#endif
63
64#ifndef EX_OSERR
65#define EX_OSERR (71)
66#endif
67
68#ifndef EX_CANTCREAT
69#define EX_CANTCREAT (73)
70#endif
71
72#ifndef EX_TEMPFAIL
73#define EX_TEMPFAIL (75)
74#endif
75
76#ifndef EX_CONFIG
77#define EX_CONFIG (78)
78#endif
79
80/* These character lists should, perhaps, be in sysdep.h.  */
81
82/* This is the list of shell metacharacters that we check for.  If one
83   of these is present, we request uuxqt to execute the command with
84   /bin/sh.  Otherwise we let it execute using execve.  */
85
86#define ZSHELLCHARS "\"'`*?[;&()|<>\\$"
87
88/* This is the list of word separators.  We break filename arguments
89   at these characters.  */
90#define ZSHELLSEPS ";&*|<> \t"
91
92/* This is the list of word separators without the redirection
93   operators.  */
94#define ZSHELLNONREDIRSEPS ";&*| \t"
95
96/* Whether we need to backslash quote the entries in the execution
97   file.  */
98static boolean fXquote;
99
100/* Whether we have output the 'Q' command required for quoting.  */
101static boolean fXquote_output;
102
103/* Whether this execution is occurring on the local system.  */
104static boolean fXxqtlocal;
105
106/* The execution system.  */
107static struct uuconf_system sXxqtsys;
108
109/* The name of local system from the point of view of the execution
110   system.  */
111static const char *zXxqtloc;
112
113/* The job grade to use.  */
114static char bXgrade = BDEFAULT_UUX_GRADE;
115
116/* The temporary file name of the execute file.  */
117static char abXxqt_tname[CFILE_NAME_LEN];
118
119/* The name of the execute file on the remote system.  */
120static char abXxqt_xname[CFILE_NAME_LEN];
121
122/* The execute file we are creating.  */
123static FILE *eXxqt_file;
124
125/* A list of commands to be spooled.  */
126static struct scmd *pasXcmds;
127static int cXcmds;
128
129/* Name of the spool file for this execution */
130static char *zXqt_name = NULL;
131
132
133/* A file to close if we're forced to exit.  */
134static FILE *eXclose;
135
136/* A list of file names which will match the file names which appear
137   in the uucico logs.  */
138static char *zXnames;
139
140/* Local functions.  */
141static void uxusage P((void));
142static void uxhelp P((void));
143static void uxadd_xqt_line P((int bchar, const char *z1, const char *z2));
144static void uxadd_send_file P((const char *zfrom, const char *zto,
145			       const char *zoptions, const char *ztemp,
146			       const char *zforward));
147static void uxcopy_stdin P((FILE *e));
148static void uxrecord_file P((const char *zfile));
149static void uxfatal P((void));
150static void uxabort P((int istat));
151static void uxadd_name P((const char *));
152
153/* Long getopt options.  */
154static const struct option asXlongopts[] =
155{
156  { "requestor", required_argument, NULL, 'a' },
157  { "return-stdin", no_argument, NULL, 'b' },
158  { "nocopy", no_argument, NULL, 'c' },
159  { "copy", no_argument, NULL, 'C' },
160  { "grade", required_argument, NULL, 'g' },
161  { "jobid", no_argument, NULL, 'j' },
162  { "link", no_argument, NULL, 'l' },
163  { "notification", required_argument, NULL, 2 },
164  { "stdin", no_argument, NULL, 'p' },
165  { "nouucico", no_argument, NULL, 'r' },
166  { "status", required_argument, NULL, 's' },
167  { "noexpand", no_argument, NULL, 'W' },
168  { "config", required_argument, NULL, 'I' },
169  { "debug", required_argument, NULL, 'x' },
170  { "version", no_argument, NULL, 'v' },
171  { "help", no_argument, NULL, 1 },
172  { NULL, 0, NULL, 0 }
173};
174
175/* The main routine.  */
176
177int
178main (argc, argv)
179     int argc;
180     char **argv;
181{
182  /* -a: requestor address for status reports.  */
183  const char *zrequestor = NULL;
184  /* -b: if true, return standard input on error.  */
185  boolean fretstdin = FALSE;
186  /* -c,-C: if true, copy to spool directory.  */
187  boolean fcopy = FALSE;
188  /* -c: set if -c appears explicitly; if it and -l appear, then if the
189     link fails we don't copy the file.  */
190  boolean fdontcopy = FALSE;
191  /* -I: configuration file name.  */
192  const char *zconfig = NULL;
193  /* -j: output job id.  */
194  boolean fjobid = FALSE;
195  /* -l: link file to spool directory.  */
196  boolean flink = FALSE;
197  /* -n: do not notify upon command completion.  */
198  boolean fno_ack = FALSE;
199  /* -p: read standard input for command standard input.  */
200  boolean fread_stdin = FALSE;
201  /* -r: do not start uucico when finished.  */
202  boolean fuucico = TRUE;
203  /* -s: report status to named file.  */
204  const char *zstatus_file = NULL;
205  /* -W: only expand local file names.  */
206  boolean fexpand = TRUE;
207  /* -z: report status only on error.  */
208  boolean ferror_ack = FALSE;
209  int iopt;
210  pointer puuconf;
211  int iuuconf;
212  const char *zlocalname;
213  int i;
214  size_t clen;
215  char *zargs;
216  char *zarg;
217  char *zcmd;
218  const char *zsys;
219  char *zexclam;
220  boolean fgetcwd;
221  const char *zuser;
222  char *zforward;
223  char **pzargs;
224  int calloc_args;
225  int cargs;
226  const char *zinput_from;
227  const char *zinput_to;
228  const char *zinput_temp;
229  boolean finputcopied;
230  char *zcall_system;
231  boolean fcall_any;
232  struct uuconf_system slocalsys;
233  boolean fneedshell;
234  char *zfullcmd;
235  boolean fpoll;
236  char aboptions[10];
237
238  if (argc < 1)
239  {
240      zProgram = "uux";
241      uxusage ();
242  }
243
244  zProgram = argv[0];
245
246  ulog_fatal_fn (uxfatal);
247
248  /* We need to be able to read a single - as an option, which getopt
249     won't do.  We handle this by using getopt to scan the argument
250     list multiple times, replacing any single "-" with "-p".  */
251  opterr = 0;
252  while (1)
253    {
254      while (getopt_long (argc, argv, "+a:bcCg:I:jlnprs:Wvx:z",
255			  asXlongopts, (int *) NULL) != EOF)
256	;
257      if (optind >= argc || strcmp (argv[optind], "-") != 0)
258	break;
259      argv[optind] = zbufcpy ("-p");
260      optind = 0;
261    }
262  opterr = 1;
263  optind = 0;
264
265  /* The leading + in the getopt string means to stop processing
266     options as soon as a non-option argument is seen.  */
267  while ((iopt = getopt_long (argc, argv, "+a:bcCg:I:jlnprs:Wvx:z",
268			      asXlongopts, (int *) NULL)) != EOF)
269    {
270      switch (iopt)
271	{
272	case 'a':
273	  /* Set requestor name: mail address to which status reports
274	     should be sent.  */
275	  zrequestor = optarg;
276	  break;
277
278	case 'b':
279	  /* Return standard input on error.  */
280	  fretstdin = TRUE;
281	  break;
282
283	case 'c':
284	  /* Do not copy local files to spool directory.  */
285	  fcopy = FALSE;
286	  fdontcopy = TRUE;
287	  break;
288
289	case 'C':
290	  /* Copy local files to spool directory.  */
291	  fcopy = TRUE;
292	  break;
293
294	case 'I':
295	  /* Configuration file name.  */
296	  if (fsysdep_other_config (optarg))
297	    zconfig = optarg;
298	  break;
299
300	case 'j':
301	  /* Output jobid.  */
302	  fjobid = TRUE;
303	  break;
304
305	case 'g':
306	  /* Set job grade.  */
307	  bXgrade = optarg[0];
308	  break;
309
310	case 'l':
311	  /* Link file to spool directory.  */
312	  flink = TRUE;
313	  break;
314
315	case 'n':
316	  /* Do not notify upon command completion.  */
317	  fno_ack = TRUE;
318	  break;
319
320	case 'p':
321	  /* Read standard input for command standard input.  */
322	  fread_stdin = TRUE;
323	  break;
324
325	case 'r':
326	  /* Do not start uucico when finished.  */
327	  fuucico = FALSE;
328	  break;
329
330	case 's':
331	  /* Report status to named file.  */
332	  zstatus_file = optarg;
333	  break;
334
335	case 'W':
336	  /* Only expand local file names.  */
337	  fexpand = FALSE;
338	  break;
339
340	case 'x':
341#if DEBUG > 1
342	  /* Set debugging level.  */
343	  iDebug |= idebug_parse (optarg);
344#endif
345	  break;
346
347	case 'z':
348	  /* Report status only on error.  */
349	  ferror_ack = TRUE;
350	  break;
351
352	case 2:
353	  /* --notify={true,false,error}.  */
354	  if (*optarg == 't'
355	      || *optarg == 'T'
356	      || *optarg == 'y'
357	      || *optarg == 'Y'
358	      || *optarg == 'e'
359	      || *optarg == 'E')
360	    {
361	      ferror_ack = TRUE;
362	      fno_ack = FALSE;
363	    }
364	  else if (*optarg == 'f'
365		   || *optarg == 'F'
366		   || *optarg == 'n'
367		   || *optarg == 'N')
368	    {
369	      ferror_ack = FALSE;
370	      fno_ack = TRUE;
371	    }
372	  break;
373
374	case 'v':
375	  /* Print version and exit.  */
376	  printf ("uux (Taylor UUCP) %s\n", VERSION);
377	  printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n");
378	  printf ("This program is free software; you may redistribute it under the terms of\n");
379	  printf ("the GNU General Public LIcense.  This program has ABSOLUTELY NO WARRANTY.\n");
380	  exit (EX_OK);
381	  /*NOTREACHED*/
382
383	case 1:
384	  /* --help.  */
385	  uxhelp ();
386	  exit (EX_OK);
387	  /*NOTREACHED*/
388
389	case 0:
390	  /* Long option found and flag set.  */
391	  break;
392
393	default:
394	  uxusage ();
395	  break;
396	}
397    }
398
399  if (! UUCONF_GRADE_LEGAL (bXgrade)
400      || ((bXgrade < '0' || bXgrade > '9')
401	  && (bXgrade < 'a' || bXgrade > 'z')
402	  && (bXgrade < 'A' || bXgrade > 'Z')))
403    {
404      ulog (LOG_ERROR, "Ignoring illegal grade");
405      bXgrade = BDEFAULT_UUX_GRADE;
406    }
407
408  /* Check whether we need backslash quoting in the executable file.
409     We always break up the command arguments at spaces anyhow, so we
410     don't have to worry about them.  Note that this means that
411     certain commands aren't supported.  */
412  if ((zrequestor != NULL
413       && zrequestor[strcspn (zrequestor, " \t\n")] != '\0')
414      || (zstatus_file != NULL
415	  && zstatus_file[strcspn (zstatus_file, " \t\n")] != '\0'))
416    fXquote = TRUE;
417
418  if (optind == argc)
419    uxusage ();
420
421  iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig);
422  if (iuuconf != UUCONF_SUCCESS)
423    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
424
425#if DEBUG > 1
426  {
427    const char *zdebug;
428
429    iuuconf = uuconf_debuglevel (puuconf, &zdebug);
430    if (iuuconf != UUCONF_SUCCESS)
431      ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
432    if (zdebug != NULL)
433      iDebug |= idebug_parse (zdebug);
434  }
435#endif
436
437  /* The command and files arguments could be quoted in any number of
438     ways, so we split them apart ourselves.  We do this before
439     calling usysdep_initialize because we want to set fgetcwd
440     correctly.  */
441  clen = 1;
442  for (i = optind; i < argc; i++)
443    clen += strlen (argv[i]) + 1;
444
445  zargs = zbufalc (clen);
446  *zargs = '\0';
447  for (i = optind; i < argc; i++)
448    {
449      strcat (zargs, argv[i]);
450      strcat (zargs, " ");
451    }
452
453  /* The first argument is the command to execute.  */
454  clen = strcspn (zargs, ZSHELLSEPS);
455  zcmd = zbufalc (clen + 1);
456  strncpy (zcmd, zargs, clen);
457  zcmd[clen] = '\0';
458  zargs += clen;
459
460  /* Split the arguments out into an array.  We break the arguments
461     into alternating sequences of characters not in ZSHELLSEPS
462     and characters in ZSHELLSEPS.  We remove whitespace.  We
463     separate the redirection characters '>' and '<' into their
464     own arguments to make them easier to process below.  */
465  calloc_args = 10;
466  pzargs = (char **) xmalloc (calloc_args * sizeof (char *));
467  cargs = 0;
468
469  for (zarg = strtok (zargs, " \t");
470       zarg != NULL;
471       zarg = strtok ((char *) NULL, " \t"))
472    {
473      while (*zarg != '\0')
474	{
475	  if (cargs + 1 >= calloc_args)
476	    {
477	      calloc_args += 10;
478	      pzargs = (char **) xrealloc ((pointer) pzargs,
479					   calloc_args * sizeof (char *));
480	    }
481
482	  if (*zarg == '(')
483	    clen = strlen (zarg);
484	  else
485	    clen = strcspn (zarg, ZSHELLSEPS);
486	  if (clen > 0)
487	    {
488	      pzargs[cargs] = zbufalc (clen + 1);
489	      memcpy (pzargs[cargs], zarg, clen);
490	      pzargs[cargs][clen] = '\0';
491	      ++cargs;
492	      zarg += clen;
493	    }
494
495	  /* We deliberately separate '>' and '<' out.  */
496	  if (*zarg != '\0')
497	    {
498	      clen = strspn (zarg, ZSHELLNONREDIRSEPS);
499	      if (clen == 0)
500		clen = 1;
501	      pzargs[cargs] = zbufalc (clen + 1);
502	      memcpy (pzargs[cargs], zarg, clen);
503	      pzargs[cargs][clen] = '\0';
504	      ++cargs;
505	      zarg += clen;
506	    }
507	}
508    }
509
510  /* Now look through the arguments to see if we are going to need the
511     current working directory.  We don't try to make a precise
512     determination, just a conservative one.  The basic idea is that
513     we don't want to get the cwd for 'foo!rmail - user' (note that we
514     don't examine the command itself).  */
515  fgetcwd = FALSE;
516  for (i = 0; i < cargs; i++)
517    {
518      if (pzargs[i][0] == '(')
519	continue;
520      zexclam = strrchr (pzargs[i], '!');
521      if (zexclam != NULL && fsysdep_needs_cwd (zexclam + 1))
522	{
523	  fgetcwd = TRUE;
524	  break;
525	}
526      if ((pzargs[i][0] == '<' || pzargs[i][0] == '>')
527	  && i + 1 < cargs
528	  && strchr (pzargs[i + 1], '!') == NULL
529	  && fsysdep_needs_cwd (pzargs[i + 1]))
530	{
531	  fgetcwd = TRUE;
532	  break;
533	}
534    }
535    fgetcwd = TRUE;
536
537#ifdef SIGINT
538  usysdep_signal (SIGINT);
539#endif
540#ifdef SIGHUP
541  usysdep_signal (SIGHUP);
542#endif
543#ifdef SIGQUIT
544  usysdep_signal (SIGQUIT);
545#endif
546#ifdef SIGTERM
547  usysdep_signal (SIGTERM);
548#endif
549#ifdef SIGPIPE
550  usysdep_signal (SIGPIPE);
551#endif
552
553  usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0));
554
555  zuser = zsysdep_login_name ();
556
557  /* Get the local system name.  */
558  iuuconf = uuconf_localname (puuconf, &zlocalname);
559  if (iuuconf == UUCONF_NOT_FOUND)
560    {
561      zlocalname = zsysdep_localname ();
562      if (zlocalname == NULL)
563	exit (EX_CONFIG);
564    }
565  else if (iuuconf != UUCONF_SUCCESS)
566    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
567
568  /* Get the local system information.  */
569  iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys);
570  if (iuuconf != UUCONF_SUCCESS)
571    {
572      if (iuuconf != UUCONF_NOT_FOUND)
573	ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
574      iuuconf = uuconf_system_local (puuconf, &slocalsys);
575      if (iuuconf != UUCONF_SUCCESS)
576	ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
577      slocalsys.uuconf_zname = (char *) zlocalname;
578    }
579
580  /* Figure out which system the command is to be executed on.  */
581  zcmd = zremove_local_sys (&slocalsys, zcmd);
582  zexclam = strchr (zcmd, '!');
583  if (zexclam == NULL)
584    {
585      zsys = zlocalname;
586      fXxqtlocal = TRUE;
587      zforward = NULL;
588    }
589  else
590    {
591      *zexclam = '\0';
592      zsys = zcmd;
593      zcmd = zexclam + 1;
594      fXxqtlocal = FALSE;
595
596      /* See if we must forward this command through other systems
597	 (e.g. uux a!b!cmd).  */
598      zexclam = strrchr (zcmd, '!');
599      if (zexclam == NULL)
600	zforward = NULL;
601      else
602	{
603	  clen = zexclam - zcmd;
604	  zforward = zbufalc (clen + 1);
605	  memcpy (zforward, zcmd, clen);
606	  zforward[clen] = '\0';
607	  zcmd = zexclam + 1;
608	}
609    }
610
611  if (fXxqtlocal)
612    sXxqtsys = slocalsys;
613  else
614    {
615      iuuconf = uuconf_system_info (puuconf, zsys, &sXxqtsys);
616      if (iuuconf != UUCONF_SUCCESS)
617	{
618	  if (iuuconf != UUCONF_NOT_FOUND)
619	    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
620	  if (! funknown_system (puuconf, zsys, &sXxqtsys))
621	    ulog (LOG_FATAL, "%s: System not found", zsys);
622	}
623    }
624
625  /* Get the local name the remote system know us as.  */
626  zXxqtloc = sXxqtsys.uuconf_zlocalname;
627  if (zXxqtloc == NULL)
628    zXxqtloc = zlocalname;
629
630  /* Look through the arguments.  Any argument containing an
631     exclamation point character is interpreted as a file name, and is
632     sent to the appropriate system.  */
633  zinput_from = NULL;
634  zinput_to = NULL;
635  zinput_temp = NULL;
636  finputcopied = FALSE;
637  zcall_system = NULL;
638  fcall_any = FALSE;
639
640  for (i = 0; i < cargs; i++)
641    {
642      const char *zsystem;
643      char *zfile;
644      char *zforw;
645      boolean finput, foutput;
646      boolean flocal, fonxqt;
647
648      /* Check for a parenthesized argument; remove the parentheses
649	 and otherwise ignore it (this is how an exclamation point is
650	 quoted).  */
651      if (pzargs[i][0] == '(')
652	{
653	  clen = strlen (pzargs[i]);
654	  if (pzargs[i][clen - 1] != ')')
655	    ulog (LOG_ERROR, "Mismatched parentheses");
656	  else
657	    pzargs[i][clen - 1] = '\0';
658	  ++pzargs[i];
659	  continue;
660	}
661
662      /* Check whether we are doing a redirection.  */
663      finput = FALSE;
664      foutput = FALSE;
665      if (i + 1 < cargs)
666	{
667	  if (pzargs[i][0] == '<')
668	    finput = TRUE;
669	  else if (pzargs[i][0] == '>')
670	    foutput = TRUE;
671	  if (finput || foutput)
672	    {
673	      pzargs[i] = NULL;
674	      i++;
675	    }
676	}
677
678      zexclam = strchr (pzargs[i], '!');
679
680      /* If there is no exclamation point and no redirection, this
681	 argument is left untouched.  */
682      if (zexclam == NULL && ! finput && ! foutput)
683	continue;
684
685      if (zexclam != NULL)
686	{
687	  pzargs[i] = zremove_local_sys (&slocalsys, pzargs[i]);
688	  zexclam = strchr (pzargs[i], '!');
689	}
690
691      /* Get the system name and file name for this file.  */
692      if (zexclam == NULL)
693	{
694	  zsystem = zlocalname;
695	  zfile = pzargs[i];
696	  flocal = TRUE;
697	  zforw = NULL;
698	}
699      else
700	{
701	  *zexclam = '\0';
702	  zsystem = pzargs[i];
703	  zfile = zexclam + 1;
704	  flocal = FALSE;
705	  zexclam = strrchr (zfile, '!');
706	  if (zexclam == NULL)
707	    zforw = NULL;
708	  else
709	    {
710	      *zexclam = '\0';
711	      zforw = zfile;
712	      zfile = zexclam + 1;
713	    }
714	}
715
716      /* Check if the file is already on the execution system.  */
717      if (flocal)
718	fonxqt = fXxqtlocal;
719      else if (fXxqtlocal)
720	fonxqt = FALSE;
721      else if (zforward == NULL ? zforw != NULL : zforw == NULL)
722	fonxqt = FALSE;
723      else if (zforward != NULL
724	       && zforw != NULL
725	       && strcmp (zforward, zforw) != 0)
726	fonxqt = FALSE;
727      else if (strcmp (zsystem, sXxqtsys.uuconf_zname) == 0)
728	fonxqt = TRUE;
729      else if (sXxqtsys.uuconf_pzalias == NULL)
730	fonxqt = FALSE;
731      else
732	{
733	  char **pzal;
734
735	  fonxqt = FALSE;
736	  for (pzal = sXxqtsys.uuconf_pzalias; *pzal != NULL; pzal++)
737	    {
738	      if (strcmp (zsystem, *pzal) == 0)
739		{
740		  fonxqt = TRUE;
741		  break;
742		}
743	    }
744	}
745
746      /* Turn the file into an absolute path.  */
747      if (flocal)
748	zfile = zsysdep_local_file_cwd (zfile, sXxqtsys.uuconf_zpubdir,
749					(boolean *) NULL);
750      else if (fexpand)
751	zfile = zsysdep_add_cwd (zfile);
752      if (zfile == NULL)
753	uxabort (EX_OSERR);
754
755      /* Check for output redirection.  */
756      if (foutput)
757	{
758	  if (flocal)
759	    {
760	      if (! fin_directory_list (zfile,
761					sXxqtsys.uuconf_pzremote_receive,
762					sXxqtsys.uuconf_zpubdir, TRUE,
763					FALSE, (const char *) NULL))
764		ulog (LOG_FATAL, "Not permitted to create %s", zfile);
765	    }
766
767	  /* There are various cases of output redirection.
768
769	     uux cmd >out: The command is executed on the local
770		 system, and the output file is placed on the local
771		 system (fonxqt is TRUE).
772
773	     uux cmd >a!out: The command is executed on the local
774		 system, and the output file is sent to a.
775
776	     uux a!cmd >out: The command is executed on a, and the
777		 output file is returned to the local system (flocal
778		 is TRUE).
779
780	     uux a!cmd >a!out: The command is executed on a, and the
781		 output file is left on a (fonxqt is TRUE).
782
783	     uux a!cmd >b!out: The command is executed on a, and the
784		 output file is sent to b; traditionally, I believe
785		 that b is relative to a, rather than to the local
786		 system.  However, this essentially contradicts the
787		 previous two cases, in which the output file is
788		 relative to the local system.
789
790	     Now, the cases that we don't handle.
791
792	     uux cmd >a!b!out: The command is executed on the local
793		 system, and the output file is sent to b via a.  This
794		 requires the local uuxqt to support forwarding of the
795		 output file.
796
797	     uux a!b!cmd >out: The command is executed on b, which is
798		 reached via a.  Probably the output file is intended
799		 for the local system, in which case the uuxqt on b
800		 must support forwarding of the output file.
801
802	     uux a!b!cmd >c!out: Is c relative to b or to the local
803		 system?  If it's relative to b this is easy to
804		 handle.  Otherwise, we must arrange for the file to
805		 be sent back to the local system and for the local
806		 system to send it on to c.
807
808	     There are many variations of the last case.  It's not at
809	     all clear to me how they should be handled.  */
810	  if (zforward != NULL || zforw != NULL)
811	    ulog (LOG_FATAL, "May not forward standard output");
812
813	  if (fonxqt)
814	    uxadd_xqt_line ('O', zfile, (const char *) NULL);
815	  else if (flocal)
816	    uxadd_xqt_line ('O', zfile, zXxqtloc);
817	  else
818	    uxadd_xqt_line ('O', zfile, zsystem);
819	  pzargs[i] = NULL;
820	  continue;
821	}
822
823      if (finput)
824	{
825	  if (fread_stdin)
826	    ulog (LOG_FATAL, "Standard input specified twice");
827	  pzargs[i] = NULL;
828	}
829
830      if (flocal)
831	{
832	  char *zuse;
833	  char *zdata;
834	  char abtname[CFILE_NAME_LEN];
835	  char abdname[CFILE_NAME_LEN];
836
837	  /* It's a local file.  If requested by -C, copy the file to
838	     the spool directory.  If requested by -l, link the file
839	     to the spool directory; if the link fails, we copy the
840	     file, unless -c was explictly used.  If the execution is
841	     occurring on the local system, we force the copy as well,
842	     because otherwise we would have to have some way to tell
843	     uuxqt not to move the file.  If the file is being shipped
844	     to another system, we must set up a transfer request.
845	     First make sure the user has legitimate access, since we
846	     are running setuid.  */
847	  if (! fsysdep_access (zfile))
848	    uxabort (EX_NOINPUT);
849
850	  zdata = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, FALSE,
851					  abtname, abdname, (char *) NULL);
852	  if (zdata == NULL)
853	    uxabort (EX_OSERR);
854
855	  if (fcopy || flink)
856	    {
857	      /* This code path use to include fXxqtlocal, but now we skip
858	        it as fXxqtlocal will use raw path names unless fcopy or flink
859		are provided */
860	      boolean fdid;
861
862	      uxrecord_file (zdata);
863
864	      fdid = FALSE;
865	      if (flink)
866		{
867		  boolean fworked;
868
869		  if (! fsysdep_link (zfile, zdata, &fworked))
870		    uxabort (EX_OSERR);
871
872		  if (fworked)
873		    fdid = TRUE;
874		  else if (fdontcopy)
875		    ulog (LOG_FATAL, "%s: Can't link to spool directory",
876			  zfile);
877		}
878
879	      if (! fdid)
880		{
881		  openfile_t efile;
882
883		  efile = esysdep_user_fopen (zfile, TRUE, TRUE);
884		  if (! ffileisopen (efile))
885		    uxabort (EX_NOINPUT);
886		  if (! fcopy_open_file (efile, zdata, FALSE, TRUE, TRUE))
887		    uxabort (EX_CANTCREAT);
888		  (void) ffileclose (efile);
889		}
890
891	      zuse = abtname;
892	    }
893	  else
894	    {
895	      /* We don't actually use the spool file name, but we
896		 need a name to use as the destination.  */
897	      ubuffree (zdata);
898	      /* Make sure the daemon can access the file.  */
899	      if (! fsysdep_daemon_access (zfile))
900		uxabort (EX_NOINPUT);
901	      if (! fin_directory_list (zfile, sXxqtsys.uuconf_pzlocal_send,
902					sXxqtsys.uuconf_zpubdir, TRUE,
903					TRUE, zuser))
904		ulog (LOG_FATAL, "Not permitted to send from %s",
905		      zfile);
906
907	      zuse = zfile;
908	    }
909
910	  if (fXxqtlocal)
911	    {
912	      if (finput) {
913		uxadd_xqt_line ('I', zuse, (char *) NULL);
914	      } else {
915		/* XXX need full path!  /var/spool/uucp/UUNAME/D./... */
916		pzargs[i] = NULL;
917		if (fcopy || flink) {
918		    asprintf(pzargs + i, "/var/spool/uucp/%s/D./%s",
919		      zXxqtloc, zuse);
920		} else {
921		    pzargs[i] = zfile;
922		}
923	      }
924	    }
925	  else
926	    {
927	      finputcopied = fcopy || flink;
928
929	      if (finput)
930		{
931		  zinput_from = zuse;
932		  zinput_to = zbufcpy (abdname);
933		  zinput_temp = zbufcpy (abtname);
934		}
935	      else
936		{
937		  char *zbase;
938
939		  uxadd_send_file (zuse, abdname,
940				   finputcopied ? "C" : "c",
941				   abtname, zforward);
942		  zbase = zsysdep_base_name (zfile);
943		  if (zbase == NULL)
944		    uxabort (EX_OSERR);
945		  uxadd_xqt_line ('F', abdname, zbase);
946		  pzargs[i] = zbase;
947		}
948	    }
949	}
950      else if (fonxqt)
951	{
952	  /* The file is already on the system where the command is to
953	     be executed.  */
954	  if (finput)
955	    uxadd_xqt_line ('I', zfile, (const char *) NULL);
956	  else
957	    pzargs[i] = zfile;
958	}
959      else
960	{
961	  struct uuconf_system sfromsys;
962	  char abtname[CFILE_NAME_LEN];
963	  struct scmd s;
964	  char *zjobid;
965
966	  /* We need to request a remote file.  */
967	  iuuconf = uuconf_system_info (puuconf, zsystem, &sfromsys);
968	  if (iuuconf != UUCONF_SUCCESS)
969	    {
970	      if (iuuconf != UUCONF_NOT_FOUND)
971		ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
972	      if (! funknown_system (puuconf, zsystem, &sfromsys))
973		ulog (LOG_FATAL, "%s: System not found", zsystem);
974	    }
975
976	  if (fonxqt)
977	    {
978	      /* The file is already on the system where the command is to
979		 be executed.  */
980	      if (finput)
981		uxadd_xqt_line ('I', zfile, (const char *) NULL);
982	      else
983		pzargs[i] = zfile;
984	    }
985	  else
986	    {
987	      char *zdata;
988	      boolean ftemp;
989
990	      if (! sfromsys.uuconf_fcall_transfer
991		  && ! sfromsys.uuconf_fcalled_transfer)
992		ulog (LOG_FATAL,
993		      "Not permitted to transfer files to or from %s",
994		      sfromsys.uuconf_zname);
995
996	      if (zforw != NULL)
997		{
998		  /* This is ``uux cmd a!b!file''.  To make this work,
999		     we would have to be able to set up a request to a
1000		     to fetch file from b and send it to us.  But it
1001		     turns out that that will not work, because when a
1002		     sends us the file we will put it in a's spool
1003		     directory, not the local system spool directory.
1004		     So we won't have any way to find it.  This is not
1005		     a conceptual problem, and it could doubtless be
1006		     solved.  Please feel free to solve it and send me
1007		     the solution.  */
1008		  ulog (LOG_FATAL, "File forwarding not supported");
1009		}
1010
1011	      /* We must request the file from the remote system to
1012		 this one.  */
1013	      zdata = zsysdep_data_file_name (&slocalsys, zXxqtloc, bXgrade,
1014					      FALSE, abtname, (char *) NULL,
1015					      (char *) NULL);
1016	      if (zdata == NULL)
1017		uxabort (EX_OSERR);
1018	      ubuffree (zdata);
1019
1020	      /* Request the file.  The special option '9' is a signal
1021		 to uucico that it's OK to receive a file into the
1022		 spool directory; normally such requests are rejected.
1023		 This privilege is easy to abuse.  */
1024	      s.bcmd = 'R';
1025	      s.bgrade = bXgrade;
1026	      s.pseq = NULL;
1027	      s.zfrom = zfile;
1028	      s.zto = zbufcpy (abtname);
1029	      s.zuser = zuser;
1030	      s.zoptions = "9";
1031	      s.ztemp = "";
1032	      s.imode = 0600;
1033	      s.znotify = "";
1034	      s.cbytes = -1;
1035	      s.zcmd = NULL;
1036	      s.ipos = 0;
1037
1038	      zjobid = zsysdep_spool_commands (&sfromsys, bXgrade, 1, &s,
1039					       &ftemp);
1040	      if (zjobid == NULL)
1041		uxabort (ftemp ? EX_TEMPFAIL : EX_DATAERR);
1042
1043	      if (fjobid)
1044		printf ("%s\n", zjobid);
1045
1046	      ubuffree (zjobid);
1047
1048	      if (fcall_any)
1049		{
1050		  ubuffree (zcall_system);
1051		  zcall_system = NULL;
1052		}
1053	      else
1054		{
1055		  fcall_any = TRUE;
1056		  zcall_system = zbufcpy (sfromsys.uuconf_zname);
1057		}
1058
1059	      if (fXxqtlocal)
1060		{
1061		  /* Tell the command execution to wait until the file
1062		     has been received, and tell it the real file
1063		     name.  */
1064		  if (finput)
1065		    {
1066		      uxadd_xqt_line ('F', abtname, (char *) NULL);
1067		      uxadd_xqt_line ('I', abtname, (char *) NULL);
1068		    }
1069		  else
1070		    {
1071		      char *zbase;
1072
1073		      zbase = zsysdep_base_name (zfile);
1074		      if (zbase == NULL)
1075			uxabort (EX_OSERR);
1076		      uxadd_xqt_line ('F', abtname, zbase);
1077		      pzargs[i] = zbase;
1078		    }
1079		}
1080	      else
1081		{
1082		  char abxtname[CFILE_NAME_LEN];
1083		  char *zbase;
1084		  char *zxqt;
1085		  FILE *e;
1086
1087		  /* Now we must arrange to forward the file on to the
1088		     execution system.  We need to get a name to give
1089		     the file on the execution system (abxtname).  */
1090		  zdata = zsysdep_data_file_name (&sXxqtsys, zXxqtloc,
1091						  bXgrade, TRUE, abxtname,
1092						  (char *) NULL,
1093						  (char *) NULL);
1094		  if (zdata == NULL)
1095		    uxabort (EX_OSERR);
1096		  ubuffree (zdata);
1097
1098		  zbase = zsysdep_base_name (zfile);
1099		  if (zbase == NULL)
1100		    uxabort (EX_OSERR);
1101
1102		  zxqt = zsysdep_xqt_file_name ();
1103		  if (zxqt == NULL)
1104		    uxabort (EX_OSERR);
1105		  e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE);
1106		  if (e == NULL)
1107		    uxabort (EX_OSERR);
1108		  uxrecord_file (zxqt);
1109
1110		  fprintf (e, "U %s %s\n", zsysdep_login_name (),
1111			   zlocalname);
1112		  fprintf (e, "F %s %s\n", abtname, zbase);
1113		  fprintf (e, "C uucp -C -W -d -g %c %s %s!", bXgrade,
1114			   zbase, sXxqtsys.uuconf_zname);
1115		  if (zforward != NULL)
1116		    fprintf (e, "%s!", zforward);
1117		  fprintf (e, "%s\n", abxtname);
1118
1119		  if (! fstdiosync (e, zxqt))
1120		    ulog (LOG_FATAL, "fsync failed");
1121		  if (fclose (e) != 0)
1122		    ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1123
1124		  if (finput)
1125		    {
1126		      uxadd_xqt_line ('F', abxtname, (char *) NULL);
1127		      uxadd_xqt_line ('I', abxtname, (char *) NULL);
1128		      ubuffree (zbase);
1129		    }
1130		  else
1131		    {
1132		      uxadd_xqt_line ('F', abxtname, zbase);
1133		      pzargs[i] = zbase;
1134		    }
1135		}
1136	    }
1137
1138	  (void) uuconf_system_free (puuconf, &sfromsys);
1139	}
1140    }
1141
1142  /* If standard input is to be read from the stdin of uux, we read it
1143     here into a temporary file and send it to the execute system.  */
1144  if (fread_stdin)
1145    {
1146      char *zdata;
1147      char abtname[CFILE_NAME_LEN];
1148      char abdname[CFILE_NAME_LEN];
1149      FILE *e;
1150
1151      zdata = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, FALSE,
1152				      abtname, abdname, (char *) NULL);
1153      if (zdata == NULL)
1154	uxabort (EX_OSERR);
1155
1156      e = esysdep_fopen (zdata, FALSE, FALSE, TRUE);
1157      if (e == NULL)
1158	uxabort (EX_OSERR);
1159
1160      eXclose = e;
1161      uxrecord_file (zdata);
1162
1163      uxcopy_stdin (e);
1164
1165      if (! fstdiosync (e, zdata))
1166	ulog (LOG_FATAL, "fsync failed");
1167      eXclose = NULL;
1168      if (fclose (e) != 0)
1169	ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1170
1171      if (fXxqtlocal)
1172	uxadd_xqt_line ('I', abtname, (const char *) NULL);
1173      else
1174	{
1175	  zinput_from = zbufcpy (abtname);
1176	  zinput_to = zbufcpy (abdname);
1177	  zinput_temp = zinput_from;
1178	  finputcopied = TRUE;
1179	}
1180    }
1181
1182  /* If we are returning standard input, or we're putting the status
1183     in a file, we can't use an E command.  */
1184  if (fretstdin)
1185    uxadd_xqt_line ('B', (const char *) NULL, (const char *) NULL);
1186
1187  if (zstatus_file != NULL)
1188    uxadd_xqt_line ('M', zstatus_file, (const char *) NULL);
1189
1190  /* Get the complete command line, and decide whether the command
1191     needs to be executed by the shell.  */
1192  fneedshell = FALSE;
1193
1194  if (zcmd[strcspn (zcmd, ZSHELLCHARS)] != '\0')
1195    fneedshell = TRUE;
1196
1197  clen = strlen (zcmd) + 1;
1198  for (i = 0; i < cargs; i++)
1199    {
1200      if (pzargs[i] != NULL)
1201	{
1202	  clen += strlen (pzargs[i]) + 1;
1203	  if (pzargs[i][strcspn (pzargs[i], ZSHELLCHARS)] != '\0')
1204	    fneedshell = TRUE;
1205	}
1206    }
1207
1208  zfullcmd = zbufalc (clen);
1209
1210  strcpy (zfullcmd, zcmd);
1211  for (i = 0; i < cargs; i++)
1212    {
1213      if (pzargs[i] != NULL)
1214	{
1215	  strcat (zfullcmd, " ");
1216	  strcat (zfullcmd, pzargs[i]);
1217	}
1218    }
1219
1220  fpoll = FALSE;
1221
1222  /* If we haven't written anything to the execution file yet, and we
1223     have a standard input file, and we're not forwarding, then every
1224     other option can be handled in an E command.  */
1225  if (eXxqt_file == NULL && zinput_from != NULL && zforward == NULL)
1226    {
1227      struct scmd s;
1228      char *zoptions;
1229
1230      /* Set up an E command.  */
1231      s.bcmd = 'E';
1232      s.bgrade = bXgrade;
1233      s.pseq = NULL;
1234      s.zuser = zuser;
1235      s.zfrom = zinput_from;
1236      s.zto = zinput_to;
1237      s.zoptions = aboptions;
1238      zoptions = aboptions;
1239      *zoptions++ = finputcopied ? 'C' : 'c';
1240      if (fno_ack)
1241	*zoptions++ = 'N';
1242      if (ferror_ack)
1243	*zoptions++ = 'Z';
1244      if (zrequestor != NULL)
1245	*zoptions++ = 'R';
1246      if (fneedshell)
1247	*zoptions++ = 'e';
1248      *zoptions = '\0';
1249      s.ztemp = zinput_temp;
1250      s.imode = 0666;
1251      if (zrequestor == NULL)
1252	zrequestor = "\"\"";
1253      s.znotify = zrequestor;
1254      s.cbytes = -1;
1255      s.zcmd = zfullcmd;
1256      s.ipos = 0;
1257
1258      ++cXcmds;
1259      pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1260					   cXcmds * sizeof (struct scmd));
1261      pasXcmds[cXcmds - 1] = s;
1262
1263      uxadd_name (zinput_from);
1264    }
1265  else if (*zfullcmd == '\0'
1266	   && eXxqt_file == NULL
1267	   && zinput_from == NULL
1268	   && cXcmds == 0)
1269    {
1270      /* As a special case, if we are asked to execute an empty
1271         command, we create a poll file instead.  */
1272      fpoll = TRUE;
1273    }
1274  else
1275    {
1276      /* Finish up the execute file.  */
1277      uxadd_xqt_line ('U', zuser, zXxqtloc);
1278      if (zinput_from != NULL)
1279	{
1280	  uxadd_xqt_line ('F', zinput_to, (char *) NULL);
1281	  uxadd_xqt_line ('I', zinput_to, (char *) NULL);
1282	  uxadd_send_file (zinput_from, zinput_to,
1283			   finputcopied ? "C" : "c",
1284			   zinput_temp, zforward);
1285	}
1286      if (fXxqtlocal) {
1287	  uxadd_xqt_line('W', zScwd, NULL);
1288      }
1289      if (fno_ack)
1290	uxadd_xqt_line ('N', (const char *) NULL, (const char *) NULL);
1291      if (ferror_ack)
1292	uxadd_xqt_line ('Z', (const char *) NULL, (const char *) NULL);
1293      if (zrequestor != NULL)
1294	uxadd_xqt_line ('R', zrequestor, (const char *) NULL);
1295      if (fneedshell)
1296	uxadd_xqt_line ('e', (const char *) NULL, (const char *) NULL);
1297      uxadd_xqt_line ('C', zfullcmd, (const char *) NULL);
1298      if (! fstdiosync (eXxqt_file, "execution file"))
1299	ulog (LOG_FATAL, "fsync failed");
1300      if (fclose (eXxqt_file) != 0)
1301	ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1302      eXxqt_file = NULL;
1303
1304      /* If the execution is to occur on another system, we must now
1305	 arrange to copy the execute file to this system.  */
1306      if (! fXxqtlocal)
1307	uxadd_send_file (abXxqt_tname, abXxqt_xname, "C", abXxqt_tname,
1308			 zforward);
1309    }
1310
1311  /* If we got a signal, get out before spooling anything.  */
1312  if (FGOT_SIGNAL ())
1313    uxabort (EX_OSERR);
1314
1315  /* From here on in, it's too late.  We don't call uxabort.  */
1316  if (cXcmds > 0 || fpoll)
1317    {
1318      char *zjobid;
1319      boolean ftemp;
1320
1321      if (! fpoll
1322	  && ! sXxqtsys.uuconf_fcall_transfer
1323	  && ! sXxqtsys.uuconf_fcalled_transfer)
1324	ulog (LOG_FATAL, "Not permitted to transfer files to or from %s",
1325	      sXxqtsys.uuconf_zname);
1326
1327      zjobid = zsysdep_spool_commands (&sXxqtsys, bXgrade, cXcmds, pasXcmds,
1328				       &ftemp);
1329      if (zjobid == NULL)
1330	{
1331	  ulog_close ();
1332	  exit (ftemp ? EX_TEMPFAIL : EX_DATAERR);
1333	}
1334
1335      if (fjobid)
1336	printf ("%s\n", zjobid);
1337
1338      ubuffree (zjobid);
1339
1340      if (fcall_any)
1341	{
1342	  ubuffree (zcall_system);
1343	  zcall_system = NULL;
1344	}
1345      else
1346	{
1347	  fcall_any = TRUE;
1348	  zcall_system = zbufcpy (sXxqtsys.uuconf_zname);
1349	}
1350    }
1351
1352  if (! fpoll)
1353    {
1354      /* If all that worked, make a log file entry.  All log file
1355	 reports up to this point went to stderr.  */
1356      ulog_to_file (puuconf, TRUE);
1357      ulog_system (sXxqtsys.uuconf_zname);
1358      ulog_user (zuser);
1359
1360      if (zXnames == NULL)
1361	ulog (LOG_NORMAL, "Queuing %s", zfullcmd);
1362      else
1363	ulog (LOG_NORMAL, "Queuing %s (%s)", zfullcmd, zXnames);
1364
1365      if (fjobid) {
1366	  printf("%s\n", zXqt_name);
1367      }
1368
1369      ulog_close ();
1370    }
1371
1372  if (! fuucico
1373      || (zcall_system == NULL && ! fcall_any))
1374    {
1375      if (fXxqtlocal && fuucico)
1376	{
1377	  char *zconfigarg;
1378
1379	  if (zconfig == NULL)
1380	    zconfigarg = NULL;
1381	  else
1382	    {
1383	      zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig));
1384	      sprintf (zconfigarg, "-I%s", zconfig);
1385	    }
1386
1387	  (void) fsysdep_run (FALSE, "uuxqt", zconfigarg,
1388			      (const char *) NULL);
1389	}
1390    }
1391  else
1392    {
1393      const char *zcicoarg;
1394      char *zconfigarg;
1395
1396      if (zcall_system == NULL)
1397	zcicoarg = "-r1";
1398      else
1399	{
1400	  char *z;
1401
1402	  z = zbufalc (sizeof "-Cs" + strlen (zcall_system));
1403	  sprintf (z, "-Cs%s", zcall_system);
1404	  zcicoarg = z;
1405	}
1406
1407      if (zconfig == NULL)
1408	zconfigarg = NULL;
1409      else
1410	{
1411	  zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig));
1412	  sprintf (zconfigarg, "-I%s", zconfig);
1413	}
1414
1415      (void) fsysdep_run (FALSE, "uucico", zcicoarg, zconfigarg);
1416    }
1417
1418  exit (EX_OK);
1419
1420  /* Avoid error about not returning a value.  */
1421  return 0;
1422}
1423
1424/* Report command usage.  */
1425
1426static void
1427uxhelp ()
1428{
1429  printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n",
1430	  VERSION);
1431  printf ("Usage: %s [options] [-] command\n", zProgram);
1432  printf (" -,-p,--stdin: Read standard input for standard input of command\n");
1433  printf (" -c,--nocopy: Do not copy local files to spool directory (default)\n");
1434  printf (" -C,--copy: Copy local files to spool directory\n");
1435  printf (" -l,--link: link local files to spool directory\n");
1436  printf (" -g,--grade grade: Set job grade (must be alphabetic)\n");
1437  printf (" -n,--notification=no: Do not report completion status\n");
1438  printf (" -z,--notification=error: Report completion status only on error\n");
1439  printf (" -r,--nouucico: Do not start uucico daemon\n");
1440  printf (" -a,--requestor address: Address to mail status report to\n");
1441  printf (" -b,--return-stdin: Return standard input with status report\n");
1442  printf (" -s,--status file: Report completion status to file\n");
1443  printf (" -j,--jobid: Report job id\n");
1444  printf (" -x,--debug debug: Set debugging level\n");
1445#if HAVE_TAYLOR_CONFIG
1446  printf (" -I,--config file: Set configuration file to use\n");
1447#endif /* HAVE_TAYLOR_CONFIG */
1448  printf (" -v,--version: Print version and exit\n");
1449  printf (" --help: Print help and exit\n");
1450  printf ("Report bugs to taylor-uucp@gnu.org\n");
1451}
1452
1453static void
1454uxusage ()
1455{
1456  fprintf (stderr,
1457	   "Usage: %s [options] [-] command\n", zProgram);
1458  fprintf (stderr, "Use %s --help for help\n", zProgram);
1459  exit (EX_USAGE);
1460}
1461
1462/* Add a line to the execute file.  */
1463
1464static void
1465uxadd_xqt_line (bchar, z1, z2)
1466     int bchar;
1467     const char *z1;
1468     const char *z2;
1469{
1470  char *z1q;
1471  char *z2q;
1472
1473  if (eXxqt_file == NULL)
1474    {
1475      if (fXxqtlocal)
1476	zXqt_name = zsysdep_xqt_file_name ();
1477      else
1478	zXqt_name = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, TRUE,
1479					    abXxqt_tname, (char *) NULL,
1480					    abXxqt_xname);
1481      if (zXqt_name == NULL)
1482	uxabort (EX_OSERR);
1483
1484      uxrecord_file (zXqt_name);
1485
1486      eXxqt_file = esysdep_fopen (zXqt_name, FALSE, FALSE, TRUE);
1487      if (eXxqt_file == NULL)
1488	uxabort (EX_OSERR);
1489    }
1490
1491  z1q = NULL;
1492  z2q = NULL;
1493  if (fXquote)
1494    {
1495      if (! fXquote_output)
1496	{
1497	  fprintf (eXxqt_file, "Q\n");
1498	  fXquote_output = TRUE;
1499	}
1500
1501      if (z1 != NULL)
1502	{
1503	  z1q = zquote_cmd_string (z1, FALSE);
1504	  z1 = z1q;
1505	}
1506      if (z2 != NULL)
1507	{
1508	  z2q = zquote_cmd_string (z2, FALSE);
1509	  z2 = z2q;
1510	}
1511    }
1512
1513  if (z1 == NULL)
1514    fprintf (eXxqt_file, "%c\n", bchar);
1515  else if (z2 == NULL)
1516    fprintf (eXxqt_file, "%c %s\n", bchar, z1);
1517  else
1518    fprintf (eXxqt_file, "%c %s %s\n", bchar, z1, z2);
1519
1520  if (z1q != NULL)
1521    ubuffree (z1q);
1522  if (z2q != NULL)
1523    ubuffree (z2q);
1524}
1525
1526/* Add a file to be sent to the execute system.  */
1527
1528static void
1529uxadd_send_file (zfrom, zto, zoptions, ztemp, zforward)
1530     const char *zfrom;
1531     const char *zto;
1532     const char *zoptions;
1533     const char *ztemp;
1534     const char *zforward;
1535{
1536  struct scmd s;
1537
1538  if (zforward != NULL)
1539    {
1540      char *zbase;
1541      char *zxqt;
1542      char abtname[CFILE_NAME_LEN];
1543      char abdname[CFILE_NAME_LEN];
1544      char abxname[CFILE_NAME_LEN];
1545      FILE *e;
1546
1547      /* We want to forward this file through the first execution
1548	 system to other systems.  We set up a remote execution of
1549	 uucp to forward the file.  */
1550      zbase = zsysdep_base_name (zfrom);
1551      if (zbase == NULL)
1552	uxabort (EX_OSERR);
1553
1554      zxqt = zsysdep_data_file_name (&sXxqtsys, zXxqtloc, bXgrade, TRUE,
1555				     abtname, abdname, abxname);
1556      if (zxqt == NULL)
1557	uxabort (EX_OSERR);
1558      e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE);
1559      if (e == NULL)
1560	uxabort (EX_OSERR);
1561      uxrecord_file (zxqt);
1562
1563      fprintf (e, "U %s %s\n", zsysdep_login_name (), zXxqtloc);
1564      fprintf (e, "F %s %s\n", abdname, zbase);
1565      fprintf (e, "C uucp -C -W -d -g %c %s %s!%s\n",
1566	       bXgrade, zbase, zforward, zto);
1567
1568      ubuffree (zbase);
1569
1570      if (! fstdiosync (e, zxqt))
1571	ulog (LOG_FATAL, "fsync failed");
1572      if (fclose (e) != 0)
1573	ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1574
1575      /* Send the execution file.  */
1576      s.bcmd = 'S';
1577      s.bgrade = bXgrade;
1578      s.pseq = NULL;
1579      s.zfrom = zbufcpy (abtname);
1580      s.zto = zbufcpy (abxname);
1581      s.zuser = zsysdep_login_name ();
1582      s.zoptions = "C";
1583      s.ztemp = s.zfrom;
1584      s.imode = 0666;
1585      s.znotify = NULL;
1586      s.cbytes = -1;
1587      s.zcmd = NULL;
1588      s.ipos = 0;
1589
1590      ++cXcmds;
1591      pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1592					   cXcmds * sizeof (struct scmd));
1593      pasXcmds[cXcmds - 1] = s;
1594
1595      uxadd_name (abtname);
1596
1597      /* Send the data file to abdname where the execution file will
1598	 expect it.  */
1599      zto = abdname;
1600    }
1601
1602  s.bcmd = 'S';
1603  s.bgrade = bXgrade;
1604  s.pseq = NULL;
1605  s.zfrom = zbufcpy (zfrom);
1606  s.zto = zbufcpy (zto);
1607  s.zuser = zsysdep_login_name ();
1608  s.zoptions = zbufcpy (zoptions);
1609  s.ztemp = zbufcpy (ztemp);
1610  s.imode = 0666;
1611  s.znotify = "";
1612  s.cbytes = -1;
1613  s.zcmd = NULL;
1614  s.ipos = 0;
1615
1616  ++cXcmds;
1617  pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1618				       cXcmds * sizeof (struct scmd));
1619  pasXcmds[cXcmds - 1] = s;
1620
1621  uxadd_name (zfrom);
1622}
1623
1624/* Copy stdin to a file.  This is a separate function because it may
1625   call setjmp.  */
1626
1627static void
1628uxcopy_stdin (e)
1629     FILE *e;
1630{
1631  CATCH_PROTECT size_t cread;
1632  char ab[1024];
1633
1634  do
1635    {
1636      size_t cwrite;
1637
1638      /* I want to use fread here, but there is a bug in some versions
1639	 of SVR4 which causes fread to return less than a complete
1640	 buffer even if EOF has not been reached.  This is not online
1641	 time, so speed is not critical, but it's still quite annoying
1642	 to have to use an inefficient algorithm.  */
1643      cread = 0;
1644      if (fsysdep_catch ())
1645	{
1646	  usysdep_start_catch ();
1647
1648	  while (cread < sizeof (ab))
1649	    {
1650	      int b;
1651
1652	      if (FGOT_SIGNAL ())
1653		uxabort (EX_OSERR);
1654
1655	      /* There's an unimportant race here.  If the user hits
1656		 ^C between the FGOT_SIGNAL we just did and the time
1657		 we enter getchar, we won't know about the signal
1658		 (unless we're doing a longjmp, but we normally
1659		 aren't).  It's not a big problem, because the user
1660		 can just hit ^C again.  */
1661	      b = getchar ();
1662	      if (b == EOF)
1663		break;
1664	      ab[cread] = b;
1665	      ++cread;
1666	    }
1667	}
1668
1669      usysdep_end_catch ();
1670
1671      if (FGOT_SIGNAL ())
1672	uxabort (EX_OSERR);
1673
1674      if (cread > 0)
1675	{
1676	  cwrite = fwrite (ab, sizeof (char), cread, e);
1677	  if (cwrite != cread)
1678	    ulog (LOG_FATAL, "fwrite: Wrote %d when attempted %d",
1679		  (int) cwrite, (int) cread);
1680	}
1681    }
1682  while (cread == sizeof ab);
1683}
1684
1685/* Keep track of all files we have created so that we can delete them
1686   if we get a signal.  The argument will be on the heap.  */
1687
1688static int cXfiles;
1689static const char **pXaz;
1690
1691static void
1692uxrecord_file (zfile)
1693     const char *zfile;
1694{
1695  pXaz = (const char **) xrealloc ((pointer) pXaz,
1696				   (cXfiles + 1) * sizeof (const char *));
1697  pXaz[cXfiles] = zfile;
1698  ++cXfiles;
1699}
1700
1701/* The function called for a LOG_FATAL error.  */
1702
1703static void
1704uxfatal ()
1705{
1706  uxabort (EX_UNAVAILABLE);
1707}
1708
1709/* Delete all the files we have recorded and exit.  */
1710
1711static void
1712uxabort (istat)
1713     int istat;
1714{
1715  int i;
1716
1717  if (eXxqt_file != NULL)
1718    (void) fclose (eXxqt_file);
1719  if (eXclose != NULL)
1720    (void) fclose (eXclose);
1721  for (i = 0; i < cXfiles; i++)
1722    (void) remove (pXaz[i]);
1723  ulog_close ();
1724  exit (istat);
1725}
1726
1727/* Add a name to the list of file names we are going to log.  We log
1728   all the file names which will appear in the uucico log file.  This
1729   permits people to associate the file send in the uucico log with
1730   the uux entry which created the file.  Normally only one file name
1731   will appear.  */
1732
1733static void
1734uxadd_name (z)
1735     const char *z;
1736{
1737  if (zXnames == NULL)
1738    zXnames = zbufcpy (z);
1739  else
1740    {
1741      size_t cold, cadd;
1742      char *znew;
1743
1744      cold = strlen (zXnames);
1745      cadd = strlen (z);
1746      znew = zbufalc (cold + 2 + cadd);
1747      memcpy (znew, zXnames, cold);
1748      znew[cold] = ' ';
1749      memcpy (znew + cold + 1, z, cadd + 1);
1750      ubuffree (zXnames);
1751      zXnames = znew;
1752    }
1753}
1754