Ignore:
Timestamp:
Mar 14, 2018, 10:28:10 PM (7 years ago)
Author:
bird
Message:

kmk: Merged in changes from GNU make 4.2.1 (2e55f5e4abdc0e38c1d64be703b446695e70b3b6 / https://git.savannah.gnu.org/git/make.git).

Location:
trunk/src/kmk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk

  • trunk/src/kmk/vmsjobs.c

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