1 /* vms.c 2 * 3 * VMS-specific routines for perl5 4 * 5 * Copyright (C) 1993-2015 by Charles Bailey and others. 6 * 7 * You may distribute under the terms of either the GNU General Public 8 * License or the Artistic License, as specified in the README file. 9 */ 10 11 /* 12 * Yet small as was their hunted band 13 * still fell and fearless was each hand, 14 * and strong deeds they wrought yet oft, 15 * and loved the woods, whose ways more soft 16 * them seemed than thralls of that black throne 17 * to live and languish in halls of stone. 18 * "The Lay of Leithian", Canto II, lines 135-40 19 * 20 * [p.162 of _The Lays of Beleriand_] 21 */ 22 23 #include <acedef.h> 24 #include <acldef.h> 25 #include <armdef.h> 26 #include <chpdef.h> 27 #include <clidef.h> 28 #include <climsgdef.h> 29 #include <dcdef.h> 30 #include <descrip.h> 31 #include <devdef.h> 32 #include <dvidef.h> 33 #include <float.h> 34 #include <fscndef.h> 35 #include <iodef.h> 36 #include <jpidef.h> 37 #include <kgbdef.h> 38 #include <libclidef.h> 39 #include <libdef.h> 40 #include <lib$routines.h> 41 #include <lnmdef.h> 42 #include <ossdef.h> 43 #include <ppropdef.h> 44 #include <prvdef.h> 45 #include <pscandef.h> 46 #include <psldef.h> 47 #include <rms.h> 48 #include <shrdef.h> 49 #include <ssdef.h> 50 #include <starlet.h> 51 #include <strdef.h> 52 #include <str$routines.h> 53 #include <syidef.h> 54 #include <uaidef.h> 55 #include <uicdef.h> 56 #include <stsdef.h> 57 #include <efndef.h> 58 #define NO_EFN EFN$C_ENF 59 60 #include <unixlib.h> 61 62 #pragma member_alignment save 63 #pragma nomember_alignment longword 64 struct item_list_3 { 65 unsigned short len; 66 unsigned short code; 67 void * bufadr; 68 unsigned short * retadr; 69 }; 70 #pragma member_alignment restore 71 72 /* Older versions of ssdef.h don't have these */ 73 #ifndef SS$_INVFILFOROP 74 # define SS$_INVFILFOROP 3930 75 #endif 76 #ifndef SS$_NOSUCHOBJECT 77 # define SS$_NOSUCHOBJECT 2696 78 #endif 79 80 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */ 81 #define PERLIO_NOT_STDIO 0 82 83 /* Don't replace system definitions of vfork, getenv, lstat, and stat, 84 * code below needs to get to the underlying CRTL routines. */ 85 #define DONT_MASK_RTL_CALLS 86 #include "EXTERN.h" 87 #include "perl.h" 88 #include "XSUB.h" 89 /* Anticipating future expansion in lexical warnings . . . */ 90 #ifndef WARN_INTERNAL 91 # define WARN_INTERNAL WARN_MISC 92 #endif 93 94 #ifdef VMS_LONGNAME_SUPPORT 95 #include <libfildef.h> 96 #endif 97 98 #if __CRTL_VER >= 80200000 99 #ifdef lstat 100 #undef lstat 101 #endif 102 #else 103 #ifdef lstat 104 #undef lstat 105 #endif 106 #define lstat(_x, _y) stat(_x, _y) 107 #endif 108 109 /* Routine to create a decterm for use with the Perl debugger */ 110 /* No headers, this information was found in the Programming Concepts Manual */ 111 112 static int (*decw_term_port) 113 (const struct dsc$descriptor_s * display, 114 const struct dsc$descriptor_s * setup_file, 115 const struct dsc$descriptor_s * customization, 116 struct dsc$descriptor_s * result_device_name, 117 unsigned short * result_device_name_length, 118 void * controller, 119 void * char_buffer, 120 void * char_change_buffer) = 0; 121 122 #if defined(NEED_AN_H_ERRNO) 123 dEXT int h_errno; 124 #endif 125 126 #if defined(__DECC) || defined(__DECCXX) 127 #pragma member_alignment save 128 #pragma nomember_alignment longword 129 #pragma message save 130 #pragma message disable misalgndmem 131 #endif 132 struct itmlst_3 { 133 unsigned short int buflen; 134 unsigned short int itmcode; 135 void *bufadr; 136 unsigned short int *retlen; 137 }; 138 139 struct filescan_itmlst_2 { 140 unsigned short length; 141 unsigned short itmcode; 142 char * component; 143 }; 144 145 struct vs_str_st { 146 unsigned short length; 147 char str[VMS_MAXRSS]; 148 unsigned short pad; /* for longword struct alignment */ 149 }; 150 151 #if defined(__DECC) || defined(__DECCXX) 152 #pragma message restore 153 #pragma member_alignment restore 154 #endif 155 156 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d) 157 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d) 158 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d) 159 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d) 160 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g) 161 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c) 162 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c) 163 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d) 164 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d) 165 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a) 166 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d) 167 #define getredirection(a,b) mp_getredirection(aTHX_ a,b) 168 169 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *); 170 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *); 171 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 172 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *); 173 174 static char * int_rmsexpand_vms( 175 const char * filespec, char * outbuf, unsigned opts); 176 static char * int_rmsexpand_tovms( 177 const char * filespec, char * outbuf, unsigned opts); 178 static char *int_tovmsspec 179 (const char *path, char *buf, int dir_flag, int * utf8_flag); 180 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl); 181 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl); 182 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl); 183 184 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */ 185 #define PERL_LNM_MAX_ALLOWED_INDEX 127 186 187 /* OpenVMS User's Guide says at least 9 iterative translations will be performed, 188 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for 189 * the Perl facility. 190 */ 191 #define PERL_LNM_MAX_ITER 10 192 193 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */ 194 #define MAX_DCL_SYMBOL (8192) 195 #define MAX_DCL_LINE_LENGTH (4096 - 4) 196 197 static char *__mystrtolower(char *str) 198 { 199 if (str) for (; *str; ++str) *str= tolower(*str); 200 return str; 201 } 202 203 static struct dsc$descriptor_s fildevdsc = 204 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" }; 205 static struct dsc$descriptor_s crtlenvdsc = 206 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" }; 207 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL }; 208 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL }; 209 static struct dsc$descriptor_s **env_tables = defenv; 210 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */ 211 212 /* True if we shouldn't treat barewords as logicals during directory */ 213 /* munching */ 214 static int no_translate_barewords; 215 216 /* DECC Features that may need to affect how Perl interprets 217 * displays filename information 218 */ 219 static int decc_disable_to_vms_logname_translation = 1; 220 static int decc_disable_posix_root = 1; 221 int decc_efs_case_preserve = 0; 222 static int decc_efs_charset = 0; 223 static int decc_efs_charset_index = -1; 224 static int decc_filename_unix_no_version = 0; 225 static int decc_filename_unix_only = 0; 226 int decc_filename_unix_report = 0; 227 int decc_posix_compliant_pathnames = 0; 228 int decc_readdir_dropdotnotype = 0; 229 static int vms_process_case_tolerant = 1; 230 int vms_vtf7_filenames = 0; 231 int gnv_unix_shell = 0; 232 static int vms_unlink_all_versions = 0; 233 static int vms_posix_exit = 0; 234 235 /* bug workarounds if needed */ 236 int decc_bug_devnull = 1; 237 int vms_bug_stat_filename = 0; 238 239 static int vms_debug_on_exception = 0; 240 static int vms_debug_fileify = 0; 241 242 /* Simple logical name translation */ 243 static int 244 simple_trnlnm(const char * logname, char * value, int value_len) 245 { 246 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV"); 247 const unsigned long attr = LNM$M_CASE_BLIND; 248 struct dsc$descriptor_s name_dsc; 249 int status; 250 unsigned short result; 251 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result}, 252 {0, 0, 0, 0}}; 253 254 name_dsc.dsc$w_length = strlen(logname); 255 name_dsc.dsc$a_pointer = (char *)logname; 256 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 257 name_dsc.dsc$b_class = DSC$K_CLASS_S; 258 259 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst); 260 261 if ($VMS_STATUS_SUCCESS(status)) { 262 263 /* Null terminate and return the string */ 264 /*--------------------------------------*/ 265 value[result] = 0; 266 return result; 267 } 268 269 return 0; 270 } 271 272 273 /* Is this a UNIX file specification? 274 * No longer a simple check with EFS file specs 275 * For now, not a full check, but need to 276 * handle POSIX ^UP^ specifications 277 * Fixing to handle ^/ cases would require 278 * changes to many other conversion routines. 279 */ 280 281 static int 282 is_unix_filespec(const char *path) 283 { 284 int ret_val; 285 const char * pch1; 286 287 ret_val = 0; 288 if (strncmp(path,"\"^UP^",5) != 0) { 289 pch1 = strchr(path, '/'); 290 if (pch1 != NULL) 291 ret_val = 1; 292 else { 293 294 /* If the user wants UNIX files, "." needs to be treated as in UNIX */ 295 if (decc_filename_unix_report || decc_filename_unix_only) { 296 if (strcmp(path,".") == 0) 297 ret_val = 1; 298 } 299 } 300 } 301 return ret_val; 302 } 303 304 /* This routine converts a UCS-2 character to be VTF-7 encoded. 305 */ 306 307 static void 308 ucs2_to_vtf7(char *outspec, unsigned long ucs2_char, int * output_cnt) 309 { 310 unsigned char * ucs_ptr; 311 int hex; 312 313 ucs_ptr = (unsigned char *)&ucs2_char; 314 315 outspec[0] = '^'; 316 outspec[1] = 'U'; 317 hex = (ucs_ptr[1] >> 4) & 0xf; 318 if (hex < 0xA) 319 outspec[2] = hex + '0'; 320 else 321 outspec[2] = (hex - 9) + 'A'; 322 hex = ucs_ptr[1] & 0xF; 323 if (hex < 0xA) 324 outspec[3] = hex + '0'; 325 else { 326 outspec[3] = (hex - 9) + 'A'; 327 } 328 hex = (ucs_ptr[0] >> 4) & 0xf; 329 if (hex < 0xA) 330 outspec[4] = hex + '0'; 331 else 332 outspec[4] = (hex - 9) + 'A'; 333 hex = ucs_ptr[1] & 0xF; 334 if (hex < 0xA) 335 outspec[5] = hex + '0'; 336 else { 337 outspec[5] = (hex - 9) + 'A'; 338 } 339 *output_cnt = 6; 340 } 341 342 343 /* This handles the conversion of a UNIX extended character set to a ^ 344 * escaped VMS character. 345 * in a UNIX file specification. 346 * 347 * The output count variable contains the number of characters added 348 * to the output string. 349 * 350 * The return value is the number of characters read from the input string 351 */ 352 static int 353 copy_expand_unix_filename_escape(char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl) 354 { 355 int count; 356 int utf8_flag; 357 358 utf8_flag = 0; 359 if (utf8_fl) 360 utf8_flag = *utf8_fl; 361 362 count = 0; 363 *output_cnt = 0; 364 if (*inspec >= 0x80) { 365 if (utf8_fl && vms_vtf7_filenames) { 366 unsigned long ucs_char; 367 368 ucs_char = 0; 369 370 if ((*inspec & 0xE0) == 0xC0) { 371 /* 2 byte Unicode */ 372 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f); 373 if (ucs_char >= 0x80) { 374 ucs2_to_vtf7(outspec, ucs_char, output_cnt); 375 return 2; 376 } 377 } else if ((*inspec & 0xF0) == 0xE0) { 378 /* 3 byte Unicode */ 379 ucs_char = ((inspec[0] & 0xF) << 12) + 380 ((inspec[1] & 0x3f) << 6) + 381 (inspec[2] & 0x3f); 382 if (ucs_char >= 0x800) { 383 ucs2_to_vtf7(outspec, ucs_char, output_cnt); 384 return 3; 385 } 386 387 #if 0 /* I do not see longer sequences supported by OpenVMS */ 388 /* Maybe some one can fix this later */ 389 } else if ((*inspec & 0xF8) == 0xF0) { 390 /* 4 byte Unicode */ 391 /* UCS-4 to UCS-2 */ 392 } else if ((*inspec & 0xFC) == 0xF8) { 393 /* 5 byte Unicode */ 394 /* UCS-4 to UCS-2 */ 395 } else if ((*inspec & 0xFE) == 0xFC) { 396 /* 6 byte Unicode */ 397 /* UCS-4 to UCS-2 */ 398 #endif 399 } 400 } 401 402 /* High bit set, but not a Unicode character! */ 403 404 /* Non printing DECMCS or ISO Latin-1 character? */ 405 if ((unsigned char)*inspec <= 0x9F) { 406 int hex; 407 outspec[0] = '^'; 408 outspec++; 409 hex = (*inspec >> 4) & 0xF; 410 if (hex < 0xA) 411 outspec[1] = hex + '0'; 412 else { 413 outspec[1] = (hex - 9) + 'A'; 414 } 415 hex = *inspec & 0xF; 416 if (hex < 0xA) 417 outspec[2] = hex + '0'; 418 else { 419 outspec[2] = (hex - 9) + 'A'; 420 } 421 *output_cnt = 3; 422 return 1; 423 } else if ((unsigned char)*inspec == 0xA0) { 424 outspec[0] = '^'; 425 outspec[1] = 'A'; 426 outspec[2] = '0'; 427 *output_cnt = 3; 428 return 1; 429 } else if ((unsigned char)*inspec == 0xFF) { 430 outspec[0] = '^'; 431 outspec[1] = 'F'; 432 outspec[2] = 'F'; 433 *output_cnt = 3; 434 return 1; 435 } 436 *outspec = *inspec; 437 *output_cnt = 1; 438 return 1; 439 } 440 441 /* Is this a macro that needs to be passed through? 442 * Macros start with $( and an alpha character, followed 443 * by a string of alpha numeric characters ending with a ) 444 * If this does not match, then encode it as ODS-5. 445 */ 446 if ((inspec[0] == '$') && (inspec[1] == '(')) { 447 int tcnt; 448 449 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) { 450 tcnt = 3; 451 outspec[0] = inspec[0]; 452 outspec[1] = inspec[1]; 453 outspec[2] = inspec[2]; 454 455 while(isalnum(inspec[tcnt]) || 456 (inspec[2] == '.') || (inspec[2] == '_')) { 457 outspec[tcnt] = inspec[tcnt]; 458 tcnt++; 459 } 460 if (inspec[tcnt] == ')') { 461 outspec[tcnt] = inspec[tcnt]; 462 tcnt++; 463 *output_cnt = tcnt; 464 return tcnt; 465 } 466 } 467 } 468 469 switch (*inspec) { 470 case 0x7f: 471 outspec[0] = '^'; 472 outspec[1] = '7'; 473 outspec[2] = 'F'; 474 *output_cnt = 3; 475 return 1; 476 break; 477 case '?': 478 if (decc_efs_charset == 0) 479 outspec[0] = '%'; 480 else 481 outspec[0] = '?'; 482 *output_cnt = 1; 483 return 1; 484 break; 485 case '.': 486 case '~': 487 case '!': 488 case '#': 489 case '&': 490 case '\'': 491 case '`': 492 case '(': 493 case ')': 494 case '+': 495 case '@': 496 case '{': 497 case '}': 498 case ',': 499 case ';': 500 case '[': 501 case ']': 502 case '%': 503 case '^': 504 case '\\': 505 /* Don't escape again if following character is 506 * already something we escape. 507 */ 508 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) { 509 *outspec = *inspec; 510 *output_cnt = 1; 511 return 1; 512 break; 513 } 514 /* But otherwise fall through and escape it. */ 515 case '=': 516 /* Assume that this is to be escaped */ 517 outspec[0] = '^'; 518 outspec[1] = *inspec; 519 *output_cnt = 2; 520 return 1; 521 break; 522 case ' ': /* space */ 523 /* Assume that this is to be escaped */ 524 outspec[0] = '^'; 525 outspec[1] = '_'; 526 *output_cnt = 2; 527 return 1; 528 break; 529 default: 530 *outspec = *inspec; 531 *output_cnt = 1; 532 return 1; 533 break; 534 } 535 return 0; 536 } 537 538 539 /* This handles the expansion of a '^' prefix to the proper character 540 * in a UNIX file specification. 541 * 542 * The output count variable contains the number of characters added 543 * to the output string. 544 * 545 * The return value is the number of characters read from the input 546 * string 547 */ 548 static int 549 copy_expand_vms_filename_escape(char *outspec, const char *inspec, int *output_cnt) 550 { 551 int count; 552 int scnt; 553 554 count = 0; 555 *output_cnt = 0; 556 if (*inspec == '^') { 557 inspec++; 558 switch (*inspec) { 559 /* Spaces and non-trailing dots should just be passed through, 560 * but eat the escape character. 561 */ 562 case '.': 563 *outspec = *inspec; 564 count += 2; 565 (*output_cnt)++; 566 break; 567 case '_': /* space */ 568 *outspec = ' '; 569 count += 2; 570 (*output_cnt)++; 571 break; 572 case '^': 573 /* Hmm. Better leave the escape escaped. */ 574 outspec[0] = '^'; 575 outspec[1] = '^'; 576 count += 2; 577 (*output_cnt) += 2; 578 break; 579 case 'U': /* Unicode - FIX-ME this is wrong. */ 580 inspec++; 581 count++; 582 scnt = strspn(inspec, "0123456789ABCDEFabcdef"); 583 if (scnt == 4) { 584 unsigned int c1, c2; 585 scnt = sscanf(inspec, "%2x%2x", &c1, &c2); 586 outspec[0] = c1 & 0xff; 587 outspec[1] = c2 & 0xff; 588 if (scnt > 1) { 589 (*output_cnt) += 2; 590 count += 4; 591 } 592 } 593 else { 594 /* Error - do best we can to continue */ 595 *outspec = 'U'; 596 outspec++; 597 (*output_cnt++); 598 *outspec = *inspec; 599 count++; 600 (*output_cnt++); 601 } 602 break; 603 default: 604 scnt = strspn(inspec, "0123456789ABCDEFabcdef"); 605 if (scnt == 2) { 606 /* Hex encoded */ 607 unsigned int c1; 608 scnt = sscanf(inspec, "%2x", &c1); 609 outspec[0] = c1 & 0xff; 610 if (scnt > 0) { 611 (*output_cnt++); 612 count += 2; 613 } 614 } 615 else { 616 *outspec = *inspec; 617 count++; 618 (*output_cnt++); 619 } 620 } 621 } 622 else { 623 *outspec = *inspec; 624 count++; 625 (*output_cnt)++; 626 } 627 return count; 628 } 629 630 /* vms_split_path - Verify that the input file specification is a 631 * VMS format file specification, and provide pointers to the components of 632 * it. With EFS format filenames, this is virtually the only way to 633 * parse a VMS path specification into components. 634 * 635 * If the sum of the components do not add up to the length of the 636 * string, then the passed file specification is probably a UNIX style 637 * path. 638 */ 639 static int 640 vms_split_path(const char * path, char * * volume, int * vol_len, char * * root, int * root_len, 641 char * * dir, int * dir_len, char * * name, int * name_len, 642 char * * ext, int * ext_len, char * * version, int * ver_len) 643 { 644 struct dsc$descriptor path_desc; 645 int status; 646 unsigned long flags; 647 int ret_stat; 648 struct filescan_itmlst_2 item_list[9]; 649 const int filespec = 0; 650 const int nodespec = 1; 651 const int devspec = 2; 652 const int rootspec = 3; 653 const int dirspec = 4; 654 const int namespec = 5; 655 const int typespec = 6; 656 const int verspec = 7; 657 658 /* Assume the worst for an easy exit */ 659 ret_stat = -1; 660 *volume = NULL; 661 *vol_len = 0; 662 *root = NULL; 663 *root_len = 0; 664 *dir = NULL; 665 *name = NULL; 666 *name_len = 0; 667 *ext = NULL; 668 *ext_len = 0; 669 *version = NULL; 670 *ver_len = 0; 671 672 path_desc.dsc$a_pointer = (char *)path; /* cast ok */ 673 path_desc.dsc$w_length = strlen(path); 674 path_desc.dsc$b_dtype = DSC$K_DTYPE_T; 675 path_desc.dsc$b_class = DSC$K_CLASS_S; 676 677 /* Get the total length, if it is shorter than the string passed 678 * then this was probably not a VMS formatted file specification 679 */ 680 item_list[filespec].itmcode = FSCN$_FILESPEC; 681 item_list[filespec].length = 0; 682 item_list[filespec].component = NULL; 683 684 /* If the node is present, then it gets considered as part of the 685 * volume name to hopefully make things simple. 686 */ 687 item_list[nodespec].itmcode = FSCN$_NODE; 688 item_list[nodespec].length = 0; 689 item_list[nodespec].component = NULL; 690 691 item_list[devspec].itmcode = FSCN$_DEVICE; 692 item_list[devspec].length = 0; 693 item_list[devspec].component = NULL; 694 695 /* root is a special case, adding it to either the directory or 696 * the device components will probably complicate things for the 697 * callers of this routine, so leave it separate. 698 */ 699 item_list[rootspec].itmcode = FSCN$_ROOT; 700 item_list[rootspec].length = 0; 701 item_list[rootspec].component = NULL; 702 703 item_list[dirspec].itmcode = FSCN$_DIRECTORY; 704 item_list[dirspec].length = 0; 705 item_list[dirspec].component = NULL; 706 707 item_list[namespec].itmcode = FSCN$_NAME; 708 item_list[namespec].length = 0; 709 item_list[namespec].component = NULL; 710 711 item_list[typespec].itmcode = FSCN$_TYPE; 712 item_list[typespec].length = 0; 713 item_list[typespec].component = NULL; 714 715 item_list[verspec].itmcode = FSCN$_VERSION; 716 item_list[verspec].length = 0; 717 item_list[verspec].component = NULL; 718 719 item_list[8].itmcode = 0; 720 item_list[8].length = 0; 721 item_list[8].component = NULL; 722 723 status = sys$filescan 724 ((const struct dsc$descriptor_s *)&path_desc, item_list, 725 &flags, NULL, NULL); 726 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */ 727 728 /* If we parsed it successfully these two lengths should be the same */ 729 if (path_desc.dsc$w_length != item_list[filespec].length) 730 return ret_stat; 731 732 /* If we got here, then it is a VMS file specification */ 733 ret_stat = 0; 734 735 /* set the volume name */ 736 if (item_list[nodespec].length > 0) { 737 *volume = item_list[nodespec].component; 738 *vol_len = item_list[nodespec].length + item_list[devspec].length; 739 } 740 else { 741 *volume = item_list[devspec].component; 742 *vol_len = item_list[devspec].length; 743 } 744 745 *root = item_list[rootspec].component; 746 *root_len = item_list[rootspec].length; 747 748 *dir = item_list[dirspec].component; 749 *dir_len = item_list[dirspec].length; 750 751 /* Now fun with versions and EFS file specifications 752 * The parser can not tell the difference when a "." is a version 753 * delimiter or a part of the file specification. 754 */ 755 if ((decc_efs_charset) && 756 (item_list[verspec].length > 0) && 757 (item_list[verspec].component[0] == '.')) { 758 *name = item_list[namespec].component; 759 *name_len = item_list[namespec].length + item_list[typespec].length; 760 *ext = item_list[verspec].component; 761 *ext_len = item_list[verspec].length; 762 *version = NULL; 763 *ver_len = 0; 764 } 765 else { 766 *name = item_list[namespec].component; 767 *name_len = item_list[namespec].length; 768 *ext = item_list[typespec].component; 769 *ext_len = item_list[typespec].length; 770 *version = item_list[verspec].component; 771 *ver_len = item_list[verspec].length; 772 } 773 return ret_stat; 774 } 775 776 /* Routine to determine if the file specification ends with .dir */ 777 static int 778 is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) 779 { 780 781 /* e_len must be 4, and version must be <= 2 characters */ 782 if (e_len != 4 || vs_len > 2) 783 return 0; 784 785 /* If a version number is present, it needs to be one */ 786 if ((vs_len == 2) && (vs_spec[1] != '1')) 787 return 0; 788 789 /* Look for the DIR on the extension */ 790 if (vms_process_case_tolerant) { 791 if ((toupper(e_spec[1]) == 'D') && 792 (toupper(e_spec[2]) == 'I') && 793 (toupper(e_spec[3]) == 'R')) { 794 return 1; 795 } 796 } else { 797 /* Directory extensions are supposed to be in upper case only */ 798 /* I would not be surprised if this rule can not be enforced */ 799 /* if and when someone fully debugs the case sensitive mode */ 800 if ((e_spec[1] == 'D') && 801 (e_spec[2] == 'I') && 802 (e_spec[3] == 'R')) { 803 return 1; 804 } 805 } 806 return 0; 807 } 808 809 810 /* my_maxidx 811 * Routine to retrieve the maximum equivalence index for an input 812 * logical name. Some calls to this routine have no knowledge if 813 * the variable is a logical or not. So on error we return a max 814 * index of zero. 815 */ 816 /*{{{int my_maxidx(const char *lnm) */ 817 static int 818 my_maxidx(const char *lnm) 819 { 820 int status; 821 int midx; 822 int attr = LNM$M_CASE_BLIND; 823 struct dsc$descriptor lnmdsc; 824 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0}, 825 {0, 0, 0, 0}}; 826 827 lnmdsc.dsc$w_length = strlen(lnm); 828 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T; 829 lnmdsc.dsc$b_class = DSC$K_CLASS_S; 830 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */ 831 832 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst); 833 if ((status & 1) == 0) 834 midx = 0; 835 836 return (midx); 837 } 838 /*}}}*/ 839 840 /* Routine to remove the 2-byte prefix from the translation of a 841 * process-permanent file (PPF). 842 */ 843 static inline unsigned short int 844 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen) 845 { 846 if (*((int *)lnm) == *((int *)"SYS$") && 847 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 && 848 ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT")) || 849 (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT")) || 850 (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR")) || 851 (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) ) ) { 852 853 memmove(eqv, eqv+4, eqvlen-4); 854 eqvlen -= 4; 855 } 856 return eqvlen; 857 } 858 859 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */ 860 int 861 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, 862 struct dsc$descriptor_s **tabvec, unsigned long int flags) 863 { 864 const char *cp1; 865 char uplnm[LNM$C_NAMLENGTH+1], *cp2; 866 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure; 867 bool found_in_crtlenv = 0, found_in_clisym = 0; 868 unsigned long int retsts, attr = LNM$M_CASE_BLIND; 869 int midx; 870 unsigned char acmode; 871 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 872 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 873 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0}, 874 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen}, 875 {0, 0, 0, 0}}; 876 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 877 #if defined(PERL_IMPLICIT_CONTEXT) 878 pTHX = NULL; 879 if (PL_curinterp) { 880 aTHX = PERL_GET_INTERP; 881 } else { 882 aTHX = NULL; 883 } 884 #endif 885 886 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) { 887 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0; 888 } 889 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 890 *cp2 = _toupper(*cp1); 891 if (cp1 - lnm > LNM$C_NAMLENGTH) { 892 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 893 return 0; 894 } 895 } 896 lnmdsc.dsc$w_length = cp1 - lnm; 897 lnmdsc.dsc$a_pointer = uplnm; 898 uplnm[lnmdsc.dsc$w_length] = '\0'; 899 secure = flags & PERL__TRNENV_SECURE; 900 acmode = secure ? PSL$C_EXEC : PSL$C_USER; 901 if (!tabvec || !*tabvec) tabvec = env_tables; 902 903 for (curtab = 0; tabvec[curtab]; curtab++) { 904 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) { 905 if (!ivenv && !secure) { 906 char *eq; 907 int i; 908 if (!environ) { 909 ivenv = 1; 910 #if defined(PERL_IMPLICIT_CONTEXT) 911 if (aTHX == NULL) { 912 fprintf(stderr, 913 "Can't read CRTL environ\n"); 914 } else 915 #endif 916 Perl_warn(aTHX_ "Can't read CRTL environ\n"); 917 continue; 918 } 919 retsts = SS$_NOLOGNAM; 920 for (i = 0; environ[i]; i++) { 921 if ((eq = strchr(environ[i],'=')) && 922 lnmdsc.dsc$w_length == (eq - environ[i]) && 923 !strncmp(environ[i],lnm,eq - environ[i])) { 924 eq++; 925 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen]; 926 if (!eqvlen) continue; 927 retsts = SS$_NORMAL; 928 break; 929 } 930 } 931 if (retsts != SS$_NOLOGNAM) { 932 found_in_crtlenv = 1; 933 break; 934 } 935 } 936 } 937 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 938 !str$case_blind_compare(&tmpdsc,&clisym)) { 939 if (!ivsym && !secure) { 940 unsigned short int deflen = LNM$C_NAMLENGTH; 941 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 942 /* dynamic dsc to accommodate possible long value */ 943 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc)); 944 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0); 945 if (retsts & 1) { 946 if (eqvlen > MAX_DCL_SYMBOL) { 947 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU); 948 eqvlen = MAX_DCL_SYMBOL; 949 /* Special hack--we might be called before the interpreter's */ 950 /* fully initialized, in which case either thr or PL_curcop */ 951 /* might be bogus. We have to check, since ckWARN needs them */ 952 /* both to be valid if running threaded */ 953 #if defined(PERL_IMPLICIT_CONTEXT) 954 if (aTHX == NULL) { 955 fprintf(stderr, 956 "Value of CLI symbol \"%s\" too long",lnm); 957 } else 958 #endif 959 if (ckWARN(WARN_MISC)) { 960 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm); 961 } 962 } 963 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen); 964 } 965 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc)); 966 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 967 if (retsts == LIB$_NOSUCHSYM) continue; 968 found_in_clisym = 1; 969 break; 970 } 971 } 972 else if (!ivlnm) { 973 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) { 974 midx = my_maxidx(lnm); 975 for (idx = 0, cp2 = eqv; idx <= midx; idx++) { 976 lnmlst[1].bufadr = cp2; 977 eqvlen = 0; 978 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 979 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; } 980 if (retsts == SS$_NOLOGNAM) break; 981 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen); 982 cp2 += eqvlen; 983 *cp2 = '\0'; 984 } 985 if ((retsts == SS$_IVLOGNAM) || 986 (retsts == SS$_NOLOGNAM)) { continue; } 987 eqvlen = strlen(eqv); 988 } 989 else { 990 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 991 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 992 if (retsts == SS$_NOLOGNAM) continue; 993 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen); 994 eqv[eqvlen] = '\0'; 995 } 996 break; 997 } 998 } 999 /* An index only makes sense for logical names, so make sure we aren't 1000 * iterating over an index for an environ var or DCL symbol and getting 1001 * the same answer ad infinitum. 1002 */ 1003 if (idx > 0 && (found_in_crtlenv || found_in_clisym)) { 1004 return 0; 1005 } 1006 else if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; } 1007 else if (retsts == LIB$_NOSUCHSYM || 1008 retsts == SS$_NOLOGNAM) { 1009 /* Unsuccessful lookup is normal -- no need to set errno */ 1010 return 0; 1011 } 1012 else if (retsts == LIB$_INVSYMNAM || 1013 retsts == SS$_IVLOGNAM || 1014 retsts == SS$_IVLOGTAB) { 1015 set_errno(EINVAL); set_vaxc_errno(retsts); 1016 } 1017 else _ckvmssts_noperl(retsts); 1018 return 0; 1019 } /* end of vmstrnenv */ 1020 /*}}}*/ 1021 1022 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/ 1023 /* Define as a function so we can access statics. */ 1024 int 1025 Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx) 1026 { 1027 int flags = 0; 1028 1029 #if defined(PERL_IMPLICIT_CONTEXT) 1030 if (aTHX != NULL) 1031 #endif 1032 #ifdef SECURE_INTERNAL_GETENV 1033 flags = (PL_curinterp ? TAINTING_get : will_taint) ? 1034 PERL__TRNENV_SECURE : 0; 1035 #endif 1036 1037 return vmstrnenv(lnm, eqv, idx, fildev, flags); 1038 } 1039 /*}}}*/ 1040 1041 /* my_getenv 1042 * Note: Uses Perl temp to store result so char * can be returned to 1043 * caller; this pointer will be invalidated at next Perl statement 1044 * transition. 1045 * We define this as a function rather than a macro in terms of my_getenv_len() 1046 * so that it'll work when PL_curinterp is undefined (and we therefore can't 1047 * allocate SVs). 1048 */ 1049 /*{{{ char *my_getenv(const char *lnm, bool sys)*/ 1050 char * 1051 Perl_my_getenv(pTHX_ const char *lnm, bool sys) 1052 { 1053 const char *cp1; 1054 static char *__my_getenv_eqv = NULL; 1055 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv; 1056 unsigned long int idx = 0; 1057 int success, secure; 1058 int midx, flags; 1059 SV *tmpsv; 1060 1061 midx = my_maxidx(lnm) + 1; 1062 1063 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1064 /* Set up a temporary buffer for the return value; Perl will 1065 * clean it up at the next statement transition */ 1066 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1067 if (!tmpsv) return NULL; 1068 eqv = SvPVX(tmpsv); 1069 } 1070 else { 1071 /* Assume no interpreter ==> single thread */ 1072 if (__my_getenv_eqv != NULL) { 1073 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1074 } 1075 else { 1076 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1077 } 1078 eqv = __my_getenv_eqv; 1079 } 1080 1081 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1082 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) { 1083 int len; 1084 getcwd(eqv,LNM$C_NAMLENGTH); 1085 1086 len = strlen(eqv); 1087 1088 /* Get rid of "000000/ in rooted filespecs */ 1089 if (len > 7) { 1090 char * zeros; 1091 zeros = strstr(eqv, "/000000/"); 1092 if (zeros != NULL) { 1093 int mlen; 1094 mlen = len - (zeros - eqv) - 7; 1095 memmove(zeros, &zeros[7], mlen); 1096 len = len - 7; 1097 eqv[len] = '\0'; 1098 } 1099 } 1100 return eqv; 1101 } 1102 else { 1103 /* Impose security constraints only if tainting */ 1104 if (sys) { 1105 /* Impose security constraints only if tainting */ 1106 secure = PL_curinterp ? TAINTING_get : will_taint; 1107 } 1108 else { 1109 secure = 0; 1110 } 1111 1112 flags = 1113 #ifdef SECURE_INTERNAL_GETENV 1114 secure ? PERL__TRNENV_SECURE : 0 1115 #else 1116 0 1117 #endif 1118 ; 1119 1120 /* For the getenv interface we combine all the equivalence names 1121 * of a search list logical into one value to acquire a maximum 1122 * value length of 255*128 (assuming %ENV is using logicals). 1123 */ 1124 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1125 1126 /* If the name contains a semicolon-delimited index, parse it 1127 * off and make sure we only retrieve the equivalence name for 1128 * that index. */ 1129 if ((cp2 = strchr(lnm,';')) != NULL) { 1130 my_strlcpy(uplnm, lnm, cp2 - lnm + 1); 1131 idx = strtoul(cp2+1,NULL,0); 1132 lnm = uplnm; 1133 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1134 } 1135 1136 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags); 1137 1138 return success ? eqv : NULL; 1139 } 1140 1141 } /* end of my_getenv() */ 1142 /*}}}*/ 1143 1144 1145 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/ 1146 char * 1147 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys) 1148 { 1149 const char *cp1; 1150 char *buf, *cp2; 1151 unsigned long idx = 0; 1152 int midx, flags; 1153 static char *__my_getenv_len_eqv = NULL; 1154 int secure; 1155 SV *tmpsv; 1156 1157 midx = my_maxidx(lnm) + 1; 1158 1159 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1160 /* Set up a temporary buffer for the return value; Perl will 1161 * clean it up at the next statement transition */ 1162 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1163 if (!tmpsv) return NULL; 1164 buf = SvPVX(tmpsv); 1165 } 1166 else { 1167 /* Assume no interpreter ==> single thread */ 1168 if (__my_getenv_len_eqv != NULL) { 1169 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1170 } 1171 else { 1172 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1173 } 1174 buf = __my_getenv_len_eqv; 1175 } 1176 1177 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1178 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) { 1179 char * zeros; 1180 1181 getcwd(buf,LNM$C_NAMLENGTH); 1182 *len = strlen(buf); 1183 1184 /* Get rid of "000000/ in rooted filespecs */ 1185 if (*len > 7) { 1186 zeros = strstr(buf, "/000000/"); 1187 if (zeros != NULL) { 1188 int mlen; 1189 mlen = *len - (zeros - buf) - 7; 1190 memmove(zeros, &zeros[7], mlen); 1191 *len = *len - 7; 1192 buf[*len] = '\0'; 1193 } 1194 } 1195 return buf; 1196 } 1197 else { 1198 if (sys) { 1199 /* Impose security constraints only if tainting */ 1200 secure = PL_curinterp ? TAINTING_get : will_taint; 1201 } 1202 else { 1203 secure = 0; 1204 } 1205 1206 flags = 1207 #ifdef SECURE_INTERNAL_GETENV 1208 secure ? PERL__TRNENV_SECURE : 0 1209 #else 1210 0 1211 #endif 1212 ; 1213 1214 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1215 1216 if ((cp2 = strchr(lnm,';')) != NULL) { 1217 my_strlcpy(buf, lnm, cp2 - lnm + 1); 1218 idx = strtoul(cp2+1,NULL,0); 1219 lnm = buf; 1220 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1221 } 1222 1223 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags); 1224 1225 /* Get rid of "000000/ in rooted filespecs */ 1226 if (*len > 7) { 1227 char * zeros; 1228 zeros = strstr(buf, "/000000/"); 1229 if (zeros != NULL) { 1230 int mlen; 1231 mlen = *len - (zeros - buf) - 7; 1232 memmove(zeros, &zeros[7], mlen); 1233 *len = *len - 7; 1234 buf[*len] = '\0'; 1235 } 1236 } 1237 1238 return *len ? buf : NULL; 1239 } 1240 1241 } /* end of my_getenv_len() */ 1242 /*}}}*/ 1243 1244 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *); 1245 1246 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); } 1247 1248 /*{{{ void prime_env_iter() */ 1249 void 1250 prime_env_iter(void) 1251 /* Fill the %ENV associative array with all logical names we can 1252 * find, in preparation for iterating over it. 1253 */ 1254 { 1255 static int primed = 0; 1256 HV *seenhv = NULL, *envhv; 1257 SV *sv = NULL; 1258 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL; 1259 unsigned short int chan; 1260 #ifndef CLI$M_TRUSTED 1261 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */ 1262 #endif 1263 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED; 1264 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0; 1265 long int i; 1266 bool have_sym = FALSE, have_lnm = FALSE; 1267 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1268 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:"); 1269 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES"); 1270 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1271 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 1272 #if defined(PERL_IMPLICIT_CONTEXT) 1273 pTHX; 1274 #endif 1275 #if defined(USE_ITHREADS) 1276 static perl_mutex primenv_mutex; 1277 MUTEX_INIT(&primenv_mutex); 1278 #endif 1279 1280 #if defined(PERL_IMPLICIT_CONTEXT) 1281 /* We jump through these hoops because we can be called at */ 1282 /* platform-specific initialization time, which is before anything is */ 1283 /* set up--we can't even do a plain dTHX since that relies on the */ 1284 /* interpreter structure to be initialized */ 1285 if (PL_curinterp) { 1286 aTHX = PERL_GET_INTERP; 1287 } else { 1288 /* we never get here because the NULL pointer will cause the */ 1289 /* several of the routines called by this routine to access violate */ 1290 1291 /* This routine is only called by hv.c/hv_iterinit which has a */ 1292 /* context, so the real fix may be to pass it through instead of */ 1293 /* the hoops above */ 1294 aTHX = NULL; 1295 } 1296 #endif 1297 1298 if (primed || !PL_envgv) return; 1299 MUTEX_LOCK(&primenv_mutex); 1300 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; } 1301 envhv = GvHVn(PL_envgv); 1302 /* Perform a dummy fetch as an lval to insure that the hash table is 1303 * set up. Otherwise, the hv_store() will turn into a nullop. */ 1304 (void) hv_fetch(envhv,"DEFAULT",7,TRUE); 1305 1306 for (i = 0; env_tables[i]; i++) { 1307 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1308 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1; 1309 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1; 1310 } 1311 if (have_sym || have_lnm) { 1312 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM; 1313 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0)); 1314 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0)); 1315 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length)); 1316 } 1317 1318 for (i--; i >= 0; i--) { 1319 if (!str$case_blind_compare(env_tables[i],&crtlenv)) { 1320 char *start; 1321 int j; 1322 /* Start at the end, so if there is a duplicate we keep the first one. */ 1323 for (j = 0; environ[j]; j++); 1324 for (j--; j >= 0; j--) { 1325 if (!(start = strchr(environ[j],'='))) { 1326 if (ckWARN(WARN_INTERNAL)) 1327 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]); 1328 } 1329 else { 1330 start++; 1331 sv = newSVpv(start,0); 1332 SvTAINTED_on(sv); 1333 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0); 1334 } 1335 } 1336 continue; 1337 } 1338 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1339 !str$case_blind_compare(&tmpdsc,&clisym)) { 1340 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd)); 1341 cmddsc.dsc$w_length = 20; 1342 if (env_tables[i]->dsc$w_length == 12 && 1343 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) && 1344 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12); 1345 flags = defflags | CLI$M_NOLOGNAM; 1346 } 1347 else { 1348 my_strlcpy(cmd, "Show Logical *", sizeof(cmd)); 1349 if (str$case_blind_compare(env_tables[i],&fildevdsc)) { 1350 my_strlcat(cmd," /Table=", sizeof(cmd)); 1351 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd)); 1352 } 1353 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */ 1354 flags = defflags | CLI$M_NOCLISYM; 1355 } 1356 1357 /* Create a new subprocess to execute each command, to exclude the 1358 * remote possibility that someone could subvert a mbx or file used 1359 * to write multiple commands to a single subprocess. 1360 */ 1361 do { 1362 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs, 1363 0,&riseandshine,0,0,&clidsc,&clitabdsc); 1364 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */ 1365 defflags &= ~CLI$M_TRUSTED; 1366 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED)); 1367 _ckvmssts(retsts); 1368 if (!buf) Newx(buf,mbxbufsiz + 1,char); 1369 if (seenhv) SvREFCNT_dec(seenhv); 1370 seenhv = newHV(); 1371 while (1) { 1372 char *cp1, *cp2, *key; 1373 unsigned long int sts, iosb[2], retlen, keylen; 1374 U32 hash; 1375 1376 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0); 1377 if (sts & 1) sts = iosb[0] & 0xffff; 1378 if (sts == SS$_ENDOFFILE) { 1379 int wakect = 0; 1380 while (substs == 0) { sys$hiber(); wakect++;} 1381 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */ 1382 _ckvmssts(substs); 1383 break; 1384 } 1385 _ckvmssts(sts); 1386 retlen = iosb[0] >> 16; 1387 if (!retlen) continue; /* blank line */ 1388 buf[retlen] = '\0'; 1389 if (iosb[1] != subpid) { 1390 if (iosb[1]) { 1391 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf); 1392 } 1393 continue; 1394 } 1395 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL)) 1396 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf); 1397 1398 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ; 1399 if (*cp1 == '(' || /* Logical name table name */ 1400 *cp1 == '=' /* Next eqv of searchlist */) continue; 1401 if (*cp1 == '"') cp1++; 1402 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ; 1403 key = cp1; keylen = cp2 - cp1; 1404 if (keylen && hv_exists(seenhv,key,keylen)) continue; 1405 while (*cp2 && *cp2 != '=') cp2++; 1406 while (*cp2 && *cp2 == '=') cp2++; 1407 while (*cp2 && *cp2 == ' ') cp2++; 1408 if (*cp2 == '"') { /* String translation; may embed "" */ 1409 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ; 1410 cp2++; cp1--; /* Skip "" surrounding translation */ 1411 } 1412 else { /* Numeric translation */ 1413 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ; 1414 cp1--; /* stop on last non-space char */ 1415 } 1416 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) { 1417 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf); 1418 continue; 1419 } 1420 PERL_HASH(hash,key,keylen); 1421 1422 if (cp1 == cp2 && *cp2 == '.') { 1423 /* A single dot usually means an unprintable character, such as a null 1424 * to indicate a zero-length value. Get the actual value to make sure. 1425 */ 1426 char lnm[LNM$C_NAMLENGTH+1]; 1427 char eqv[MAX_DCL_SYMBOL+1]; 1428 int trnlen; 1429 strncpy(lnm, key, keylen); 1430 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0); 1431 sv = newSVpvn(eqv, strlen(eqv)); 1432 } 1433 else { 1434 sv = newSVpvn(cp2,cp1 - cp2 + 1); 1435 } 1436 1437 SvTAINTED_on(sv); 1438 hv_store(envhv,key,keylen,sv,hash); 1439 hv_store(seenhv,key,keylen,&PL_sv_yes,hash); 1440 } 1441 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */ 1442 /* get the PPFs for this process, not the subprocess */ 1443 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL}; 1444 char eqv[LNM$C_NAMLENGTH+1]; 1445 int trnlen, i; 1446 for (i = 0; ppfs[i]; i++) { 1447 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0); 1448 sv = newSVpv(eqv,trnlen); 1449 SvTAINTED_on(sv); 1450 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0); 1451 } 1452 } 1453 } 1454 primed = 1; 1455 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan)); 1456 if (buf) Safefree(buf); 1457 if (seenhv) SvREFCNT_dec(seenhv); 1458 MUTEX_UNLOCK(&primenv_mutex); 1459 return; 1460 1461 } /* end of prime_env_iter */ 1462 /*}}}*/ 1463 1464 1465 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/ 1466 /* Define or delete an element in the same "environment" as 1467 * vmstrnenv(). If an element is to be deleted, it's removed from 1468 * the first place it's found. If it's to be set, it's set in the 1469 * place designated by the first element of the table vector. 1470 * Like setenv() returns 0 for success, non-zero on error. 1471 */ 1472 int 1473 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec) 1474 { 1475 const char *cp1; 1476 char uplnm[LNM$C_NAMLENGTH], *cp2, *c; 1477 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0; 1478 int nseg = 0, j; 1479 unsigned long int retsts, usermode = PSL$C_USER; 1480 struct itmlst_3 *ile, *ilist; 1481 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm}, 1482 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 1483 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1484 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1485 $DESCRIPTOR(local,"_LOCAL"); 1486 1487 if (!lnm) { 1488 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1489 return SS$_IVLOGNAM; 1490 } 1491 1492 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 1493 *cp2 = _toupper(*cp1); 1494 if (cp1 - lnm > LNM$C_NAMLENGTH) { 1495 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1496 return SS$_IVLOGNAM; 1497 } 1498 } 1499 lnmdsc.dsc$w_length = cp1 - lnm; 1500 if (!tabvec || !*tabvec) tabvec = env_tables; 1501 1502 if (!eqv) { /* we're deleting n element */ 1503 for (curtab = 0; tabvec[curtab]; curtab++) { 1504 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) { 1505 int i; 1506 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */ 1507 if ((cp1 = strchr(environ[i],'=')) && 1508 lnmdsc.dsc$w_length == (cp1 - environ[i]) && 1509 !strncmp(environ[i],lnm,cp1 - environ[i])) { 1510 unsetenv(lnm); 1511 return 0; 1512 } 1513 } 1514 ivenv = 1; retsts = SS$_NOLOGNAM; 1515 } 1516 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 1517 !str$case_blind_compare(&tmpdsc,&clisym)) { 1518 unsigned int symtype; 1519 if (tabvec[curtab]->dsc$w_length == 12 && 1520 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) && 1521 !str$case_blind_compare(&tmpdsc,&local)) 1522 symtype = LIB$K_CLI_LOCAL_SYM; 1523 else symtype = LIB$K_CLI_GLOBAL_SYM; 1524 retsts = lib$delete_symbol(&lnmdsc,&symtype); 1525 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 1526 if (retsts == LIB$_NOSUCHSYM) continue; 1527 break; 1528 } 1529 else if (!ivlnm) { 1530 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */ 1531 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1532 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1533 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */ 1534 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1535 } 1536 } 1537 } 1538 else { /* we're defining a value */ 1539 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) { 1540 return setenv(lnm,eqv,1) ? vaxc$errno : 0; 1541 } 1542 else { 1543 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */ 1544 eqvdsc.dsc$w_length = strlen(eqv); 1545 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) && 1546 !str$case_blind_compare(&tmpdsc,&clisym)) { 1547 unsigned int symtype; 1548 if (tabvec[0]->dsc$w_length == 12 && 1549 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) && 1550 !str$case_blind_compare(&tmpdsc,&local)) 1551 symtype = LIB$K_CLI_LOCAL_SYM; 1552 else symtype = LIB$K_CLI_GLOBAL_SYM; 1553 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype); 1554 } 1555 else { 1556 if (!*eqv) eqvdsc.dsc$w_length = 1; 1557 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) { 1558 1559 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH; 1560 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) { 1561 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes", 1562 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1)); 1563 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1); 1564 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1; 1565 } 1566 1567 Newx(ilist,nseg+1,struct itmlst_3); 1568 ile = ilist; 1569 if (!ile) { 1570 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM); 1571 return SS$_INSFMEM; 1572 } 1573 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1))); 1574 1575 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) { 1576 ile->itmcode = LNM$_STRING; 1577 ile->bufadr = c; 1578 if ((j+1) == nseg) { 1579 ile->buflen = strlen(c); 1580 /* in case we are truncating one that's too long */ 1581 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH; 1582 } 1583 else { 1584 ile->buflen = LNM$C_NAMLENGTH; 1585 } 1586 } 1587 1588 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist); 1589 Safefree (ilist); 1590 } 1591 else { 1592 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0); 1593 } 1594 } 1595 } 1596 } 1597 if (!(retsts & 1)) { 1598 switch (retsts) { 1599 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM: 1600 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB: 1601 set_errno(EVMSERR); break; 1602 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 1603 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM: 1604 set_errno(EINVAL); break; 1605 case SS$_NOPRIV: 1606 set_errno(EACCES); break; 1607 default: 1608 _ckvmssts(retsts); 1609 set_errno(EVMSERR); 1610 } 1611 set_vaxc_errno(retsts); 1612 return (int) retsts || 44; /* retsts should never be 0, but just in case */ 1613 } 1614 else { 1615 /* We reset error values on success because Perl does an hv_fetch() 1616 * before each hv_store(), and if the thing we're setting didn't 1617 * previously exist, we've got a leftover error message. (Of course, 1618 * this fails in the face of 1619 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo'; 1620 * in that the error reported in $! isn't spurious, 1621 * but it's right more often than not.) 1622 */ 1623 set_errno(0); set_vaxc_errno(retsts); 1624 return 0; 1625 } 1626 1627 } /* end of vmssetenv() */ 1628 /*}}}*/ 1629 1630 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/ 1631 /* This has to be a function since there's a prototype for it in proto.h */ 1632 void 1633 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv) 1634 { 1635 if (lnm && *lnm) { 1636 int len = strlen(lnm); 1637 if (len == 7) { 1638 char uplnm[8]; 1639 int i; 1640 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]); 1641 if (!strcmp(uplnm,"DEFAULT")) { 1642 if (eqv && *eqv) my_chdir(eqv); 1643 return; 1644 } 1645 } 1646 } 1647 (void) vmssetenv(lnm,eqv,NULL); 1648 } 1649 /*}}}*/ 1650 1651 /*{{{static void vmssetuserlnm(char *name, char *eqv); */ 1652 /* vmssetuserlnm 1653 * sets a user-mode logical in the process logical name table 1654 * used for redirection of sys$error 1655 */ 1656 void 1657 Perl_vmssetuserlnm(const char *name, const char *eqv) 1658 { 1659 $DESCRIPTOR(d_tab, "LNM$PROCESS"); 1660 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 1661 unsigned long int iss, attr = LNM$M_CONFINE; 1662 unsigned char acmode = PSL$C_USER; 1663 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0}, 1664 {0, 0, 0, 0}}; 1665 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */ 1666 d_name.dsc$w_length = strlen(name); 1667 1668 lnmlst[0].buflen = strlen(eqv); 1669 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */ 1670 1671 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst); 1672 if (!(iss&1)) lib$signal(iss); 1673 } 1674 /*}}}*/ 1675 1676 1677 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/ 1678 /* my_crypt - VMS password hashing 1679 * my_crypt() provides an interface compatible with the Unix crypt() 1680 * C library function, and uses sys$hash_password() to perform VMS 1681 * password hashing. The quadword hashed password value is returned 1682 * as a NUL-terminated 8 character string. my_crypt() does not change 1683 * the case of its string arguments; in order to match the behavior 1684 * of LOGINOUT et al., alphabetic characters in both arguments must 1685 * be upcased by the caller. 1686 * 1687 * - fix me to call ACM services when available 1688 */ 1689 char * 1690 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname) 1691 { 1692 # ifndef UAI$C_PREFERRED_ALGORITHM 1693 # define UAI$C_PREFERRED_ALGORITHM 127 1694 # endif 1695 unsigned char alg = UAI$C_PREFERRED_ALGORITHM; 1696 unsigned short int salt = 0; 1697 unsigned long int sts; 1698 struct const_dsc { 1699 unsigned short int dsc$w_length; 1700 unsigned char dsc$b_type; 1701 unsigned char dsc$b_class; 1702 const char * dsc$a_pointer; 1703 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}, 1704 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1705 struct itmlst_3 uailst[3] = { 1706 { sizeof alg, UAI$_ENCRYPT, &alg, 0}, 1707 { sizeof salt, UAI$_SALT, &salt, 0}, 1708 { 0, 0, NULL, NULL}}; 1709 static char hash[9]; 1710 1711 usrdsc.dsc$w_length = strlen(usrname); 1712 usrdsc.dsc$a_pointer = usrname; 1713 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) { 1714 switch (sts) { 1715 case SS$_NOGRPPRV: case SS$_NOSYSPRV: 1716 set_errno(EACCES); 1717 break; 1718 case RMS$_RNF: 1719 set_errno(ESRCH); /* There isn't a Unix no-such-user error */ 1720 break; 1721 default: 1722 set_errno(EVMSERR); 1723 } 1724 set_vaxc_errno(sts); 1725 if (sts != RMS$_RNF) return NULL; 1726 } 1727 1728 txtdsc.dsc$w_length = strlen(textpasswd); 1729 txtdsc.dsc$a_pointer = textpasswd; 1730 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) { 1731 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL; 1732 } 1733 1734 return (char *) hash; 1735 1736 } /* end of my_crypt() */ 1737 /*}}}*/ 1738 1739 1740 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *); 1741 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *); 1742 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *); 1743 1744 /* 8.3, remove() is now broken on symbolic links */ 1745 static int rms_erase(const char * vmsname); 1746 1747 1748 /* mp_do_kill_file 1749 * A little hack to get around a bug in some implementation of remove() 1750 * that do not know how to delete a directory 1751 * 1752 * Delete any file to which user has control access, regardless of whether 1753 * delete access is explicitly allowed. 1754 * Limitations: User must have write access to parent directory. 1755 * Does not block signals or ASTs; if interrupted in midstream 1756 * may leave file with an altered ACL. 1757 * HANDLE WITH CARE! 1758 */ 1759 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/ 1760 static int 1761 mp_do_kill_file(pTHX_ const char *name, int dirflag) 1762 { 1763 char *vmsname; 1764 char *rslt; 1765 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; 1766 unsigned long int cxt = 0, aclsts, fndsts; 1767 int rmsts = -1; 1768 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1769 struct myacedef { 1770 unsigned char myace$b_length; 1771 unsigned char myace$b_type; 1772 unsigned short int myace$w_flags; 1773 unsigned long int myace$l_access; 1774 unsigned long int myace$l_ident; 1775 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 1776 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0}, 1777 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 1778 struct itmlst_3 1779 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0}, 1780 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}}, 1781 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}}, 1782 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}}, 1783 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}}, 1784 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}}; 1785 1786 /* Expand the input spec using RMS, since the CRTL remove() and 1787 * system services won't do this by themselves, so we may miss 1788 * a file "hiding" behind a logical name or search list. */ 1789 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1); 1790 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM); 1791 1792 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK); 1793 if (rslt == NULL) { 1794 PerlMem_free(vmsname); 1795 return -1; 1796 } 1797 1798 /* Erase the file */ 1799 rmsts = rms_erase(vmsname); 1800 1801 /* Did it succeed */ 1802 if ($VMS_STATUS_SUCCESS(rmsts)) { 1803 PerlMem_free(vmsname); 1804 return 0; 1805 } 1806 1807 /* If not, can changing protections help? */ 1808 if (rmsts != RMS$_PRV) { 1809 set_vaxc_errno(rmsts); 1810 PerlMem_free(vmsname); 1811 return -1; 1812 } 1813 1814 /* No, so we get our own UIC to use as a rights identifier, 1815 * and the insert an ACE at the head of the ACL which allows us 1816 * to delete the file. 1817 */ 1818 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 1819 fildsc.dsc$w_length = strlen(vmsname); 1820 fildsc.dsc$a_pointer = vmsname; 1821 cxt = 0; 1822 newace.myace$l_ident = oldace.myace$l_ident; 1823 rmsts = -1; 1824 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) { 1825 switch (aclsts) { 1826 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT: 1827 set_errno(ENOENT); break; 1828 case RMS$_DIR: 1829 set_errno(ENOTDIR); break; 1830 case RMS$_DEV: 1831 set_errno(ENODEV); break; 1832 case RMS$_SYN: case SS$_INVFILFOROP: 1833 set_errno(EINVAL); break; 1834 case RMS$_PRV: 1835 set_errno(EACCES); break; 1836 default: 1837 _ckvmssts_noperl(aclsts); 1838 } 1839 set_vaxc_errno(aclsts); 1840 PerlMem_free(vmsname); 1841 return -1; 1842 } 1843 /* Grab any existing ACEs with this identifier in case we fail */ 1844 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt); 1845 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY 1846 || fndsts == SS$_NOMOREACE ) { 1847 /* Add the new ACE . . . */ 1848 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1)) 1849 goto yourroom; 1850 1851 rmsts = rms_erase(vmsname); 1852 if ($VMS_STATUS_SUCCESS(rmsts)) { 1853 rmsts = 0; 1854 } 1855 else { 1856 rmsts = -1; 1857 /* We blew it - dir with files in it, no write priv for 1858 * parent directory, etc. Put things back the way they were. */ 1859 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1)) 1860 goto yourroom; 1861 if (fndsts & 1) { 1862 addlst[0].bufadr = &oldace; 1863 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1)) 1864 goto yourroom; 1865 } 1866 } 1867 } 1868 1869 yourroom: 1870 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0); 1871 /* We just deleted it, so of course it's not there. Some versions of 1872 * VMS seem to return success on the unlock operation anyhow (after all 1873 * the unlock is successful), but others don't. 1874 */ 1875 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL; 1876 if (aclsts & 1) aclsts = fndsts; 1877 if (!(aclsts & 1)) { 1878 set_errno(EVMSERR); 1879 set_vaxc_errno(aclsts); 1880 } 1881 1882 PerlMem_free(vmsname); 1883 return rmsts; 1884 1885 } /* end of kill_file() */ 1886 /*}}}*/ 1887 1888 1889 /*{{{int do_rmdir(char *name)*/ 1890 int 1891 Perl_do_rmdir(pTHX_ const char *name) 1892 { 1893 char * dirfile; 1894 int retval; 1895 Stat_t st; 1896 1897 /* lstat returns a VMS fileified specification of the name */ 1898 /* that is looked up, and also lets verifies that this is a directory */ 1899 1900 retval = flex_lstat(name, &st); 1901 if (retval != 0) { 1902 char * ret_spec; 1903 1904 /* Due to a historical feature, flex_stat/lstat can not see some */ 1905 /* Unix format file names that the rest of the CRTL can see */ 1906 /* Fixing that feature will cause some perl tests to fail */ 1907 /* So try this one more time. */ 1908 1909 retval = lstat(name, &st.crtl_stat); 1910 if (retval != 0) 1911 return -1; 1912 1913 /* force it to a file spec for the kill file to work. */ 1914 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL); 1915 if (ret_spec == NULL) { 1916 errno = EIO; 1917 return -1; 1918 } 1919 } 1920 1921 if (!S_ISDIR(st.st_mode)) { 1922 errno = ENOTDIR; 1923 retval = -1; 1924 } 1925 else { 1926 dirfile = st.st_devnam; 1927 1928 /* It may be possible for flex_stat to find a file and vmsify() to */ 1929 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */ 1930 /* with that case, so fail it */ 1931 if (dirfile[0] == 0) { 1932 errno = EIO; 1933 return -1; 1934 } 1935 1936 retval = mp_do_kill_file(aTHX_ dirfile, 1); 1937 } 1938 1939 return retval; 1940 1941 } /* end of do_rmdir */ 1942 /*}}}*/ 1943 1944 /* kill_file 1945 * Delete any file to which user has control access, regardless of whether 1946 * delete access is explicitly allowed. 1947 * Limitations: User must have write access to parent directory. 1948 * Does not block signals or ASTs; if interrupted in midstream 1949 * may leave file with an altered ACL. 1950 * HANDLE WITH CARE! 1951 */ 1952 /*{{{int kill_file(char *name)*/ 1953 int 1954 Perl_kill_file(pTHX_ const char *name) 1955 { 1956 char * vmsfile; 1957 Stat_t st; 1958 int rmsts; 1959 1960 /* Convert the filename to VMS format and see if it is a directory */ 1961 /* flex_lstat returns a vmsified file specification */ 1962 rmsts = flex_lstat(name, &st); 1963 if (rmsts != 0) { 1964 1965 /* Due to a historical feature, flex_stat/lstat can not see some */ 1966 /* Unix format file names that the rest of the CRTL can see when */ 1967 /* ODS-2 file specifications are in use. */ 1968 /* Fixing that feature will cause some perl tests to fail */ 1969 /* [.lib.ExtUtils.t]Manifest.t is one of them */ 1970 st.st_mode = 0; 1971 vmsfile = (char *) name; /* cast ok */ 1972 1973 } else { 1974 vmsfile = st.st_devnam; 1975 if (vmsfile[0] == 0) { 1976 /* It may be possible for flex_stat to find a file and vmsify() */ 1977 /* to fail with ODS-2 specifications. mp_do_kill_file can not */ 1978 /* deal with that case, so fail it */ 1979 errno = EIO; 1980 return -1; 1981 } 1982 } 1983 1984 /* Remove() is allowed to delete directories, according to the X/Open 1985 * specifications. 1986 * This may need special handling to work with the ACL hacks. 1987 */ 1988 if (S_ISDIR(st.st_mode)) { 1989 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1); 1990 return rmsts; 1991 } 1992 1993 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0); 1994 1995 /* Need to delete all versions ? */ 1996 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) { 1997 int i = 0; 1998 1999 /* Just use lstat() here as do not need st_dev */ 2000 /* and we know that the file is in VMS format or that */ 2001 /* because of a historical bug, flex_stat can not see the file */ 2002 while (lstat(vmsfile, (stat_t *)&st) == 0) { 2003 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0); 2004 if (rmsts != 0) 2005 break; 2006 i++; 2007 2008 /* Make sure that we do not loop forever */ 2009 if (i > 32767) { 2010 errno = EIO; 2011 rmsts = -1; 2012 break; 2013 } 2014 } 2015 } 2016 2017 return rmsts; 2018 2019 } /* end of kill_file() */ 2020 /*}}}*/ 2021 2022 2023 /*{{{int my_mkdir(char *,Mode_t)*/ 2024 int 2025 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode) 2026 { 2027 STRLEN dirlen = strlen(dir); 2028 2029 /* zero length string sometimes gives ACCVIO */ 2030 if (dirlen == 0) return -1; 2031 2032 /* CRTL mkdir() doesn't tolerate trailing /, since that implies 2033 * null file name/type. However, it's commonplace under Unix, 2034 * so we'll allow it for a gain in portability. 2035 */ 2036 if (dir[dirlen-1] == '/') { 2037 char *newdir = savepvn(dir,dirlen-1); 2038 int ret = mkdir(newdir,mode); 2039 Safefree(newdir); 2040 return ret; 2041 } 2042 else return mkdir(dir,mode); 2043 } /* end of my_mkdir */ 2044 /*}}}*/ 2045 2046 /*{{{int my_chdir(char *)*/ 2047 int 2048 Perl_my_chdir(pTHX_ const char *dir) 2049 { 2050 STRLEN dirlen = strlen(dir); 2051 const char *dir1 = dir; 2052 2053 /* POSIX says we should set ENOENT for zero length string. */ 2054 if (dirlen == 0) { 2055 SETERRNO(ENOENT, RMS$_DNF); 2056 return -1; 2057 } 2058 2059 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces. 2060 * This does not work if DECC$EFS_CHARSET is active. Hack it here 2061 * so that existing scripts do not need to be changed. 2062 */ 2063 while ((dirlen > 0) && (*dir1 == ' ')) { 2064 dir1++; 2065 dirlen--; 2066 } 2067 2068 /* some versions of CRTL chdir() doesn't tolerate trailing /, since 2069 * that implies 2070 * null file name/type. However, it's commonplace under Unix, 2071 * so we'll allow it for a gain in portability. 2072 * 2073 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active. 2074 */ 2075 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) { 2076 char *newdir; 2077 int ret; 2078 newdir = (char *)PerlMem_malloc(dirlen); 2079 if (newdir ==NULL) 2080 _ckvmssts_noperl(SS$_INSFMEM); 2081 memcpy(newdir, dir1, dirlen-1); 2082 newdir[dirlen-1] = '\0'; 2083 ret = chdir(newdir); 2084 PerlMem_free(newdir); 2085 return ret; 2086 } 2087 else return chdir(dir1); 2088 } /* end of my_chdir */ 2089 /*}}}*/ 2090 2091 2092 /*{{{int my_chmod(char *, mode_t)*/ 2093 int 2094 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode) 2095 { 2096 Stat_t st; 2097 int ret = -1; 2098 char * changefile; 2099 STRLEN speclen = strlen(file_spec); 2100 2101 /* zero length string sometimes gives ACCVIO */ 2102 if (speclen == 0) return -1; 2103 2104 /* some versions of CRTL chmod() doesn't tolerate trailing /, since 2105 * that implies null file name/type. However, it's commonplace under Unix, 2106 * so we'll allow it for a gain in portability. 2107 * 2108 * Tests are showing that chmod() on VMS 8.3 is only accepting directories 2109 * in VMS file.dir notation. 2110 */ 2111 changefile = (char *) file_spec; /* cast ok */ 2112 ret = flex_lstat(file_spec, &st); 2113 if (ret != 0) { 2114 2115 /* Due to a historical feature, flex_stat/lstat can not see some */ 2116 /* Unix format file names that the rest of the CRTL can see when */ 2117 /* ODS-2 file specifications are in use. */ 2118 /* Fixing that feature will cause some perl tests to fail */ 2119 /* [.lib.ExtUtils.t]Manifest.t is one of them */ 2120 st.st_mode = 0; 2121 2122 } else { 2123 /* It may be possible to get here with nothing in st_devname */ 2124 /* chmod still may work though */ 2125 if (st.st_devnam[0] != 0) { 2126 changefile = st.st_devnam; 2127 } 2128 } 2129 ret = chmod(changefile, mode); 2130 return ret; 2131 } /* end of my_chmod */ 2132 /*}}}*/ 2133 2134 2135 /*{{{FILE *my_tmpfile()*/ 2136 FILE * 2137 my_tmpfile(void) 2138 { 2139 FILE *fp; 2140 char *cp; 2141 2142 if ((fp = tmpfile())) return fp; 2143 2144 cp = (char *)PerlMem_malloc(L_tmpnam+24); 2145 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 2146 2147 if (decc_filename_unix_only == 0) 2148 strcpy(cp,"Sys$Scratch:"); 2149 else 2150 strcpy(cp,"/tmp/"); 2151 tmpnam(cp+strlen(cp)); 2152 strcat(cp,".Perltmp"); 2153 fp = fopen(cp,"w+","fop=dlt"); 2154 PerlMem_free(cp); 2155 return fp; 2156 } 2157 /*}}}*/ 2158 2159 2160 /* 2161 * The C RTL's sigaction fails to check for invalid signal numbers so we 2162 * help it out a bit. The docs are correct, but the actual routine doesn't 2163 * do what the docs say it will. 2164 */ 2165 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/ 2166 int 2167 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act, 2168 struct sigaction* oact) 2169 { 2170 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) { 2171 SETERRNO(EINVAL, SS$_INVARG); 2172 return -1; 2173 } 2174 return sigaction(sig, act, oact); 2175 } 2176 /*}}}*/ 2177 2178 #include <errnodef.h> 2179 2180 /* We implement our own kill() using the undocumented system service 2181 sys$sigprc for one of two reasons: 2182 2183 1.) If the kill() in an older CRTL uses sys$forcex, causing the 2184 target process to do a sys$exit, which usually can't be handled 2185 gracefully...certainly not by Perl and the %SIG{} mechanism. 2186 2187 2.) If the kill() in the CRTL can't be called from a signal 2188 handler without disappearing into the ether, i.e., the signal 2189 it purportedly sends is never trapped. Still true as of VMS 7.3. 2190 2191 sys$sigprc has the same parameters as sys$forcex, but throws an exception 2192 in the target process rather than calling sys$exit. 2193 2194 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg 2195 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't 2196 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc 2197 with condition codes C$_SIG0+nsig*8, catching the exception on the 2198 target process and resignaling with appropriate arguments. 2199 2200 But we don't have that VMS 7.0+ exception handler, so if you 2201 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well. 2202 2203 Also note that SIGTERM is listed in the docs as being "unimplemented", 2204 yet always seems to be signaled with a VMS condition code of 4 (and 2205 correctly handled for that code). So we hardwire it in. 2206 2207 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal 2208 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather 2209 than signalling with an unrecognized (and unhandled by CRTL) code. 2210 */ 2211 2212 #define _MY_SIG_MAX 28 2213 2214 static unsigned int 2215 Perl_sig_to_vmscondition_int(int sig) 2216 { 2217 static unsigned int sig_code[_MY_SIG_MAX+1] = 2218 { 2219 0, /* 0 ZERO */ 2220 SS$_HANGUP, /* 1 SIGHUP */ 2221 SS$_CONTROLC, /* 2 SIGINT */ 2222 SS$_CONTROLY, /* 3 SIGQUIT */ 2223 SS$_RADRMOD, /* 4 SIGILL */ 2224 SS$_BREAK, /* 5 SIGTRAP */ 2225 SS$_OPCCUS, /* 6 SIGABRT */ 2226 SS$_COMPAT, /* 7 SIGEMT */ 2227 SS$_HPARITH, /* 8 SIGFPE AXP */ 2228 SS$_ABORT, /* 9 SIGKILL */ 2229 SS$_ACCVIO, /* 10 SIGBUS */ 2230 SS$_ACCVIO, /* 11 SIGSEGV */ 2231 SS$_BADPARAM, /* 12 SIGSYS */ 2232 SS$_NOMBX, /* 13 SIGPIPE */ 2233 SS$_ASTFLT, /* 14 SIGALRM */ 2234 4, /* 15 SIGTERM */ 2235 0, /* 16 SIGUSR1 */ 2236 0, /* 17 SIGUSR2 */ 2237 0, /* 18 */ 2238 0, /* 19 */ 2239 0, /* 20 SIGCHLD */ 2240 0, /* 21 SIGCONT */ 2241 0, /* 22 SIGSTOP */ 2242 0, /* 23 SIGTSTP */ 2243 0, /* 24 SIGTTIN */ 2244 0, /* 25 SIGTTOU */ 2245 0, /* 26 */ 2246 0, /* 27 */ 2247 0 /* 28 SIGWINCH */ 2248 }; 2249 2250 static int initted = 0; 2251 if (!initted) { 2252 initted = 1; 2253 sig_code[16] = C$_SIGUSR1; 2254 sig_code[17] = C$_SIGUSR2; 2255 sig_code[20] = C$_SIGCHLD; 2256 sig_code[28] = C$_SIGWINCH; 2257 } 2258 2259 if (sig < _SIG_MIN) return 0; 2260 if (sig > _MY_SIG_MAX) return 0; 2261 return sig_code[sig]; 2262 } 2263 2264 unsigned int 2265 Perl_sig_to_vmscondition(int sig) 2266 { 2267 #ifdef SS$_DEBUG 2268 if (vms_debug_on_exception != 0) 2269 lib$signal(SS$_DEBUG); 2270 #endif 2271 return Perl_sig_to_vmscondition_int(sig); 2272 } 2273 2274 2275 #ifdef KILL_BY_SIGPRC 2276 #define sys$sigprc SYS$SIGPRC 2277 #ifdef __cplusplus 2278 extern "C" { 2279 #endif 2280 int sys$sigprc(unsigned int *pidadr, 2281 struct dsc$descriptor_s *prcname, 2282 unsigned int code); 2283 #ifdef __cplusplus 2284 } 2285 #endif 2286 2287 int 2288 Perl_my_kill(int pid, int sig) 2289 { 2290 int iss; 2291 unsigned int code; 2292 2293 /* sig 0 means validate the PID */ 2294 /*------------------------------*/ 2295 if (sig == 0) { 2296 const unsigned long int jpicode = JPI$_PID; 2297 pid_t ret_pid; 2298 int status; 2299 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL); 2300 if ($VMS_STATUS_SUCCESS(status)) 2301 return 0; 2302 switch (status) { 2303 case SS$_NOSUCHNODE: 2304 case SS$_UNREACHABLE: 2305 case SS$_NONEXPR: 2306 errno = ESRCH; 2307 break; 2308 case SS$_NOPRIV: 2309 errno = EPERM; 2310 break; 2311 default: 2312 errno = EVMSERR; 2313 } 2314 vaxc$errno=status; 2315 return -1; 2316 } 2317 2318 code = Perl_sig_to_vmscondition_int(sig); 2319 2320 if (!code) { 2321 SETERRNO(EINVAL, SS$_BADPARAM); 2322 return -1; 2323 } 2324 2325 /* Per official UNIX specification: If pid = 0, or negative then 2326 * signals are to be sent to multiple processes. 2327 * pid = 0 - all processes in group except ones that the system exempts 2328 * pid = -1 - all processes except ones that the system exempts 2329 * pid = -n - all processes in group (abs(n)) except ... 2330 * 2331 * Handle these via killpg, which is redundant for the -n case, since OP_KILL 2332 * in doio.c already does that. killpg currently does not support the -1 case. 2333 */ 2334 2335 if (pid <= 0) { 2336 return killpg(-pid, sig); 2337 } 2338 2339 iss = sys$sigprc((unsigned int *)&pid,0,code); 2340 if (iss&1) return 0; 2341 2342 switch (iss) { 2343 case SS$_NOPRIV: 2344 set_errno(EPERM); break; 2345 case SS$_NONEXPR: 2346 case SS$_NOSUCHNODE: 2347 case SS$_UNREACHABLE: 2348 set_errno(ESRCH); break; 2349 case SS$_INSFMEM: 2350 set_errno(ENOMEM); break; 2351 default: 2352 _ckvmssts_noperl(iss); 2353 set_errno(EVMSERR); 2354 } 2355 set_vaxc_errno(iss); 2356 2357 return -1; 2358 } 2359 #endif 2360 2361 int 2362 Perl_my_killpg(pid_t master_pid, int signum) 2363 { 2364 int pid, status, i; 2365 unsigned long int jpi_context; 2366 unsigned short int iosb[4]; 2367 struct itmlst_3 il3[3]; 2368 2369 /* All processes on the system? Seems dangerous, but it looks 2370 * like we could implement this pretty easily with a wildcard 2371 * input to sys$process_scan. 2372 */ 2373 if (master_pid == -1) { 2374 SETERRNO(ENOTSUP, SS$_UNSUPPORTED); 2375 return -1; 2376 } 2377 2378 /* All processes in the current process group; find the master 2379 * pid for the current process. 2380 */ 2381 if (master_pid == 0) { 2382 i = 0; 2383 il3[i].buflen = sizeof( int ); 2384 il3[i].itmcode = JPI$_MASTER_PID; 2385 il3[i].bufadr = &master_pid; 2386 il3[i++].retlen = NULL; 2387 2388 il3[i].buflen = 0; 2389 il3[i].itmcode = 0; 2390 il3[i].bufadr = NULL; 2391 il3[i++].retlen = NULL; 2392 2393 status = sys$getjpiw(EFN$C_ENF, NULL, NULL, il3, iosb, NULL, 0); 2394 if ($VMS_STATUS_SUCCESS(status)) 2395 status = iosb[0]; 2396 2397 switch (status) { 2398 case SS$_NORMAL: 2399 break; 2400 case SS$_NOPRIV: 2401 case SS$_SUSPENDED: 2402 SETERRNO(EPERM, status); 2403 break; 2404 case SS$_NOMOREPROC: 2405 case SS$_NONEXPR: 2406 case SS$_NOSUCHNODE: 2407 case SS$_UNREACHABLE: 2408 SETERRNO(ESRCH, status); 2409 break; 2410 case SS$_ACCVIO: 2411 case SS$_BADPARAM: 2412 SETERRNO(EINVAL, status); 2413 break; 2414 default: 2415 SETERRNO(EVMSERR, status); 2416 } 2417 if (!$VMS_STATUS_SUCCESS(status)) 2418 return -1; 2419 } 2420 2421 /* Set up a process context for those processes we will scan 2422 * with sys$getjpiw. Ask for all processes belonging to the 2423 * master pid. 2424 */ 2425 2426 i = 0; 2427 il3[i].buflen = 0; 2428 il3[i].itmcode = PSCAN$_MASTER_PID; 2429 il3[i].bufadr = (void *)master_pid; 2430 il3[i++].retlen = NULL; 2431 2432 il3[i].buflen = 0; 2433 il3[i].itmcode = 0; 2434 il3[i].bufadr = NULL; 2435 il3[i++].retlen = NULL; 2436 2437 status = sys$process_scan(&jpi_context, il3); 2438 switch (status) { 2439 case SS$_NORMAL: 2440 break; 2441 case SS$_ACCVIO: 2442 case SS$_BADPARAM: 2443 case SS$_IVBUFLEN: 2444 case SS$_IVSSRQ: 2445 SETERRNO(EINVAL, status); 2446 break; 2447 default: 2448 SETERRNO(EVMSERR, status); 2449 } 2450 if (!$VMS_STATUS_SUCCESS(status)) 2451 return -1; 2452 2453 i = 0; 2454 il3[i].buflen = sizeof(int); 2455 il3[i].itmcode = JPI$_PID; 2456 il3[i].bufadr = &pid; 2457 il3[i++].retlen = NULL; 2458 2459 il3[i].buflen = 0; 2460 il3[i].itmcode = 0; 2461 il3[i].bufadr = NULL; 2462 il3[i++].retlen = NULL; 2463 2464 /* Loop through the processes matching our specified criteria 2465 */ 2466 2467 while (1) { 2468 /* Find the next process... 2469 */ 2470 status = sys$getjpiw( EFN$C_ENF, &jpi_context, NULL, il3, iosb, NULL, 0); 2471 if ($VMS_STATUS_SUCCESS(status)) status = iosb[0]; 2472 2473 switch (status) { 2474 case SS$_NORMAL: 2475 if (kill(pid, signum) == -1) 2476 break; 2477 2478 continue; /* next process */ 2479 case SS$_NOPRIV: 2480 case SS$_SUSPENDED: 2481 SETERRNO(EPERM, status); 2482 break; 2483 case SS$_NOMOREPROC: 2484 break; 2485 case SS$_NONEXPR: 2486 case SS$_NOSUCHNODE: 2487 case SS$_UNREACHABLE: 2488 SETERRNO(ESRCH, status); 2489 break; 2490 case SS$_ACCVIO: 2491 case SS$_BADPARAM: 2492 SETERRNO(EINVAL, status); 2493 break; 2494 default: 2495 SETERRNO(EVMSERR, status); 2496 } 2497 2498 if (!$VMS_STATUS_SUCCESS(status)) 2499 break; 2500 } 2501 2502 /* Release context-related resources. 2503 */ 2504 (void) sys$process_scan(&jpi_context); 2505 2506 if (status != SS$_NOMOREPROC) 2507 return -1; 2508 2509 return 0; 2510 } 2511 2512 /* Routine to convert a VMS status code to a UNIX status code. 2513 ** More tricky than it appears because of conflicting conventions with 2514 ** existing code. 2515 ** 2516 ** VMS status codes are a bit mask, with the least significant bit set for 2517 ** success. 2518 ** 2519 ** Special UNIX status of EVMSERR indicates that no translation is currently 2520 ** available, and programs should check the VMS status code. 2521 ** 2522 ** Programs compiled with _POSIX_EXIT have a special encoding that requires 2523 ** decoding. 2524 */ 2525 2526 #ifndef C_FACILITY_NO 2527 #define C_FACILITY_NO 0x350000 2528 #endif 2529 #ifndef DCL_IVVERB 2530 #define DCL_IVVERB 0x38090 2531 #endif 2532 2533 int 2534 Perl_vms_status_to_unix(int vms_status, int child_flag) 2535 { 2536 int facility; 2537 int fac_sp; 2538 int msg_no; 2539 int msg_status; 2540 int unix_status; 2541 2542 /* Assume the best or the worst */ 2543 if (vms_status & STS$M_SUCCESS) 2544 unix_status = 0; 2545 else 2546 unix_status = EVMSERR; 2547 2548 msg_status = vms_status & ~STS$M_CONTROL; 2549 2550 facility = vms_status & STS$M_FAC_NO; 2551 fac_sp = vms_status & STS$M_FAC_SP; 2552 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY); 2553 2554 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) { 2555 switch(msg_no) { 2556 case SS$_NORMAL: 2557 unix_status = 0; 2558 break; 2559 case SS$_ACCVIO: 2560 unix_status = EFAULT; 2561 break; 2562 case SS$_DEVOFFLINE: 2563 unix_status = EBUSY; 2564 break; 2565 case SS$_CLEARED: 2566 unix_status = ENOTCONN; 2567 break; 2568 case SS$_IVCHAN: 2569 case SS$_IVLOGNAM: 2570 case SS$_BADPARAM: 2571 case SS$_IVLOGTAB: 2572 case SS$_NOLOGNAM: 2573 case SS$_NOLOGTAB: 2574 case SS$_INVFILFOROP: 2575 case SS$_INVARG: 2576 case SS$_NOSUCHID: 2577 case SS$_IVIDENT: 2578 unix_status = EINVAL; 2579 break; 2580 case SS$_UNSUPPORTED: 2581 unix_status = ENOTSUP; 2582 break; 2583 case SS$_FILACCERR: 2584 case SS$_NOGRPPRV: 2585 case SS$_NOSYSPRV: 2586 unix_status = EACCES; 2587 break; 2588 case SS$_DEVICEFULL: 2589 unix_status = ENOSPC; 2590 break; 2591 case SS$_NOSUCHDEV: 2592 unix_status = ENODEV; 2593 break; 2594 case SS$_NOSUCHFILE: 2595 case SS$_NOSUCHOBJECT: 2596 unix_status = ENOENT; 2597 break; 2598 case SS$_ABORT: /* Fatal case */ 2599 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */ 2600 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */ 2601 unix_status = EINTR; 2602 break; 2603 case SS$_BUFFEROVF: 2604 unix_status = E2BIG; 2605 break; 2606 case SS$_INSFMEM: 2607 unix_status = ENOMEM; 2608 break; 2609 case SS$_NOPRIV: 2610 unix_status = EPERM; 2611 break; 2612 case SS$_NOSUCHNODE: 2613 case SS$_UNREACHABLE: 2614 unix_status = ESRCH; 2615 break; 2616 case SS$_NONEXPR: 2617 unix_status = ECHILD; 2618 break; 2619 default: 2620 if ((facility == 0) && (msg_no < 8)) { 2621 /* These are not real VMS status codes so assume that they are 2622 ** already UNIX status codes 2623 */ 2624 unix_status = msg_no; 2625 break; 2626 } 2627 } 2628 } 2629 else { 2630 /* Translate a POSIX exit code to a UNIX exit code */ 2631 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) { 2632 unix_status = (msg_no & 0x07F8) >> 3; 2633 } 2634 else { 2635 2636 /* Documented traditional behavior for handling VMS child exits */ 2637 /*--------------------------------------------------------------*/ 2638 if (child_flag != 0) { 2639 2640 /* Success / Informational return 0 */ 2641 /*----------------------------------*/ 2642 if (msg_no & STS$K_SUCCESS) 2643 return 0; 2644 2645 /* Warning returns 1 */ 2646 /*-------------------*/ 2647 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0) 2648 return 1; 2649 2650 /* Everything else pass through the severity bits */ 2651 /*------------------------------------------------*/ 2652 return (msg_no & STS$M_SEVERITY); 2653 } 2654 2655 /* Normal VMS status to ERRNO mapping attempt */ 2656 /*--------------------------------------------*/ 2657 switch(msg_status) { 2658 /* case RMS$_EOF: */ /* End of File */ 2659 case RMS$_FNF: /* File Not Found */ 2660 case RMS$_DNF: /* Dir Not Found */ 2661 unix_status = ENOENT; 2662 break; 2663 case RMS$_RNF: /* Record Not Found */ 2664 unix_status = ESRCH; 2665 break; 2666 case RMS$_DIR: 2667 unix_status = ENOTDIR; 2668 break; 2669 case RMS$_DEV: 2670 unix_status = ENODEV; 2671 break; 2672 case RMS$_IFI: 2673 case RMS$_FAC: 2674 case RMS$_ISI: 2675 unix_status = EBADF; 2676 break; 2677 case RMS$_FEX: 2678 unix_status = EEXIST; 2679 break; 2680 case RMS$_SYN: 2681 case RMS$_FNM: 2682 case LIB$_INVSTRDES: 2683 case LIB$_INVARG: 2684 case LIB$_NOSUCHSYM: 2685 case LIB$_INVSYMNAM: 2686 case DCL_IVVERB: 2687 unix_status = EINVAL; 2688 break; 2689 case CLI$_BUFOVF: 2690 case RMS$_RTB: 2691 case CLI$_TKNOVF: 2692 case CLI$_RSLOVF: 2693 unix_status = E2BIG; 2694 break; 2695 case RMS$_PRV: /* No privilege */ 2696 case RMS$_ACC: /* ACP file access failed */ 2697 case RMS$_WLK: /* Device write locked */ 2698 unix_status = EACCES; 2699 break; 2700 case RMS$_MKD: /* Failed to mark for delete */ 2701 unix_status = EPERM; 2702 break; 2703 /* case RMS$_NMF: */ /* No more files */ 2704 } 2705 } 2706 } 2707 2708 return unix_status; 2709 } 2710 2711 /* Try to guess at what VMS error status should go with a UNIX errno 2712 * value. This is hard to do as there could be many possible VMS 2713 * error statuses that caused the errno value to be set. 2714 */ 2715 2716 int 2717 Perl_unix_status_to_vms(int unix_status) 2718 { 2719 int test_unix_status; 2720 2721 /* Trivial cases first */ 2722 /*---------------------*/ 2723 if (unix_status == EVMSERR) 2724 return vaxc$errno; 2725 2726 /* Is vaxc$errno sane? */ 2727 /*---------------------*/ 2728 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0); 2729 if (test_unix_status == unix_status) 2730 return vaxc$errno; 2731 2732 /* If way out of range, must be VMS code already */ 2733 /*-----------------------------------------------*/ 2734 if (unix_status > EVMSERR) 2735 return unix_status; 2736 2737 /* If out of range, punt */ 2738 /*-----------------------*/ 2739 if (unix_status > __ERRNO_MAX) 2740 return SS$_ABORT; 2741 2742 2743 /* Ok, now we have to do it the hard way. */ 2744 /*----------------------------------------*/ 2745 switch(unix_status) { 2746 case 0: return SS$_NORMAL; 2747 case EPERM: return SS$_NOPRIV; 2748 case ENOENT: return SS$_NOSUCHOBJECT; 2749 case ESRCH: return SS$_UNREACHABLE; 2750 case EINTR: return SS$_ABORT; 2751 /* case EIO: */ 2752 /* case ENXIO: */ 2753 case E2BIG: return SS$_BUFFEROVF; 2754 /* case ENOEXEC */ 2755 case EBADF: return RMS$_IFI; 2756 case ECHILD: return SS$_NONEXPR; 2757 /* case EAGAIN */ 2758 case ENOMEM: return SS$_INSFMEM; 2759 case EACCES: return SS$_FILACCERR; 2760 case EFAULT: return SS$_ACCVIO; 2761 /* case ENOTBLK */ 2762 case EBUSY: return SS$_DEVOFFLINE; 2763 case EEXIST: return RMS$_FEX; 2764 /* case EXDEV */ 2765 case ENODEV: return SS$_NOSUCHDEV; 2766 case ENOTDIR: return RMS$_DIR; 2767 /* case EISDIR */ 2768 case EINVAL: return SS$_INVARG; 2769 /* case ENFILE */ 2770 /* case EMFILE */ 2771 /* case ENOTTY */ 2772 /* case ETXTBSY */ 2773 /* case EFBIG */ 2774 case ENOSPC: return SS$_DEVICEFULL; 2775 case ESPIPE: return LIB$_INVARG; 2776 /* case EROFS: */ 2777 /* case EMLINK: */ 2778 /* case EPIPE: */ 2779 /* case EDOM */ 2780 case ERANGE: return LIB$_INVARG; 2781 /* case EWOULDBLOCK */ 2782 /* case EINPROGRESS */ 2783 /* case EALREADY */ 2784 /* case ENOTSOCK */ 2785 /* case EDESTADDRREQ */ 2786 /* case EMSGSIZE */ 2787 /* case EPROTOTYPE */ 2788 /* case ENOPROTOOPT */ 2789 /* case EPROTONOSUPPORT */ 2790 /* case ESOCKTNOSUPPORT */ 2791 /* case EOPNOTSUPP */ 2792 /* case EPFNOSUPPORT */ 2793 /* case EAFNOSUPPORT */ 2794 /* case EADDRINUSE */ 2795 /* case EADDRNOTAVAIL */ 2796 /* case ENETDOWN */ 2797 /* case ENETUNREACH */ 2798 /* case ENETRESET */ 2799 /* case ECONNABORTED */ 2800 /* case ECONNRESET */ 2801 /* case ENOBUFS */ 2802 /* case EISCONN */ 2803 case ENOTCONN: return SS$_CLEARED; 2804 /* case ESHUTDOWN */ 2805 /* case ETOOMANYREFS */ 2806 /* case ETIMEDOUT */ 2807 /* case ECONNREFUSED */ 2808 /* case ELOOP */ 2809 /* case ENAMETOOLONG */ 2810 /* case EHOSTDOWN */ 2811 /* case EHOSTUNREACH */ 2812 /* case ENOTEMPTY */ 2813 /* case EPROCLIM */ 2814 /* case EUSERS */ 2815 /* case EDQUOT */ 2816 /* case ENOMSG */ 2817 /* case EIDRM */ 2818 /* case EALIGN */ 2819 /* case ESTALE */ 2820 /* case EREMOTE */ 2821 /* case ENOLCK */ 2822 /* case ENOSYS */ 2823 /* case EFTYPE */ 2824 /* case ECANCELED */ 2825 /* case EFAIL */ 2826 /* case EINPROG */ 2827 case ENOTSUP: 2828 return SS$_UNSUPPORTED; 2829 /* case EDEADLK */ 2830 /* case ENWAIT */ 2831 /* case EILSEQ */ 2832 /* case EBADCAT */ 2833 /* case EBADMSG */ 2834 /* case EABANDONED */ 2835 default: 2836 return SS$_ABORT; /* punt */ 2837 } 2838 } 2839 2840 2841 /* default piping mailbox size */ 2842 #define PERL_BUFSIZ 8192 2843 2844 2845 static void 2846 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc) 2847 { 2848 unsigned long int mbxbufsiz; 2849 static unsigned long int syssize = 0; 2850 unsigned long int dviitm = DVI$_DEVNAM; 2851 char csize[LNM$C_NAMLENGTH+1]; 2852 int sts; 2853 2854 if (!syssize) { 2855 unsigned long syiitm = SYI$_MAXBUF; 2856 /* 2857 * Get the SYSGEN parameter MAXBUF 2858 * 2859 * If the logical 'PERL_MBX_SIZE' is defined 2860 * use the value of the logical instead of PERL_BUFSIZ, but 2861 * keep the size between 128 and MAXBUF. 2862 * 2863 */ 2864 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0)); 2865 } 2866 2867 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) { 2868 mbxbufsiz = atoi(csize); 2869 } else { 2870 mbxbufsiz = PERL_BUFSIZ; 2871 } 2872 if (mbxbufsiz < 128) mbxbufsiz = 128; 2873 if (mbxbufsiz > syssize) mbxbufsiz = syssize; 2874 2875 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0)); 2876 2877 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length); 2878 _ckvmssts_noperl(sts); 2879 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0'; 2880 2881 } /* end of create_mbx() */ 2882 2883 2884 /*{{{ my_popen and my_pclose*/ 2885 2886 typedef struct _iosb IOSB; 2887 typedef struct _iosb* pIOSB; 2888 typedef struct _pipe Pipe; 2889 typedef struct _pipe* pPipe; 2890 typedef struct pipe_details Info; 2891 typedef struct pipe_details* pInfo; 2892 typedef struct _srqp RQE; 2893 typedef struct _srqp* pRQE; 2894 typedef struct _tochildbuf CBuf; 2895 typedef struct _tochildbuf* pCBuf; 2896 2897 struct _iosb { 2898 unsigned short status; 2899 unsigned short count; 2900 unsigned long dvispec; 2901 }; 2902 2903 #pragma member_alignment save 2904 #pragma nomember_alignment quadword 2905 struct _srqp { /* VMS self-relative queue entry */ 2906 unsigned long qptr[2]; 2907 }; 2908 #pragma member_alignment restore 2909 static RQE RQE_ZERO = {0,0}; 2910 2911 struct _tochildbuf { 2912 RQE q; 2913 int eof; 2914 unsigned short size; 2915 char *buf; 2916 }; 2917 2918 struct _pipe { 2919 RQE free; 2920 RQE wait; 2921 int fd_out; 2922 unsigned short chan_in; 2923 unsigned short chan_out; 2924 char *buf; 2925 unsigned int bufsize; 2926 IOSB iosb; 2927 IOSB iosb2; 2928 int *pipe_done; 2929 int retry; 2930 int type; 2931 int shut_on_empty; 2932 int need_wake; 2933 pPipe *home; 2934 pInfo info; 2935 pCBuf curr; 2936 pCBuf curr2; 2937 #if defined(PERL_IMPLICIT_CONTEXT) 2938 void *thx; /* Either a thread or an interpreter */ 2939 /* pointer, depending on how we're built */ 2940 #endif 2941 }; 2942 2943 2944 struct pipe_details 2945 { 2946 pInfo next; 2947 PerlIO *fp; /* file pointer to pipe mailbox */ 2948 int useFILE; /* using stdio, not perlio */ 2949 int pid; /* PID of subprocess */ 2950 int mode; /* == 'r' if pipe open for reading */ 2951 int done; /* subprocess has completed */ 2952 int waiting; /* waiting for completion/closure */ 2953 int closing; /* my_pclose is closing this pipe */ 2954 unsigned long completion; /* termination status of subprocess */ 2955 pPipe in; /* pipe in to sub */ 2956 pPipe out; /* pipe out of sub */ 2957 pPipe err; /* pipe of sub's sys$error */ 2958 int in_done; /* true when in pipe finished */ 2959 int out_done; 2960 int err_done; 2961 unsigned short xchan; /* channel to debug xterm */ 2962 unsigned short xchan_valid; /* channel is assigned */ 2963 }; 2964 2965 struct exit_control_block 2966 { 2967 struct exit_control_block *flink; 2968 unsigned long int (*exit_routine)(void); 2969 unsigned long int arg_count; 2970 unsigned long int *status_address; 2971 unsigned long int exit_status; 2972 }; 2973 2974 typedef struct _closed_pipes Xpipe; 2975 typedef struct _closed_pipes* pXpipe; 2976 2977 struct _closed_pipes { 2978 int pid; /* PID of subprocess */ 2979 unsigned long completion; /* termination status of subprocess */ 2980 }; 2981 #define NKEEPCLOSED 50 2982 static Xpipe closed_list[NKEEPCLOSED]; 2983 static int closed_index = 0; 2984 static int closed_num = 0; 2985 2986 #define RETRY_DELAY "0 ::0.20" 2987 #define MAX_RETRY 50 2988 2989 static int pipe_ef = 0; /* first call to safe_popen inits these*/ 2990 static unsigned long mypid; 2991 static unsigned long delaytime[2]; 2992 2993 static pInfo open_pipes = NULL; 2994 static $DESCRIPTOR(nl_desc, "NL:"); 2995 2996 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */ 2997 2998 2999 3000 static unsigned long int 3001 pipe_exit_routine(void) 3002 { 3003 pInfo info; 3004 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT; 3005 int sts, did_stuff, j; 3006 3007 /* 3008 * Flush any pending i/o, but since we are in process run-down, be 3009 * careful about referencing PerlIO structures that may already have 3010 * been deallocated. We may not even have an interpreter anymore. 3011 */ 3012 info = open_pipes; 3013 while (info) { 3014 if (info->fp) { 3015 #if defined(PERL_IMPLICIT_CONTEXT) 3016 /* We need to use the Perl context of the thread that created */ 3017 /* the pipe. */ 3018 pTHX; 3019 if (info->err) 3020 aTHX = info->err->thx; 3021 else if (info->out) 3022 aTHX = info->out->thx; 3023 else if (info->in) 3024 aTHX = info->in->thx; 3025 #endif 3026 if (!info->useFILE 3027 #if defined(USE_ITHREADS) 3028 && my_perl 3029 #endif 3030 #ifdef USE_PERLIO 3031 && PL_perlio_fd_refcnt 3032 #endif 3033 ) 3034 PerlIO_flush(info->fp); 3035 else 3036 fflush((FILE *)info->fp); 3037 } 3038 info = info->next; 3039 } 3040 3041 /* 3042 next we try sending an EOF...ignore if doesn't work, make sure we 3043 don't hang 3044 */ 3045 did_stuff = 0; 3046 info = open_pipes; 3047 3048 while (info) { 3049 _ckvmssts_noperl(sys$setast(0)); 3050 if (info->in && !info->in->shut_on_empty) { 3051 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 3052 0, 0, 0, 0, 0, 0)); 3053 info->waiting = 1; 3054 did_stuff = 1; 3055 } 3056 _ckvmssts_noperl(sys$setast(1)); 3057 info = info->next; 3058 } 3059 3060 /* wait for EOF to have effect, up to ~ 30 sec [default] */ 3061 3062 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 3063 int nwait = 0; 3064 3065 info = open_pipes; 3066 while (info) { 3067 _ckvmssts_noperl(sys$setast(0)); 3068 if (info->waiting && info->done) 3069 info->waiting = 0; 3070 nwait += info->waiting; 3071 _ckvmssts_noperl(sys$setast(1)); 3072 info = info->next; 3073 } 3074 if (!nwait) break; 3075 sleep(1); 3076 } 3077 3078 did_stuff = 0; 3079 info = open_pipes; 3080 while (info) { 3081 _ckvmssts_noperl(sys$setast(0)); 3082 if (!info->done) { /* Tap them gently on the shoulder . . .*/ 3083 sts = sys$forcex(&info->pid,0,&abort); 3084 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 3085 did_stuff = 1; 3086 } 3087 _ckvmssts_noperl(sys$setast(1)); 3088 info = info->next; 3089 } 3090 3091 /* again, wait for effect */ 3092 3093 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 3094 int nwait = 0; 3095 3096 info = open_pipes; 3097 while (info) { 3098 _ckvmssts_noperl(sys$setast(0)); 3099 if (info->waiting && info->done) 3100 info->waiting = 0; 3101 nwait += info->waiting; 3102 _ckvmssts_noperl(sys$setast(1)); 3103 info = info->next; 3104 } 3105 if (!nwait) break; 3106 sleep(1); 3107 } 3108 3109 info = open_pipes; 3110 while (info) { 3111 _ckvmssts_noperl(sys$setast(0)); 3112 if (!info->done) { /* We tried to be nice . . . */ 3113 sts = sys$delprc(&info->pid,0); 3114 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 3115 info->done = 1; /* sys$delprc is as done as we're going to get. */ 3116 } 3117 _ckvmssts_noperl(sys$setast(1)); 3118 info = info->next; 3119 } 3120 3121 while(open_pipes) { 3122 3123 #if defined(PERL_IMPLICIT_CONTEXT) 3124 /* We need to use the Perl context of the thread that created */ 3125 /* the pipe. */ 3126 pTHX; 3127 if (open_pipes->err) 3128 aTHX = open_pipes->err->thx; 3129 else if (open_pipes->out) 3130 aTHX = open_pipes->out->thx; 3131 else if (open_pipes->in) 3132 aTHX = open_pipes->in->thx; 3133 #endif 3134 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno; 3135 else if (!(sts & 1)) retsts = sts; 3136 } 3137 return retsts; 3138 } 3139 3140 static struct exit_control_block pipe_exitblock = 3141 {(struct exit_control_block *) 0, 3142 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0}; 3143 3144 static void pipe_mbxtofd_ast(pPipe p); 3145 static void pipe_tochild1_ast(pPipe p); 3146 static void pipe_tochild2_ast(pPipe p); 3147 3148 static void 3149 popen_completion_ast(pInfo info) 3150 { 3151 pInfo i = open_pipes; 3152 int iss; 3153 3154 info->completion &= 0x0FFFFFFF; /* strip off "control" field */ 3155 closed_list[closed_index].pid = info->pid; 3156 closed_list[closed_index].completion = info->completion; 3157 closed_index++; 3158 if (closed_index == NKEEPCLOSED) 3159 closed_index = 0; 3160 closed_num++; 3161 3162 while (i) { 3163 if (i == info) break; 3164 i = i->next; 3165 } 3166 if (!i) return; /* unlinked, probably freed too */ 3167 3168 info->done = TRUE; 3169 3170 /* 3171 Writing to subprocess ... 3172 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe 3173 3174 chan_out may be waiting for "done" flag, or hung waiting 3175 for i/o completion to child...cancel the i/o. This will 3176 put it into "snarf mode" (done but no EOF yet) that discards 3177 input. 3178 3179 Output from subprocess (stdout, stderr) needs to be flushed and 3180 shut down. We try sending an EOF, but if the mbx is full the pipe 3181 routine should still catch the "shut_on_empty" flag, telling it to 3182 use immediate-style reads so that "mbx empty" -> EOF. 3183 3184 3185 */ 3186 if (info->in && !info->in_done) { /* only for mode=w */ 3187 if (info->in->shut_on_empty && info->in->need_wake) { 3188 info->in->need_wake = FALSE; 3189 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0)); 3190 } else { 3191 _ckvmssts_noperl(sys$cancel(info->in->chan_out)); 3192 } 3193 } 3194 3195 if (info->out && !info->out_done) { /* were we also piping output? */ 3196 info->out->shut_on_empty = TRUE; 3197 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3198 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3199 _ckvmssts_noperl(iss); 3200 } 3201 3202 if (info->err && !info->err_done) { /* we were piping stderr */ 3203 info->err->shut_on_empty = TRUE; 3204 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3205 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3206 _ckvmssts_noperl(iss); 3207 } 3208 _ckvmssts_noperl(sys$setef(pipe_ef)); 3209 3210 } 3211 3212 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd); 3213 static void vms_execfree(struct dsc$descriptor_s *vmscmd); 3214 static void pipe_infromchild_ast(pPipe p); 3215 3216 /* 3217 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate 3218 inside an AST routine without worrying about reentrancy and which Perl 3219 memory allocator is being used. 3220 3221 We read data and queue up the buffers, then spit them out one at a 3222 time to the output mailbox when the output mailbox is ready for one. 3223 3224 */ 3225 #define INITIAL_TOCHILDQUEUE 2 3226 3227 static pPipe 3228 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx) 3229 { 3230 pPipe p; 3231 pCBuf b; 3232 char mbx1[64], mbx2[64]; 3233 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3234 DSC$K_CLASS_S, mbx1}, 3235 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3236 DSC$K_CLASS_S, mbx2}; 3237 unsigned int dviitm = DVI$_DEVBUFSIZ; 3238 int j, n; 3239 3240 n = sizeof(Pipe); 3241 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3242 3243 create_mbx(&p->chan_in , &d_mbx1); 3244 create_mbx(&p->chan_out, &d_mbx2); 3245 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3246 3247 p->buf = 0; 3248 p->shut_on_empty = FALSE; 3249 p->need_wake = FALSE; 3250 p->type = 0; 3251 p->retry = 0; 3252 p->iosb.status = SS$_NORMAL; 3253 p->iosb2.status = SS$_NORMAL; 3254 p->free = RQE_ZERO; 3255 p->wait = RQE_ZERO; 3256 p->curr = 0; 3257 p->curr2 = 0; 3258 p->info = 0; 3259 #ifdef PERL_IMPLICIT_CONTEXT 3260 p->thx = aTHX; 3261 #endif 3262 3263 n = sizeof(CBuf) + p->bufsize; 3264 3265 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) { 3266 _ckvmssts_noperl(lib$get_vm(&n, &b)); 3267 b->buf = (char *) b + sizeof(CBuf); 3268 _ckvmssts_noperl(lib$insqhi(b, &p->free)); 3269 } 3270 3271 pipe_tochild2_ast(p); 3272 pipe_tochild1_ast(p); 3273 strcpy(wmbx, mbx1); 3274 strcpy(rmbx, mbx2); 3275 return p; 3276 } 3277 3278 /* reads the MBX Perl is writing, and queues */ 3279 3280 static void 3281 pipe_tochild1_ast(pPipe p) 3282 { 3283 pCBuf b = p->curr; 3284 int iss = p->iosb.status; 3285 int eof = (iss == SS$_ENDOFFILE); 3286 int sts; 3287 #ifdef PERL_IMPLICIT_CONTEXT 3288 pTHX = p->thx; 3289 #endif 3290 3291 if (p->retry) { 3292 if (eof) { 3293 p->shut_on_empty = TRUE; 3294 b->eof = TRUE; 3295 _ckvmssts_noperl(sys$dassgn(p->chan_in)); 3296 } else { 3297 _ckvmssts_noperl(iss); 3298 } 3299 3300 b->eof = eof; 3301 b->size = p->iosb.count; 3302 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait)); 3303 if (p->need_wake) { 3304 p->need_wake = FALSE; 3305 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0)); 3306 } 3307 } else { 3308 p->retry = 1; /* initial call */ 3309 } 3310 3311 if (eof) { /* flush the free queue, return when done */ 3312 int n = sizeof(CBuf) + p->bufsize; 3313 while (1) { 3314 iss = lib$remqti(&p->free, &b); 3315 if (iss == LIB$_QUEWASEMP) return; 3316 _ckvmssts_noperl(iss); 3317 _ckvmssts_noperl(lib$free_vm(&n, &b)); 3318 } 3319 } 3320 3321 iss = lib$remqti(&p->free, &b); 3322 if (iss == LIB$_QUEWASEMP) { 3323 int n = sizeof(CBuf) + p->bufsize; 3324 _ckvmssts_noperl(lib$get_vm(&n, &b)); 3325 b->buf = (char *) b + sizeof(CBuf); 3326 } else { 3327 _ckvmssts_noperl(iss); 3328 } 3329 3330 p->curr = b; 3331 iss = sys$qio(0,p->chan_in, 3332 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0), 3333 &p->iosb, 3334 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0); 3335 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL; 3336 _ckvmssts_noperl(iss); 3337 } 3338 3339 3340 /* writes queued buffers to output, waits for each to complete before 3341 doing the next */ 3342 3343 static void 3344 pipe_tochild2_ast(pPipe p) 3345 { 3346 pCBuf b = p->curr2; 3347 int iss = p->iosb2.status; 3348 int n = sizeof(CBuf) + p->bufsize; 3349 int done = (p->info && p->info->done) || 3350 iss == SS$_CANCEL || iss == SS$_ABORT; 3351 #if defined(PERL_IMPLICIT_CONTEXT) 3352 pTHX = p->thx; 3353 #endif 3354 3355 do { 3356 if (p->type) { /* type=1 has old buffer, dispose */ 3357 if (p->shut_on_empty) { 3358 _ckvmssts_noperl(lib$free_vm(&n, &b)); 3359 } else { 3360 _ckvmssts_noperl(lib$insqhi(b, &p->free)); 3361 } 3362 p->type = 0; 3363 } 3364 3365 iss = lib$remqti(&p->wait, &b); 3366 if (iss == LIB$_QUEWASEMP) { 3367 if (p->shut_on_empty) { 3368 if (done) { 3369 _ckvmssts_noperl(sys$dassgn(p->chan_out)); 3370 *p->pipe_done = TRUE; 3371 _ckvmssts_noperl(sys$setef(pipe_ef)); 3372 } else { 3373 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, 3374 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3375 } 3376 return; 3377 } 3378 p->need_wake = TRUE; 3379 return; 3380 } 3381 _ckvmssts_noperl(iss); 3382 p->type = 1; 3383 } while (done); 3384 3385 3386 p->curr2 = b; 3387 if (b->eof) { 3388 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, 3389 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3390 } else { 3391 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK, 3392 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0)); 3393 } 3394 3395 return; 3396 3397 } 3398 3399 3400 static pPipe 3401 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx) 3402 { 3403 pPipe p; 3404 char mbx1[64], mbx2[64]; 3405 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3406 DSC$K_CLASS_S, mbx1}, 3407 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3408 DSC$K_CLASS_S, mbx2}; 3409 unsigned int dviitm = DVI$_DEVBUFSIZ; 3410 3411 int n = sizeof(Pipe); 3412 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3413 create_mbx(&p->chan_in , &d_mbx1); 3414 create_mbx(&p->chan_out, &d_mbx2); 3415 3416 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3417 n = p->bufsize * sizeof(char); 3418 _ckvmssts_noperl(lib$get_vm(&n, &p->buf)); 3419 p->shut_on_empty = FALSE; 3420 p->info = 0; 3421 p->type = 0; 3422 p->iosb.status = SS$_NORMAL; 3423 #if defined(PERL_IMPLICIT_CONTEXT) 3424 p->thx = aTHX; 3425 #endif 3426 pipe_infromchild_ast(p); 3427 3428 strcpy(wmbx, mbx1); 3429 strcpy(rmbx, mbx2); 3430 return p; 3431 } 3432 3433 static void 3434 pipe_infromchild_ast(pPipe p) 3435 { 3436 int iss = p->iosb.status; 3437 int eof = (iss == SS$_ENDOFFILE); 3438 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0)); 3439 int kideof = (eof && (p->iosb.dvispec == p->info->pid)); 3440 #if defined(PERL_IMPLICIT_CONTEXT) 3441 pTHX = p->thx; 3442 #endif 3443 3444 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */ 3445 _ckvmssts_noperl(sys$dassgn(p->chan_out)); 3446 p->chan_out = 0; 3447 } 3448 3449 /* read completed: 3450 input shutdown if EOF from self (done or shut_on_empty) 3451 output shutdown if closing flag set (my_pclose) 3452 send data/eof from child or eof from self 3453 otherwise, re-read (snarf of data from child) 3454 */ 3455 3456 if (p->type == 1) { 3457 p->type = 0; 3458 if (myeof && p->chan_in) { /* input shutdown */ 3459 _ckvmssts_noperl(sys$dassgn(p->chan_in)); 3460 p->chan_in = 0; 3461 } 3462 3463 if (p->chan_out) { 3464 if (myeof || kideof) { /* pass EOF to parent */ 3465 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb, 3466 pipe_infromchild_ast, p, 3467 0, 0, 0, 0, 0, 0)); 3468 return; 3469 } else if (eof) { /* eat EOF --- fall through to read*/ 3470 3471 } else { /* transmit data */ 3472 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb, 3473 pipe_infromchild_ast,p, 3474 p->buf, p->iosb.count, 0, 0, 0, 0)); 3475 return; 3476 } 3477 } 3478 } 3479 3480 /* everything shut? flag as done */ 3481 3482 if (!p->chan_in && !p->chan_out) { 3483 *p->pipe_done = TRUE; 3484 _ckvmssts_noperl(sys$setef(pipe_ef)); 3485 return; 3486 } 3487 3488 /* write completed (or read, if snarfing from child) 3489 if still have input active, 3490 queue read...immediate mode if shut_on_empty so we get EOF if empty 3491 otherwise, 3492 check if Perl reading, generate EOFs as needed 3493 */ 3494 3495 if (p->type == 0) { 3496 p->type = 1; 3497 if (p->chan_in) { 3498 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb, 3499 pipe_infromchild_ast,p, 3500 p->buf, p->bufsize, 0, 0, 0, 0); 3501 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL; 3502 _ckvmssts_noperl(iss); 3503 } else { /* send EOFs for extra reads */ 3504 p->iosb.status = SS$_ENDOFFILE; 3505 p->iosb.dvispec = 0; 3506 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN, 3507 0, 0, 0, 3508 pipe_infromchild_ast, p, 0, 0, 0, 0)); 3509 } 3510 } 3511 } 3512 3513 static pPipe 3514 pipe_mbxtofd_setup(pTHX_ int fd, char *out) 3515 { 3516 pPipe p; 3517 char mbx[64]; 3518 unsigned long dviitm = DVI$_DEVBUFSIZ; 3519 struct stat s; 3520 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T, 3521 DSC$K_CLASS_S, mbx}; 3522 int n = sizeof(Pipe); 3523 3524 /* things like terminals and mbx's don't need this filter */ 3525 if (fd && fstat(fd,&s) == 0) { 3526 unsigned long devchar; 3527 char device[65]; 3528 unsigned short dev_len; 3529 struct dsc$descriptor_s d_dev; 3530 char * cptr; 3531 struct item_list_3 items[3]; 3532 int status; 3533 unsigned short dvi_iosb[4]; 3534 3535 cptr = getname(fd, out, 1); 3536 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV); 3537 d_dev.dsc$a_pointer = out; 3538 d_dev.dsc$w_length = strlen(out); 3539 d_dev.dsc$b_dtype = DSC$K_DTYPE_T; 3540 d_dev.dsc$b_class = DSC$K_CLASS_S; 3541 3542 items[0].len = 4; 3543 items[0].code = DVI$_DEVCHAR; 3544 items[0].bufadr = &devchar; 3545 items[0].retadr = NULL; 3546 items[1].len = 64; 3547 items[1].code = DVI$_FULLDEVNAM; 3548 items[1].bufadr = device; 3549 items[1].retadr = &dev_len; 3550 items[2].len = 0; 3551 items[2].code = 0; 3552 3553 status = sys$getdviw 3554 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL); 3555 _ckvmssts_noperl(status); 3556 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) { 3557 device[dev_len] = 0; 3558 3559 if (!(devchar & DEV$M_DIR)) { 3560 strcpy(out, device); 3561 return 0; 3562 } 3563 } 3564 } 3565 3566 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3567 p->fd_out = dup(fd); 3568 create_mbx(&p->chan_in, &d_mbx); 3569 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3570 n = (p->bufsize+1) * sizeof(char); 3571 _ckvmssts_noperl(lib$get_vm(&n, &p->buf)); 3572 p->shut_on_empty = FALSE; 3573 p->retry = 0; 3574 p->info = 0; 3575 strcpy(out, mbx); 3576 3577 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb, 3578 pipe_mbxtofd_ast, p, 3579 p->buf, p->bufsize, 0, 0, 0, 0)); 3580 3581 return p; 3582 } 3583 3584 static void 3585 pipe_mbxtofd_ast(pPipe p) 3586 { 3587 int iss = p->iosb.status; 3588 int done = p->info->done; 3589 int iss2; 3590 int eof = (iss == SS$_ENDOFFILE); 3591 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0)); 3592 int err = !(iss&1) && !eof; 3593 #if defined(PERL_IMPLICIT_CONTEXT) 3594 pTHX = p->thx; 3595 #endif 3596 3597 if (done && myeof) { /* end piping */ 3598 close(p->fd_out); 3599 sys$dassgn(p->chan_in); 3600 *p->pipe_done = TRUE; 3601 _ckvmssts_noperl(sys$setef(pipe_ef)); 3602 return; 3603 } 3604 3605 if (!err && !eof) { /* good data to send to file */ 3606 p->buf[p->iosb.count] = '\n'; 3607 iss2 = write(p->fd_out, p->buf, p->iosb.count+1); 3608 if (iss2 < 0) { 3609 p->retry++; 3610 if (p->retry < MAX_RETRY) { 3611 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p)); 3612 return; 3613 } 3614 } 3615 p->retry = 0; 3616 } else if (err) { 3617 _ckvmssts_noperl(iss); 3618 } 3619 3620 3621 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb, 3622 pipe_mbxtofd_ast, p, 3623 p->buf, p->bufsize, 0, 0, 0, 0); 3624 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL; 3625 _ckvmssts_noperl(iss); 3626 } 3627 3628 3629 typedef struct _pipeloc PLOC; 3630 typedef struct _pipeloc* pPLOC; 3631 3632 struct _pipeloc { 3633 pPLOC next; 3634 char dir[NAM$C_MAXRSS+1]; 3635 }; 3636 static pPLOC head_PLOC = 0; 3637 3638 void 3639 free_pipelocs(pTHX_ void *head) 3640 { 3641 pPLOC p, pnext; 3642 pPLOC *pHead = (pPLOC *)head; 3643 3644 p = *pHead; 3645 while (p) { 3646 pnext = p->next; 3647 PerlMem_free(p); 3648 p = pnext; 3649 } 3650 *pHead = 0; 3651 } 3652 3653 static void 3654 store_pipelocs(pTHX) 3655 { 3656 int i; 3657 pPLOC p; 3658 AV *av = 0; 3659 SV *dirsv; 3660 char *dir, *x; 3661 char *unixdir; 3662 char temp[NAM$C_MAXRSS+1]; 3663 STRLEN n_a; 3664 3665 if (head_PLOC) 3666 free_pipelocs(aTHX_ &head_PLOC); 3667 3668 /* the . directory from @INC comes last */ 3669 3670 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3671 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3672 p->next = head_PLOC; 3673 head_PLOC = p; 3674 strcpy(p->dir,"./"); 3675 3676 /* get the directory from $^X */ 3677 3678 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS); 3679 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3680 3681 #ifdef PERL_IMPLICIT_CONTEXT 3682 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3683 #else 3684 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3685 #endif 3686 my_strlcpy(temp, PL_origargv[0], sizeof(temp)); 3687 x = strrchr(temp,']'); 3688 if (x == NULL) { 3689 x = strrchr(temp,'>'); 3690 if (x == NULL) { 3691 /* It could be a UNIX path */ 3692 x = strrchr(temp,'/'); 3693 } 3694 } 3695 if (x) 3696 x[1] = '\0'; 3697 else { 3698 /* Got a bare name, so use default directory */ 3699 temp[0] = '.'; 3700 temp[1] = '\0'; 3701 } 3702 3703 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) { 3704 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3705 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3706 p->next = head_PLOC; 3707 head_PLOC = p; 3708 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3709 } 3710 } 3711 3712 /* reverse order of @INC entries, skip "." since entered above */ 3713 3714 #ifdef PERL_IMPLICIT_CONTEXT 3715 if (aTHX) 3716 #endif 3717 if (PL_incgv) av = GvAVn(PL_incgv); 3718 3719 for (i = 0; av && i <= AvFILL(av); i++) { 3720 dirsv = *av_fetch(av,i,TRUE); 3721 3722 if (SvROK(dirsv)) continue; 3723 dir = SvPVx(dirsv,n_a); 3724 if (strcmp(dir,".") == 0) continue; 3725 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL) 3726 continue; 3727 3728 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3729 p->next = head_PLOC; 3730 head_PLOC = p; 3731 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3732 } 3733 3734 /* most likely spot (ARCHLIB) put first in the list */ 3735 3736 #ifdef ARCHLIB_EXP 3737 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) { 3738 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3739 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3740 p->next = head_PLOC; 3741 head_PLOC = p; 3742 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3743 } 3744 #endif 3745 PerlMem_free(unixdir); 3746 } 3747 3748 static I32 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective, 3749 const char *fname, int opts); 3750 #if !defined(PERL_IMPLICIT_CONTEXT) 3751 #define cando_by_name_int Perl_cando_by_name_int 3752 #else 3753 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d) 3754 #endif 3755 3756 static char * 3757 find_vmspipe(pTHX) 3758 { 3759 static int vmspipe_file_status = 0; 3760 static char vmspipe_file[NAM$C_MAXRSS+1]; 3761 3762 /* already found? Check and use ... need read+execute permission */ 3763 3764 if (vmspipe_file_status == 1) { 3765 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3766 && cando_by_name_int 3767 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3768 return vmspipe_file; 3769 } 3770 vmspipe_file_status = 0; 3771 } 3772 3773 /* scan through stored @INC, $^X */ 3774 3775 if (vmspipe_file_status == 0) { 3776 char file[NAM$C_MAXRSS+1]; 3777 pPLOC p = head_PLOC; 3778 3779 while (p) { 3780 char * exp_res; 3781 int dirlen; 3782 dirlen = my_strlcpy(file, p->dir, sizeof(file)); 3783 my_strlcat(file, "vmspipe.com", sizeof(file)); 3784 p = p->next; 3785 3786 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0); 3787 if (!exp_res) continue; 3788 3789 if (cando_by_name_int 3790 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3791 && cando_by_name_int 3792 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3793 vmspipe_file_status = 1; 3794 return vmspipe_file; 3795 } 3796 } 3797 vmspipe_file_status = -1; /* failed, use tempfiles */ 3798 } 3799 3800 return 0; 3801 } 3802 3803 static FILE * 3804 vmspipe_tempfile(pTHX) 3805 { 3806 char file[NAM$C_MAXRSS+1]; 3807 FILE *fp; 3808 static int index = 0; 3809 Stat_t s0, s1; 3810 int cmp_result; 3811 3812 /* create a tempfile */ 3813 3814 /* we can't go from W, shr=get to R, shr=get without 3815 an intermediate vulnerable state, so don't bother trying... 3816 3817 and lib$spawn doesn't shr=put, so have to close the write 3818 3819 So... match up the creation date/time and the FID to 3820 make sure we're dealing with the same file 3821 3822 */ 3823 3824 index++; 3825 if (!decc_filename_unix_only) { 3826 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index); 3827 fp = fopen(file,"w"); 3828 if (!fp) { 3829 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index); 3830 fp = fopen(file,"w"); 3831 if (!fp) { 3832 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index); 3833 fp = fopen(file,"w"); 3834 } 3835 } 3836 } 3837 else { 3838 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index); 3839 fp = fopen(file,"w"); 3840 if (!fp) { 3841 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index); 3842 fp = fopen(file,"w"); 3843 if (!fp) { 3844 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index); 3845 fp = fopen(file,"w"); 3846 } 3847 } 3848 } 3849 if (!fp) return 0; /* we're hosed */ 3850 3851 fprintf(fp,"$! 'f$verify(0)'\n"); 3852 fprintf(fp,"$! --- protect against nonstandard definitions ---\n"); 3853 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n"); 3854 fprintf(fp,"$ perl_define = \"define/nolog\"\n"); 3855 fprintf(fp,"$ perl_on = \"set noon\"\n"); 3856 fprintf(fp,"$ perl_exit = \"exit\"\n"); 3857 fprintf(fp,"$ perl_del = \"delete\"\n"); 3858 fprintf(fp,"$ pif = \"if\"\n"); 3859 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n"); 3860 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n"); 3861 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n"); 3862 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n"); 3863 fprintf(fp,"$! --- build command line to get max possible length\n"); 3864 fprintf(fp,"$c=perl_popen_cmd0\n"); 3865 fprintf(fp,"$c=c+perl_popen_cmd1\n"); 3866 fprintf(fp,"$c=c+perl_popen_cmd2\n"); 3867 fprintf(fp,"$x=perl_popen_cmd3\n"); 3868 fprintf(fp,"$c=c+x\n"); 3869 fprintf(fp,"$ perl_on\n"); 3870 fprintf(fp,"$ 'c'\n"); 3871 fprintf(fp,"$ perl_status = $STATUS\n"); 3872 fprintf(fp,"$ perl_del 'perl_cfile'\n"); 3873 fprintf(fp,"$ perl_exit 'perl_status'\n"); 3874 fsync(fileno(fp)); 3875 3876 fgetname(fp, file, 1); 3877 fstat(fileno(fp), &s0.crtl_stat); 3878 fclose(fp); 3879 3880 if (decc_filename_unix_only) 3881 int_tounixspec(file, file, NULL); 3882 fp = fopen(file,"r","shr=get"); 3883 if (!fp) return 0; 3884 fstat(fileno(fp), &s1.crtl_stat); 3885 3886 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino); 3887 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) { 3888 fclose(fp); 3889 return 0; 3890 } 3891 3892 return fp; 3893 } 3894 3895 3896 static int 3897 vms_is_syscommand_xterm(void) 3898 { 3899 const static struct dsc$descriptor_s syscommand_dsc = 3900 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" }; 3901 3902 const static struct dsc$descriptor_s decwdisplay_dsc = 3903 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" }; 3904 3905 struct item_list_3 items[2]; 3906 unsigned short dvi_iosb[4]; 3907 unsigned long devchar; 3908 unsigned long devclass; 3909 int status; 3910 3911 /* Very simple check to guess if sys$command is a decterm? */ 3912 /* First see if the DECW$DISPLAY: device exists */ 3913 items[0].len = 4; 3914 items[0].code = DVI$_DEVCHAR; 3915 items[0].bufadr = &devchar; 3916 items[0].retadr = NULL; 3917 items[1].len = 0; 3918 items[1].code = 0; 3919 3920 status = sys$getdviw 3921 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL); 3922 3923 if ($VMS_STATUS_SUCCESS(status)) { 3924 status = dvi_iosb[0]; 3925 } 3926 3927 if (!$VMS_STATUS_SUCCESS(status)) { 3928 SETERRNO(EVMSERR, status); 3929 return -1; 3930 } 3931 3932 /* If it does, then for now assume that we are on a workstation */ 3933 /* Now verify that SYS$COMMAND is a terminal */ 3934 /* for creating the debugger DECTerm */ 3935 3936 items[0].len = 4; 3937 items[0].code = DVI$_DEVCLASS; 3938 items[0].bufadr = &devclass; 3939 items[0].retadr = NULL; 3940 items[1].len = 0; 3941 items[1].code = 0; 3942 3943 status = sys$getdviw 3944 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL); 3945 3946 if ($VMS_STATUS_SUCCESS(status)) { 3947 status = dvi_iosb[0]; 3948 } 3949 3950 if (!$VMS_STATUS_SUCCESS(status)) { 3951 SETERRNO(EVMSERR, status); 3952 return -1; 3953 } 3954 else { 3955 if (devclass == DC$_TERM) { 3956 return 0; 3957 } 3958 } 3959 return -1; 3960 } 3961 3962 /* If we are on a DECTerm, we can pretend to fork xterms when requested */ 3963 static PerlIO* 3964 create_forked_xterm(pTHX_ const char *cmd, const char *mode) 3965 { 3966 int status; 3967 int ret_stat; 3968 char * ret_char; 3969 char device_name[65]; 3970 unsigned short device_name_len; 3971 struct dsc$descriptor_s customization_dsc; 3972 struct dsc$descriptor_s device_name_dsc; 3973 const char * cptr; 3974 char customization[200]; 3975 char title[40]; 3976 pInfo info = NULL; 3977 char mbx1[64]; 3978 unsigned short p_chan; 3979 int n; 3980 unsigned short iosb[4]; 3981 const char * cust_str = 3982 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n"; 3983 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3984 DSC$K_CLASS_S, mbx1}; 3985 3986 /* LIB$FIND_IMAGE_SIGNAL needs a handler */ 3987 /*---------------------------------------*/ 3988 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret); 3989 3990 3991 /* Make sure that this is from the Perl debugger */ 3992 ret_char = strstr(cmd," xterm "); 3993 if (ret_char == NULL) 3994 return NULL; 3995 cptr = ret_char + 7; 3996 ret_char = strstr(cmd,"tty"); 3997 if (ret_char == NULL) 3998 return NULL; 3999 ret_char = strstr(cmd,"sleep"); 4000 if (ret_char == NULL) 4001 return NULL; 4002 4003 if (decw_term_port == 0) { 4004 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12"); 4005 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR"); 4006 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT"); 4007 4008 status = lib$find_image_symbol 4009 (&filename1_dsc, 4010 &decw_term_port_dsc, 4011 (void *)&decw_term_port, 4012 NULL, 4013 0); 4014 4015 /* Try again with the other image name */ 4016 if (!$VMS_STATUS_SUCCESS(status)) { 4017 4018 status = lib$find_image_symbol 4019 (&filename2_dsc, 4020 &decw_term_port_dsc, 4021 (void *)&decw_term_port, 4022 NULL, 4023 0); 4024 4025 } 4026 4027 } 4028 4029 4030 /* No decw$term_port, give it up */ 4031 if (!$VMS_STATUS_SUCCESS(status)) 4032 return NULL; 4033 4034 /* Are we on a workstation? */ 4035 /* to do: capture the rows / columns and pass their properties */ 4036 ret_stat = vms_is_syscommand_xterm(); 4037 if (ret_stat < 0) 4038 return NULL; 4039 4040 /* Make the title: */ 4041 ret_char = strstr(cptr,"-title"); 4042 if (ret_char != NULL) { 4043 while ((*cptr != 0) && (*cptr != '\"')) { 4044 cptr++; 4045 } 4046 if (*cptr == '\"') 4047 cptr++; 4048 n = 0; 4049 while ((*cptr != 0) && (*cptr != '\"')) { 4050 title[n] = *cptr; 4051 n++; 4052 if (n == 39) { 4053 title[39] = 0; 4054 break; 4055 } 4056 cptr++; 4057 } 4058 title[n] = 0; 4059 } 4060 else { 4061 /* Default title */ 4062 strcpy(title,"Perl Debug DECTerm"); 4063 } 4064 sprintf(customization, cust_str, title); 4065 4066 customization_dsc.dsc$a_pointer = customization; 4067 customization_dsc.dsc$w_length = strlen(customization); 4068 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 4069 customization_dsc.dsc$b_class = DSC$K_CLASS_S; 4070 4071 device_name_dsc.dsc$a_pointer = device_name; 4072 device_name_dsc.dsc$w_length = sizeof device_name -1; 4073 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 4074 device_name_dsc.dsc$b_class = DSC$K_CLASS_S; 4075 4076 device_name_len = 0; 4077 4078 /* Try to create the window */ 4079 status = (*decw_term_port) 4080 (NULL, 4081 NULL, 4082 &customization_dsc, 4083 &device_name_dsc, 4084 &device_name_len, 4085 NULL, 4086 NULL, 4087 NULL); 4088 if (!$VMS_STATUS_SUCCESS(status)) { 4089 SETERRNO(EVMSERR, status); 4090 return NULL; 4091 } 4092 4093 device_name[device_name_len] = '\0'; 4094 4095 /* Need to set this up to look like a pipe for cleanup */ 4096 n = sizeof(Info); 4097 status = lib$get_vm(&n, &info); 4098 if (!$VMS_STATUS_SUCCESS(status)) { 4099 SETERRNO(ENOMEM, status); 4100 return NULL; 4101 } 4102 4103 info->mode = *mode; 4104 info->done = FALSE; 4105 info->completion = 0; 4106 info->closing = FALSE; 4107 info->in = 0; 4108 info->out = 0; 4109 info->err = 0; 4110 info->fp = NULL; 4111 info->useFILE = 0; 4112 info->waiting = 0; 4113 info->in_done = TRUE; 4114 info->out_done = TRUE; 4115 info->err_done = TRUE; 4116 4117 /* Assign a channel on this so that it will persist, and not login */ 4118 /* We stash this channel in the info structure for reference. */ 4119 /* The created xterm self destructs when the last channel is removed */ 4120 /* and it appears that perl5db.pl (perl debugger) does this routinely */ 4121 /* So leave this assigned. */ 4122 device_name_dsc.dsc$w_length = device_name_len; 4123 status = sys$assign(&device_name_dsc,&info->xchan,0,0); 4124 if (!$VMS_STATUS_SUCCESS(status)) { 4125 SETERRNO(EVMSERR, status); 4126 return NULL; 4127 } 4128 info->xchan_valid = 1; 4129 4130 /* Now create a mailbox to be read by the application */ 4131 4132 create_mbx(&p_chan, &d_mbx1); 4133 4134 /* write the name of the created terminal to the mailbox */ 4135 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW, 4136 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0); 4137 4138 if (!$VMS_STATUS_SUCCESS(status)) { 4139 SETERRNO(EVMSERR, status); 4140 return NULL; 4141 } 4142 4143 info->fp = PerlIO_open(mbx1, mode); 4144 4145 /* Done with this channel */ 4146 sys$dassgn(p_chan); 4147 4148 /* If any errors, then clean up */ 4149 if (!info->fp) { 4150 n = sizeof(Info); 4151 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4152 return NULL; 4153 } 4154 4155 /* All done */ 4156 return info->fp; 4157 } 4158 4159 static I32 my_pclose_pinfo(pTHX_ pInfo info); 4160 4161 static PerlIO * 4162 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) 4163 { 4164 static int handler_set_up = FALSE; 4165 PerlIO * ret_fp; 4166 unsigned long int sts, flags = CLI$M_NOWAIT; 4167 /* The use of a GLOBAL table (as was done previously) rendered 4168 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL 4169 * environment. Hence we've switched to LOCAL symbol table. 4170 */ 4171 unsigned int table = LIB$K_CLI_LOCAL_SYM; 4172 int j, wait = 0, n; 4173 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe; 4174 char *in, *out, *err, mbx[512]; 4175 FILE *tpipe = 0; 4176 char tfilebuf[NAM$C_MAXRSS+1]; 4177 pInfo info = NULL; 4178 char cmd_sym_name[20]; 4179 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T, 4180 DSC$K_CLASS_S, symbol}; 4181 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T, 4182 DSC$K_CLASS_S, 0}; 4183 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T, 4184 DSC$K_CLASS_S, cmd_sym_name}; 4185 struct dsc$descriptor_s *vmscmd; 4186 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN"); 4187 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT"); 4188 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR"); 4189 4190 /* Check here for Xterm create request. This means looking for 4191 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it 4192 * is possible to create an xterm. 4193 */ 4194 if (*in_mode == 'r') { 4195 PerlIO * xterm_fd; 4196 4197 #if defined(PERL_IMPLICIT_CONTEXT) 4198 /* Can not fork an xterm with a NULL context */ 4199 /* This probably could never happen */ 4200 xterm_fd = NULL; 4201 if (aTHX != NULL) 4202 #endif 4203 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode); 4204 if (xterm_fd != NULL) 4205 return xterm_fd; 4206 } 4207 4208 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */ 4209 4210 /* once-per-program initialization... 4211 note that the SETAST calls and the dual test of pipe_ef 4212 makes sure that only the FIRST thread through here does 4213 the initialization...all other threads wait until it's 4214 done. 4215 4216 Yeah, uglier than a pthread call, it's got all the stuff inline 4217 rather than in a separate routine. 4218 */ 4219 4220 if (!pipe_ef) { 4221 _ckvmssts_noperl(sys$setast(0)); 4222 if (!pipe_ef) { 4223 unsigned long int pidcode = JPI$_PID; 4224 $DESCRIPTOR(d_delay, RETRY_DELAY); 4225 _ckvmssts_noperl(lib$get_ef(&pipe_ef)); 4226 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4227 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime)); 4228 } 4229 if (!handler_set_up) { 4230 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock)); 4231 handler_set_up = TRUE; 4232 } 4233 _ckvmssts_noperl(sys$setast(1)); 4234 } 4235 4236 /* see if we can find a VMSPIPE.COM */ 4237 4238 tfilebuf[0] = '@'; 4239 vmspipe = find_vmspipe(aTHX); 4240 if (vmspipe) { 4241 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1; 4242 } else { /* uh, oh...we're in tempfile hell */ 4243 tpipe = vmspipe_tempfile(aTHX); 4244 if (!tpipe) { /* a fish popular in Boston */ 4245 if (ckWARN(WARN_PIPE)) { 4246 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping"); 4247 } 4248 return NULL; 4249 } 4250 fgetname(tpipe,tfilebuf+1,1); 4251 vmspipedsc.dsc$w_length = strlen(tfilebuf); 4252 } 4253 vmspipedsc.dsc$a_pointer = tfilebuf; 4254 4255 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd); 4256 if (!(sts & 1)) { 4257 switch (sts) { 4258 case RMS$_FNF: case RMS$_DNF: 4259 set_errno(ENOENT); break; 4260 case RMS$_DIR: 4261 set_errno(ENOTDIR); break; 4262 case RMS$_DEV: 4263 set_errno(ENODEV); break; 4264 case RMS$_PRV: 4265 set_errno(EACCES); break; 4266 case RMS$_SYN: 4267 set_errno(EINVAL); break; 4268 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 4269 set_errno(E2BIG); break; 4270 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 4271 _ckvmssts_noperl(sts); /* fall through */ 4272 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 4273 set_errno(EVMSERR); 4274 } 4275 set_vaxc_errno(sts); 4276 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) { 4277 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno)); 4278 } 4279 *psts = sts; 4280 return NULL; 4281 } 4282 n = sizeof(Info); 4283 _ckvmssts_noperl(lib$get_vm(&n, &info)); 4284 4285 my_strlcpy(mode, in_mode, sizeof(mode)); 4286 info->mode = *mode; 4287 info->done = FALSE; 4288 info->completion = 0; 4289 info->closing = FALSE; 4290 info->in = 0; 4291 info->out = 0; 4292 info->err = 0; 4293 info->fp = NULL; 4294 info->useFILE = 0; 4295 info->waiting = 0; 4296 info->in_done = TRUE; 4297 info->out_done = TRUE; 4298 info->err_done = TRUE; 4299 info->xchan = 0; 4300 info->xchan_valid = 0; 4301 4302 in = (char *)PerlMem_malloc(VMS_MAXRSS); 4303 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4304 out = (char *)PerlMem_malloc(VMS_MAXRSS); 4305 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4306 err = (char *)PerlMem_malloc(VMS_MAXRSS); 4307 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4308 4309 in[0] = out[0] = err[0] = '\0'; 4310 4311 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */ 4312 info->useFILE = 1; 4313 strcpy(p,p+1); 4314 } 4315 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */ 4316 wait = 1; 4317 strcpy(p,p+1); 4318 } 4319 4320 if (*mode == 'r') { /* piping from subroutine */ 4321 4322 info->out = pipe_infromchild_setup(aTHX_ mbx,out); 4323 if (info->out) { 4324 info->out->pipe_done = &info->out_done; 4325 info->out_done = FALSE; 4326 info->out->info = info; 4327 } 4328 if (!info->useFILE) { 4329 info->fp = PerlIO_open(mbx, mode); 4330 } else { 4331 info->fp = (PerlIO *) freopen(mbx, mode, stdin); 4332 vmssetuserlnm("SYS$INPUT", mbx); 4333 } 4334 4335 if (!info->fp && info->out) { 4336 sys$cancel(info->out->chan_out); 4337 4338 while (!info->out_done) { 4339 int done; 4340 _ckvmssts_noperl(sys$setast(0)); 4341 done = info->out_done; 4342 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4343 _ckvmssts_noperl(sys$setast(1)); 4344 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4345 } 4346 4347 if (info->out->buf) { 4348 n = info->out->bufsize * sizeof(char); 4349 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf)); 4350 } 4351 n = sizeof(Pipe); 4352 _ckvmssts_noperl(lib$free_vm(&n, &info->out)); 4353 n = sizeof(Info); 4354 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4355 *psts = RMS$_FNF; 4356 return NULL; 4357 } 4358 4359 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4360 if (info->err) { 4361 info->err->pipe_done = &info->err_done; 4362 info->err_done = FALSE; 4363 info->err->info = info; 4364 } 4365 4366 } else if (*mode == 'w') { /* piping to subroutine */ 4367 4368 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4369 if (info->out) { 4370 info->out->pipe_done = &info->out_done; 4371 info->out_done = FALSE; 4372 info->out->info = info; 4373 } 4374 4375 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4376 if (info->err) { 4377 info->err->pipe_done = &info->err_done; 4378 info->err_done = FALSE; 4379 info->err->info = info; 4380 } 4381 4382 info->in = pipe_tochild_setup(aTHX_ in,mbx); 4383 if (!info->useFILE) { 4384 info->fp = PerlIO_open(mbx, mode); 4385 } else { 4386 info->fp = (PerlIO *) freopen(mbx, mode, stdout); 4387 vmssetuserlnm("SYS$OUTPUT", mbx); 4388 } 4389 4390 if (info->in) { 4391 info->in->pipe_done = &info->in_done; 4392 info->in_done = FALSE; 4393 info->in->info = info; 4394 } 4395 4396 /* error cleanup */ 4397 if (!info->fp && info->in) { 4398 info->done = TRUE; 4399 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0, 4400 0, 0, 0, 0, 0, 0, 0, 0)); 4401 4402 while (!info->in_done) { 4403 int done; 4404 _ckvmssts_noperl(sys$setast(0)); 4405 done = info->in_done; 4406 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4407 _ckvmssts_noperl(sys$setast(1)); 4408 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4409 } 4410 4411 if (info->in->buf) { 4412 n = info->in->bufsize * sizeof(char); 4413 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf)); 4414 } 4415 n = sizeof(Pipe); 4416 _ckvmssts_noperl(lib$free_vm(&n, &info->in)); 4417 n = sizeof(Info); 4418 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4419 *psts = RMS$_FNF; 4420 return NULL; 4421 } 4422 4423 4424 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */ 4425 /* Let the child inherit standard input, unless it's a directory. */ 4426 Stat_t st; 4427 if (my_trnlnm("SYS$INPUT", in, 0)) { 4428 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode)) 4429 *in = '\0'; 4430 } 4431 4432 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4433 if (info->out) { 4434 info->out->pipe_done = &info->out_done; 4435 info->out_done = FALSE; 4436 info->out->info = info; 4437 } 4438 4439 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4440 if (info->err) { 4441 info->err->pipe_done = &info->err_done; 4442 info->err_done = FALSE; 4443 info->err->info = info; 4444 } 4445 } 4446 4447 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol)); 4448 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table)); 4449 4450 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol)); 4451 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table)); 4452 4453 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol)); 4454 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table)); 4455 4456 /* Done with the names for the pipes */ 4457 PerlMem_free(err); 4458 PerlMem_free(out); 4459 PerlMem_free(in); 4460 4461 p = vmscmd->dsc$a_pointer; 4462 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */ 4463 if (*p == '$') p++; /* remove leading $ */ 4464 while (*p == ' ' || *p == '\t') p++; 4465 4466 for (j = 0; j < 4; j++) { 4467 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4468 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4469 4470 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol)); 4471 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table)); 4472 4473 if (strlen(p) > MAX_DCL_SYMBOL) { 4474 p += MAX_DCL_SYMBOL; 4475 } else { 4476 p += strlen(p); 4477 } 4478 } 4479 _ckvmssts_noperl(sys$setast(0)); 4480 info->next=open_pipes; /* prepend to list */ 4481 open_pipes=info; 4482 _ckvmssts_noperl(sys$setast(1)); 4483 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT 4484 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still 4485 * have SYS$COMMAND if we need it. 4486 */ 4487 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags, 4488 0, &info->pid, &info->completion, 4489 0, popen_completion_ast,info,0,0,0)); 4490 4491 /* if we were using a tempfile, close it now */ 4492 4493 if (tpipe) fclose(tpipe); 4494 4495 /* once the subprocess is spawned, it has copied the symbols and 4496 we can get rid of ours */ 4497 4498 for (j = 0; j < 4; j++) { 4499 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4500 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4501 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table)); 4502 } 4503 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table)); 4504 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table)); 4505 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table)); 4506 vms_execfree(vmscmd); 4507 4508 #ifdef PERL_IMPLICIT_CONTEXT 4509 if (aTHX) 4510 #endif 4511 PL_forkprocess = info->pid; 4512 4513 ret_fp = info->fp; 4514 if (wait) { 4515 dSAVEDERRNO; 4516 int done = 0; 4517 while (!done) { 4518 _ckvmssts_noperl(sys$setast(0)); 4519 done = info->done; 4520 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4521 _ckvmssts_noperl(sys$setast(1)); 4522 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4523 } 4524 *psts = info->completion; 4525 /* Caller thinks it is open and tries to close it. */ 4526 /* This causes some problems, as it changes the error status */ 4527 /* my_pclose(info->fp); */ 4528 4529 /* If we did not have a file pointer open, then we have to */ 4530 /* clean up here or eventually we will run out of something */ 4531 SAVE_ERRNO; 4532 if (info->fp == NULL) { 4533 my_pclose_pinfo(aTHX_ info); 4534 } 4535 RESTORE_ERRNO; 4536 4537 } else { 4538 *psts = info->pid; 4539 } 4540 return ret_fp; 4541 } /* end of safe_popen */ 4542 4543 4544 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/ 4545 PerlIO * 4546 Perl_my_popen(pTHX_ const char *cmd, const char *mode) 4547 { 4548 int sts; 4549 TAINT_ENV(); 4550 TAINT_PROPER("popen"); 4551 PERL_FLUSHALL_FOR_CHILD; 4552 return safe_popen(aTHX_ cmd,mode,&sts); 4553 } 4554 4555 /*}}}*/ 4556 4557 4558 /* Routine to close and cleanup a pipe info structure */ 4559 4560 static I32 4561 my_pclose_pinfo(pTHX_ pInfo info) { 4562 4563 unsigned long int retsts; 4564 int done, n; 4565 pInfo next, last; 4566 4567 /* If we were writing to a subprocess, insure that someone reading from 4568 * the mailbox gets an EOF. It looks like a simple fclose() doesn't 4569 * produce an EOF record in the mailbox. 4570 * 4571 * well, at least sometimes it *does*, so we have to watch out for 4572 * the first EOF closing the pipe (and DASSGN'ing the channel)... 4573 */ 4574 if (info->fp) { 4575 if (!info->useFILE 4576 #if defined(USE_ITHREADS) 4577 && my_perl 4578 #endif 4579 #ifdef USE_PERLIO 4580 && PL_perlio_fd_refcnt 4581 #endif 4582 ) 4583 PerlIO_flush(info->fp); 4584 else 4585 fflush((FILE *)info->fp); 4586 } 4587 4588 _ckvmssts(sys$setast(0)); 4589 info->closing = TRUE; 4590 done = info->done && info->in_done && info->out_done && info->err_done; 4591 /* hanging on write to Perl's input? cancel it */ 4592 if (info->mode == 'r' && info->out && !info->out_done) { 4593 if (info->out->chan_out) { 4594 _ckvmssts(sys$cancel(info->out->chan_out)); 4595 if (!info->out->chan_in) { /* EOF generation, need AST */ 4596 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0)); 4597 } 4598 } 4599 } 4600 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */ 4601 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 4602 0, 0, 0, 0, 0, 0)); 4603 _ckvmssts(sys$setast(1)); 4604 if (info->fp) { 4605 if (!info->useFILE 4606 #if defined(USE_ITHREADS) 4607 && my_perl 4608 #endif 4609 #ifdef USE_PERLIO 4610 && PL_perlio_fd_refcnt 4611 #endif 4612 ) 4613 PerlIO_close(info->fp); 4614 else 4615 fclose((FILE *)info->fp); 4616 } 4617 /* 4618 we have to wait until subprocess completes, but ALSO wait until all 4619 the i/o completes...otherwise we'll be freeing the "info" structure 4620 that the i/o ASTs could still be using... 4621 */ 4622 4623 while (!done) { 4624 _ckvmssts(sys$setast(0)); 4625 done = info->done && info->in_done && info->out_done && info->err_done; 4626 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4627 _ckvmssts(sys$setast(1)); 4628 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4629 } 4630 retsts = info->completion; 4631 4632 /* remove from list of open pipes */ 4633 _ckvmssts(sys$setast(0)); 4634 last = NULL; 4635 for (next = open_pipes; next != NULL; last = next, next = next->next) { 4636 if (next == info) 4637 break; 4638 } 4639 4640 if (last) 4641 last->next = info->next; 4642 else 4643 open_pipes = info->next; 4644 _ckvmssts(sys$setast(1)); 4645 4646 /* free buffers and structures */ 4647 4648 if (info->in) { 4649 if (info->in->buf) { 4650 n = info->in->bufsize * sizeof(char); 4651 _ckvmssts(lib$free_vm(&n, &info->in->buf)); 4652 } 4653 n = sizeof(Pipe); 4654 _ckvmssts(lib$free_vm(&n, &info->in)); 4655 } 4656 if (info->out) { 4657 if (info->out->buf) { 4658 n = info->out->bufsize * sizeof(char); 4659 _ckvmssts(lib$free_vm(&n, &info->out->buf)); 4660 } 4661 n = sizeof(Pipe); 4662 _ckvmssts(lib$free_vm(&n, &info->out)); 4663 } 4664 if (info->err) { 4665 if (info->err->buf) { 4666 n = info->err->bufsize * sizeof(char); 4667 _ckvmssts(lib$free_vm(&n, &info->err->buf)); 4668 } 4669 n = sizeof(Pipe); 4670 _ckvmssts(lib$free_vm(&n, &info->err)); 4671 } 4672 n = sizeof(Info); 4673 _ckvmssts(lib$free_vm(&n, &info)); 4674 4675 return retsts; 4676 } 4677 4678 4679 /*{{{ I32 my_pclose(PerlIO *fp)*/ 4680 I32 Perl_my_pclose(pTHX_ PerlIO *fp) 4681 { 4682 pInfo info, last = NULL; 4683 I32 ret_status; 4684 4685 /* Fixme - need ast and mutex protection here */ 4686 for (info = open_pipes; info != NULL; last = info, info = info->next) 4687 if (info->fp == fp) break; 4688 4689 if (info == NULL) { /* no such pipe open */ 4690 set_errno(ECHILD); /* quoth POSIX */ 4691 set_vaxc_errno(SS$_NONEXPR); 4692 return -1; 4693 } 4694 4695 ret_status = my_pclose_pinfo(aTHX_ info); 4696 4697 return ret_status; 4698 4699 } /* end of my_pclose() */ 4700 4701 /* Roll our own prototype because we want this regardless of whether 4702 * _VMS_WAIT is defined. 4703 */ 4704 4705 #ifdef __cplusplus 4706 extern "C" { 4707 #endif 4708 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options ); 4709 #ifdef __cplusplus 4710 } 4711 #endif 4712 4713 /* sort-of waitpid; special handling of pipe clean-up for subprocesses 4714 created with popen(); otherwise partially emulate waitpid() unless 4715 we have a suitable one from the CRTL that came with VMS 7.2 and later. 4716 Also check processes not considered by the CRTL waitpid(). 4717 */ 4718 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/ 4719 Pid_t 4720 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags) 4721 { 4722 pInfo info; 4723 int done; 4724 int sts; 4725 int j; 4726 4727 if (statusp) *statusp = 0; 4728 4729 for (info = open_pipes; info != NULL; info = info->next) 4730 if (info->pid == pid) break; 4731 4732 if (info != NULL) { /* we know about this child */ 4733 while (!info->done) { 4734 _ckvmssts(sys$setast(0)); 4735 done = info->done; 4736 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4737 _ckvmssts(sys$setast(1)); 4738 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4739 } 4740 4741 if (statusp) *statusp = info->completion; 4742 return pid; 4743 } 4744 4745 /* child that already terminated? */ 4746 4747 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) { 4748 if (closed_list[j].pid == pid) { 4749 if (statusp) *statusp = closed_list[j].completion; 4750 return pid; 4751 } 4752 } 4753 4754 /* fall through if this child is not one of our own pipe children */ 4755 4756 /* waitpid() became available in the CRTL as of VMS 7.0, but only 4757 * in 7.2 did we get a version that fills in the VMS completion 4758 * status as Perl has always tried to do. 4759 */ 4760 4761 sts = __vms_waitpid( pid, statusp, flags ); 4762 4763 if ( sts == 0 || !(sts == -1 && errno == ECHILD) ) 4764 return sts; 4765 4766 /* If the real waitpid tells us the child does not exist, we 4767 * fall through here to implement waiting for a child that 4768 * was created by some means other than exec() (say, spawned 4769 * from DCL) or to wait for a process that is not a subprocess 4770 * of the current process. 4771 */ 4772 4773 { 4774 $DESCRIPTOR(intdsc,"0 00:00:01"); 4775 unsigned long int ownercode = JPI$_OWNER, ownerpid; 4776 unsigned long int pidcode = JPI$_PID, mypid; 4777 unsigned long int interval[2]; 4778 unsigned int jpi_iosb[2]; 4779 struct itmlst_3 jpilist[2] = { 4780 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0}, 4781 { 0, 0, 0, 0} 4782 }; 4783 4784 if (pid <= 0) { 4785 /* Sorry folks, we don't presently implement rooting around for 4786 the first child we can find, and we definitely don't want to 4787 pass a pid of -1 to $getjpi, where it is a wildcard operation. 4788 */ 4789 set_errno(ENOTSUP); 4790 return -1; 4791 } 4792 4793 /* Get the owner of the child so I can warn if it's not mine. If the 4794 * process doesn't exist or I don't have the privs to look at it, 4795 * I can go home early. 4796 */ 4797 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL); 4798 if (sts & 1) sts = jpi_iosb[0]; 4799 if (!(sts & 1)) { 4800 switch (sts) { 4801 case SS$_NONEXPR: 4802 set_errno(ECHILD); 4803 break; 4804 case SS$_NOPRIV: 4805 set_errno(EACCES); 4806 break; 4807 default: 4808 _ckvmssts(sts); 4809 } 4810 set_vaxc_errno(sts); 4811 return -1; 4812 } 4813 4814 if (ckWARN(WARN_EXEC)) { 4815 /* remind folks they are asking for non-standard waitpid behavior */ 4816 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4817 if (ownerpid != mypid) 4818 Perl_warner(aTHX_ packWARN(WARN_EXEC), 4819 "waitpid: process %x is not a child of process %x", 4820 pid,mypid); 4821 } 4822 4823 /* simply check on it once a second until it's not there anymore. */ 4824 4825 _ckvmssts(sys$bintim(&intdsc,interval)); 4826 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) { 4827 _ckvmssts(sys$schdwk(0,0,interval,0)); 4828 _ckvmssts(sys$hiber()); 4829 } 4830 if (sts == SS$_NONEXPR) sts = SS$_NORMAL; 4831 4832 _ckvmssts(sts); 4833 return pid; 4834 } 4835 } /* end of waitpid() */ 4836 /*}}}*/ 4837 /*}}}*/ 4838 /*}}}*/ 4839 4840 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */ 4841 char * 4842 my_gconvert(double val, int ndig, int trail, char *buf) 4843 { 4844 static char __gcvtbuf[DBL_DIG+1]; 4845 char *loc; 4846 4847 loc = buf ? buf : __gcvtbuf; 4848 4849 if (val) { 4850 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG; 4851 return gcvt(val,ndig,loc); 4852 } 4853 else { 4854 loc[0] = '0'; loc[1] = '\0'; 4855 return loc; 4856 } 4857 4858 } 4859 /*}}}*/ 4860 4861 #if !defined(NAML$C_MAXRSS) 4862 static int 4863 rms_free_search_context(struct FAB * fab) 4864 { 4865 struct NAM * nam; 4866 4867 nam = fab->fab$l_nam; 4868 nam->nam$b_nop |= NAM$M_SYNCHK; 4869 nam->nam$l_rlf = NULL; 4870 fab->fab$b_dns = 0; 4871 return sys$parse(fab, NULL, NULL); 4872 } 4873 4874 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam 4875 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0; 4876 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt) 4877 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt) 4878 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt)) 4879 #define rms_nam_esll(nam) nam.nam$b_esl 4880 #define rms_nam_esl(nam) nam.nam$b_esl 4881 #define rms_nam_name(nam) nam.nam$l_name 4882 #define rms_nam_namel(nam) nam.nam$l_name 4883 #define rms_nam_type(nam) nam.nam$l_type 4884 #define rms_nam_typel(nam) nam.nam$l_type 4885 #define rms_nam_ver(nam) nam.nam$l_ver 4886 #define rms_nam_verl(nam) nam.nam$l_ver 4887 #define rms_nam_rsll(nam) nam.nam$b_rsl 4888 #define rms_nam_rsl(nam) nam.nam$b_rsl 4889 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam 4890 #define rms_set_fna(fab, nam, name, size) \ 4891 { fab.fab$b_fns = size; fab.fab$l_fna = name; } 4892 #define rms_get_fna(fab, nam) fab.fab$l_fna 4893 #define rms_set_dna(fab, nam, name, size) \ 4894 { fab.fab$b_dns = size; fab.fab$l_dna = name; } 4895 #define rms_nam_dns(fab, nam) fab.fab$b_dns 4896 #define rms_set_esa(nam, name, size) \ 4897 { nam.nam$b_ess = size; nam.nam$l_esa = name; } 4898 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4899 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;} 4900 #define rms_set_rsa(nam, name, size) \ 4901 { nam.nam$l_rsa = name; nam.nam$b_rss = size; } 4902 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4903 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; } 4904 #define rms_nam_name_type_l_size(nam) \ 4905 (nam.nam$b_name + nam.nam$b_type) 4906 #else 4907 static int 4908 rms_free_search_context(struct FAB * fab) 4909 { 4910 struct NAML * nam; 4911 4912 nam = fab->fab$l_naml; 4913 nam->naml$b_nop |= NAM$M_SYNCHK; 4914 nam->naml$l_rlf = NULL; 4915 nam->naml$l_long_defname_size = 0; 4916 4917 fab->fab$b_dns = 0; 4918 return sys$parse(fab, NULL, NULL); 4919 } 4920 4921 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml 4922 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0; 4923 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt) 4924 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt) 4925 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt)) 4926 #define rms_nam_esll(nam) nam.naml$l_long_expand_size 4927 #define rms_nam_esl(nam) nam.naml$b_esl 4928 #define rms_nam_name(nam) nam.naml$l_name 4929 #define rms_nam_namel(nam) nam.naml$l_long_name 4930 #define rms_nam_type(nam) nam.naml$l_type 4931 #define rms_nam_typel(nam) nam.naml$l_long_type 4932 #define rms_nam_ver(nam) nam.naml$l_ver 4933 #define rms_nam_verl(nam) nam.naml$l_long_ver 4934 #define rms_nam_rsll(nam) nam.naml$l_long_result_size 4935 #define rms_nam_rsl(nam) nam.naml$b_rsl 4936 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam 4937 #define rms_set_fna(fab, nam, name, size) \ 4938 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \ 4939 nam.naml$l_long_filename_size = size; \ 4940 nam.naml$l_long_filename = name;} 4941 #define rms_get_fna(fab, nam) nam.naml$l_long_filename 4942 #define rms_set_dna(fab, nam, name, size) \ 4943 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \ 4944 nam.naml$l_long_defname_size = size; \ 4945 nam.naml$l_long_defname = name; } 4946 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size 4947 #define rms_set_esa(nam, name, size) \ 4948 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \ 4949 nam.naml$l_long_expand_alloc = size; \ 4950 nam.naml$l_long_expand = name; } 4951 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4952 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \ 4953 nam.naml$l_long_expand = l_name; \ 4954 nam.naml$l_long_expand_alloc = l_size; } 4955 #define rms_set_rsa(nam, name, size) \ 4956 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \ 4957 nam.naml$l_long_result = name; \ 4958 nam.naml$l_long_result_alloc = size; } 4959 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4960 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \ 4961 nam.naml$l_long_result = l_name; \ 4962 nam.naml$l_long_result_alloc = l_size; } 4963 #define rms_nam_name_type_l_size(nam) \ 4964 (nam.naml$l_long_name_size + nam.naml$l_long_type_size) 4965 #endif 4966 4967 4968 /* rms_erase 4969 * The CRTL for 8.3 and later can create symbolic links in any mode, 4970 * however in 8.3 the unlink/remove/delete routines will only properly handle 4971 * them if one of the PCP modes is active. 4972 */ 4973 static int 4974 rms_erase(const char * vmsname) 4975 { 4976 int status; 4977 struct FAB myfab = cc$rms_fab; 4978 rms_setup_nam(mynam); 4979 4980 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */ 4981 rms_bind_fab_nam(myfab, mynam); 4982 4983 #ifdef NAML$M_OPEN_SPECIAL 4984 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 4985 #endif 4986 4987 status = sys$erase(&myfab, 0, 0); 4988 4989 return status; 4990 } 4991 4992 4993 static int 4994 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc, 4995 const struct dsc$descriptor_s * vms_dst_dsc, 4996 unsigned long flags) 4997 { 4998 /* VMS and UNIX handle file permissions differently and the 4999 * the same ACL trick may be needed for renaming files, 5000 * especially if they are directories. 5001 */ 5002 5003 /* todo: get kill_file and rename to share common code */ 5004 /* I can not find online documentation for $change_acl 5005 * it appears to be replaced by $set_security some time ago */ 5006 5007 const unsigned int access_mode = 0; 5008 $DESCRIPTOR(obj_file_dsc,"FILE"); 5009 char *vmsname; 5010 char *rslt; 5011 unsigned long int jpicode = JPI$_UIC; 5012 int aclsts, fndsts, rnsts = -1; 5013 unsigned int ctx = 0; 5014 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 5015 struct dsc$descriptor_s * clean_dsc; 5016 5017 struct myacedef { 5018 unsigned char myace$b_length; 5019 unsigned char myace$b_type; 5020 unsigned short int myace$w_flags; 5021 unsigned long int myace$l_access; 5022 unsigned long int myace$l_ident; 5023 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 5024 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 5025 0}, 5026 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 5027 5028 struct item_list_3 5029 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0}, 5030 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0}, 5031 {0,0,0,0}}, 5032 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}}, 5033 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0}, 5034 {0,0,0,0}}; 5035 5036 5037 /* Expand the input spec using RMS, since we do not want to put 5038 * ACLs on the target of a symbolic link */ 5039 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1); 5040 if (vmsname == NULL) 5041 return SS$_INSFMEM; 5042 5043 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer, 5044 vmsname, 5045 PERL_RMSEXPAND_M_SYMLINK); 5046 if (rslt == NULL) { 5047 PerlMem_free(vmsname); 5048 return SS$_INSFMEM; 5049 } 5050 5051 /* So we get our own UIC to use as a rights identifier, 5052 * and the insert an ACE at the head of the ACL which allows us 5053 * to delete the file. 5054 */ 5055 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 5056 5057 fildsc.dsc$w_length = strlen(vmsname); 5058 fildsc.dsc$a_pointer = vmsname; 5059 ctx = 0; 5060 newace.myace$l_ident = oldace.myace$l_ident; 5061 rnsts = SS$_ABORT; 5062 5063 /* Grab any existing ACEs with this identifier in case we fail */ 5064 clean_dsc = &fildsc; 5065 aclsts = fndsts = sys$get_security(&obj_file_dsc, 5066 &fildsc, 5067 NULL, 5068 OSS$M_WLOCK, 5069 findlst, 5070 &ctx, 5071 &access_mode); 5072 5073 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) { 5074 /* Add the new ACE . . . */ 5075 5076 /* if the sys$get_security succeeded, then ctx is valid, and the 5077 * object/file descriptors will be ignored. But otherwise they 5078 * are needed 5079 */ 5080 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL, 5081 OSS$M_RELCTX, addlst, &ctx, &access_mode); 5082 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 5083 set_errno(EVMSERR); 5084 set_vaxc_errno(aclsts); 5085 PerlMem_free(vmsname); 5086 return aclsts; 5087 } 5088 5089 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc, 5090 NULL, NULL, 5091 &flags, 5092 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 5093 5094 if ($VMS_STATUS_SUCCESS(rnsts)) { 5095 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc; 5096 } 5097 5098 /* Put things back the way they were. */ 5099 ctx = 0; 5100 aclsts = sys$get_security(&obj_file_dsc, 5101 clean_dsc, 5102 NULL, 5103 OSS$M_WLOCK, 5104 findlst, 5105 &ctx, 5106 &access_mode); 5107 5108 if ($VMS_STATUS_SUCCESS(aclsts)) { 5109 int sec_flags; 5110 5111 sec_flags = 0; 5112 if (!$VMS_STATUS_SUCCESS(fndsts)) 5113 sec_flags = OSS$M_RELCTX; 5114 5115 /* Get rid of the new ACE */ 5116 aclsts = sys$set_security(NULL, NULL, NULL, 5117 sec_flags, dellst, &ctx, &access_mode); 5118 5119 /* If there was an old ACE, put it back */ 5120 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) { 5121 addlst[0].bufadr = &oldace; 5122 aclsts = sys$set_security(NULL, NULL, NULL, 5123 OSS$M_RELCTX, addlst, &ctx, &access_mode); 5124 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 5125 set_errno(EVMSERR); 5126 set_vaxc_errno(aclsts); 5127 rnsts = aclsts; 5128 } 5129 } else { 5130 int aclsts2; 5131 5132 /* Try to clear the lock on the ACL list */ 5133 aclsts2 = sys$set_security(NULL, NULL, NULL, 5134 OSS$M_RELCTX, NULL, &ctx, &access_mode); 5135 5136 /* Rename errors are most important */ 5137 if (!$VMS_STATUS_SUCCESS(rnsts)) 5138 aclsts = rnsts; 5139 set_errno(EVMSERR); 5140 set_vaxc_errno(aclsts); 5141 rnsts = aclsts; 5142 } 5143 } 5144 else { 5145 if (aclsts != SS$_ACLEMPTY) 5146 rnsts = aclsts; 5147 } 5148 } 5149 else 5150 rnsts = fndsts; 5151 5152 PerlMem_free(vmsname); 5153 return rnsts; 5154 } 5155 5156 5157 /*{{{int rename(const char *, const char * */ 5158 /* Not exactly what X/Open says to do, but doing it absolutely right 5159 * and efficiently would require a lot more work. This should be close 5160 * enough to pass all but the most strict X/Open compliance test. 5161 */ 5162 int 5163 Perl_rename(pTHX_ const char *src, const char * dst) 5164 { 5165 int retval; 5166 int pre_delete = 0; 5167 int src_sts; 5168 int dst_sts; 5169 Stat_t src_st; 5170 Stat_t dst_st; 5171 5172 /* Validate the source file */ 5173 src_sts = flex_lstat(src, &src_st); 5174 if (src_sts != 0) { 5175 5176 /* No source file or other problem */ 5177 return src_sts; 5178 } 5179 if (src_st.st_devnam[0] == 0) { 5180 /* This may be possible so fail if it is seen. */ 5181 errno = EIO; 5182 return -1; 5183 } 5184 5185 dst_sts = flex_lstat(dst, &dst_st); 5186 if (dst_sts == 0) { 5187 5188 if (dst_st.st_dev != src_st.st_dev) { 5189 /* Must be on the same device */ 5190 errno = EXDEV; 5191 return -1; 5192 } 5193 5194 /* VMS_INO_T_COMPARE is true if the inodes are different 5195 * to match the output of memcmp 5196 */ 5197 5198 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) { 5199 /* That was easy, the files are the same! */ 5200 return 0; 5201 } 5202 5203 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) { 5204 /* If source is a directory, so must be dest */ 5205 errno = EISDIR; 5206 return -1; 5207 } 5208 5209 } 5210 5211 5212 if ((dst_sts == 0) && 5213 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) { 5214 5215 /* We have issues here if vms_unlink_all_versions is set 5216 * If the destination exists, and is not a directory, then 5217 * we must delete in advance. 5218 * 5219 * If the src is a directory, then we must always pre-delete 5220 * the destination. 5221 * 5222 * If we successfully delete the dst in advance, and the rename fails 5223 * X/Open requires that errno be EIO. 5224 * 5225 */ 5226 5227 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) { 5228 int d_sts; 5229 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 5230 S_ISDIR(dst_st.st_mode)); 5231 5232 /* Need to delete all versions ? */ 5233 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) { 5234 int i = 0; 5235 5236 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) { 5237 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0); 5238 if (d_sts != 0) 5239 break; 5240 i++; 5241 5242 /* Make sure that we do not loop forever */ 5243 if (i > 32767) { 5244 errno = EIO; 5245 d_sts = -1; 5246 break; 5247 } 5248 } 5249 } 5250 5251 if (d_sts != 0) 5252 return d_sts; 5253 5254 /* We killed the destination, so only errno now is EIO */ 5255 pre_delete = 1; 5256 } 5257 } 5258 5259 /* Originally the idea was to call the CRTL rename() and only 5260 * try the lib$rename_file if it failed. 5261 * It turns out that there are too many variants in what the 5262 * the CRTL rename might do, so only use lib$rename_file 5263 */ 5264 retval = -1; 5265 5266 { 5267 /* Is the source and dest both in VMS format */ 5268 /* if the source is a directory, then need to fileify */ 5269 /* and dest must be a directory or non-existent. */ 5270 5271 char * vms_dst; 5272 int sts; 5273 char * ret_str; 5274 unsigned long flags; 5275 struct dsc$descriptor_s old_file_dsc; 5276 struct dsc$descriptor_s new_file_dsc; 5277 5278 /* We need to modify the src and dst depending 5279 * on if one or more of them are directories. 5280 */ 5281 5282 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS); 5283 if (vms_dst == NULL) 5284 _ckvmssts_noperl(SS$_INSFMEM); 5285 5286 if (S_ISDIR(src_st.st_mode)) { 5287 char * ret_str; 5288 char * vms_dir_file; 5289 5290 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS); 5291 if (vms_dir_file == NULL) 5292 _ckvmssts_noperl(SS$_INSFMEM); 5293 5294 /* If the dest is a directory, we must remove it */ 5295 if (dst_sts == 0) { 5296 int d_sts; 5297 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1); 5298 if (d_sts != 0) { 5299 PerlMem_free(vms_dst); 5300 errno = EIO; 5301 return d_sts; 5302 } 5303 5304 pre_delete = 1; 5305 } 5306 5307 /* The dest must be a VMS file specification */ 5308 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL); 5309 if (ret_str == NULL) { 5310 PerlMem_free(vms_dst); 5311 errno = EIO; 5312 return -1; 5313 } 5314 5315 /* The source must be a file specification */ 5316 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL); 5317 if (ret_str == NULL) { 5318 PerlMem_free(vms_dst); 5319 PerlMem_free(vms_dir_file); 5320 errno = EIO; 5321 return -1; 5322 } 5323 PerlMem_free(vms_dst); 5324 vms_dst = vms_dir_file; 5325 5326 } else { 5327 /* File to file or file to new dir */ 5328 5329 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) { 5330 /* VMS pathify a dir target */ 5331 ret_str = int_tovmspath(dst, vms_dst, NULL); 5332 if (ret_str == NULL) { 5333 PerlMem_free(vms_dst); 5334 errno = EIO; 5335 return -1; 5336 } 5337 } else { 5338 char * v_spec, * r_spec, * d_spec, * n_spec; 5339 char * e_spec, * vs_spec; 5340 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 5341 5342 /* fileify a target VMS file specification */ 5343 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL); 5344 if (ret_str == NULL) { 5345 PerlMem_free(vms_dst); 5346 errno = EIO; 5347 return -1; 5348 } 5349 5350 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len, 5351 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 5352 &e_len, &vs_spec, &vs_len); 5353 if (sts == 0) { 5354 if (e_len == 0) { 5355 /* Get rid of the version */ 5356 if (vs_len != 0) { 5357 *vs_spec = '\0'; 5358 } 5359 /* Need to specify a '.' so that the extension */ 5360 /* is not inherited */ 5361 strcat(vms_dst,"."); 5362 } 5363 } 5364 } 5365 } 5366 5367 old_file_dsc.dsc$a_pointer = src_st.st_devnam; 5368 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam); 5369 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5370 old_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5371 5372 new_file_dsc.dsc$a_pointer = vms_dst; 5373 new_file_dsc.dsc$w_length = strlen(vms_dst); 5374 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5375 new_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5376 5377 flags = 0; 5378 #if defined(NAML$C_MAXRSS) 5379 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */ 5380 #endif 5381 5382 sts = lib$rename_file(&old_file_dsc, 5383 &new_file_dsc, 5384 NULL, NULL, 5385 &flags, 5386 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 5387 if (!$VMS_STATUS_SUCCESS(sts)) { 5388 5389 /* We could have failed because VMS style permissions do not 5390 * permit renames that UNIX will allow. Just like the hack 5391 * in for kill_file. 5392 */ 5393 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags); 5394 } 5395 5396 PerlMem_free(vms_dst); 5397 if (!$VMS_STATUS_SUCCESS(sts)) { 5398 errno = EIO; 5399 return -1; 5400 } 5401 retval = 0; 5402 } 5403 5404 if (vms_unlink_all_versions) { 5405 /* Now get rid of any previous versions of the source file that 5406 * might still exist 5407 */ 5408 int i = 0; 5409 dSAVEDERRNO; 5410 SAVE_ERRNO; 5411 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam, 5412 S_ISDIR(src_st.st_mode)); 5413 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) { 5414 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam, 5415 S_ISDIR(src_st.st_mode)); 5416 if (src_sts != 0) 5417 break; 5418 i++; 5419 5420 /* Make sure that we do not loop forever */ 5421 if (i > 32767) { 5422 src_sts = -1; 5423 break; 5424 } 5425 } 5426 RESTORE_ERRNO; 5427 } 5428 5429 /* We deleted the destination, so must force the error to be EIO */ 5430 if ((retval != 0) && (pre_delete != 0)) 5431 errno = EIO; 5432 5433 return retval; 5434 } 5435 /*}}}*/ 5436 5437 5438 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/ 5439 /* Shortcut for common case of simple calls to $PARSE and $SEARCH 5440 * to expand file specification. Allows for a single default file 5441 * specification and a simple mask of options. If outbuf is non-NULL, 5442 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which 5443 * the resultant file specification is placed. If outbuf is NULL, the 5444 * resultant file specification is placed into a static buffer. 5445 * The third argument, if non-NULL, is taken to be a default file 5446 * specification string. The fourth argument is unused at present. 5447 * rmesexpand() returns the address of the resultant string if 5448 * successful, and NULL on error. 5449 * 5450 * New functionality for previously unused opts value: 5451 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format. 5452 * PERL_RMSEXPAND_M_LONG - Want output in long formst 5453 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify 5454 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target 5455 */ 5456 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 5457 5458 static char * 5459 int_rmsexpand 5460 (const char *filespec, 5461 char *outbuf, 5462 const char *defspec, 5463 unsigned opts, 5464 int * fs_utf8, 5465 int * dfs_utf8) 5466 { 5467 char * ret_spec; 5468 const char * in_spec; 5469 char * spec_buf; 5470 const char * def_spec; 5471 char * vmsfspec, *vmsdefspec; 5472 char * esa; 5473 char * esal = NULL; 5474 char * outbufl; 5475 struct FAB myfab = cc$rms_fab; 5476 rms_setup_nam(mynam); 5477 STRLEN speclen; 5478 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0; 5479 int sts; 5480 5481 /* temp hack until UTF8 is actually implemented */ 5482 if (fs_utf8 != NULL) 5483 *fs_utf8 = 0; 5484 5485 if (!filespec || !*filespec) { 5486 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL); 5487 return NULL; 5488 } 5489 5490 vmsfspec = NULL; 5491 vmsdefspec = NULL; 5492 outbufl = NULL; 5493 5494 in_spec = filespec; 5495 isunix = 0; 5496 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) { 5497 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 5498 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 5499 5500 /* If this is a UNIX file spec, convert it to VMS */ 5501 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len, 5502 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 5503 &e_len, &vs_spec, &vs_len); 5504 if (sts != 0) { 5505 isunix = 1; 5506 char * ret_spec; 5507 5508 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS); 5509 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5510 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8); 5511 if (ret_spec == NULL) { 5512 PerlMem_free(vmsfspec); 5513 return NULL; 5514 } 5515 in_spec = (const char *)vmsfspec; 5516 5517 /* Unless we are forcing to VMS format, a UNIX input means 5518 * UNIX output, and that requires long names to be used 5519 */ 5520 if ((opts & PERL_RMSEXPAND_M_VMS) == 0) 5521 #if defined(NAML$C_MAXRSS) 5522 opts |= PERL_RMSEXPAND_M_LONG; 5523 #else 5524 NOOP; 5525 #endif 5526 else 5527 isunix = 0; 5528 } 5529 5530 } 5531 5532 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */ 5533 rms_bind_fab_nam(myfab, mynam); 5534 5535 /* Process the default file specification if present */ 5536 def_spec = defspec; 5537 if (defspec && *defspec) { 5538 int t_isunix; 5539 t_isunix = is_unix_filespec(defspec); 5540 if (t_isunix) { 5541 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS); 5542 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5543 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8); 5544 5545 if (ret_spec == NULL) { 5546 /* Clean up and bail */ 5547 PerlMem_free(vmsdefspec); 5548 if (vmsfspec != NULL) 5549 PerlMem_free(vmsfspec); 5550 return NULL; 5551 } 5552 def_spec = (const char *)vmsdefspec; 5553 } 5554 rms_set_dna(myfab, mynam, 5555 (char *)def_spec, strlen(def_spec)); /* cast ok */ 5556 } 5557 5558 /* Now we need the expansion buffers */ 5559 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 5560 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5561 #if defined(NAML$C_MAXRSS) 5562 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 5563 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5564 #endif 5565 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1); 5566 5567 /* If a NAML block is used RMS always writes to the long and short 5568 * addresses unless you suppress the short name. 5569 */ 5570 #if defined(NAML$C_MAXRSS) 5571 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS); 5572 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5573 #endif 5574 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1)); 5575 5576 #ifdef NAM$M_NO_SHORT_UPCASE 5577 if (decc_efs_case_preserve) 5578 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE); 5579 #endif 5580 5581 /* We may not want to follow symbolic links */ 5582 #ifdef NAML$M_OPEN_SPECIAL 5583 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5584 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5585 #endif 5586 5587 /* First attempt to parse as an existing file */ 5588 retsts = sys$parse(&myfab,0,0); 5589 if (!(retsts & STS$K_SUCCESS)) { 5590 5591 /* Could not find the file, try as syntax only if error is not fatal */ 5592 rms_set_nam_nop(mynam, NAM$M_SYNCHK); 5593 if (retsts == RMS$_DNF || 5594 retsts == RMS$_DIR || 5595 retsts == RMS$_DEV || 5596 retsts == RMS$_PRV) { 5597 retsts = sys$parse(&myfab,0,0); 5598 if (retsts & STS$K_SUCCESS) goto int_expanded; 5599 } 5600 5601 /* Still could not parse the file specification */ 5602 /*----------------------------------------------*/ 5603 sts = rms_free_search_context(&myfab); /* Free search context */ 5604 if (vmsdefspec != NULL) 5605 PerlMem_free(vmsdefspec); 5606 if (vmsfspec != NULL) 5607 PerlMem_free(vmsfspec); 5608 if (outbufl != NULL) 5609 PerlMem_free(outbufl); 5610 PerlMem_free(esa); 5611 if (esal != NULL) 5612 PerlMem_free(esal); 5613 set_vaxc_errno(retsts); 5614 if (retsts == RMS$_PRV) set_errno(EACCES); 5615 else if (retsts == RMS$_DEV) set_errno(ENODEV); 5616 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 5617 else set_errno(EVMSERR); 5618 return NULL; 5619 } 5620 retsts = sys$search(&myfab,0,0); 5621 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) { 5622 sts = rms_free_search_context(&myfab); /* Free search context */ 5623 if (vmsdefspec != NULL) 5624 PerlMem_free(vmsdefspec); 5625 if (vmsfspec != NULL) 5626 PerlMem_free(vmsfspec); 5627 if (outbufl != NULL) 5628 PerlMem_free(outbufl); 5629 PerlMem_free(esa); 5630 if (esal != NULL) 5631 PerlMem_free(esal); 5632 set_vaxc_errno(retsts); 5633 if (retsts == RMS$_PRV) set_errno(EACCES); 5634 else set_errno(EVMSERR); 5635 return NULL; 5636 } 5637 5638 /* If the input filespec contained any lowercase characters, 5639 * downcase the result for compatibility with Unix-minded code. */ 5640 int_expanded: 5641 if (!decc_efs_case_preserve) { 5642 char * tbuf; 5643 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++) 5644 if (islower(*tbuf)) { haslower = 1; break; } 5645 } 5646 5647 /* Is a long or a short name expected */ 5648 /*------------------------------------*/ 5649 spec_buf = NULL; 5650 #if defined(NAML$C_MAXRSS) 5651 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5652 if (rms_nam_rsll(mynam)) { 5653 spec_buf = outbufl; 5654 speclen = rms_nam_rsll(mynam); 5655 } 5656 else { 5657 spec_buf = esal; /* Not esa */ 5658 speclen = rms_nam_esll(mynam); 5659 } 5660 } 5661 else { 5662 #endif 5663 if (rms_nam_rsl(mynam)) { 5664 spec_buf = outbuf; 5665 speclen = rms_nam_rsl(mynam); 5666 } 5667 else { 5668 spec_buf = esa; /* Not esal */ 5669 speclen = rms_nam_esl(mynam); 5670 } 5671 #if defined(NAML$C_MAXRSS) 5672 } 5673 #endif 5674 spec_buf[speclen] = '\0'; 5675 5676 /* Trim off null fields added by $PARSE 5677 * If type > 1 char, must have been specified in original or default spec 5678 * (not true for version; $SEARCH may have added version of existing file). 5679 */ 5680 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER); 5681 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5682 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5683 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1); 5684 } 5685 else { 5686 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5687 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1); 5688 } 5689 if (trimver || trimtype) { 5690 if (defspec && *defspec) { 5691 char *defesal = NULL; 5692 char *defesa = NULL; 5693 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5694 if (defesa != NULL) { 5695 struct FAB deffab = cc$rms_fab; 5696 #if defined(NAML$C_MAXRSS) 5697 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5698 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5699 #endif 5700 rms_setup_nam(defnam); 5701 5702 rms_bind_fab_nam(deffab, defnam); 5703 5704 /* Cast ok */ 5705 rms_set_fna 5706 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam)); 5707 5708 /* RMS needs the esa/esal as a work area if wildcards are involved */ 5709 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1); 5710 5711 rms_clear_nam_nop(defnam); 5712 rms_set_nam_nop(defnam, NAM$M_SYNCHK); 5713 #ifdef NAM$M_NO_SHORT_UPCASE 5714 if (decc_efs_case_preserve) 5715 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE); 5716 #endif 5717 #ifdef NAML$M_OPEN_SPECIAL 5718 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5719 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5720 #endif 5721 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) { 5722 if (trimver) { 5723 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER); 5724 } 5725 if (trimtype) { 5726 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE); 5727 } 5728 } 5729 if (defesal != NULL) 5730 PerlMem_free(defesal); 5731 PerlMem_free(defesa); 5732 } else { 5733 _ckvmssts_noperl(SS$_INSFMEM); 5734 } 5735 } 5736 if (trimver) { 5737 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5738 if (*(rms_nam_verl(mynam)) != '\"') 5739 speclen = rms_nam_verl(mynam) - spec_buf; 5740 } 5741 else { 5742 if (*(rms_nam_ver(mynam)) != '\"') 5743 speclen = rms_nam_ver(mynam) - spec_buf; 5744 } 5745 } 5746 if (trimtype) { 5747 /* If we didn't already trim version, copy down */ 5748 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5749 if (speclen > rms_nam_verl(mynam) - spec_buf) 5750 memmove 5751 (rms_nam_typel(mynam), 5752 rms_nam_verl(mynam), 5753 speclen - (rms_nam_verl(mynam) - spec_buf)); 5754 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam); 5755 } 5756 else { 5757 if (speclen > rms_nam_ver(mynam) - spec_buf) 5758 memmove 5759 (rms_nam_type(mynam), 5760 rms_nam_ver(mynam), 5761 speclen - (rms_nam_ver(mynam) - spec_buf)); 5762 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam); 5763 } 5764 } 5765 } 5766 5767 /* Done with these copies of the input files */ 5768 /*-------------------------------------------*/ 5769 if (vmsfspec != NULL) 5770 PerlMem_free(vmsfspec); 5771 if (vmsdefspec != NULL) 5772 PerlMem_free(vmsdefspec); 5773 5774 /* If we just had a directory spec on input, $PARSE "helpfully" 5775 * adds an empty name and type for us */ 5776 #if defined(NAML$C_MAXRSS) 5777 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5778 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) && 5779 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 && 5780 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5781 speclen = rms_nam_namel(mynam) - spec_buf; 5782 } 5783 else 5784 #endif 5785 { 5786 if (rms_nam_name(mynam) == rms_nam_type(mynam) && 5787 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 && 5788 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5789 speclen = rms_nam_name(mynam) - spec_buf; 5790 } 5791 5792 /* Posix format specifications must have matching quotes */ 5793 if (speclen < (VMS_MAXRSS - 1)) { 5794 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) { 5795 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) { 5796 spec_buf[speclen] = '\"'; 5797 speclen++; 5798 } 5799 } 5800 } 5801 spec_buf[speclen] = '\0'; 5802 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf); 5803 5804 /* Have we been working with an expanded, but not resultant, spec? */ 5805 /* Also, convert back to Unix syntax if necessary. */ 5806 { 5807 int rsl; 5808 5809 #if defined(NAML$C_MAXRSS) 5810 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5811 rsl = rms_nam_rsll(mynam); 5812 } else 5813 #endif 5814 { 5815 rsl = rms_nam_rsl(mynam); 5816 } 5817 if (!rsl) { 5818 /* rsl is not present, it means that spec_buf is either */ 5819 /* esa or esal, and needs to be copied to outbuf */ 5820 /* convert to Unix if desired */ 5821 if (isunix) { 5822 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8); 5823 } else { 5824 /* VMS file specs are not in UTF-8 */ 5825 if (fs_utf8 != NULL) 5826 *fs_utf8 = 0; 5827 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS); 5828 ret_spec = outbuf; 5829 } 5830 } 5831 else { 5832 /* Now spec_buf is either outbuf or outbufl */ 5833 /* We need the result into outbuf */ 5834 if (isunix) { 5835 /* If we need this in UNIX, then we need another buffer */ 5836 /* to keep things in order */ 5837 char * src; 5838 char * new_src = NULL; 5839 if (spec_buf == outbuf) { 5840 new_src = (char *)PerlMem_malloc(VMS_MAXRSS); 5841 my_strlcpy(new_src, spec_buf, VMS_MAXRSS); 5842 } else { 5843 src = spec_buf; 5844 } 5845 ret_spec = int_tounixspec(src, outbuf, fs_utf8); 5846 if (new_src) { 5847 PerlMem_free(new_src); 5848 } 5849 } else { 5850 /* VMS file specs are not in UTF-8 */ 5851 if (fs_utf8 != NULL) 5852 *fs_utf8 = 0; 5853 5854 /* Copy the buffer if needed */ 5855 if (outbuf != spec_buf) 5856 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS); 5857 ret_spec = outbuf; 5858 } 5859 } 5860 } 5861 5862 /* Need to clean up the search context */ 5863 rms_set_rsal(mynam, NULL, 0, NULL, 0); 5864 sts = rms_free_search_context(&myfab); /* Free search context */ 5865 5866 /* Clean up the extra buffers */ 5867 if (esal != NULL) 5868 PerlMem_free(esal); 5869 PerlMem_free(esa); 5870 if (outbufl != NULL) 5871 PerlMem_free(outbufl); 5872 5873 /* Return the result */ 5874 return ret_spec; 5875 } 5876 5877 /* Common simple case - Expand an already VMS spec */ 5878 static char * 5879 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) { 5880 opts |= PERL_RMSEXPAND_M_VMS_IN; 5881 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 5882 } 5883 5884 /* Common simple case - Expand to a VMS spec */ 5885 static char * 5886 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) { 5887 opts |= PERL_RMSEXPAND_M_VMS; 5888 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 5889 } 5890 5891 5892 /* Entry point used by perl routines */ 5893 static char * 5894 mp_do_rmsexpand 5895 (pTHX_ const char *filespec, 5896 char *outbuf, 5897 int ts, 5898 const char *defspec, 5899 unsigned opts, 5900 int * fs_utf8, 5901 int * dfs_utf8) 5902 { 5903 static char __rmsexpand_retbuf[VMS_MAXRSS]; 5904 char * expanded, *ret_spec, *ret_buf; 5905 5906 expanded = NULL; 5907 ret_buf = outbuf; 5908 if (ret_buf == NULL) { 5909 if (ts) { 5910 Newx(expanded, VMS_MAXRSS, char); 5911 if (expanded == NULL) 5912 _ckvmssts(SS$_INSFMEM); 5913 ret_buf = expanded; 5914 } else { 5915 ret_buf = __rmsexpand_retbuf; 5916 } 5917 } 5918 5919 5920 ret_spec = int_rmsexpand(filespec, ret_buf, defspec, 5921 opts, fs_utf8, dfs_utf8); 5922 5923 if (ret_spec == NULL) { 5924 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 5925 if (expanded) 5926 Safefree(expanded); 5927 } 5928 5929 return ret_spec; 5930 } 5931 /*}}}*/ 5932 /* External entry points */ 5933 char * 5934 Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5935 { 5936 return do_rmsexpand(spec, buf, 0, def, opt, NULL, NULL); 5937 } 5938 5939 char * 5940 Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5941 { 5942 return do_rmsexpand(spec, buf, 1, def, opt, NULL, NULL); 5943 } 5944 5945 char * 5946 Perl_rmsexpand_utf8(pTHX_ const char *spec, char *buf, const char *def, 5947 unsigned opt, int * fs_utf8, int * dfs_utf8) 5948 { 5949 return do_rmsexpand(spec, buf, 0, def, opt, fs_utf8, dfs_utf8); 5950 } 5951 5952 char * 5953 Perl_rmsexpand_utf8_ts(pTHX_ const char *spec, char *buf, const char *def, 5954 unsigned opt, int * fs_utf8, int * dfs_utf8) 5955 { 5956 return do_rmsexpand(spec, buf, 1, def, opt, fs_utf8, dfs_utf8); 5957 } 5958 5959 5960 /* 5961 ** The following routines are provided to make life easier when 5962 ** converting among VMS-style and Unix-style directory specifications. 5963 ** All will take input specifications in either VMS or Unix syntax. On 5964 ** failure, all return NULL. If successful, the routines listed below 5965 ** return a pointer to a buffer containing the appropriately 5966 ** reformatted spec (and, therefore, subsequent calls to that routine 5967 ** will clobber the result), while the routines of the same names with 5968 ** a _ts suffix appended will return a pointer to a mallocd string 5969 ** containing the appropriately reformatted spec. 5970 ** In all cases, only explicit syntax is altered; no check is made that 5971 ** the resulting string is valid or that the directory in question 5972 ** actually exists. 5973 ** 5974 ** fileify_dirspec() - convert a directory spec into the name of the 5975 ** directory file (i.e. what you can stat() to see if it's a dir). 5976 ** The style (VMS or Unix) of the result is the same as the style 5977 ** of the parameter passed in. 5978 ** pathify_dirspec() - convert a directory spec into a path (i.e. 5979 ** what you prepend to a filename to indicate what directory it's in). 5980 ** The style (VMS or Unix) of the result is the same as the style 5981 ** of the parameter passed in. 5982 ** tounixpath() - convert a directory spec into a Unix-style path. 5983 ** tovmspath() - convert a directory spec into a VMS-style path. 5984 ** tounixspec() - convert any file spec into a Unix-style file spec. 5985 ** tovmsspec() - convert any file spec into a VMS-style spec. 5986 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec. 5987 ** 5988 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu> 5989 ** Permission is given to distribute this code as part of the Perl 5990 ** standard distribution under the terms of the GNU General Public 5991 ** License or the Perl Artistic License. Copies of each may be 5992 ** found in the Perl standard distribution. 5993 */ 5994 5995 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 5996 static char * 5997 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl) 5998 { 5999 unsigned long int dirlen, retlen, hasfilename = 0; 6000 char *cp1, *cp2, *lastdir; 6001 char *trndir, *vmsdir; 6002 unsigned short int trnlnm_iter_count; 6003 int sts; 6004 if (utf8_fl != NULL) 6005 *utf8_fl = 0; 6006 6007 if (!dir || !*dir) { 6008 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL; 6009 } 6010 dirlen = strlen(dir); 6011 while (dirlen && dir[dirlen-1] == '/') --dirlen; 6012 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */ 6013 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) { 6014 dir = "/sys$disk"; 6015 dirlen = 9; 6016 } 6017 else 6018 dirlen = 1; 6019 } 6020 if (dirlen > (VMS_MAXRSS - 1)) { 6021 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); 6022 return NULL; 6023 } 6024 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 6025 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6026 if (!strpbrk(dir+1,"/]>:") && 6027 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) { 6028 strcpy(trndir,*dir == '/' ? dir + 1: dir); 6029 trnlnm_iter_count = 0; 6030 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) { 6031 trnlnm_iter_count++; 6032 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 6033 } 6034 dirlen = strlen(trndir); 6035 } 6036 else { 6037 memcpy(trndir, dir, dirlen); 6038 trndir[dirlen] = '\0'; 6039 } 6040 6041 /* At this point we are done with *dir and use *trndir which is a 6042 * copy that can be modified. *dir must not be modified. 6043 */ 6044 6045 /* If we were handed a rooted logical name or spec, treat it like a 6046 * simple directory, so that 6047 * $ Define myroot dev:[dir.] 6048 * ... do_fileify_dirspec("myroot",buf,1) ... 6049 * does something useful. 6050 */ 6051 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) { 6052 trndir[--dirlen] = '\0'; 6053 trndir[dirlen-1] = ']'; 6054 } 6055 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) { 6056 trndir[--dirlen] = '\0'; 6057 trndir[dirlen-1] = '>'; 6058 } 6059 6060 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) { 6061 /* If we've got an explicit filename, we can just shuffle the string. */ 6062 if (*(cp1+1)) hasfilename = 1; 6063 /* Similarly, we can just back up a level if we've got multiple levels 6064 of explicit directories in a VMS spec which ends with directories. */ 6065 else { 6066 for (cp2 = cp1; cp2 > trndir; cp2--) { 6067 if (*cp2 == '.') { 6068 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) { 6069 /* fix-me, can not scan EFS file specs backward like this */ 6070 *cp2 = *cp1; *cp1 = '\0'; 6071 hasfilename = 1; 6072 break; 6073 } 6074 } 6075 if (*cp2 == '[' || *cp2 == '<') break; 6076 } 6077 } 6078 } 6079 6080 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 6081 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6082 cp1 = strpbrk(trndir,"]:>"); 6083 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */ 6084 cp1 = strpbrk(cp1+2,"]:>"); 6085 6086 if (hasfilename || !cp1) { /* filename present or not VMS */ 6087 6088 if (trndir[0] == '.') { 6089 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) { 6090 PerlMem_free(trndir); 6091 PerlMem_free(vmsdir); 6092 return int_fileify_dirspec("[]", buf, NULL); 6093 } 6094 else if (trndir[1] == '.' && 6095 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) { 6096 PerlMem_free(trndir); 6097 PerlMem_free(vmsdir); 6098 return int_fileify_dirspec("[-]", buf, NULL); 6099 } 6100 } 6101 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */ 6102 dirlen -= 1; /* to last element */ 6103 lastdir = strrchr(trndir,'/'); 6104 } 6105 else if ((cp1 = strstr(trndir,"/.")) != NULL) { 6106 /* If we have "/." or "/..", VMSify it and let the VMS code 6107 * below expand it, rather than repeating the code to handle 6108 * relative components of a filespec here */ 6109 do { 6110 if (*(cp1+2) == '.') cp1++; 6111 if (*(cp1+2) == '/' || *(cp1+2) == '\0') { 6112 char * ret_chr; 6113 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) { 6114 PerlMem_free(trndir); 6115 PerlMem_free(vmsdir); 6116 return NULL; 6117 } 6118 if (strchr(vmsdir,'/') != NULL) { 6119 /* If int_tovmsspec() returned it, it must have VMS syntax 6120 * delimiters in it, so it's a mixed VMS/Unix spec. We take 6121 * the time to check this here only so we avoid a recursion 6122 * loop; otherwise, gigo. 6123 */ 6124 PerlMem_free(trndir); 6125 PerlMem_free(vmsdir); 6126 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); 6127 return NULL; 6128 } 6129 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) { 6130 PerlMem_free(trndir); 6131 PerlMem_free(vmsdir); 6132 return NULL; 6133 } 6134 ret_chr = int_tounixspec(trndir, buf, utf8_fl); 6135 PerlMem_free(trndir); 6136 PerlMem_free(vmsdir); 6137 return ret_chr; 6138 } 6139 cp1++; 6140 } while ((cp1 = strstr(cp1,"/.")) != NULL); 6141 lastdir = strrchr(trndir,'/'); 6142 } 6143 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) { 6144 char * ret_chr; 6145 /* Ditto for specs that end in an MFD -- let the VMS code 6146 * figure out whether it's a real device or a rooted logical. */ 6147 6148 /* This should not happen any more. Allowing the fake /000000 6149 * in a UNIX pathname causes all sorts of problems when trying 6150 * to run in UNIX emulation. So the VMS to UNIX conversions 6151 * now remove the fake /000000 directories. 6152 */ 6153 6154 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0'; 6155 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) { 6156 PerlMem_free(trndir); 6157 PerlMem_free(vmsdir); 6158 return NULL; 6159 } 6160 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) { 6161 PerlMem_free(trndir); 6162 PerlMem_free(vmsdir); 6163 return NULL; 6164 } 6165 ret_chr = int_tounixspec(trndir, buf, utf8_fl); 6166 PerlMem_free(trndir); 6167 PerlMem_free(vmsdir); 6168 return ret_chr; 6169 } 6170 else { 6171 6172 if ( !(lastdir = cp1 = strrchr(trndir,'/')) && 6173 !(lastdir = cp1 = strrchr(trndir,']')) && 6174 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir; 6175 6176 cp2 = strrchr(cp1,'.'); 6177 if (cp2) { 6178 int e_len, vs_len = 0; 6179 int is_dir = 0; 6180 char * cp3; 6181 cp3 = strchr(cp2,';'); 6182 e_len = strlen(cp2); 6183 if (cp3) { 6184 vs_len = strlen(cp3); 6185 e_len = e_len - vs_len; 6186 } 6187 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len); 6188 if (!is_dir) { 6189 if (!decc_efs_charset) { 6190 /* If this is not EFS, then not a directory */ 6191 PerlMem_free(trndir); 6192 PerlMem_free(vmsdir); 6193 set_errno(ENOTDIR); 6194 set_vaxc_errno(RMS$_DIR); 6195 return NULL; 6196 } 6197 } else { 6198 /* Ok, here we have an issue, technically if a .dir shows */ 6199 /* from inside a directory, then we should treat it as */ 6200 /* xxx^.dir.dir. But we do not have that context at this */ 6201 /* point unless this is totally restructured, so we remove */ 6202 /* The .dir for now, and fix this better later */ 6203 dirlen = cp2 - trndir; 6204 } 6205 if (decc_efs_charset && !strchr(trndir,'/')) { 6206 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */ 6207 char *cp4 = is_dir ? (cp2 - 1) : cp2; 6208 6209 for (; cp4 > cp1; cp4--) { 6210 if (*cp4 == '.') { 6211 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) { 6212 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1); 6213 *cp4 = '^'; 6214 dirlen++; 6215 } 6216 } 6217 } 6218 } 6219 } 6220 6221 } 6222 6223 retlen = dirlen + 6; 6224 memcpy(buf, trndir, dirlen); 6225 buf[dirlen] = '\0'; 6226 6227 /* We've picked up everything up to the directory file name. 6228 Now just add the type and version, and we're set. */ 6229 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) 6230 strcat(buf,".dir"); 6231 else 6232 strcat(buf,".DIR"); 6233 if (!decc_filename_unix_no_version) 6234 strcat(buf,";1"); 6235 PerlMem_free(trndir); 6236 PerlMem_free(vmsdir); 6237 return buf; 6238 } 6239 else { /* VMS-style directory spec */ 6240 6241 char *esa, *esal, term, *cp; 6242 char *my_esa; 6243 int my_esa_len; 6244 unsigned long int cmplen, haslower = 0; 6245 struct FAB dirfab = cc$rms_fab; 6246 rms_setup_nam(savnam); 6247 rms_setup_nam(dirnam); 6248 6249 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 6250 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6251 esal = NULL; 6252 #if defined(NAML$C_MAXRSS) 6253 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 6254 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6255 #endif 6256 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir)); 6257 rms_bind_fab_nam(dirfab, dirnam); 6258 rms_set_dna(dirfab, dirnam, ".DIR;1", 6); 6259 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 6260 #ifdef NAM$M_NO_SHORT_UPCASE 6261 if (decc_efs_case_preserve) 6262 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6263 #endif 6264 6265 for (cp = trndir; *cp; cp++) 6266 if (islower(*cp)) { haslower = 1; break; } 6267 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) { 6268 if ((dirfab.fab$l_sts == RMS$_DIR) || 6269 (dirfab.fab$l_sts == RMS$_DNF) || 6270 (dirfab.fab$l_sts == RMS$_PRV)) { 6271 rms_set_nam_nop(dirnam, NAM$M_SYNCHK); 6272 sts = sys$parse(&dirfab); 6273 } 6274 if (!sts) { 6275 PerlMem_free(esa); 6276 if (esal != NULL) 6277 PerlMem_free(esal); 6278 PerlMem_free(trndir); 6279 PerlMem_free(vmsdir); 6280 set_errno(EVMSERR); 6281 set_vaxc_errno(dirfab.fab$l_sts); 6282 return NULL; 6283 } 6284 } 6285 else { 6286 savnam = dirnam; 6287 /* Does the file really exist? */ 6288 if (sys$search(&dirfab)& STS$K_SUCCESS) { 6289 /* Yes; fake the fnb bits so we'll check type below */ 6290 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER)); 6291 } 6292 else { /* No; just work with potential name */ 6293 if (dirfab.fab$l_sts == RMS$_FNF 6294 || dirfab.fab$l_sts == RMS$_DNF 6295 || dirfab.fab$l_sts == RMS$_FND) 6296 dirnam = savnam; 6297 else { 6298 int fab_sts; 6299 fab_sts = dirfab.fab$l_sts; 6300 sts = rms_free_search_context(&dirfab); 6301 PerlMem_free(esa); 6302 if (esal != NULL) 6303 PerlMem_free(esal); 6304 PerlMem_free(trndir); 6305 PerlMem_free(vmsdir); 6306 set_errno(EVMSERR); set_vaxc_errno(fab_sts); 6307 return NULL; 6308 } 6309 } 6310 } 6311 6312 /* Make sure we are using the right buffer */ 6313 #if defined(NAML$C_MAXRSS) 6314 if (esal != NULL) { 6315 my_esa = esal; 6316 my_esa_len = rms_nam_esll(dirnam); 6317 } else { 6318 #endif 6319 my_esa = esa; 6320 my_esa_len = rms_nam_esl(dirnam); 6321 #if defined(NAML$C_MAXRSS) 6322 } 6323 #endif 6324 my_esa[my_esa_len] = '\0'; 6325 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) { 6326 cp1 = strchr(my_esa,']'); 6327 if (!cp1) cp1 = strchr(my_esa,'>'); 6328 if (cp1) { /* Should always be true */ 6329 my_esa_len -= cp1 - my_esa - 1; 6330 memmove(my_esa, cp1 + 1, my_esa_len); 6331 } 6332 } 6333 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */ 6334 /* Yep; check version while we're at it, if it's there. */ 6335 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4; 6336 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) { 6337 /* Something other than .DIR[;1]. Bzzt. */ 6338 sts = rms_free_search_context(&dirfab); 6339 PerlMem_free(esa); 6340 if (esal != NULL) 6341 PerlMem_free(esal); 6342 PerlMem_free(trndir); 6343 PerlMem_free(vmsdir); 6344 set_errno(ENOTDIR); 6345 set_vaxc_errno(RMS$_DIR); 6346 return NULL; 6347 } 6348 } 6349 6350 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) { 6351 /* They provided at least the name; we added the type, if necessary, */ 6352 my_strlcpy(buf, my_esa, VMS_MAXRSS); 6353 sts = rms_free_search_context(&dirfab); 6354 PerlMem_free(trndir); 6355 PerlMem_free(esa); 6356 if (esal != NULL) 6357 PerlMem_free(esal); 6358 PerlMem_free(vmsdir); 6359 return buf; 6360 } 6361 if ((cp1 = strstr(esa,".][000000]")) != NULL) { 6362 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2; 6363 *cp1 = '\0'; 6364 my_esa_len -= 9; 6365 } 6366 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>'); 6367 if (cp1 == NULL) { /* should never happen */ 6368 sts = rms_free_search_context(&dirfab); 6369 PerlMem_free(trndir); 6370 PerlMem_free(esa); 6371 if (esal != NULL) 6372 PerlMem_free(esal); 6373 PerlMem_free(vmsdir); 6374 return NULL; 6375 } 6376 term = *cp1; 6377 *cp1 = '\0'; 6378 retlen = strlen(my_esa); 6379 cp1 = strrchr(my_esa,'.'); 6380 /* ODS-5 directory specifications can have extra "." in them. */ 6381 /* Fix-me, can not scan EFS file specifications backwards */ 6382 while (cp1 != NULL) { 6383 if ((cp1-1 == my_esa) || (*(cp1-1) != '^')) 6384 break; 6385 else { 6386 cp1--; 6387 while ((cp1 > my_esa) && (*cp1 != '.')) 6388 cp1--; 6389 } 6390 if (cp1 == my_esa) 6391 cp1 = NULL; 6392 } 6393 6394 if ((cp1) != NULL) { 6395 /* There's more than one directory in the path. Just roll back. */ 6396 *cp1 = term; 6397 my_strlcpy(buf, my_esa, VMS_MAXRSS); 6398 } 6399 else { 6400 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) { 6401 /* Go back and expand rooted logical name */ 6402 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL); 6403 #ifdef NAM$M_NO_SHORT_UPCASE 6404 if (decc_efs_case_preserve) 6405 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6406 #endif 6407 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) { 6408 sts = rms_free_search_context(&dirfab); 6409 PerlMem_free(esa); 6410 if (esal != NULL) 6411 PerlMem_free(esal); 6412 PerlMem_free(trndir); 6413 PerlMem_free(vmsdir); 6414 set_errno(EVMSERR); 6415 set_vaxc_errno(dirfab.fab$l_sts); 6416 return NULL; 6417 } 6418 6419 /* This changes the length of the string of course */ 6420 if (esal != NULL) { 6421 my_esa_len = rms_nam_esll(dirnam); 6422 } else { 6423 my_esa_len = rms_nam_esl(dirnam); 6424 } 6425 6426 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */ 6427 cp1 = strstr(my_esa,"]["); 6428 if (!cp1) cp1 = strstr(my_esa,"]<"); 6429 dirlen = cp1 - my_esa; 6430 memcpy(buf, my_esa, dirlen); 6431 if (!strncmp(cp1+2,"000000]",7)) { 6432 buf[dirlen-1] = '\0'; 6433 /* fix-me Not full ODS-5, just extra dots in directories for now */ 6434 cp1 = buf + dirlen - 1; 6435 while (cp1 > buf) 6436 { 6437 if (*cp1 == '[') 6438 break; 6439 if (*cp1 == '.') { 6440 if (*(cp1-1) != '^') 6441 break; 6442 } 6443 cp1--; 6444 } 6445 if (*cp1 == '.') *cp1 = ']'; 6446 else { 6447 memmove(cp1+8, cp1+1, buf+dirlen-cp1); 6448 memmove(cp1+1,"000000]",7); 6449 } 6450 } 6451 else { 6452 memmove(buf+dirlen, cp1+2, retlen-dirlen); 6453 buf[retlen] = '\0'; 6454 /* Convert last '.' to ']' */ 6455 cp1 = buf+retlen-1; 6456 while (*cp != '[') { 6457 cp1--; 6458 if (*cp1 == '.') { 6459 /* Do not trip on extra dots in ODS-5 directories */ 6460 if ((cp1 == buf) || (*(cp1-1) != '^')) 6461 break; 6462 } 6463 } 6464 if (*cp1 == '.') *cp1 = ']'; 6465 else { 6466 memmove(cp1+8, cp1+1, buf+dirlen-cp1); 6467 memmove(cp1+1,"000000]",7); 6468 } 6469 } 6470 } 6471 else { /* This is a top-level dir. Add the MFD to the path. */ 6472 cp1 = strrchr(my_esa, ':'); 6473 assert(cp1); 6474 memmove(buf, my_esa, cp1 - my_esa + 1); 6475 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8); 6476 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2)); 6477 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */ 6478 } 6479 } 6480 sts = rms_free_search_context(&dirfab); 6481 /* We've set up the string up through the filename. Add the 6482 type and version, and we're done. */ 6483 strcat(buf,".DIR;1"); 6484 6485 /* $PARSE may have upcased filespec, so convert output to lower 6486 * case if input contained any lowercase characters. */ 6487 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf); 6488 PerlMem_free(trndir); 6489 PerlMem_free(esa); 6490 if (esal != NULL) 6491 PerlMem_free(esal); 6492 PerlMem_free(vmsdir); 6493 return buf; 6494 } 6495 } /* end of int_fileify_dirspec() */ 6496 6497 6498 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 6499 static char * 6500 mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl) 6501 { 6502 static char __fileify_retbuf[VMS_MAXRSS]; 6503 char * fileified, *ret_spec, *ret_buf; 6504 6505 fileified = NULL; 6506 ret_buf = buf; 6507 if (ret_buf == NULL) { 6508 if (ts) { 6509 Newx(fileified, VMS_MAXRSS, char); 6510 if (fileified == NULL) 6511 _ckvmssts(SS$_INSFMEM); 6512 ret_buf = fileified; 6513 } else { 6514 ret_buf = __fileify_retbuf; 6515 } 6516 } 6517 6518 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl); 6519 6520 if (ret_spec == NULL) { 6521 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 6522 if (fileified) 6523 Safefree(fileified); 6524 } 6525 6526 return ret_spec; 6527 } /* end of do_fileify_dirspec() */ 6528 /*}}}*/ 6529 6530 /* External entry points */ 6531 char * 6532 Perl_fileify_dirspec(pTHX_ const char *dir, char *buf) 6533 { 6534 return do_fileify_dirspec(dir, buf, 0, NULL); 6535 } 6536 6537 char * 6538 Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf) 6539 { 6540 return do_fileify_dirspec(dir, buf, 1, NULL); 6541 } 6542 6543 char * 6544 Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl) 6545 { 6546 return do_fileify_dirspec(dir, buf, 0, utf8_fl); 6547 } 6548 6549 char * 6550 Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl) 6551 { 6552 return do_fileify_dirspec(dir, buf, 1, utf8_fl); 6553 } 6554 6555 static char * 6556 int_pathify_dirspec_simple(const char * dir, char * buf, 6557 char * v_spec, int v_len, char * r_spec, int r_len, 6558 char * d_spec, int d_len, char * n_spec, int n_len, 6559 char * e_spec, int e_len, char * vs_spec, int vs_len) 6560 { 6561 6562 /* VMS specification - Try to do this the simple way */ 6563 if ((v_len + r_len > 0) || (d_len > 0)) { 6564 int is_dir; 6565 6566 /* No name or extension component, already a directory */ 6567 if ((n_len + e_len + vs_len) == 0) { 6568 strcpy(buf, dir); 6569 return buf; 6570 } 6571 6572 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */ 6573 /* This results from catfile() being used instead of catdir() */ 6574 /* So even though it should not work, we need to allow it */ 6575 6576 /* If this is .DIR;1 then do a simple conversion */ 6577 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len); 6578 if (is_dir || (e_len == 0) && (d_len > 0)) { 6579 int len; 6580 len = v_len + r_len + d_len - 1; 6581 char dclose = d_spec[d_len - 1]; 6582 memcpy(buf, dir, len); 6583 buf[len] = '.'; 6584 len++; 6585 memcpy(&buf[len], n_spec, n_len); 6586 len += n_len; 6587 buf[len] = dclose; 6588 buf[len + 1] = '\0'; 6589 return buf; 6590 } 6591 6592 #ifdef HAS_SYMLINK 6593 else if (d_len > 0) { 6594 /* In the olden days, a directory needed to have a .DIR */ 6595 /* extension to be a valid directory, but now it could */ 6596 /* be a symbolic link */ 6597 int len; 6598 len = v_len + r_len + d_len - 1; 6599 char dclose = d_spec[d_len - 1]; 6600 memcpy(buf, dir, len); 6601 buf[len] = '.'; 6602 len++; 6603 memcpy(&buf[len], n_spec, n_len); 6604 len += n_len; 6605 if (e_len > 0) { 6606 if (decc_efs_charset) { 6607 if (e_len == 4 6608 && (toupper(e_spec[1]) == 'D') 6609 && (toupper(e_spec[2]) == 'I') 6610 && (toupper(e_spec[3]) == 'R')) { 6611 6612 /* Corner case: directory spec with invalid version. 6613 * Valid would have followed is_dir path above. 6614 */ 6615 SETERRNO(ENOTDIR, RMS$_DIR); 6616 return NULL; 6617 } 6618 else { 6619 buf[len] = '^'; 6620 len++; 6621 memcpy(&buf[len], e_spec, e_len); 6622 len += e_len; 6623 } 6624 } 6625 else { 6626 SETERRNO(ENOTDIR, RMS$_DIR); 6627 return NULL; 6628 } 6629 } 6630 buf[len] = dclose; 6631 buf[len + 1] = '\0'; 6632 return buf; 6633 } 6634 #else 6635 else { 6636 set_vaxc_errno(RMS$_DIR); 6637 set_errno(ENOTDIR); 6638 return NULL; 6639 } 6640 #endif 6641 } 6642 set_vaxc_errno(RMS$_DIR); 6643 set_errno(ENOTDIR); 6644 return NULL; 6645 } 6646 6647 6648 /* Internal routine to make sure or convert a directory to be in a */ 6649 /* path specification. No utf8 flag because it is not changed or used */ 6650 static char * 6651 int_pathify_dirspec(const char *dir, char *buf) 6652 { 6653 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 6654 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 6655 char * exp_spec, *ret_spec; 6656 char * trndir; 6657 unsigned short int trnlnm_iter_count; 6658 STRLEN trnlen; 6659 int need_to_lower; 6660 6661 if (vms_debug_fileify) { 6662 if (dir == NULL) 6663 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n"); 6664 else 6665 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir); 6666 } 6667 6668 /* We may need to lower case the result if we translated */ 6669 /* a logical name or got the current working directory */ 6670 need_to_lower = 0; 6671 6672 if (!dir || !*dir) { 6673 set_errno(EINVAL); 6674 set_vaxc_errno(SS$_BADPARAM); 6675 return NULL; 6676 } 6677 6678 trndir = (char *)PerlMem_malloc(VMS_MAXRSS); 6679 if (trndir == NULL) 6680 _ckvmssts_noperl(SS$_INSFMEM); 6681 6682 /* If no directory specified use the current default */ 6683 if (*dir) 6684 my_strlcpy(trndir, dir, VMS_MAXRSS); 6685 else { 6686 getcwd(trndir, VMS_MAXRSS - 1); 6687 need_to_lower = 1; 6688 } 6689 6690 /* now deal with bare names that could be logical names */ 6691 trnlnm_iter_count = 0; 6692 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords 6693 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) { 6694 trnlnm_iter_count++; 6695 need_to_lower = 1; 6696 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) 6697 break; 6698 trnlen = strlen(trndir); 6699 6700 /* Trap simple rooted lnms, and return lnm:[000000] */ 6701 if (!strcmp(trndir+trnlen-2,".]")) { 6702 my_strlcpy(buf, dir, VMS_MAXRSS); 6703 strcat(buf, ":[000000]"); 6704 PerlMem_free(trndir); 6705 6706 if (vms_debug_fileify) { 6707 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf); 6708 } 6709 return buf; 6710 } 6711 } 6712 6713 /* At this point we do not work with *dir, but the copy in *trndir */ 6714 6715 if (need_to_lower && !decc_efs_case_preserve) { 6716 /* Legacy mode, lower case the returned value */ 6717 __mystrtolower(trndir); 6718 } 6719 6720 6721 /* Some special cases, '..', '.' */ 6722 sts = 0; 6723 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) { 6724 /* Force UNIX filespec */ 6725 sts = 1; 6726 6727 } else { 6728 /* Is this Unix or VMS format? */ 6729 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len, 6730 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 6731 &e_len, &vs_spec, &vs_len); 6732 if (sts == 0) { 6733 6734 /* Just a filename? */ 6735 if ((v_len + r_len + d_len) == 0) { 6736 6737 /* Now we have a problem, this could be Unix or VMS */ 6738 /* We have to guess. .DIR usually means VMS */ 6739 6740 /* In UNIX report mode, the .DIR extension is removed */ 6741 /* if one shows up, it is for a non-directory or a directory */ 6742 /* in EFS charset mode */ 6743 6744 /* So if we are in Unix report mode, assume that this */ 6745 /* is a relative Unix directory specification */ 6746 6747 sts = 1; 6748 if (!decc_filename_unix_report && decc_efs_charset) { 6749 int is_dir; 6750 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len); 6751 6752 if (is_dir) { 6753 /* Traditional mode, assume .DIR is directory */ 6754 buf[0] = '['; 6755 buf[1] = '.'; 6756 memcpy(&buf[2], n_spec, n_len); 6757 buf[n_len + 2] = ']'; 6758 buf[n_len + 3] = '\0'; 6759 PerlMem_free(trndir); 6760 if (vms_debug_fileify) { 6761 fprintf(stderr, 6762 "int_pathify_dirspec: buf = %s\n", 6763 buf); 6764 } 6765 return buf; 6766 } 6767 } 6768 } 6769 } 6770 } 6771 if (sts == 0) { 6772 ret_spec = int_pathify_dirspec_simple(trndir, buf, 6773 v_spec, v_len, r_spec, r_len, 6774 d_spec, d_len, n_spec, n_len, 6775 e_spec, e_len, vs_spec, vs_len); 6776 6777 if (ret_spec != NULL) { 6778 PerlMem_free(trndir); 6779 if (vms_debug_fileify) { 6780 fprintf(stderr, 6781 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6782 } 6783 return ret_spec; 6784 } 6785 6786 /* Simple way did not work, which means that a logical name */ 6787 /* was present for the directory specification. */ 6788 /* Need to use an rmsexpand variant to decode it completely */ 6789 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS); 6790 if (exp_spec == NULL) 6791 _ckvmssts_noperl(SS$_INSFMEM); 6792 6793 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG); 6794 if (ret_spec != NULL) { 6795 sts = vms_split_path(exp_spec, &v_spec, &v_len, 6796 &r_spec, &r_len, &d_spec, &d_len, 6797 &n_spec, &n_len, &e_spec, 6798 &e_len, &vs_spec, &vs_len); 6799 if (sts == 0) { 6800 ret_spec = int_pathify_dirspec_simple( 6801 exp_spec, buf, v_spec, v_len, r_spec, r_len, 6802 d_spec, d_len, n_spec, n_len, 6803 e_spec, e_len, vs_spec, vs_len); 6804 6805 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) { 6806 /* Legacy mode, lower case the returned value */ 6807 __mystrtolower(ret_spec); 6808 } 6809 } else { 6810 set_vaxc_errno(RMS$_DIR); 6811 set_errno(ENOTDIR); 6812 ret_spec = NULL; 6813 } 6814 } 6815 PerlMem_free(exp_spec); 6816 PerlMem_free(trndir); 6817 if (vms_debug_fileify) { 6818 if (ret_spec == NULL) 6819 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n"); 6820 else 6821 fprintf(stderr, 6822 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6823 } 6824 return ret_spec; 6825 6826 } else { 6827 /* Unix specification, Could be trivial conversion, */ 6828 /* but have to deal with trailing '.dir' or extra '.' */ 6829 6830 char * lastdot; 6831 char * lastslash; 6832 int is_dir; 6833 STRLEN dir_len = strlen(trndir); 6834 6835 lastslash = strrchr(trndir, '/'); 6836 if (lastslash == NULL) 6837 lastslash = trndir; 6838 else 6839 lastslash++; 6840 6841 lastdot = NULL; 6842 6843 /* '..' or '.' are valid directory components */ 6844 is_dir = 0; 6845 if (lastslash[0] == '.') { 6846 if (lastslash[1] == '\0') { 6847 is_dir = 1; 6848 } else if (lastslash[1] == '.') { 6849 if (lastslash[2] == '\0') { 6850 is_dir = 1; 6851 } else { 6852 /* And finally allow '...' */ 6853 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) { 6854 is_dir = 1; 6855 } 6856 } 6857 } 6858 } 6859 6860 if (!is_dir) { 6861 lastdot = strrchr(lastslash, '.'); 6862 } 6863 if (lastdot != NULL) { 6864 STRLEN e_len; 6865 /* '.dir' is discarded, and any other '.' is invalid */ 6866 e_len = strlen(lastdot); 6867 6868 is_dir = is_dir_ext(lastdot, e_len, NULL, 0); 6869 6870 if (is_dir) { 6871 dir_len = dir_len - 4; 6872 } 6873 } 6874 6875 my_strlcpy(buf, trndir, VMS_MAXRSS); 6876 if (buf[dir_len - 1] != '/') { 6877 buf[dir_len] = '/'; 6878 buf[dir_len + 1] = '\0'; 6879 } 6880 6881 /* Under ODS-2 rules, '.' becomes '_', so fix it up */ 6882 if (!decc_efs_charset) { 6883 int dir_start = 0; 6884 char * str = buf; 6885 if (str[0] == '.') { 6886 char * dots = str; 6887 int cnt = 1; 6888 while ((dots[cnt] == '.') && (cnt < 3)) 6889 cnt++; 6890 if (cnt <= 3) { 6891 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) { 6892 dir_start = 1; 6893 str += cnt; 6894 } 6895 } 6896 } 6897 for (; *str; ++str) { 6898 while (*str == '/') { 6899 dir_start = 1; 6900 *str++; 6901 } 6902 if (dir_start) { 6903 6904 /* Have to skip up to three dots which could be */ 6905 /* directories, 3 dots being a VMS extension for Perl */ 6906 char * dots = str; 6907 int cnt = 0; 6908 while ((dots[cnt] == '.') && (cnt < 3)) { 6909 cnt++; 6910 } 6911 if (dots[cnt] == '\0') 6912 break; 6913 if ((cnt > 1) && (dots[cnt] != '/')) { 6914 dir_start = 0; 6915 } else { 6916 str += cnt; 6917 } 6918 6919 /* too many dots? */ 6920 if ((cnt == 0) || (cnt > 3)) { 6921 dir_start = 0; 6922 } 6923 } 6924 if (!dir_start && (*str == '.')) { 6925 *str = '_'; 6926 } 6927 } 6928 } 6929 PerlMem_free(trndir); 6930 ret_spec = buf; 6931 if (vms_debug_fileify) { 6932 if (ret_spec == NULL) 6933 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n"); 6934 else 6935 fprintf(stderr, 6936 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6937 } 6938 return ret_spec; 6939 } 6940 } 6941 6942 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/ 6943 static char * 6944 mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl) 6945 { 6946 static char __pathify_retbuf[VMS_MAXRSS]; 6947 char * pathified, *ret_spec, *ret_buf; 6948 6949 pathified = NULL; 6950 ret_buf = buf; 6951 if (ret_buf == NULL) { 6952 if (ts) { 6953 Newx(pathified, VMS_MAXRSS, char); 6954 if (pathified == NULL) 6955 _ckvmssts(SS$_INSFMEM); 6956 ret_buf = pathified; 6957 } else { 6958 ret_buf = __pathify_retbuf; 6959 } 6960 } 6961 6962 ret_spec = int_pathify_dirspec(dir, ret_buf); 6963 6964 if (ret_spec == NULL) { 6965 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 6966 if (pathified) 6967 Safefree(pathified); 6968 } 6969 6970 return ret_spec; 6971 6972 } /* end of do_pathify_dirspec() */ 6973 6974 6975 /* External entry points */ 6976 char * 6977 Perl_pathify_dirspec(pTHX_ const char *dir, char *buf) 6978 { 6979 return do_pathify_dirspec(dir, buf, 0, NULL); 6980 } 6981 6982 char * 6983 Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf) 6984 { 6985 return do_pathify_dirspec(dir, buf, 1, NULL); 6986 } 6987 6988 char * 6989 Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl) 6990 { 6991 return do_pathify_dirspec(dir, buf, 0, utf8_fl); 6992 } 6993 6994 char * 6995 Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl) 6996 { 6997 return do_pathify_dirspec(dir, buf, 1, utf8_fl); 6998 } 6999 7000 /* Internal tounixspec routine that does not use a thread context */ 7001 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/ 7002 static char * 7003 int_tounixspec(const char *spec, char *rslt, int * utf8_fl) 7004 { 7005 char *dirend, *cp1, *cp3, *tmp; 7006 const char *cp2; 7007 int dirlen; 7008 unsigned short int trnlnm_iter_count; 7009 int cmp_rslt, outchars_added; 7010 if (utf8_fl != NULL) 7011 *utf8_fl = 0; 7012 7013 if (vms_debug_fileify) { 7014 if (spec == NULL) 7015 fprintf(stderr, "int_tounixspec: spec = NULL\n"); 7016 else 7017 fprintf(stderr, "int_tounixspec: spec = %s\n", spec); 7018 } 7019 7020 7021 if (spec == NULL) { 7022 set_errno(EINVAL); 7023 set_vaxc_errno(SS$_BADPARAM); 7024 return NULL; 7025 } 7026 if (strlen(spec) > (VMS_MAXRSS-1)) { 7027 set_errno(E2BIG); 7028 set_vaxc_errno(SS$_BUFFEROVF); 7029 return NULL; 7030 } 7031 7032 /* New VMS specific format needs translation 7033 * glob passes filenames with trailing '\n' and expects this preserved. 7034 */ 7035 if (decc_posix_compliant_pathnames) { 7036 if (strncmp(spec, "\"^UP^", 5) == 0) { 7037 char * uspec; 7038 char *tunix; 7039 int tunix_len; 7040 int nl_flag; 7041 7042 tunix = (char *)PerlMem_malloc(VMS_MAXRSS); 7043 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7044 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS); 7045 nl_flag = 0; 7046 if (tunix[tunix_len - 1] == '\n') { 7047 tunix[tunix_len - 1] = '\"'; 7048 tunix[tunix_len] = '\0'; 7049 tunix_len--; 7050 nl_flag = 1; 7051 } 7052 uspec = decc$translate_vms(tunix); 7053 PerlMem_free(tunix); 7054 if ((int)uspec > 0) { 7055 my_strlcpy(rslt, uspec, VMS_MAXRSS); 7056 if (nl_flag) { 7057 strcat(rslt,"\n"); 7058 } 7059 else { 7060 /* If we can not translate it, makemaker wants as-is */ 7061 my_strlcpy(rslt, spec, VMS_MAXRSS); 7062 } 7063 return rslt; 7064 } 7065 } 7066 } 7067 7068 cmp_rslt = 0; /* Presume VMS */ 7069 cp1 = strchr(spec, '/'); 7070 if (cp1 == NULL) 7071 cmp_rslt = 0; 7072 7073 /* Look for EFS ^/ */ 7074 if (decc_efs_charset) { 7075 while (cp1 != NULL) { 7076 cp2 = cp1 - 1; 7077 if (*cp2 != '^') { 7078 /* Found illegal VMS, assume UNIX */ 7079 cmp_rslt = 1; 7080 break; 7081 } 7082 cp1++; 7083 cp1 = strchr(cp1, '/'); 7084 } 7085 } 7086 7087 /* Look for "." and ".." */ 7088 if (decc_filename_unix_report) { 7089 if (spec[0] == '.') { 7090 if ((spec[1] == '\0') || (spec[1] == '\n')) { 7091 cmp_rslt = 1; 7092 } 7093 else { 7094 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) { 7095 cmp_rslt = 1; 7096 } 7097 } 7098 } 7099 } 7100 7101 cp1 = rslt; 7102 cp2 = spec; 7103 7104 /* This is already UNIX or at least nothing VMS understands, 7105 * so all we can reasonably do is unescape extended chars. 7106 */ 7107 if (cmp_rslt) { 7108 while (*cp2) { 7109 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7110 cp1 += outchars_added; 7111 } 7112 *cp1 = '\0'; 7113 if (vms_debug_fileify) { 7114 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 7115 } 7116 return rslt; 7117 } 7118 7119 dirend = strrchr(spec,']'); 7120 if (dirend == NULL) dirend = strrchr(spec,'>'); 7121 if (dirend == NULL) dirend = strchr(spec,':'); 7122 if (dirend == NULL) { 7123 while (*cp2) { 7124 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7125 cp1 += outchars_added; 7126 } 7127 *cp1 = '\0'; 7128 if (vms_debug_fileify) { 7129 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 7130 } 7131 return rslt; 7132 } 7133 7134 /* Special case 1 - sys$posix_root = / */ 7135 if (!decc_disable_posix_root) { 7136 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) { 7137 *cp1 = '/'; 7138 cp1++; 7139 cp2 = cp2 + 15; 7140 } 7141 } 7142 7143 /* Special case 2 - Convert NLA0: to /dev/null */ 7144 cmp_rslt = strncasecmp(spec,"NLA0:", 5); 7145 if (cmp_rslt == 0) { 7146 strcpy(rslt, "/dev/null"); 7147 cp1 = cp1 + 9; 7148 cp2 = cp2 + 5; 7149 if (spec[6] != '\0') { 7150 cp1[9] = '/'; 7151 cp1++; 7152 cp2++; 7153 } 7154 } 7155 7156 /* Also handle special case "SYS$SCRATCH:" */ 7157 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12); 7158 tmp = (char *)PerlMem_malloc(VMS_MAXRSS); 7159 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7160 if (cmp_rslt == 0) { 7161 int islnm; 7162 7163 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1); 7164 if (!islnm) { 7165 strcpy(rslt, "/tmp"); 7166 cp1 = cp1 + 4; 7167 cp2 = cp2 + 12; 7168 if (spec[12] != '\0') { 7169 cp1[4] = '/'; 7170 cp1++; 7171 cp2++; 7172 } 7173 } 7174 } 7175 7176 if (*cp2 != '[' && *cp2 != '<') { 7177 *(cp1++) = '/'; 7178 } 7179 else { /* the VMS spec begins with directories */ 7180 cp2++; 7181 if (*cp2 == ']' || *cp2 == '>') { 7182 *(cp1++) = '.'; 7183 *(cp1++) = '/'; 7184 } 7185 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */ 7186 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) { 7187 PerlMem_free(tmp); 7188 if (vms_debug_fileify) { 7189 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7190 } 7191 return NULL; 7192 } 7193 trnlnm_iter_count = 0; 7194 do { 7195 cp3 = tmp; 7196 while (*cp3 != ':' && *cp3) cp3++; 7197 *(cp3++) = '\0'; 7198 if (strchr(cp3,']') != NULL) break; 7199 trnlnm_iter_count++; 7200 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break; 7201 } while (vmstrnenv(tmp,tmp,0,fildev,0)); 7202 cp1 = rslt; 7203 cp3 = tmp; 7204 *(cp1++) = '/'; 7205 while (*cp3) { 7206 *(cp1++) = *(cp3++); 7207 if (cp1 - rslt > (VMS_MAXRSS - 1)) { 7208 PerlMem_free(tmp); 7209 set_errno(ENAMETOOLONG); 7210 set_vaxc_errno(SS$_BUFFEROVF); 7211 if (vms_debug_fileify) { 7212 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7213 } 7214 return NULL; /* No room */ 7215 } 7216 } 7217 *(cp1++) = '/'; 7218 } 7219 if ((*cp2 == '^')) { 7220 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7221 cp1 += outchars_added; 7222 } 7223 else if ( *cp2 == '.') { 7224 if (*(cp2+1) == '.' && *(cp2+2) == '.') { 7225 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 7226 cp2 += 3; 7227 } 7228 else cp2++; 7229 } 7230 } 7231 PerlMem_free(tmp); 7232 for (; cp2 <= dirend; cp2++) { 7233 if ((*cp2 == '^')) { 7234 /* EFS file escape -- unescape it. */ 7235 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added) - 1; 7236 cp1 += outchars_added; 7237 } 7238 else if (*cp2 == ':') { 7239 *(cp1++) = '/'; 7240 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 7241 } 7242 else if (*cp2 == ']' || *cp2 == '>') { 7243 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */ 7244 } 7245 else if ((*cp2 == '.') && (*cp2-1 != '^')) { 7246 *(cp1++) = '/'; 7247 if (*(cp2+1) == ']' || *(cp2+1) == '>') { 7248 while (*(cp2+1) == ']' || *(cp2+1) == '>' || 7249 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 7250 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' || 7251 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7; 7252 } 7253 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') { 7254 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/'; 7255 cp2 += 2; 7256 } 7257 } 7258 else if (*cp2 == '-') { 7259 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') { 7260 while (*cp2 == '-') { 7261 cp2++; 7262 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 7263 } 7264 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */ 7265 /* filespecs like */ 7266 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */ 7267 if (vms_debug_fileify) { 7268 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7269 } 7270 return NULL; 7271 } 7272 } 7273 else *(cp1++) = *cp2; 7274 } 7275 else *(cp1++) = *cp2; 7276 } 7277 /* Translate the rest of the filename. */ 7278 while (*cp2) { 7279 int dot_seen = 0; 7280 switch(*cp2) { 7281 /* Fixme - for compatibility with the CRTL we should be removing */ 7282 /* spaces from the file specifications, but this may show that */ 7283 /* some tests that were appearing to pass are not really passing */ 7284 case '%': 7285 cp2++; 7286 *(cp1++) = '?'; 7287 break; 7288 case '^': 7289 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7290 cp1 += outchars_added; 7291 break; 7292 case ';': 7293 if (decc_filename_unix_no_version) { 7294 /* Easy, drop the version */ 7295 while (*cp2) 7296 cp2++; 7297 break; 7298 } else { 7299 /* Punt - passing the version as a dot will probably */ 7300 /* break perl in weird ways, but so did passing */ 7301 /* through the ; as a version. Follow the CRTL and */ 7302 /* hope for the best. */ 7303 cp2++; 7304 *(cp1++) = '.'; 7305 } 7306 break; 7307 case '.': 7308 if (dot_seen) { 7309 /* We will need to fix this properly later */ 7310 /* As Perl may be installed on an ODS-5 volume, but not */ 7311 /* have the EFS_CHARSET enabled, it still may encounter */ 7312 /* filenames with extra dots in them, and a precedent got */ 7313 /* set which allowed them to work, that we will uphold here */ 7314 /* If extra dots are present in a name and no ^ is on them */ 7315 /* VMS assumes that the first one is the extension delimiter */ 7316 /* the rest have an implied ^. */ 7317 7318 /* this is also a conflict as the . is also a version */ 7319 /* delimiter in VMS, */ 7320 7321 *(cp1++) = *(cp2++); 7322 break; 7323 } 7324 dot_seen = 1; 7325 /* This is an extension */ 7326 if (decc_readdir_dropdotnotype) { 7327 cp2++; 7328 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) { 7329 /* Drop the dot for the extension */ 7330 break; 7331 } else { 7332 *(cp1++) = '.'; 7333 } 7334 break; 7335 } 7336 default: 7337 *(cp1++) = *(cp2++); 7338 } 7339 } 7340 *cp1 = '\0'; 7341 7342 /* This still leaves /000000/ when working with a 7343 * VMS device root or concealed root. 7344 */ 7345 { 7346 int ulen; 7347 char * zeros; 7348 7349 ulen = strlen(rslt); 7350 7351 /* Get rid of "000000/ in rooted filespecs */ 7352 if (ulen > 7) { 7353 zeros = strstr(rslt, "/000000/"); 7354 if (zeros != NULL) { 7355 int mlen; 7356 mlen = ulen - (zeros - rslt) - 7; 7357 memmove(zeros, &zeros[7], mlen); 7358 ulen = ulen - 7; 7359 rslt[ulen] = '\0'; 7360 } 7361 } 7362 } 7363 7364 if (vms_debug_fileify) { 7365 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 7366 } 7367 return rslt; 7368 7369 } /* end of int_tounixspec() */ 7370 7371 7372 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/ 7373 static char * 7374 mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl) 7375 { 7376 static char __tounixspec_retbuf[VMS_MAXRSS]; 7377 char * unixspec, *ret_spec, *ret_buf; 7378 7379 unixspec = NULL; 7380 ret_buf = buf; 7381 if (ret_buf == NULL) { 7382 if (ts) { 7383 Newx(unixspec, VMS_MAXRSS, char); 7384 if (unixspec == NULL) 7385 _ckvmssts(SS$_INSFMEM); 7386 ret_buf = unixspec; 7387 } else { 7388 ret_buf = __tounixspec_retbuf; 7389 } 7390 } 7391 7392 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl); 7393 7394 if (ret_spec == NULL) { 7395 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 7396 if (unixspec) 7397 Safefree(unixspec); 7398 } 7399 7400 return ret_spec; 7401 7402 } /* end of do_tounixspec() */ 7403 /*}}}*/ 7404 /* External entry points */ 7405 char * 7406 Perl_tounixspec(pTHX_ const char *spec, char *buf) 7407 { 7408 return do_tounixspec(spec, buf, 0, NULL); 7409 } 7410 7411 char * 7412 Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) 7413 { 7414 return do_tounixspec(spec,buf,1, NULL); 7415 } 7416 7417 char * 7418 Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl) 7419 { 7420 return do_tounixspec(spec,buf,0, utf8_fl); 7421 } 7422 7423 char * 7424 Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl) 7425 { 7426 return do_tounixspec(spec,buf,1, utf8_fl); 7427 } 7428 7429 /* 7430 This procedure is used to identify if a path is based in either 7431 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and 7432 it returns the OpenVMS format directory for it. 7433 7434 It is expecting specifications of only '/' or '/xxxx/' 7435 7436 If a posix root does not exist, or 'xxxx' is not a directory 7437 in the posix root, it returns a failure. 7438 7439 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7. 7440 7441 It is used only internally by posix_to_vmsspec_hardway(). 7442 */ 7443 7444 static int 7445 posix_root_to_vms(char *vmspath, int vmspath_len, 7446 const char *unixpath, const int * utf8_fl) 7447 { 7448 int sts; 7449 struct FAB myfab = cc$rms_fab; 7450 rms_setup_nam(mynam); 7451 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 7452 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 7453 char * esa, * esal, * rsa, * rsal; 7454 int dir_flag; 7455 int unixlen; 7456 7457 dir_flag = 0; 7458 vmspath[0] = '\0'; 7459 unixlen = strlen(unixpath); 7460 if (unixlen == 0) { 7461 return RMS$_FNF; 7462 } 7463 7464 #if __CRTL_VER >= 80200000 7465 /* If not a posix spec already, convert it */ 7466 if (decc_posix_compliant_pathnames) { 7467 if (strncmp(unixpath,"\"^UP^",5) != 0) { 7468 sprintf(vmspath,"\"^UP^%s\"",unixpath); 7469 } 7470 else { 7471 /* This is already a VMS specification, no conversion */ 7472 unixlen--; 7473 my_strlcpy(vmspath, unixpath, vmspath_len + 1); 7474 } 7475 } 7476 else 7477 #endif 7478 { 7479 int path_len; 7480 int i,j; 7481 7482 /* Check to see if this is under the POSIX root */ 7483 if (decc_disable_posix_root) { 7484 return RMS$_FNF; 7485 } 7486 7487 /* Skip leading / */ 7488 if (unixpath[0] == '/') { 7489 unixpath++; 7490 unixlen--; 7491 } 7492 7493 7494 strcpy(vmspath,"SYS$POSIX_ROOT:"); 7495 7496 /* If this is only the / , or blank, then... */ 7497 if (unixpath[0] == '\0') { 7498 /* by definition, this is the answer */ 7499 return SS$_NORMAL; 7500 } 7501 7502 /* Need to look up a directory */ 7503 vmspath[15] = '['; 7504 vmspath[16] = '\0'; 7505 7506 /* Copy and add '^' escape characters as needed */ 7507 j = 16; 7508 i = 0; 7509 while (unixpath[i] != 0) { 7510 int k; 7511 7512 j += copy_expand_unix_filename_escape 7513 (&vmspath[j], &unixpath[i], &k, utf8_fl); 7514 i += k; 7515 } 7516 7517 path_len = strlen(vmspath); 7518 if (vmspath[path_len - 1] == '/') 7519 path_len--; 7520 vmspath[path_len] = ']'; 7521 path_len++; 7522 vmspath[path_len] = '\0'; 7523 7524 } 7525 vmspath[vmspath_len] = 0; 7526 if (unixpath[unixlen - 1] == '/') 7527 dir_flag = 1; 7528 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 7529 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7530 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 7531 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7532 rsal = (char *)PerlMem_malloc(VMS_MAXRSS); 7533 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7534 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 7535 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7536 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */ 7537 rms_bind_fab_nam(myfab, mynam); 7538 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1); 7539 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1); 7540 if (decc_efs_case_preserve) 7541 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE; 7542 #ifdef NAML$M_OPEN_SPECIAL 7543 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL; 7544 #endif 7545 7546 /* Set up the remaining naml fields */ 7547 sts = sys$parse(&myfab); 7548 7549 /* It failed! Try again as a UNIX filespec */ 7550 if (!(sts & 1)) { 7551 PerlMem_free(esal); 7552 PerlMem_free(esa); 7553 PerlMem_free(rsal); 7554 PerlMem_free(rsa); 7555 return sts; 7556 } 7557 7558 /* get the Device ID and the FID */ 7559 sts = sys$search(&myfab); 7560 7561 /* These are no longer needed */ 7562 PerlMem_free(esa); 7563 PerlMem_free(rsal); 7564 PerlMem_free(rsa); 7565 7566 /* on any failure, returned the POSIX ^UP^ filespec */ 7567 if (!(sts & 1)) { 7568 PerlMem_free(esal); 7569 return sts; 7570 } 7571 specdsc.dsc$a_pointer = vmspath; 7572 specdsc.dsc$w_length = vmspath_len; 7573 7574 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1]; 7575 dvidsc.dsc$w_length = mynam.naml$t_dvi[0]; 7576 sts = lib$fid_to_name 7577 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length); 7578 7579 /* on any failure, returned the POSIX ^UP^ filespec */ 7580 if (!(sts & 1)) { 7581 /* This can happen if user does not have permission to read directories */ 7582 if (strncmp(unixpath,"\"^UP^",5) != 0) 7583 sprintf(vmspath,"\"^UP^%s\"",unixpath); 7584 else 7585 my_strlcpy(vmspath, unixpath, vmspath_len + 1); 7586 } 7587 else { 7588 vmspath[specdsc.dsc$w_length] = 0; 7589 7590 /* Are we expecting a directory? */ 7591 if (dir_flag != 0) { 7592 int i; 7593 char *eptr; 7594 7595 eptr = NULL; 7596 7597 i = specdsc.dsc$w_length - 1; 7598 while (i > 0) { 7599 int zercnt; 7600 zercnt = 0; 7601 /* Version must be '1' */ 7602 if (vmspath[i--] != '1') 7603 break; 7604 /* Version delimiter is one of ".;" */ 7605 if ((vmspath[i] != '.') && (vmspath[i] != ';')) 7606 break; 7607 i--; 7608 if (vmspath[i--] != 'R') 7609 break; 7610 if (vmspath[i--] != 'I') 7611 break; 7612 if (vmspath[i--] != 'D') 7613 break; 7614 if (vmspath[i--] != '.') 7615 break; 7616 eptr = &vmspath[i+1]; 7617 while (i > 0) { 7618 if ((vmspath[i] == ']') || (vmspath[i] == '>')) { 7619 if (vmspath[i-1] != '^') { 7620 if (zercnt != 6) { 7621 *eptr = vmspath[i]; 7622 eptr[1] = '\0'; 7623 vmspath[i] = '.'; 7624 break; 7625 } 7626 else { 7627 /* Get rid of 6 imaginary zero directory filename */ 7628 vmspath[i+1] = '\0'; 7629 } 7630 } 7631 } 7632 if (vmspath[i] == '0') 7633 zercnt++; 7634 else 7635 zercnt = 10; 7636 i--; 7637 } 7638 break; 7639 } 7640 } 7641 } 7642 PerlMem_free(esal); 7643 return sts; 7644 } 7645 7646 /* /dev/mumble needs to be handled special. 7647 /dev/null becomes NLA0:, And there is the potential for other stuff 7648 like /dev/tty which may need to be mapped to something. 7649 */ 7650 7651 static int 7652 slash_dev_special_to_vms(const char *unixptr, char *vmspath, int vmspath_len) 7653 { 7654 char * nextslash; 7655 int len; 7656 int cmp; 7657 7658 unixptr += 4; 7659 nextslash = strchr(unixptr, '/'); 7660 len = strlen(unixptr); 7661 if (nextslash != NULL) 7662 len = nextslash - unixptr; 7663 cmp = strncmp("null", unixptr, 5); 7664 if (cmp == 0) { 7665 if (vmspath_len >= 6) { 7666 strcpy(vmspath, "_NLA0:"); 7667 return SS$_NORMAL; 7668 } 7669 } 7670 return 0; 7671 } 7672 7673 7674 /* The built in routines do not understand perl's special needs, so 7675 doing a manual conversion from UNIX to VMS 7676 7677 If the utf8_fl is not null and points to a non-zero value, then 7678 treat 8 bit characters as UTF-8. 7679 7680 The sequence starting with '$(' and ending with ')' will be passed 7681 through with out interpretation instead of being escaped. 7682 7683 */ 7684 static int 7685 posix_to_vmsspec_hardway(char *vmspath, int vmspath_len, const char *unixpath, 7686 int dir_flag, int * utf8_fl) 7687 { 7688 7689 char *esa; 7690 const char *unixptr; 7691 const char *unixend; 7692 char *vmsptr; 7693 const char *lastslash; 7694 const char *lastdot; 7695 int unixlen; 7696 int vmslen; 7697 int dir_start; 7698 int dir_dot; 7699 int quoted; 7700 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 7701 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 7702 7703 if (utf8_fl != NULL) 7704 *utf8_fl = 0; 7705 7706 unixptr = unixpath; 7707 dir_dot = 0; 7708 7709 /* Ignore leading "/" characters */ 7710 while((unixptr[0] == '/') && (unixptr[1] == '/')) { 7711 unixptr++; 7712 } 7713 unixlen = strlen(unixptr); 7714 7715 /* Do nothing with blank paths */ 7716 if (unixlen == 0) { 7717 vmspath[0] = '\0'; 7718 return SS$_NORMAL; 7719 } 7720 7721 quoted = 0; 7722 /* This could have a "^UP^ on the front */ 7723 if (strncmp(unixptr,"\"^UP^",5) == 0) { 7724 quoted = 1; 7725 unixptr+= 5; 7726 unixlen-= 5; 7727 } 7728 7729 lastslash = strrchr(unixptr,'/'); 7730 lastdot = strrchr(unixptr,'.'); 7731 unixend = strrchr(unixptr,'\"'); 7732 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) { 7733 unixend = unixptr + unixlen; 7734 } 7735 7736 /* last dot is last dot or past end of string */ 7737 if (lastdot == NULL) 7738 lastdot = unixptr + unixlen; 7739 7740 /* if no directories, set last slash to beginning of string */ 7741 if (lastslash == NULL) { 7742 lastslash = unixptr; 7743 } 7744 else { 7745 /* Watch out for trailing "." after last slash, still a directory */ 7746 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) { 7747 lastslash = unixptr + unixlen; 7748 } 7749 7750 /* Watch out for trailing ".." after last slash, still a directory */ 7751 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) { 7752 lastslash = unixptr + unixlen; 7753 } 7754 7755 /* dots in directories are aways escaped */ 7756 if (lastdot < lastslash) 7757 lastdot = unixptr + unixlen; 7758 } 7759 7760 /* if (unixptr < lastslash) then we are in a directory */ 7761 7762 dir_start = 0; 7763 7764 vmsptr = vmspath; 7765 vmslen = 0; 7766 7767 /* Start with the UNIX path */ 7768 if (*unixptr != '/') { 7769 /* relative paths */ 7770 7771 /* If allowing logical names on relative pathnames, then handle here */ 7772 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation && 7773 !decc_posix_compliant_pathnames) { 7774 char * nextslash; 7775 int seg_len; 7776 char * trn; 7777 int islnm; 7778 7779 /* Find the next slash */ 7780 nextslash = strchr(unixptr,'/'); 7781 7782 esa = (char *)PerlMem_malloc(vmspath_len); 7783 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7784 7785 trn = (char *)PerlMem_malloc(VMS_MAXRSS); 7786 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7787 7788 if (nextslash != NULL) { 7789 7790 seg_len = nextslash - unixptr; 7791 memcpy(esa, unixptr, seg_len); 7792 esa[seg_len] = 0; 7793 } 7794 else { 7795 seg_len = my_strlcpy(esa, unixptr, sizeof(esa)); 7796 } 7797 /* trnlnm(section) */ 7798 islnm = vmstrnenv(esa, trn, 0, fildev, 0); 7799 7800 if (islnm) { 7801 /* Now fix up the directory */ 7802 7803 /* Split up the path to find the components */ 7804 sts = vms_split_path 7805 (trn, 7806 &v_spec, 7807 &v_len, 7808 &r_spec, 7809 &r_len, 7810 &d_spec, 7811 &d_len, 7812 &n_spec, 7813 &n_len, 7814 &e_spec, 7815 &e_len, 7816 &vs_spec, 7817 &vs_len); 7818 7819 while (sts == 0) { 7820 int cmp; 7821 7822 /* A logical name must be a directory or the full 7823 specification. It is only a full specification if 7824 it is the only component */ 7825 if ((unixptr[seg_len] == '\0') || 7826 (unixptr[seg_len+1] == '\0')) { 7827 7828 /* Is a directory being required? */ 7829 if (((n_len + e_len) != 0) && (dir_flag !=0)) { 7830 /* Not a logical name */ 7831 break; 7832 } 7833 7834 7835 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) { 7836 /* This must be a directory */ 7837 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) { 7838 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1); 7839 vmsptr[vmslen] = ':'; 7840 vmslen++; 7841 vmsptr[vmslen] = '\0'; 7842 return SS$_NORMAL; 7843 } 7844 } 7845 7846 } 7847 7848 7849 /* must be dev/directory - ignore version */ 7850 if ((n_len + e_len) != 0) 7851 break; 7852 7853 /* transfer the volume */ 7854 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) { 7855 memcpy(vmsptr, v_spec, v_len); 7856 vmsptr += v_len; 7857 vmsptr[0] = '\0'; 7858 vmslen += v_len; 7859 } 7860 7861 /* unroot the rooted directory */ 7862 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) { 7863 r_spec[0] = '['; 7864 r_spec[r_len - 1] = ']'; 7865 7866 /* This should not be there, but nothing is perfect */ 7867 if (r_len > 9) { 7868 cmp = strcmp(&r_spec[1], "000000."); 7869 if (cmp == 0) { 7870 r_spec += 7; 7871 r_spec[7] = '['; 7872 r_len -= 7; 7873 if (r_len == 2) 7874 r_len = 0; 7875 } 7876 } 7877 if (r_len > 0) { 7878 memcpy(vmsptr, r_spec, r_len); 7879 vmsptr += r_len; 7880 vmslen += r_len; 7881 vmsptr[0] = '\0'; 7882 } 7883 } 7884 /* Bring over the directory. */ 7885 if ((d_len > 0) && 7886 ((d_len + vmslen) < vmspath_len)) { 7887 d_spec[0] = '['; 7888 d_spec[d_len - 1] = ']'; 7889 if (d_len > 9) { 7890 cmp = strcmp(&d_spec[1], "000000."); 7891 if (cmp == 0) { 7892 d_spec += 7; 7893 d_spec[7] = '['; 7894 d_len -= 7; 7895 if (d_len == 2) 7896 d_len = 0; 7897 } 7898 } 7899 7900 if (r_len > 0) { 7901 /* Remove the redundant root */ 7902 if (r_len > 0) { 7903 /* remove the ][ */ 7904 vmsptr--; 7905 vmslen--; 7906 d_spec++; 7907 d_len--; 7908 } 7909 memcpy(vmsptr, d_spec, d_len); 7910 vmsptr += d_len; 7911 vmslen += d_len; 7912 vmsptr[0] = '\0'; 7913 } 7914 } 7915 break; 7916 } 7917 } 7918 7919 PerlMem_free(esa); 7920 PerlMem_free(trn); 7921 } 7922 7923 if (lastslash > unixptr) { 7924 int dotdir_seen; 7925 7926 /* skip leading ./ */ 7927 dotdir_seen = 0; 7928 while ((unixptr[0] == '.') && (unixptr[1] == '/')) { 7929 dotdir_seen = 1; 7930 unixptr++; 7931 unixptr++; 7932 } 7933 7934 /* Are we still in a directory? */ 7935 if (unixptr <= lastslash) { 7936 *vmsptr++ = '['; 7937 vmslen = 1; 7938 dir_start = 1; 7939 7940 /* if not backing up, then it is relative forward. */ 7941 if (!((*unixptr == '.') && (unixptr[1] == '.') && 7942 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) { 7943 *vmsptr++ = '.'; 7944 vmslen++; 7945 dir_dot = 1; 7946 } 7947 } 7948 else { 7949 if (dotdir_seen) { 7950 /* Perl wants an empty directory here to tell the difference 7951 * between a DCL command and a filename 7952 */ 7953 *vmsptr++ = '['; 7954 *vmsptr++ = ']'; 7955 vmslen = 2; 7956 } 7957 } 7958 } 7959 else { 7960 /* Handle two special files . and .. */ 7961 if (unixptr[0] == '.') { 7962 if (&unixptr[1] == unixend) { 7963 *vmsptr++ = '['; 7964 *vmsptr++ = ']'; 7965 vmslen += 2; 7966 *vmsptr++ = '\0'; 7967 return SS$_NORMAL; 7968 } 7969 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) { 7970 *vmsptr++ = '['; 7971 *vmsptr++ = '-'; 7972 *vmsptr++ = ']'; 7973 vmslen += 3; 7974 *vmsptr++ = '\0'; 7975 return SS$_NORMAL; 7976 } 7977 } 7978 } 7979 } 7980 else { /* Absolute PATH handling */ 7981 int sts; 7982 char * nextslash; 7983 int seg_len; 7984 /* Need to find out where root is */ 7985 7986 /* In theory, this procedure should never get an absolute POSIX pathname 7987 * that can not be found on the POSIX root. 7988 * In practice, that can not be relied on, and things will show up 7989 * here that are a VMS device name or concealed logical name instead. 7990 * So to make things work, this procedure must be tolerant. 7991 */ 7992 esa = (char *)PerlMem_malloc(vmspath_len); 7993 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7994 7995 sts = SS$_NORMAL; 7996 nextslash = strchr(&unixptr[1],'/'); 7997 seg_len = 0; 7998 if (nextslash != NULL) { 7999 int cmp; 8000 seg_len = nextslash - &unixptr[1]; 8001 my_strlcpy(vmspath, unixptr, seg_len + 2); 8002 cmp = 1; 8003 if (seg_len == 3) { 8004 cmp = strncmp(vmspath, "dev", 4); 8005 if (cmp == 0) { 8006 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len); 8007 if (sts == SS$_NORMAL) 8008 return SS$_NORMAL; 8009 } 8010 } 8011 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl); 8012 } 8013 8014 if ($VMS_STATUS_SUCCESS(sts)) { 8015 /* This is verified to be a real path */ 8016 8017 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL); 8018 if ($VMS_STATUS_SUCCESS(sts)) { 8019 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1); 8020 vmsptr = vmspath + vmslen; 8021 unixptr++; 8022 if (unixptr < lastslash) { 8023 char * rptr; 8024 vmsptr--; 8025 *vmsptr++ = '.'; 8026 dir_start = 1; 8027 dir_dot = 1; 8028 if (vmslen > 7) { 8029 int cmp; 8030 rptr = vmsptr - 7; 8031 cmp = strcmp(rptr,"000000."); 8032 if (cmp == 0) { 8033 vmslen -= 7; 8034 vmsptr -= 7; 8035 vmsptr[1] = '\0'; 8036 } /* removing 6 zeros */ 8037 } /* vmslen < 7, no 6 zeros possible */ 8038 } /* Not in a directory */ 8039 } /* Posix root found */ 8040 else { 8041 /* No posix root, fall back to default directory */ 8042 strcpy(vmspath, "SYS$DISK:["); 8043 vmsptr = &vmspath[10]; 8044 vmslen = 10; 8045 if (unixptr > lastslash) { 8046 *vmsptr = ']'; 8047 vmsptr++; 8048 vmslen++; 8049 } 8050 else { 8051 dir_start = 1; 8052 } 8053 } 8054 } /* end of verified real path handling */ 8055 else { 8056 int add_6zero; 8057 int islnm; 8058 8059 /* Ok, we have a device or a concealed root that is not in POSIX 8060 * or we have garbage. Make the best of it. 8061 */ 8062 8063 /* Posix to VMS destroyed this, so copy it again */ 8064 my_strlcpy(vmspath, &unixptr[1], seg_len + 1); 8065 vmslen = strlen(vmspath); /* We know we're truncating. */ 8066 vmsptr = &vmsptr[vmslen]; 8067 islnm = 0; 8068 8069 /* Now do we need to add the fake 6 zero directory to it? */ 8070 add_6zero = 1; 8071 if ((*lastslash == '/') && (nextslash < lastslash)) { 8072 /* No there is another directory */ 8073 add_6zero = 0; 8074 } 8075 else { 8076 int trnend; 8077 int cmp; 8078 8079 /* now we have foo:bar or foo:[000000]bar to decide from */ 8080 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0); 8081 8082 if (!islnm && !decc_posix_compliant_pathnames) { 8083 8084 cmp = strncmp("bin", vmspath, 4); 8085 if (cmp == 0) { 8086 /* bin => SYS$SYSTEM: */ 8087 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0); 8088 } 8089 else { 8090 /* tmp => SYS$SCRATCH: */ 8091 cmp = strncmp("tmp", vmspath, 4); 8092 if (cmp == 0) { 8093 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0); 8094 } 8095 } 8096 } 8097 8098 trnend = islnm ? islnm - 1 : 0; 8099 8100 /* if this was a logical name, ']' or '>' must be present */ 8101 /* if not a logical name, then assume a device and hope. */ 8102 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0; 8103 8104 /* if log name and trailing '.' then rooted - treat as device */ 8105 add_6zero = islnm ? (esa[trnend-1] == '.') : 0; 8106 8107 /* Fix me, if not a logical name, a device lookup should be 8108 * done to see if the device is file structured. If the device 8109 * is not file structured, the 6 zeros should not be put on. 8110 * 8111 * As it is, perl is occasionally looking for dev:[000000]tty. 8112 * which looks a little strange. 8113 * 8114 * Not that easy to detect as "/dev" may be file structured with 8115 * special device files. 8116 */ 8117 8118 if (!islnm && (add_6zero == 0) && (*nextslash == '/') && 8119 (&nextslash[1] == unixend)) { 8120 /* No real directory present */ 8121 add_6zero = 1; 8122 } 8123 } 8124 8125 /* Put the device delimiter on */ 8126 *vmsptr++ = ':'; 8127 vmslen++; 8128 unixptr = nextslash; 8129 unixptr++; 8130 8131 /* Start directory if needed */ 8132 if (!islnm || add_6zero) { 8133 *vmsptr++ = '['; 8134 vmslen++; 8135 dir_start = 1; 8136 } 8137 8138 /* add fake 000000] if needed */ 8139 if (add_6zero) { 8140 *vmsptr++ = '0'; 8141 *vmsptr++ = '0'; 8142 *vmsptr++ = '0'; 8143 *vmsptr++ = '0'; 8144 *vmsptr++ = '0'; 8145 *vmsptr++ = '0'; 8146 *vmsptr++ = ']'; 8147 vmslen += 7; 8148 dir_start = 0; 8149 } 8150 8151 } /* non-POSIX translation */ 8152 PerlMem_free(esa); 8153 } /* End of relative/absolute path handling */ 8154 8155 while ((unixptr <= unixend) && (vmslen < vmspath_len)){ 8156 int dash_flag; 8157 int in_cnt; 8158 int out_cnt; 8159 8160 dash_flag = 0; 8161 8162 if (dir_start != 0) { 8163 8164 /* First characters in a directory are handled special */ 8165 while ((*unixptr == '/') || 8166 ((*unixptr == '.') && 8167 ((unixptr[1]=='.') || (unixptr[1]=='/') || 8168 (&unixptr[1]==unixend)))) { 8169 int loop_flag; 8170 8171 loop_flag = 0; 8172 8173 /* Skip redundant / in specification */ 8174 while ((*unixptr == '/') && (dir_start != 0)) { 8175 loop_flag = 1; 8176 unixptr++; 8177 if (unixptr == lastslash) 8178 break; 8179 } 8180 if (unixptr == lastslash) 8181 break; 8182 8183 /* Skip redundant ./ characters */ 8184 while ((*unixptr == '.') && 8185 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) { 8186 loop_flag = 1; 8187 unixptr++; 8188 if (unixptr == lastslash) 8189 break; 8190 if (*unixptr == '/') 8191 unixptr++; 8192 } 8193 if (unixptr == lastslash) 8194 break; 8195 8196 /* Skip redundant ../ characters */ 8197 while ((*unixptr == '.') && (unixptr[1] == '.') && 8198 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) { 8199 /* Set the backing up flag */ 8200 loop_flag = 1; 8201 dir_dot = 0; 8202 dash_flag = 1; 8203 *vmsptr++ = '-'; 8204 vmslen++; 8205 unixptr++; /* first . */ 8206 unixptr++; /* second . */ 8207 if (unixptr == lastslash) 8208 break; 8209 if (*unixptr == '/') /* The slash */ 8210 unixptr++; 8211 } 8212 if (unixptr == lastslash) 8213 break; 8214 8215 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 8216 /* Not needed when VMS is pretending to be UNIX. */ 8217 8218 /* Is this loop stuck because of too many dots? */ 8219 if (loop_flag == 0) { 8220 /* Exit the loop and pass the rest through */ 8221 break; 8222 } 8223 } 8224 8225 /* Are we done with directories yet? */ 8226 if (unixptr >= lastslash) { 8227 8228 /* Watch out for trailing dots */ 8229 if (dir_dot != 0) { 8230 vmslen --; 8231 vmsptr--; 8232 } 8233 *vmsptr++ = ']'; 8234 vmslen++; 8235 dash_flag = 0; 8236 dir_start = 0; 8237 if (*unixptr == '/') 8238 unixptr++; 8239 } 8240 else { 8241 /* Have we stopped backing up? */ 8242 if (dash_flag) { 8243 *vmsptr++ = '.'; 8244 vmslen++; 8245 dash_flag = 0; 8246 /* dir_start continues to be = 1 */ 8247 } 8248 if (*unixptr == '-') { 8249 *vmsptr++ = '^'; 8250 *vmsptr++ = *unixptr++; 8251 vmslen += 2; 8252 dir_start = 0; 8253 8254 /* Now are we done with directories yet? */ 8255 if (unixptr >= lastslash) { 8256 8257 /* Watch out for trailing dots */ 8258 if (dir_dot != 0) { 8259 vmslen --; 8260 vmsptr--; 8261 } 8262 8263 *vmsptr++ = ']'; 8264 vmslen++; 8265 dash_flag = 0; 8266 dir_start = 0; 8267 } 8268 } 8269 } 8270 } 8271 8272 /* All done? */ 8273 if (unixptr >= unixend) 8274 break; 8275 8276 /* Normal characters - More EFS work probably needed */ 8277 dir_start = 0; 8278 dir_dot = 0; 8279 8280 switch(*unixptr) { 8281 case '/': 8282 /* remove multiple / */ 8283 while (unixptr[1] == '/') { 8284 unixptr++; 8285 } 8286 if (unixptr == lastslash) { 8287 /* Watch out for trailing dots */ 8288 if (dir_dot != 0) { 8289 vmslen --; 8290 vmsptr--; 8291 } 8292 *vmsptr++ = ']'; 8293 } 8294 else { 8295 dir_start = 1; 8296 *vmsptr++ = '.'; 8297 dir_dot = 1; 8298 8299 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 8300 /* Not needed when VMS is pretending to be UNIX. */ 8301 8302 } 8303 dash_flag = 0; 8304 if (unixptr != unixend) 8305 unixptr++; 8306 vmslen++; 8307 break; 8308 case '.': 8309 if ((unixptr < lastdot) || (unixptr < lastslash) || 8310 (&unixptr[1] == unixend)) { 8311 *vmsptr++ = '^'; 8312 *vmsptr++ = '.'; 8313 vmslen += 2; 8314 unixptr++; 8315 8316 /* trailing dot ==> '^..' on VMS */ 8317 if (unixptr == unixend) { 8318 *vmsptr++ = '.'; 8319 vmslen++; 8320 unixptr++; 8321 } 8322 break; 8323 } 8324 8325 *vmsptr++ = *unixptr++; 8326 vmslen ++; 8327 break; 8328 case '"': 8329 if (quoted && (&unixptr[1] == unixend)) { 8330 unixptr++; 8331 break; 8332 } 8333 in_cnt = copy_expand_unix_filename_escape 8334 (vmsptr, unixptr, &out_cnt, utf8_fl); 8335 vmsptr += out_cnt; 8336 unixptr += in_cnt; 8337 break; 8338 case '~': 8339 case ';': 8340 case '\\': 8341 case '?': 8342 case ' ': 8343 default: 8344 in_cnt = copy_expand_unix_filename_escape 8345 (vmsptr, unixptr, &out_cnt, utf8_fl); 8346 vmsptr += out_cnt; 8347 unixptr += in_cnt; 8348 break; 8349 } 8350 } 8351 8352 /* Make sure directory is closed */ 8353 if (unixptr == lastslash) { 8354 char *vmsptr2; 8355 vmsptr2 = vmsptr - 1; 8356 8357 if (*vmsptr2 != ']') { 8358 *vmsptr2--; 8359 8360 /* directories do not end in a dot bracket */ 8361 if (*vmsptr2 == '.') { 8362 vmsptr2--; 8363 8364 /* ^. is allowed */ 8365 if (*vmsptr2 != '^') { 8366 vmsptr--; /* back up over the dot */ 8367 } 8368 } 8369 *vmsptr++ = ']'; 8370 } 8371 } 8372 else { 8373 char *vmsptr2; 8374 /* Add a trailing dot if a file with no extension */ 8375 vmsptr2 = vmsptr - 1; 8376 if ((vmslen > 1) && 8377 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') && 8378 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) { 8379 *vmsptr++ = '.'; 8380 vmslen++; 8381 } 8382 } 8383 8384 *vmsptr = '\0'; 8385 return SS$_NORMAL; 8386 } 8387 8388 /* A convenience macro for copying dots in filenames and escaping 8389 * them when they haven't already been escaped, with guards to 8390 * avoid checking before the start of the buffer or advancing 8391 * beyond the end of it (allowing room for the NUL terminator). 8392 */ 8393 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \ 8394 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \ 8395 || ((vmsefsdot) == (vmsefsbuf))) \ 8396 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \ 8397 ) { \ 8398 *((vmsefsdot)++) = '^'; \ 8399 } \ 8400 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \ 8401 *((vmsefsdot)++) = '.'; \ 8402 } STMT_END 8403 8404 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 8405 static char * 8406 int_tovmsspec(const char *path, char *rslt, int dir_flag, int * utf8_flag) 8407 { 8408 char *dirend; 8409 char *lastdot; 8410 char *cp1; 8411 const char *cp2; 8412 unsigned long int infront = 0, hasdir = 1; 8413 int rslt_len; 8414 int no_type_seen; 8415 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 8416 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 8417 8418 if (vms_debug_fileify) { 8419 if (path == NULL) 8420 fprintf(stderr, "int_tovmsspec: path = NULL\n"); 8421 else 8422 fprintf(stderr, "int_tovmsspec: path = %s\n", path); 8423 } 8424 8425 if (path == NULL) { 8426 /* If we fail, we should be setting errno */ 8427 set_errno(EINVAL); 8428 set_vaxc_errno(SS$_BADPARAM); 8429 return NULL; 8430 } 8431 rslt_len = VMS_MAXRSS-1; 8432 8433 /* '.' and '..' are "[]" and "[-]" for a quick check */ 8434 if (path[0] == '.') { 8435 if (path[1] == '\0') { 8436 strcpy(rslt,"[]"); 8437 if (utf8_flag != NULL) 8438 *utf8_flag = 0; 8439 return rslt; 8440 } 8441 else { 8442 if (path[1] == '.' && path[2] == '\0') { 8443 strcpy(rslt,"[-]"); 8444 if (utf8_flag != NULL) 8445 *utf8_flag = 0; 8446 return rslt; 8447 } 8448 } 8449 } 8450 8451 /* Posix specifications are now a native VMS format */ 8452 /*--------------------------------------------------*/ 8453 #if __CRTL_VER >= 80200000 8454 if (decc_posix_compliant_pathnames) { 8455 if (strncmp(path,"\"^UP^",5) == 0) { 8456 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag); 8457 return rslt; 8458 } 8459 } 8460 #endif 8461 8462 /* This is really the only way to see if this is already in VMS format */ 8463 sts = vms_split_path 8464 (path, 8465 &v_spec, 8466 &v_len, 8467 &r_spec, 8468 &r_len, 8469 &d_spec, 8470 &d_len, 8471 &n_spec, 8472 &n_len, 8473 &e_spec, 8474 &e_len, 8475 &vs_spec, 8476 &vs_len); 8477 if (sts == 0) { 8478 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath() 8479 replacement, because the above parse just took care of most of 8480 what is needed to do vmspath when the specification is already 8481 in VMS format. 8482 8483 And if it is not already, it is easier to do the conversion as 8484 part of this routine than to call this routine and then work on 8485 the result. 8486 */ 8487 8488 /* If VMS punctuation was found, it is already VMS format */ 8489 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) { 8490 if (utf8_flag != NULL) 8491 *utf8_flag = 0; 8492 my_strlcpy(rslt, path, VMS_MAXRSS); 8493 if (vms_debug_fileify) { 8494 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8495 } 8496 return rslt; 8497 } 8498 /* Now, what to do with trailing "." cases where there is no 8499 extension? If this is a UNIX specification, and EFS characters 8500 are enabled, then the trailing "." should be converted to a "^.". 8501 But if this was already a VMS specification, then it should be 8502 left alone. 8503 8504 So in the case of ambiguity, leave the specification alone. 8505 */ 8506 8507 8508 /* If there is a possibility of UTF8, then if any UTF8 characters 8509 are present, then they must be converted to VTF-7 8510 */ 8511 if (utf8_flag != NULL) 8512 *utf8_flag = 0; 8513 my_strlcpy(rslt, path, VMS_MAXRSS); 8514 if (vms_debug_fileify) { 8515 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8516 } 8517 return rslt; 8518 } 8519 8520 dirend = strrchr(path,'/'); 8521 8522 if (dirend == NULL) { 8523 /* If we get here with no Unix directory delimiters, then this is an 8524 * ambiguous file specification, such as a Unix glob specification, a 8525 * shell or make macro, or a filespec that would be valid except for 8526 * unescaped extended characters. The safest thing if it's a macro 8527 * is to pass it through as-is. 8528 */ 8529 if (strstr(path, "$(")) { 8530 my_strlcpy(rslt, path, VMS_MAXRSS); 8531 if (vms_debug_fileify) { 8532 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8533 } 8534 return rslt; 8535 } 8536 hasdir = 0; 8537 } 8538 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */ 8539 if (!*(dirend+2)) dirend +=2; 8540 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3; 8541 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4; 8542 } 8543 8544 cp1 = rslt; 8545 cp2 = path; 8546 lastdot = strrchr(cp2,'.'); 8547 if (*cp2 == '/') { 8548 char *trndev; 8549 int islnm, rooted; 8550 STRLEN trnend; 8551 8552 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8553 if (!*(cp2+1)) { 8554 if (decc_disable_posix_root) { 8555 strcpy(rslt,"sys$disk:[000000]"); 8556 } 8557 else { 8558 strcpy(rslt,"sys$posix_root:[000000]"); 8559 } 8560 if (utf8_flag != NULL) 8561 *utf8_flag = 0; 8562 if (vms_debug_fileify) { 8563 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8564 } 8565 return rslt; 8566 } 8567 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2; 8568 *cp1 = '\0'; 8569 trndev = (char *)PerlMem_malloc(VMS_MAXRSS); 8570 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8571 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8572 8573 /* DECC special handling */ 8574 if (!islnm) { 8575 if (strcmp(rslt,"bin") == 0) { 8576 strcpy(rslt,"sys$system"); 8577 cp1 = rslt + 10; 8578 *cp1 = 0; 8579 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8580 } 8581 else if (strcmp(rslt,"tmp") == 0) { 8582 strcpy(rslt,"sys$scratch"); 8583 cp1 = rslt + 11; 8584 *cp1 = 0; 8585 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8586 } 8587 else if (!decc_disable_posix_root) { 8588 strcpy(rslt, "sys$posix_root"); 8589 cp1 = rslt + 14; 8590 *cp1 = 0; 8591 cp2 = path; 8592 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8593 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8594 } 8595 else if (strcmp(rslt,"dev") == 0) { 8596 if (strncmp(cp2,"/null", 5) == 0) { 8597 if ((cp2[5] == 0) || (cp2[5] == '/')) { 8598 strcpy(rslt,"NLA0"); 8599 cp1 = rslt + 4; 8600 *cp1 = 0; 8601 cp2 = cp2 + 5; 8602 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8603 } 8604 } 8605 } 8606 } 8607 8608 trnend = islnm ? strlen(trndev) - 1 : 0; 8609 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0; 8610 rooted = islnm ? (trndev[trnend-1] == '.') : 0; 8611 /* If the first element of the path is a logical name, determine 8612 * whether it has to be translated so we can add more directories. */ 8613 if (!islnm || rooted) { 8614 *(cp1++) = ':'; 8615 *(cp1++) = '['; 8616 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0'; 8617 else cp2++; 8618 } 8619 else { 8620 if (cp2 != dirend) { 8621 my_strlcpy(rslt, trndev, VMS_MAXRSS); 8622 cp1 = rslt + trnend; 8623 if (*cp2 != 0) { 8624 *(cp1++) = '.'; 8625 cp2++; 8626 } 8627 } 8628 else { 8629 if (decc_disable_posix_root) { 8630 *(cp1++) = ':'; 8631 hasdir = 0; 8632 } 8633 } 8634 } 8635 PerlMem_free(trndev); 8636 } 8637 else if (hasdir) { 8638 *(cp1++) = '['; 8639 if (*cp2 == '.') { 8640 if (*(cp2+1) == '/' || *(cp2+1) == '\0') { 8641 cp2 += 2; /* skip over "./" - it's redundant */ 8642 *(cp1++) = '.'; /* but it does indicate a relative dirspec */ 8643 } 8644 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8645 *(cp1++) = '-'; /* "../" --> "-" */ 8646 cp2 += 3; 8647 } 8648 else if (*(cp2+1) == '.' && *(cp2+2) == '.' && 8649 (*(cp2+3) == '/' || *(cp2+3) == '\0')) { 8650 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8651 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */ 8652 cp2 += 4; 8653 } 8654 else if ((cp2 != lastdot) || (lastdot < dirend)) { 8655 /* Escape the extra dots in EFS file specifications */ 8656 *(cp1++) = '^'; 8657 } 8658 if (cp2 > dirend) cp2 = dirend; 8659 } 8660 else *(cp1++) = '.'; 8661 } 8662 for (; cp2 < dirend; cp2++) { 8663 if (*cp2 == '/') { 8664 if (*(cp2-1) == '/') continue; 8665 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; 8666 infront = 0; 8667 } 8668 else if (!infront && *cp2 == '.') { 8669 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; } 8670 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */ 8671 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8672 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */ 8673 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-'; 8674 else { 8675 *(cp1++) = '-'; 8676 } 8677 cp2 += 2; 8678 if (cp2 == dirend) break; 8679 } 8680 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' && 8681 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) { 8682 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */ 8683 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8684 if (!*(cp2+3)) { 8685 *(cp1++) = '.'; /* Simulate trailing '/' */ 8686 cp2 += 2; /* for loop will incr this to == dirend */ 8687 } 8688 else cp2 += 3; /* Trailing '/' was there, so skip it, too */ 8689 } 8690 else { 8691 if (decc_efs_charset == 0) { 8692 if (cp1 > rslt && *(cp1-1) == '^') 8693 cp1--; /* remove the escape, if any */ 8694 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */ 8695 } 8696 else { 8697 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8698 } 8699 } 8700 } 8701 else { 8702 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.'; 8703 if (*cp2 == '.') { 8704 if (decc_efs_charset == 0) { 8705 if (cp1 > rslt && *(cp1-1) == '^') 8706 cp1--; /* remove the escape, if any */ 8707 *(cp1++) = '_'; 8708 } 8709 else { 8710 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8711 } 8712 } 8713 else { 8714 int out_cnt; 8715 cp2 += copy_expand_unix_filename_escape(cp1, cp2, &out_cnt, utf8_flag); 8716 cp2--; /* we're in a loop that will increment this */ 8717 cp1 += out_cnt; 8718 } 8719 infront = 1; 8720 } 8721 } 8722 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */ 8723 if (hasdir) *(cp1++) = ']'; 8724 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */ 8725 no_type_seen = 0; 8726 if (cp2 > lastdot) 8727 no_type_seen = 1; 8728 while (*cp2) { 8729 switch(*cp2) { 8730 case '?': 8731 if (decc_efs_charset == 0) 8732 *(cp1++) = '%'; 8733 else 8734 *(cp1++) = '?'; 8735 cp2++; 8736 case ' ': 8737 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */ 8738 *(cp1)++ = '^'; 8739 *(cp1)++ = '_'; 8740 cp2++; 8741 break; 8742 case '.': 8743 if (((cp2 < lastdot) || (cp2[1] == '\0')) && 8744 decc_readdir_dropdotnotype) { 8745 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8746 cp2++; 8747 8748 /* trailing dot ==> '^..' on VMS */ 8749 if (*cp2 == '\0') { 8750 *(cp1++) = '.'; 8751 no_type_seen = 0; 8752 } 8753 } 8754 else { 8755 *(cp1++) = *(cp2++); 8756 no_type_seen = 0; 8757 } 8758 break; 8759 case '$': 8760 /* This could be a macro to be passed through */ 8761 *(cp1++) = *(cp2++); 8762 if (*cp2 == '(') { 8763 const char * save_cp2; 8764 char * save_cp1; 8765 int is_macro; 8766 8767 /* paranoid check */ 8768 save_cp2 = cp2; 8769 save_cp1 = cp1; 8770 is_macro = 0; 8771 8772 /* Test through */ 8773 *(cp1++) = *(cp2++); 8774 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8775 *(cp1++) = *(cp2++); 8776 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8777 *(cp1++) = *(cp2++); 8778 } 8779 if (*cp2 == ')') { 8780 *(cp1++) = *(cp2++); 8781 is_macro = 1; 8782 } 8783 } 8784 if (is_macro == 0) { 8785 /* Not really a macro - never mind */ 8786 cp2 = save_cp2; 8787 cp1 = save_cp1; 8788 } 8789 } 8790 break; 8791 case '\"': 8792 case '~': 8793 case '`': 8794 case '!': 8795 case '#': 8796 case '%': 8797 case '^': 8798 /* Don't escape again if following character is 8799 * already something we escape. 8800 */ 8801 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) { 8802 *(cp1++) = *(cp2++); 8803 break; 8804 } 8805 /* But otherwise fall through and escape it. */ 8806 case '&': 8807 case '(': 8808 case ')': 8809 case '=': 8810 case '+': 8811 case '\'': 8812 case '@': 8813 case '[': 8814 case ']': 8815 case '{': 8816 case '}': 8817 case ':': 8818 case '\\': 8819 case '|': 8820 case '<': 8821 case '>': 8822 if (cp2 >= path && *(cp2-1) != '^') /* not previously escaped */ 8823 *(cp1++) = '^'; 8824 *(cp1++) = *(cp2++); 8825 break; 8826 case ';': 8827 /* If it doesn't look like the beginning of a version number, 8828 * or we've been promised there are no version numbers, then 8829 * escape it. 8830 */ 8831 if (decc_filename_unix_no_version) { 8832 *(cp1++) = '^'; 8833 } 8834 else { 8835 size_t all_nums = strspn(cp2+1, "0123456789"); 8836 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0') 8837 *(cp1++) = '^'; 8838 } 8839 *(cp1++) = *(cp2++); 8840 break; 8841 default: 8842 *(cp1++) = *(cp2++); 8843 } 8844 } 8845 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) { 8846 char *lcp1; 8847 lcp1 = cp1; 8848 lcp1--; 8849 /* Fix me for "^]", but that requires making sure that you do 8850 * not back up past the start of the filename 8851 */ 8852 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%')) 8853 *cp1++ = '.'; 8854 } 8855 *cp1 = '\0'; 8856 8857 if (utf8_flag != NULL) 8858 *utf8_flag = 0; 8859 if (vms_debug_fileify) { 8860 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8861 } 8862 return rslt; 8863 8864 } /* end of int_tovmsspec() */ 8865 8866 8867 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 8868 static char * 8869 mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) 8870 { 8871 static char __tovmsspec_retbuf[VMS_MAXRSS]; 8872 char * vmsspec, *ret_spec, *ret_buf; 8873 8874 vmsspec = NULL; 8875 ret_buf = buf; 8876 if (ret_buf == NULL) { 8877 if (ts) { 8878 Newx(vmsspec, VMS_MAXRSS, char); 8879 if (vmsspec == NULL) 8880 _ckvmssts(SS$_INSFMEM); 8881 ret_buf = vmsspec; 8882 } else { 8883 ret_buf = __tovmsspec_retbuf; 8884 } 8885 } 8886 8887 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag); 8888 8889 if (ret_spec == NULL) { 8890 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 8891 if (vmsspec) 8892 Safefree(vmsspec); 8893 } 8894 8895 return ret_spec; 8896 8897 } /* end of mp_do_tovmsspec() */ 8898 /*}}}*/ 8899 /* External entry points */ 8900 char * 8901 Perl_tovmsspec(pTHX_ const char *path, char *buf) 8902 { 8903 return do_tovmsspec(path, buf, 0, NULL); 8904 } 8905 8906 char * 8907 Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) 8908 { 8909 return do_tovmsspec(path, buf, 1, NULL); 8910 } 8911 8912 char * 8913 Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8914 { 8915 return do_tovmsspec(path, buf, 0, utf8_fl); 8916 } 8917 8918 char * 8919 Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8920 { 8921 return do_tovmsspec(path, buf, 1, utf8_fl); 8922 } 8923 8924 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/ 8925 /* Internal routine for use with out an explicit context present */ 8926 static char * 8927 int_tovmspath(const char *path, char *buf, int * utf8_fl) 8928 { 8929 char * ret_spec, *pathified; 8930 8931 if (path == NULL) 8932 return NULL; 8933 8934 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8935 if (pathified == NULL) 8936 _ckvmssts_noperl(SS$_INSFMEM); 8937 8938 ret_spec = int_pathify_dirspec(path, pathified); 8939 8940 if (ret_spec == NULL) { 8941 PerlMem_free(pathified); 8942 return NULL; 8943 } 8944 8945 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl); 8946 8947 PerlMem_free(pathified); 8948 return ret_spec; 8949 8950 } 8951 8952 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/ 8953 static char * 8954 mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) 8955 { 8956 static char __tovmspath_retbuf[VMS_MAXRSS]; 8957 int vmslen; 8958 char *pathified, *vmsified, *cp; 8959 8960 if (path == NULL) return NULL; 8961 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8962 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8963 if (int_pathify_dirspec(path, pathified) == NULL) { 8964 PerlMem_free(pathified); 8965 return NULL; 8966 } 8967 8968 vmsified = NULL; 8969 if (buf == NULL) 8970 Newx(vmsified, VMS_MAXRSS, char); 8971 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) { 8972 PerlMem_free(pathified); 8973 if (vmsified) Safefree(vmsified); 8974 return NULL; 8975 } 8976 PerlMem_free(pathified); 8977 if (buf) { 8978 return buf; 8979 } 8980 else if (ts) { 8981 vmslen = strlen(vmsified); 8982 Newx(cp,vmslen+1,char); 8983 memcpy(cp,vmsified,vmslen); 8984 cp[vmslen] = '\0'; 8985 Safefree(vmsified); 8986 return cp; 8987 } 8988 else { 8989 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf)); 8990 Safefree(vmsified); 8991 return __tovmspath_retbuf; 8992 } 8993 8994 } /* end of do_tovmspath() */ 8995 /*}}}*/ 8996 /* External entry points */ 8997 char * 8998 Perl_tovmspath(pTHX_ const char *path, char *buf) 8999 { 9000 return do_tovmspath(path, buf, 0, NULL); 9001 } 9002 9003 char * 9004 Perl_tovmspath_ts(pTHX_ const char *path, char *buf) 9005 { 9006 return do_tovmspath(path, buf, 1, NULL); 9007 } 9008 9009 char * 9010 Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl) 9011 { 9012 return do_tovmspath(path, buf, 0, utf8_fl); 9013 } 9014 9015 char * 9016 Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl) 9017 { 9018 return do_tovmspath(path, buf, 1, utf8_fl); 9019 } 9020 9021 9022 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/ 9023 static char * 9024 mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) 9025 { 9026 static char __tounixpath_retbuf[VMS_MAXRSS]; 9027 int unixlen; 9028 char *pathified, *unixified, *cp; 9029 9030 if (path == NULL) return NULL; 9031 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 9032 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 9033 if (int_pathify_dirspec(path, pathified) == NULL) { 9034 PerlMem_free(pathified); 9035 return NULL; 9036 } 9037 9038 unixified = NULL; 9039 if (buf == NULL) { 9040 Newx(unixified, VMS_MAXRSS, char); 9041 } 9042 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) { 9043 PerlMem_free(pathified); 9044 if (unixified) Safefree(unixified); 9045 return NULL; 9046 } 9047 PerlMem_free(pathified); 9048 if (buf) { 9049 return buf; 9050 } 9051 else if (ts) { 9052 unixlen = strlen(unixified); 9053 Newx(cp,unixlen+1,char); 9054 memcpy(cp,unixified,unixlen); 9055 cp[unixlen] = '\0'; 9056 Safefree(unixified); 9057 return cp; 9058 } 9059 else { 9060 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf)); 9061 Safefree(unixified); 9062 return __tounixpath_retbuf; 9063 } 9064 9065 } /* end of do_tounixpath() */ 9066 /*}}}*/ 9067 /* External entry points */ 9068 char * 9069 Perl_tounixpath(pTHX_ const char *path, char *buf) 9070 { 9071 return do_tounixpath(path, buf, 0, NULL); 9072 } 9073 9074 char * 9075 Perl_tounixpath_ts(pTHX_ const char *path, char *buf) 9076 { 9077 return do_tounixpath(path, buf, 1, NULL); 9078 } 9079 9080 char * 9081 Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 9082 { 9083 return do_tounixpath(path, buf, 0, utf8_fl); 9084 } 9085 9086 char * 9087 Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 9088 { 9089 return do_tounixpath(path, buf, 1, utf8_fl); 9090 } 9091 9092 /* 9093 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com) 9094 * 9095 ***************************************************************************** 9096 * * 9097 * Copyright (C) 1989-1994, 2007 by * 9098 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 * 9099 * * 9100 * Permission is hereby granted for the reproduction of this software * 9101 * on condition that this copyright notice is included in source * 9102 * distributions of the software. The code may be modified and * 9103 * distributed under the same terms as Perl itself. * 9104 * * 9105 * 27-Aug-1994 Modified for inclusion in perl5 * 9106 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) * 9107 ***************************************************************************** 9108 */ 9109 9110 /* 9111 * getredirection() is intended to aid in porting C programs 9112 * to VMS (Vax-11 C). The native VMS environment does not support 9113 * '>' and '<' I/O redirection, or command line wild card expansion, 9114 * or a command line pipe mechanism using the '|' AND background 9115 * command execution '&'. All of these capabilities are provided to any 9116 * C program which calls this procedure as the first thing in the 9117 * main program. 9118 * The piping mechanism will probably work with almost any 'filter' type 9119 * of program. With suitable modification, it may useful for other 9120 * portability problems as well. 9121 * 9122 * Author: Mark Pizzolato (mark AT infocomm DOT com) 9123 */ 9124 struct list_item 9125 { 9126 struct list_item *next; 9127 char *value; 9128 }; 9129 9130 static void add_item(struct list_item **head, 9131 struct list_item **tail, 9132 char *value, 9133 int *count); 9134 9135 static void mp_expand_wild_cards(pTHX_ char *item, 9136 struct list_item **head, 9137 struct list_item **tail, 9138 int *count); 9139 9140 static int background_process(pTHX_ int argc, char **argv); 9141 9142 static void pipe_and_fork(pTHX_ char **cmargv); 9143 9144 /*{{{ void getredirection(int *ac, char ***av)*/ 9145 static void 9146 mp_getredirection(pTHX_ int *ac, char ***av) 9147 /* 9148 * Process vms redirection arg's. Exit if any error is seen. 9149 * If getredirection() processes an argument, it is erased 9150 * from the vector. getredirection() returns a new argc and argv value. 9151 * In the event that a background command is requested (by a trailing "&"), 9152 * this routine creates a background subprocess, and simply exits the program. 9153 * 9154 * Warning: do not try to simplify the code for vms. The code 9155 * presupposes that getredirection() is called before any data is 9156 * read from stdin or written to stdout. 9157 * 9158 * Normal usage is as follows: 9159 * 9160 * main(argc, argv) 9161 * int argc; 9162 * char *argv[]; 9163 * { 9164 * getredirection(&argc, &argv); 9165 * } 9166 */ 9167 { 9168 int argc = *ac; /* Argument Count */ 9169 char **argv = *av; /* Argument Vector */ 9170 char *ap; /* Argument pointer */ 9171 int j; /* argv[] index */ 9172 int item_count = 0; /* Count of Items in List */ 9173 struct list_item *list_head = 0; /* First Item in List */ 9174 struct list_item *list_tail; /* Last Item in List */ 9175 char *in = NULL; /* Input File Name */ 9176 char *out = NULL; /* Output File Name */ 9177 char *outmode = "w"; /* Mode to Open Output File */ 9178 char *err = NULL; /* Error File Name */ 9179 char *errmode = "w"; /* Mode to Open Error File */ 9180 int cmargc = 0; /* Piped Command Arg Count */ 9181 char **cmargv = NULL;/* Piped Command Arg Vector */ 9182 9183 /* 9184 * First handle the case where the last thing on the line ends with 9185 * a '&'. This indicates the desire for the command to be run in a 9186 * subprocess, so we satisfy that desire. 9187 */ 9188 ap = argv[argc-1]; 9189 if (0 == strcmp("&", ap)) 9190 exit(background_process(aTHX_ --argc, argv)); 9191 if (*ap && '&' == ap[strlen(ap)-1]) 9192 { 9193 ap[strlen(ap)-1] = '\0'; 9194 exit(background_process(aTHX_ argc, argv)); 9195 } 9196 /* 9197 * Now we handle the general redirection cases that involve '>', '>>', 9198 * '<', and pipes '|'. 9199 */ 9200 for (j = 0; j < argc; ++j) 9201 { 9202 if (0 == strcmp("<", argv[j])) 9203 { 9204 if (j+1 >= argc) 9205 { 9206 fprintf(stderr,"No input file after < on command line"); 9207 exit(LIB$_WRONUMARG); 9208 } 9209 in = argv[++j]; 9210 continue; 9211 } 9212 if ('<' == *(ap = argv[j])) 9213 { 9214 in = 1 + ap; 9215 continue; 9216 } 9217 if (0 == strcmp(">", ap)) 9218 { 9219 if (j+1 >= argc) 9220 { 9221 fprintf(stderr,"No output file after > on command line"); 9222 exit(LIB$_WRONUMARG); 9223 } 9224 out = argv[++j]; 9225 continue; 9226 } 9227 if ('>' == *ap) 9228 { 9229 if ('>' == ap[1]) 9230 { 9231 outmode = "a"; 9232 if ('\0' == ap[2]) 9233 out = argv[++j]; 9234 else 9235 out = 2 + ap; 9236 } 9237 else 9238 out = 1 + ap; 9239 if (j >= argc) 9240 { 9241 fprintf(stderr,"No output file after > or >> on command line"); 9242 exit(LIB$_WRONUMARG); 9243 } 9244 continue; 9245 } 9246 if (('2' == *ap) && ('>' == ap[1])) 9247 { 9248 if ('>' == ap[2]) 9249 { 9250 errmode = "a"; 9251 if ('\0' == ap[3]) 9252 err = argv[++j]; 9253 else 9254 err = 3 + ap; 9255 } 9256 else 9257 if ('\0' == ap[2]) 9258 err = argv[++j]; 9259 else 9260 err = 2 + ap; 9261 if (j >= argc) 9262 { 9263 fprintf(stderr,"No output file after 2> or 2>> on command line"); 9264 exit(LIB$_WRONUMARG); 9265 } 9266 continue; 9267 } 9268 if (0 == strcmp("|", argv[j])) 9269 { 9270 if (j+1 >= argc) 9271 { 9272 fprintf(stderr,"No command into which to pipe on command line"); 9273 exit(LIB$_WRONUMARG); 9274 } 9275 cmargc = argc-(j+1); 9276 cmargv = &argv[j+1]; 9277 argc = j; 9278 continue; 9279 } 9280 if ('|' == *(ap = argv[j])) 9281 { 9282 ++argv[j]; 9283 cmargc = argc-j; 9284 cmargv = &argv[j]; 9285 argc = j; 9286 continue; 9287 } 9288 expand_wild_cards(ap, &list_head, &list_tail, &item_count); 9289 } 9290 /* 9291 * Allocate and fill in the new argument vector, Some Unix's terminate 9292 * the list with an extra null pointer. 9293 */ 9294 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *)); 9295 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9296 *av = argv; 9297 for (j = 0; j < item_count; ++j, list_head = list_head->next) 9298 argv[j] = list_head->value; 9299 *ac = item_count; 9300 if (cmargv != NULL) 9301 { 9302 if (out != NULL) 9303 { 9304 fprintf(stderr,"'|' and '>' may not both be specified on command line"); 9305 exit(LIB$_INVARGORD); 9306 } 9307 pipe_and_fork(aTHX_ cmargv); 9308 } 9309 9310 /* Check for input from a pipe (mailbox) */ 9311 9312 if (in == NULL && 1 == isapipe(0)) 9313 { 9314 char mbxname[L_tmpnam]; 9315 long int bufsize; 9316 long int dvi_item = DVI$_DEVBUFSIZ; 9317 $DESCRIPTOR(mbxnam, ""); 9318 $DESCRIPTOR(mbxdevnam, ""); 9319 9320 /* Input from a pipe, reopen it in binary mode to disable */ 9321 /* carriage control processing. */ 9322 9323 fgetname(stdin, mbxname, 1); 9324 mbxnam.dsc$a_pointer = mbxname; 9325 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer); 9326 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0); 9327 mbxdevnam.dsc$a_pointer = mbxname; 9328 mbxdevnam.dsc$w_length = sizeof(mbxname); 9329 dvi_item = DVI$_DEVNAM; 9330 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length); 9331 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0'; 9332 set_errno(0); 9333 set_vaxc_errno(1); 9334 freopen(mbxname, "rb", stdin); 9335 if (errno != 0) 9336 { 9337 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname); 9338 exit(vaxc$errno); 9339 } 9340 } 9341 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) 9342 { 9343 fprintf(stderr,"Can't open input file %s as stdin",in); 9344 exit(vaxc$errno); 9345 } 9346 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) 9347 { 9348 fprintf(stderr,"Can't open output file %s as stdout",out); 9349 exit(vaxc$errno); 9350 } 9351 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out); 9352 9353 if (err != NULL) { 9354 if (strcmp(err,"&1") == 0) { 9355 dup2(fileno(stdout), fileno(stderr)); 9356 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT"); 9357 } else { 9358 FILE *tmperr; 9359 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2"))) 9360 { 9361 fprintf(stderr,"Can't open error file %s as stderr",err); 9362 exit(vaxc$errno); 9363 } 9364 fclose(tmperr); 9365 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2")) 9366 { 9367 exit(vaxc$errno); 9368 } 9369 vmssetuserlnm("SYS$ERROR", err); 9370 } 9371 } 9372 #ifdef ARGPROC_DEBUG 9373 PerlIO_printf(Perl_debug_log, "Arglist:\n"); 9374 for (j = 0; j < *ac; ++j) 9375 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]); 9376 #endif 9377 /* Clear errors we may have hit expanding wildcards, so they don't 9378 show up in Perl's $! later */ 9379 set_errno(0); set_vaxc_errno(1); 9380 } /* end of getredirection() */ 9381 /*}}}*/ 9382 9383 static void 9384 add_item(struct list_item **head, struct list_item **tail, char *value, int *count) 9385 { 9386 if (*head == 0) 9387 { 9388 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 9389 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9390 *tail = *head; 9391 } 9392 else { 9393 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 9394 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9395 *tail = (*tail)->next; 9396 } 9397 (*tail)->value = value; 9398 ++(*count); 9399 } 9400 9401 static void 9402 mp_expand_wild_cards(pTHX_ char *item, struct list_item **head, 9403 struct list_item **tail, int *count) 9404 { 9405 int expcount = 0; 9406 unsigned long int context = 0; 9407 int isunix = 0; 9408 int item_len = 0; 9409 char *had_version; 9410 char *had_device; 9411 int had_directory; 9412 char *devdir,*cp; 9413 char *vmsspec; 9414 $DESCRIPTOR(filespec, ""); 9415 $DESCRIPTOR(defaultspec, "SYS$DISK:[]"); 9416 $DESCRIPTOR(resultspec, ""); 9417 unsigned long int lff_flags = 0; 9418 int sts; 9419 int rms_sts; 9420 9421 #ifdef VMS_LONGNAME_SUPPORT 9422 lff_flags = LIB$M_FIL_LONG_NAMES; 9423 #endif 9424 9425 for (cp = item; *cp; cp++) { 9426 if (*cp == '*' || *cp == '%' || isspace(*cp)) break; 9427 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break; 9428 } 9429 if (!*cp || isspace(*cp)) 9430 { 9431 add_item(head, tail, item, count); 9432 return; 9433 } 9434 else 9435 { 9436 /* "double quoted" wild card expressions pass as is */ 9437 /* From DCL that means using e.g.: */ 9438 /* perl program """perl.*""" */ 9439 item_len = strlen(item); 9440 if ( '"' == *item && '"' == item[item_len-1] ) 9441 { 9442 item++; 9443 item[item_len-2] = '\0'; 9444 add_item(head, tail, item, count); 9445 return; 9446 } 9447 } 9448 resultspec.dsc$b_dtype = DSC$K_DTYPE_T; 9449 resultspec.dsc$b_class = DSC$K_CLASS_D; 9450 resultspec.dsc$a_pointer = NULL; 9451 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS); 9452 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9453 if ((isunix = (int) strchr(item,'/')) != (int) NULL) 9454 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL); 9455 if (!isunix || !filespec.dsc$a_pointer) 9456 filespec.dsc$a_pointer = item; 9457 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer); 9458 /* 9459 * Only return version specs, if the caller specified a version 9460 */ 9461 had_version = strchr(item, ';'); 9462 /* 9463 * Only return device and directory specs, if the caller specified either. 9464 */ 9465 had_device = strchr(item, ':'); 9466 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<')); 9467 9468 while ($VMS_STATUS_SUCCESS(sts = lib$find_file 9469 (&filespec, &resultspec, &context, 9470 &defaultspec, 0, &rms_sts, &lff_flags))) 9471 { 9472 char *string; 9473 char *c; 9474 9475 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1); 9476 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9477 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1); 9478 if (NULL == had_version) 9479 *(strrchr(string, ';')) = '\0'; 9480 if ((!had_directory) && (had_device == NULL)) 9481 { 9482 if (NULL == (devdir = strrchr(string, ']'))) 9483 devdir = strrchr(string, '>'); 9484 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1); 9485 } 9486 /* 9487 * Be consistent with what the C RTL has already done to the rest of 9488 * the argv items and lowercase all of these names. 9489 */ 9490 if (!decc_efs_case_preserve) { 9491 for (c = string; *c; ++c) 9492 if (isupper(*c)) 9493 *c = tolower(*c); 9494 } 9495 if (isunix) trim_unixpath(string,item,1); 9496 add_item(head, tail, string, count); 9497 ++expcount; 9498 } 9499 PerlMem_free(vmsspec); 9500 if (sts != RMS$_NMF) 9501 { 9502 set_vaxc_errno(sts); 9503 switch (sts) 9504 { 9505 case RMS$_FNF: case RMS$_DNF: 9506 set_errno(ENOENT); break; 9507 case RMS$_DIR: 9508 set_errno(ENOTDIR); break; 9509 case RMS$_DEV: 9510 set_errno(ENODEV); break; 9511 case RMS$_FNM: case RMS$_SYN: 9512 set_errno(EINVAL); break; 9513 case RMS$_PRV: 9514 set_errno(EACCES); break; 9515 default: 9516 _ckvmssts_noperl(sts); 9517 } 9518 } 9519 if (expcount == 0) 9520 add_item(head, tail, item, count); 9521 _ckvmssts_noperl(lib$sfree1_dd(&resultspec)); 9522 _ckvmssts_noperl(lib$find_file_end(&context)); 9523 } 9524 9525 9526 static void 9527 pipe_and_fork(pTHX_ char **cmargv) 9528 { 9529 PerlIO *fp; 9530 struct dsc$descriptor_s *vmscmd; 9531 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q; 9532 int sts, j, l, ismcr, quote, tquote = 0; 9533 9534 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd); 9535 vms_execfree(vmscmd); 9536 9537 j = l = 0; 9538 p = subcmd; 9539 q = cmargv[0]; 9540 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C' 9541 && toupper(*(q+2)) == 'R' && !*(q+3); 9542 9543 while (q && l < MAX_DCL_LINE_LENGTH) { 9544 if (!*q) { 9545 if (j > 0 && quote) { 9546 *p++ = '"'; 9547 l++; 9548 } 9549 q = cmargv[++j]; 9550 if (q) { 9551 if (ismcr && j > 1) quote = 1; 9552 tquote = (strchr(q,' ')) != NULL || *q == '\0'; 9553 *p++ = ' '; 9554 l++; 9555 if (quote || tquote) { 9556 *p++ = '"'; 9557 l++; 9558 } 9559 } 9560 } else { 9561 if ((quote||tquote) && *q == '"') { 9562 *p++ = '"'; 9563 l++; 9564 } 9565 *p++ = *q++; 9566 l++; 9567 } 9568 } 9569 *p = '\0'; 9570 9571 fp = safe_popen(aTHX_ subcmd,"wbF",&sts); 9572 if (fp == NULL) { 9573 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts); 9574 } 9575 } 9576 9577 static int 9578 background_process(pTHX_ int argc, char **argv) 9579 { 9580 char command[MAX_DCL_SYMBOL + 1] = "$"; 9581 $DESCRIPTOR(value, ""); 9582 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND"); 9583 static $DESCRIPTOR(null, "NLA0:"); 9584 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID"); 9585 char pidstring[80]; 9586 $DESCRIPTOR(pidstr, ""); 9587 int pid; 9588 unsigned long int flags = 17, one = 1, retsts; 9589 int len; 9590 9591 len = my_strlcat(command, argv[0], sizeof(command)); 9592 while (--argc && (len < MAX_DCL_SYMBOL)) 9593 { 9594 my_strlcat(command, " \"", sizeof(command)); 9595 my_strlcat(command, *(++argv), sizeof(command)); 9596 len = my_strlcat(command, "\"", sizeof(command)); 9597 } 9598 value.dsc$a_pointer = command; 9599 value.dsc$w_length = strlen(value.dsc$a_pointer); 9600 _ckvmssts_noperl(lib$set_symbol(&cmd, &value)); 9601 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid); 9602 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */ 9603 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid)); 9604 } 9605 else { 9606 _ckvmssts_noperl(retsts); 9607 } 9608 #ifdef ARGPROC_DEBUG 9609 PerlIO_printf(Perl_debug_log, "%s\n", command); 9610 #endif 9611 sprintf(pidstring, "%08X", pid); 9612 PerlIO_printf(Perl_debug_log, "%s\n", pidstring); 9613 pidstr.dsc$a_pointer = pidstring; 9614 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer); 9615 lib$set_symbol(&pidsymbol, &pidstr); 9616 return(SS$_NORMAL); 9617 } 9618 /*}}}*/ 9619 /***** End of code taken from Mark Pizzolato's argproc.c package *****/ 9620 9621 9622 /* OS-specific initialization at image activation (not thread startup) */ 9623 /* Older VAXC header files lack these constants */ 9624 #ifndef JPI$_RIGHTS_SIZE 9625 # define JPI$_RIGHTS_SIZE 817 9626 #endif 9627 #ifndef KGB$M_SUBSYSTEM 9628 # define KGB$M_SUBSYSTEM 0x8 9629 #endif 9630 9631 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */ 9632 9633 /*{{{void vms_image_init(int *, char ***)*/ 9634 void 9635 vms_image_init(int *argcp, char ***argvp) 9636 { 9637 int status; 9638 char eqv[LNM$C_NAMLENGTH+1] = ""; 9639 unsigned int len, tabct = 8, tabidx = 0; 9640 unsigned long int *mask, iosb[2], i, rlst[128], rsz; 9641 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)]; 9642 unsigned short int dummy, rlen; 9643 struct dsc$descriptor_s **tabvec; 9644 #if defined(PERL_IMPLICIT_CONTEXT) 9645 pTHX = NULL; 9646 #endif 9647 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy}, 9648 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen}, 9649 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy}, 9650 { 0, 0, 0, 0} }; 9651 9652 #ifdef KILL_BY_SIGPRC 9653 Perl_csighandler_init(); 9654 #endif 9655 9656 /* This was moved from the pre-image init handler because on threaded */ 9657 /* Perl it was always returning 0 for the default value. */ 9658 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH); 9659 if (status > 0) { 9660 int s; 9661 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT"); 9662 if (s > 0) { 9663 int initial; 9664 initial = decc$feature_get_value(s, 4); 9665 if (initial > 0) { 9666 /* initial is: 0 if nothing has set the feature */ 9667 /* -1 if initialized to default */ 9668 /* 1 if set by logical name */ 9669 /* 2 if set by decc$feature_set_value */ 9670 decc_disable_posix_root = decc$feature_get_value(s, 1); 9671 9672 /* If the value is not valid, force the feature off */ 9673 if (decc_disable_posix_root < 0) { 9674 decc$feature_set_value(s, 1, 1); 9675 decc_disable_posix_root = 1; 9676 } 9677 } 9678 else { 9679 /* Nothing has asked for it explicitly, so use our own default. */ 9680 decc_disable_posix_root = 1; 9681 decc$feature_set_value(s, 1, 1); 9682 } 9683 } 9684 } 9685 9686 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL)); 9687 _ckvmssts_noperl(iosb[0]); 9688 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) { 9689 if (iprv[i]) { /* Running image installed with privs? */ 9690 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */ 9691 will_taint = TRUE; 9692 break; 9693 } 9694 } 9695 /* Rights identifiers might trigger tainting as well. */ 9696 if (!will_taint && (rlen || rsz)) { 9697 while (rlen < rsz) { 9698 /* We didn't get all the identifiers on the first pass. Allocate a 9699 * buffer much larger than $GETJPI wants (rsz is size in bytes that 9700 * were needed to hold all identifiers at time of last call; we'll 9701 * allocate that many unsigned long ints), and go back and get 'em. 9702 * If it gave us less than it wanted to despite ample buffer space, 9703 * something's broken. Is your system missing a system identifier? 9704 */ 9705 if (rsz <= jpilist[1].buflen) { 9706 /* Perl_croak accvios when used this early in startup. */ 9707 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s", 9708 rsz, (unsigned long) jpilist[1].buflen, 9709 "Check your rights database for corruption.\n"); 9710 exit(SS$_ABORT); 9711 } 9712 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr); 9713 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int)); 9714 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9715 jpilist[1].buflen = rsz * sizeof(unsigned long int); 9716 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL)); 9717 _ckvmssts_noperl(iosb[0]); 9718 } 9719 mask = (unsigned long int *)jpilist[1].bufadr; 9720 /* Check attribute flags for each identifier (2nd longword); protected 9721 * subsystem identifiers trigger tainting. 9722 */ 9723 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) { 9724 if (mask[i] & KGB$M_SUBSYSTEM) { 9725 will_taint = TRUE; 9726 break; 9727 } 9728 } 9729 if (mask != rlst) PerlMem_free(mask); 9730 } 9731 9732 /* When Perl is in decc_filename_unix_report mode and is run from a concealed 9733 * logical, some versions of the CRTL will add a phanthom /000000/ 9734 * directory. This needs to be removed. 9735 */ 9736 if (decc_filename_unix_report) { 9737 char * zeros; 9738 int ulen; 9739 ulen = strlen(argvp[0][0]); 9740 if (ulen > 7) { 9741 zeros = strstr(argvp[0][0], "/000000/"); 9742 if (zeros != NULL) { 9743 int mlen; 9744 mlen = ulen - (zeros - argvp[0][0]) - 7; 9745 memmove(zeros, &zeros[7], mlen); 9746 ulen = ulen - 7; 9747 argvp[0][0][ulen] = '\0'; 9748 } 9749 } 9750 /* It also may have a trailing dot that needs to be removed otherwise 9751 * it will be converted to VMS mode incorrectly. 9752 */ 9753 ulen--; 9754 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype)) 9755 argvp[0][0][ulen] = '\0'; 9756 } 9757 9758 /* We need to use this hack to tell Perl it should run with tainting, 9759 * since its tainting flag may be part of the PL_curinterp struct, which 9760 * hasn't been allocated when vms_image_init() is called. 9761 */ 9762 if (will_taint) { 9763 char **newargv, **oldargv; 9764 oldargv = *argvp; 9765 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *)); 9766 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9767 newargv[0] = oldargv[0]; 9768 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char)); 9769 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9770 strcpy(newargv[1], "-T"); 9771 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **); 9772 (*argcp)++; 9773 newargv[*argcp] = NULL; 9774 /* We orphan the old argv, since we don't know where it's come from, 9775 * so we don't know how to free it. 9776 */ 9777 *argvp = newargv; 9778 } 9779 else { /* Did user explicitly request tainting? */ 9780 int i; 9781 char *cp, **av = *argvp; 9782 for (i = 1; i < *argcp; i++) { 9783 if (*av[i] != '-') break; 9784 for (cp = av[i]+1; *cp; cp++) { 9785 if (*cp == 'T') { will_taint = 1; break; } 9786 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' || 9787 strchr("DFIiMmx",*cp)) break; 9788 } 9789 if (will_taint) break; 9790 } 9791 } 9792 9793 for (tabidx = 0; 9794 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx); 9795 tabidx++) { 9796 if (!tabidx) { 9797 tabvec = (struct dsc$descriptor_s **) 9798 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *)); 9799 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9800 } 9801 else if (tabidx >= tabct) { 9802 tabct += 8; 9803 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *)); 9804 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9805 } 9806 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 9807 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9808 tabvec[tabidx]->dsc$w_length = len; 9809 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T; 9810 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S; 9811 tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1); 9812 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9813 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1); 9814 } 9815 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; } 9816 9817 getredirection(argcp,argvp); 9818 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) ) 9819 { 9820 # include <reentrancy.h> 9821 decc$set_reentrancy(C$C_MULTITHREAD); 9822 } 9823 #endif 9824 return; 9825 } 9826 /*}}}*/ 9827 9828 9829 /* trim_unixpath() 9830 * Trim Unix-style prefix off filespec, so it looks like what a shell 9831 * glob expansion would return (i.e. from specified prefix on, not 9832 * full path). Note that returned filespec is Unix-style, regardless 9833 * of whether input filespec was VMS-style or Unix-style. 9834 * 9835 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to 9836 * determine prefix (both may be in VMS or Unix syntax). opts is a bit 9837 * vector of options; at present, only bit 0 is used, and if set tells 9838 * trim unixpath to try the current default directory as a prefix when 9839 * presented with a possibly ambiguous ... wildcard. 9840 * 9841 * Returns !=0 on success, with trimmed filespec replacing contents of 9842 * fspec, and 0 on failure, with contents of fpsec unchanged. 9843 */ 9844 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/ 9845 int 9846 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts) 9847 { 9848 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2; 9849 int tmplen, reslen = 0, dirs = 0; 9850 9851 if (!wildspec || !fspec) return 0; 9852 9853 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS); 9854 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9855 tplate = unixwild; 9856 if (strpbrk(wildspec,"]>:") != NULL) { 9857 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) { 9858 PerlMem_free(unixwild); 9859 return 0; 9860 } 9861 } 9862 else { 9863 my_strlcpy(unixwild, wildspec, VMS_MAXRSS); 9864 } 9865 unixified = (char *)PerlMem_malloc(VMS_MAXRSS); 9866 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9867 if (strpbrk(fspec,"]>:") != NULL) { 9868 if (int_tounixspec(fspec, unixified, NULL) == NULL) { 9869 PerlMem_free(unixwild); 9870 PerlMem_free(unixified); 9871 return 0; 9872 } 9873 else base = unixified; 9874 /* reslen != 0 ==> we had to unixify resultant filespec, so we must 9875 * check to see that final result fits into (isn't longer than) fspec */ 9876 reslen = strlen(fspec); 9877 } 9878 else base = fspec; 9879 9880 /* No prefix or absolute path on wildcard, so nothing to remove */ 9881 if (!*tplate || *tplate == '/') { 9882 PerlMem_free(unixwild); 9883 if (base == fspec) { 9884 PerlMem_free(unixified); 9885 return 1; 9886 } 9887 tmplen = strlen(unixified); 9888 if (tmplen > reslen) { 9889 PerlMem_free(unixified); 9890 return 0; /* not enough space */ 9891 } 9892 /* Copy unixified resultant, including trailing NUL */ 9893 memmove(fspec,unixified,tmplen+1); 9894 PerlMem_free(unixified); 9895 return 1; 9896 } 9897 9898 for (end = base; *end; end++) ; /* Find end of resultant filespec */ 9899 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */ 9900 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++; 9901 for (cp1 = end ;cp1 >= base; cp1--) 9902 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */ 9903 { cp1++; break; } 9904 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1); 9905 PerlMem_free(unixified); 9906 PerlMem_free(unixwild); 9907 return 1; 9908 } 9909 else { 9910 char *tpl, *lcres; 9911 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1; 9912 int ells = 1, totells, segdirs, match; 9913 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, 9914 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 9915 9916 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;} 9917 totells = ells; 9918 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++; 9919 tpl = (char *)PerlMem_malloc(VMS_MAXRSS); 9920 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9921 if (ellipsis == tplate && opts & 1) { 9922 /* Template begins with an ellipsis. Since we can't tell how many 9923 * directory names at the front of the resultant to keep for an 9924 * arbitrary starting point, we arbitrarily choose the current 9925 * default directory as a starting point. If it's there as a prefix, 9926 * clip it off. If not, fall through and act as if the leading 9927 * ellipsis weren't there (i.e. return shortest possible path that 9928 * could match template). 9929 */ 9930 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) { 9931 PerlMem_free(tpl); 9932 PerlMem_free(unixified); 9933 PerlMem_free(unixwild); 9934 return 0; 9935 } 9936 if (!decc_efs_case_preserve) { 9937 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9938 if (_tolower(*cp1) != _tolower(*cp2)) break; 9939 } 9940 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9941 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--; 9942 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) { 9943 memmove(fspec,cp2+1,end - cp2); 9944 PerlMem_free(tpl); 9945 PerlMem_free(unixified); 9946 PerlMem_free(unixwild); 9947 return 1; 9948 } 9949 } 9950 /* First off, back up over constant elements at end of path */ 9951 if (dirs) { 9952 for (front = end ; front >= base; front--) 9953 if (*front == '/' && !dirs--) { front++; break; } 9954 } 9955 lcres = (char *)PerlMem_malloc(VMS_MAXRSS); 9956 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9957 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1); 9958 cp1++,cp2++) { 9959 if (!decc_efs_case_preserve) { 9960 *cp2 = _tolower(*cp1); /* Make lc copy for match */ 9961 } 9962 else { 9963 *cp2 = *cp1; 9964 } 9965 } 9966 if (cp1 != '\0') { 9967 PerlMem_free(tpl); 9968 PerlMem_free(unixified); 9969 PerlMem_free(unixwild); 9970 PerlMem_free(lcres); 9971 return 0; /* Path too long. */ 9972 } 9973 lcend = cp2; 9974 *cp2 = '\0'; /* Pick up with memcpy later */ 9975 lcfront = lcres + (front - base); 9976 /* Now skip over each ellipsis and try to match the path in front of it. */ 9977 while (ells--) { 9978 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--) 9979 if (*(cp1) == '.' && *(cp1+1) == '.' && 9980 *(cp1+2) == '.' && *(cp1+3) == '/' ) break; 9981 if (cp1 < tplate) break; /* template started with an ellipsis */ 9982 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */ 9983 ellipsis = cp1; continue; 9984 } 9985 wilddsc.dsc$a_pointer = tpl; 9986 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1; 9987 nextell = cp1; 9988 for (segdirs = 0, cp2 = tpl; 9989 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1); 9990 cp1++, cp2++) { 9991 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */ 9992 else { 9993 if (!decc_efs_case_preserve) { 9994 *cp2 = _tolower(*cp1); /* else lowercase for match */ 9995 } 9996 else { 9997 *cp2 = *cp1; /* else preserve case for match */ 9998 } 9999 } 10000 if (*cp2 == '/') segdirs++; 10001 } 10002 if (cp1 != ellipsis - 1) { 10003 PerlMem_free(tpl); 10004 PerlMem_free(unixified); 10005 PerlMem_free(unixwild); 10006 PerlMem_free(lcres); 10007 return 0; /* Path too long */ 10008 } 10009 /* Back up at least as many dirs as in template before matching */ 10010 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--) 10011 if (*cp1 == '/' && !segdirs--) { cp1++; break; } 10012 for (match = 0; cp1 > lcres;) { 10013 resdsc.dsc$a_pointer = cp1; 10014 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) { 10015 match++; 10016 if (match == 1) lcfront = cp1; 10017 } 10018 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; } 10019 } 10020 if (!match) { 10021 PerlMem_free(tpl); 10022 PerlMem_free(unixified); 10023 PerlMem_free(unixwild); 10024 PerlMem_free(lcres); 10025 return 0; /* Can't find prefix ??? */ 10026 } 10027 if (match > 1 && opts & 1) { 10028 /* This ... wildcard could cover more than one set of dirs (i.e. 10029 * a set of similar dir names is repeated). If the template 10030 * contains more than 1 ..., upstream elements could resolve the 10031 * ambiguity, but it's not worth a full backtracking setup here. 10032 * As a quick heuristic, clip off the current default directory 10033 * if it's present to find the trimmed spec, else use the 10034 * shortest string that this ... could cover. 10035 */ 10036 char def[NAM$C_MAXRSS+1], *st; 10037 10038 if (getcwd(def, sizeof def,0) == NULL) { 10039 PerlMem_free(unixified); 10040 PerlMem_free(unixwild); 10041 PerlMem_free(lcres); 10042 PerlMem_free(tpl); 10043 return 0; 10044 } 10045 if (!decc_efs_case_preserve) { 10046 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 10047 if (_tolower(*cp1) != _tolower(*cp2)) break; 10048 } 10049 segdirs = dirs - totells; /* Min # of dirs we must have left */ 10050 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--; 10051 if (*cp1 == '\0' && *cp2 == '/') { 10052 memmove(fspec,cp2+1,end - cp2); 10053 PerlMem_free(tpl); 10054 PerlMem_free(unixified); 10055 PerlMem_free(unixwild); 10056 PerlMem_free(lcres); 10057 return 1; 10058 } 10059 /* Nope -- stick with lcfront from above and keep going. */ 10060 } 10061 } 10062 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1); 10063 PerlMem_free(tpl); 10064 PerlMem_free(unixified); 10065 PerlMem_free(unixwild); 10066 PerlMem_free(lcres); 10067 return 1; 10068 } 10069 10070 } /* end of trim_unixpath() */ 10071 /*}}}*/ 10072 10073 10074 /* 10075 * VMS readdir() routines. 10076 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990. 10077 * 10078 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu 10079 * Minor modifications to original routines. 10080 */ 10081 10082 /* readdir may have been redefined by reentr.h, so make sure we get 10083 * the local version for what we do here. 10084 */ 10085 #ifdef readdir 10086 # undef readdir 10087 #endif 10088 #if !defined(PERL_IMPLICIT_CONTEXT) 10089 # define readdir Perl_readdir 10090 #else 10091 # define readdir(a) Perl_readdir(aTHX_ a) 10092 #endif 10093 10094 /* Number of elements in vms_versions array */ 10095 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0]) 10096 10097 /* 10098 * Open a directory, return a handle for later use. 10099 */ 10100 /*{{{ DIR *opendir(char*name) */ 10101 DIR * 10102 Perl_opendir(pTHX_ const char *name) 10103 { 10104 DIR *dd; 10105 char *dir; 10106 Stat_t sb; 10107 10108 Newx(dir, VMS_MAXRSS, char); 10109 if (int_tovmspath(name, dir, NULL) == NULL) { 10110 Safefree(dir); 10111 return NULL; 10112 } 10113 /* Check access before stat; otherwise stat does not 10114 * accurately report whether it's a directory. 10115 */ 10116 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */ 10117 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) { 10118 /* cando_by_name has already set errno */ 10119 Safefree(dir); 10120 return NULL; 10121 } 10122 if (flex_stat(dir,&sb) == -1) return NULL; 10123 if (!S_ISDIR(sb.st_mode)) { 10124 Safefree(dir); 10125 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR); 10126 return NULL; 10127 } 10128 /* Get memory for the handle, and the pattern. */ 10129 Newx(dd,1,DIR); 10130 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char); 10131 10132 /* Fill in the fields; mainly playing with the descriptor. */ 10133 sprintf(dd->pattern, "%s*.*",dir); 10134 Safefree(dir); 10135 dd->context = 0; 10136 dd->count = 0; 10137 dd->flags = 0; 10138 /* By saying we want the result of readdir() in unix format, we are really 10139 * saying we want all the escapes removed, translating characters that 10140 * must be escaped in a VMS-format name to their unescaped form, which is 10141 * presumably allowed in a Unix-format name. 10142 */ 10143 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0; 10144 dd->pat.dsc$a_pointer = dd->pattern; 10145 dd->pat.dsc$w_length = strlen(dd->pattern); 10146 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T; 10147 dd->pat.dsc$b_class = DSC$K_CLASS_S; 10148 #if defined(USE_ITHREADS) 10149 Newx(dd->mutex,1,perl_mutex); 10150 MUTEX_INIT( (perl_mutex *) dd->mutex ); 10151 #else 10152 dd->mutex = NULL; 10153 #endif 10154 10155 return dd; 10156 } /* end of opendir() */ 10157 /*}}}*/ 10158 10159 /* 10160 * Set the flag to indicate we want versions or not. 10161 */ 10162 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/ 10163 void 10164 vmsreaddirversions(DIR *dd, int flag) 10165 { 10166 if (flag) 10167 dd->flags |= PERL_VMSDIR_M_VERSIONS; 10168 else 10169 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 10170 } 10171 /*}}}*/ 10172 10173 /* 10174 * Free up an opened directory. 10175 */ 10176 /*{{{ void closedir(DIR *dd)*/ 10177 void 10178 Perl_closedir(DIR *dd) 10179 { 10180 int sts; 10181 10182 sts = lib$find_file_end(&dd->context); 10183 Safefree(dd->pattern); 10184 #if defined(USE_ITHREADS) 10185 MUTEX_DESTROY( (perl_mutex *) dd->mutex ); 10186 Safefree(dd->mutex); 10187 #endif 10188 Safefree(dd); 10189 } 10190 /*}}}*/ 10191 10192 /* 10193 * Collect all the version numbers for the current file. 10194 */ 10195 static void 10196 collectversions(pTHX_ DIR *dd) 10197 { 10198 struct dsc$descriptor_s pat; 10199 struct dsc$descriptor_s res; 10200 struct dirent *e; 10201 char *p, *text, *buff; 10202 int i; 10203 unsigned long context, tmpsts; 10204 10205 /* Convenient shorthand. */ 10206 e = &dd->entry; 10207 10208 /* Add the version wildcard, ignoring the "*.*" put on before */ 10209 i = strlen(dd->pattern); 10210 Newx(text,i + e->d_namlen + 3,char); 10211 my_strlcpy(text, dd->pattern, i + 1); 10212 sprintf(&text[i - 3], "%s;*", e->d_name); 10213 10214 /* Set up the pattern descriptor. */ 10215 pat.dsc$a_pointer = text; 10216 pat.dsc$w_length = i + e->d_namlen - 1; 10217 pat.dsc$b_dtype = DSC$K_DTYPE_T; 10218 pat.dsc$b_class = DSC$K_CLASS_S; 10219 10220 /* Set up result descriptor. */ 10221 Newx(buff, VMS_MAXRSS, char); 10222 res.dsc$a_pointer = buff; 10223 res.dsc$w_length = VMS_MAXRSS - 1; 10224 res.dsc$b_dtype = DSC$K_DTYPE_T; 10225 res.dsc$b_class = DSC$K_CLASS_S; 10226 10227 /* Read files, collecting versions. */ 10228 for (context = 0, e->vms_verscount = 0; 10229 e->vms_verscount < VERSIZE(e); 10230 e->vms_verscount++) { 10231 unsigned long rsts; 10232 unsigned long flags = 0; 10233 10234 #ifdef VMS_LONGNAME_SUPPORT 10235 flags = LIB$M_FIL_LONG_NAMES; 10236 #endif 10237 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags); 10238 if (tmpsts == RMS$_NMF || context == 0) break; 10239 _ckvmssts(tmpsts); 10240 buff[VMS_MAXRSS - 1] = '\0'; 10241 if ((p = strchr(buff, ';'))) 10242 e->vms_versions[e->vms_verscount] = atoi(p + 1); 10243 else 10244 e->vms_versions[e->vms_verscount] = -1; 10245 } 10246 10247 _ckvmssts(lib$find_file_end(&context)); 10248 Safefree(text); 10249 Safefree(buff); 10250 10251 } /* end of collectversions() */ 10252 10253 /* 10254 * Read the next entry from the directory. 10255 */ 10256 /*{{{ struct dirent *readdir(DIR *dd)*/ 10257 struct dirent * 10258 Perl_readdir(pTHX_ DIR *dd) 10259 { 10260 struct dsc$descriptor_s res; 10261 char *p, *buff; 10262 unsigned long int tmpsts; 10263 unsigned long rsts; 10264 unsigned long flags = 0; 10265 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 10266 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 10267 10268 /* Set up result descriptor, and get next file. */ 10269 Newx(buff, VMS_MAXRSS, char); 10270 res.dsc$a_pointer = buff; 10271 res.dsc$w_length = VMS_MAXRSS - 1; 10272 res.dsc$b_dtype = DSC$K_DTYPE_T; 10273 res.dsc$b_class = DSC$K_CLASS_S; 10274 10275 #ifdef VMS_LONGNAME_SUPPORT 10276 flags = LIB$M_FIL_LONG_NAMES; 10277 #endif 10278 10279 tmpsts = lib$find_file 10280 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags); 10281 if (dd->context == 0) 10282 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */ 10283 10284 if (!(tmpsts & 1)) { 10285 switch (tmpsts) { 10286 case RMS$_NMF: 10287 break; /* no more files considered success */ 10288 case RMS$_PRV: 10289 SETERRNO(EACCES, tmpsts); break; 10290 case RMS$_DEV: 10291 SETERRNO(ENODEV, tmpsts); break; 10292 case RMS$_DIR: 10293 SETERRNO(ENOTDIR, tmpsts); break; 10294 case RMS$_FNF: case RMS$_DNF: 10295 SETERRNO(ENOENT, tmpsts); break; 10296 default: 10297 SETERRNO(EVMSERR, tmpsts); 10298 } 10299 Safefree(buff); 10300 return NULL; 10301 } 10302 dd->count++; 10303 /* Force the buffer to end with a NUL, and downcase name to match C convention. */ 10304 buff[res.dsc$w_length] = '\0'; 10305 p = buff + res.dsc$w_length; 10306 while (--p >= buff) if (!isspace(*p)) break; 10307 *p = '\0'; 10308 if (!decc_efs_case_preserve) { 10309 for (p = buff; *p; p++) *p = _tolower(*p); 10310 } 10311 10312 /* Skip any directory component and just copy the name. */ 10313 sts = vms_split_path 10314 (buff, 10315 &v_spec, 10316 &v_len, 10317 &r_spec, 10318 &r_len, 10319 &d_spec, 10320 &d_len, 10321 &n_spec, 10322 &n_len, 10323 &e_spec, 10324 &e_len, 10325 &vs_spec, 10326 &vs_len); 10327 10328 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 10329 10330 /* In Unix report mode, remove the ".dir;1" from the name */ 10331 /* if it is a real directory. */ 10332 if (decc_filename_unix_report && decc_efs_charset) { 10333 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 10334 Stat_t statbuf; 10335 int ret_sts; 10336 10337 ret_sts = flex_lstat(buff, &statbuf); 10338 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) { 10339 e_len = 0; 10340 e_spec[0] = 0; 10341 } 10342 } 10343 } 10344 10345 /* Drop NULL extensions on UNIX file specification */ 10346 if ((e_len == 1) && decc_readdir_dropdotnotype) { 10347 e_len = 0; 10348 e_spec[0] = '\0'; 10349 } 10350 } 10351 10352 memcpy(dd->entry.d_name, n_spec, n_len + e_len); 10353 dd->entry.d_name[n_len + e_len] = '\0'; 10354 dd->entry.d_namlen = n_len + e_len; 10355 10356 /* Convert the filename to UNIX format if needed */ 10357 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 10358 10359 /* Translate the encoded characters. */ 10360 /* Fixme: Unicode handling could result in embedded 0 characters */ 10361 if (strchr(dd->entry.d_name, '^') != NULL) { 10362 char new_name[256]; 10363 char * q; 10364 p = dd->entry.d_name; 10365 q = new_name; 10366 while (*p != 0) { 10367 int inchars_read, outchars_added; 10368 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added); 10369 p += inchars_read; 10370 q += outchars_added; 10371 /* fix-me */ 10372 /* if outchars_added > 1, then this is a wide file specification */ 10373 /* Wide file specifications need to be passed in Perl */ 10374 /* counted strings apparently with a Unicode flag */ 10375 } 10376 *q = 0; 10377 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name)); 10378 } 10379 } 10380 10381 dd->entry.vms_verscount = 0; 10382 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd); 10383 Safefree(buff); 10384 return &dd->entry; 10385 10386 } /* end of readdir() */ 10387 /*}}}*/ 10388 10389 /* 10390 * Read the next entry from the directory -- thread-safe version. 10391 */ 10392 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/ 10393 int 10394 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result) 10395 { 10396 int retval; 10397 10398 MUTEX_LOCK( (perl_mutex *) dd->mutex ); 10399 10400 entry = readdir(dd); 10401 *result = entry; 10402 retval = ( *result == NULL ? errno : 0 ); 10403 10404 MUTEX_UNLOCK( (perl_mutex *) dd->mutex ); 10405 10406 return retval; 10407 10408 } /* end of readdir_r() */ 10409 /*}}}*/ 10410 10411 /* 10412 * Return something that can be used in a seekdir later. 10413 */ 10414 /*{{{ long telldir(DIR *dd)*/ 10415 long 10416 Perl_telldir(DIR *dd) 10417 { 10418 return dd->count; 10419 } 10420 /*}}}*/ 10421 10422 /* 10423 * Return to a spot where we used to be. Brute force. 10424 */ 10425 /*{{{ void seekdir(DIR *dd,long count)*/ 10426 void 10427 Perl_seekdir(pTHX_ DIR *dd, long count) 10428 { 10429 int old_flags; 10430 10431 /* If we haven't done anything yet... */ 10432 if (dd->count == 0) 10433 return; 10434 10435 /* Remember some state, and clear it. */ 10436 old_flags = dd->flags; 10437 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 10438 _ckvmssts(lib$find_file_end(&dd->context)); 10439 dd->context = 0; 10440 10441 /* The increment is in readdir(). */ 10442 for (dd->count = 0; dd->count < count; ) 10443 readdir(dd); 10444 10445 dd->flags = old_flags; 10446 10447 } /* end of seekdir() */ 10448 /*}}}*/ 10449 10450 /* VMS subprocess management 10451 * 10452 * my_vfork() - just a vfork(), after setting a flag to record that 10453 * the current script is trying a Unix-style fork/exec. 10454 * 10455 * vms_do_aexec() and vms_do_exec() are called in response to the 10456 * perl 'exec' function. If this follows a vfork call, then they 10457 * call out the regular perl routines in doio.c which do an 10458 * execvp (for those who really want to try this under VMS). 10459 * Otherwise, they do exactly what the perl docs say exec should 10460 * do - terminate the current script and invoke a new command 10461 * (See below for notes on command syntax.) 10462 * 10463 * do_aspawn() and do_spawn() implement the VMS side of the perl 10464 * 'system' function. 10465 * 10466 * Note on command arguments to perl 'exec' and 'system': When handled 10467 * in 'VMSish fashion' (i.e. not after a call to vfork) The args 10468 * are concatenated to form a DCL command string. If the first non-numeric 10469 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such), 10470 * the command string is handed off to DCL directly. Otherwise, 10471 * the first token of the command is taken as the filespec of an image 10472 * to run. The filespec is expanded using a default type of '.EXE' and 10473 * the process defaults for device, directory, etc., and if found, the resultant 10474 * filespec is invoked using the DCL verb 'MCR', and passed the rest of 10475 * the command string as parameters. This is perhaps a bit complicated, 10476 * but I hope it will form a happy medium between what VMS folks expect 10477 * from lib$spawn and what Unix folks expect from exec. 10478 */ 10479 10480 static int vfork_called; 10481 10482 /*{{{int my_vfork(void)*/ 10483 int 10484 my_vfork(void) 10485 { 10486 vfork_called++; 10487 return vfork(); 10488 } 10489 /*}}}*/ 10490 10491 10492 static void 10493 vms_execfree(struct dsc$descriptor_s *vmscmd) 10494 { 10495 if (vmscmd) { 10496 if (vmscmd->dsc$a_pointer) { 10497 PerlMem_free(vmscmd->dsc$a_pointer); 10498 } 10499 PerlMem_free(vmscmd); 10500 } 10501 } 10502 10503 static char * 10504 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp) 10505 { 10506 char *junk, *tmps = NULL; 10507 size_t cmdlen = 0; 10508 size_t rlen; 10509 SV **idx; 10510 STRLEN n_a; 10511 10512 idx = mark; 10513 if (really) { 10514 tmps = SvPV(really,rlen); 10515 if (*tmps) { 10516 cmdlen += rlen + 1; 10517 idx++; 10518 } 10519 } 10520 10521 for (idx++; idx <= sp; idx++) { 10522 if (*idx) { 10523 junk = SvPVx(*idx,rlen); 10524 cmdlen += rlen ? rlen + 1 : 0; 10525 } 10526 } 10527 Newx(PL_Cmd, cmdlen+1, char); 10528 10529 if (tmps && *tmps) { 10530 my_strlcpy(PL_Cmd, tmps, cmdlen + 1); 10531 mark++; 10532 } 10533 else *PL_Cmd = '\0'; 10534 while (++mark <= sp) { 10535 if (*mark) { 10536 char *s = SvPVx(*mark,n_a); 10537 if (!*s) continue; 10538 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1); 10539 my_strlcat(PL_Cmd, s, cmdlen+1); 10540 } 10541 } 10542 return PL_Cmd; 10543 10544 } /* end of setup_argstr() */ 10545 10546 10547 static unsigned long int 10548 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote, 10549 struct dsc$descriptor_s **pvmscmd) 10550 { 10551 char * vmsspec; 10552 char * resspec; 10553 char image_name[NAM$C_MAXRSS+1]; 10554 char image_argv[NAM$C_MAXRSS+1]; 10555 $DESCRIPTOR(defdsc,".EXE"); 10556 $DESCRIPTOR(defdsc2,"."); 10557 struct dsc$descriptor_s resdsc; 10558 struct dsc$descriptor_s *vmscmd; 10559 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10560 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL; 10561 char *s, *rest, *cp, *wordbreak; 10562 char * cmd; 10563 int cmdlen; 10564 int isdcl; 10565 10566 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 10567 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10568 10569 /* vmsspec is a DCL command buffer, not just a filename */ 10570 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1); 10571 if (vmsspec == NULL) 10572 _ckvmssts_noperl(SS$_INSFMEM); 10573 10574 resspec = (char *)PerlMem_malloc(VMS_MAXRSS); 10575 if (resspec == NULL) 10576 _ckvmssts_noperl(SS$_INSFMEM); 10577 10578 /* Make a copy for modification */ 10579 cmdlen = strlen(incmd); 10580 cmd = (char *)PerlMem_malloc(cmdlen+1); 10581 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10582 my_strlcpy(cmd, incmd, cmdlen + 1); 10583 image_name[0] = 0; 10584 image_argv[0] = 0; 10585 10586 resdsc.dsc$a_pointer = resspec; 10587 resdsc.dsc$b_dtype = DSC$K_DTYPE_T; 10588 resdsc.dsc$b_class = DSC$K_CLASS_S; 10589 resdsc.dsc$w_length = VMS_MAXRSS - 1; 10590 10591 vmscmd->dsc$a_pointer = NULL; 10592 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T; 10593 vmscmd->dsc$b_class = DSC$K_CLASS_S; 10594 vmscmd->dsc$w_length = 0; 10595 if (pvmscmd) *pvmscmd = vmscmd; 10596 10597 if (suggest_quote) *suggest_quote = 0; 10598 10599 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) { 10600 PerlMem_free(cmd); 10601 PerlMem_free(vmsspec); 10602 PerlMem_free(resspec); 10603 return CLI$_BUFOVF; /* continuation lines currently unsupported */ 10604 } 10605 10606 s = cmd; 10607 10608 while (*s && isspace(*s)) s++; 10609 10610 if (*s == '@' || *s == '$') { 10611 vmsspec[0] = *s; rest = s + 1; 10612 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest; 10613 } 10614 else { cp = vmsspec; rest = s; } 10615 10616 /* If the first word is quoted, then we need to unquote it and 10617 * escape spaces within it. We'll expand into the resspec buffer, 10618 * then copy back into the cmd buffer, expanding the latter if 10619 * necessary. 10620 */ 10621 if (*rest == '"') { 10622 char *cp2; 10623 char *r = rest; 10624 bool in_quote = 0; 10625 int clen = cmdlen; 10626 int soff = s - cmd; 10627 10628 for (cp2 = resspec; 10629 *rest && cp2 - resspec < (VMS_MAXRSS - 1); 10630 rest++) { 10631 10632 if (*rest == ' ') { /* Escape ' ' to '^_'. */ 10633 *cp2 = '^'; 10634 *(++cp2) = '_'; 10635 cp2++; 10636 clen++; 10637 } 10638 else if (*rest == '"') { 10639 clen--; 10640 if (in_quote) { /* Must be closing quote. */ 10641 rest++; 10642 break; 10643 } 10644 in_quote = 1; 10645 } 10646 else { 10647 *cp2 = *rest; 10648 cp2++; 10649 } 10650 } 10651 *cp2 = '\0'; 10652 10653 /* Expand the command buffer if necessary. */ 10654 if (clen > cmdlen) { 10655 cmd = (char *)PerlMem_realloc(cmd, clen); 10656 if (cmd == NULL) 10657 _ckvmssts_noperl(SS$_INSFMEM); 10658 /* Where we are may have changed, so recompute offsets */ 10659 r = cmd + (r - s - soff); 10660 rest = cmd + (rest - s - soff); 10661 s = cmd + soff; 10662 } 10663 10664 /* Shift the non-verb portion of the command (if any) up or 10665 * down as necessary. 10666 */ 10667 if (*rest) 10668 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest); 10669 10670 /* Copy the unquoted and escaped command verb into place. */ 10671 memcpy(r, resspec, cp2 - resspec); 10672 cmd[clen] = '\0'; 10673 cmdlen = clen; 10674 rest = r; /* Rewind for subsequent operations. */ 10675 } 10676 10677 if (*rest == '.' || *rest == '/') { 10678 char *cp2; 10679 for (cp2 = resspec; 10680 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1); 10681 rest++, cp2++) *cp2 = *rest; 10682 *cp2 = '\0'; 10683 if (int_tovmsspec(resspec, cp, 0, NULL)) { 10684 s = vmsspec; 10685 10686 /* When a UNIX spec with no file type is translated to VMS, */ 10687 /* A trailing '.' is appended under ODS-5 rules. */ 10688 /* Here we do not want that trailing "." as it prevents */ 10689 /* Looking for a implied ".exe" type. */ 10690 if (decc_efs_charset) { 10691 int i; 10692 i = strlen(vmsspec); 10693 if (vmsspec[i-1] == '.') { 10694 vmsspec[i-1] = '\0'; 10695 } 10696 } 10697 10698 if (*rest) { 10699 for (cp2 = vmsspec + strlen(vmsspec); 10700 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH; 10701 rest++, cp2++) *cp2 = *rest; 10702 *cp2 = '\0'; 10703 } 10704 } 10705 } 10706 /* Intuit whether verb (first word of cmd) is a DCL command: 10707 * - if first nonspace char is '@', it's a DCL indirection 10708 * otherwise 10709 * - if verb contains a filespec separator, it's not a DCL command 10710 * - if it doesn't, caller tells us whether to default to a DCL 10711 * command, or to a local image unless told it's DCL (by leading '$') 10712 */ 10713 if (*s == '@') { 10714 isdcl = 1; 10715 if (suggest_quote) *suggest_quote = 1; 10716 } else { 10717 char *filespec = strpbrk(s,":<[.;"); 10718 rest = wordbreak = strpbrk(s," \"\t/"); 10719 if (!wordbreak) wordbreak = s + strlen(s); 10720 if (*s == '$') check_img = 0; 10721 if (filespec && (filespec < wordbreak)) isdcl = 0; 10722 else isdcl = !check_img; 10723 } 10724 10725 if (!isdcl) { 10726 int rsts; 10727 imgdsc.dsc$a_pointer = s; 10728 imgdsc.dsc$w_length = wordbreak - s; 10729 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 10730 if (!(retsts&1)) { 10731 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10732 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 10733 if (!(retsts & 1) && *s == '$') { 10734 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10735 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--; 10736 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 10737 if (!(retsts&1)) { 10738 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10739 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 10740 } 10741 } 10742 } 10743 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10744 10745 if (retsts & 1) { 10746 FILE *fp; 10747 s = resspec; 10748 while (*s && !isspace(*s)) s++; 10749 *s = '\0'; 10750 10751 /* check that it's really not DCL with no file extension */ 10752 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get"); 10753 if (fp) { 10754 char b[256] = {0,0,0,0}; 10755 read(fileno(fp), b, 256); 10756 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]); 10757 if (isdcl) { 10758 int shebang_len; 10759 10760 /* Check for script */ 10761 shebang_len = 0; 10762 if ((b[0] == '#') && (b[1] == '!')) 10763 shebang_len = 2; 10764 #ifdef ALTERNATE_SHEBANG 10765 else { 10766 shebang_len = strlen(ALTERNATE_SHEBANG); 10767 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) { 10768 char * perlstr; 10769 perlstr = strstr("perl",b); 10770 if (perlstr == NULL) 10771 shebang_len = 0; 10772 } 10773 else 10774 shebang_len = 0; 10775 } 10776 #endif 10777 10778 if (shebang_len > 0) { 10779 int i; 10780 int j; 10781 char tmpspec[NAM$C_MAXRSS + 1]; 10782 10783 i = shebang_len; 10784 /* Image is following after white space */ 10785 /*--------------------------------------*/ 10786 while (isprint(b[i]) && isspace(b[i])) 10787 i++; 10788 10789 j = 0; 10790 while (isprint(b[i]) && !isspace(b[i])) { 10791 tmpspec[j++] = b[i++]; 10792 if (j >= NAM$C_MAXRSS) 10793 break; 10794 } 10795 tmpspec[j] = '\0'; 10796 10797 /* There may be some default parameters to the image */ 10798 /*---------------------------------------------------*/ 10799 j = 0; 10800 while (isprint(b[i])) { 10801 image_argv[j++] = b[i++]; 10802 if (j >= NAM$C_MAXRSS) 10803 break; 10804 } 10805 while ((j > 0) && !isprint(image_argv[j-1])) 10806 j--; 10807 image_argv[j] = 0; 10808 10809 /* It will need to be converted to VMS format and validated */ 10810 if (tmpspec[0] != '\0') { 10811 char * iname; 10812 10813 /* Try to find the exact program requested to be run */ 10814 /*---------------------------------------------------*/ 10815 iname = int_rmsexpand 10816 (tmpspec, image_name, ".exe", 10817 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10818 if (iname != NULL) { 10819 if (cando_by_name_int 10820 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) { 10821 /* MCR prefix needed */ 10822 isdcl = 0; 10823 } 10824 else { 10825 /* Try again with a null type */ 10826 /*----------------------------*/ 10827 iname = int_rmsexpand 10828 (tmpspec, image_name, ".", 10829 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10830 if (iname != NULL) { 10831 if (cando_by_name_int 10832 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) { 10833 /* MCR prefix needed */ 10834 isdcl = 0; 10835 } 10836 } 10837 } 10838 10839 /* Did we find the image to run the script? */ 10840 /*------------------------------------------*/ 10841 if (isdcl) { 10842 char *tchr; 10843 10844 /* Assume DCL or foreign command exists */ 10845 /*--------------------------------------*/ 10846 tchr = strrchr(tmpspec, '/'); 10847 if (tchr != NULL) { 10848 tchr++; 10849 } 10850 else { 10851 tchr = tmpspec; 10852 } 10853 my_strlcpy(image_name, tchr, sizeof(image_name)); 10854 } 10855 } 10856 } 10857 } 10858 } 10859 fclose(fp); 10860 } 10861 if (check_img && isdcl) { 10862 PerlMem_free(cmd); 10863 PerlMem_free(resspec); 10864 PerlMem_free(vmsspec); 10865 return RMS$_FNF; 10866 } 10867 10868 if (cando_by_name(S_IXUSR,0,resspec)) { 10869 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH); 10870 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10871 if (!isdcl) { 10872 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH); 10873 if (image_name[0] != 0) { 10874 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH); 10875 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10876 } 10877 } else if (image_name[0] != 0) { 10878 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH); 10879 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10880 } else { 10881 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH); 10882 } 10883 if (suggest_quote) *suggest_quote = 1; 10884 10885 /* If there is an image name, use original command */ 10886 if (image_name[0] == 0) 10887 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH); 10888 else { 10889 rest = cmd; 10890 while (*rest && isspace(*rest)) rest++; 10891 } 10892 10893 if (image_argv[0] != 0) { 10894 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH); 10895 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10896 } 10897 if (rest) { 10898 int rest_len; 10899 int vmscmd_len; 10900 10901 rest_len = strlen(rest); 10902 vmscmd_len = strlen(vmscmd->dsc$a_pointer); 10903 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH) 10904 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH); 10905 else 10906 retsts = CLI$_BUFOVF; 10907 } 10908 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer); 10909 PerlMem_free(cmd); 10910 PerlMem_free(vmsspec); 10911 PerlMem_free(resspec); 10912 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10913 } 10914 else 10915 retsts = RMS$_PRV; 10916 } 10917 } 10918 /* It's either a DCL command or we couldn't find a suitable image */ 10919 vmscmd->dsc$w_length = strlen(cmd); 10920 10921 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1); 10922 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1); 10923 10924 PerlMem_free(cmd); 10925 PerlMem_free(resspec); 10926 PerlMem_free(vmsspec); 10927 10928 /* check if it's a symbol (for quoting purposes) */ 10929 if (suggest_quote && !*suggest_quote) { 10930 int iss; 10931 char equiv[LNM$C_NAMLENGTH]; 10932 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10933 eqvdsc.dsc$a_pointer = equiv; 10934 10935 iss = lib$get_symbol(vmscmd,&eqvdsc); 10936 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1; 10937 } 10938 if (!(retsts & 1)) { 10939 /* just hand off status values likely to be due to user error */ 10940 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV || 10941 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN || 10942 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts; 10943 else { _ckvmssts_noperl(retsts); } 10944 } 10945 10946 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10947 10948 } /* end of setup_cmddsc() */ 10949 10950 10951 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */ 10952 bool 10953 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp) 10954 { 10955 bool exec_sts; 10956 char * cmd; 10957 10958 if (sp > mark) { 10959 if (vfork_called) { /* this follows a vfork - act Unixish */ 10960 vfork_called--; 10961 if (vfork_called < 0) { 10962 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10963 vfork_called = 0; 10964 } 10965 else return do_aexec(really,mark,sp); 10966 } 10967 /* no vfork - act VMSish */ 10968 cmd = setup_argstr(aTHX_ really,mark,sp); 10969 exec_sts = vms_do_exec(cmd); 10970 Safefree(cmd); /* Clean up from setup_argstr() */ 10971 return exec_sts; 10972 } 10973 10974 return FALSE; 10975 } /* end of vms_do_aexec() */ 10976 /*}}}*/ 10977 10978 /* {{{bool vms_do_exec(char *cmd) */ 10979 bool 10980 Perl_vms_do_exec(pTHX_ const char *cmd) 10981 { 10982 struct dsc$descriptor_s *vmscmd; 10983 10984 if (vfork_called) { /* this follows a vfork - act Unixish */ 10985 vfork_called--; 10986 if (vfork_called < 0) { 10987 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10988 vfork_called = 0; 10989 } 10990 else return do_exec(cmd); 10991 } 10992 10993 { /* no vfork - act VMSish */ 10994 unsigned long int retsts; 10995 10996 TAINT_ENV(); 10997 TAINT_PROPER("exec"); 10998 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1) 10999 retsts = lib$do_command(vmscmd); 11000 11001 switch (retsts) { 11002 case RMS$_FNF: case RMS$_DNF: 11003 set_errno(ENOENT); break; 11004 case RMS$_DIR: 11005 set_errno(ENOTDIR); break; 11006 case RMS$_DEV: 11007 set_errno(ENODEV); break; 11008 case RMS$_PRV: 11009 set_errno(EACCES); break; 11010 case RMS$_SYN: 11011 set_errno(EINVAL); break; 11012 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 11013 set_errno(E2BIG); break; 11014 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 11015 _ckvmssts_noperl(retsts); /* fall through */ 11016 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 11017 set_errno(EVMSERR); 11018 } 11019 set_vaxc_errno(retsts); 11020 if (ckWARN(WARN_EXEC)) { 11021 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s", 11022 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno)); 11023 } 11024 vms_execfree(vmscmd); 11025 } 11026 11027 return FALSE; 11028 11029 } /* end of vms_do_exec() */ 11030 /*}}}*/ 11031 11032 int do_spawn2(pTHX_ const char *, int); 11033 11034 int 11035 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp) 11036 { 11037 unsigned long int sts; 11038 char * cmd; 11039 int flags = 0; 11040 11041 if (sp > mark) { 11042 11043 /* We'll copy the (undocumented?) Win32 behavior and allow a 11044 * numeric first argument. But the only value we'll support 11045 * through do_aspawn is a value of 1, which means spawn without 11046 * waiting for completion -- other values are ignored. 11047 */ 11048 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) { 11049 ++mark; 11050 flags = SvIVx(*mark); 11051 } 11052 11053 if (flags && flags == 1) /* the Win32 P_NOWAIT value */ 11054 flags = CLI$M_NOWAIT; 11055 else 11056 flags = 0; 11057 11058 cmd = setup_argstr(aTHX_ really, mark, sp); 11059 sts = do_spawn2(aTHX_ cmd, flags); 11060 /* pp_sys will clean up cmd */ 11061 return sts; 11062 } 11063 return SS$_ABORT; 11064 } /* end of do_aspawn() */ 11065 /*}}}*/ 11066 11067 11068 /* {{{int do_spawn(char* cmd) */ 11069 int 11070 Perl_do_spawn(pTHX_ char* cmd) 11071 { 11072 PERL_ARGS_ASSERT_DO_SPAWN; 11073 11074 return do_spawn2(aTHX_ cmd, 0); 11075 } 11076 /*}}}*/ 11077 11078 /* {{{int do_spawn_nowait(char* cmd) */ 11079 int 11080 Perl_do_spawn_nowait(pTHX_ char* cmd) 11081 { 11082 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT; 11083 11084 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT); 11085 } 11086 /*}}}*/ 11087 11088 /* {{{int do_spawn2(char *cmd) */ 11089 int 11090 do_spawn2(pTHX_ const char *cmd, int flags) 11091 { 11092 unsigned long int sts, substs; 11093 11094 /* The caller of this routine expects to Safefree(PL_Cmd) */ 11095 Newx(PL_Cmd,10,char); 11096 11097 TAINT_ENV(); 11098 TAINT_PROPER("spawn"); 11099 if (!cmd || !*cmd) { 11100 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0); 11101 if (!(sts & 1)) { 11102 switch (sts) { 11103 case RMS$_FNF: case RMS$_DNF: 11104 set_errno(ENOENT); break; 11105 case RMS$_DIR: 11106 set_errno(ENOTDIR); break; 11107 case RMS$_DEV: 11108 set_errno(ENODEV); break; 11109 case RMS$_PRV: 11110 set_errno(EACCES); break; 11111 case RMS$_SYN: 11112 set_errno(EINVAL); break; 11113 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 11114 set_errno(E2BIG); break; 11115 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 11116 _ckvmssts_noperl(sts); /* fall through */ 11117 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 11118 set_errno(EVMSERR); 11119 } 11120 set_vaxc_errno(sts); 11121 if (ckWARN(WARN_EXEC)) { 11122 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s", 11123 Strerror(errno)); 11124 } 11125 } 11126 sts = substs; 11127 } 11128 else { 11129 char mode[3]; 11130 PerlIO * fp; 11131 if (flags & CLI$M_NOWAIT) 11132 strcpy(mode, "n"); 11133 else 11134 strcpy(mode, "nW"); 11135 11136 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts); 11137 if (fp != NULL) 11138 my_pclose(fp); 11139 /* sts will be the pid in the nowait case, so leave a 11140 * hint saying not to do any bit shifting to it. 11141 */ 11142 if (flags & CLI$M_NOWAIT) 11143 PL_statusvalue = -1; 11144 } 11145 return sts; 11146 } /* end of do_spawn2() */ 11147 /*}}}*/ 11148 11149 11150 static unsigned int *sockflags, sockflagsize; 11151 11152 /* 11153 * Shim fdopen to identify sockets for my_fwrite later, since the stdio 11154 * routines found in some versions of the CRTL can't deal with sockets. 11155 * We don't shim the other file open routines since a socket isn't 11156 * likely to be opened by a name. 11157 */ 11158 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/ 11159 FILE * 11160 my_fdopen(int fd, const char *mode) 11161 { 11162 FILE *fp = fdopen(fd, mode); 11163 11164 if (fp) { 11165 unsigned int fdoff = fd / sizeof(unsigned int); 11166 Stat_t sbuf; /* native stat; we don't need flex_stat */ 11167 if (!sockflagsize || fdoff > sockflagsize) { 11168 if (sockflags) Renew( sockflags,fdoff+2,unsigned int); 11169 else Newx (sockflags,fdoff+2,unsigned int); 11170 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize); 11171 sockflagsize = fdoff + 2; 11172 } 11173 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode)) 11174 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int)); 11175 } 11176 return fp; 11177 11178 } 11179 /*}}}*/ 11180 11181 11182 /* 11183 * Clear the corresponding bit when the (possibly) socket stream is closed. 11184 * There still a small hole: we miss an implicit close which might occur 11185 * via freopen(). >> Todo 11186 */ 11187 /*{{{ int my_fclose(FILE *fp)*/ 11188 int 11189 my_fclose(FILE *fp) { 11190 if (fp) { 11191 unsigned int fd = fileno(fp); 11192 unsigned int fdoff = fd / sizeof(unsigned int); 11193 11194 if (sockflagsize && fdoff < sockflagsize) 11195 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int)); 11196 } 11197 return fclose(fp); 11198 } 11199 /*}}}*/ 11200 11201 11202 /* 11203 * A simple fwrite replacement which outputs itmsz*nitm chars without 11204 * introducing record boundaries every itmsz chars. 11205 * We are using fputs, which depends on a terminating null. We may 11206 * well be writing binary data, so we need to accommodate not only 11207 * data with nulls sprinkled in the middle but also data with no null 11208 * byte at the end. 11209 */ 11210 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/ 11211 int 11212 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest) 11213 { 11214 char *cp, *end, *cpd; 11215 char *data; 11216 unsigned int fd = fileno(dest); 11217 unsigned int fdoff = fd / sizeof(unsigned int); 11218 int retval; 11219 int bufsize = itmsz * nitm + 1; 11220 11221 if (fdoff < sockflagsize && 11222 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) { 11223 if (write(fd, src, itmsz * nitm) == EOF) return EOF; 11224 return nitm; 11225 } 11226 11227 _ckvmssts_noperl(lib$get_vm(&bufsize, &data)); 11228 memcpy( data, src, itmsz*nitm ); 11229 data[itmsz*nitm] = '\0'; 11230 11231 end = data + itmsz * nitm; 11232 retval = (int) nitm; /* on success return # items written */ 11233 11234 cpd = data; 11235 while (cpd <= end) { 11236 for (cp = cpd; cp <= end; cp++) if (!*cp) break; 11237 if (fputs(cpd,dest) == EOF) { retval = EOF; break; } 11238 if (cp < end) 11239 if (fputc('\0',dest) == EOF) { retval = EOF; break; } 11240 cpd = cp + 1; 11241 } 11242 11243 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data)); 11244 return retval; 11245 11246 } /* end of my_fwrite() */ 11247 /*}}}*/ 11248 11249 /*{{{ int my_flush(FILE *fp)*/ 11250 int 11251 Perl_my_flush(pTHX_ FILE *fp) 11252 { 11253 int res; 11254 if ((res = fflush(fp)) == 0 && fp) { 11255 #ifdef VMS_DO_SOCKETS 11256 Stat_t s; 11257 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode)) 11258 #endif 11259 res = fsync(fileno(fp)); 11260 } 11261 /* 11262 * If the flush succeeded but set end-of-file, we need to clear 11263 * the error because our caller may check ferror(). BTW, this 11264 * probably means we just flushed an empty file. 11265 */ 11266 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp); 11267 11268 return res; 11269 } 11270 /*}}}*/ 11271 11272 /* fgetname() is not returning the correct file specifications when 11273 * decc_filename_unix_report mode is active. So we have to have it 11274 * aways return filenames in VMS mode and convert it ourselves. 11275 */ 11276 11277 /*{{{ char * my_fgetname(FILE *fp, buf)*/ 11278 char * 11279 Perl_my_fgetname(FILE *fp, char * buf) { 11280 char * retname; 11281 char * vms_name; 11282 11283 retname = fgetname(fp, buf, 1); 11284 11285 /* If we are in VMS mode, then we are done */ 11286 if (!decc_filename_unix_report || (retname == NULL)) { 11287 return retname; 11288 } 11289 11290 /* Convert this to Unix format */ 11291 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS); 11292 my_strlcpy(vms_name, retname, VMS_MAXRSS); 11293 retname = int_tounixspec(vms_name, buf, NULL); 11294 PerlMem_free(vms_name); 11295 11296 return retname; 11297 } 11298 /*}}}*/ 11299 11300 /* 11301 * Here are replacements for the following Unix routines in the VMS environment: 11302 * getpwuid Get information for a particular UIC or UID 11303 * getpwnam Get information for a named user 11304 * getpwent Get information for each user in the rights database 11305 * setpwent Reset search to the start of the rights database 11306 * endpwent Finish searching for users in the rights database 11307 * 11308 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure 11309 * (defined in pwd.h), which contains the following fields:- 11310 * struct passwd { 11311 * char *pw_name; Username (in lower case) 11312 * char *pw_passwd; Hashed password 11313 * unsigned int pw_uid; UIC 11314 * unsigned int pw_gid; UIC group number 11315 * char *pw_unixdir; Default device/directory (VMS-style) 11316 * char *pw_gecos; Owner name 11317 * char *pw_dir; Default device/directory (Unix-style) 11318 * char *pw_shell; Default CLI name (eg. DCL) 11319 * }; 11320 * If the specified user does not exist, getpwuid and getpwnam return NULL. 11321 * 11322 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid). 11323 * not the UIC member number (eg. what's returned by getuid()), 11324 * getpwuid() can accept either as input (if uid is specified, the caller's 11325 * UIC group is used), though it won't recognise gid=0. 11326 * 11327 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return 11328 * information about other users in your group or in other groups, respectively. 11329 * If the required privilege is not available, then these routines fill only 11330 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty 11331 * string). 11332 * 11333 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995. 11334 */ 11335 11336 /* sizes of various UAF record fields */ 11337 #define UAI$S_USERNAME 12 11338 #define UAI$S_IDENT 31 11339 #define UAI$S_OWNER 31 11340 #define UAI$S_DEFDEV 31 11341 #define UAI$S_DEFDIR 63 11342 #define UAI$S_DEFCLI 31 11343 #define UAI$S_PWD 8 11344 11345 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \ 11346 (uic).uic$v_member != UIC$K_WILD_MEMBER && \ 11347 (uic).uic$v_group != UIC$K_WILD_GROUP) 11348 11349 static char __empty[]= ""; 11350 static struct passwd __passwd_empty= 11351 {(char *) __empty, (char *) __empty, 0, 0, 11352 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty}; 11353 static int contxt= 0; 11354 static struct passwd __pwdcache; 11355 static char __pw_namecache[UAI$S_IDENT+1]; 11356 11357 /* 11358 * This routine does most of the work extracting the user information. 11359 */ 11360 static int 11361 fillpasswd (pTHX_ const char *name, struct passwd *pwd) 11362 { 11363 static struct { 11364 unsigned char length; 11365 char pw_gecos[UAI$S_OWNER+1]; 11366 } owner; 11367 static union uicdef uic; 11368 static struct { 11369 unsigned char length; 11370 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1]; 11371 } defdev; 11372 static struct { 11373 unsigned char length; 11374 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1]; 11375 } defdir; 11376 static struct { 11377 unsigned char length; 11378 char pw_shell[UAI$S_DEFCLI+1]; 11379 } defcli; 11380 static char pw_passwd[UAI$S_PWD+1]; 11381 11382 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd; 11383 struct dsc$descriptor_s name_desc; 11384 unsigned long int sts; 11385 11386 static struct itmlst_3 itmlst[]= { 11387 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner}, 11388 {sizeof(uic), UAI$_UIC, &uic, &luic}, 11389 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev}, 11390 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir}, 11391 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli}, 11392 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd}, 11393 {0, 0, NULL, NULL}}; 11394 11395 name_desc.dsc$w_length= strlen(name); 11396 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 11397 name_desc.dsc$b_class= DSC$K_CLASS_S; 11398 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */ 11399 11400 /* Note that sys$getuai returns many fields as counted strings. */ 11401 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0); 11402 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) { 11403 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES); 11404 } 11405 else { _ckvmssts(sts); } 11406 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */ 11407 11408 if ((int) owner.length < lowner) lowner= (int) owner.length; 11409 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length; 11410 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length; 11411 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length; 11412 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir); 11413 owner.pw_gecos[lowner]= '\0'; 11414 defdev.pw_dir[ldefdev+ldefdir]= '\0'; 11415 defcli.pw_shell[ldefcli]= '\0'; 11416 if (valid_uic(uic)) { 11417 pwd->pw_uid= uic.uic$l_uic; 11418 pwd->pw_gid= uic.uic$v_group; 11419 } 11420 else 11421 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\""); 11422 pwd->pw_passwd= pw_passwd; 11423 pwd->pw_gecos= owner.pw_gecos; 11424 pwd->pw_dir= defdev.pw_dir; 11425 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL); 11426 pwd->pw_shell= defcli.pw_shell; 11427 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) { 11428 int ldir; 11429 ldir= strlen(pwd->pw_unixdir) - 1; 11430 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0'; 11431 } 11432 else 11433 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir)); 11434 if (!decc_efs_case_preserve) 11435 __mystrtolower(pwd->pw_unixdir); 11436 return 1; 11437 } 11438 11439 /* 11440 * Get information for a named user. 11441 */ 11442 /*{{{struct passwd *getpwnam(char *name)*/ 11443 struct passwd * 11444 Perl_my_getpwnam(pTHX_ const char *name) 11445 { 11446 struct dsc$descriptor_s name_desc; 11447 union uicdef uic; 11448 unsigned long int sts; 11449 11450 __pwdcache = __passwd_empty; 11451 if (!fillpasswd(aTHX_ name, &__pwdcache)) { 11452 /* We still may be able to determine pw_uid and pw_gid */ 11453 name_desc.dsc$w_length= strlen(name); 11454 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 11455 name_desc.dsc$b_class= DSC$K_CLASS_S; 11456 name_desc.dsc$a_pointer= (char *) name; 11457 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) { 11458 __pwdcache.pw_uid= uic.uic$l_uic; 11459 __pwdcache.pw_gid= uic.uic$v_group; 11460 } 11461 else { 11462 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) { 11463 set_vaxc_errno(sts); 11464 set_errno(sts == RMS$_PRV ? EACCES : EINVAL); 11465 return NULL; 11466 } 11467 else { _ckvmssts(sts); } 11468 } 11469 } 11470 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache)); 11471 __pwdcache.pw_name= __pw_namecache; 11472 return &__pwdcache; 11473 } /* end of my_getpwnam() */ 11474 /*}}}*/ 11475 11476 /* 11477 * Get information for a particular UIC or UID. 11478 * Called by my_getpwent with uid=-1 to list all users. 11479 */ 11480 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/ 11481 struct passwd * 11482 Perl_my_getpwuid(pTHX_ Uid_t uid) 11483 { 11484 const $DESCRIPTOR(name_desc,__pw_namecache); 11485 unsigned short lname; 11486 union uicdef uic; 11487 unsigned long int status; 11488 11489 if (uid == (unsigned int) -1) { 11490 do { 11491 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt); 11492 if (status == SS$_NOSUCHID || status == RMS$_PRV) { 11493 set_vaxc_errno(status); 11494 set_errno(status == RMS$_PRV ? EACCES : EINVAL); 11495 my_endpwent(); 11496 return NULL; 11497 } 11498 else { _ckvmssts(status); } 11499 } while (!valid_uic (uic)); 11500 } 11501 else { 11502 uic.uic$l_uic= uid; 11503 if (!uic.uic$v_group) 11504 uic.uic$v_group= PerlProc_getgid(); 11505 if (valid_uic(uic)) 11506 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0); 11507 else status = SS$_IVIDENT; 11508 if (status == SS$_IVIDENT || status == SS$_NOSUCHID || 11509 status == RMS$_PRV) { 11510 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL); 11511 return NULL; 11512 } 11513 else { _ckvmssts(status); } 11514 } 11515 __pw_namecache[lname]= '\0'; 11516 __mystrtolower(__pw_namecache); 11517 11518 __pwdcache = __passwd_empty; 11519 __pwdcache.pw_name = __pw_namecache; 11520 11521 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege). 11522 The identifier's value is usually the UIC, but it doesn't have to be, 11523 so if we can, we let fillpasswd update this. */ 11524 __pwdcache.pw_uid = uic.uic$l_uic; 11525 __pwdcache.pw_gid = uic.uic$v_group; 11526 11527 fillpasswd(aTHX_ __pw_namecache, &__pwdcache); 11528 return &__pwdcache; 11529 11530 } /* end of my_getpwuid() */ 11531 /*}}}*/ 11532 11533 /* 11534 * Get information for next user. 11535 */ 11536 /*{{{struct passwd *my_getpwent()*/ 11537 struct passwd * 11538 Perl_my_getpwent(pTHX) 11539 { 11540 return (my_getpwuid((unsigned int) -1)); 11541 } 11542 /*}}}*/ 11543 11544 /* 11545 * Finish searching rights database for users. 11546 */ 11547 /*{{{void my_endpwent()*/ 11548 void 11549 Perl_my_endpwent(pTHX) 11550 { 11551 if (contxt) { 11552 _ckvmssts(sys$finish_rdb(&contxt)); 11553 contxt= 0; 11554 } 11555 } 11556 /*}}}*/ 11557 11558 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(), 11559 * my_utime(), and flex_stat(), all of which operate on UTC unless 11560 * VMSISH_TIMES is true. 11561 */ 11562 /* method used to handle UTC conversions: 11563 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction 11564 */ 11565 static int gmtime_emulation_type; 11566 /* number of secs to add to UTC POSIX-style time to get local time */ 11567 static long int utc_offset_secs; 11568 11569 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc. 11570 * in vmsish.h. #undef them here so we can call the CRTL routines 11571 * directly. 11572 */ 11573 #undef gmtime 11574 #undef localtime 11575 #undef time 11576 11577 11578 static time_t toutc_dst(time_t loc) { 11579 struct tm *rsltmp; 11580 11581 if ((rsltmp = localtime(&loc)) == NULL) return -1u; 11582 loc -= utc_offset_secs; 11583 if (rsltmp->tm_isdst) loc -= 3600; 11584 return loc; 11585 } 11586 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 11587 ((gmtime_emulation_type || my_time(NULL)), \ 11588 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \ 11589 ((secs) - utc_offset_secs)))) 11590 11591 static time_t toloc_dst(time_t utc) { 11592 struct tm *rsltmp; 11593 11594 utc += utc_offset_secs; 11595 if ((rsltmp = localtime(&utc)) == NULL) return -1u; 11596 if (rsltmp->tm_isdst) utc += 3600; 11597 return utc; 11598 } 11599 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 11600 ((gmtime_emulation_type || my_time(NULL)), \ 11601 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \ 11602 ((secs) + utc_offset_secs)))) 11603 11604 /* my_time(), my_localtime(), my_gmtime() 11605 * By default traffic in UTC time values, using CRTL gmtime() or 11606 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone. 11607 * Note: We need to use these functions even when the CRTL has working 11608 * UTC support, since they also handle C<use vmsish qw(times);> 11609 * 11610 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu> 11611 * Modified by Charles Bailey <bailey@newman.upenn.edu> 11612 */ 11613 11614 /*{{{time_t my_time(time_t *timep)*/ 11615 time_t 11616 Perl_my_time(pTHX_ time_t *timep) 11617 { 11618 time_t when; 11619 struct tm *tm_p; 11620 11621 if (gmtime_emulation_type == 0) { 11622 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */ 11623 /* results of calls to gmtime() and localtime() */ 11624 /* for same &base */ 11625 11626 gmtime_emulation_type++; 11627 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */ 11628 char off[LNM$C_NAMLENGTH+1];; 11629 11630 gmtime_emulation_type++; 11631 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) { 11632 gmtime_emulation_type++; 11633 utc_offset_secs = 0; 11634 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC"); 11635 } 11636 else { utc_offset_secs = atol(off); } 11637 } 11638 else { /* We've got a working gmtime() */ 11639 struct tm gmt, local; 11640 11641 gmt = *tm_p; 11642 tm_p = localtime(&base); 11643 local = *tm_p; 11644 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400; 11645 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600; 11646 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60; 11647 utc_offset_secs += (local.tm_sec - gmt.tm_sec); 11648 } 11649 } 11650 11651 when = time(NULL); 11652 # ifdef VMSISH_TIME 11653 if (VMSISH_TIME) when = _toloc(when); 11654 # endif 11655 if (timep != NULL) *timep = when; 11656 return when; 11657 11658 } /* end of my_time() */ 11659 /*}}}*/ 11660 11661 11662 /*{{{struct tm *my_gmtime(const time_t *timep)*/ 11663 struct tm * 11664 Perl_my_gmtime(pTHX_ const time_t *timep) 11665 { 11666 time_t when; 11667 struct tm *rsltmp; 11668 11669 if (timep == NULL) { 11670 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11671 return NULL; 11672 } 11673 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11674 11675 when = *timep; 11676 # ifdef VMSISH_TIME 11677 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */ 11678 # endif 11679 return gmtime(&when); 11680 } /* end of my_gmtime() */ 11681 /*}}}*/ 11682 11683 11684 /*{{{struct tm *my_localtime(const time_t *timep)*/ 11685 struct tm * 11686 Perl_my_localtime(pTHX_ const time_t *timep) 11687 { 11688 time_t when; 11689 11690 if (timep == NULL) { 11691 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11692 return NULL; 11693 } 11694 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11695 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */ 11696 11697 when = *timep; 11698 # ifdef VMSISH_TIME 11699 if (VMSISH_TIME) when = _toutc(when); 11700 # endif 11701 /* CRTL localtime() wants UTC as input, does tz correction itself */ 11702 return localtime(&when); 11703 } /* end of my_localtime() */ 11704 /*}}}*/ 11705 11706 /* Reset definitions for later calls */ 11707 #define gmtime(t) my_gmtime(t) 11708 #define localtime(t) my_localtime(t) 11709 #define time(t) my_time(t) 11710 11711 11712 /* my_utime - update modification/access time of a file 11713 * 11714 * Only the UTC translation is home-grown. The rest is handled by the 11715 * CRTL utime(), which will take into account the relevant feature 11716 * logicals and ODS-5 volume characteristics for true access times. 11717 * 11718 */ 11719 11720 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00) 11721 * to VMS epoch (01-JAN-1858 00:00:00.00) 11722 * in 100 ns intervals. 11723 */ 11724 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 }; 11725 11726 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/ 11727 int 11728 Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes) 11729 { 11730 struct utimbuf utc_utimes, *utc_utimesp; 11731 11732 if (utimes != NULL) { 11733 utc_utimes.actime = utimes->actime; 11734 utc_utimes.modtime = utimes->modtime; 11735 # ifdef VMSISH_TIME 11736 /* If input was local; convert to UTC for sys svc */ 11737 if (VMSISH_TIME) { 11738 utc_utimes.actime = _toutc(utimes->actime); 11739 utc_utimes.modtime = _toutc(utimes->modtime); 11740 } 11741 # endif 11742 utc_utimesp = &utc_utimes; 11743 } 11744 else { 11745 utc_utimesp = NULL; 11746 } 11747 11748 return utime(file, utc_utimesp); 11749 11750 } /* end of my_utime() */ 11751 /*}}}*/ 11752 11753 /* 11754 * flex_stat, flex_lstat, flex_fstat 11755 * basic stat, but gets it right when asked to stat 11756 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3) 11757 */ 11758 11759 #ifndef _USE_STD_STAT 11760 /* encode_dev packs a VMS device name string into an integer to allow 11761 * simple comparisons. This can be used, for example, to check whether two 11762 * files are located on the same device, by comparing their encoded device 11763 * names. Even a string comparison would not do, because stat() reuses the 11764 * device name buffer for each call; so without encode_dev, it would be 11765 * necessary to save the buffer and use strcmp (this would mean a number of 11766 * changes to the standard Perl code, to say nothing of what a Perl script 11767 * would have to do. 11768 * 11769 * The device lock id, if it exists, should be unique (unless perhaps compared 11770 * with lock ids transferred from other nodes). We have a lock id if the disk is 11771 * mounted cluster-wide, which is when we tend to get long (host-qualified) 11772 * device names. Thus we use the lock id in preference, and only if that isn't 11773 * available, do we try to pack the device name into an integer (flagged by 11774 * the sign bit (LOCKID_MASK) being set). 11775 * 11776 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device 11777 * name and its encoded form, but it seems very unlikely that we will find 11778 * two files on different disks that share the same encoded device names, 11779 * and even more remote that they will share the same file id (if the test 11780 * is to check for the same file). 11781 * 11782 * A better method might be to use sys$device_scan on the first call, and to 11783 * search for the device, returning an index into the cached array. 11784 * The number returned would be more intelligible. 11785 * This is probably not worth it, and anyway would take quite a bit longer 11786 * on the first call. 11787 */ 11788 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */ 11789 static mydev_t 11790 encode_dev (pTHX_ const char *dev) 11791 { 11792 int i; 11793 unsigned long int f; 11794 mydev_t enc; 11795 char c; 11796 const char *q; 11797 11798 if (!dev || !dev[0]) return 0; 11799 11800 #if LOCKID_MASK 11801 { 11802 struct dsc$descriptor_s dev_desc; 11803 unsigned long int status, lockid = 0, item = DVI$_LOCKID; 11804 11805 /* For cluster-mounted disks, the disk lock identifier is unique, so we 11806 can try that first. */ 11807 dev_desc.dsc$w_length = strlen (dev); 11808 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T; 11809 dev_desc.dsc$b_class = DSC$K_CLASS_S; 11810 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */ 11811 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0); 11812 if (!$VMS_STATUS_SUCCESS(status)) { 11813 switch (status) { 11814 case SS$_NOSUCHDEV: 11815 SETERRNO(ENODEV, status); 11816 return 0; 11817 default: 11818 _ckvmssts(status); 11819 } 11820 } 11821 if (lockid) return (lockid & ~LOCKID_MASK); 11822 } 11823 #endif 11824 11825 /* Otherwise we try to encode the device name */ 11826 enc = 0; 11827 f = 1; 11828 i = 0; 11829 for (q = dev + strlen(dev); q--; q >= dev) { 11830 if (*q == ':') 11831 break; 11832 if (isdigit (*q)) 11833 c= (*q) - '0'; 11834 else if (isalpha (toupper (*q))) 11835 c= toupper (*q) - 'A' + (char)10; 11836 else 11837 continue; /* Skip '$'s */ 11838 i++; 11839 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */ 11840 if (i>1) f *= 36; 11841 enc += f * (unsigned long int) c; 11842 } 11843 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */ 11844 11845 } /* end of encode_dev() */ 11846 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11847 device_no = encode_dev(aTHX_ devname) 11848 #else 11849 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11850 device_no = new_dev_no 11851 #endif 11852 11853 static int 11854 is_null_device(const char *name) 11855 { 11856 if (decc_bug_devnull != 0) { 11857 if (strncmp("/dev/null", name, 9) == 0) 11858 return 1; 11859 } 11860 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:". 11861 The underscore prefix, controller letter, and unit number are 11862 independently optional; for our purposes, the colon punctuation 11863 is not. The colon can be trailed by optional directory and/or 11864 filename, but two consecutive colons indicates a nodename rather 11865 than a device. [pr] */ 11866 if (*name == '_') ++name; 11867 if (tolower(*name++) != 'n') return 0; 11868 if (tolower(*name++) != 'l') return 0; 11869 if (tolower(*name) == 'a') ++name; 11870 if (*name == '0') ++name; 11871 return (*name++ == ':') && (*name != ':'); 11872 } 11873 11874 static int 11875 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag); 11876 11877 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c) 11878 11879 static I32 11880 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective, const char *fname, int opts) 11881 { 11882 char usrname[L_cuserid]; 11883 struct dsc$descriptor_s usrdsc = 11884 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname}; 11885 char *vmsname = NULL, *fileified = NULL; 11886 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags; 11887 unsigned short int retlen, trnlnm_iter_count; 11888 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11889 union prvdef curprv; 11890 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen}, 11891 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen}, 11892 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}}; 11893 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen}, 11894 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length}, 11895 {0,0,0,0}}; 11896 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen}, 11897 {0,0,0,0}}; 11898 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11899 Stat_t st; 11900 static int profile_context = -1; 11901 11902 if (!fname || !*fname) return FALSE; 11903 11904 /* Make sure we expand logical names, since sys$check_access doesn't */ 11905 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 11906 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11907 if (!strpbrk(fname,"/]>:")) { 11908 my_strlcpy(fileified, fname, VMS_MAXRSS); 11909 trnlnm_iter_count = 0; 11910 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) { 11911 trnlnm_iter_count++; 11912 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 11913 } 11914 fname = fileified; 11915 } 11916 11917 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS); 11918 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11919 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) { 11920 /* Don't know if already in VMS format, so make sure */ 11921 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) { 11922 PerlMem_free(fileified); 11923 PerlMem_free(vmsname); 11924 return FALSE; 11925 } 11926 } 11927 else { 11928 my_strlcpy(vmsname, fname, VMS_MAXRSS); 11929 } 11930 11931 /* sys$check_access needs a file spec, not a directory spec. 11932 * flex_stat now will handle a null thread context during startup. 11933 */ 11934 11935 retlen = namdsc.dsc$w_length = strlen(vmsname); 11936 if (vmsname[retlen-1] == ']' 11937 || vmsname[retlen-1] == '>' 11938 || vmsname[retlen-1] == ':' 11939 || (!flex_stat_int(vmsname, &st, 1) && 11940 S_ISDIR(st.st_mode))) { 11941 11942 if (!int_fileify_dirspec(vmsname, fileified, NULL)) { 11943 PerlMem_free(fileified); 11944 PerlMem_free(vmsname); 11945 return FALSE; 11946 } 11947 fname = fileified; 11948 } 11949 else { 11950 fname = vmsname; 11951 } 11952 11953 retlen = namdsc.dsc$w_length = strlen(fname); 11954 namdsc.dsc$a_pointer = (char *)fname; 11955 11956 switch (bit) { 11957 case S_IXUSR: case S_IXGRP: case S_IXOTH: 11958 access = ARM$M_EXECUTE; 11959 flags = CHP$M_READ; 11960 break; 11961 case S_IRUSR: case S_IRGRP: case S_IROTH: 11962 access = ARM$M_READ; 11963 flags = CHP$M_READ | CHP$M_USEREADALL; 11964 break; 11965 case S_IWUSR: case S_IWGRP: case S_IWOTH: 11966 access = ARM$M_WRITE; 11967 flags = CHP$M_READ | CHP$M_WRITE; 11968 break; 11969 case S_IDUSR: case S_IDGRP: case S_IDOTH: 11970 access = ARM$M_DELETE; 11971 flags = CHP$M_READ | CHP$M_WRITE; 11972 break; 11973 default: 11974 if (fileified != NULL) 11975 PerlMem_free(fileified); 11976 if (vmsname != NULL) 11977 PerlMem_free(vmsname); 11978 return FALSE; 11979 } 11980 11981 /* Before we call $check_access, create a user profile with the current 11982 * process privs since otherwise it just uses the default privs from the 11983 * UAF and might give false positives or negatives. This only works on 11984 * VMS versions v6.0 and later since that's when sys$create_user_profile 11985 * became available. 11986 */ 11987 11988 /* get current process privs and username */ 11989 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0)); 11990 _ckvmssts_noperl(iosb[0]); 11991 11992 /* find out the space required for the profile */ 11993 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0, 11994 &usrprodsc.dsc$w_length,&profile_context)); 11995 11996 /* allocate space for the profile and get it filled in */ 11997 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length); 11998 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11999 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer, 12000 &usrprodsc.dsc$w_length,&profile_context)); 12001 12002 /* use the profile to check access to the file; free profile & analyze results */ 12003 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc); 12004 PerlMem_free(usrprodsc.dsc$a_pointer); 12005 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */ 12006 12007 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT || 12008 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN || 12009 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) { 12010 set_vaxc_errno(retsts); 12011 if (retsts == SS$_NOPRIV) set_errno(EACCES); 12012 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL); 12013 else set_errno(ENOENT); 12014 if (fileified != NULL) 12015 PerlMem_free(fileified); 12016 if (vmsname != NULL) 12017 PerlMem_free(vmsname); 12018 return FALSE; 12019 } 12020 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) { 12021 if (fileified != NULL) 12022 PerlMem_free(fileified); 12023 if (vmsname != NULL) 12024 PerlMem_free(vmsname); 12025 return TRUE; 12026 } 12027 _ckvmssts_noperl(retsts); 12028 12029 if (fileified != NULL) 12030 PerlMem_free(fileified); 12031 if (vmsname != NULL) 12032 PerlMem_free(vmsname); 12033 return FALSE; /* Should never get here */ 12034 12035 } 12036 12037 /* Do the permissions allow some operation? Assumes PL_statcache already set. */ 12038 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a 12039 * subset of the applicable information. 12040 */ 12041 bool 12042 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp) 12043 { 12044 return cando_by_name_int 12045 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN); 12046 } /* end of cando() */ 12047 /*}}}*/ 12048 12049 12050 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/ 12051 I32 12052 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname) 12053 { 12054 return cando_by_name_int(bit, effective, fname, 0); 12055 12056 } /* end of cando_by_name() */ 12057 /*}}}*/ 12058 12059 12060 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/ 12061 int 12062 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp) 12063 { 12064 dSAVE_ERRNO; /* fstat may set this even on success */ 12065 if (!fstat(fd, &statbufp->crtl_stat)) { 12066 char *cptr; 12067 char *vms_filename; 12068 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS); 12069 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM); 12070 12071 /* Save name for cando by name in VMS format */ 12072 cptr = getname(fd, vms_filename, 1); 12073 12074 /* This should not happen, but just in case */ 12075 if (cptr == NULL) { 12076 statbufp->st_devnam[0] = 0; 12077 } 12078 else { 12079 /* Make sure that the saved name fits in 255 characters */ 12080 cptr = int_rmsexpand_vms 12081 (vms_filename, 12082 statbufp->st_devnam, 12083 0); 12084 if (cptr == NULL) 12085 statbufp->st_devnam[0] = 0; 12086 } 12087 PerlMem_free(vms_filename); 12088 12089 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 12090 VMS_DEVICE_ENCODE 12091 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 12092 12093 # ifdef VMSISH_TIME 12094 if (VMSISH_TIME) { 12095 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12096 statbufp->st_atime = _toloc(statbufp->st_atime); 12097 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12098 } 12099 # endif 12100 RESTORE_ERRNO; 12101 return 0; 12102 } 12103 return -1; 12104 12105 } /* end of flex_fstat() */ 12106 /*}}}*/ 12107 12108 static int 12109 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) 12110 { 12111 char *temp_fspec = NULL; 12112 char *fileified = NULL; 12113 const char *save_spec; 12114 char *ret_spec; 12115 int retval = -1; 12116 char efs_hack = 0; 12117 char already_fileified = 0; 12118 dSAVEDERRNO; 12119 12120 if (!fspec) { 12121 errno = EINVAL; 12122 return retval; 12123 } 12124 12125 if (decc_bug_devnull != 0) { 12126 if (is_null_device(fspec)) { /* Fake a stat() for the null device */ 12127 memset(statbufp,0,sizeof *statbufp); 12128 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0); 12129 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC; 12130 statbufp->st_uid = 0x00010001; 12131 statbufp->st_gid = 0x0001; 12132 time((time_t *)&statbufp->st_mtime); 12133 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime; 12134 return 0; 12135 } 12136 } 12137 12138 SAVE_ERRNO; 12139 12140 #if __CRTL_VER >= 80200000 12141 /* 12142 * If we are in POSIX filespec mode, accept the filename as is. 12143 */ 12144 if (decc_posix_compliant_pathnames == 0) { 12145 #endif 12146 12147 /* Try for a simple stat first. If fspec contains a filename without 12148 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir 12149 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here. 12150 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir, 12151 * not sea:[wine.dark]., if the latter exists. If the intended target is 12152 * the file with null type, specify this by calling flex_stat() with 12153 * a '.' at the end of fspec. 12154 */ 12155 12156 if (lstat_flag == 0) 12157 retval = stat(fspec, &statbufp->crtl_stat); 12158 else 12159 retval = lstat(fspec, &statbufp->crtl_stat); 12160 12161 if (!retval) { 12162 save_spec = fspec; 12163 } 12164 else { 12165 /* In the odd case where we have write but not read access 12166 * to a directory, stat('foo.DIR') works but stat('foo') doesn't. 12167 */ 12168 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 12169 if (fileified == NULL) 12170 _ckvmssts_noperl(SS$_INSFMEM); 12171 12172 ret_spec = int_fileify_dirspec(fspec, fileified, NULL); 12173 if (ret_spec != NULL) { 12174 if (lstat_flag == 0) 12175 retval = stat(fileified, &statbufp->crtl_stat); 12176 else 12177 retval = lstat(fileified, &statbufp->crtl_stat); 12178 save_spec = fileified; 12179 already_fileified = 1; 12180 } 12181 } 12182 12183 if (retval && vms_bug_stat_filename) { 12184 12185 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS); 12186 if (temp_fspec == NULL) 12187 _ckvmssts_noperl(SS$_INSFMEM); 12188 12189 /* We should try again as a vmsified file specification. */ 12190 12191 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL); 12192 if (ret_spec != NULL) { 12193 if (lstat_flag == 0) 12194 retval = stat(temp_fspec, &statbufp->crtl_stat); 12195 else 12196 retval = lstat(temp_fspec, &statbufp->crtl_stat); 12197 save_spec = temp_fspec; 12198 } 12199 } 12200 12201 if (retval) { 12202 /* Last chance - allow multiple dots without EFS CHARSET */ 12203 /* The CRTL stat() falls down hard on multi-dot filenames in unix 12204 * format unless * DECC$EFS_CHARSET is in effect, so temporarily 12205 * enable it if it isn't already. 12206 */ 12207 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 12208 decc$feature_set_value(decc_efs_charset_index, 1, 1); 12209 if (lstat_flag == 0) 12210 retval = stat(fspec, &statbufp->crtl_stat); 12211 else 12212 retval = lstat(fspec, &statbufp->crtl_stat); 12213 save_spec = fspec; 12214 if (!decc_efs_charset && (decc_efs_charset_index > 0)) { 12215 decc$feature_set_value(decc_efs_charset_index, 1, 0); 12216 efs_hack = 1; 12217 } 12218 } 12219 12220 #if __CRTL_VER >= 80200000 12221 } else { 12222 if (lstat_flag == 0) 12223 retval = stat(temp_fspec, &statbufp->crtl_stat); 12224 else 12225 retval = lstat(temp_fspec, &statbufp->crtl_stat); 12226 save_spec = temp_fspec; 12227 } 12228 #endif 12229 12230 /* As you were... */ 12231 if (!decc_efs_charset) 12232 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0); 12233 12234 if (!retval) { 12235 char *cptr; 12236 int rmsex_flags = PERL_RMSEXPAND_M_VMS; 12237 12238 /* If this is an lstat, do not follow the link */ 12239 if (lstat_flag) 12240 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK; 12241 12242 /* If we used the efs_hack above, we must also use it here for */ 12243 /* perl_cando to work */ 12244 if (efs_hack && (decc_efs_charset_index > 0)) { 12245 decc$feature_set_value(decc_efs_charset_index, 1, 1); 12246 } 12247 12248 /* If we've got a directory, save a fileified, expanded version of it 12249 * in st_devnam. If not a directory, just an expanded version. 12250 */ 12251 if (S_ISDIR(statbufp->st_mode) && !already_fileified) { 12252 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 12253 if (fileified == NULL) 12254 _ckvmssts_noperl(SS$_INSFMEM); 12255 12256 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL); 12257 if (cptr != NULL) 12258 save_spec = fileified; 12259 } 12260 12261 cptr = int_rmsexpand(save_spec, 12262 statbufp->st_devnam, 12263 NULL, 12264 rmsex_flags, 12265 0, 12266 0); 12267 12268 if (efs_hack && (decc_efs_charset_index > 0)) { 12269 decc$feature_set_value(decc_efs_charset, 1, 0); 12270 } 12271 12272 /* Fix me: If this is NULL then stat found a file, and we could */ 12273 /* not convert the specification to VMS - Should never happen */ 12274 if (cptr == NULL) 12275 statbufp->st_devnam[0] = 0; 12276 12277 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 12278 VMS_DEVICE_ENCODE 12279 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 12280 # ifdef VMSISH_TIME 12281 if (VMSISH_TIME) { 12282 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12283 statbufp->st_atime = _toloc(statbufp->st_atime); 12284 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12285 } 12286 # endif 12287 } 12288 /* If we were successful, leave errno where we found it */ 12289 if (retval == 0) RESTORE_ERRNO; 12290 if (temp_fspec) 12291 PerlMem_free(temp_fspec); 12292 if (fileified) 12293 PerlMem_free(fileified); 12294 return retval; 12295 12296 } /* end of flex_stat_int() */ 12297 12298 12299 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/ 12300 int 12301 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp) 12302 { 12303 return flex_stat_int(fspec, statbufp, 0); 12304 } 12305 /*}}}*/ 12306 12307 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/ 12308 int 12309 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp) 12310 { 12311 return flex_stat_int(fspec, statbufp, 1); 12312 } 12313 /*}}}*/ 12314 12315 12316 /* rmscopy - copy a file using VMS RMS routines 12317 * 12318 * Copies contents and attributes of spec_in to spec_out, except owner 12319 * and protection information. Name and type of spec_in are used as 12320 * defaults for spec_out. The third parameter specifies whether rmscopy() 12321 * should try to propagate timestamps from the input file to the output file. 12322 * If it is less than 0, no timestamps are preserved. If it is 0, then 12323 * rmscopy() will behave similarly to the DCL COPY command: timestamps are 12324 * propagated to the output file at creation iff the output file specification 12325 * did not contain an explicit name or type, and the revision date is always 12326 * updated at the end of the copy operation. If it is greater than 0, then 12327 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps 12328 * other than the revision date should be propagated, and bit 1 indicates 12329 * that the revision date should be propagated. 12330 * 12331 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure. 12332 * 12333 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>. 12334 * Incorporates, with permission, some code from EZCOPY by Tim Adye 12335 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code 12336 * as part of the Perl standard distribution under the terms of the 12337 * GNU General Public License or the Perl Artistic License. Copies 12338 * of each may be found in the Perl standard distribution. 12339 */ /* FIXME */ 12340 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/ 12341 int 12342 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates) 12343 { 12344 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out, 12345 *rsa, *rsal, *rsa_out, *rsal_out, *ubf; 12346 unsigned long int sts; 12347 int dna_len; 12348 struct FAB fab_in, fab_out; 12349 struct RAB rab_in, rab_out; 12350 rms_setup_nam(nam); 12351 rms_setup_nam(nam_out); 12352 struct XABDAT xabdat; 12353 struct XABFHC xabfhc; 12354 struct XABRDT xabrdt; 12355 struct XABSUM xabsum; 12356 12357 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS); 12358 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12359 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS); 12360 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12361 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) || 12362 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) { 12363 PerlMem_free(vmsin); 12364 PerlMem_free(vmsout); 12365 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12366 return 0; 12367 } 12368 12369 esa = (char *)PerlMem_malloc(VMS_MAXRSS); 12370 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12371 esal = NULL; 12372 #if defined(NAML$C_MAXRSS) 12373 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 12374 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12375 #endif 12376 fab_in = cc$rms_fab; 12377 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin)); 12378 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI; 12379 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET; 12380 fab_in.fab$l_fop = FAB$M_SQO; 12381 rms_bind_fab_nam(fab_in, nam); 12382 fab_in.fab$l_xab = (void *) &xabdat; 12383 12384 rsa = (char *)PerlMem_malloc(VMS_MAXRSS); 12385 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12386 rsal = NULL; 12387 #if defined(NAML$C_MAXRSS) 12388 rsal = (char *)PerlMem_malloc(VMS_MAXRSS); 12389 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12390 #endif 12391 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1)); 12392 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 12393 rms_nam_esl(nam) = 0; 12394 rms_nam_rsl(nam) = 0; 12395 rms_nam_esll(nam) = 0; 12396 rms_nam_rsll(nam) = 0; 12397 #ifdef NAM$M_NO_SHORT_UPCASE 12398 if (decc_efs_case_preserve) 12399 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE); 12400 #endif 12401 12402 xabdat = cc$rms_xabdat; /* To get creation date */ 12403 xabdat.xab$l_nxt = (void *) &xabfhc; 12404 12405 xabfhc = cc$rms_xabfhc; /* To get record length */ 12406 xabfhc.xab$l_nxt = (void *) &xabsum; 12407 12408 xabsum = cc$rms_xabsum; /* To get key and area information */ 12409 12410 if (!((sts = sys$open(&fab_in)) & 1)) { 12411 PerlMem_free(vmsin); 12412 PerlMem_free(vmsout); 12413 PerlMem_free(esa); 12414 if (esal != NULL) 12415 PerlMem_free(esal); 12416 PerlMem_free(rsa); 12417 if (rsal != NULL) 12418 PerlMem_free(rsal); 12419 set_vaxc_errno(sts); 12420 switch (sts) { 12421 case RMS$_FNF: case RMS$_DNF: 12422 set_errno(ENOENT); break; 12423 case RMS$_DIR: 12424 set_errno(ENOTDIR); break; 12425 case RMS$_DEV: 12426 set_errno(ENODEV); break; 12427 case RMS$_SYN: 12428 set_errno(EINVAL); break; 12429 case RMS$_PRV: 12430 set_errno(EACCES); break; 12431 default: 12432 set_errno(EVMSERR); 12433 } 12434 return 0; 12435 } 12436 12437 nam_out = nam; 12438 fab_out = fab_in; 12439 fab_out.fab$w_ifi = 0; 12440 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT; 12441 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI; 12442 fab_out.fab$l_fop = FAB$M_SQO; 12443 rms_bind_fab_nam(fab_out, nam_out); 12444 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout)); 12445 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0; 12446 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len); 12447 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 12448 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12449 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 12450 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12451 esal_out = NULL; 12452 rsal_out = NULL; 12453 #if defined(NAML$C_MAXRSS) 12454 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS); 12455 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12456 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS); 12457 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12458 #endif 12459 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1)); 12460 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1)); 12461 12462 if (preserve_dates == 0) { /* Act like DCL COPY */ 12463 rms_set_nam_nop(nam_out, NAM$M_SYNCHK); 12464 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */ 12465 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) { 12466 PerlMem_free(vmsin); 12467 PerlMem_free(vmsout); 12468 PerlMem_free(esa); 12469 if (esal != NULL) 12470 PerlMem_free(esal); 12471 PerlMem_free(rsa); 12472 if (rsal != NULL) 12473 PerlMem_free(rsal); 12474 PerlMem_free(esa_out); 12475 if (esal_out != NULL) 12476 PerlMem_free(esal_out); 12477 PerlMem_free(rsa_out); 12478 if (rsal_out != NULL) 12479 PerlMem_free(rsal_out); 12480 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR); 12481 set_vaxc_errno(sts); 12482 return 0; 12483 } 12484 fab_out.fab$l_xab = (void *) &xabdat; 12485 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) 12486 preserve_dates = 1; 12487 } 12488 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */ 12489 preserve_dates =0; /* bitmask from this point forward */ 12490 12491 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc; 12492 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) { 12493 PerlMem_free(vmsin); 12494 PerlMem_free(vmsout); 12495 PerlMem_free(esa); 12496 if (esal != NULL) 12497 PerlMem_free(esal); 12498 PerlMem_free(rsa); 12499 if (rsal != NULL) 12500 PerlMem_free(rsal); 12501 PerlMem_free(esa_out); 12502 if (esal_out != NULL) 12503 PerlMem_free(esal_out); 12504 PerlMem_free(rsa_out); 12505 if (rsal_out != NULL) 12506 PerlMem_free(rsal_out); 12507 set_vaxc_errno(sts); 12508 switch (sts) { 12509 case RMS$_DNF: 12510 set_errno(ENOENT); break; 12511 case RMS$_DIR: 12512 set_errno(ENOTDIR); break; 12513 case RMS$_DEV: 12514 set_errno(ENODEV); break; 12515 case RMS$_SYN: 12516 set_errno(EINVAL); break; 12517 case RMS$_PRV: 12518 set_errno(EACCES); break; 12519 default: 12520 set_errno(EVMSERR); 12521 } 12522 return 0; 12523 } 12524 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */ 12525 if (preserve_dates & 2) { 12526 /* sys$close() will process xabrdt, not xabdat */ 12527 xabrdt = cc$rms_xabrdt; 12528 xabrdt.xab$q_rdt = xabdat.xab$q_rdt; 12529 fab_out.fab$l_xab = (void *) &xabrdt; 12530 } 12531 12532 ubf = (char *)PerlMem_malloc(32256); 12533 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12534 rab_in = cc$rms_rab; 12535 rab_in.rab$l_fab = &fab_in; 12536 rab_in.rab$l_rop = RAB$M_BIO; 12537 rab_in.rab$l_ubf = ubf; 12538 rab_in.rab$w_usz = 32256; 12539 if (!((sts = sys$connect(&rab_in)) & 1)) { 12540 sys$close(&fab_in); sys$close(&fab_out); 12541 PerlMem_free(vmsin); 12542 PerlMem_free(vmsout); 12543 PerlMem_free(ubf); 12544 PerlMem_free(esa); 12545 if (esal != NULL) 12546 PerlMem_free(esal); 12547 PerlMem_free(rsa); 12548 if (rsal != NULL) 12549 PerlMem_free(rsal); 12550 PerlMem_free(esa_out); 12551 if (esal_out != NULL) 12552 PerlMem_free(esal_out); 12553 PerlMem_free(rsa_out); 12554 if (rsal_out != NULL) 12555 PerlMem_free(rsal_out); 12556 set_errno(EVMSERR); set_vaxc_errno(sts); 12557 return 0; 12558 } 12559 12560 rab_out = cc$rms_rab; 12561 rab_out.rab$l_fab = &fab_out; 12562 rab_out.rab$l_rbf = ubf; 12563 if (!((sts = sys$connect(&rab_out)) & 1)) { 12564 sys$close(&fab_in); sys$close(&fab_out); 12565 PerlMem_free(vmsin); 12566 PerlMem_free(vmsout); 12567 PerlMem_free(ubf); 12568 PerlMem_free(esa); 12569 if (esal != NULL) 12570 PerlMem_free(esal); 12571 PerlMem_free(rsa); 12572 if (rsal != NULL) 12573 PerlMem_free(rsal); 12574 PerlMem_free(esa_out); 12575 if (esal_out != NULL) 12576 PerlMem_free(esal_out); 12577 PerlMem_free(rsa_out); 12578 if (rsal_out != NULL) 12579 PerlMem_free(rsal_out); 12580 set_errno(EVMSERR); set_vaxc_errno(sts); 12581 return 0; 12582 } 12583 12584 while ((sts = sys$read(&rab_in))) { /* always true */ 12585 if (sts == RMS$_EOF) break; 12586 rab_out.rab$w_rsz = rab_in.rab$w_rsz; 12587 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) { 12588 sys$close(&fab_in); sys$close(&fab_out); 12589 PerlMem_free(vmsin); 12590 PerlMem_free(vmsout); 12591 PerlMem_free(ubf); 12592 PerlMem_free(esa); 12593 if (esal != NULL) 12594 PerlMem_free(esal); 12595 PerlMem_free(rsa); 12596 if (rsal != NULL) 12597 PerlMem_free(rsal); 12598 PerlMem_free(esa_out); 12599 if (esal_out != NULL) 12600 PerlMem_free(esal_out); 12601 PerlMem_free(rsa_out); 12602 if (rsal_out != NULL) 12603 PerlMem_free(rsal_out); 12604 set_errno(EVMSERR); set_vaxc_errno(sts); 12605 return 0; 12606 } 12607 } 12608 12609 12610 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */ 12611 sys$close(&fab_in); sys$close(&fab_out); 12612 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts; 12613 12614 PerlMem_free(vmsin); 12615 PerlMem_free(vmsout); 12616 PerlMem_free(ubf); 12617 PerlMem_free(esa); 12618 if (esal != NULL) 12619 PerlMem_free(esal); 12620 PerlMem_free(rsa); 12621 if (rsal != NULL) 12622 PerlMem_free(rsal); 12623 PerlMem_free(esa_out); 12624 if (esal_out != NULL) 12625 PerlMem_free(esal_out); 12626 PerlMem_free(rsa_out); 12627 if (rsal_out != NULL) 12628 PerlMem_free(rsal_out); 12629 12630 if (!(sts & 1)) { 12631 set_errno(EVMSERR); set_vaxc_errno(sts); 12632 return 0; 12633 } 12634 12635 return 1; 12636 12637 } /* end of rmscopy() */ 12638 /*}}}*/ 12639 12640 12641 /*** The following glue provides 'hooks' to make some of the routines 12642 * from this file available from Perl. These routines are sufficiently 12643 * basic, and are required sufficiently early in the build process, 12644 * that's it's nice to have them available to miniperl as well as the 12645 * full Perl, so they're set up here instead of in an extension. The 12646 * Perl code which handles importation of these names into a given 12647 * package lives in [.VMS]Filespec.pm in @INC. 12648 */ 12649 12650 void 12651 rmsexpand_fromperl(pTHX_ CV *cv) 12652 { 12653 dXSARGS; 12654 char *fspec, *defspec = NULL, *rslt; 12655 STRLEN n_a; 12656 int fs_utf8, dfs_utf8; 12657 12658 fs_utf8 = 0; 12659 dfs_utf8 = 0; 12660 if (!items || items > 2) 12661 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])"); 12662 fspec = SvPV(ST(0),n_a); 12663 fs_utf8 = SvUTF8(ST(0)); 12664 if (!fspec || !*fspec) XSRETURN_UNDEF; 12665 if (items == 2) { 12666 defspec = SvPV(ST(1),n_a); 12667 dfs_utf8 = SvUTF8(ST(1)); 12668 } 12669 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8); 12670 ST(0) = sv_newmortal(); 12671 if (rslt != NULL) { 12672 sv_usepvn(ST(0),rslt,strlen(rslt)); 12673 if (fs_utf8) { 12674 SvUTF8_on(ST(0)); 12675 } 12676 } 12677 XSRETURN(1); 12678 } 12679 12680 void 12681 vmsify_fromperl(pTHX_ CV *cv) 12682 { 12683 dXSARGS; 12684 char *vmsified; 12685 STRLEN n_a; 12686 int utf8_fl; 12687 12688 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)"); 12689 utf8_fl = SvUTF8(ST(0)); 12690 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12691 ST(0) = sv_newmortal(); 12692 if (vmsified != NULL) { 12693 sv_usepvn(ST(0),vmsified,strlen(vmsified)); 12694 if (utf8_fl) { 12695 SvUTF8_on(ST(0)); 12696 } 12697 } 12698 XSRETURN(1); 12699 } 12700 12701 void 12702 unixify_fromperl(pTHX_ CV *cv) 12703 { 12704 dXSARGS; 12705 char *unixified; 12706 STRLEN n_a; 12707 int utf8_fl; 12708 12709 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)"); 12710 utf8_fl = SvUTF8(ST(0)); 12711 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12712 ST(0) = sv_newmortal(); 12713 if (unixified != NULL) { 12714 sv_usepvn(ST(0),unixified,strlen(unixified)); 12715 if (utf8_fl) { 12716 SvUTF8_on(ST(0)); 12717 } 12718 } 12719 XSRETURN(1); 12720 } 12721 12722 void 12723 fileify_fromperl(pTHX_ CV *cv) 12724 { 12725 dXSARGS; 12726 char *fileified; 12727 STRLEN n_a; 12728 int utf8_fl; 12729 12730 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)"); 12731 utf8_fl = SvUTF8(ST(0)); 12732 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12733 ST(0) = sv_newmortal(); 12734 if (fileified != NULL) { 12735 sv_usepvn(ST(0),fileified,strlen(fileified)); 12736 if (utf8_fl) { 12737 SvUTF8_on(ST(0)); 12738 } 12739 } 12740 XSRETURN(1); 12741 } 12742 12743 void 12744 pathify_fromperl(pTHX_ CV *cv) 12745 { 12746 dXSARGS; 12747 char *pathified; 12748 STRLEN n_a; 12749 int utf8_fl; 12750 12751 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)"); 12752 utf8_fl = SvUTF8(ST(0)); 12753 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12754 ST(0) = sv_newmortal(); 12755 if (pathified != NULL) { 12756 sv_usepvn(ST(0),pathified,strlen(pathified)); 12757 if (utf8_fl) { 12758 SvUTF8_on(ST(0)); 12759 } 12760 } 12761 XSRETURN(1); 12762 } 12763 12764 void 12765 vmspath_fromperl(pTHX_ CV *cv) 12766 { 12767 dXSARGS; 12768 char *vmspath; 12769 STRLEN n_a; 12770 int utf8_fl; 12771 12772 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)"); 12773 utf8_fl = SvUTF8(ST(0)); 12774 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12775 ST(0) = sv_newmortal(); 12776 if (vmspath != NULL) { 12777 sv_usepvn(ST(0),vmspath,strlen(vmspath)); 12778 if (utf8_fl) { 12779 SvUTF8_on(ST(0)); 12780 } 12781 } 12782 XSRETURN(1); 12783 } 12784 12785 void 12786 unixpath_fromperl(pTHX_ CV *cv) 12787 { 12788 dXSARGS; 12789 char *unixpath; 12790 STRLEN n_a; 12791 int utf8_fl; 12792 12793 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)"); 12794 utf8_fl = SvUTF8(ST(0)); 12795 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12796 ST(0) = sv_newmortal(); 12797 if (unixpath != NULL) { 12798 sv_usepvn(ST(0),unixpath,strlen(unixpath)); 12799 if (utf8_fl) { 12800 SvUTF8_on(ST(0)); 12801 } 12802 } 12803 XSRETURN(1); 12804 } 12805 12806 void 12807 candelete_fromperl(pTHX_ CV *cv) 12808 { 12809 dXSARGS; 12810 char *fspec, *fsp; 12811 SV *mysv; 12812 IO *io; 12813 STRLEN n_a; 12814 12815 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)"); 12816 12817 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12818 Newx(fspec, VMS_MAXRSS, char); 12819 if (fspec == NULL) _ckvmssts(SS$_INSFMEM); 12820 if (isGV_with_GP(mysv)) { 12821 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) { 12822 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12823 ST(0) = &PL_sv_no; 12824 Safefree(fspec); 12825 XSRETURN(1); 12826 } 12827 fsp = fspec; 12828 } 12829 else { 12830 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) { 12831 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12832 ST(0) = &PL_sv_no; 12833 Safefree(fspec); 12834 XSRETURN(1); 12835 } 12836 } 12837 12838 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp)); 12839 Safefree(fspec); 12840 XSRETURN(1); 12841 } 12842 12843 void 12844 rmscopy_fromperl(pTHX_ CV *cv) 12845 { 12846 dXSARGS; 12847 char *inspec, *outspec, *inp, *outp; 12848 int date_flag; 12849 SV *mysv; 12850 IO *io; 12851 STRLEN n_a; 12852 12853 if (items < 2 || items > 3) 12854 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])"); 12855 12856 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12857 Newx(inspec, VMS_MAXRSS, char); 12858 if (isGV_with_GP(mysv)) { 12859 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) { 12860 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12861 ST(0) = sv_2mortal(newSViv(0)); 12862 Safefree(inspec); 12863 XSRETURN(1); 12864 } 12865 inp = inspec; 12866 } 12867 else { 12868 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) { 12869 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12870 ST(0) = sv_2mortal(newSViv(0)); 12871 Safefree(inspec); 12872 XSRETURN(1); 12873 } 12874 } 12875 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1); 12876 Newx(outspec, VMS_MAXRSS, char); 12877 if (isGV_with_GP(mysv)) { 12878 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) { 12879 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12880 ST(0) = sv_2mortal(newSViv(0)); 12881 Safefree(inspec); 12882 Safefree(outspec); 12883 XSRETURN(1); 12884 } 12885 outp = outspec; 12886 } 12887 else { 12888 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) { 12889 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12890 ST(0) = sv_2mortal(newSViv(0)); 12891 Safefree(inspec); 12892 Safefree(outspec); 12893 XSRETURN(1); 12894 } 12895 } 12896 date_flag = (items == 3) ? SvIV(ST(2)) : 0; 12897 12898 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag))); 12899 Safefree(inspec); 12900 Safefree(outspec); 12901 XSRETURN(1); 12902 } 12903 12904 /* The mod2fname is limited to shorter filenames by design, so it should 12905 * not be modified to support longer EFS pathnames 12906 */ 12907 void 12908 mod2fname(pTHX_ CV *cv) 12909 { 12910 dXSARGS; 12911 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1], 12912 workbuff[NAM$C_MAXRSS*1 + 1]; 12913 SSize_t counter, num_entries; 12914 /* ODS-5 ups this, but we want to be consistent, so... */ 12915 int max_name_len = 39; 12916 AV *in_array = (AV *)SvRV(ST(0)); 12917 12918 num_entries = av_tindex(in_array); 12919 12920 /* All the names start with PL_. */ 12921 strcpy(ultimate_name, "PL_"); 12922 12923 /* Clean up our working buffer */ 12924 Zero(work_name, sizeof(work_name), char); 12925 12926 /* Run through the entries and build up a working name */ 12927 for(counter = 0; counter <= num_entries; counter++) { 12928 /* If it's not the first name then tack on a __ */ 12929 if (counter) { 12930 my_strlcat(work_name, "__", sizeof(work_name)); 12931 } 12932 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name)); 12933 } 12934 12935 /* Check to see if we actually have to bother...*/ 12936 if (strlen(work_name) + 3 <= max_name_len) { 12937 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name)); 12938 } else { 12939 /* It's too darned big, so we need to go strip. We use the same */ 12940 /* algorithm as xsubpp does. First, strip out doubled __ */ 12941 char *source, *dest, last; 12942 dest = workbuff; 12943 last = 0; 12944 for (source = work_name; *source; source++) { 12945 if (last == *source && last == '_') { 12946 continue; 12947 } 12948 *dest++ = *source; 12949 last = *source; 12950 } 12951 /* Go put it back */ 12952 my_strlcpy(work_name, workbuff, sizeof(work_name)); 12953 /* Is it still too big? */ 12954 if (strlen(work_name) + 3 > max_name_len) { 12955 /* Strip duplicate letters */ 12956 last = 0; 12957 dest = workbuff; 12958 for (source = work_name; *source; source++) { 12959 if (last == toupper(*source)) { 12960 continue; 12961 } 12962 *dest++ = *source; 12963 last = toupper(*source); 12964 } 12965 my_strlcpy(work_name, workbuff, sizeof(work_name)); 12966 } 12967 12968 /* Is it *still* too big? */ 12969 if (strlen(work_name) + 3 > max_name_len) { 12970 /* Too bad, we truncate */ 12971 work_name[max_name_len - 2] = 0; 12972 } 12973 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name)); 12974 } 12975 12976 /* Okay, return it */ 12977 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0)); 12978 XSRETURN(1); 12979 } 12980 12981 void 12982 hushexit_fromperl(pTHX_ CV *cv) 12983 { 12984 dXSARGS; 12985 12986 if (items > 0) { 12987 VMSISH_HUSHED = SvTRUE(ST(0)); 12988 } 12989 ST(0) = boolSV(VMSISH_HUSHED); 12990 XSRETURN(1); 12991 } 12992 12993 12994 PerlIO * 12995 Perl_vms_start_glob(pTHX_ SV *tmpglob, IO *io) 12996 { 12997 PerlIO *fp; 12998 struct vs_str_st *rslt; 12999 char *vmsspec; 13000 char *rstr; 13001 char *begin, *cp; 13002 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;"); 13003 PerlIO *tmpfp; 13004 STRLEN i; 13005 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13006 struct dsc$descriptor_vs rsdsc; 13007 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0; 13008 unsigned long hasver = 0, isunix = 0; 13009 unsigned long int lff_flags = 0; 13010 int rms_sts; 13011 int vms_old_glob = 1; 13012 13013 if (!SvOK(tmpglob)) { 13014 SETERRNO(ENOENT,RMS$_FNF); 13015 return NULL; 13016 } 13017 13018 vms_old_glob = !decc_filename_unix_report; 13019 13020 #ifdef VMS_LONGNAME_SUPPORT 13021 lff_flags = LIB$M_FIL_LONG_NAMES; 13022 #endif 13023 /* The Newx macro will not allow me to assign a smaller array 13024 * to the rslt pointer, so we will assign it to the begin char pointer 13025 * and then copy the value into the rslt pointer. 13026 */ 13027 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char); 13028 rslt = (struct vs_str_st *)begin; 13029 rslt->length = 0; 13030 rstr = &rslt->str[0]; 13031 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */ 13032 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int); 13033 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT; 13034 rsdsc.dsc$b_class = DSC$K_CLASS_VS; 13035 13036 Newx(vmsspec, VMS_MAXRSS, char); 13037 13038 /* We could find out if there's an explicit dev/dir or version 13039 by peeking into lib$find_file's internal context at 13040 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb 13041 but that's unsupported, so I don't want to do it now and 13042 have it bite someone in the future. */ 13043 /* Fix-me: vms_split_path() is the only way to do this, the 13044 existing method will fail with many legal EFS or UNIX specifications 13045 */ 13046 13047 cp = SvPV(tmpglob,i); 13048 13049 for (; i; i--) { 13050 if (cp[i] == ';') hasver = 1; 13051 if (cp[i] == '.') { 13052 if (sts) hasver = 1; 13053 else sts = 1; 13054 } 13055 if (cp[i] == '/') { 13056 hasdir = isunix = 1; 13057 break; 13058 } 13059 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') { 13060 hasdir = 1; 13061 break; 13062 } 13063 } 13064 13065 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */ 13066 if ((hasdir == 0) && decc_filename_unix_report) { 13067 isunix = 1; 13068 } 13069 13070 if ((tmpfp = PerlIO_tmpfile()) != NULL) { 13071 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec; 13072 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len; 13073 int wildstar = 0; 13074 int wildquery = 0; 13075 int found = 0; 13076 Stat_t st; 13077 int stat_sts; 13078 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st); 13079 if (!stat_sts && S_ISDIR(st.st_mode)) { 13080 char * vms_dir; 13081 const char * fname; 13082 STRLEN fname_len; 13083 13084 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */ 13085 /* path delimiter of ':>]', if so, then the old behavior has */ 13086 /* obviously been specifically requested */ 13087 13088 fname = SvPVX_const(tmpglob); 13089 fname_len = strlen(fname); 13090 vms_dir = strpbrk(&fname[fname_len - 1], ":>]"); 13091 if (vms_old_glob || (vms_dir != NULL)) { 13092 wilddsc.dsc$a_pointer = tovmspath_utf8( 13093 SvPVX(tmpglob),vmsspec,NULL); 13094 ok = (wilddsc.dsc$a_pointer != NULL); 13095 /* maybe passed 'foo' rather than '[.foo]', thus not 13096 detected above */ 13097 hasdir = 1; 13098 } else { 13099 /* Operate just on the directory, the special stat/fstat for */ 13100 /* leaves the fileified specification in the st_devnam */ 13101 /* member. */ 13102 wilddsc.dsc$a_pointer = st.st_devnam; 13103 ok = 1; 13104 } 13105 } 13106 else { 13107 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL); 13108 ok = (wilddsc.dsc$a_pointer != NULL); 13109 } 13110 if (ok) 13111 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer); 13112 13113 /* If not extended character set, replace ? with % */ 13114 /* With extended character set, ? is a wildcard single character */ 13115 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) { 13116 if (*cp == '?') { 13117 wildquery = 1; 13118 if (!decc_efs_charset) 13119 *cp = '%'; 13120 } else if (*cp == '%') { 13121 wildquery = 1; 13122 } else if (*cp == '*') { 13123 wildstar = 1; 13124 } 13125 } 13126 13127 if (ok) { 13128 wv_sts = vms_split_path( 13129 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len, 13130 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len, 13131 &wvs_spec, &wvs_len); 13132 } else { 13133 wn_spec = NULL; 13134 wn_len = 0; 13135 we_spec = NULL; 13136 we_len = 0; 13137 } 13138 13139 sts = SS$_NORMAL; 13140 while (ok && $VMS_STATUS_SUCCESS(sts)) { 13141 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13142 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13143 int valid_find; 13144 13145 valid_find = 0; 13146 sts = lib$find_file(&wilddsc,&rsdsc,&cxt, 13147 &dfltdsc,NULL,&rms_sts,&lff_flags); 13148 if (!$VMS_STATUS_SUCCESS(sts)) 13149 break; 13150 13151 /* with varying string, 1st word of buffer contains result length */ 13152 rstr[rslt->length] = '\0'; 13153 13154 /* Find where all the components are */ 13155 v_sts = vms_split_path 13156 (rstr, 13157 &v_spec, 13158 &v_len, 13159 &r_spec, 13160 &r_len, 13161 &d_spec, 13162 &d_len, 13163 &n_spec, 13164 &n_len, 13165 &e_spec, 13166 &e_len, 13167 &vs_spec, 13168 &vs_len); 13169 13170 /* If no version on input, truncate the version on output */ 13171 if (!hasver && (vs_len > 0)) { 13172 *vs_spec = '\0'; 13173 vs_len = 0; 13174 } 13175 13176 if (isunix) { 13177 13178 /* In Unix report mode, remove the ".dir;1" from the name */ 13179 /* if it is a real directory */ 13180 if (decc_filename_unix_report && decc_efs_charset) { 13181 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 13182 Stat_t statbuf; 13183 int ret_sts; 13184 13185 ret_sts = flex_lstat(rstr, &statbuf); 13186 if ((ret_sts == 0) && 13187 S_ISDIR(statbuf.st_mode)) { 13188 e_len = 0; 13189 e_spec[0] = 0; 13190 } 13191 } 13192 } 13193 13194 /* No version & a null extension on UNIX handling */ 13195 if ((e_len == 1) && decc_readdir_dropdotnotype) { 13196 e_len = 0; 13197 *e_spec = '\0'; 13198 } 13199 } 13200 13201 if (!decc_efs_case_preserve) { 13202 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp); 13203 } 13204 13205 /* Find File treats a Null extension as return all extensions */ 13206 /* This is contrary to Perl expectations */ 13207 13208 if (wildstar || wildquery || vms_old_glob) { 13209 /* really need to see if the returned file name matched */ 13210 /* but for now will assume that it matches */ 13211 valid_find = 1; 13212 } else { 13213 /* Exact Match requested */ 13214 /* How are directories handled? - like a file */ 13215 if ((e_len == we_len) && (n_len == wn_len)) { 13216 int t1; 13217 t1 = e_len; 13218 if (t1 > 0) 13219 t1 = strncmp(e_spec, we_spec, e_len); 13220 if (t1 == 0) { 13221 t1 = n_len; 13222 if (t1 > 0) 13223 t1 = strncmp(n_spec, we_spec, n_len); 13224 if (t1 == 0) 13225 valid_find = 1; 13226 } 13227 } 13228 } 13229 13230 if (valid_find) { 13231 found++; 13232 13233 if (hasdir) { 13234 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1); 13235 begin = rstr; 13236 } 13237 else { 13238 /* Start with the name */ 13239 begin = n_spec; 13240 } 13241 strcat(begin,"\n"); 13242 ok = (PerlIO_puts(tmpfp,begin) != EOF); 13243 } 13244 } 13245 if (cxt) (void)lib$find_file_end(&cxt); 13246 13247 if (!found) { 13248 /* Be POSIXish: return the input pattern when no matches */ 13249 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS); 13250 strcat(rstr,"\n"); 13251 ok = (PerlIO_puts(tmpfp,rstr) != EOF); 13252 } 13253 13254 if (ok && sts != RMS$_NMF && 13255 sts != RMS$_DNF && sts != RMS_FNF) ok = 0; 13256 if (!ok) { 13257 if (!(sts & 1)) { 13258 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts); 13259 } 13260 PerlIO_close(tmpfp); 13261 fp = NULL; 13262 } 13263 else { 13264 PerlIO_rewind(tmpfp); 13265 IoTYPE(io) = IoTYPE_RDONLY; 13266 IoIFP(io) = fp = tmpfp; 13267 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */ 13268 } 13269 } 13270 Safefree(vmsspec); 13271 Safefree(rslt); 13272 return fp; 13273 } 13274 13275 13276 static char * 13277 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, 13278 int *utf8_fl); 13279 13280 void 13281 unixrealpath_fromperl(pTHX_ CV *cv) 13282 { 13283 dXSARGS; 13284 char *fspec, *rslt_spec, *rslt; 13285 STRLEN n_a; 13286 13287 if (!items || items != 1) 13288 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)"); 13289 13290 fspec = SvPV(ST(0),n_a); 13291 if (!fspec || !*fspec) XSRETURN_UNDEF; 13292 13293 Newx(rslt_spec, VMS_MAXRSS + 1, char); 13294 rslt = do_vms_realpath(fspec, rslt_spec, NULL); 13295 13296 ST(0) = sv_newmortal(); 13297 if (rslt != NULL) 13298 sv_usepvn(ST(0),rslt,strlen(rslt)); 13299 else 13300 Safefree(rslt_spec); 13301 XSRETURN(1); 13302 } 13303 13304 static char * 13305 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec, 13306 int *utf8_fl); 13307 13308 void 13309 vmsrealpath_fromperl(pTHX_ CV *cv) 13310 { 13311 dXSARGS; 13312 char *fspec, *rslt_spec, *rslt; 13313 STRLEN n_a; 13314 13315 if (!items || items != 1) 13316 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)"); 13317 13318 fspec = SvPV(ST(0),n_a); 13319 if (!fspec || !*fspec) XSRETURN_UNDEF; 13320 13321 Newx(rslt_spec, VMS_MAXRSS + 1, char); 13322 rslt = do_vms_realname(fspec, rslt_spec, NULL); 13323 13324 ST(0) = sv_newmortal(); 13325 if (rslt != NULL) 13326 sv_usepvn(ST(0),rslt,strlen(rslt)); 13327 else 13328 Safefree(rslt_spec); 13329 XSRETURN(1); 13330 } 13331 13332 #ifdef HAS_SYMLINK 13333 /* 13334 * A thin wrapper around decc$symlink to make sure we follow the 13335 * standard and do not create a symlink with a zero-length name, 13336 * and convert the target to Unix format, as the CRTL can't handle 13337 * targets in VMS format. 13338 */ 13339 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/ 13340 int 13341 Perl_my_symlink(pTHX_ const char *contents, const char *link_name) 13342 { 13343 int sts; 13344 char * utarget; 13345 13346 if (!link_name || !*link_name) { 13347 SETERRNO(ENOENT, SS$_NOSUCHFILE); 13348 return -1; 13349 } 13350 13351 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 13352 /* An untranslatable filename should be passed through. */ 13353 (void) int_tounixspec(contents, utarget, NULL); 13354 sts = symlink(utarget, link_name); 13355 PerlMem_free(utarget); 13356 return sts; 13357 } 13358 /*}}}*/ 13359 13360 #endif /* HAS_SYMLINK */ 13361 13362 int do_vms_case_tolerant(void); 13363 13364 void 13365 case_tolerant_process_fromperl(pTHX_ CV *cv) 13366 { 13367 dXSARGS; 13368 ST(0) = boolSV(do_vms_case_tolerant()); 13369 XSRETURN(1); 13370 } 13371 13372 #ifdef USE_ITHREADS 13373 13374 void 13375 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, 13376 struct interp_intern *dst) 13377 { 13378 PERL_ARGS_ASSERT_SYS_INTERN_DUP; 13379 13380 memcpy(dst,src,sizeof(struct interp_intern)); 13381 } 13382 13383 #endif 13384 13385 void 13386 Perl_sys_intern_clear(pTHX) 13387 { 13388 } 13389 13390 void 13391 Perl_sys_intern_init(pTHX) 13392 { 13393 unsigned int ix = RAND_MAX; 13394 double x; 13395 13396 VMSISH_HUSHED = 0; 13397 13398 MY_POSIX_EXIT = vms_posix_exit; 13399 13400 x = (float)ix; 13401 MY_INV_RAND_MAX = 1./x; 13402 } 13403 13404 void 13405 init_os_extras(void) 13406 { 13407 dTHX; 13408 char* file = __FILE__; 13409 if (decc_disable_to_vms_logname_translation) { 13410 no_translate_barewords = TRUE; 13411 } else { 13412 no_translate_barewords = FALSE; 13413 } 13414 13415 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$"); 13416 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$"); 13417 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$"); 13418 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$"); 13419 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$"); 13420 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$"); 13421 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$"); 13422 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$"); 13423 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$"); 13424 newXS("File::Copy::rmscopy",rmscopy_fromperl,file); 13425 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$"); 13426 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$"); 13427 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$"); 13428 newXSproto("VMS::Filespec::case_tolerant_process", 13429 case_tolerant_process_fromperl,file,""); 13430 13431 store_pipelocs(aTHX); /* will redo any earlier attempts */ 13432 13433 return; 13434 } 13435 13436 #if __CRTL_VER == 80200000 13437 /* This missed getting in to the DECC SDK for 8.2 */ 13438 char *realpath(const char *file_name, char * resolved_name, ...); 13439 #endif 13440 13441 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/ 13442 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK. 13443 * The perl fallback routine to provide realpath() is not as efficient 13444 * on OpenVMS. 13445 */ 13446 13447 #ifdef __cplusplus 13448 extern "C" { 13449 #endif 13450 13451 /* Hack, use old stat() as fastest way of getting ino_t and device */ 13452 int decc$stat(const char *name, void * statbuf); 13453 #if __CRTL_VER >= 80200000 13454 int decc$lstat(const char *name, void * statbuf); 13455 #else 13456 #define decc$lstat decc$stat 13457 #endif 13458 13459 #ifdef __cplusplus 13460 } 13461 #endif 13462 13463 13464 /* Realpath is fragile. In 8.3 it does not work if the feature 13465 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic 13466 * links are implemented in RMS, not the CRTL. It also can fail if the 13467 * user does not have read/execute access to some of the directories. 13468 * So in order for Do What I Mean mode to work, if realpath() fails, 13469 * fall back to looking up the filename by the device name and FID. 13470 */ 13471 13472 int vms_fid_to_name(char * outname, int outlen, 13473 const char * name, int lstat_flag, mode_t * mode) 13474 { 13475 #pragma message save 13476 #pragma message disable MISALGNDSTRCT 13477 #pragma message disable MISALGNDMEM 13478 #pragma member_alignment save 13479 #pragma nomember_alignment 13480 struct statbuf_t { 13481 char * st_dev; 13482 unsigned short st_ino[3]; 13483 unsigned short old_st_mode; 13484 unsigned long padl[30]; /* plenty of room */ 13485 } statbuf; 13486 #pragma message restore 13487 #pragma member_alignment restore 13488 13489 int sts; 13490 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13491 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13492 char *fileified; 13493 char *temp_fspec; 13494 char *ret_spec; 13495 13496 /* Need to follow the mostly the same rules as flex_stat_int, or we may get 13497 * unexpected answers 13498 */ 13499 13500 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 13501 if (fileified == NULL) 13502 _ckvmssts_noperl(SS$_INSFMEM); 13503 13504 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS); 13505 if (temp_fspec == NULL) 13506 _ckvmssts_noperl(SS$_INSFMEM); 13507 13508 sts = -1; 13509 /* First need to try as a directory */ 13510 ret_spec = int_tovmspath(name, temp_fspec, NULL); 13511 if (ret_spec != NULL) { 13512 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL); 13513 if (ret_spec != NULL) { 13514 if (lstat_flag == 0) 13515 sts = decc$stat(fileified, &statbuf); 13516 else 13517 sts = decc$lstat(fileified, &statbuf); 13518 } 13519 } 13520 13521 /* Then as a VMS file spec */ 13522 if (sts != 0) { 13523 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL); 13524 if (ret_spec != NULL) { 13525 if (lstat_flag == 0) { 13526 sts = decc$stat(temp_fspec, &statbuf); 13527 } else { 13528 sts = decc$lstat(temp_fspec, &statbuf); 13529 } 13530 } 13531 } 13532 13533 if (sts) { 13534 /* Next try - allow multiple dots with out EFS CHARSET */ 13535 /* The CRTL stat() falls down hard on multi-dot filenames in unix 13536 * format unless * DECC$EFS_CHARSET is in effect, so temporarily 13537 * enable it if it isn't already. 13538 */ 13539 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 13540 decc$feature_set_value(decc_efs_charset_index, 1, 1); 13541 ret_spec = int_tovmspath(name, temp_fspec, NULL); 13542 if (lstat_flag == 0) { 13543 sts = decc$stat(name, &statbuf); 13544 } else { 13545 sts = decc$lstat(name, &statbuf); 13546 } 13547 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 13548 decc$feature_set_value(decc_efs_charset_index, 1, 0); 13549 } 13550 13551 13552 /* and then because the Perl Unix to VMS conversion is not perfect */ 13553 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */ 13554 /* characters from filenames so we need to try it as-is */ 13555 if (sts) { 13556 if (lstat_flag == 0) { 13557 sts = decc$stat(name, &statbuf); 13558 } else { 13559 sts = decc$lstat(name, &statbuf); 13560 } 13561 } 13562 13563 if (sts == 0) { 13564 int vms_sts; 13565 13566 dvidsc.dsc$a_pointer=statbuf.st_dev; 13567 dvidsc.dsc$w_length=strlen(statbuf.st_dev); 13568 13569 specdsc.dsc$a_pointer = outname; 13570 specdsc.dsc$w_length = outlen-1; 13571 13572 vms_sts = lib$fid_to_name 13573 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length); 13574 if ($VMS_STATUS_SUCCESS(vms_sts)) { 13575 outname[specdsc.dsc$w_length] = 0; 13576 13577 /* Return the mode */ 13578 if (mode) { 13579 *mode = statbuf.old_st_mode; 13580 } 13581 } 13582 } 13583 PerlMem_free(temp_fspec); 13584 PerlMem_free(fileified); 13585 return sts; 13586 } 13587 13588 13589 13590 static char * 13591 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, 13592 int *utf8_fl) 13593 { 13594 char * rslt = NULL; 13595 13596 #ifdef HAS_SYMLINK 13597 if (decc_posix_compliant_pathnames > 0 ) { 13598 /* realpath currently only works if posix compliant pathnames are 13599 * enabled. It may start working when they are not, but in that 13600 * case we still want the fallback behavior for backwards compatibility 13601 */ 13602 rslt = realpath(filespec, outbuf); 13603 } 13604 #endif 13605 13606 if (rslt == NULL) { 13607 char * vms_spec; 13608 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13609 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13610 mode_t my_mode; 13611 13612 /* Fall back to fid_to_name */ 13613 13614 Newx(vms_spec, VMS_MAXRSS + 1, char); 13615 13616 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode); 13617 if (sts == 0) { 13618 13619 13620 /* Now need to trim the version off */ 13621 sts = vms_split_path 13622 (vms_spec, 13623 &v_spec, 13624 &v_len, 13625 &r_spec, 13626 &r_len, 13627 &d_spec, 13628 &d_len, 13629 &n_spec, 13630 &n_len, 13631 &e_spec, 13632 &e_len, 13633 &vs_spec, 13634 &vs_len); 13635 13636 13637 if (sts == 0) { 13638 int haslower = 0; 13639 const char *cp; 13640 13641 /* Trim off the version */ 13642 int file_len = v_len + r_len + d_len + n_len + e_len; 13643 vms_spec[file_len] = 0; 13644 13645 /* Trim off the .DIR if this is a directory */ 13646 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 13647 if (S_ISDIR(my_mode)) { 13648 e_len = 0; 13649 e_spec[0] = 0; 13650 } 13651 } 13652 13653 /* Drop NULL extensions on UNIX file specification */ 13654 if ((e_len == 1) && decc_readdir_dropdotnotype) { 13655 e_len = 0; 13656 e_spec[0] = '\0'; 13657 } 13658 13659 /* The result is expected to be in UNIX format */ 13660 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl); 13661 13662 /* Downcase if input had any lower case letters and 13663 * case preservation is not in effect. 13664 */ 13665 if (!decc_efs_case_preserve) { 13666 for (cp = filespec; *cp; cp++) 13667 if (islower(*cp)) { haslower = 1; break; } 13668 13669 if (haslower) __mystrtolower(rslt); 13670 } 13671 } 13672 } else { 13673 13674 /* Now for some hacks to deal with backwards and forward */ 13675 /* compatibility */ 13676 if (!decc_efs_charset) { 13677 13678 /* 1. ODS-2 mode wants to do a syntax only translation */ 13679 rslt = int_rmsexpand(filespec, outbuf, 13680 NULL, 0, NULL, utf8_fl); 13681 13682 } else { 13683 if (decc_filename_unix_report) { 13684 char * dir_name; 13685 char * vms_dir_name; 13686 char * file_name; 13687 13688 /* 2. ODS-5 / UNIX report mode should return a failure */ 13689 /* if the parent directory also does not exist */ 13690 /* Otherwise, get the real path for the parent */ 13691 /* and add the child to it. */ 13692 13693 /* basename / dirname only available for VMS 7.0+ */ 13694 /* So we may need to implement them as common routines */ 13695 13696 Newx(dir_name, VMS_MAXRSS + 1, char); 13697 Newx(vms_dir_name, VMS_MAXRSS + 1, char); 13698 dir_name[0] = '\0'; 13699 file_name = NULL; 13700 13701 /* First try a VMS parse */ 13702 sts = vms_split_path 13703 (filespec, 13704 &v_spec, 13705 &v_len, 13706 &r_spec, 13707 &r_len, 13708 &d_spec, 13709 &d_len, 13710 &n_spec, 13711 &n_len, 13712 &e_spec, 13713 &e_len, 13714 &vs_spec, 13715 &vs_len); 13716 13717 if (sts == 0) { 13718 /* This is VMS */ 13719 13720 int dir_len = v_len + r_len + d_len + n_len; 13721 if (dir_len > 0) { 13722 memcpy(dir_name, filespec, dir_len); 13723 dir_name[dir_len] = '\0'; 13724 file_name = (char *)&filespec[dir_len + 1]; 13725 } 13726 } else { 13727 /* This must be UNIX */ 13728 char * tchar; 13729 13730 tchar = strrchr(filespec, '/'); 13731 13732 if (tchar != NULL) { 13733 int dir_len = tchar - filespec; 13734 memcpy(dir_name, filespec, dir_len); 13735 dir_name[dir_len] = '\0'; 13736 file_name = (char *) &filespec[dir_len + 1]; 13737 } 13738 } 13739 13740 /* Dir name is defaulted */ 13741 if (dir_name[0] == 0) { 13742 dir_name[0] = '.'; 13743 dir_name[1] = '\0'; 13744 } 13745 13746 /* Need realpath for the directory */ 13747 sts = vms_fid_to_name(vms_dir_name, 13748 VMS_MAXRSS + 1, 13749 dir_name, 0, NULL); 13750 13751 if (sts == 0) { 13752 /* Now need to pathify it. */ 13753 char *tdir = int_pathify_dirspec(vms_dir_name, 13754 outbuf); 13755 13756 /* And now add the original filespec to it */ 13757 if (file_name != NULL) { 13758 my_strlcat(outbuf, file_name, VMS_MAXRSS); 13759 } 13760 return outbuf; 13761 } 13762 Safefree(vms_dir_name); 13763 Safefree(dir_name); 13764 } 13765 } 13766 } 13767 Safefree(vms_spec); 13768 } 13769 return rslt; 13770 } 13771 13772 static char * 13773 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf, 13774 int *utf8_fl) 13775 { 13776 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13777 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13778 13779 /* Fall back to fid_to_name */ 13780 13781 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL); 13782 if (sts != 0) { 13783 return NULL; 13784 } 13785 else { 13786 13787 13788 /* Now need to trim the version off */ 13789 sts = vms_split_path 13790 (outbuf, 13791 &v_spec, 13792 &v_len, 13793 &r_spec, 13794 &r_len, 13795 &d_spec, 13796 &d_len, 13797 &n_spec, 13798 &n_len, 13799 &e_spec, 13800 &e_len, 13801 &vs_spec, 13802 &vs_len); 13803 13804 13805 if (sts == 0) { 13806 int haslower = 0; 13807 const char *cp; 13808 13809 /* Trim off the version */ 13810 int file_len = v_len + r_len + d_len + n_len + e_len; 13811 outbuf[file_len] = 0; 13812 13813 /* Downcase if input had any lower case letters and 13814 * case preservation is not in effect. 13815 */ 13816 if (!decc_efs_case_preserve) { 13817 for (cp = filespec; *cp; cp++) 13818 if (islower(*cp)) { haslower = 1; break; } 13819 13820 if (haslower) __mystrtolower(outbuf); 13821 } 13822 } 13823 } 13824 return outbuf; 13825 } 13826 13827 13828 /*}}}*/ 13829 /* External entry points */ 13830 char * 13831 Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13832 { 13833 return do_vms_realpath(filespec, outbuf, utf8_fl); 13834 } 13835 13836 char * 13837 Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13838 { 13839 return do_vms_realname(filespec, outbuf, utf8_fl); 13840 } 13841 13842 /* case_tolerant */ 13843 13844 /*{{{int do_vms_case_tolerant(void)*/ 13845 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is 13846 * controlled by a process setting. 13847 */ 13848 int 13849 do_vms_case_tolerant(void) 13850 { 13851 return vms_process_case_tolerant; 13852 } 13853 /*}}}*/ 13854 /* External entry points */ 13855 int 13856 Perl_vms_case_tolerant(void) 13857 { 13858 return do_vms_case_tolerant(); 13859 } 13860 13861 /* Start of DECC RTL Feature handling */ 13862 13863 static int 13864 set_feature_default(const char *name, int value) 13865 { 13866 int status; 13867 int index; 13868 char val_str[10]; 13869 13870 /* If the feature has been explicitly disabled in the environment, 13871 * then don't enable it here. 13872 */ 13873 if (value > 0) { 13874 status = simple_trnlnm(name, val_str, sizeof(val_str)); 13875 if (status) { 13876 val_str[0] = _toupper(val_str[0]); 13877 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F') 13878 return 0; 13879 } 13880 } 13881 13882 index = decc$feature_get_index(name); 13883 13884 status = decc$feature_set_value(index, 1, value); 13885 if (index == -1 || (status == -1)) { 13886 return -1; 13887 } 13888 13889 status = decc$feature_get_value(index, 1); 13890 if (status != value) { 13891 return -1; 13892 } 13893 13894 /* Various things may check for an environment setting 13895 * rather than the feature directly, so set that too. 13896 */ 13897 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE"); 13898 13899 return 0; 13900 } 13901 13902 13903 /* C RTL Feature settings */ 13904 13905 #if defined(__DECC) || defined(__DECCXX) 13906 13907 #ifdef __cplusplus 13908 extern "C" { 13909 #endif 13910 13911 extern void 13912 vmsperl_set_features(void) 13913 { 13914 int status; 13915 int s; 13916 char val_str[10]; 13917 #if defined(JPI$_CASE_LOOKUP_PERM) 13918 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM; 13919 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE; 13920 unsigned long case_perm; 13921 unsigned long case_image; 13922 #endif 13923 13924 /* Allow an exception to bring Perl into the VMS debugger */ 13925 vms_debug_on_exception = 0; 13926 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str)); 13927 if (status) { 13928 val_str[0] = _toupper(val_str[0]); 13929 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13930 vms_debug_on_exception = 1; 13931 else 13932 vms_debug_on_exception = 0; 13933 } 13934 13935 /* Debug unix/vms file translation routines */ 13936 vms_debug_fileify = 0; 13937 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str)); 13938 if (status) { 13939 val_str[0] = _toupper(val_str[0]); 13940 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13941 vms_debug_fileify = 1; 13942 else 13943 vms_debug_fileify = 0; 13944 } 13945 13946 13947 /* Historically PERL has been doing vmsify / stat differently than */ 13948 /* the CRTL. In particular, under some conditions the CRTL will */ 13949 /* remove some illegal characters like spaces from filenames */ 13950 /* resulting in some differences. The stat()/lstat() wrapper has */ 13951 /* been reporting such file names as invalid and fails to stat them */ 13952 /* fixing this bug so that stat()/lstat() accept these like the */ 13953 /* CRTL does will result in several tests failing. */ 13954 /* This should really be fixed, but for now, set up a feature to */ 13955 /* enable it so that the impact can be studied. */ 13956 vms_bug_stat_filename = 0; 13957 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str)); 13958 if (status) { 13959 val_str[0] = _toupper(val_str[0]); 13960 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13961 vms_bug_stat_filename = 1; 13962 else 13963 vms_bug_stat_filename = 0; 13964 } 13965 13966 13967 /* Create VTF-7 filenames from Unicode instead of UTF-8 */ 13968 vms_vtf7_filenames = 0; 13969 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str)); 13970 if (status) { 13971 val_str[0] = _toupper(val_str[0]); 13972 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13973 vms_vtf7_filenames = 1; 13974 else 13975 vms_vtf7_filenames = 0; 13976 } 13977 13978 /* unlink all versions on unlink() or rename() */ 13979 vms_unlink_all_versions = 0; 13980 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str)); 13981 if (status) { 13982 val_str[0] = _toupper(val_str[0]); 13983 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13984 vms_unlink_all_versions = 1; 13985 else 13986 vms_unlink_all_versions = 0; 13987 } 13988 13989 /* Detect running under GNV Bash or other UNIX like shell */ 13990 gnv_unix_shell = 0; 13991 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str)); 13992 if (status) { 13993 gnv_unix_shell = 1; 13994 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1); 13995 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1); 13996 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1); 13997 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0); 13998 vms_unlink_all_versions = 1; 13999 vms_posix_exit = 1; 14000 /* Reverse default ordering of PERL_ENV_TABLES. */ 14001 defenv[0] = &crtlenvdsc; 14002 defenv[1] = &fildevdsc; 14003 } 14004 /* Some reasonable defaults that are not CRTL defaults */ 14005 set_feature_default("DECC$EFS_CASE_PRESERVE", 1); 14006 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */ 14007 set_feature_default("DECC$EFS_CHARSET", 1); 14008 14009 /* hacks to see if known bugs are still present for testing */ 14010 14011 /* PCP mode requires creating /dev/null special device file */ 14012 decc_bug_devnull = 0; 14013 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str)); 14014 if (status) { 14015 val_str[0] = _toupper(val_str[0]); 14016 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14017 decc_bug_devnull = 1; 14018 else 14019 decc_bug_devnull = 0; 14020 } 14021 14022 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION"); 14023 if (s >= 0) { 14024 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1); 14025 if (decc_disable_to_vms_logname_translation < 0) 14026 decc_disable_to_vms_logname_translation = 0; 14027 } 14028 14029 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE"); 14030 if (s >= 0) { 14031 decc_efs_case_preserve = decc$feature_get_value(s, 1); 14032 if (decc_efs_case_preserve < 0) 14033 decc_efs_case_preserve = 0; 14034 } 14035 14036 s = decc$feature_get_index("DECC$EFS_CHARSET"); 14037 decc_efs_charset_index = s; 14038 if (s >= 0) { 14039 decc_efs_charset = decc$feature_get_value(s, 1); 14040 if (decc_efs_charset < 0) 14041 decc_efs_charset = 0; 14042 } 14043 14044 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT"); 14045 if (s >= 0) { 14046 decc_filename_unix_report = decc$feature_get_value(s, 1); 14047 if (decc_filename_unix_report > 0) { 14048 decc_filename_unix_report = 1; 14049 vms_posix_exit = 1; 14050 } 14051 else 14052 decc_filename_unix_report = 0; 14053 } 14054 14055 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY"); 14056 if (s >= 0) { 14057 decc_filename_unix_only = decc$feature_get_value(s, 1); 14058 if (decc_filename_unix_only > 0) { 14059 decc_filename_unix_only = 1; 14060 } 14061 else { 14062 decc_filename_unix_only = 0; 14063 } 14064 } 14065 14066 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION"); 14067 if (s >= 0) { 14068 decc_filename_unix_no_version = decc$feature_get_value(s, 1); 14069 if (decc_filename_unix_no_version < 0) 14070 decc_filename_unix_no_version = 0; 14071 } 14072 14073 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE"); 14074 if (s >= 0) { 14075 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1); 14076 if (decc_readdir_dropdotnotype < 0) 14077 decc_readdir_dropdotnotype = 0; 14078 } 14079 14080 #if __CRTL_VER >= 80200000 14081 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES"); 14082 if (s >= 0) { 14083 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1); 14084 if (decc_posix_compliant_pathnames < 0) 14085 decc_posix_compliant_pathnames = 0; 14086 if (decc_posix_compliant_pathnames > 4) 14087 decc_posix_compliant_pathnames = 0; 14088 } 14089 14090 #endif 14091 14092 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) 14093 14094 /* Report true case tolerance */ 14095 /*----------------------------*/ 14096 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0); 14097 if (!$VMS_STATUS_SUCCESS(status)) 14098 case_perm = PPROP$K_CASE_BLIND; 14099 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0); 14100 if (!$VMS_STATUS_SUCCESS(status)) 14101 case_image = PPROP$K_CASE_BLIND; 14102 if ((case_perm == PPROP$K_CASE_SENSITIVE) || 14103 (case_image == PPROP$K_CASE_SENSITIVE)) 14104 vms_process_case_tolerant = 0; 14105 14106 #endif 14107 14108 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */ 14109 /* for strict backward compatibility */ 14110 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str)); 14111 if (status) { 14112 val_str[0] = _toupper(val_str[0]); 14113 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14114 vms_posix_exit = 1; 14115 else 14116 vms_posix_exit = 0; 14117 } 14118 } 14119 14120 /* Use 32-bit pointers because that's what the image activator 14121 * assumes for the LIB$INITIALZE psect. 14122 */ 14123 #if __INITIAL_POINTER_SIZE 14124 #pragma pointer_size save 14125 #pragma pointer_size 32 14126 #endif 14127 14128 /* Create a reference to the LIB$INITIALIZE function. */ 14129 extern void LIB$INITIALIZE(void); 14130 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE; 14131 14132 /* Create an array of pointers to the init functions in the special 14133 * LIB$INITIALIZE section. In our case, the array only has one entry. 14134 */ 14135 #pragma extern_model save 14136 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long 14137 extern void (* const vmsperl_unused_global_2[])() = 14138 { 14139 vmsperl_set_features, 14140 }; 14141 #pragma extern_model restore 14142 14143 #if __INITIAL_POINTER_SIZE 14144 #pragma pointer_size restore 14145 #endif 14146 14147 #ifdef __cplusplus 14148 } 14149 #endif 14150 14151 #endif /* defined(__DECC) || defined(__DECCXX) */ 14152 /* End of vms.c */ 14153