1 /* --------------- Moved here from job.c ---------------
2 This file must be #included in job.c, as it accesses static functions.
4 Copyright (C) 1996-2016 Free Software Foundation, Inc.
5 This file is part of GNU Make.
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
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.
16 You should have received a copy of the GNU General Public License along with
17 this program. If not, see <http://www.gnu.org/licenses/>. */
23 /* TODO - VMS specific header file conditionally included in makeint.h */
28 decc$exit (int status);
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 */
34 #ifndef MAX_EXPECTED_EXIT_CODE
35 # define MAX_EXPECTED_EXIT_CODE 7
39 #if __CRTL_VER >= 70302000 && !defined(__VAX)
40 # define MAX_DCL_LINE_LENGTH 4095
41 # define MAX_DCL_CMD_LINE_LENGTH 8192
43 # define MAX_DCL_LINE_LENGTH 255
44 # define MAX_DCL_CMD_LINE_LENGTH 1024
46 #define MAX_DCL_TOKEN_LENGTH 255
47 #define MAX_DCL_TOKENS 127
49 enum auto_pipe { nopipe, add_pipe, dcl_pipe };
51 char *vmsify (char *name, int type);
53 static int vms_jobsefnmask = 0;
55 /* returns whether path is assumed to be a unix like shell. */
57 _is_unixy_shell (const char *path)
62 #define VMS_GETMSG_MAX 256
63 static char vms_strsignal_text[VMS_GETMSG_MAX + 2];
66 vms_strsignal (int status)
68 if (status <= MAX_EXPECTED_EXIT_CODE)
69 sprintf (vms_strsignal_text, "lib$spawn returned %x", status);
73 unsigned short * msg_len;
75 vms_status = SYS$GETMSG (status, &msg_len,
76 vms_strsignal_text, 7, *out);
79 return vms_strsignal_text;
83 /* Wait for nchildren children to terminate */
85 vmsWaitForChildren (int *status)
95 *status = sys$wflor (32, vms_jobsefnmask);
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.
107 vmsHandleChildTerm (struct child *child)
110 register struct child *lastc, *c;
113 /* The child efn is 0 when a built-in or null command is executed
114 successfully with out actually creating a child.
118 vms_jobsefnmask &= ~(1 << (child->efn - 32));
120 lib$free_ef (&child->efn);
124 if (!ISDB (DB_JOBS) && !ctrlYPressed)
125 unlink (child->comname);
126 free (child->comname);
129 (void) sigblock (fatal_signal_mask);
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)
134 exit_code = (child->cstatus >> 3) & 255;
135 if (exit_code != MAKE_SUCCESS)
140 child_failed = !$VMS_STATUS_SUCCESS (child->cstatus);
142 exit_code = child->cstatus;
145 /* Search for a child matching the deceased one. */
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)
155 if ($VMS_STATUS_SUCCESS (child->vms_launch_status))
157 /* Convert VMS success status to 0 for UNIX code to be happy */
158 child->vms_launch_status = 0;
161 /* Set the state flag to say the commands have finished. */
162 c->file->command_state = cs_finished;
163 notice_finished_file (c->file);
165 (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask));
171 Spawn a process executing the command in ARGV and return its pid. */
173 /* local helpers to make ctrl+c and ctrl+y working, see below */
175 #include <libclidef.h>
178 static int ctrlMask= LIB$M_CLI_CTRLY;
179 static int oldCtrlMask;
180 static int setupYAstTried= 0;
181 static unsigned short int chan= 0;
186 lib$enable_ctrl (&oldCtrlMask,0);
193 for (c = children; c != 0; c = c->next)
194 sys$delprc (&c->pid, 0, 0);
196 kill (getpid(),SIGQUIT);
203 $DESCRIPTOR(inputDsc,"SYS$COMMAND");
206 short int status, count;
209 unsigned short int loc_chan;
217 status= sys$assign(&inputDsc,&loc_chan,0,0);
218 if (!(status&SS$_NORMAL))
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)
228 if (status!=SS$_NORMAL)
231 sys$dassgn(loc_chan);
232 if (status!=SS$_ILLIOFUNC && status!=SS$_NOPRIV)
237 /* called from AST handler ? */
238 if (setupYAstTried>1)
240 if (atexit(reEnableAst))
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))
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; }}
258 #define INC_TOKEN_LEN_OR_BREAK {token->length++; \
259 if (token->length >= MAX_DCL_TOKEN_LENGTH) \
260 { token->cmd_errno = ERANGE; break; }}
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; }}
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; } \
273 #define UPDATE_TOKEN {cmd_tokens[cmd_tkn_index] = strdup(token.text); \
276 #define EOS_ERROR(x) { if (*x == 0) { token->cmd_errno = ERANGE; break; }}
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 */
288 /* Extract a Posix single quoted string from input line */
290 posix_parse_sq (struct token_info *token)
292 /* A Posix quoted string with no expansion unless in a string
293 Unix simulation means no lexical functions present.
302 INC_TOKEN_LEN_OR_RETURN (p);
304 while (*p != '\'' && (token->length < MAX_DCL_TOKEN_LENGTH))
309 /* Embedded double quotes need to be doubled */
311 INC_TOKEN_LEN_OR_BREAK;
319 INC_TOKEN_LEN_OR_BREAK;
323 INC_TOKEN_LEN_OR_RETURN (p);
328 /* Extract a Posix double quoted string from input line */
330 posix_parse_dq (struct token_info *token)
332 /* Unix mode: Any imbedded \" becomes doubled.
333 \t is tab, \\, \$ leading character stripped.
334 $ character replaced with \' unless escaped.
341 INC_TOKEN_LEN_OR_RETURN (p);
348 case 't': /* Convert tabs */
352 case '\\': /* Just remove leading backslash */
361 INC_TOKEN_LEN_OR_BREAK;
362 default: /* Pass through unchanged */
364 INC_TOKEN_LEN_OR_BREAK;
366 INC_TOKEN_LEN_OR_BREAK;
368 else if (*p == '$' && isalpha (p[1]))
370 /* A symbol we should be able to substitute */
372 INC_TOKEN_LEN_OR_BREAK;
374 INC_TOKEN_LEN_OR_BREAK;
375 token->use_cmd_file = 1;
380 INC_TOKEN_LEN_OR_BREAK;
395 /* Extract a VMS quoted string or substitution string from input line */
397 vms_parse_quotes (struct token_info *token)
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.
409 int parse_level[MAX_DCL_TOKENS + 1];
413 if (parse_level[0] == '\'')
414 token->use_cmd_file = 1;
417 INC_TOKEN_LEN_OR_RETURN (p);
420 /* Copy everything until after the next single quote at nest == 0 */
421 while (token->length < MAX_DCL_TOKEN_LENGTH)
425 INC_TOKEN_LEN_OR_BREAK;
426 if ((*p == parse_level[nest]) && (p[1] != '"'))
440 /* Handle continuation on to next line */
448 /* Parenthesis only in single quote level */
449 if (parse_level[nest] == '\'')
452 parse_level[nest] == ')';
456 /* Double quotes only in parenthesis */
457 if (parse_level[nest] == ')')
460 parse_level[nest] == '"';
464 /* Symbol substitution ony in double quotes */
465 if ((p[1] == '\'') && (parse_level[nest] == '"'))
468 parse_level[nest] == '\'';
470 token->use_cmd_file = 1;
471 INC_TOKEN_LEN_OR_BREAK;
479 /* Pass through doubled double quotes */
480 if ((*p == '"') && (p[1] == '"') && (parse_level[nest] == '"'))
483 INC_TOKEN_LEN_OR_BREAK;
485 INC_TOKEN_LEN_OR_BREAK;
492 /* Extract a $ string from the input line */
494 posix_parse_dollar (struct token_info *token)
496 /* $foo becomes 'foo' */
501 token->use_cmd_file = 1;
505 INC_TOKEN_LEN_OR_RETURN (p);
507 while ((isalnum (*p)) || (*p == '_'))
510 INC_TOKEN_LEN_OR_BREAK;
515 INC_TOKEN_LEN_OR_BREAK;
522 const char *vms_filechars = "0123456789abcdefghijklmnopqrstuvwxyz" \
523 "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]<>:/_-.$";
525 /* Simple text copy */
527 parse_text (struct token_info *token, int assignment_hack)
535 /* If assignment hack, then this text needs to be double quoted. */
536 if (vms_unix_simulation && (assignment_hack == 2))
539 INC_TOKEN_LEN_OR_RETURN (p);
543 INC_TOKEN_LEN_OR_RETURN (p);
547 str_len = strspn (p, vms_filechars);
550 /* Pass through backslash escapes in Unix simulation
551 probably will not work anyway.
552 All any character after a ^ otherwise to support EFS.
554 if (vms_unix_simulation && (p[0] == '\\') && (p[1] != 0))
556 else if ((p[0] == '^') && (p[1] != 0))
558 else if (!vms_unix_simulation && (p[0] == ';'))
563 /* If assignment hack, then this needs to be double quoted. */
564 if (vms_unix_simulation && (assignment_hack == 2))
567 INC_TOKEN_LEN_OR_RETURN (p);
575 ADD_TOKEN_LEN_OR_RETURN (str_len, p);
576 strncpy (q, p, str_len);
582 /* If assignment hack, then this text needs to be double quoted. */
583 if (vms_unix_simulation && (assignment_hack == 2))
586 INC_TOKEN_LEN_OR_RETURN (p);
591 /* single character copy */
593 parse_char (struct token_info *token, int count)
603 INC_TOKEN_LEN_OR_RETURN (p);
610 /* Build a command string from the collected tokens
611 and process built-ins now
613 static struct dsc$descriptor_s *
614 build_vms_cmd (char **cmd_tokens,
615 enum auto_pipe use_pipe_cmd,
618 struct dsc$descriptor_s *cmd_dsc;
625 cmd_dsc = xmalloc (sizeof (struct dsc$descriptor_s));
628 if (cmd_tokens[0] == NULL)
630 cmd_dsc->dsc$a_pointer = NULL;
631 cmd_dsc->dsc$w_length = 0;
635 /* Max DCL command + 1 extra token and trailing space */
636 cmd = xmalloc (MAX_DCL_CMD_LINE_LENGTH + 256);
642 /* Handle real or auto-pipe */
643 if (use_pipe_cmd == add_pipe)
645 /* We need to auto convert to a pipe command */
646 strcat (cmd, "pipe ");
651 while (cmd_tokens[cmd_tkn_index] != NULL)
654 /* Check for buffer overflow */
655 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
661 /* Eliminate double ';' */
662 if (semicolon_seen && (cmd_tokens[cmd_tkn_index][0] == ';'))
665 free (cmd_tokens[cmd_tkn_index++]);
666 if (cmd_tokens[cmd_tkn_index] == NULL)
670 /* Special handling for CD built-in */
671 if (strncmp (cmd_tokens[cmd_tkn_index], "builtin_cd", 11) == 0)
675 free (cmd_tokens[cmd_tkn_index]);
677 if (cmd_tokens[cmd_tkn_index] == NULL)
679 DB(DB_JOBS, (_("BUILTIN CD %s\n"), cmd_tokens[cmd_tkn_index]));
681 /* TODO: chdir fails with some valid syntaxes */
682 result = chdir (cmd_tokens[cmd_tkn_index]);
685 /* TODO: Handle failure better */
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;
694 else if (strncmp (cmd_tokens[cmd_tkn_index], "exit", 5) == 0)
696 /* Copy the exit command */
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)
707 /* Optional whitespace */
708 if (isspace (cmd_tokens[cmd_tkn_index][0]))
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)
720 /* There should be a status, but it is optional */
721 if (cmd_tokens[cmd_tkn_index][0] == ';')
724 /* If Unix simulation, add '((' */
725 if (vms_unix_simulation)
727 strcpy (&cmd[cmd_len], "((");
729 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
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)
746 /* Add " * 8) .and. %x7f8) .or. %x1035a002" */
747 if (vms_unix_simulation)
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)
761 /* auto pipe needs spaces before semicolon */
762 if (use_pipe_cmd == add_pipe)
763 if (cmd_tokens[cmd_tkn_index][0] == ';')
765 cmd[cmd_len++] = ' ';
767 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
776 ch = cmd_tokens[cmd_tkn_index][0];
777 if (!(ch == ' ' || ch == '\t'))
781 strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
782 cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
784 free (cmd_tokens[cmd_tkn_index++]);
786 /* Skip the append tokens if they exist */
787 if (cmd_tkn_index == append_token)
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++]);
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;
806 child_execute_job (struct child *child, char *argv)
810 static struct dsc$descriptor_s *cmd_dsc;
811 static struct dsc$descriptor_s pnamedsc;
812 int spflags = CLI$M_NOWAIT;
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;
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 */
829 /* Parse IO redirection. */
831 child->comname = NULL;
833 DB (DB_JOBS, ("child_execute_job (%s)\n", argv));
835 while (isspace ((unsigned char)*argv))
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 */
846 vmsHandleChildTerm (child);
850 sprintf (procname, "GMAKE_%05x", getpid () & 0xfffff);
851 pnamedsc.dsc$w_length = strlen (procname);
852 pnamedsc.dsc$a_pointer = procname;
853 pnamedsc.dsc$b_dtype = DSC$K_DTYPE_T;
854 pnamedsc.dsc$b_class = DSC$K_CLASS_S;
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
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
871 6. Currently limiting command files to 2 * MAX_DCL_TOKENS.
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.
878 cmd_tokens[cmd_tkn_index] = NULL;
881 token.text = token_str;
884 token.use_cmd_file = 0;
888 /* We can not build this command so give up */
889 if (token.cmd_errno != 0)
897 if (vms_unix_simulation || unix_echo_cmd)
899 p = posix_parse_sq (&token);
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.
910 p = vms_parse_quotes (&token);
914 if (vms_unix_simulation)
916 p = posix_parse_dq (&token);
921 /* VMS quoted string, can contain lexical functions with
922 quoted strings and nested lexical functions.
924 p = vms_parse_quotes (&token);
929 if (vms_unix_simulation)
931 p = posix_parse_dollar (&token);
936 /* Otherwise nothing special */
937 p = parse_text (&token, 0);
943 /* Line continuation, remove it */
948 /* Ordinary character otherwise */
949 if (assignment_hack != 0)
951 if (assignment_hack > 2)
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 */
960 p = parse_text (&token, assignment_hack);
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
972 while (*p != '\n' && *p != 0)
976 /* Subshell, equation, or lexical function argument start */
977 p = parse_char (&token, 1);
982 /* Close out a paren level */
983 p = parse_char (&token, 1);
986 /* TODO: Should we diagnose if paren_level goes negative? */
989 if (isalpha (p[1]) && !vms_unix_simulation)
991 /* VMS symbol substitution */
992 p = parse_text (&token, 0);
993 token.use_cmd_file = 1;
997 if (use_pipe_cmd == nopipe)
998 use_pipe_cmd = add_pipe;
1000 p = parse_char (&token, 1);
1002 p = parse_char (&token, 2);
1006 if (use_pipe_cmd == nopipe)
1007 use_pipe_cmd = add_pipe;
1009 p = parse_char (&token, 1);
1011 p = parse_char (&token, 2);
1015 /* Separator - convert to a pipe command. */
1018 if (use_pipe_cmd == nopipe)
1019 use_pipe_cmd = add_pipe;
1020 p = parse_char (&token, 1);
1024 if (use_pipe_cmd == nopipe)
1025 use_pipe_cmd = add_pipe;
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???
1038 p = parse_char (&token, 2);
1039 append_token = cmd_tkn_index;
1040 token.use_cmd_file = 1;
1043 p = parse_char (&token, 1);
1047 /* Unix path or VMS option start, read until non-path symbol */
1048 if (assignment_hack != 0)
1050 if (assignment_hack > 2)
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 */
1059 p = parse_text (&token, assignment_hack);
1063 if ((p[1] == 0) || isspace (p[1]))
1065 /* Unix Null command - treat as comment until next command */
1072 /* Remove Null command from pipeline */
1081 /* String assignment */
1084 p = parse_char (&token, 1);
1085 else if (p[2] != '=')
1086 p = parse_char (&token, 2);
1088 p = parse_char (&token, 3);
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;
1099 p = parse_char (&token, 1);
1101 p = parse_char (&token, 2);
1107 p = parse_char (&token, 1);
1111 /* .xxx. operation, VMS does not require the trailing . */
1112 p = parse_text (&token, 0);
1116 /* Skip repetitive whitespace */
1119 p = parse_char (&token, 1);
1121 /* Force to a space or a tab */
1122 if ((token_str[0] != ' ') ||
1123 (token_str[0] != '\t'))
1127 while (isspace (*p))
1129 if (assignment_hack != 0)
1134 if (assignment_hack != 0)
1136 if (assignment_hack > 2)
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 */
1145 p = parse_text (&token, assignment_hack);
1146 if (strncasecmp (token.text, "echo", 4) == 0)
1148 else if (strncasecmp (token.text, "pipe", 4) == 0)
1149 use_pipe_cmd = dcl_pipe;
1155 /* End up here with a list of tokens to build a command line.
1156 Deal with errors detected during parsing.
1158 if (token.cmd_errno != 0)
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 */
1167 errno = token.cmd_errno;
1171 /* Save any redirection to append file */
1172 if (append_token != -1)
1177 char * raw_append_file;
1178 file_token = append_token;
1180 if (isspace (cmd_tokens[file_token][0]))
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))
1192 append_file = xmalloc (strlen (raw_append_file) + 1);
1193 strcpy (append_file, raw_append_file);
1194 strcat (append_file, ".");
1197 append_file = strdup(raw_append_file);
1200 cmd_dsc = build_vms_cmd (cmd_tokens, use_pipe_cmd, append_token);
1201 if (cmd_dsc->dsc$a_pointer == NULL)
1203 if (cmd_dsc->dsc$w_length < 0)
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 */
1214 /* Only a built-in or a null command - Still need to run term AST */
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 */
1221 vmsHandleChildTerm (child);
1225 if (cmd_dsc->dsc$w_length > MAX_DCL_LINE_LENGTH)
1226 token.use_cmd_file = 1;
1228 DB(DB_JOBS, (_("DCL: %s\n"), cmd_dsc->dsc$a_pointer));
1230 /* Enforce the creation of a command file if "vms_always_use_cmd_file" is
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.
1236 Otherwise the behavior is:
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.
1243 if (vms_always_use_cmd_file || token.use_cmd_file)
1248 outfile = output_tmpfile (&child->comname,
1249 "sys$scratch:gnv$make_cmdXXXXXX.com");
1250 /* 012345678901234567890 */
1252 pfatal_with_name (_("fopen (temporary file)"));
1253 comnamelen = strlen (child->comname);
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");
1272 /* Handle append redirection */
1273 if (append_file != NULL)
1275 /* If file does not exist, create it */
1277 "$ gnv$$make_al = \"gnv$$make_append''gnv$$make_pid'\"\n");
1279 "$ if f$search(\"%s\") .eqs. \"\" then create %s\n",
1280 append_file, append_file);
1283 "$ open/append 'gnv$$make_al' %s\n", append_file);
1285 /* define sys$output to that file */
1287 "$ define/user sys$output 'gnv$$make_al'\n");
1288 DB (DB_JOBS, (_("Append output to %s\n"), append_file));
1292 fprintf (outfile, "$ gnv$$make_verify = f$verify(gnv$$make_verify)\n");
1295 Only for ONESHELL there will be several commands separated by
1296 '\n'. But there can always be multiple continuation lines.
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");
1303 /* Exit and clean up */
1304 fprintf (outfile, "$ gnv$$make_error: ! 'f$verify(0)\n");
1305 fprintf (outfile, "$ gnv$$make_status_2 = $status\n");
1307 if (append_token != -1)
1309 fprintf (outfile, "$ deassign sys$output\n");
1310 fprintf (outfile, "$ close 'gnv$$make_al'\n");
1313 (_("Append %.*s and cleanup\n"), comnamelen-3, child->comname));
1315 fprintf (outfile, "$ gnv$$make_exit: ! 'f$verify(0)\n");
1317 "$ exit 'gnv$$make_status_2' + (0*f$verify(gnv$$make_verify))\n");
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);
1326 DB (DB_JOBS, (_("Executing %s instead\n"), child->comname));
1330 while (child->efn < 32 || child->efn > 63)
1332 status = LIB$GET_EF ((unsigned long *)&child->efn);
1333 if (!$VMS_STATUS_SUCCESS (status))
1337 if (!ISDB (DB_JOBS))
1338 unlink (child->comname);
1339 free (child->comname);
1345 SYS$CLREF (child->efn);
1347 vms_jobsefnmask |= (1 << (child->efn - 32));
1349 /* Export the child environment into DCL symbols */
1350 if (child->environment != 0)
1352 char **ep = child->environment;
1355 vms_putenv_symbol (*ep);
1361 LIB$SPAWN [command-string]
1366 [,process-id] [,completion-status-address] [,byte-integer-event-flag-num]
1367 [,AST-address] [,varying-AST-argument]
1368 [,prompt-string] [,cli] [,table]
1371 #ifndef DONTWAITFORCHILD
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.
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 :-)
1410 if (!setupYAstTried)
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,
1421 status = child->vms_launch_status;
1422 if ($VMS_STATUS_SUCCESS (status))
1424 status = sys$waitfr (child->efn);
1425 vmsHandleChildTerm (child);
1428 child->vms_launch_status = lib$spawn (cmd_dsc,
1433 &child->pid, &child->cstatus, &child->efn,
1434 vmsHandleChildTerm, child,
1436 status = child->vms_launch_status;
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);
1444 if (!$VMS_STATUS_SUCCESS (status))
1456 /* Restore the VMS symbols that were changed */
1457 if (child->environment != 0)
1459 char **ep = child->environment;
1462 vms_restore_symbol (*ep);
1467 return (status & 1);