1 /* --------------- Moved here from job.c ---------------
2 This file must be #included in job.c, as it accesses static functions.
4 Copyright (C) 1996-2022 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 <https://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 childbase *cbase)
109 struct child *child = (struct child*)cbase;
110 struct child *lastc, *c;
114 /* The child efn is 0 when a built-in or null command is executed
115 successfully with out actually creating a child.
119 vms_jobsefnmask &= ~(1 << (child->efn - 32));
121 lib$free_ef (&child->efn);
125 if (!ISDB (DB_JOBS) && !ctrlYPressed)
126 unlink (child->comname);
127 free (child->comname);
130 (void) sigblock (fatal_signal_mask);
132 /* First check to see if this is a POSIX exit status and handle */
133 if ((child->cstatus & VMS_POSIX_EXIT_MASK) == VMS_POSIX_EXIT_MASK)
135 exit_code = (child->cstatus >> 3) & 255;
136 if (exit_code != MAKE_SUCCESS)
141 child_failed = !$VMS_STATUS_SUCCESS (child->cstatus);
143 exit_code = child->cstatus;
146 /* Search for a child matching the deceased one. */
148 #if defined(RECURSIVEJOBS)
149 /* I've had problems with recursive stuff and process handling */
150 for (c = children; c != 0 && c != child; lastc = c, c = c->next)
156 if ($VMS_STATUS_SUCCESS (child->vms_launch_status))
158 /* Convert VMS success status to 0 for UNIX code to be happy */
159 child->vms_launch_status = 0;
162 /* Set the state flag to say the commands have finished. */
163 c->file->command_state = cs_finished;
164 notice_finished_file (c->file);
166 (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask));
172 Spawn a process executing the command in ARGV and return its pid. */
174 /* local helpers to make ctrl+c and ctrl+y working, see below */
176 #include <libclidef.h>
179 static int ctrlMask= LIB$M_CLI_CTRLY;
180 static int oldCtrlMask;
181 static int setupYAstTried= 0;
182 static unsigned short int chan= 0;
187 lib$enable_ctrl (&oldCtrlMask,0);
194 for (c = children; c != 0; c = c->next)
196 sys$delprc (&c->pid, 0, 0);
198 kill (getpid(),SIGQUIT);
205 $DESCRIPTOR(inputDsc,"SYS$COMMAND");
208 short int status, count;
211 unsigned short int loc_chan;
219 status= sys$assign(&inputDsc,&loc_chan,0,0);
220 if (!(status&SS$_NORMAL))
226 status= sys$qiow (0, loc_chan, IO$_SETMODE|IO$M_CTRLYAST,&iosb,0,0,
227 astYHandler,0,0,0,0,0);
228 if (status==SS$_NORMAL)
230 if (status!=SS$_NORMAL)
233 sys$dassgn(loc_chan);
234 if (status!=SS$_ILLIOFUNC && status!=SS$_NOPRIV)
239 /* called from AST handler ? */
240 if (setupYAstTried>1)
242 if (atexit(reEnableAst))
244 _("-warning, you may have to re-enable CTRL-Y handling from DCL.\n"));
245 status= lib$disable_ctrl (&ctrlMask, &oldCtrlMask);
246 if (!(status&SS$_NORMAL))
255 /* Check if a token is too long */
256 #define INC_TOKEN_LEN_OR_RETURN(x) {token->length++; \
257 if (token->length >= MAX_DCL_TOKEN_LENGTH) \
258 { token->cmd_errno = ERANGE; return x; }}
260 #define INC_TOKEN_LEN_OR_BREAK {token->length++; \
261 if (token->length >= MAX_DCL_TOKEN_LENGTH) \
262 { token->cmd_errno = ERANGE; break; }}
264 #define ADD_TOKEN_LEN_OR_RETURN(add_len, x) {token->length += add_len; \
265 if (token->length >= MAX_DCL_TOKEN_LENGTH) \
266 { token->cmd_errno = ERANGE; return x; }}
268 /* Check if we are out of space for more tokens */
269 #define V_NEXT_TOKEN { if (cmd_tkn_index < MAX_DCL_TOKENS) \
270 cmd_tokens[++cmd_tkn_index] = NULL; \
271 else { token.cmd_errno = E2BIG; break; } \
275 #define UPDATE_TOKEN {cmd_tokens[cmd_tkn_index] = strdup(token.text); \
278 #define EOS_ERROR(x) { if (*x == 0) { token->cmd_errno = ERANGE; break; }}
282 char *text; /* Parsed text */
283 int length; /* Length of parsed text */
284 char *src; /* Pointer to source text */
285 int cmd_errno; /* Error status of parse */
286 int use_cmd_file; /* Force use of a command file */
290 /* Extract a Posix single quoted string from input line */
292 posix_parse_sq (struct token_info *token)
294 /* A Posix quoted string with no expansion unless in a string
295 Unix simulation means no lexical functions present.
304 INC_TOKEN_LEN_OR_RETURN (p);
306 while (*p != '\'' && (token->length < MAX_DCL_TOKEN_LENGTH))
311 /* Embedded double quotes need to be doubled */
313 INC_TOKEN_LEN_OR_BREAK;
321 INC_TOKEN_LEN_OR_BREAK;
325 INC_TOKEN_LEN_OR_RETURN (p);
330 /* Extract a Posix double quoted string from input line */
332 posix_parse_dq (struct token_info *token)
334 /* Unix mode: Any embedded \" becomes doubled.
335 \t is tab, \\, \$ leading character stripped.
336 $ character replaced with \' unless escaped.
343 INC_TOKEN_LEN_OR_RETURN (p);
350 case 't': /* Convert tabs */
354 case '\\': /* Just remove leading backslash */
363 INC_TOKEN_LEN_OR_BREAK;
364 default: /* Pass through unchanged */
366 INC_TOKEN_LEN_OR_BREAK;
368 INC_TOKEN_LEN_OR_BREAK;
370 else if (*p == '$' && isalpha ((unsigned char) p[1]))
372 /* A symbol we should be able to substitute */
374 INC_TOKEN_LEN_OR_BREAK;
376 INC_TOKEN_LEN_OR_BREAK;
377 token->use_cmd_file = 1;
382 INC_TOKEN_LEN_OR_BREAK;
397 /* Extract a VMS quoted string or substitution string from input line */
399 vms_parse_quotes (struct token_info *token)
401 /* VMS mode, the \' means that a symbol substitution is starting
402 so while you might think you can just copy until the next
403 \'. Unfortunately the substitution can be a lexical function
404 which can contain embedded strings and lexical functions.
405 Messy, so both types need to be handled together.
411 int parse_level[MAX_DCL_TOKENS + 1];
415 if (parse_level[0] == '\'')
416 token->use_cmd_file = 1;
419 INC_TOKEN_LEN_OR_RETURN (p);
422 /* Copy everything until after the next single quote at nest == 0 */
423 while (token->length < MAX_DCL_TOKEN_LENGTH)
427 INC_TOKEN_LEN_OR_BREAK;
428 if ((*p == parse_level[nest]) && (p[1] != '"'))
442 /* Handle continuation on to next line */
450 /* Parenthesis only in single quote level */
451 if (parse_level[nest] == '\'')
454 parse_level[nest] == ')';
458 /* Double quotes only in parenthesis */
459 if (parse_level[nest] == ')')
462 parse_level[nest] == '"';
466 /* Symbol substitution only in double quotes */
467 if ((p[1] == '\'') && (parse_level[nest] == '"'))
470 parse_level[nest] == '\'';
472 token->use_cmd_file = 1;
473 INC_TOKEN_LEN_OR_BREAK;
481 /* Pass through doubled double quotes */
482 if ((*p == '"') && (p[1] == '"') && (parse_level[nest] == '"'))
485 INC_TOKEN_LEN_OR_BREAK;
487 INC_TOKEN_LEN_OR_BREAK;
494 /* Extract a $ string from the input line */
496 posix_parse_dollar (struct token_info *token)
498 /* $foo becomes 'foo' */
503 token->use_cmd_file = 1;
507 INC_TOKEN_LEN_OR_RETURN (p);
509 while ((isalnum ((unsigned char) *p)) || (*p == '_'))
512 INC_TOKEN_LEN_OR_BREAK;
517 INC_TOKEN_LEN_OR_BREAK;
524 const char *vms_filechars = "0123456789abcdefghijklmnopqrstuvwxyz" \
525 "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]<>:/_-.$";
527 /* Simple text copy */
529 parse_text (struct token_info *token, int assignment_hack)
537 /* If assignment hack, then this text needs to be double quoted. */
538 if (vms_unix_simulation && (assignment_hack == 2))
541 INC_TOKEN_LEN_OR_RETURN (p);
545 INC_TOKEN_LEN_OR_RETURN (p);
549 str_len = strspn (p, vms_filechars);
552 /* Pass through backslash escapes in Unix simulation
553 probably will not work anyway.
554 All any character after a ^ otherwise to support EFS.
556 if (vms_unix_simulation && (p[0] == '\\') && (p[1] != 0))
558 else if ((p[0] == '^') && (p[1] != 0))
560 else if (!vms_unix_simulation && (p[0] == ';'))
565 /* If assignment hack, then this needs to be double quoted. */
566 if (vms_unix_simulation && (assignment_hack == 2))
569 INC_TOKEN_LEN_OR_RETURN (p);
577 ADD_TOKEN_LEN_OR_RETURN (str_len, p);
578 strncpy (q, p, str_len);
584 /* If assignment hack, then this text needs to be double quoted. */
585 if (vms_unix_simulation && (assignment_hack == 2))
588 INC_TOKEN_LEN_OR_RETURN (p);
593 /* single character copy */
595 parse_char (struct token_info *token, int count)
605 INC_TOKEN_LEN_OR_RETURN (p);
612 /* Build a command string from the collected tokens
613 and process built-ins now
615 static struct dsc$descriptor_s *
616 build_vms_cmd (char **cmd_tokens,
617 enum auto_pipe use_pipe_cmd,
620 struct dsc$descriptor_s *cmd_dsc;
627 cmd_dsc = xmalloc (sizeof (struct dsc$descriptor_s));
630 if (cmd_tokens[0] == NULL)
632 cmd_dsc->dsc$a_pointer = NULL;
633 cmd_dsc->dsc$w_length = 0;
637 /* Max DCL command + 1 extra token and trailing space */
638 cmd = xmalloc (MAX_DCL_CMD_LINE_LENGTH + 256);
644 /* Handle real or auto-pipe */
645 if (use_pipe_cmd == add_pipe)
647 /* We need to auto convert to a pipe command */
648 strcat (cmd, "pipe ");
653 while (cmd_tokens[cmd_tkn_index] != NULL)
656 /* Check for buffer overflow */
657 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
663 /* Eliminate double ';' */
664 if (semicolon_seen && (cmd_tokens[cmd_tkn_index][0] == ';'))
667 free (cmd_tokens[cmd_tkn_index++]);
668 if (cmd_tokens[cmd_tkn_index] == NULL)
672 /* Special handling for CD built-in */
673 if (strncmp (cmd_tokens[cmd_tkn_index], "builtin_cd", 11) == 0)
677 free (cmd_tokens[cmd_tkn_index]);
679 if (cmd_tokens[cmd_tkn_index] == NULL)
681 DB(DB_JOBS, (_("BUILTIN CD %s\n"), cmd_tokens[cmd_tkn_index]));
683 /* TODO: chdir fails with some valid syntaxes */
684 result = chdir (cmd_tokens[cmd_tkn_index]);
687 /* TODO: Handle failure better */
689 while (cmd_tokens[cmd_tkn_index] == NULL)
690 free (cmd_tokens[cmd_tkn_index++]);
691 cmd_dsc->dsc$w_length = -1;
692 cmd_dsc->dsc$a_pointer = NULL;
696 else if (strncmp (cmd_tokens[cmd_tkn_index], "exit", 5) == 0)
698 /* Copy the exit command */
700 strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
701 cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
702 free (cmd_tokens[cmd_tkn_index++]);
703 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
709 /* Optional whitespace */
710 if (isspace ((unsigned char) cmd_tokens[cmd_tkn_index][0]))
712 strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
713 cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
714 free (cmd_tokens[cmd_tkn_index++]);
715 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
722 /* There should be a status, but it is optional */
723 if (cmd_tokens[cmd_tkn_index][0] == ';')
726 /* If Unix simulation, add '((' */
727 if (vms_unix_simulation)
729 strcpy (&cmd[cmd_len], "((");
731 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
738 /* Add the parameter */
739 strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
740 cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
741 free (cmd_tokens[cmd_tkn_index++]);
742 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
748 /* Add " * 8) .and. %x7f8) .or. %x1035a002" */
749 if (vms_unix_simulation)
751 const char *end_str = " * 8) .and. %x7f8) .or. %x1035a002";
752 strcpy (&cmd[cmd_len], end_str);
753 cmd_len += strlen (end_str);
754 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
763 /* auto pipe needs spaces before semicolon */
764 if (use_pipe_cmd == add_pipe)
765 if (cmd_tokens[cmd_tkn_index][0] == ';')
767 cmd[cmd_len++] = ' ';
769 if (cmd_len > MAX_DCL_CMD_LINE_LENGTH)
778 ch = cmd_tokens[cmd_tkn_index][0];
779 if (!(ch == ' ' || ch == '\t'))
783 strcpy (&cmd[cmd_len], cmd_tokens[cmd_tkn_index]);
784 cmd_len += strlen (cmd_tokens[cmd_tkn_index]);
786 free (cmd_tokens[cmd_tkn_index++]);
788 /* Skip the append tokens if they exist */
789 if (cmd_tkn_index == append_token)
791 free (cmd_tokens[cmd_tkn_index++]);
792 if (isspace ((unsigned char) cmd_tokens[cmd_tkn_index][0]))
793 free (cmd_tokens[cmd_tkn_index++]);
794 free (cmd_tokens[cmd_tkn_index++]);
799 cmd_dsc->dsc$w_length = cmd_len;
800 cmd_dsc->dsc$a_pointer = cmd;
801 cmd_dsc->dsc$b_dtype = DSC$K_DTYPE_T;
802 cmd_dsc->dsc$b_class = DSC$K_CLASS_S;
808 child_execute_job (struct childbase *child, int good_stdin UNUSED, char *argv)
812 static struct dsc$descriptor_s *cmd_dsc;
813 static struct dsc$descriptor_s pnamedsc;
814 int spflags = CLI$M_NOWAIT;
820 char *cmd_tokens[(MAX_DCL_TOKENS * 2) + 1]; /* whitespace does not count */
821 char token_str[MAX_DCL_TOKEN_LENGTH + 1];
822 struct token_info token;
825 enum auto_pipe use_pipe_cmd = nopipe;
826 int append_token = -1;
827 char *append_file = NULL;
828 int unix_echo_cmd = 0; /* Special handle Unix echo command */
829 int assignment_hack = 0; /* Handle x=y command as piped command */
831 /* Parse IO redirection. */
833 child->comname = NULL;
835 DB (DB_JOBS, ("child_execute_job (%s)\n", argv));
837 while (isspace ((unsigned char)*argv))
842 /* Only a built-in or a null command - Still need to run term AST */
843 child->cstatus = VMS_POSIX_EXIT_MASK;
844 child->vms_launch_status = SS$_NORMAL;
846 vmsHandleChildTerm (child);
847 /* TODO what is this "magic number" */
848 return 270163; /* Special built-in */
851 sprintf (procname, "GMAKE_%05x", getpid () & 0xfffff);
852 pnamedsc.dsc$w_length = strlen (procname);
853 pnamedsc.dsc$a_pointer = procname;
854 pnamedsc.dsc$b_dtype = DSC$K_DTYPE_T;
855 pnamedsc.dsc$b_class = DSC$K_CLASS_S;
858 /* Handle comments and redirection.
859 For ONESHELL, the redirection must be on the first line. Any other
860 redirection token is handled by DCL, that is, the pipe command with
861 redirection can be used, but it should not be used on the first line
865 1. A token is any of DCL verbs, qualifiers, parameters, or punctuation.
866 2. Only MAX_DCL_TOKENS per line in both one line or command file mode.
867 3. Each token limited to MAC_DCL_TOKEN_LENGTH
868 4. If the line to DCL is greater than MAX_DCL_LINE_LENGTH then a
869 command file must be used.
870 5. Currently a command file must be used symbol substitution is to
872 6. Currently limiting command files to 2 * MAX_DCL_TOKENS.
874 Build both a command file token list and command line token list
875 until it is determined that the command line limits are exceeded.
879 cmd_tokens[cmd_tkn_index] = NULL;
882 token.text = token_str;
885 token.use_cmd_file = 0;
889 /* We can not build this command so give up */
890 if (token.cmd_errno != 0)
898 if (vms_unix_simulation || unix_echo_cmd)
900 p = posix_parse_sq (&token);
905 /* VMS mode, the \' means that a symbol substitution is starting
906 so while you might think you can just copy until the next
907 \'. Unfortunately the substitution can be a lexical function
908 which can contain embedded strings and lexical functions.
911 p = vms_parse_quotes (&token);
915 if (vms_unix_simulation)
917 p = posix_parse_dq (&token);
922 /* VMS quoted string, can contain lexical functions with
923 quoted strings and nested lexical functions.
925 p = vms_parse_quotes (&token);
930 if (vms_unix_simulation)
932 p = posix_parse_dollar (&token);
937 /* Otherwise nothing special */
938 p = parse_text (&token, 0);
944 /* Line continuation, remove it */
949 /* Ordinary character otherwise */
950 if (assignment_hack != 0)
952 if (assignment_hack > 2)
954 assignment_hack = 0; /* Reset */
955 if (use_pipe_cmd == nopipe) /* force pipe use */
956 use_pipe_cmd = add_pipe;
957 token_str[0] = ';'; /* add ; token */
961 p = parse_text (&token, assignment_hack);
966 /* Unix '#' is VMS '!' which comments out the rest of the line.
967 Historically the rest of the line has been skipped.
968 Not quite the right thing to do, as the f$verify lexical
969 function works in comments. But this helps keep the line
973 while (*p != '\n' && *p != 0)
977 /* Subshell, equation, or lexical function argument start */
978 p = parse_char (&token, 1);
983 /* Close out a paren level */
984 p = parse_char (&token, 1);
987 /* TODO: Should we diagnose if paren_level goes negative? */
990 if (isalpha ((unsigned char) p[1]) && !vms_unix_simulation)
992 /* VMS symbol substitution */
993 p = parse_text (&token, 0);
994 token.use_cmd_file = 1;
998 if (use_pipe_cmd == nopipe)
999 use_pipe_cmd = add_pipe;
1001 p = parse_char (&token, 1);
1003 p = parse_char (&token, 2);
1007 if (use_pipe_cmd == nopipe)
1008 use_pipe_cmd = add_pipe;
1010 p = parse_char (&token, 1);
1012 p = parse_char (&token, 2);
1016 /* Separator - convert to a pipe command. */
1019 if (use_pipe_cmd == nopipe)
1020 use_pipe_cmd = add_pipe;
1021 p = parse_char (&token, 1);
1025 if (use_pipe_cmd == nopipe)
1026 use_pipe_cmd = add_pipe;
1029 /* Parsing would have been simple until support for the >>
1030 append redirect was added.
1031 Implementation needs:
1032 * if not exist output file create empty
1033 * open/append gnv$make_temp??? output_file
1034 * define/user sys$output gnv$make_temp???
1035 ** And all this done before the command previously tokenized.
1036 * command previously tokenized
1037 * close gnv$make_temp???
1039 p = parse_char (&token, 2);
1040 append_token = cmd_tkn_index;
1041 token.use_cmd_file = 1;
1044 p = parse_char (&token, 1);
1048 /* Unix path or VMS option start, read until non-path symbol */
1049 if (assignment_hack != 0)
1051 if (assignment_hack > 2)
1053 assignment_hack = 0; /* Reset */
1054 if (use_pipe_cmd == nopipe) /* force pipe use */
1055 use_pipe_cmd = add_pipe;
1056 token_str[0] = ';'; /* add ; token */
1060 p = parse_text (&token, assignment_hack);
1064 if ((p[1] == 0) || isspace ((unsigned char) p[1]))
1066 /* Unix Null command - treat as comment until next command */
1073 /* Remove Null command from pipeline */
1082 /* String assignment */
1085 p = parse_char (&token, 1);
1086 else if (p[2] != '=')
1087 p = parse_char (&token, 2);
1089 p = parse_char (&token, 3);
1094 /* If this is not an echo statement, this could be a shell
1095 assignment. VMS requires the target to be quoted if it
1096 is not a macro substitution */
1097 if (!unix_echo_cmd && vms_unix_simulation && (assignment_hack == 0))
1098 assignment_hack = 1;
1100 p = parse_char (&token, 1);
1102 p = parse_char (&token, 2);
1108 p = parse_char (&token, 1);
1112 /* .xxx. operation, VMS does not require the trailing . */
1113 p = parse_text (&token, 0);
1117 /* Skip repetitive whitespace */
1118 if (isspace ((unsigned char) *p))
1120 p = parse_char (&token, 1);
1122 /* Force to a space or a tab */
1123 if ((token_str[0] != ' ') ||
1124 (token_str[0] != '\t'))
1128 while (isspace ((unsigned char) *p))
1130 if (assignment_hack != 0)
1135 if (assignment_hack != 0)
1137 if (assignment_hack > 2)
1139 assignment_hack = 0; /* Reset */
1140 if (use_pipe_cmd == nopipe) /* force pipe use */
1141 use_pipe_cmd = add_pipe;
1142 token_str[0] = ';'; /* add ; token */
1146 p = parse_text (&token, assignment_hack);
1147 if (strncasecmp (token.text, "echo", 4) == 0)
1149 else if (strncasecmp (token.text, "pipe", 4) == 0)
1150 use_pipe_cmd = dcl_pipe;
1156 /* End up here with a list of tokens to build a command line.
1157 Deal with errors detected during parsing.
1159 if (token.cmd_errno != 0)
1161 while (cmd_tokens[cmd_tkn_index] == NULL)
1162 free (cmd_tokens[cmd_tkn_index++]);
1163 child->cstatus = VMS_POSIX_EXIT_MASK | (MAKE_TROUBLE << 3);
1164 child->vms_launch_status = SS$_ABORT;
1166 errno = token.cmd_errno;
1170 /* Save any redirection to append file */
1171 if (append_token != -1)
1176 char * raw_append_file;
1177 file_token = append_token;
1179 if (isspace ((unsigned char) cmd_tokens[file_token][0]))
1181 raw_append_file = vmsify (cmd_tokens[file_token], 0);
1182 /* VMS DCL needs a trailing dot if null file extension */
1183 lastdot = strrchr(raw_append_file, '.');
1184 lastdir = strrchr(raw_append_file, ']');
1185 if (lastdir == NULL)
1186 lastdir = strrchr(raw_append_file, '>');
1187 if (lastdir == NULL)
1188 lastdir = strrchr(raw_append_file, ':');
1189 if ((lastdot == NULL) || (lastdot > lastdir))
1191 append_file = xmalloc (strlen (raw_append_file) + 1);
1192 strcpy (append_file, raw_append_file);
1193 strcat (append_file, ".");
1196 append_file = strdup(raw_append_file);
1199 cmd_dsc = build_vms_cmd (cmd_tokens, use_pipe_cmd, append_token);
1200 if (cmd_dsc->dsc$a_pointer == NULL)
1202 if (cmd_dsc->dsc$w_length < 0)
1205 child->cstatus = VMS_POSIX_EXIT_MASK | (MAKE_TROUBLE << 3);
1206 child->vms_launch_status = SS$_ABORT;
1211 /* Only a built-in or a null command - Still need to run term AST */
1213 child->cstatus = VMS_POSIX_EXIT_MASK;
1214 child->vms_launch_status = SS$_NORMAL;
1216 vmsHandleChildTerm (child);
1217 /* TODO what is this "magic number" */
1218 return 270163; /* Special built-in */
1221 if (cmd_dsc->dsc$w_length > MAX_DCL_LINE_LENGTH)
1222 token.use_cmd_file = 1;
1224 DB(DB_JOBS, (_("DCL: %s\n"), cmd_dsc->dsc$a_pointer));
1226 /* Enforce the creation of a command file if "vms_always_use_cmd_file" is
1228 Further, this way DCL reads the input stream and therefore does
1229 'forced' symbol substitution, which it doesn't do for one-liners when
1230 they are 'lib$spawn'ed.
1232 Otherwise the behavior is:
1234 Create a *.com file if either the command is too long for
1235 lib$spawn, or if a redirect appending to a file is desired, or
1236 symbol substitition.
1239 if (vms_always_use_cmd_file || token.use_cmd_file)
1244 outfile = get_tmpfile (&child->comname);
1246 comnamelen = strlen (child->comname);
1248 /* The whole DCL "script" is executed as one action, and it behaves as
1249 any DCL "script", that is errors stop it but warnings do not. Usually
1250 the command on the last line, defines the exit code. However, with
1251 redirections there is a prolog and possibly an epilog to implement
1252 the redirection. Both are part of the script which is actually
1253 executed. So if the redirection encounters an error in the prolog,
1254 the user actions will not run; if in the epilog, the user actions
1255 ran, but output is not captured. In both error cases, the error of
1256 redirection is passed back and not the exit code of the actions. The
1257 user should be able to enable DCL "script" verification with "set
1258 verify". However, the prolog and epilog commands are not shown. Also,
1259 if output redirection is used, the verification output is redirected
1260 into that file as well. */
1261 fprintf (outfile, "$ gnv$$make_verify = \"''f$verify(0)'\"\n");
1262 fprintf (outfile, "$ gnv$$make_pid = f$getjpi(\"\",\"pid\")\n");
1263 fprintf (outfile, "$ on error then $ goto gnv$$make_error\n");
1265 /* Handle append redirection */
1266 if (append_file != NULL)
1268 /* If file does not exist, create it */
1270 "$ gnv$$make_al = \"gnv$$make_append''gnv$$make_pid'\"\n");
1272 "$ if f$search(\"%s\") .eqs. \"\" then create %s\n",
1273 append_file, append_file);
1276 "$ open/append 'gnv$$make_al' %s\n", append_file);
1278 /* define sys$output to that file */
1280 "$ define/user sys$output 'gnv$$make_al'\n");
1281 DB (DB_JOBS, (_("Append output to %s\n"), append_file));
1285 fprintf (outfile, "$ gnv$$make_verify = f$verify(gnv$$make_verify)\n");
1288 Only for ONESHELL there will be several commands separated by
1289 '\n'. But there can always be multiple continuation lines.
1292 fprintf (outfile, "%s\n", cmd_dsc->dsc$a_pointer);
1293 fprintf (outfile, "$ gnv$$make_status_2 = $status\n");
1294 fprintf (outfile, "$ goto gnv$$make_exit\n");
1296 /* Exit and clean up */
1297 fprintf (outfile, "$ gnv$$make_error: ! 'f$verify(0)\n");
1298 fprintf (outfile, "$ gnv$$make_status_2 = $status\n");
1300 if (append_token != -1)
1302 fprintf (outfile, "$ deassign sys$output\n");
1303 fprintf (outfile, "$ close 'gnv$$make_al'\n");
1306 (_("Append %.*s and cleanup\n"), comnamelen-3, child->comname));
1308 fprintf (outfile, "$ gnv$$make_exit: ! 'f$verify(0)\n");
1310 "$ exit 'gnv$$make_status_2' + (0*f$verify(gnv$$make_verify))\n");
1314 free (cmd_dsc->dsc$a_pointer);
1315 cmd_dsc->dsc$a_pointer = xmalloc (256 + 4);
1316 sprintf (cmd_dsc->dsc$a_pointer, "$ @%s", child->comname);
1317 cmd_dsc->dsc$w_length = strlen (cmd_dsc->dsc$a_pointer);
1319 DB (DB_JOBS, (_("Executing %s instead\n"), child->comname));
1323 while (child->efn < 32 || child->efn > 63)
1325 status = LIB$GET_EF ((unsigned long *)&child->efn);
1326 if (!$VMS_STATUS_SUCCESS (status))
1330 if (!ISDB (DB_JOBS))
1331 unlink (child->comname);
1332 free (child->comname);
1338 SYS$CLREF (child->efn);
1340 vms_jobsefnmask |= (1 << (child->efn - 32));
1342 /* Export the child environment into DCL symbols */
1343 if (child->environment != 0)
1345 char **ep = child->environment;
1348 vms_putenv_symbol (*ep);
1354 LIB$SPAWN [command-string]
1359 [,process-id] [,completion-status-address] [,byte-integer-event-flag-num]
1360 [,AST-address] [,varying-AST-argument]
1361 [,prompt-string] [,cli] [,table]
1364 #ifndef DONTWAITFORCHILD
1366 * Code to make ctrl+c and ctrl+y working.
1367 * The problem starts with the synchronous case where after lib$spawn is
1368 * called any input will go to the child. But with input re-directed,
1369 * both control characters won't make it to any of the programs, neither
1370 * the spawning nor to the spawned one. Hence the caller needs to spawn
1371 * with CLI$M_NOWAIT to NOT give up the input focus. A sys$waitfr
1372 * has to follow to simulate the wanted synchronous behaviour.
1373 * The next problem is ctrl+y which isn't caught by the crtl and
1374 * therefore isn't converted to SIGQUIT (for a signal handler which is
1375 * already established). The only way to catch ctrl+y, is an AST
1376 * assigned to the input channel. But ctrl+y handling of DCL needs to be
1377 * disabled, otherwise it will handle it. Not to mention the previous
1378 * ctrl+y handling of DCL needs to be re-established before make exits.
1379 * One more: At the time of LIB$SPAWN signals are blocked. SIGQUIT will
1380 * make it to the signal handler after the child "normally" terminates.
1381 * This isn't enough. It seems reasonable for simple command lines like
1382 * a 'cc foobar.c' spawned in a subprocess but it is unacceptable for
1383 * spawning make. Therefore we need to abort the process in the AST.
1385 * Prior to the spawn it is checked if an AST is already set up for
1386 * ctrl+y, if not one is set up for a channel to SYS$COMMAND. In general
1387 * this will work except if make is run in a batch environment, but there
1388 * nobody can press ctrl+y. During the setup the DCL handling of ctrl+y
1389 * is disabled and an exit handler is established to re-enable it.
1390 * If the user interrupts with ctrl+y, the assigned AST will fire, force
1391 * an abort to the subprocess and signal SIGQUIT, which will be caught by
1392 * the already established handler and will bring us back to common code.
1393 * After the spawn (now /nowait) a sys$waitfr simulates the /wait and
1394 * enables the ctrl+y be delivered to this code. And the ctrl+c too,
1395 * which the crtl converts to SIGINT and which is caught by the common
1396 * signal handler. Because signals were blocked before entering this code
1397 * sys$waitfr will always complete and the SIGQUIT will be processed after
1398 * it (after termination of the current block, somewhere in common code).
1399 * And SIGINT too will be delayed. That is ctrl+c can only abort when the
1400 * current command completes. Anyway it's better than nothing :-)
1403 if (!setupYAstTried)
1405 child->vms_launch_status = lib$spawn (cmd_dsc, /* cmd-string */
1406 NULL, /* input-file */
1407 NULL, /* output-file */
1408 &spflags, /* flags */
1409 &pnamedsc, /* proc name */
1410 &child->pid, &child->cstatus, &child->efn,
1414 status = child->vms_launch_status;
1415 if ($VMS_STATUS_SUCCESS (status))
1417 status = sys$waitfr (child->efn);
1418 vmsHandleChildTerm (child);
1421 child->vms_launch_status = lib$spawn (cmd_dsc,
1426 &child->pid, &child->cstatus, &child->efn,
1427 vmsHandleChildTerm, child,
1429 status = child->vms_launch_status;
1432 /* Free the pointer if not a command file */
1433 if (!vms_always_use_cmd_file && !token.use_cmd_file)
1434 free (cmd_dsc->dsc$a_pointer);
1437 if (!$VMS_STATUS_SUCCESS (status))
1449 /* Restore the VMS symbols that were changed */
1450 if (child->environment != 0)
1452 char **ep = child->environment;
1455 vms_restore_symbol (*ep);
1460 /* TODO what is this "magic number" */
1461 return (status & 1) ? 270163 : -1 ;