VirtualBox

source: kBuild/trunk/src/kmk/vmsjobs.c@ 3387

Last change on this file since 3387 was 3140, checked in by bird, 6 years ago

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

  • Property svn:eol-style set to native
File size: 42.0 KB
Line 
1/* --------------- Moved here from job.c ---------------
2 This file must be #included in job.c, as it accesses static functions.
3
4Copyright (C) 1996-2016 Free Software Foundation, Inc.
5This file is part of GNU Make.
6
7GNU Make is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 3 of the License, or (at your option) any later
10version.
11
12GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License along with
17this program. If not, see <http://www.gnu.org/licenses/>. */
18
19#include <string.h>
20#include <descrip.h>
21#include <clidef.h>
22
23/* TODO - VMS specific header file conditionally included in makeint.h */
24
25#include <stsdef.h>
26#include <ssdef.h>
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
51char *vmsify (char *name, int type);
52
53static 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
82
83/* Wait for nchildren children to terminate */
84static void
85vmsWaitForChildren (int *status)
86{
87 while (1)
88 {
89 if (!vms_jobsefnmask)
90 {
91 *status = 0;
92 return;
93 }
94
95 *status = sys$wflor (32, vms_jobsefnmask);
96 }
97 return;
98}
99
100static int ctrlYPressed= 0;
101/* This is called at main or AST level. It is at AST level for DONTWAITFORCHILD
102 and at main level otherwise. In any case it is called when a child process
103 terminated. At AST level it won't get interrupted by anything except a
104 inner mode level AST.
105*/
106static int
107vmsHandleChildTerm (struct child *child)
108{
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 {
118 vms_jobsefnmask &= ~(1 << (child->efn - 32));
119
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 ;
151#else
152 c = child;
153#endif
154
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;
168}
169
170/* VMS:
171 Spawn a process executing the command in ARGV and return its pid. */
172
173/* local helpers to make ctrl+c and ctrl+y working, see below */
174#include <iodef.h>
175#include <libclidef.h>
176#include <ssdef.h>
177
178static int ctrlMask= LIB$M_CLI_CTRLY;
179static int oldCtrlMask;
180static int setupYAstTried= 0;
181static unsigned short int chan= 0;
182
183static void
184reEnableAst(void)
185{
186 lib$enable_ctrl (&oldCtrlMask,0);
187}
188
189static int
190astYHandler (void)
191{
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;
198}
199
200static void
201tryToSetupYAst(void)
202{
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;
251}
252
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
805int
806child_execute_job (struct child *child, char *argv)
807{
808 int i;
809
810 static struct dsc$descriptor_s *cmd_dsc;
811 static struct dsc$descriptor_s pnamedsc;
812 int spflags = CLI$M_NOWAIT;
813 int status;
814 int comnamelen;
815 char procname[100];
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 */
828
829 /* Parse IO redirection. */
830
831 child->comname = NULL;
832
833 DB (DB_JOBS, ("child_execute_job (%s)\n", argv));
834
835 while (isspace ((unsigned char)*argv))
836 argv++;
837
838 if (*argv == 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 }
849
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;
855
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
894 switch (*p)
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 */
1166 child->efn = 0;
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 }
1196 else
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)
1244 {
1245 FILE *outfile;
1246 int cmd_len;
1247
1248 outfile = output_tmpfile (&child->comname,
1249 "sys$scratch:gnv$make_cmdXXXXXX.com");
1250 /* 012345678901234567890 */
1251 if (outfile == 0)
1252 pfatal_with_name (_("fopen (temporary file)"));
1253 comnamelen = strlen (child->comname);
1254
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");
1318
1319 fclose (outfile);
1320
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 }
1328
1329 child->efn = 0;
1330 while (child->efn < 32 || child->efn > 63)
1331 {
1332 status = LIB$GET_EF ((unsigned long *)&child->efn);
1333 if (!$VMS_STATUS_SUCCESS (status))
1334 {
1335 if (child->comname)
1336 {
1337 if (!ISDB (DB_JOBS))
1338 unlink (child->comname);
1339 free (child->comname);
1340 }
1341 return 0;
1342 }
1343 }
1344
1345 SYS$CLREF (child->efn);
1346
1347 vms_jobsefnmask |= (1 << (child->efn - 32));
1348
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 */
1370
1371#ifndef DONTWAITFORCHILD
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 */
1409
1410 if (!setupYAstTried)
1411 tryToSetupYAst();
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);
1426 }
1427#else
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;
1437#endif
1438
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 {
1446 switch (status)
1447 {
1448 case SS$_EXQUOTA:
1449 errno = EPROCLIM;
1450 break;
1451 default:
1452 errno = EFAIL;
1453 }
1454 }
1455
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
1467 return (status & 1);
1468}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use