home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2J (Developer) / os42jdev.iso / NextDeveloper / Source / GNU / uucp / uux / uux.c < prev   
Encoding:
C/C++ Source or Header  |  1995-08-20  |  40.8 KB  |  1,627 lines

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