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