(root)/
make-4.4/
src/
vmsjobs.c
       1  /* --------------- Moved here from job.c ---------------
       2     This file must be #included in job.c, as it accesses static functions.
       3  
       4  Copyright (C) 1996-2022 Free Software Foundation, Inc.
       5  This file is part of GNU Make.
       6  
       7  GNU Make is free software; you can redistribute it and/or modify it under the
       8  terms of the GNU General Public License as published by the Free Software
       9  Foundation; either version 3 of the License, or (at your option) any later
      10  version.
      11  
      12  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      13  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      14  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      15  
      16  You should have received a copy of the GNU General Public License along with
      17  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <string.h>
      20  #include <descrip.h>
      21  #include <clidef.h>
      22  
      23  /* TODO - VMS specific header file conditionally included in makeint.h */
      24  
      25  #include <stsdef.h>
      26  #include <ssdef.h>
      27  void
      28  decc$exit (int status);
      29  
      30  /* Lowest legal non-success VMS exit code is 8 */
      31  /* GNU make only defines codes 0, 1, 2 */
      32  /* So assume any exit code > 8 is a VMS exit code */
      33  
      34  #ifndef MAX_EXPECTED_EXIT_CODE
      35  # define MAX_EXPECTED_EXIT_CODE 7
      36  #endif
      37  
      38  
      39  #if __CRTL_VER >= 70302000 && !defined(__VAX)
      40  # define MAX_DCL_LINE_LENGTH 4095
      41  # define MAX_DCL_CMD_LINE_LENGTH 8192
      42  #else
      43  # define MAX_DCL_LINE_LENGTH 255
      44  # define MAX_DCL_CMD_LINE_LENGTH 1024
      45  #endif
      46  #define MAX_DCL_TOKEN_LENGTH 255
      47  #define MAX_DCL_TOKENS 127
      48  
      49  enum auto_pipe { nopipe, add_pipe, dcl_pipe };
      50  
      51  char *vmsify (char *name, int type);
      52  
      53  static int vms_jobsefnmask = 0;
      54  
      55  /* returns whether path is assumed to be a unix like shell. */
      56  int
      57  _is_unixy_shell (const char *path)
      58  {
      59    return vms_gnv_shell;
      60  }
      61  
      62  #define VMS_GETMSG_MAX 256
      63  static char vms_strsignal_text[VMS_GETMSG_MAX + 2];
      64  
      65  char *
      66  vms_strsignal (int status)
      67  {
      68    if (status <= MAX_EXPECTED_EXIT_CODE)
      69      sprintf (vms_strsignal_text, "lib$spawn returned %x", status);
      70    else
      71      {
      72        int vms_status;
      73        unsigned short * msg_len;
      74        unsigned char out[4];
      75        vms_status = SYS$GETMSG (status, &msg_len,
      76                                 vms_strsignal_text, 7, *out);
      77      }
      78  
      79    return vms_strsignal_text;
      80  }
      81  
      82  
      83  /* Wait for nchildren children to terminate */
      84  static void
      85  vmsWaitForChildren (int *status)
      86  {
      87    while (1)
      88      {
      89        if (!vms_jobsefnmask)
      90          {
      91            *status = 0;
      92            return;
      93          }
      94  
      95        *status = sys$wflor (32, vms_jobsefnmask);
      96      }
      97    return;
      98  }
      99  
     100  static int ctrlYPressed= 0;
     101  /* This is called at main or AST level. It is at AST level for DONTWAITFORCHILD
     102     and at main level otherwise. In any case it is called when a child process
     103     terminated. At AST level it won't get interrupted by anything except a
     104     inner mode level AST.
     105  */
     106  static int
     107  vmsHandleChildTerm (struct childbase *cbase)
     108  {
     109    struct child *child = (struct child*)cbase;
     110    struct child *lastc, *c;
     111    int child_failed;
     112    int exit_code;
     113  
     114    /* The child efn is 0 when a built-in or null command is executed
     115       successfully with out actually creating a child.
     116    */
     117    if (child->efn > 0)
     118    {
     119      vms_jobsefnmask &= ~(1 << (child->efn - 32));
     120  
     121      lib$free_ef (&child->efn);
     122    }
     123    if (child->comname)
     124      {
     125        if (!ISDB (DB_JOBS) && !ctrlYPressed)
     126          unlink (child->comname);
     127        free (child->comname);
     128      }
     129  
     130    (void) sigblock (fatal_signal_mask);
     131  
     132    /* First check to see if this is a POSIX exit status and handle */
     133    if ((child->cstatus & VMS_POSIX_EXIT_MASK) == VMS_POSIX_EXIT_MASK)
     134      {
     135        exit_code = (child->cstatus >> 3) & 255;
     136        if (exit_code != MAKE_SUCCESS)
     137          child_failed = 1;
     138      }
     139    else
     140      {
     141        child_failed = !$VMS_STATUS_SUCCESS (child->cstatus);
     142        if (child_failed)
     143          exit_code = child->cstatus;
     144      }
     145  
     146    /* Search for a child matching the deceased one.  */
     147    lastc = 0;
     148  #if defined(RECURSIVEJOBS)
     149    /* I've had problems with recursive stuff and process handling */
     150    for (c = children; c != 0 && c != child; lastc = c, c = c->next)
     151      ;
     152  #else
     153    c = child;
     154  #endif
     155  
     156    if ($VMS_STATUS_SUCCESS (child->vms_launch_status))
     157      {
     158        /* Convert VMS success status to 0 for UNIX code to be happy */
     159        child->vms_launch_status = 0;
     160      }
     161  
     162    /* Set the state flag to say the commands have finished.  */
     163    c->file->command_state = cs_finished;
     164    notice_finished_file (c->file);
     165  
     166    (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask));
     167  
     168    return 1;
     169  }
     170  
     171  /* VMS:
     172     Spawn a process executing the command in ARGV and return its pid. */
     173  
     174  /* local helpers to make ctrl+c and ctrl+y working, see below */
     175  #include <iodef.h>
     176  #include <libclidef.h>
     177  #include <ssdef.h>
     178  
     179  static int ctrlMask= LIB$M_CLI_CTRLY;
     180  static int oldCtrlMask;
     181  static int setupYAstTried= 0;
     182  static unsigned short int chan= 0;
     183  
     184  static void
     185  reEnableAst(void)
     186  {
     187    lib$enable_ctrl (&oldCtrlMask,0);
     188  }
     189  
     190  static int
     191  astYHandler (void)
     192  {
     193    struct child *c;
     194    for (c = children; c != 0; c = c->next)
     195      if (c->pid > 0)
     196        sys$delprc (&c->pid, 0, 0);
     197    ctrlYPressed= 1;
     198    kill (getpid(),SIGQUIT);
     199    return SS$_NORMAL;
     200  }
     201  
     202  static void
     203  tryToSetupYAst(void)
     204  {
     205    $DESCRIPTOR(inputDsc,"SYS$COMMAND");
     206    int     status;
     207    struct {
     208      short int       status, count;
     209      int     dvi;
     210    } iosb;
     211    unsigned short int loc_chan;
     212  
     213    setupYAstTried++;
     214  
     215    if (chan)
     216      loc_chan= chan;
     217    else
     218      {
     219        status= sys$assign(&inputDsc,&loc_chan,0,0);
     220        if (!(status&SS$_NORMAL))
     221          {
     222            lib$signal(status);
     223            return;
     224          }
     225      }
     226    status= sys$qiow (0, loc_chan, IO$_SETMODE|IO$M_CTRLYAST,&iosb,0,0,
     227                      astYHandler,0,0,0,0,0);
     228    if (status==SS$_NORMAL)
     229      status= iosb.status;
     230    if (status!=SS$_NORMAL)
     231      {
     232        if (!chan)
     233          sys$dassgn(loc_chan);
     234        if (status!=SS$_ILLIOFUNC && status!=SS$_NOPRIV)
     235          lib$signal(status);
     236        return;
     237      }
     238  
     239    /* called from AST handler ? */
     240    if (setupYAstTried>1)
     241      return;
     242    if (atexit(reEnableAst))
     243      fprintf (stderr,
     244               _("-warning, you may have to re-enable CTRL-Y handling from DCL.\n"));
     245    status= lib$disable_ctrl (&ctrlMask, &oldCtrlMask);
     246    if (!(status&SS$_NORMAL))
     247      {
     248        lib$signal(status);
     249        return;
     250      }
     251    if (!chan)
     252      chan = loc_chan;
     253  }
     254  
     255  /* Check if a token is too long */
     256  #define INC_TOKEN_LEN_OR_RETURN(x) {token->length++; \
     257    if (token->length >= MAX_DCL_TOKEN_LENGTH) \
     258      { token->cmd_errno = ERANGE; return x; }}
     259  
     260  #define INC_TOKEN_LEN_OR_BREAK {token->length++; \
     261    if (token->length >= MAX_DCL_TOKEN_LENGTH) \
     262      { token->cmd_errno = ERANGE; break; }}
     263  
     264  #define ADD_TOKEN_LEN_OR_RETURN(add_len, x) {token->length += add_len; \
     265    if (token->length >= MAX_DCL_TOKEN_LENGTH) \
     266      { token->cmd_errno = ERANGE; return x; }}
     267  
     268  /* Check if we are out of space for more tokens */
     269  #define V_NEXT_TOKEN { if (cmd_tkn_index < MAX_DCL_TOKENS) \
     270    cmd_tokens[++cmd_tkn_index] = NULL; \
     271    else { token.cmd_errno = E2BIG; break; } \
     272    token.length = 0;}
     273  
     274  
     275  #define UPDATE_TOKEN {cmd_tokens[cmd_tkn_index] = strdup(token.text); \
     276    V_NEXT_TOKEN;}
     277  
     278  #define EOS_ERROR(x) { if (*x == 0) { token->cmd_errno = ERANGE; break; }}
     279  
     280  struct token_info
     281    {
     282      char *text;       /* Parsed text */
     283      int length;       /* Length of parsed text */
     284      char *src;        /* Pointer to source text */
     285      int cmd_errno;    /* Error status of parse */
     286      int use_cmd_file; /* Force use of a command file */
     287    };
     288  
     289  
     290  /* Extract a Posix single quoted string from input line */
     291  static char *
     292  posix_parse_sq (struct token_info *token)
     293  {
     294    /* A Posix quoted string with no expansion unless in a string
     295       Unix simulation means no lexical functions present.
     296    */
     297    char * q;
     298    char * p;
     299    q = token->text;
     300    p = token->src;
     301  
     302    *q++ = '"';
     303    p++;
     304    INC_TOKEN_LEN_OR_RETURN (p);
     305  
     306    while (*p != '\'' && (token->length < MAX_DCL_TOKEN_LENGTH))
     307      {
     308        EOS_ERROR (p);
     309        if (*p == '"')
     310          {
     311            /* Embedded double quotes need to be doubled */
     312            *q++ = '"';
     313            INC_TOKEN_LEN_OR_BREAK;
     314            *q = '"';
     315          }
     316        else
     317          *q = *p;
     318  
     319        q++;
     320        p++;
     321        INC_TOKEN_LEN_OR_BREAK;
     322      }
     323    *q++ = '"';
     324    p++;
     325    INC_TOKEN_LEN_OR_RETURN (p);
     326    *q = 0;
     327    return p;
     328  }
     329  
     330  /* Extract a Posix double quoted string from input line */
     331  static char *
     332  posix_parse_dq (struct token_info *token)
     333  {
     334    /* Unix mode:  Any embedded \" becomes doubled.
     335                   \t is tab, \\, \$ leading character stripped.
     336                   $ character replaced with \' unless escaped.
     337    */
     338    char * q;
     339    char * p;
     340    q = token->text;
     341    p = token->src;
     342    *q++ = *p++;
     343    INC_TOKEN_LEN_OR_RETURN (p);
     344    while (*p != 0)
     345      {
     346        if (*p == '\\')
     347          {
     348            switch(p[1])
     349              {
     350              case 't':     /* Convert tabs */
     351                *q = '\t';
     352                p++;
     353                break;
     354              case '\\':     /* Just remove leading backslash */
     355              case '$':
     356                p++;
     357                *q = *p;
     358                break;
     359              case '"':
     360                p++;
     361                *q = *p;
     362                *q++ = '"';
     363                INC_TOKEN_LEN_OR_BREAK;
     364              default:      /* Pass through unchanged */
     365                *q++ = *p++;
     366                INC_TOKEN_LEN_OR_BREAK;
     367              }
     368            INC_TOKEN_LEN_OR_BREAK;
     369          }
     370        else if (*p == '$' && isalpha ((unsigned char) p[1]))
     371          {
     372            /* A symbol we should be able to substitute */
     373            *q++ = '\'';
     374            INC_TOKEN_LEN_OR_BREAK;
     375            *q = '\'';
     376            INC_TOKEN_LEN_OR_BREAK;
     377            token->use_cmd_file = 1;
     378          }
     379        else
     380          {
     381            *q = *p;
     382            INC_TOKEN_LEN_OR_BREAK;
     383            if (*p == '"')
     384              {
     385                p++;
     386                q++;
     387                break;
     388              }
     389          }
     390        p++;
     391        q++;
     392      }
     393    *q = 0;
     394    return p;
     395  }
     396  
     397  /* Extract a VMS quoted string or substitution string from input line */
     398  static char *
     399  vms_parse_quotes (struct token_info *token)
     400  {
     401    /* VMS mode, the \' means that a symbol substitution is starting
     402       so while you might think you can just copy until the next
     403       \'.  Unfortunately the substitution can be a lexical function
     404       which can contain embedded strings and lexical functions.
     405       Messy, so both types need to be handled together.
     406    */
     407    char * q;
     408    char * p;
     409    q = token->text;
     410    p = token->src;
     411    int parse_level[MAX_DCL_TOKENS + 1];
     412    int nest = 0;
     413  
     414    parse_level[0] = *p;
     415    if (parse_level[0] == '\'')
     416      token->use_cmd_file = 1;
     417  
     418    *q++ = *p++;
     419    INC_TOKEN_LEN_OR_RETURN (p);
     420  
     421  
     422    /* Copy everything until after the next single quote at nest == 0 */
     423    while (token->length < MAX_DCL_TOKEN_LENGTH)
     424      {
     425        EOS_ERROR (p);
     426        *q = *p;
     427        INC_TOKEN_LEN_OR_BREAK;
     428        if ((*p == parse_level[nest]) && (p[1] != '"'))
     429          {
     430            if (nest == 0)
     431              {
     432                *q++ = *p++;
     433                break;
     434              }
     435            nest--;
     436          }
     437        else
     438          {
     439            switch(*p)
     440              {
     441              case '\\':
     442                /* Handle continuation on to next line */
     443                if (p[1] != '\n')
     444                  break;
     445                p++;
     446                p++;
     447                *q = *p;
     448                break;
     449              case '(':
     450                /* Parenthesis only in single quote level */
     451                if (parse_level[nest] == '\'')
     452                  {
     453                    nest++;
     454                    parse_level[nest] == ')';
     455                  }
     456                break;
     457              case '"':
     458                /* Double quotes only in parenthesis */
     459                if (parse_level[nest] == ')')
     460                  {
     461                    nest++;
     462                    parse_level[nest] == '"';
     463                  }
     464                break;
     465              case '\'':
     466                /* Symbol substitution only in double quotes */
     467                if ((p[1] == '\'') && (parse_level[nest] == '"'))
     468                  {
     469                    nest++;
     470                    parse_level[nest] == '\'';
     471                    *p++ = *q++;
     472                    token->use_cmd_file = 1;
     473                    INC_TOKEN_LEN_OR_BREAK;
     474                    break;
     475                  }
     476                *q = *p;
     477              }
     478          }
     479        p++;
     480        q++;
     481        /* Pass through doubled double quotes */
     482        if ((*p == '"') && (p[1] == '"') && (parse_level[nest] == '"'))
     483        {
     484          *p++ = *q++;
     485          INC_TOKEN_LEN_OR_BREAK;
     486          *p++ = *q++;
     487          INC_TOKEN_LEN_OR_BREAK;
     488        }
     489      }
     490    *q = 0;
     491    return p;
     492  }
     493  
     494  /* Extract a $ string from the input line */
     495  static char *
     496  posix_parse_dollar (struct token_info *token)
     497  {
     498    /* $foo becomes 'foo' */
     499    char * q;
     500    char * p;
     501    q = token->text;
     502    p = token->src;
     503    token->use_cmd_file = 1;
     504  
     505    p++;
     506    *q++ = '\'';
     507    INC_TOKEN_LEN_OR_RETURN (p);
     508  
     509    while ((isalnum ((unsigned char) *p)) || (*p == '_'))
     510      {
     511        *q++ = *p++;
     512        INC_TOKEN_LEN_OR_BREAK;
     513      }
     514    *q++ = '\'';
     515    while (1)
     516      {
     517        INC_TOKEN_LEN_OR_BREAK;
     518        break;
     519      }
     520    *q = 0;
     521    return p;
     522  }
     523  
     524  const char *vms_filechars = "0123456789abcdefghijklmnopqrstuvwxyz" \
     525     "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]<>:/_-.$";
     526  
     527  /* Simple text copy */
     528  static char *
     529  parse_text (struct token_info *token, int assignment_hack)
     530  {
     531    char * q;
     532    char * p;
     533    int str_len;
     534    q = token->text;
     535    p = token->src;
     536  
     537    /* If assignment hack, then this text needs to be double quoted. */
     538    if (vms_unix_simulation && (assignment_hack == 2))
     539      {
     540        *q++ = '"';
     541        INC_TOKEN_LEN_OR_RETURN (p);
     542      }
     543  
     544    *q++ = *p++;
     545    INC_TOKEN_LEN_OR_RETURN (p);
     546  
     547    while (*p != 0)
     548      {
     549        str_len = strspn (p, vms_filechars);
     550        if (str_len == 0)
     551          {
     552            /* Pass through backslash escapes in Unix simulation
     553               probably will not work anyway.
     554               All any character after a ^ otherwise to support EFS.
     555            */
     556            if (vms_unix_simulation && (p[0] == '\\') && (p[1] != 0))
     557              str_len = 2;
     558            else if ((p[0] == '^') && (p[1] != 0))
     559              str_len = 2;
     560            else if (!vms_unix_simulation && (p[0] == ';'))
     561              str_len = 1;
     562  
     563            if (str_len == 0)
     564              {
     565                /* If assignment hack, then this needs to be double quoted. */
     566                if (vms_unix_simulation && (assignment_hack == 2))
     567                {
     568                  *q++ = '"';
     569                  INC_TOKEN_LEN_OR_RETURN (p);
     570                }
     571                *q = 0;
     572                return p;
     573              }
     574          }
     575        if (str_len > 0)
     576          {
     577            ADD_TOKEN_LEN_OR_RETURN (str_len, p);
     578            strncpy (q, p, str_len);
     579            p += str_len;
     580            q += str_len;
     581            *q = 0;
     582          }
     583      }
     584    /* If assignment hack, then this text needs to be double quoted. */
     585    if (vms_unix_simulation && (assignment_hack == 2))
     586      {
     587        *q++ = '"';
     588        INC_TOKEN_LEN_OR_RETURN (p);
     589      }
     590    return p;
     591  }
     592  
     593  /* single character copy */
     594  static char *
     595  parse_char (struct token_info *token, int count)
     596  {
     597    char * q;
     598    char * p;
     599    q = token->text;
     600    p = token->src;
     601  
     602    while (count > 0)
     603      {
     604        *q++ = *p++;
     605        INC_TOKEN_LEN_OR_RETURN (p);
     606        count--;
     607      }
     608    *q = 0;
     609    return p;
     610  }
     611  
     612  /* Build a command string from the collected tokens
     613     and process built-ins now
     614  */
     615  static struct dsc$descriptor_s *
     616  build_vms_cmd (char **cmd_tokens,
     617                 enum auto_pipe use_pipe_cmd,
     618                 int append_token)
     619  {
     620    struct dsc$descriptor_s *cmd_dsc;
     621    int cmd_tkn_index;
     622    char * cmd;
     623    int cmd_len;
     624    int semicolon_seen;
     625  
     626    cmd_tkn_index = 0;
     627    cmd_dsc = xmalloc (sizeof (struct dsc$descriptor_s));
     628  
     629    /* Empty command? */
     630    if (cmd_tokens[0] == NULL)
     631      {
     632        cmd_dsc->dsc$a_pointer = NULL;
     633        cmd_dsc->dsc$w_length = 0;
     634        return cmd_dsc;
     635      }
     636  
     637    /* Max DCL command + 1 extra token and trailing space */
     638    cmd = xmalloc (MAX_DCL_CMD_LINE_LENGTH + 256);
     639  
     640    cmd[0] = '$';
     641    cmd[1] = 0;
     642    cmd_len = 1;
     643  
     644    /* Handle real or auto-pipe */
     645    if (use_pipe_cmd == add_pipe)
     646      {
     647        /* We need to auto convert to a pipe command */
     648        strcat (cmd, "pipe ");
     649        cmd_len += 5;
     650      }
     651  
     652    semicolon_seen = 0;
     653    while (cmd_tokens[cmd_tkn_index] != NULL)
     654      {
     655  
     656        /* Check for buffer overflow */
     657        if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     658          {
     659            errno = E2BIG;
     660            break;
     661          }
     662  
     663        /* Eliminate double ';' */
     664        if (semicolon_seen && (cmd_tokens[cmd_tkn_index][0] == ';'))
     665          {
     666            semicolon_seen = 0;
     667            free (cmd_tokens[cmd_tkn_index++]);
     668            if (cmd_tokens[cmd_tkn_index] == NULL)
     669              break;
     670          }
     671  
     672        /* Special handling for CD built-in */
     673        if (strncmp (cmd_tokens[cmd_tkn_index], "builtin_cd", 11) == 0)
     674          {
     675            int result;
     676            semicolon_seen = 0;
     677            free (cmd_tokens[cmd_tkn_index]);
     678            cmd_tkn_index++;
     679            if (cmd_tokens[cmd_tkn_index] == NULL)
     680              break;
     681            DB(DB_JOBS, (_("BUILTIN CD %s\n"), cmd_tokens[cmd_tkn_index]));
     682  
     683            /* TODO: chdir fails with some valid syntaxes */
     684            result = chdir (cmd_tokens[cmd_tkn_index]);
     685            if (result != 0)
     686              {
     687                /* TODO: Handle failure better */
     688                free (cmd);
     689                while (cmd_tokens[cmd_tkn_index] == NULL)
     690                  free (cmd_tokens[cmd_tkn_index++]);
     691                cmd_dsc->dsc$w_length = -1;
     692                cmd_dsc->dsc$a_pointer = NULL;
     693                return cmd_dsc;
     694              }
     695          }
     696        else if (strncmp (cmd_tokens[cmd_tkn_index], "exit", 5) == 0)
     697          {
     698            /* Copy the exit command */
     699            semicolon_seen = 0;
     700            strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
     701            cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
     702            free (cmd_tokens[cmd_tkn_index++]);
     703            if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     704              {
     705                errno = E2BIG;
     706                break;
     707              }
     708  
     709            /* Optional whitespace */
     710            if (isspace ((unsigned char) cmd_tokens[cmd_tkn_index][0]))
     711              {
     712                strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
     713                cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
     714                free (cmd_tokens[cmd_tkn_index++]);
     715                if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     716                {
     717                  errno = E2BIG;
     718                  break;
     719                }
     720              }
     721  
     722            /* There should be a status, but it is optional */
     723            if (cmd_tokens[cmd_tkn_index][0] == ';')
     724              continue;
     725  
     726            /* If Unix simulation, add '((' */
     727            if (vms_unix_simulation)
     728              {
     729                strcpy (&cmd[cmd_len], "((");
     730                cmd_len += 2;
     731                if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     732                  {
     733                    errno = E2BIG;
     734                    break;
     735                  }
     736              }
     737  
     738            /* Add the parameter */
     739            strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
     740            cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
     741            free (cmd_tokens[cmd_tkn_index++]);
     742            if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     743              {
     744                errno = E2BIG;
     745                break;
     746              }
     747  
     748            /* Add " * 8) .and. %x7f8) .or. %x1035a002" */
     749            if (vms_unix_simulation)
     750              {
     751                const char *end_str = " * 8) .and. %x7f8) .or. %x1035a002";
     752                strcpy (&cmd[cmd_len], end_str);
     753                cmd_len += strlen (end_str);
     754                if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     755                  {
     756                    errno = E2BIG;
     757                    break;
     758                  }
     759              }
     760            continue;
     761          }
     762  
     763        /* auto pipe needs spaces before semicolon */
     764        if (use_pipe_cmd == add_pipe)
     765          if (cmd_tokens[cmd_tkn_index][0] == ';')
     766            {
     767              cmd[cmd_len++] = ' ';
     768              semicolon_seen = 1;
     769              if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
     770                {
     771                  errno = E2BIG;
     772                  break;
     773                }
     774            }
     775          else
     776            {
     777              char ch;
     778              ch = cmd_tokens[cmd_tkn_index][0];
     779              if (!(ch == ' ' || ch == '\t'))
     780                semicolon_seen = 0;
     781            }
     782  
     783        strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
     784        cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
     785  
     786        free (cmd_tokens[cmd_tkn_index++]);
     787  
     788        /* Skip the append tokens if they exist */
     789        if (cmd_tkn_index == append_token)
     790          {
     791            free (cmd_tokens[cmd_tkn_index++]);
     792            if (isspace ((unsigned char) cmd_tokens[cmd_tkn_index][0]))
     793              free (cmd_tokens[cmd_tkn_index++]);
     794            free (cmd_tokens[cmd_tkn_index++]);
     795          }
     796      }
     797  
     798    cmd[cmd_len] = 0;
     799    cmd_dsc->dsc$w_length = cmd_len;
     800    cmd_dsc->dsc$a_pointer = cmd;
     801    cmd_dsc->dsc$b_dtype = DSC$K_DTYPE_T;
     802    cmd_dsc->dsc$b_class = DSC$K_CLASS_S;
     803  
     804    return cmd_dsc;
     805  }
     806  
     807  pid_t
     808  child_execute_job (struct childbase *child, int good_stdin UNUSED, char *argv)
     809  {
     810    int i;
     811  
     812    static struct dsc$descriptor_s *cmd_dsc;
     813    static struct dsc$descriptor_s pnamedsc;
     814    int spflags = CLI$M_NOWAIT;
     815    int status;
     816    int comnamelen;
     817    char procname[100];
     818  
     819    char *p;
     820    char *cmd_tokens[(MAX_DCL_TOKENS * 2) + 1]; /* whitespace does not count */
     821    char token_str[MAX_DCL_TOKEN_LENGTH + 1];
     822    struct token_info token;
     823    int cmd_tkn_index;
     824    int paren_level = 0;
     825    enum auto_pipe use_pipe_cmd = nopipe;
     826    int append_token = -1;
     827    char *append_file = NULL;
     828    int unix_echo_cmd = 0;  /* Special handle Unix echo command */
     829    int assignment_hack = 0; /* Handle x=y command as piped command */
     830  
     831    /* Parse IO redirection.  */
     832  
     833    child->comname = NULL;
     834  
     835    DB (DB_JOBS, ("child_execute_job (%s)\n", argv));
     836  
     837    while (isspace ((unsigned char)*argv))
     838      argv++;
     839  
     840    if (*argv == 0)
     841      {
     842        /* Only a built-in or a null command - Still need to run term AST */
     843        child->cstatus = VMS_POSIX_EXIT_MASK;
     844        child->vms_launch_status = SS$_NORMAL;
     845        child->efn = 0;
     846        vmsHandleChildTerm (child);
     847        /* TODO what is this "magic number" */
     848        return 270163; /* Special built-in */
     849      }
     850  
     851    sprintf (procname, "GMAKE_%05x", getpid () & 0xfffff);
     852    pnamedsc.dsc$w_length = strlen (procname);
     853    pnamedsc.dsc$a_pointer = procname;
     854    pnamedsc.dsc$b_dtype = DSC$K_DTYPE_T;
     855    pnamedsc.dsc$b_class = DSC$K_CLASS_S;
     856  
     857    /* Old */
     858    /* Handle comments and redirection.
     859       For ONESHELL, the redirection must be on the first line. Any other
     860       redirection token is handled by DCL, that is, the pipe command with
     861       redirection can be used, but it should not be used on the first line
     862       for ONESHELL. */
     863  
     864    /* VMS parser notes:
     865       1. A token is any of DCL verbs, qualifiers, parameters, or punctuation.
     866       2. Only MAX_DCL_TOKENS per line in both one line or command file mode.
     867       3. Each token limited to MAC_DCL_TOKEN_LENGTH
     868       4. If the line to DCL is greater than MAX_DCL_LINE_LENGTH then a
     869          command file must be used.
     870       5. Currently a command file must be used symbol substitution is to
     871          be performed.
     872       6. Currently limiting command files to 2 * MAX_DCL_TOKENS.
     873  
     874       Build both a command file token list and command line token list
     875       until it is determined that the command line limits are exceeded.
     876    */
     877  
     878    cmd_tkn_index = 0;
     879    cmd_tokens[cmd_tkn_index] = NULL;
     880    p = argv;
     881  
     882    token.text = token_str;
     883    token.length = 0;
     884    token.cmd_errno = 0;
     885    token.use_cmd_file = 0;
     886  
     887    while (*p != 0)
     888      {
     889        /* We can not build this command so give up */
     890        if (token.cmd_errno != 0)
     891          break;
     892  
     893        token.src = p;
     894  
     895        switch (*p)
     896          {
     897          case '\'':
     898            if (vms_unix_simulation || unix_echo_cmd)
     899              {
     900                p = posix_parse_sq (&token);
     901                UPDATE_TOKEN;
     902                break;
     903              }
     904  
     905            /* VMS mode, the \' means that a symbol substitution is starting
     906               so while you might think you can just copy until the next
     907               \'.  Unfortunately the substitution can be a lexical function
     908               which can contain embedded strings and lexical functions.
     909               Messy.
     910            */
     911            p = vms_parse_quotes (&token);
     912            UPDATE_TOKEN;
     913            break;
     914          case '"':
     915            if (vms_unix_simulation)
     916              {
     917                p = posix_parse_dq (&token);
     918                UPDATE_TOKEN;
     919                break;
     920              }
     921  
     922            /* VMS quoted string, can contain lexical functions with
     923               quoted strings and nested lexical functions.
     924            */
     925            p = vms_parse_quotes (&token);
     926            UPDATE_TOKEN;
     927            break;
     928  
     929          case '$':
     930            if (vms_unix_simulation)
     931              {
     932                p = posix_parse_dollar (&token);
     933                UPDATE_TOKEN;
     934                break;
     935              }
     936  
     937            /* Otherwise nothing special */
     938            p = parse_text (&token, 0);
     939            UPDATE_TOKEN;
     940            break;
     941          case '\\':
     942            if (p[1] == '\n')
     943              {
     944                /* Line continuation, remove it */
     945                p += 2;
     946                break;
     947              }
     948  
     949            /* Ordinary character otherwise */
     950            if (assignment_hack != 0)
     951              assignment_hack++;
     952            if (assignment_hack > 2)
     953              {
     954                assignment_hack = 0;          /* Reset */
     955                if (use_pipe_cmd == nopipe)   /* force pipe use */
     956                  use_pipe_cmd = add_pipe;
     957                token_str[0] = ';';              /* add ; token */
     958                token_str[1] = 0;
     959                UPDATE_TOKEN;
     960              }
     961            p = parse_text (&token, assignment_hack);
     962            UPDATE_TOKEN;
     963            break;
     964          case '!':
     965          case '#':
     966            /* Unix '#' is VMS '!' which comments out the rest of the line.
     967               Historically the rest of the line has been skipped.
     968               Not quite the right thing to do, as the f$verify lexical
     969               function works in comments.  But this helps keep the line
     970               lengths short.
     971            */
     972            unix_echo_cmd = 0;
     973            while (*p != '\n' && *p != 0)
     974              p++;
     975            break;
     976          case '(':
     977            /* Subshell, equation, or lexical function argument start */
     978            p = parse_char (&token, 1);
     979            UPDATE_TOKEN;
     980            paren_level++;
     981            break;
     982          case ')':
     983            /* Close out a paren level */
     984            p = parse_char (&token, 1);
     985            UPDATE_TOKEN;
     986            paren_level--;
     987            /* TODO: Should we diagnose if paren_level goes negative? */
     988            break;
     989          case '&':
     990            if (isalpha ((unsigned char) p[1]) && !vms_unix_simulation)
     991              {
     992                /* VMS symbol substitution */
     993                p = parse_text (&token, 0);
     994                token.use_cmd_file = 1;
     995                UPDATE_TOKEN;
     996                break;
     997              }
     998            if (use_pipe_cmd == nopipe)
     999              use_pipe_cmd = add_pipe;
    1000            if (p[1] != '&')
    1001              p = parse_char (&token, 1);
    1002            else
    1003              p = parse_char (&token, 2);
    1004            UPDATE_TOKEN;
    1005            break;
    1006          case '|':
    1007            if (use_pipe_cmd == nopipe)
    1008              use_pipe_cmd = add_pipe;
    1009            if (p[1] != '|')
    1010              p = parse_char (&token, 1);
    1011            else
    1012              p = parse_char (&token, 2);
    1013            UPDATE_TOKEN;
    1014            break;
    1015          case ';':
    1016            /* Separator - convert to a pipe command. */
    1017            unix_echo_cmd = 0;
    1018          case '<':
    1019            if (use_pipe_cmd == nopipe)
    1020              use_pipe_cmd = add_pipe;
    1021            p = parse_char (&token, 1);
    1022            UPDATE_TOKEN;
    1023            break;
    1024          case '>':
    1025            if (use_pipe_cmd == nopipe)
    1026              use_pipe_cmd = add_pipe;
    1027            if (p[1] == '>')
    1028              {
    1029                /* Parsing would have been simple until support for the >>
    1030                   append redirect was added.
    1031                   Implementation needs:
    1032                   * if not exist output file create empty
    1033                   * open/append gnv$make_temp??? output_file
    1034                   * define/user sys$output gnv$make_temp???
    1035                   ** And all this done before the command previously tokenized.
    1036                   * command previously tokenized
    1037                   * close gnv$make_temp???
    1038                */
    1039                p = parse_char (&token, 2);
    1040                append_token = cmd_tkn_index;
    1041                token.use_cmd_file = 1;
    1042              }
    1043            else
    1044              p = parse_char (&token, 1);
    1045            UPDATE_TOKEN;
    1046            break;
    1047          case '/':
    1048            /* Unix path or VMS option start, read until non-path symbol */
    1049            if (assignment_hack != 0)
    1050              assignment_hack++;
    1051            if (assignment_hack > 2)
    1052              {
    1053                assignment_hack = 0;          /* Reset */
    1054                if (use_pipe_cmd == nopipe)   /* force pipe use */
    1055                  use_pipe_cmd = add_pipe;
    1056                token_str[0] = ';';              /* add ; token */
    1057                token_str[1] = 0;
    1058                UPDATE_TOKEN;
    1059              }
    1060            p = parse_text (&token, assignment_hack);
    1061            UPDATE_TOKEN;
    1062            break;
    1063          case ':':
    1064            if ((p[1] == 0) || isspace ((unsigned char) p[1]))
    1065              {
    1066                /* Unix Null command - treat as comment until next command */
    1067                unix_echo_cmd = 0;
    1068                p++;
    1069                while (*p != 0)
    1070                  {
    1071                    if (*p == ';')
    1072                      {
    1073                        /* Remove Null command from pipeline */
    1074                        p++;
    1075                        break;
    1076                      }
    1077                    p++;
    1078                  }
    1079                break;
    1080              }
    1081  
    1082            /* String assignment */
    1083            /* := :== or : */
    1084            if (p[1] != '=')
    1085              p = parse_char (&token, 1);
    1086            else if (p[2] != '=')
    1087              p = parse_char (&token, 2);
    1088            else
    1089              p = parse_char (&token, 3);
    1090            UPDATE_TOKEN;
    1091            break;
    1092          case '=':
    1093            /* = or == */
    1094            /* If this is not an echo statement, this could be a shell
    1095               assignment.  VMS requires the target to be quoted if it
    1096               is not a macro substitution */
    1097            if (!unix_echo_cmd && vms_unix_simulation && (assignment_hack == 0))
    1098              assignment_hack = 1;
    1099            if (p[1] != '=')
    1100              p = parse_char (&token, 1);
    1101            else
    1102              p = parse_char (&token, 2);
    1103            UPDATE_TOKEN;
    1104            break;
    1105          case '+':
    1106          case '-':
    1107          case '*':
    1108            p = parse_char (&token, 1);
    1109            UPDATE_TOKEN;
    1110            break;
    1111          case '.':
    1112            /* .xxx. operation, VMS does not require the trailing . */
    1113            p = parse_text (&token, 0);
    1114            UPDATE_TOKEN;
    1115            break;
    1116          default:
    1117            /* Skip repetitive whitespace */
    1118            if (isspace ((unsigned char) *p))
    1119              {
    1120                p = parse_char (&token, 1);
    1121  
    1122                /* Force to a space or a tab */
    1123                if ((token_str[0] != ' ') ||
    1124                    (token_str[0] != '\t'))
    1125                  token_str[0] = ' ';
    1126                UPDATE_TOKEN;
    1127  
    1128                while (isspace ((unsigned char) *p))
    1129                  p++;
    1130                if (assignment_hack != 0)
    1131                  assignment_hack++;
    1132                break;
    1133              }
    1134  
    1135            if (assignment_hack != 0)
    1136              assignment_hack++;
    1137            if (assignment_hack > 2)
    1138              {
    1139                assignment_hack = 0;          /* Reset */
    1140                if (use_pipe_cmd == nopipe)   /* force pipe use */
    1141                  use_pipe_cmd = add_pipe;
    1142                token_str[0] = ';';              /* add ; token */
    1143                token_str[1] = 0;
    1144                UPDATE_TOKEN;
    1145              }
    1146            p = parse_text (&token, assignment_hack);
    1147            if (strncasecmp (token.text, "echo", 4) == 0)
    1148              unix_echo_cmd = 1;
    1149            else if (strncasecmp (token.text, "pipe", 4) == 0)
    1150              use_pipe_cmd = dcl_pipe;
    1151            UPDATE_TOKEN;
    1152            break;
    1153          }
    1154      }
    1155  
    1156    /* End up here with a list of tokens to build a command line.
    1157       Deal with errors detected during parsing.
    1158     */
    1159    if (token.cmd_errno != 0)
    1160      {
    1161        while (cmd_tokens[cmd_tkn_index] == NULL)
    1162          free (cmd_tokens[cmd_tkn_index++]);
    1163        child->cstatus = VMS_POSIX_EXIT_MASK | (MAKE_TROUBLE << 3);
    1164        child->vms_launch_status = SS$_ABORT;
    1165        child->efn = 0;
    1166        errno = token.cmd_errno;
    1167        return -1;
    1168      }
    1169  
    1170    /* Save any redirection to append file */
    1171    if (append_token != -1)
    1172      {
    1173        int file_token;
    1174        char * lastdot;
    1175        char * lastdir;
    1176        char * raw_append_file;
    1177        file_token = append_token;
    1178        file_token++;
    1179        if (isspace ((unsigned char) cmd_tokens[file_token][0]))
    1180          file_token++;
    1181        raw_append_file = vmsify (cmd_tokens[file_token], 0);
    1182        /* VMS DCL needs a trailing dot if null file extension */
    1183        lastdot = strrchr(raw_append_file, '.');
    1184        lastdir = strrchr(raw_append_file, ']');
    1185        if (lastdir == NULL)
    1186          lastdir = strrchr(raw_append_file, '>');
    1187        if (lastdir == NULL)
    1188          lastdir = strrchr(raw_append_file, ':');
    1189        if ((lastdot == NULL) || (lastdot > lastdir))
    1190          {
    1191            append_file = xmalloc (strlen (raw_append_file) + 1);
    1192            strcpy (append_file, raw_append_file);
    1193            strcat (append_file, ".");
    1194          }
    1195        else
    1196          append_file = strdup(raw_append_file);
    1197      }
    1198  
    1199    cmd_dsc = build_vms_cmd (cmd_tokens, use_pipe_cmd, append_token);
    1200    if (cmd_dsc->dsc$a_pointer == NULL)
    1201      {
    1202        if (cmd_dsc->dsc$w_length < 0)
    1203          {
    1204            free (cmd_dsc);
    1205            child->cstatus = VMS_POSIX_EXIT_MASK | (MAKE_TROUBLE << 3);
    1206            child->vms_launch_status = SS$_ABORT;
    1207            child->efn = 0;
    1208            return -1;
    1209          }
    1210  
    1211        /* Only a built-in or a null command - Still need to run term AST */
    1212        free (cmd_dsc);
    1213        child->cstatus = VMS_POSIX_EXIT_MASK;
    1214        child->vms_launch_status = SS$_NORMAL;
    1215        child->efn = 0;
    1216        vmsHandleChildTerm (child);
    1217        /* TODO what is this "magic number" */
    1218        return 270163; /* Special built-in */
    1219      }
    1220  
    1221    if (cmd_dsc->dsc$w_length > MAX_DCL_LINE_LENGTH)
    1222      token.use_cmd_file = 1;
    1223  
    1224    DB(DB_JOBS, (_("DCL: %s\n"), cmd_dsc->dsc$a_pointer));
    1225  
    1226    /* Enforce the creation of a command file if "vms_always_use_cmd_file" is
    1227       non-zero.
    1228       Further, this way DCL reads the input stream and therefore does
    1229       'forced' symbol substitution, which it doesn't do for one-liners when
    1230       they are 'lib$spawn'ed.
    1231  
    1232       Otherwise the behavior is:
    1233  
    1234       Create a *.com file if either the command is too long for
    1235       lib$spawn, or if a redirect appending to a file is desired, or
    1236       symbol substitition.
    1237    */
    1238  
    1239    if (vms_always_use_cmd_file || token.use_cmd_file)
    1240      {
    1241        FILE *outfile;
    1242        int cmd_len;
    1243  
    1244        outfile = get_tmpfile (&child->comname);
    1245  
    1246        comnamelen = strlen (child->comname);
    1247  
    1248        /* The whole DCL "script" is executed as one action, and it behaves as
    1249           any DCL "script", that is errors stop it but warnings do not. Usually
    1250           the command on the last line, defines the exit code.  However, with
    1251           redirections there is a prolog and possibly an epilog to implement
    1252           the redirection.  Both are part of the script which is actually
    1253           executed. So if the redirection encounters an error in the prolog,
    1254           the user actions will not run; if in the epilog, the user actions
    1255           ran, but output is not captured. In both error cases, the error of
    1256           redirection is passed back and not the exit code of the actions. The
    1257           user should be able to enable DCL "script" verification with "set
    1258           verify". However, the prolog and epilog commands are not shown. Also,
    1259           if output redirection is used, the verification output is redirected
    1260           into that file as well. */
    1261        fprintf (outfile, "$ gnv$$make_verify = \"''f$verify(0)'\"\n");
    1262        fprintf (outfile, "$ gnv$$make_pid = f$getjpi(\"\",\"pid\")\n");
    1263        fprintf (outfile, "$ on error then $ goto gnv$$make_error\n");
    1264  
    1265        /* Handle append redirection */
    1266        if (append_file != NULL)
    1267          {
    1268            /* If file does not exist, create it */
    1269            fprintf (outfile,
    1270                     "$ gnv$$make_al = \"gnv$$make_append''gnv$$make_pid'\"\n");
    1271            fprintf (outfile,
    1272                     "$ if f$search(\"%s\") .eqs. \"\" then create %s\n",
    1273                     append_file, append_file);
    1274  
    1275            fprintf (outfile,
    1276                     "$ open/append 'gnv$$make_al' %s\n", append_file);
    1277  
    1278            /* define sys$output to that file */
    1279            fprintf (outfile,
    1280                     "$ define/user sys$output 'gnv$$make_al'\n");
    1281            DB (DB_JOBS, (_("Append output to %s\n"), append_file));
    1282            free(append_file);
    1283          }
    1284  
    1285        fprintf (outfile, "$ gnv$$make_verify = f$verify(gnv$$make_verify)\n");
    1286  
    1287        /* TODO:
    1288           Only for ONESHELL there will be several commands separated by
    1289           '\n'. But there can always be multiple continuation lines.
    1290        */
    1291  
    1292        fprintf (outfile, "%s\n", cmd_dsc->dsc$a_pointer);
    1293        fprintf (outfile, "$ gnv$$make_status_2 = $status\n");
    1294        fprintf (outfile, "$ goto gnv$$make_exit\n");
    1295  
    1296        /* Exit and clean up */
    1297        fprintf (outfile, "$ gnv$$make_error: ! 'f$verify(0)\n");
    1298        fprintf (outfile, "$ gnv$$make_status_2 = $status\n");
    1299  
    1300        if (append_token != -1)
    1301          {
    1302            fprintf (outfile, "$ deassign sys$output\n");
    1303            fprintf (outfile, "$ close 'gnv$$make_al'\n");
    1304  
    1305            DB (DB_JOBS,
    1306                (_("Append %.*s and cleanup\n"), comnamelen-3, child->comname));
    1307          }
    1308        fprintf (outfile, "$ gnv$$make_exit: ! 'f$verify(0)\n");
    1309        fprintf (outfile,
    1310               "$ exit 'gnv$$make_status_2' + (0*f$verify(gnv$$make_verify))\n");
    1311  
    1312        fclose (outfile);
    1313  
    1314        free (cmd_dsc->dsc$a_pointer);
    1315        cmd_dsc->dsc$a_pointer = xmalloc (256 + 4);
    1316        sprintf (cmd_dsc->dsc$a_pointer, "$ @%s", child->comname);
    1317        cmd_dsc->dsc$w_length = strlen (cmd_dsc->dsc$a_pointer);
    1318  
    1319        DB (DB_JOBS, (_("Executing %s instead\n"), child->comname));
    1320      }
    1321  
    1322    child->efn = 0;
    1323    while (child->efn < 32 || child->efn > 63)
    1324      {
    1325        status = LIB$GET_EF ((unsigned long *)&child->efn);
    1326        if (!$VMS_STATUS_SUCCESS (status))
    1327          {
    1328            if (child->comname)
    1329              {
    1330                if (!ISDB (DB_JOBS))
    1331                  unlink (child->comname);
    1332                free (child->comname);
    1333              }
    1334            return -1;
    1335          }
    1336      }
    1337  
    1338    SYS$CLREF (child->efn);
    1339  
    1340    vms_jobsefnmask |= (1 << (child->efn - 32));
    1341  
    1342    /* Export the child environment into DCL symbols */
    1343    if (child->environment != 0)
    1344      {
    1345        char **ep = child->environment;
    1346        while (*ep != 0)
    1347          {
    1348            vms_putenv_symbol (*ep);
    1349            *ep++;
    1350          }
    1351      }
    1352  
    1353    /*
    1354      LIB$SPAWN  [command-string]
    1355      [,input-file]
    1356      [,output-file]
    1357      [,flags]
    1358      [,process-name]
    1359      [,process-id] [,completion-status-address] [,byte-integer-event-flag-num]
    1360      [,AST-address] [,varying-AST-argument]
    1361      [,prompt-string] [,cli] [,table]
    1362    */
    1363  
    1364  #ifndef DONTWAITFORCHILD
    1365    /*
    1366     * Code to make ctrl+c and ctrl+y working.
    1367     * The problem starts with the synchronous case where after lib$spawn is
    1368     * called any input will go to the child. But with input re-directed,
    1369     * both control characters won't make it to any of the programs, neither
    1370     * the spawning nor to the spawned one. Hence the caller needs to spawn
    1371     * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr
    1372     * has to follow to simulate the wanted synchronous behaviour.
    1373     * The next problem is ctrl+y which isn't caught by the crtl and
    1374     * therefore isn't converted to SIGQUIT (for a signal handler which is
    1375     * already established). The only way to catch ctrl+y, is an AST
    1376     * assigned to the input channel. But ctrl+y handling of DCL needs to be
    1377     * disabled, otherwise it will handle it. Not to mention the previous
    1378     * ctrl+y handling of DCL needs to be re-established before make exits.
    1379     * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will
    1380     * make it to the signal handler after the child "normally" terminates.
    1381     * This isn't enough. It seems reasonable for simple command lines like
    1382     * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for
    1383     * spawning make. Therefore we need to abort the process in the AST.
    1384     *
    1385     * Prior to the spawn it is checked if an AST is already set up for
    1386     * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general
    1387     * this will work except if make is run in a batch environment, but there
    1388     * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y
    1389     * is disabled and an exit handler is established to re-enable it.
    1390     * If the user interrupts with ctrl+y, the assigned AST will fire, force
    1391     * an abort to the subprocess and signal SIGQUIT, which will be caught by
    1392     * the already established handler and will bring us back to common code.
    1393     * After the spawn (now /nowait) a sys$waitfr simulates the /wait and
    1394     * enables the ctrl+y be delivered to this code. And the ctrl+c too,
    1395     * which the crtl converts to SIGINT and which is caught by the common
    1396     * signal handler. Because signals were blocked before entering this code
    1397     * sys$waitfr will always complete and the SIGQUIT will be processed after
    1398     * it (after termination of the current block, somewhere in common code).
    1399     * And SIGINT too will be delayed. That is ctrl+c can only abort when the
    1400     * current command completes. Anyway it's better than nothing :-)
    1401     */
    1402  
    1403    if (!setupYAstTried)
    1404      tryToSetupYAst();
    1405    child->vms_launch_status = lib$spawn (cmd_dsc,               /* cmd-string */
    1406                       NULL, /* input-file */
    1407                       NULL, /* output-file */
    1408                       &spflags,                                 /* flags */
    1409                       &pnamedsc,                                /* proc name */
    1410                       &child->pid, &child->cstatus, &child->efn,
    1411                       0, 0,
    1412                       0, 0, 0);
    1413  
    1414    status = child->vms_launch_status;
    1415    if ($VMS_STATUS_SUCCESS (status))
    1416      {
    1417        status = sys$waitfr (child->efn);
    1418        vmsHandleChildTerm (child);
    1419      }
    1420  #else
    1421    child->vms_launch_status = lib$spawn (cmd_dsc,
    1422                        NULL,
    1423                        NULL,
    1424                        &spflags,
    1425                        &pnamedsc,
    1426                        &child->pid, &child->cstatus, &child->efn,
    1427                        vmsHandleChildTerm, child,
    1428                        0, 0, 0);
    1429     status = child->vms_launch_status;
    1430  #endif
    1431  
    1432    /* Free the pointer if not a command file */
    1433    if (!vms_always_use_cmd_file && !token.use_cmd_file)
    1434      free (cmd_dsc->dsc$a_pointer);
    1435    free (cmd_dsc);
    1436  
    1437    if (!$VMS_STATUS_SUCCESS (status))
    1438      {
    1439        switch (status)
    1440          {
    1441          case SS$_EXQUOTA:
    1442            errno = EPROCLIM;
    1443            break;
    1444          default:
    1445            errno = EFAIL;
    1446          }
    1447      }
    1448  
    1449    /* Restore the VMS symbols that were changed */
    1450    if (child->environment != 0)
    1451      {
    1452        char **ep = child->environment;
    1453        while (*ep != 0)
    1454          {
    1455            vms_restore_symbol (*ep);
    1456            *ep++;
    1457          }
    1458      }
    1459  
    1460    /* TODO what is this "magic number" */
    1461    return (status & 1) ? 270163 : -1 ;
    1462  }