1 /* vms.c 2 * 3 * VMS-specific routines for perl5 4 * 5 * Copyright (C) 1993-2013 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 #if __CRTL_VER < 70300000 27 /* needed for home-rolled utime() */ 28 #include <atrdef.h> 29 #include <fibdef.h> 30 #endif 31 #include <chpdef.h> 32 #include <clidef.h> 33 #include <climsgdef.h> 34 #include <dcdef.h> 35 #include <descrip.h> 36 #include <devdef.h> 37 #include <dvidef.h> 38 #include <float.h> 39 #include <fscndef.h> 40 #include <iodef.h> 41 #include <jpidef.h> 42 #include <kgbdef.h> 43 #include <libclidef.h> 44 #include <libdef.h> 45 #include <lib$routines.h> 46 #include <lnmdef.h> 47 #include <ossdef.h> 48 #if __CRTL_VER >= 70301000 && !defined(__VAX) 49 #include <ppropdef.h> 50 #endif 51 #include <prvdef.h> 52 #include <psldef.h> 53 #include <rms.h> 54 #include <shrdef.h> 55 #include <ssdef.h> 56 #include <starlet.h> 57 #include <strdef.h> 58 #include <str$routines.h> 59 #include <syidef.h> 60 #include <uaidef.h> 61 #include <uicdef.h> 62 #include <stsdef.h> 63 #include <efndef.h> 64 #define NO_EFN EFN$C_ENF 65 66 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000 67 int decc$feature_get_index(const char *name); 68 char* decc$feature_get_name(int index); 69 int decc$feature_get_value(int index, int mode); 70 int decc$feature_set_value(int index, int mode, int value); 71 #else 72 #include <unixlib.h> 73 #endif 74 75 #pragma member_alignment save 76 #pragma nomember_alignment longword 77 struct item_list_3 { 78 unsigned short len; 79 unsigned short code; 80 void * bufadr; 81 unsigned short * retadr; 82 }; 83 #pragma member_alignment restore 84 85 /* Older versions of ssdef.h don't have these */ 86 #ifndef SS$_INVFILFOROP 87 # define SS$_INVFILFOROP 3930 88 #endif 89 #ifndef SS$_NOSUCHOBJECT 90 # define SS$_NOSUCHOBJECT 2696 91 #endif 92 93 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */ 94 #define PERLIO_NOT_STDIO 0 95 96 /* Don't replace system definitions of vfork, getenv, lstat, and stat, 97 * code below needs to get to the underlying CRTL routines. */ 98 #define DONT_MASK_RTL_CALLS 99 #include "EXTERN.h" 100 #include "perl.h" 101 #include "XSUB.h" 102 /* Anticipating future expansion in lexical warnings . . . */ 103 #ifndef WARN_INTERNAL 104 # define WARN_INTERNAL WARN_MISC 105 #endif 106 107 #ifdef VMS_LONGNAME_SUPPORT 108 #include <libfildef.h> 109 #endif 110 111 #if !defined(__VAX) && __CRTL_VER >= 80200000 112 #ifdef lstat 113 #undef lstat 114 #endif 115 #else 116 #ifdef lstat 117 #undef lstat 118 #endif 119 #define lstat(_x, _y) stat(_x, _y) 120 #endif 121 122 /* Routine to create a decterm for use with the Perl debugger */ 123 /* No headers, this information was found in the Programming Concepts Manual */ 124 125 static int (*decw_term_port) 126 (const struct dsc$descriptor_s * display, 127 const struct dsc$descriptor_s * setup_file, 128 const struct dsc$descriptor_s * customization, 129 struct dsc$descriptor_s * result_device_name, 130 unsigned short * result_device_name_length, 131 void * controller, 132 void * char_buffer, 133 void * char_change_buffer) = 0; 134 135 /* gcc's header files don't #define direct access macros 136 * corresponding to VAXC's variant structs */ 137 #ifdef __GNUC__ 138 # define uic$v_format uic$r_uic_form.uic$v_format 139 # define uic$v_group uic$r_uic_form.uic$v_group 140 # define uic$v_member uic$r_uic_form.uic$v_member 141 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass 142 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv 143 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall 144 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv 145 #endif 146 147 #if defined(NEED_AN_H_ERRNO) 148 dEXT int h_errno; 149 #endif 150 151 #if defined(__DECC) || defined(__DECCXX) 152 #pragma member_alignment save 153 #pragma nomember_alignment longword 154 #pragma message save 155 #pragma message disable misalgndmem 156 #endif 157 struct itmlst_3 { 158 unsigned short int buflen; 159 unsigned short int itmcode; 160 void *bufadr; 161 unsigned short int *retlen; 162 }; 163 164 struct filescan_itmlst_2 { 165 unsigned short length; 166 unsigned short itmcode; 167 char * component; 168 }; 169 170 struct vs_str_st { 171 unsigned short length; 172 char str[VMS_MAXRSS]; 173 unsigned short pad; /* for longword struct alignment */ 174 }; 175 176 #if defined(__DECC) || defined(__DECCXX) 177 #pragma message restore 178 #pragma member_alignment restore 179 #endif 180 181 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d) 182 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d) 183 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d) 184 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d) 185 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g) 186 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c) 187 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c) 188 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d) 189 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d) 190 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a) 191 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d) 192 #define getredirection(a,b) mp_getredirection(aTHX_ a,b) 193 194 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *); 195 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *); 196 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 197 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *); 198 199 static char * int_rmsexpand_vms( 200 const char * filespec, char * outbuf, unsigned opts); 201 static char * int_rmsexpand_tovms( 202 const char * filespec, char * outbuf, unsigned opts); 203 static char *int_tovmsspec 204 (const char *path, char *buf, int dir_flag, int * utf8_flag); 205 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl); 206 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl); 207 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl); 208 209 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */ 210 #define PERL_LNM_MAX_ALLOWED_INDEX 127 211 212 /* OpenVMS User's Guide says at least 9 iterative translations will be performed, 213 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for 214 * the Perl facility. 215 */ 216 #define PERL_LNM_MAX_ITER 10 217 218 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */ 219 #if __CRTL_VER >= 70302000 && !defined(__VAX) 220 #define MAX_DCL_SYMBOL (8192) 221 #define MAX_DCL_LINE_LENGTH (4096 - 4) 222 #else 223 #define MAX_DCL_SYMBOL (1024) 224 #define MAX_DCL_LINE_LENGTH (1024 - 4) 225 #endif 226 227 static char *__mystrtolower(char *str) 228 { 229 if (str) for (; *str; ++str) *str= tolower(*str); 230 return str; 231 } 232 233 static struct dsc$descriptor_s fildevdsc = 234 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" }; 235 static struct dsc$descriptor_s crtlenvdsc = 236 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" }; 237 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL }; 238 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL }; 239 static struct dsc$descriptor_s **env_tables = defenv; 240 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */ 241 242 /* True if we shouldn't treat barewords as logicals during directory */ 243 /* munching */ 244 static int no_translate_barewords; 245 246 /* DECC Features that may need to affect how Perl interprets 247 * displays filename information 248 */ 249 static int decc_disable_to_vms_logname_translation = 1; 250 static int decc_disable_posix_root = 1; 251 int decc_efs_case_preserve = 0; 252 static int decc_efs_charset = 0; 253 static int decc_efs_charset_index = -1; 254 static int decc_filename_unix_no_version = 0; 255 static int decc_filename_unix_only = 0; 256 int decc_filename_unix_report = 0; 257 int decc_posix_compliant_pathnames = 0; 258 int decc_readdir_dropdotnotype = 0; 259 static int vms_process_case_tolerant = 1; 260 int vms_vtf7_filenames = 0; 261 int gnv_unix_shell = 0; 262 static int vms_unlink_all_versions = 0; 263 static int vms_posix_exit = 0; 264 265 /* bug workarounds if needed */ 266 int decc_bug_devnull = 1; 267 int vms_bug_stat_filename = 0; 268 269 static int vms_debug_on_exception = 0; 270 static int vms_debug_fileify = 0; 271 272 /* Simple logical name translation */ 273 static int simple_trnlnm 274 (const char * logname, 275 char * value, 276 int value_len) 277 { 278 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV"); 279 const unsigned long attr = LNM$M_CASE_BLIND; 280 struct dsc$descriptor_s name_dsc; 281 int status; 282 unsigned short result; 283 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result}, 284 {0, 0, 0, 0}}; 285 286 name_dsc.dsc$w_length = strlen(logname); 287 name_dsc.dsc$a_pointer = (char *)logname; 288 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 289 name_dsc.dsc$b_class = DSC$K_CLASS_S; 290 291 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst); 292 293 if ($VMS_STATUS_SUCCESS(status)) { 294 295 /* Null terminate and return the string */ 296 /*--------------------------------------*/ 297 value[result] = 0; 298 return result; 299 } 300 301 return 0; 302 } 303 304 305 /* Is this a UNIX file specification? 306 * No longer a simple check with EFS file specs 307 * For now, not a full check, but need to 308 * handle POSIX ^UP^ specifications 309 * Fixing to handle ^/ cases would require 310 * changes to many other conversion routines. 311 */ 312 313 static int is_unix_filespec(const char *path) 314 { 315 int ret_val; 316 const char * pch1; 317 318 ret_val = 0; 319 if (strncmp(path,"\"^UP^",5) != 0) { 320 pch1 = strchr(path, '/'); 321 if (pch1 != NULL) 322 ret_val = 1; 323 else { 324 325 /* If the user wants UNIX files, "." needs to be treated as in UNIX */ 326 if (decc_filename_unix_report || decc_filename_unix_only) { 327 if (strcmp(path,".") == 0) 328 ret_val = 1; 329 } 330 } 331 } 332 return ret_val; 333 } 334 335 /* This routine converts a UCS-2 character to be VTF-7 encoded. 336 */ 337 338 static void ucs2_to_vtf7 339 (char *outspec, 340 unsigned long ucs2_char, 341 int * output_cnt) 342 { 343 unsigned char * ucs_ptr; 344 int hex; 345 346 ucs_ptr = (unsigned char *)&ucs2_char; 347 348 outspec[0] = '^'; 349 outspec[1] = 'U'; 350 hex = (ucs_ptr[1] >> 4) & 0xf; 351 if (hex < 0xA) 352 outspec[2] = hex + '0'; 353 else 354 outspec[2] = (hex - 9) + 'A'; 355 hex = ucs_ptr[1] & 0xF; 356 if (hex < 0xA) 357 outspec[3] = hex + '0'; 358 else { 359 outspec[3] = (hex - 9) + 'A'; 360 } 361 hex = (ucs_ptr[0] >> 4) & 0xf; 362 if (hex < 0xA) 363 outspec[4] = hex + '0'; 364 else 365 outspec[4] = (hex - 9) + 'A'; 366 hex = ucs_ptr[1] & 0xF; 367 if (hex < 0xA) 368 outspec[5] = hex + '0'; 369 else { 370 outspec[5] = (hex - 9) + 'A'; 371 } 372 *output_cnt = 6; 373 } 374 375 376 /* This handles the conversion of a UNIX extended character set to a ^ 377 * escaped VMS character. 378 * in a UNIX file specification. 379 * 380 * The output count variable contains the number of characters added 381 * to the output string. 382 * 383 * The return value is the number of characters read from the input string 384 */ 385 static int copy_expand_unix_filename_escape 386 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl) 387 { 388 int count; 389 int utf8_flag; 390 391 utf8_flag = 0; 392 if (utf8_fl) 393 utf8_flag = *utf8_fl; 394 395 count = 0; 396 *output_cnt = 0; 397 if (*inspec >= 0x80) { 398 if (utf8_fl && vms_vtf7_filenames) { 399 unsigned long ucs_char; 400 401 ucs_char = 0; 402 403 if ((*inspec & 0xE0) == 0xC0) { 404 /* 2 byte Unicode */ 405 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f); 406 if (ucs_char >= 0x80) { 407 ucs2_to_vtf7(outspec, ucs_char, output_cnt); 408 return 2; 409 } 410 } else if ((*inspec & 0xF0) == 0xE0) { 411 /* 3 byte Unicode */ 412 ucs_char = ((inspec[0] & 0xF) << 12) + 413 ((inspec[1] & 0x3f) << 6) + 414 (inspec[2] & 0x3f); 415 if (ucs_char >= 0x800) { 416 ucs2_to_vtf7(outspec, ucs_char, output_cnt); 417 return 3; 418 } 419 420 #if 0 /* I do not see longer sequences supported by OpenVMS */ 421 /* Maybe some one can fix this later */ 422 } else if ((*inspec & 0xF8) == 0xF0) { 423 /* 4 byte Unicode */ 424 /* UCS-4 to UCS-2 */ 425 } else if ((*inspec & 0xFC) == 0xF8) { 426 /* 5 byte Unicode */ 427 /* UCS-4 to UCS-2 */ 428 } else if ((*inspec & 0xFE) == 0xFC) { 429 /* 6 byte Unicode */ 430 /* UCS-4 to UCS-2 */ 431 #endif 432 } 433 } 434 435 /* High bit set, but not a Unicode character! */ 436 437 /* Non printing DECMCS or ISO Latin-1 character? */ 438 if ((unsigned char)*inspec <= 0x9F) { 439 int hex; 440 outspec[0] = '^'; 441 outspec++; 442 hex = (*inspec >> 4) & 0xF; 443 if (hex < 0xA) 444 outspec[1] = hex + '0'; 445 else { 446 outspec[1] = (hex - 9) + 'A'; 447 } 448 hex = *inspec & 0xF; 449 if (hex < 0xA) 450 outspec[2] = hex + '0'; 451 else { 452 outspec[2] = (hex - 9) + 'A'; 453 } 454 *output_cnt = 3; 455 return 1; 456 } else if ((unsigned char)*inspec == 0xA0) { 457 outspec[0] = '^'; 458 outspec[1] = 'A'; 459 outspec[2] = '0'; 460 *output_cnt = 3; 461 return 1; 462 } else if ((unsigned char)*inspec == 0xFF) { 463 outspec[0] = '^'; 464 outspec[1] = 'F'; 465 outspec[2] = 'F'; 466 *output_cnt = 3; 467 return 1; 468 } 469 *outspec = *inspec; 470 *output_cnt = 1; 471 return 1; 472 } 473 474 /* Is this a macro that needs to be passed through? 475 * Macros start with $( and an alpha character, followed 476 * by a string of alpha numeric characters ending with a ) 477 * If this does not match, then encode it as ODS-5. 478 */ 479 if ((inspec[0] == '$') && (inspec[1] == '(')) { 480 int tcnt; 481 482 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) { 483 tcnt = 3; 484 outspec[0] = inspec[0]; 485 outspec[1] = inspec[1]; 486 outspec[2] = inspec[2]; 487 488 while(isalnum(inspec[tcnt]) || 489 (inspec[2] == '.') || (inspec[2] == '_')) { 490 outspec[tcnt] = inspec[tcnt]; 491 tcnt++; 492 } 493 if (inspec[tcnt] == ')') { 494 outspec[tcnt] = inspec[tcnt]; 495 tcnt++; 496 *output_cnt = tcnt; 497 return tcnt; 498 } 499 } 500 } 501 502 switch (*inspec) { 503 case 0x7f: 504 outspec[0] = '^'; 505 outspec[1] = '7'; 506 outspec[2] = 'F'; 507 *output_cnt = 3; 508 return 1; 509 break; 510 case '?': 511 if (decc_efs_charset == 0) 512 outspec[0] = '%'; 513 else 514 outspec[0] = '?'; 515 *output_cnt = 1; 516 return 1; 517 break; 518 case '.': 519 case '~': 520 case '!': 521 case '#': 522 case '&': 523 case '\'': 524 case '`': 525 case '(': 526 case ')': 527 case '+': 528 case '@': 529 case '{': 530 case '}': 531 case ',': 532 case ';': 533 case '[': 534 case ']': 535 case '%': 536 case '^': 537 case '\\': 538 /* Don't escape again if following character is 539 * already something we escape. 540 */ 541 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) { 542 *outspec = *inspec; 543 *output_cnt = 1; 544 return 1; 545 break; 546 } 547 /* But otherwise fall through and escape it. */ 548 case '=': 549 /* Assume that this is to be escaped */ 550 outspec[0] = '^'; 551 outspec[1] = *inspec; 552 *output_cnt = 2; 553 return 1; 554 break; 555 case ' ': /* space */ 556 /* Assume that this is to be escaped */ 557 outspec[0] = '^'; 558 outspec[1] = '_'; 559 *output_cnt = 2; 560 return 1; 561 break; 562 default: 563 *outspec = *inspec; 564 *output_cnt = 1; 565 return 1; 566 break; 567 } 568 return 0; 569 } 570 571 572 /* This handles the expansion of a '^' prefix to the proper character 573 * in a UNIX file specification. 574 * 575 * The output count variable contains the number of characters added 576 * to the output string. 577 * 578 * The return value is the number of characters read from the input 579 * string 580 */ 581 static int copy_expand_vms_filename_escape 582 (char *outspec, const char *inspec, int *output_cnt) 583 { 584 int count; 585 int scnt; 586 587 count = 0; 588 *output_cnt = 0; 589 if (*inspec == '^') { 590 inspec++; 591 switch (*inspec) { 592 /* Spaces and non-trailing dots should just be passed through, 593 * but eat the escape character. 594 */ 595 case '.': 596 *outspec = *inspec; 597 count += 2; 598 (*output_cnt)++; 599 break; 600 case '_': /* space */ 601 *outspec = ' '; 602 count += 2; 603 (*output_cnt)++; 604 break; 605 case '^': 606 /* Hmm. Better leave the escape escaped. */ 607 outspec[0] = '^'; 608 outspec[1] = '^'; 609 count += 2; 610 (*output_cnt) += 2; 611 break; 612 case 'U': /* Unicode - FIX-ME this is wrong. */ 613 inspec++; 614 count++; 615 scnt = strspn(inspec, "0123456789ABCDEFabcdef"); 616 if (scnt == 4) { 617 unsigned int c1, c2; 618 scnt = sscanf(inspec, "%2x%2x", &c1, &c2); 619 outspec[0] = c1 & 0xff; 620 outspec[1] = c2 & 0xff; 621 if (scnt > 1) { 622 (*output_cnt) += 2; 623 count += 4; 624 } 625 } 626 else { 627 /* Error - do best we can to continue */ 628 *outspec = 'U'; 629 outspec++; 630 (*output_cnt++); 631 *outspec = *inspec; 632 count++; 633 (*output_cnt++); 634 } 635 break; 636 default: 637 scnt = strspn(inspec, "0123456789ABCDEFabcdef"); 638 if (scnt == 2) { 639 /* Hex encoded */ 640 unsigned int c1; 641 scnt = sscanf(inspec, "%2x", &c1); 642 outspec[0] = c1 & 0xff; 643 if (scnt > 0) { 644 (*output_cnt++); 645 count += 2; 646 } 647 } 648 else { 649 *outspec = *inspec; 650 count++; 651 (*output_cnt++); 652 } 653 } 654 } 655 else { 656 *outspec = *inspec; 657 count++; 658 (*output_cnt)++; 659 } 660 return count; 661 } 662 663 /* vms_split_path - Verify that the input file specification is a 664 * VMS format file specification, and provide pointers to the components of 665 * it. With EFS format filenames, this is virtually the only way to 666 * parse a VMS path specification into components. 667 * 668 * If the sum of the components do not add up to the length of the 669 * string, then the passed file specification is probably a UNIX style 670 * path. 671 */ 672 static int vms_split_path 673 (const char * path, 674 char * * volume, 675 int * vol_len, 676 char * * root, 677 int * root_len, 678 char * * dir, 679 int * dir_len, 680 char * * name, 681 int * name_len, 682 char * * ext, 683 int * ext_len, 684 char * * version, 685 int * ver_len) 686 { 687 struct dsc$descriptor path_desc; 688 int status; 689 unsigned long flags; 690 int ret_stat; 691 struct filescan_itmlst_2 item_list[9]; 692 const int filespec = 0; 693 const int nodespec = 1; 694 const int devspec = 2; 695 const int rootspec = 3; 696 const int dirspec = 4; 697 const int namespec = 5; 698 const int typespec = 6; 699 const int verspec = 7; 700 701 /* Assume the worst for an easy exit */ 702 ret_stat = -1; 703 *volume = NULL; 704 *vol_len = 0; 705 *root = NULL; 706 *root_len = 0; 707 *dir = NULL; 708 *name = NULL; 709 *name_len = 0; 710 *ext = NULL; 711 *ext_len = 0; 712 *version = NULL; 713 *ver_len = 0; 714 715 path_desc.dsc$a_pointer = (char *)path; /* cast ok */ 716 path_desc.dsc$w_length = strlen(path); 717 path_desc.dsc$b_dtype = DSC$K_DTYPE_T; 718 path_desc.dsc$b_class = DSC$K_CLASS_S; 719 720 /* Get the total length, if it is shorter than the string passed 721 * then this was probably not a VMS formatted file specification 722 */ 723 item_list[filespec].itmcode = FSCN$_FILESPEC; 724 item_list[filespec].length = 0; 725 item_list[filespec].component = NULL; 726 727 /* If the node is present, then it gets considered as part of the 728 * volume name to hopefully make things simple. 729 */ 730 item_list[nodespec].itmcode = FSCN$_NODE; 731 item_list[nodespec].length = 0; 732 item_list[nodespec].component = NULL; 733 734 item_list[devspec].itmcode = FSCN$_DEVICE; 735 item_list[devspec].length = 0; 736 item_list[devspec].component = NULL; 737 738 /* root is a special case, adding it to either the directory or 739 * the device components will probably complicate things for the 740 * callers of this routine, so leave it separate. 741 */ 742 item_list[rootspec].itmcode = FSCN$_ROOT; 743 item_list[rootspec].length = 0; 744 item_list[rootspec].component = NULL; 745 746 item_list[dirspec].itmcode = FSCN$_DIRECTORY; 747 item_list[dirspec].length = 0; 748 item_list[dirspec].component = NULL; 749 750 item_list[namespec].itmcode = FSCN$_NAME; 751 item_list[namespec].length = 0; 752 item_list[namespec].component = NULL; 753 754 item_list[typespec].itmcode = FSCN$_TYPE; 755 item_list[typespec].length = 0; 756 item_list[typespec].component = NULL; 757 758 item_list[verspec].itmcode = FSCN$_VERSION; 759 item_list[verspec].length = 0; 760 item_list[verspec].component = NULL; 761 762 item_list[8].itmcode = 0; 763 item_list[8].length = 0; 764 item_list[8].component = NULL; 765 766 status = sys$filescan 767 ((const struct dsc$descriptor_s *)&path_desc, item_list, 768 &flags, NULL, NULL); 769 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */ 770 771 /* If we parsed it successfully these two lengths should be the same */ 772 if (path_desc.dsc$w_length != item_list[filespec].length) 773 return ret_stat; 774 775 /* If we got here, then it is a VMS file specification */ 776 ret_stat = 0; 777 778 /* set the volume name */ 779 if (item_list[nodespec].length > 0) { 780 *volume = item_list[nodespec].component; 781 *vol_len = item_list[nodespec].length + item_list[devspec].length; 782 } 783 else { 784 *volume = item_list[devspec].component; 785 *vol_len = item_list[devspec].length; 786 } 787 788 *root = item_list[rootspec].component; 789 *root_len = item_list[rootspec].length; 790 791 *dir = item_list[dirspec].component; 792 *dir_len = item_list[dirspec].length; 793 794 /* Now fun with versions and EFS file specifications 795 * The parser can not tell the difference when a "." is a version 796 * delimiter or a part of the file specification. 797 */ 798 if ((decc_efs_charset) && 799 (item_list[verspec].length > 0) && 800 (item_list[verspec].component[0] == '.')) { 801 *name = item_list[namespec].component; 802 *name_len = item_list[namespec].length + item_list[typespec].length; 803 *ext = item_list[verspec].component; 804 *ext_len = item_list[verspec].length; 805 *version = NULL; 806 *ver_len = 0; 807 } 808 else { 809 *name = item_list[namespec].component; 810 *name_len = item_list[namespec].length; 811 *ext = item_list[typespec].component; 812 *ext_len = item_list[typespec].length; 813 *version = item_list[verspec].component; 814 *ver_len = item_list[verspec].length; 815 } 816 return ret_stat; 817 } 818 819 /* Routine to determine if the file specification ends with .dir */ 820 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) { 821 822 /* e_len must be 4, and version must be <= 2 characters */ 823 if (e_len != 4 || vs_len > 2) 824 return 0; 825 826 /* If a version number is present, it needs to be one */ 827 if ((vs_len == 2) && (vs_spec[1] != '1')) 828 return 0; 829 830 /* Look for the DIR on the extension */ 831 if (vms_process_case_tolerant) { 832 if ((toupper(e_spec[1]) == 'D') && 833 (toupper(e_spec[2]) == 'I') && 834 (toupper(e_spec[3]) == 'R')) { 835 return 1; 836 } 837 } else { 838 /* Directory extensions are supposed to be in upper case only */ 839 /* I would not be surprised if this rule can not be enforced */ 840 /* if and when someone fully debugs the case sensitive mode */ 841 if ((e_spec[1] == 'D') && 842 (e_spec[2] == 'I') && 843 (e_spec[3] == 'R')) { 844 return 1; 845 } 846 } 847 return 0; 848 } 849 850 851 /* my_maxidx 852 * Routine to retrieve the maximum equivalence index for an input 853 * logical name. Some calls to this routine have no knowledge if 854 * the variable is a logical or not. So on error we return a max 855 * index of zero. 856 */ 857 /*{{{int my_maxidx(const char *lnm) */ 858 static int 859 my_maxidx(const char *lnm) 860 { 861 int status; 862 int midx; 863 int attr = LNM$M_CASE_BLIND; 864 struct dsc$descriptor lnmdsc; 865 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0}, 866 {0, 0, 0, 0}}; 867 868 lnmdsc.dsc$w_length = strlen(lnm); 869 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T; 870 lnmdsc.dsc$b_class = DSC$K_CLASS_S; 871 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */ 872 873 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst); 874 if ((status & 1) == 0) 875 midx = 0; 876 877 return (midx); 878 } 879 /*}}}*/ 880 881 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */ 882 int 883 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, 884 struct dsc$descriptor_s **tabvec, unsigned long int flags) 885 { 886 const char *cp1; 887 char uplnm[LNM$C_NAMLENGTH+1], *cp2; 888 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure; 889 unsigned long int retsts, attr = LNM$M_CASE_BLIND; 890 int midx; 891 unsigned char acmode; 892 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 893 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 894 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0}, 895 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen}, 896 {0, 0, 0, 0}}; 897 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 898 #if defined(PERL_IMPLICIT_CONTEXT) 899 pTHX = NULL; 900 if (PL_curinterp) { 901 aTHX = PERL_GET_INTERP; 902 } else { 903 aTHX = NULL; 904 } 905 #endif 906 907 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) { 908 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0; 909 } 910 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 911 *cp2 = _toupper(*cp1); 912 if (cp1 - lnm > LNM$C_NAMLENGTH) { 913 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 914 return 0; 915 } 916 } 917 lnmdsc.dsc$w_length = cp1 - lnm; 918 lnmdsc.dsc$a_pointer = uplnm; 919 uplnm[lnmdsc.dsc$w_length] = '\0'; 920 secure = flags & PERL__TRNENV_SECURE; 921 acmode = secure ? PSL$C_EXEC : PSL$C_USER; 922 if (!tabvec || !*tabvec) tabvec = env_tables; 923 924 for (curtab = 0; tabvec[curtab]; curtab++) { 925 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) { 926 if (!ivenv && !secure) { 927 char *eq; 928 int i; 929 if (!environ) { 930 ivenv = 1; 931 #if defined(PERL_IMPLICIT_CONTEXT) 932 if (aTHX == NULL) { 933 fprintf(stderr, 934 "Can't read CRTL environ\n"); 935 } else 936 #endif 937 Perl_warn(aTHX_ "Can't read CRTL environ\n"); 938 continue; 939 } 940 retsts = SS$_NOLOGNAM; 941 for (i = 0; environ[i]; i++) { 942 if ((eq = strchr(environ[i],'=')) && 943 lnmdsc.dsc$w_length == (eq - environ[i]) && 944 !strncmp(environ[i],uplnm,eq - environ[i])) { 945 eq++; 946 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen]; 947 if (!eqvlen) continue; 948 retsts = SS$_NORMAL; 949 break; 950 } 951 } 952 if (retsts != SS$_NOLOGNAM) break; 953 } 954 } 955 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 956 !str$case_blind_compare(&tmpdsc,&clisym)) { 957 if (!ivsym && !secure) { 958 unsigned short int deflen = LNM$C_NAMLENGTH; 959 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 960 /* dynamic dsc to accommodate possible long value */ 961 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc)); 962 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0); 963 if (retsts & 1) { 964 if (eqvlen > MAX_DCL_SYMBOL) { 965 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU); 966 eqvlen = MAX_DCL_SYMBOL; 967 /* Special hack--we might be called before the interpreter's */ 968 /* fully initialized, in which case either thr or PL_curcop */ 969 /* might be bogus. We have to check, since ckWARN needs them */ 970 /* both to be valid if running threaded */ 971 #if defined(PERL_IMPLICIT_CONTEXT) 972 if (aTHX == NULL) { 973 fprintf(stderr, 974 "Value of CLI symbol \"%s\" too long",lnm); 975 } else 976 #endif 977 if (ckWARN(WARN_MISC)) { 978 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm); 979 } 980 } 981 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen); 982 } 983 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc)); 984 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 985 if (retsts == LIB$_NOSUCHSYM) continue; 986 break; 987 } 988 } 989 else if (!ivlnm) { 990 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) { 991 midx = my_maxidx(lnm); 992 for (idx = 0, cp2 = eqv; idx <= midx; idx++) { 993 lnmlst[1].bufadr = cp2; 994 eqvlen = 0; 995 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 996 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; } 997 if (retsts == SS$_NOLOGNAM) break; 998 /* PPFs have a prefix */ 999 if ( 1000 #if INTSIZE == 4 1001 *((int *)uplnm) == *((int *)"SYS$") && 1002 #endif 1003 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 && 1004 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) || 1005 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) || 1006 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) || 1007 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) { 1008 memmove(eqv,eqv+4,eqvlen-4); 1009 eqvlen -= 4; 1010 } 1011 cp2 += eqvlen; 1012 *cp2 = '\0'; 1013 } 1014 if ((retsts == SS$_IVLOGNAM) || 1015 (retsts == SS$_NOLOGNAM)) { continue; } 1016 } 1017 else { 1018 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 1019 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1020 if (retsts == SS$_NOLOGNAM) continue; 1021 eqv[eqvlen] = '\0'; 1022 } 1023 eqvlen = strlen(eqv); 1024 break; 1025 } 1026 } 1027 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; } 1028 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM || 1029 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB || 1030 retsts == SS$_NOLOGNAM) { 1031 set_errno(EINVAL); set_vaxc_errno(retsts); 1032 } 1033 else _ckvmssts_noperl(retsts); 1034 return 0; 1035 } /* end of vmstrnenv */ 1036 /*}}}*/ 1037 1038 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/ 1039 /* Define as a function so we can access statics. */ 1040 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx) 1041 { 1042 int flags = 0; 1043 1044 #if defined(PERL_IMPLICIT_CONTEXT) 1045 if (aTHX != NULL) 1046 #endif 1047 #ifdef SECURE_INTERNAL_GETENV 1048 flags = (PL_curinterp ? TAINTING_get : will_taint) ? 1049 PERL__TRNENV_SECURE : 0; 1050 #endif 1051 1052 return vmstrnenv(lnm, eqv, idx, fildev, flags); 1053 } 1054 /*}}}*/ 1055 1056 /* my_getenv 1057 * Note: Uses Perl temp to store result so char * can be returned to 1058 * caller; this pointer will be invalidated at next Perl statement 1059 * transition. 1060 * We define this as a function rather than a macro in terms of my_getenv_len() 1061 * so that it'll work when PL_curinterp is undefined (and we therefore can't 1062 * allocate SVs). 1063 */ 1064 /*{{{ char *my_getenv(const char *lnm, bool sys)*/ 1065 char * 1066 Perl_my_getenv(pTHX_ const char *lnm, bool sys) 1067 { 1068 const char *cp1; 1069 static char *__my_getenv_eqv = NULL; 1070 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv; 1071 unsigned long int idx = 0; 1072 int success, secure, saverr, savvmserr; 1073 int midx, flags; 1074 SV *tmpsv; 1075 1076 midx = my_maxidx(lnm) + 1; 1077 1078 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1079 /* Set up a temporary buffer for the return value; Perl will 1080 * clean it up at the next statement transition */ 1081 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1082 if (!tmpsv) return NULL; 1083 eqv = SvPVX(tmpsv); 1084 } 1085 else { 1086 /* Assume no interpreter ==> single thread */ 1087 if (__my_getenv_eqv != NULL) { 1088 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1089 } 1090 else { 1091 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1092 } 1093 eqv = __my_getenv_eqv; 1094 } 1095 1096 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1097 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) { 1098 int len; 1099 getcwd(eqv,LNM$C_NAMLENGTH); 1100 1101 len = strlen(eqv); 1102 1103 /* Get rid of "000000/ in rooted filespecs */ 1104 if (len > 7) { 1105 char * zeros; 1106 zeros = strstr(eqv, "/000000/"); 1107 if (zeros != NULL) { 1108 int mlen; 1109 mlen = len - (zeros - eqv) - 7; 1110 memmove(zeros, &zeros[7], mlen); 1111 len = len - 7; 1112 eqv[len] = '\0'; 1113 } 1114 } 1115 return eqv; 1116 } 1117 else { 1118 /* Impose security constraints only if tainting */ 1119 if (sys) { 1120 /* Impose security constraints only if tainting */ 1121 secure = PL_curinterp ? TAINTING_get : will_taint; 1122 saverr = errno; savvmserr = vaxc$errno; 1123 } 1124 else { 1125 secure = 0; 1126 } 1127 1128 flags = 1129 #ifdef SECURE_INTERNAL_GETENV 1130 secure ? PERL__TRNENV_SECURE : 0 1131 #else 1132 0 1133 #endif 1134 ; 1135 1136 /* For the getenv interface we combine all the equivalence names 1137 * of a search list logical into one value to acquire a maximum 1138 * value length of 255*128 (assuming %ENV is using logicals). 1139 */ 1140 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1141 1142 /* If the name contains a semicolon-delimited index, parse it 1143 * off and make sure we only retrieve the equivalence name for 1144 * that index. */ 1145 if ((cp2 = strchr(lnm,';')) != NULL) { 1146 my_strlcpy(uplnm, lnm, cp2 - lnm + 1); 1147 idx = strtoul(cp2+1,NULL,0); 1148 lnm = uplnm; 1149 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1150 } 1151 1152 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags); 1153 1154 /* Discard NOLOGNAM on internal calls since we're often looking 1155 * for an optional name, and this "error" often shows up as the 1156 * (bogus) exit status for a die() call later on. */ 1157 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr); 1158 return success ? eqv : NULL; 1159 } 1160 1161 } /* end of my_getenv() */ 1162 /*}}}*/ 1163 1164 1165 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/ 1166 char * 1167 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys) 1168 { 1169 const char *cp1; 1170 char *buf, *cp2; 1171 unsigned long idx = 0; 1172 int midx, flags; 1173 static char *__my_getenv_len_eqv = NULL; 1174 int secure, saverr, savvmserr; 1175 SV *tmpsv; 1176 1177 midx = my_maxidx(lnm) + 1; 1178 1179 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1180 /* Set up a temporary buffer for the return value; Perl will 1181 * clean it up at the next statement transition */ 1182 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1183 if (!tmpsv) return NULL; 1184 buf = SvPVX(tmpsv); 1185 } 1186 else { 1187 /* Assume no interpreter ==> single thread */ 1188 if (__my_getenv_len_eqv != NULL) { 1189 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1190 } 1191 else { 1192 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1193 } 1194 buf = __my_getenv_len_eqv; 1195 } 1196 1197 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1198 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) { 1199 char * zeros; 1200 1201 getcwd(buf,LNM$C_NAMLENGTH); 1202 *len = strlen(buf); 1203 1204 /* Get rid of "000000/ in rooted filespecs */ 1205 if (*len > 7) { 1206 zeros = strstr(buf, "/000000/"); 1207 if (zeros != NULL) { 1208 int mlen; 1209 mlen = *len - (zeros - buf) - 7; 1210 memmove(zeros, &zeros[7], mlen); 1211 *len = *len - 7; 1212 buf[*len] = '\0'; 1213 } 1214 } 1215 return buf; 1216 } 1217 else { 1218 if (sys) { 1219 /* Impose security constraints only if tainting */ 1220 secure = PL_curinterp ? TAINTING_get : will_taint; 1221 saverr = errno; savvmserr = vaxc$errno; 1222 } 1223 else { 1224 secure = 0; 1225 } 1226 1227 flags = 1228 #ifdef SECURE_INTERNAL_GETENV 1229 secure ? PERL__TRNENV_SECURE : 0 1230 #else 1231 0 1232 #endif 1233 ; 1234 1235 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1236 1237 if ((cp2 = strchr(lnm,';')) != NULL) { 1238 my_strlcpy(buf, lnm, cp2 - lnm + 1); 1239 idx = strtoul(cp2+1,NULL,0); 1240 lnm = buf; 1241 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1242 } 1243 1244 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags); 1245 1246 /* Get rid of "000000/ in rooted filespecs */ 1247 if (*len > 7) { 1248 char * zeros; 1249 zeros = strstr(buf, "/000000/"); 1250 if (zeros != NULL) { 1251 int mlen; 1252 mlen = *len - (zeros - buf) - 7; 1253 memmove(zeros, &zeros[7], mlen); 1254 *len = *len - 7; 1255 buf[*len] = '\0'; 1256 } 1257 } 1258 1259 /* Discard NOLOGNAM on internal calls since we're often looking 1260 * for an optional name, and this "error" often shows up as the 1261 * (bogus) exit status for a die() call later on. */ 1262 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr); 1263 return *len ? buf : NULL; 1264 } 1265 1266 } /* end of my_getenv_len() */ 1267 /*}}}*/ 1268 1269 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *); 1270 1271 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); } 1272 1273 /*{{{ void prime_env_iter() */ 1274 void 1275 prime_env_iter(void) 1276 /* Fill the %ENV associative array with all logical names we can 1277 * find, in preparation for iterating over it. 1278 */ 1279 { 1280 static int primed = 0; 1281 HV *seenhv = NULL, *envhv; 1282 SV *sv = NULL; 1283 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL; 1284 unsigned short int chan; 1285 #ifndef CLI$M_TRUSTED 1286 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */ 1287 #endif 1288 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED; 1289 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0; 1290 long int i; 1291 bool have_sym = FALSE, have_lnm = FALSE; 1292 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1293 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:"); 1294 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES"); 1295 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1296 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 1297 #if defined(PERL_IMPLICIT_CONTEXT) 1298 pTHX; 1299 #endif 1300 #if defined(USE_ITHREADS) 1301 static perl_mutex primenv_mutex; 1302 MUTEX_INIT(&primenv_mutex); 1303 #endif 1304 1305 #if defined(PERL_IMPLICIT_CONTEXT) 1306 /* We jump through these hoops because we can be called at */ 1307 /* platform-specific initialization time, which is before anything is */ 1308 /* set up--we can't even do a plain dTHX since that relies on the */ 1309 /* interpreter structure to be initialized */ 1310 if (PL_curinterp) { 1311 aTHX = PERL_GET_INTERP; 1312 } else { 1313 /* we never get here because the NULL pointer will cause the */ 1314 /* several of the routines called by this routine to access violate */ 1315 1316 /* This routine is only called by hv.c/hv_iterinit which has a */ 1317 /* context, so the real fix may be to pass it through instead of */ 1318 /* the hoops above */ 1319 aTHX = NULL; 1320 } 1321 #endif 1322 1323 if (primed || !PL_envgv) return; 1324 MUTEX_LOCK(&primenv_mutex); 1325 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; } 1326 envhv = GvHVn(PL_envgv); 1327 /* Perform a dummy fetch as an lval to insure that the hash table is 1328 * set up. Otherwise, the hv_store() will turn into a nullop. */ 1329 (void) hv_fetch(envhv,"DEFAULT",7,TRUE); 1330 1331 for (i = 0; env_tables[i]; i++) { 1332 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1333 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1; 1334 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1; 1335 } 1336 if (have_sym || have_lnm) { 1337 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM; 1338 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0)); 1339 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0)); 1340 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length)); 1341 } 1342 1343 for (i--; i >= 0; i--) { 1344 if (!str$case_blind_compare(env_tables[i],&crtlenv)) { 1345 char *start; 1346 int j; 1347 for (j = 0; environ[j]; j++) { 1348 if (!(start = strchr(environ[j],'='))) { 1349 if (ckWARN(WARN_INTERNAL)) 1350 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]); 1351 } 1352 else { 1353 start++; 1354 sv = newSVpv(start,0); 1355 SvTAINTED_on(sv); 1356 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0); 1357 } 1358 } 1359 continue; 1360 } 1361 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1362 !str$case_blind_compare(&tmpdsc,&clisym)) { 1363 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd)); 1364 cmddsc.dsc$w_length = 20; 1365 if (env_tables[i]->dsc$w_length == 12 && 1366 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) && 1367 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12); 1368 flags = defflags | CLI$M_NOLOGNAM; 1369 } 1370 else { 1371 my_strlcpy(cmd, "Show Logical *", sizeof(cmd)); 1372 if (str$case_blind_compare(env_tables[i],&fildevdsc)) { 1373 my_strlcat(cmd," /Table=", sizeof(cmd)); 1374 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd)); 1375 } 1376 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */ 1377 flags = defflags | CLI$M_NOCLISYM; 1378 } 1379 1380 /* Create a new subprocess to execute each command, to exclude the 1381 * remote possibility that someone could subvert a mbx or file used 1382 * to write multiple commands to a single subprocess. 1383 */ 1384 do { 1385 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs, 1386 0,&riseandshine,0,0,&clidsc,&clitabdsc); 1387 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */ 1388 defflags &= ~CLI$M_TRUSTED; 1389 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED)); 1390 _ckvmssts(retsts); 1391 if (!buf) Newx(buf,mbxbufsiz + 1,char); 1392 if (seenhv) SvREFCNT_dec(seenhv); 1393 seenhv = newHV(); 1394 while (1) { 1395 char *cp1, *cp2, *key; 1396 unsigned long int sts, iosb[2], retlen, keylen; 1397 U32 hash; 1398 1399 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0); 1400 if (sts & 1) sts = iosb[0] & 0xffff; 1401 if (sts == SS$_ENDOFFILE) { 1402 int wakect = 0; 1403 while (substs == 0) { sys$hiber(); wakect++;} 1404 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */ 1405 _ckvmssts(substs); 1406 break; 1407 } 1408 _ckvmssts(sts); 1409 retlen = iosb[0] >> 16; 1410 if (!retlen) continue; /* blank line */ 1411 buf[retlen] = '\0'; 1412 if (iosb[1] != subpid) { 1413 if (iosb[1]) { 1414 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf); 1415 } 1416 continue; 1417 } 1418 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL)) 1419 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf); 1420 1421 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ; 1422 if (*cp1 == '(' || /* Logical name table name */ 1423 *cp1 == '=' /* Next eqv of searchlist */) continue; 1424 if (*cp1 == '"') cp1++; 1425 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ; 1426 key = cp1; keylen = cp2 - cp1; 1427 if (keylen && hv_exists(seenhv,key,keylen)) continue; 1428 while (*cp2 && *cp2 != '=') cp2++; 1429 while (*cp2 && *cp2 == '=') cp2++; 1430 while (*cp2 && *cp2 == ' ') cp2++; 1431 if (*cp2 == '"') { /* String translation; may embed "" */ 1432 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ; 1433 cp2++; cp1--; /* Skip "" surrounding translation */ 1434 } 1435 else { /* Numeric translation */ 1436 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ; 1437 cp1--; /* stop on last non-space char */ 1438 } 1439 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) { 1440 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf); 1441 continue; 1442 } 1443 PERL_HASH(hash,key,keylen); 1444 1445 if (cp1 == cp2 && *cp2 == '.') { 1446 /* A single dot usually means an unprintable character, such as a null 1447 * to indicate a zero-length value. Get the actual value to make sure. 1448 */ 1449 char lnm[LNM$C_NAMLENGTH+1]; 1450 char eqv[MAX_DCL_SYMBOL+1]; 1451 int trnlen; 1452 strncpy(lnm, key, keylen); 1453 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0); 1454 sv = newSVpvn(eqv, strlen(eqv)); 1455 } 1456 else { 1457 sv = newSVpvn(cp2,cp1 - cp2 + 1); 1458 } 1459 1460 SvTAINTED_on(sv); 1461 hv_store(envhv,key,keylen,sv,hash); 1462 hv_store(seenhv,key,keylen,&PL_sv_yes,hash); 1463 } 1464 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */ 1465 /* get the PPFs for this process, not the subprocess */ 1466 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL}; 1467 char eqv[LNM$C_NAMLENGTH+1]; 1468 int trnlen, i; 1469 for (i = 0; ppfs[i]; i++) { 1470 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0); 1471 sv = newSVpv(eqv,trnlen); 1472 SvTAINTED_on(sv); 1473 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0); 1474 } 1475 } 1476 } 1477 primed = 1; 1478 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan)); 1479 if (buf) Safefree(buf); 1480 if (seenhv) SvREFCNT_dec(seenhv); 1481 MUTEX_UNLOCK(&primenv_mutex); 1482 return; 1483 1484 } /* end of prime_env_iter */ 1485 /*}}}*/ 1486 1487 1488 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/ 1489 /* Define or delete an element in the same "environment" as 1490 * vmstrnenv(). If an element is to be deleted, it's removed from 1491 * the first place it's found. If it's to be set, it's set in the 1492 * place designated by the first element of the table vector. 1493 * Like setenv() returns 0 for success, non-zero on error. 1494 */ 1495 int 1496 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec) 1497 { 1498 const char *cp1; 1499 char uplnm[LNM$C_NAMLENGTH], *cp2, *c; 1500 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0; 1501 int nseg = 0, j; 1502 unsigned long int retsts, usermode = PSL$C_USER; 1503 struct itmlst_3 *ile, *ilist; 1504 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm}, 1505 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 1506 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1507 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1508 $DESCRIPTOR(local,"_LOCAL"); 1509 1510 if (!lnm) { 1511 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1512 return SS$_IVLOGNAM; 1513 } 1514 1515 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 1516 *cp2 = _toupper(*cp1); 1517 if (cp1 - lnm > LNM$C_NAMLENGTH) { 1518 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1519 return SS$_IVLOGNAM; 1520 } 1521 } 1522 lnmdsc.dsc$w_length = cp1 - lnm; 1523 if (!tabvec || !*tabvec) tabvec = env_tables; 1524 1525 if (!eqv) { /* we're deleting n element */ 1526 for (curtab = 0; tabvec[curtab]; curtab++) { 1527 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) { 1528 int i; 1529 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */ 1530 if ((cp1 = strchr(environ[i],'=')) && 1531 lnmdsc.dsc$w_length == (cp1 - environ[i]) && 1532 !strncmp(environ[i],lnm,cp1 - environ[i])) { 1533 #ifdef HAS_SETENV 1534 return setenv(lnm,"",1) ? vaxc$errno : 0; 1535 } 1536 } 1537 ivenv = 1; retsts = SS$_NOLOGNAM; 1538 #else 1539 if (ckWARN(WARN_INTERNAL)) 1540 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm); 1541 ivenv = 1; retsts = SS$_NOSUCHPGM; 1542 break; 1543 } 1544 } 1545 #endif 1546 } 1547 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 1548 !str$case_blind_compare(&tmpdsc,&clisym)) { 1549 unsigned int symtype; 1550 if (tabvec[curtab]->dsc$w_length == 12 && 1551 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) && 1552 !str$case_blind_compare(&tmpdsc,&local)) 1553 symtype = LIB$K_CLI_LOCAL_SYM; 1554 else symtype = LIB$K_CLI_GLOBAL_SYM; 1555 retsts = lib$delete_symbol(&lnmdsc,&symtype); 1556 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 1557 if (retsts == LIB$_NOSUCHSYM) continue; 1558 break; 1559 } 1560 else if (!ivlnm) { 1561 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */ 1562 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1563 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1564 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */ 1565 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1566 } 1567 } 1568 } 1569 else { /* we're defining a value */ 1570 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) { 1571 #ifdef HAS_SETENV 1572 return setenv(lnm,eqv,1) ? vaxc$errno : 0; 1573 #else 1574 if (ckWARN(WARN_INTERNAL)) 1575 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv); 1576 retsts = SS$_NOSUCHPGM; 1577 #endif 1578 } 1579 else { 1580 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */ 1581 eqvdsc.dsc$w_length = strlen(eqv); 1582 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) && 1583 !str$case_blind_compare(&tmpdsc,&clisym)) { 1584 unsigned int symtype; 1585 if (tabvec[0]->dsc$w_length == 12 && 1586 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) && 1587 !str$case_blind_compare(&tmpdsc,&local)) 1588 symtype = LIB$K_CLI_LOCAL_SYM; 1589 else symtype = LIB$K_CLI_GLOBAL_SYM; 1590 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype); 1591 } 1592 else { 1593 if (!*eqv) eqvdsc.dsc$w_length = 1; 1594 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) { 1595 1596 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH; 1597 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) { 1598 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes", 1599 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1)); 1600 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1); 1601 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1; 1602 } 1603 1604 Newx(ilist,nseg+1,struct itmlst_3); 1605 ile = ilist; 1606 if (!ile) { 1607 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM); 1608 return SS$_INSFMEM; 1609 } 1610 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1))); 1611 1612 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) { 1613 ile->itmcode = LNM$_STRING; 1614 ile->bufadr = c; 1615 if ((j+1) == nseg) { 1616 ile->buflen = strlen(c); 1617 /* in case we are truncating one that's too long */ 1618 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH; 1619 } 1620 else { 1621 ile->buflen = LNM$C_NAMLENGTH; 1622 } 1623 } 1624 1625 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist); 1626 Safefree (ilist); 1627 } 1628 else { 1629 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0); 1630 } 1631 } 1632 } 1633 } 1634 if (!(retsts & 1)) { 1635 switch (retsts) { 1636 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM: 1637 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB: 1638 set_errno(EVMSERR); break; 1639 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 1640 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM: 1641 set_errno(EINVAL); break; 1642 case SS$_NOPRIV: 1643 set_errno(EACCES); break; 1644 default: 1645 _ckvmssts(retsts); 1646 set_errno(EVMSERR); 1647 } 1648 set_vaxc_errno(retsts); 1649 return (int) retsts || 44; /* retsts should never be 0, but just in case */ 1650 } 1651 else { 1652 /* We reset error values on success because Perl does an hv_fetch() 1653 * before each hv_store(), and if the thing we're setting didn't 1654 * previously exist, we've got a leftover error message. (Of course, 1655 * this fails in the face of 1656 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo'; 1657 * in that the error reported in $! isn't spurious, 1658 * but it's right more often than not.) 1659 */ 1660 set_errno(0); set_vaxc_errno(retsts); 1661 return 0; 1662 } 1663 1664 } /* end of vmssetenv() */ 1665 /*}}}*/ 1666 1667 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/ 1668 /* This has to be a function since there's a prototype for it in proto.h */ 1669 void 1670 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv) 1671 { 1672 if (lnm && *lnm) { 1673 int len = strlen(lnm); 1674 if (len == 7) { 1675 char uplnm[8]; 1676 int i; 1677 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]); 1678 if (!strcmp(uplnm,"DEFAULT")) { 1679 if (eqv && *eqv) my_chdir(eqv); 1680 return; 1681 } 1682 } 1683 } 1684 (void) vmssetenv(lnm,eqv,NULL); 1685 } 1686 /*}}}*/ 1687 1688 /*{{{static void vmssetuserlnm(char *name, char *eqv); */ 1689 /* vmssetuserlnm 1690 * sets a user-mode logical in the process logical name table 1691 * used for redirection of sys$error 1692 */ 1693 void 1694 Perl_vmssetuserlnm(const char *name, const char *eqv) 1695 { 1696 $DESCRIPTOR(d_tab, "LNM$PROCESS"); 1697 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 1698 unsigned long int iss, attr = LNM$M_CONFINE; 1699 unsigned char acmode = PSL$C_USER; 1700 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0}, 1701 {0, 0, 0, 0}}; 1702 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */ 1703 d_name.dsc$w_length = strlen(name); 1704 1705 lnmlst[0].buflen = strlen(eqv); 1706 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */ 1707 1708 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst); 1709 if (!(iss&1)) lib$signal(iss); 1710 } 1711 /*}}}*/ 1712 1713 1714 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/ 1715 /* my_crypt - VMS password hashing 1716 * my_crypt() provides an interface compatible with the Unix crypt() 1717 * C library function, and uses sys$hash_password() to perform VMS 1718 * password hashing. The quadword hashed password value is returned 1719 * as a NUL-terminated 8 character string. my_crypt() does not change 1720 * the case of its string arguments; in order to match the behavior 1721 * of LOGINOUT et al., alphabetic characters in both arguments must 1722 * be upcased by the caller. 1723 * 1724 * - fix me to call ACM services when available 1725 */ 1726 char * 1727 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname) 1728 { 1729 # ifndef UAI$C_PREFERRED_ALGORITHM 1730 # define UAI$C_PREFERRED_ALGORITHM 127 1731 # endif 1732 unsigned char alg = UAI$C_PREFERRED_ALGORITHM; 1733 unsigned short int salt = 0; 1734 unsigned long int sts; 1735 struct const_dsc { 1736 unsigned short int dsc$w_length; 1737 unsigned char dsc$b_type; 1738 unsigned char dsc$b_class; 1739 const char * dsc$a_pointer; 1740 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}, 1741 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1742 struct itmlst_3 uailst[3] = { 1743 { sizeof alg, UAI$_ENCRYPT, &alg, 0}, 1744 { sizeof salt, UAI$_SALT, &salt, 0}, 1745 { 0, 0, NULL, NULL}}; 1746 static char hash[9]; 1747 1748 usrdsc.dsc$w_length = strlen(usrname); 1749 usrdsc.dsc$a_pointer = usrname; 1750 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) { 1751 switch (sts) { 1752 case SS$_NOGRPPRV: case SS$_NOSYSPRV: 1753 set_errno(EACCES); 1754 break; 1755 case RMS$_RNF: 1756 set_errno(ESRCH); /* There isn't a Unix no-such-user error */ 1757 break; 1758 default: 1759 set_errno(EVMSERR); 1760 } 1761 set_vaxc_errno(sts); 1762 if (sts != RMS$_RNF) return NULL; 1763 } 1764 1765 txtdsc.dsc$w_length = strlen(textpasswd); 1766 txtdsc.dsc$a_pointer = textpasswd; 1767 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) { 1768 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL; 1769 } 1770 1771 return (char *) hash; 1772 1773 } /* end of my_crypt() */ 1774 /*}}}*/ 1775 1776 1777 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *); 1778 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *); 1779 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *); 1780 1781 /* fixup barenames that are directories for internal use. 1782 * There have been problems with the consistent handling of UNIX 1783 * style directory names when routines are presented with a name that 1784 * has no directory delimiters at all. So this routine will eventually 1785 * fix the issue. 1786 */ 1787 static char * fixup_bare_dirnames(const char * name) 1788 { 1789 if (decc_disable_to_vms_logname_translation) { 1790 /* fix me */ 1791 } 1792 return NULL; 1793 } 1794 1795 /* 8.3, remove() is now broken on symbolic links */ 1796 static int rms_erase(const char * vmsname); 1797 1798 1799 /* mp_do_kill_file 1800 * A little hack to get around a bug in some implementation of remove() 1801 * that do not know how to delete a directory 1802 * 1803 * Delete any file to which user has control access, regardless of whether 1804 * delete access is explicitly allowed. 1805 * Limitations: User must have write access to parent directory. 1806 * Does not block signals or ASTs; if interrupted in midstream 1807 * may leave file with an altered ACL. 1808 * HANDLE WITH CARE! 1809 */ 1810 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/ 1811 static int 1812 mp_do_kill_file(pTHX_ const char *name, int dirflag) 1813 { 1814 char *vmsname; 1815 char *rslt; 1816 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; 1817 unsigned long int cxt = 0, aclsts, fndsts; 1818 int rmsts = -1; 1819 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1820 struct myacedef { 1821 unsigned char myace$b_length; 1822 unsigned char myace$b_type; 1823 unsigned short int myace$w_flags; 1824 unsigned long int myace$l_access; 1825 unsigned long int myace$l_ident; 1826 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 1827 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0}, 1828 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 1829 struct itmlst_3 1830 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0}, 1831 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}}, 1832 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}}, 1833 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}}, 1834 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}}, 1835 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}}; 1836 1837 /* Expand the input spec using RMS, since the CRTL remove() and 1838 * system services won't do this by themselves, so we may miss 1839 * a file "hiding" behind a logical name or search list. */ 1840 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1); 1841 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM); 1842 1843 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK); 1844 if (rslt == NULL) { 1845 PerlMem_free(vmsname); 1846 return -1; 1847 } 1848 1849 /* Erase the file */ 1850 rmsts = rms_erase(vmsname); 1851 1852 /* Did it succeed */ 1853 if ($VMS_STATUS_SUCCESS(rmsts)) { 1854 PerlMem_free(vmsname); 1855 return 0; 1856 } 1857 1858 /* If not, can changing protections help? */ 1859 if (rmsts != RMS$_PRV) { 1860 set_vaxc_errno(rmsts); 1861 PerlMem_free(vmsname); 1862 return -1; 1863 } 1864 1865 /* No, so we get our own UIC to use as a rights identifier, 1866 * and the insert an ACE at the head of the ACL which allows us 1867 * to delete the file. 1868 */ 1869 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 1870 fildsc.dsc$w_length = strlen(vmsname); 1871 fildsc.dsc$a_pointer = vmsname; 1872 cxt = 0; 1873 newace.myace$l_ident = oldace.myace$l_ident; 1874 rmsts = -1; 1875 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) { 1876 switch (aclsts) { 1877 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT: 1878 set_errno(ENOENT); break; 1879 case RMS$_DIR: 1880 set_errno(ENOTDIR); break; 1881 case RMS$_DEV: 1882 set_errno(ENODEV); break; 1883 case RMS$_SYN: case SS$_INVFILFOROP: 1884 set_errno(EINVAL); break; 1885 case RMS$_PRV: 1886 set_errno(EACCES); break; 1887 default: 1888 _ckvmssts_noperl(aclsts); 1889 } 1890 set_vaxc_errno(aclsts); 1891 PerlMem_free(vmsname); 1892 return -1; 1893 } 1894 /* Grab any existing ACEs with this identifier in case we fail */ 1895 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt); 1896 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY 1897 || fndsts == SS$_NOMOREACE ) { 1898 /* Add the new ACE . . . */ 1899 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1)) 1900 goto yourroom; 1901 1902 rmsts = rms_erase(vmsname); 1903 if ($VMS_STATUS_SUCCESS(rmsts)) { 1904 rmsts = 0; 1905 } 1906 else { 1907 rmsts = -1; 1908 /* We blew it - dir with files in it, no write priv for 1909 * parent directory, etc. Put things back the way they were. */ 1910 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1)) 1911 goto yourroom; 1912 if (fndsts & 1) { 1913 addlst[0].bufadr = &oldace; 1914 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1)) 1915 goto yourroom; 1916 } 1917 } 1918 } 1919 1920 yourroom: 1921 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0); 1922 /* We just deleted it, so of course it's not there. Some versions of 1923 * VMS seem to return success on the unlock operation anyhow (after all 1924 * the unlock is successful), but others don't. 1925 */ 1926 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL; 1927 if (aclsts & 1) aclsts = fndsts; 1928 if (!(aclsts & 1)) { 1929 set_errno(EVMSERR); 1930 set_vaxc_errno(aclsts); 1931 } 1932 1933 PerlMem_free(vmsname); 1934 return rmsts; 1935 1936 } /* end of kill_file() */ 1937 /*}}}*/ 1938 1939 1940 /*{{{int do_rmdir(char *name)*/ 1941 int 1942 Perl_do_rmdir(pTHX_ const char *name) 1943 { 1944 char * dirfile; 1945 int retval; 1946 Stat_t st; 1947 1948 /* lstat returns a VMS fileified specification of the name */ 1949 /* that is looked up, and also lets verifies that this is a directory */ 1950 1951 retval = flex_lstat(name, &st); 1952 if (retval != 0) { 1953 char * ret_spec; 1954 1955 /* Due to a historical feature, flex_stat/lstat can not see some */ 1956 /* Unix format file names that the rest of the CRTL can see */ 1957 /* Fixing that feature will cause some perl tests to fail */ 1958 /* So try this one more time. */ 1959 1960 retval = lstat(name, &st.crtl_stat); 1961 if (retval != 0) 1962 return -1; 1963 1964 /* force it to a file spec for the kill file to work. */ 1965 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL); 1966 if (ret_spec == NULL) { 1967 errno = EIO; 1968 return -1; 1969 } 1970 } 1971 1972 if (!S_ISDIR(st.st_mode)) { 1973 errno = ENOTDIR; 1974 retval = -1; 1975 } 1976 else { 1977 dirfile = st.st_devnam; 1978 1979 /* It may be possible for flex_stat to find a file and vmsify() to */ 1980 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */ 1981 /* with that case, so fail it */ 1982 if (dirfile[0] == 0) { 1983 errno = EIO; 1984 return -1; 1985 } 1986 1987 retval = mp_do_kill_file(aTHX_ dirfile, 1); 1988 } 1989 1990 return retval; 1991 1992 } /* end of do_rmdir */ 1993 /*}}}*/ 1994 1995 /* kill_file 1996 * Delete any file to which user has control access, regardless of whether 1997 * delete access is explicitly allowed. 1998 * Limitations: User must have write access to parent directory. 1999 * Does not block signals or ASTs; if interrupted in midstream 2000 * may leave file with an altered ACL. 2001 * HANDLE WITH CARE! 2002 */ 2003 /*{{{int kill_file(char *name)*/ 2004 int 2005 Perl_kill_file(pTHX_ const char *name) 2006 { 2007 char * vmsfile; 2008 Stat_t st; 2009 int rmsts; 2010 2011 /* Convert the filename to VMS format and see if it is a directory */ 2012 /* flex_lstat returns a vmsified file specification */ 2013 rmsts = flex_lstat(name, &st); 2014 if (rmsts != 0) { 2015 2016 /* Due to a historical feature, flex_stat/lstat can not see some */ 2017 /* Unix format file names that the rest of the CRTL can see when */ 2018 /* ODS-2 file specifications are in use. */ 2019 /* Fixing that feature will cause some perl tests to fail */ 2020 /* [.lib.ExtUtils.t]Manifest.t is one of them */ 2021 st.st_mode = 0; 2022 vmsfile = (char *) name; /* cast ok */ 2023 2024 } else { 2025 vmsfile = st.st_devnam; 2026 if (vmsfile[0] == 0) { 2027 /* It may be possible for flex_stat to find a file and vmsify() */ 2028 /* to fail with ODS-2 specifications. mp_do_kill_file can not */ 2029 /* deal with that case, so fail it */ 2030 errno = EIO; 2031 return -1; 2032 } 2033 } 2034 2035 /* Remove() is allowed to delete directories, according to the X/Open 2036 * specifications. 2037 * This may need special handling to work with the ACL hacks. 2038 */ 2039 if (S_ISDIR(st.st_mode)) { 2040 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1); 2041 return rmsts; 2042 } 2043 2044 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0); 2045 2046 /* Need to delete all versions ? */ 2047 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) { 2048 int i = 0; 2049 2050 /* Just use lstat() here as do not need st_dev */ 2051 /* and we know that the file is in VMS format or that */ 2052 /* because of a historical bug, flex_stat can not see the file */ 2053 while (lstat(vmsfile, (stat_t *)&st) == 0) { 2054 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0); 2055 if (rmsts != 0) 2056 break; 2057 i++; 2058 2059 /* Make sure that we do not loop forever */ 2060 if (i > 32767) { 2061 errno = EIO; 2062 rmsts = -1; 2063 break; 2064 } 2065 } 2066 } 2067 2068 return rmsts; 2069 2070 } /* end of kill_file() */ 2071 /*}}}*/ 2072 2073 2074 /*{{{int my_mkdir(char *,Mode_t)*/ 2075 int 2076 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode) 2077 { 2078 STRLEN dirlen = strlen(dir); 2079 2080 /* zero length string sometimes gives ACCVIO */ 2081 if (dirlen == 0) return -1; 2082 2083 /* CRTL mkdir() doesn't tolerate trailing /, since that implies 2084 * null file name/type. However, it's commonplace under Unix, 2085 * so we'll allow it for a gain in portability. 2086 */ 2087 if (dir[dirlen-1] == '/') { 2088 char *newdir = savepvn(dir,dirlen-1); 2089 int ret = mkdir(newdir,mode); 2090 Safefree(newdir); 2091 return ret; 2092 } 2093 else return mkdir(dir,mode); 2094 } /* end of my_mkdir */ 2095 /*}}}*/ 2096 2097 /*{{{int my_chdir(char *)*/ 2098 int 2099 Perl_my_chdir(pTHX_ const char *dir) 2100 { 2101 STRLEN dirlen = strlen(dir); 2102 const char *dir1 = dir; 2103 2104 /* zero length string sometimes gives ACCVIO */ 2105 if (dirlen == 0) { 2106 SETERRNO(EINVAL, SS$_BADPARAM); 2107 return -1; 2108 } 2109 2110 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces. 2111 * This does not work if DECC$EFS_CHARSET is active. Hack it here 2112 * so that existing scripts do not need to be changed. 2113 */ 2114 while ((dirlen > 0) && (*dir1 == ' ')) { 2115 dir1++; 2116 dirlen--; 2117 } 2118 2119 /* some versions of CRTL chdir() doesn't tolerate trailing /, since 2120 * that implies 2121 * null file name/type. However, it's commonplace under Unix, 2122 * so we'll allow it for a gain in portability. 2123 * 2124 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active. 2125 */ 2126 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) { 2127 char *newdir; 2128 int ret; 2129 newdir = (char *)PerlMem_malloc(dirlen); 2130 if (newdir ==NULL) 2131 _ckvmssts_noperl(SS$_INSFMEM); 2132 memcpy(newdir, dir1, dirlen-1); 2133 newdir[dirlen-1] = '\0'; 2134 ret = chdir(newdir); 2135 PerlMem_free(newdir); 2136 return ret; 2137 } 2138 else return chdir(dir1); 2139 } /* end of my_chdir */ 2140 /*}}}*/ 2141 2142 2143 /*{{{int my_chmod(char *, mode_t)*/ 2144 int 2145 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode) 2146 { 2147 Stat_t st; 2148 int ret = -1; 2149 char * changefile; 2150 STRLEN speclen = strlen(file_spec); 2151 2152 /* zero length string sometimes gives ACCVIO */ 2153 if (speclen == 0) return -1; 2154 2155 /* some versions of CRTL chmod() doesn't tolerate trailing /, since 2156 * that implies null file name/type. However, it's commonplace under Unix, 2157 * so we'll allow it for a gain in portability. 2158 * 2159 * Tests are showing that chmod() on VMS 8.3 is only accepting directories 2160 * in VMS file.dir notation. 2161 */ 2162 changefile = (char *) file_spec; /* cast ok */ 2163 ret = flex_lstat(file_spec, &st); 2164 if (ret != 0) { 2165 2166 /* Due to a historical feature, flex_stat/lstat can not see some */ 2167 /* Unix format file names that the rest of the CRTL can see when */ 2168 /* ODS-2 file specifications are in use. */ 2169 /* Fixing that feature will cause some perl tests to fail */ 2170 /* [.lib.ExtUtils.t]Manifest.t is one of them */ 2171 st.st_mode = 0; 2172 2173 } else { 2174 /* It may be possible to get here with nothing in st_devname */ 2175 /* chmod still may work though */ 2176 if (st.st_devnam[0] != 0) { 2177 changefile = st.st_devnam; 2178 } 2179 } 2180 ret = chmod(changefile, mode); 2181 return ret; 2182 } /* end of my_chmod */ 2183 /*}}}*/ 2184 2185 2186 /*{{{FILE *my_tmpfile()*/ 2187 FILE * 2188 my_tmpfile(void) 2189 { 2190 FILE *fp; 2191 char *cp; 2192 2193 if ((fp = tmpfile())) return fp; 2194 2195 cp = (char *)PerlMem_malloc(L_tmpnam+24); 2196 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 2197 2198 if (decc_filename_unix_only == 0) 2199 strcpy(cp,"Sys$Scratch:"); 2200 else 2201 strcpy(cp,"/tmp/"); 2202 tmpnam(cp+strlen(cp)); 2203 strcat(cp,".Perltmp"); 2204 fp = fopen(cp,"w+","fop=dlt"); 2205 PerlMem_free(cp); 2206 return fp; 2207 } 2208 /*}}}*/ 2209 2210 2211 /* 2212 * The C RTL's sigaction fails to check for invalid signal numbers so we 2213 * help it out a bit. The docs are correct, but the actual routine doesn't 2214 * do what the docs say it will. 2215 */ 2216 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/ 2217 int 2218 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act, 2219 struct sigaction* oact) 2220 { 2221 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) { 2222 SETERRNO(EINVAL, SS$_INVARG); 2223 return -1; 2224 } 2225 return sigaction(sig, act, oact); 2226 } 2227 /*}}}*/ 2228 2229 #ifdef KILL_BY_SIGPRC 2230 #include <errnodef.h> 2231 2232 /* We implement our own kill() using the undocumented system service 2233 sys$sigprc for one of two reasons: 2234 2235 1.) If the kill() in an older CRTL uses sys$forcex, causing the 2236 target process to do a sys$exit, which usually can't be handled 2237 gracefully...certainly not by Perl and the %SIG{} mechanism. 2238 2239 2.) If the kill() in the CRTL can't be called from a signal 2240 handler without disappearing into the ether, i.e., the signal 2241 it purportedly sends is never trapped. Still true as of VMS 7.3. 2242 2243 sys$sigprc has the same parameters as sys$forcex, but throws an exception 2244 in the target process rather than calling sys$exit. 2245 2246 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg 2247 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't 2248 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc 2249 with condition codes C$_SIG0+nsig*8, catching the exception on the 2250 target process and resignaling with appropriate arguments. 2251 2252 But we don't have that VMS 7.0+ exception handler, so if you 2253 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well. 2254 2255 Also note that SIGTERM is listed in the docs as being "unimplemented", 2256 yet always seems to be signaled with a VMS condition code of 4 (and 2257 correctly handled for that code). So we hardwire it in. 2258 2259 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal 2260 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather 2261 than signalling with an unrecognized (and unhandled by CRTL) code. 2262 */ 2263 2264 #define _MY_SIG_MAX 28 2265 2266 static unsigned int 2267 Perl_sig_to_vmscondition_int(int sig) 2268 { 2269 static unsigned int sig_code[_MY_SIG_MAX+1] = 2270 { 2271 0, /* 0 ZERO */ 2272 SS$_HANGUP, /* 1 SIGHUP */ 2273 SS$_CONTROLC, /* 2 SIGINT */ 2274 SS$_CONTROLY, /* 3 SIGQUIT */ 2275 SS$_RADRMOD, /* 4 SIGILL */ 2276 SS$_BREAK, /* 5 SIGTRAP */ 2277 SS$_OPCCUS, /* 6 SIGABRT */ 2278 SS$_COMPAT, /* 7 SIGEMT */ 2279 #ifdef __VAX 2280 SS$_FLTOVF, /* 8 SIGFPE VAX */ 2281 #else 2282 SS$_HPARITH, /* 8 SIGFPE AXP */ 2283 #endif 2284 SS$_ABORT, /* 9 SIGKILL */ 2285 SS$_ACCVIO, /* 10 SIGBUS */ 2286 SS$_ACCVIO, /* 11 SIGSEGV */ 2287 SS$_BADPARAM, /* 12 SIGSYS */ 2288 SS$_NOMBX, /* 13 SIGPIPE */ 2289 SS$_ASTFLT, /* 14 SIGALRM */ 2290 4, /* 15 SIGTERM */ 2291 0, /* 16 SIGUSR1 */ 2292 0, /* 17 SIGUSR2 */ 2293 0, /* 18 */ 2294 0, /* 19 */ 2295 0, /* 20 SIGCHLD */ 2296 0, /* 21 SIGCONT */ 2297 0, /* 22 SIGSTOP */ 2298 0, /* 23 SIGTSTP */ 2299 0, /* 24 SIGTTIN */ 2300 0, /* 25 SIGTTOU */ 2301 0, /* 26 */ 2302 0, /* 27 */ 2303 0 /* 28 SIGWINCH */ 2304 }; 2305 2306 static int initted = 0; 2307 if (!initted) { 2308 initted = 1; 2309 sig_code[16] = C$_SIGUSR1; 2310 sig_code[17] = C$_SIGUSR2; 2311 sig_code[20] = C$_SIGCHLD; 2312 #if __CRTL_VER >= 70300000 2313 sig_code[28] = C$_SIGWINCH; 2314 #endif 2315 } 2316 2317 if (sig < _SIG_MIN) return 0; 2318 if (sig > _MY_SIG_MAX) return 0; 2319 return sig_code[sig]; 2320 } 2321 2322 unsigned int 2323 Perl_sig_to_vmscondition(int sig) 2324 { 2325 #ifdef SS$_DEBUG 2326 if (vms_debug_on_exception != 0) 2327 lib$signal(SS$_DEBUG); 2328 #endif 2329 return Perl_sig_to_vmscondition_int(sig); 2330 } 2331 2332 2333 #define sys$sigprc SYS$SIGPRC 2334 #ifdef __cplusplus 2335 extern "C" { 2336 #endif 2337 int sys$sigprc(unsigned int *pidadr, 2338 struct dsc$descriptor_s *prcname, 2339 unsigned int code); 2340 #ifdef __cplusplus 2341 } 2342 #endif 2343 2344 int 2345 Perl_my_kill(int pid, int sig) 2346 { 2347 int iss; 2348 unsigned int code; 2349 2350 /* sig 0 means validate the PID */ 2351 /*------------------------------*/ 2352 if (sig == 0) { 2353 const unsigned long int jpicode = JPI$_PID; 2354 pid_t ret_pid; 2355 int status; 2356 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL); 2357 if ($VMS_STATUS_SUCCESS(status)) 2358 return 0; 2359 switch (status) { 2360 case SS$_NOSUCHNODE: 2361 case SS$_UNREACHABLE: 2362 case SS$_NONEXPR: 2363 errno = ESRCH; 2364 break; 2365 case SS$_NOPRIV: 2366 errno = EPERM; 2367 break; 2368 default: 2369 errno = EVMSERR; 2370 } 2371 vaxc$errno=status; 2372 return -1; 2373 } 2374 2375 code = Perl_sig_to_vmscondition_int(sig); 2376 2377 if (!code) { 2378 SETERRNO(EINVAL, SS$_BADPARAM); 2379 return -1; 2380 } 2381 2382 /* Fixme: Per official UNIX specification: If pid = 0, or negative then 2383 * signals are to be sent to multiple processes. 2384 * pid = 0 - all processes in group except ones that the system exempts 2385 * pid = -1 - all processes except ones that the system exempts 2386 * pid = -n - all processes in group (abs(n)) except ... 2387 * For now, just report as not supported. 2388 */ 2389 2390 if (pid <= 0) { 2391 SETERRNO(ENOTSUP, SS$_UNSUPPORTED); 2392 return -1; 2393 } 2394 2395 iss = sys$sigprc((unsigned int *)&pid,0,code); 2396 if (iss&1) return 0; 2397 2398 switch (iss) { 2399 case SS$_NOPRIV: 2400 set_errno(EPERM); break; 2401 case SS$_NONEXPR: 2402 case SS$_NOSUCHNODE: 2403 case SS$_UNREACHABLE: 2404 set_errno(ESRCH); break; 2405 case SS$_INSFMEM: 2406 set_errno(ENOMEM); break; 2407 default: 2408 _ckvmssts_noperl(iss); 2409 set_errno(EVMSERR); 2410 } 2411 set_vaxc_errno(iss); 2412 2413 return -1; 2414 } 2415 #endif 2416 2417 /* Routine to convert a VMS status code to a UNIX status code. 2418 ** More tricky than it appears because of conflicting conventions with 2419 ** existing code. 2420 ** 2421 ** VMS status codes are a bit mask, with the least significant bit set for 2422 ** success. 2423 ** 2424 ** Special UNIX status of EVMSERR indicates that no translation is currently 2425 ** available, and programs should check the VMS status code. 2426 ** 2427 ** Programs compiled with _POSIX_EXIT have a special encoding that requires 2428 ** decoding. 2429 */ 2430 2431 #ifndef C_FACILITY_NO 2432 #define C_FACILITY_NO 0x350000 2433 #endif 2434 #ifndef DCL_IVVERB 2435 #define DCL_IVVERB 0x38090 2436 #endif 2437 2438 int Perl_vms_status_to_unix(int vms_status, int child_flag) 2439 { 2440 int facility; 2441 int fac_sp; 2442 int msg_no; 2443 int msg_status; 2444 int unix_status; 2445 2446 /* Assume the best or the worst */ 2447 if (vms_status & STS$M_SUCCESS) 2448 unix_status = 0; 2449 else 2450 unix_status = EVMSERR; 2451 2452 msg_status = vms_status & ~STS$M_CONTROL; 2453 2454 facility = vms_status & STS$M_FAC_NO; 2455 fac_sp = vms_status & STS$M_FAC_SP; 2456 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY); 2457 2458 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) { 2459 switch(msg_no) { 2460 case SS$_NORMAL: 2461 unix_status = 0; 2462 break; 2463 case SS$_ACCVIO: 2464 unix_status = EFAULT; 2465 break; 2466 case SS$_DEVOFFLINE: 2467 unix_status = EBUSY; 2468 break; 2469 case SS$_CLEARED: 2470 unix_status = ENOTCONN; 2471 break; 2472 case SS$_IVCHAN: 2473 case SS$_IVLOGNAM: 2474 case SS$_BADPARAM: 2475 case SS$_IVLOGTAB: 2476 case SS$_NOLOGNAM: 2477 case SS$_NOLOGTAB: 2478 case SS$_INVFILFOROP: 2479 case SS$_INVARG: 2480 case SS$_NOSUCHID: 2481 case SS$_IVIDENT: 2482 unix_status = EINVAL; 2483 break; 2484 case SS$_UNSUPPORTED: 2485 unix_status = ENOTSUP; 2486 break; 2487 case SS$_FILACCERR: 2488 case SS$_NOGRPPRV: 2489 case SS$_NOSYSPRV: 2490 unix_status = EACCES; 2491 break; 2492 case SS$_DEVICEFULL: 2493 unix_status = ENOSPC; 2494 break; 2495 case SS$_NOSUCHDEV: 2496 unix_status = ENODEV; 2497 break; 2498 case SS$_NOSUCHFILE: 2499 case SS$_NOSUCHOBJECT: 2500 unix_status = ENOENT; 2501 break; 2502 case SS$_ABORT: /* Fatal case */ 2503 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */ 2504 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */ 2505 unix_status = EINTR; 2506 break; 2507 case SS$_BUFFEROVF: 2508 unix_status = E2BIG; 2509 break; 2510 case SS$_INSFMEM: 2511 unix_status = ENOMEM; 2512 break; 2513 case SS$_NOPRIV: 2514 unix_status = EPERM; 2515 break; 2516 case SS$_NOSUCHNODE: 2517 case SS$_UNREACHABLE: 2518 unix_status = ESRCH; 2519 break; 2520 case SS$_NONEXPR: 2521 unix_status = ECHILD; 2522 break; 2523 default: 2524 if ((facility == 0) && (msg_no < 8)) { 2525 /* These are not real VMS status codes so assume that they are 2526 ** already UNIX status codes 2527 */ 2528 unix_status = msg_no; 2529 break; 2530 } 2531 } 2532 } 2533 else { 2534 /* Translate a POSIX exit code to a UNIX exit code */ 2535 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) { 2536 unix_status = (msg_no & 0x07F8) >> 3; 2537 } 2538 else { 2539 2540 /* Documented traditional behavior for handling VMS child exits */ 2541 /*--------------------------------------------------------------*/ 2542 if (child_flag != 0) { 2543 2544 /* Success / Informational return 0 */ 2545 /*----------------------------------*/ 2546 if (msg_no & STS$K_SUCCESS) 2547 return 0; 2548 2549 /* Warning returns 1 */ 2550 /*-------------------*/ 2551 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0) 2552 return 1; 2553 2554 /* Everything else pass through the severity bits */ 2555 /*------------------------------------------------*/ 2556 return (msg_no & STS$M_SEVERITY); 2557 } 2558 2559 /* Normal VMS status to ERRNO mapping attempt */ 2560 /*--------------------------------------------*/ 2561 switch(msg_status) { 2562 /* case RMS$_EOF: */ /* End of File */ 2563 case RMS$_FNF: /* File Not Found */ 2564 case RMS$_DNF: /* Dir Not Found */ 2565 unix_status = ENOENT; 2566 break; 2567 case RMS$_RNF: /* Record Not Found */ 2568 unix_status = ESRCH; 2569 break; 2570 case RMS$_DIR: 2571 unix_status = ENOTDIR; 2572 break; 2573 case RMS$_DEV: 2574 unix_status = ENODEV; 2575 break; 2576 case RMS$_IFI: 2577 case RMS$_FAC: 2578 case RMS$_ISI: 2579 unix_status = EBADF; 2580 break; 2581 case RMS$_FEX: 2582 unix_status = EEXIST; 2583 break; 2584 case RMS$_SYN: 2585 case RMS$_FNM: 2586 case LIB$_INVSTRDES: 2587 case LIB$_INVARG: 2588 case LIB$_NOSUCHSYM: 2589 case LIB$_INVSYMNAM: 2590 case DCL_IVVERB: 2591 unix_status = EINVAL; 2592 break; 2593 case CLI$_BUFOVF: 2594 case RMS$_RTB: 2595 case CLI$_TKNOVF: 2596 case CLI$_RSLOVF: 2597 unix_status = E2BIG; 2598 break; 2599 case RMS$_PRV: /* No privilege */ 2600 case RMS$_ACC: /* ACP file access failed */ 2601 case RMS$_WLK: /* Device write locked */ 2602 unix_status = EACCES; 2603 break; 2604 case RMS$_MKD: /* Failed to mark for delete */ 2605 unix_status = EPERM; 2606 break; 2607 /* case RMS$_NMF: */ /* No more files */ 2608 } 2609 } 2610 } 2611 2612 return unix_status; 2613 } 2614 2615 /* Try to guess at what VMS error status should go with a UNIX errno 2616 * value. This is hard to do as there could be many possible VMS 2617 * error statuses that caused the errno value to be set. 2618 */ 2619 2620 int Perl_unix_status_to_vms(int unix_status) 2621 { 2622 int test_unix_status; 2623 2624 /* Trivial cases first */ 2625 /*---------------------*/ 2626 if (unix_status == EVMSERR) 2627 return vaxc$errno; 2628 2629 /* Is vaxc$errno sane? */ 2630 /*---------------------*/ 2631 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0); 2632 if (test_unix_status == unix_status) 2633 return vaxc$errno; 2634 2635 /* If way out of range, must be VMS code already */ 2636 /*-----------------------------------------------*/ 2637 if (unix_status > EVMSERR) 2638 return unix_status; 2639 2640 /* If out of range, punt */ 2641 /*-----------------------*/ 2642 if (unix_status > __ERRNO_MAX) 2643 return SS$_ABORT; 2644 2645 2646 /* Ok, now we have to do it the hard way. */ 2647 /*----------------------------------------*/ 2648 switch(unix_status) { 2649 case 0: return SS$_NORMAL; 2650 case EPERM: return SS$_NOPRIV; 2651 case ENOENT: return SS$_NOSUCHOBJECT; 2652 case ESRCH: return SS$_UNREACHABLE; 2653 case EINTR: return SS$_ABORT; 2654 /* case EIO: */ 2655 /* case ENXIO: */ 2656 case E2BIG: return SS$_BUFFEROVF; 2657 /* case ENOEXEC */ 2658 case EBADF: return RMS$_IFI; 2659 case ECHILD: return SS$_NONEXPR; 2660 /* case EAGAIN */ 2661 case ENOMEM: return SS$_INSFMEM; 2662 case EACCES: return SS$_FILACCERR; 2663 case EFAULT: return SS$_ACCVIO; 2664 /* case ENOTBLK */ 2665 case EBUSY: return SS$_DEVOFFLINE; 2666 case EEXIST: return RMS$_FEX; 2667 /* case EXDEV */ 2668 case ENODEV: return SS$_NOSUCHDEV; 2669 case ENOTDIR: return RMS$_DIR; 2670 /* case EISDIR */ 2671 case EINVAL: return SS$_INVARG; 2672 /* case ENFILE */ 2673 /* case EMFILE */ 2674 /* case ENOTTY */ 2675 /* case ETXTBSY */ 2676 /* case EFBIG */ 2677 case ENOSPC: return SS$_DEVICEFULL; 2678 case ESPIPE: return LIB$_INVARG; 2679 /* case EROFS: */ 2680 /* case EMLINK: */ 2681 /* case EPIPE: */ 2682 /* case EDOM */ 2683 case ERANGE: return LIB$_INVARG; 2684 /* case EWOULDBLOCK */ 2685 /* case EINPROGRESS */ 2686 /* case EALREADY */ 2687 /* case ENOTSOCK */ 2688 /* case EDESTADDRREQ */ 2689 /* case EMSGSIZE */ 2690 /* case EPROTOTYPE */ 2691 /* case ENOPROTOOPT */ 2692 /* case EPROTONOSUPPORT */ 2693 /* case ESOCKTNOSUPPORT */ 2694 /* case EOPNOTSUPP */ 2695 /* case EPFNOSUPPORT */ 2696 /* case EAFNOSUPPORT */ 2697 /* case EADDRINUSE */ 2698 /* case EADDRNOTAVAIL */ 2699 /* case ENETDOWN */ 2700 /* case ENETUNREACH */ 2701 /* case ENETRESET */ 2702 /* case ECONNABORTED */ 2703 /* case ECONNRESET */ 2704 /* case ENOBUFS */ 2705 /* case EISCONN */ 2706 case ENOTCONN: return SS$_CLEARED; 2707 /* case ESHUTDOWN */ 2708 /* case ETOOMANYREFS */ 2709 /* case ETIMEDOUT */ 2710 /* case ECONNREFUSED */ 2711 /* case ELOOP */ 2712 /* case ENAMETOOLONG */ 2713 /* case EHOSTDOWN */ 2714 /* case EHOSTUNREACH */ 2715 /* case ENOTEMPTY */ 2716 /* case EPROCLIM */ 2717 /* case EUSERS */ 2718 /* case EDQUOT */ 2719 /* case ENOMSG */ 2720 /* case EIDRM */ 2721 /* case EALIGN */ 2722 /* case ESTALE */ 2723 /* case EREMOTE */ 2724 /* case ENOLCK */ 2725 /* case ENOSYS */ 2726 /* case EFTYPE */ 2727 /* case ECANCELED */ 2728 /* case EFAIL */ 2729 /* case EINPROG */ 2730 case ENOTSUP: 2731 return SS$_UNSUPPORTED; 2732 /* case EDEADLK */ 2733 /* case ENWAIT */ 2734 /* case EILSEQ */ 2735 /* case EBADCAT */ 2736 /* case EBADMSG */ 2737 /* case EABANDONED */ 2738 default: 2739 return SS$_ABORT; /* punt */ 2740 } 2741 } 2742 2743 2744 /* default piping mailbox size */ 2745 #ifdef __VAX 2746 # define PERL_BUFSIZ 512 2747 #else 2748 # define PERL_BUFSIZ 8192 2749 #endif 2750 2751 2752 static void 2753 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc) 2754 { 2755 unsigned long int mbxbufsiz; 2756 static unsigned long int syssize = 0; 2757 unsigned long int dviitm = DVI$_DEVNAM; 2758 char csize[LNM$C_NAMLENGTH+1]; 2759 int sts; 2760 2761 if (!syssize) { 2762 unsigned long syiitm = SYI$_MAXBUF; 2763 /* 2764 * Get the SYSGEN parameter MAXBUF 2765 * 2766 * If the logical 'PERL_MBX_SIZE' is defined 2767 * use the value of the logical instead of PERL_BUFSIZ, but 2768 * keep the size between 128 and MAXBUF. 2769 * 2770 */ 2771 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0)); 2772 } 2773 2774 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) { 2775 mbxbufsiz = atoi(csize); 2776 } else { 2777 mbxbufsiz = PERL_BUFSIZ; 2778 } 2779 if (mbxbufsiz < 128) mbxbufsiz = 128; 2780 if (mbxbufsiz > syssize) mbxbufsiz = syssize; 2781 2782 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0)); 2783 2784 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length); 2785 _ckvmssts_noperl(sts); 2786 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0'; 2787 2788 } /* end of create_mbx() */ 2789 2790 2791 /*{{{ my_popen and my_pclose*/ 2792 2793 typedef struct _iosb IOSB; 2794 typedef struct _iosb* pIOSB; 2795 typedef struct _pipe Pipe; 2796 typedef struct _pipe* pPipe; 2797 typedef struct pipe_details Info; 2798 typedef struct pipe_details* pInfo; 2799 typedef struct _srqp RQE; 2800 typedef struct _srqp* pRQE; 2801 typedef struct _tochildbuf CBuf; 2802 typedef struct _tochildbuf* pCBuf; 2803 2804 struct _iosb { 2805 unsigned short status; 2806 unsigned short count; 2807 unsigned long dvispec; 2808 }; 2809 2810 #pragma member_alignment save 2811 #pragma nomember_alignment quadword 2812 struct _srqp { /* VMS self-relative queue entry */ 2813 unsigned long qptr[2]; 2814 }; 2815 #pragma member_alignment restore 2816 static RQE RQE_ZERO = {0,0}; 2817 2818 struct _tochildbuf { 2819 RQE q; 2820 int eof; 2821 unsigned short size; 2822 char *buf; 2823 }; 2824 2825 struct _pipe { 2826 RQE free; 2827 RQE wait; 2828 int fd_out; 2829 unsigned short chan_in; 2830 unsigned short chan_out; 2831 char *buf; 2832 unsigned int bufsize; 2833 IOSB iosb; 2834 IOSB iosb2; 2835 int *pipe_done; 2836 int retry; 2837 int type; 2838 int shut_on_empty; 2839 int need_wake; 2840 pPipe *home; 2841 pInfo info; 2842 pCBuf curr; 2843 pCBuf curr2; 2844 #if defined(PERL_IMPLICIT_CONTEXT) 2845 void *thx; /* Either a thread or an interpreter */ 2846 /* pointer, depending on how we're built */ 2847 #endif 2848 }; 2849 2850 2851 struct pipe_details 2852 { 2853 pInfo next; 2854 PerlIO *fp; /* file pointer to pipe mailbox */ 2855 int useFILE; /* using stdio, not perlio */ 2856 int pid; /* PID of subprocess */ 2857 int mode; /* == 'r' if pipe open for reading */ 2858 int done; /* subprocess has completed */ 2859 int waiting; /* waiting for completion/closure */ 2860 int closing; /* my_pclose is closing this pipe */ 2861 unsigned long completion; /* termination status of subprocess */ 2862 pPipe in; /* pipe in to sub */ 2863 pPipe out; /* pipe out of sub */ 2864 pPipe err; /* pipe of sub's sys$error */ 2865 int in_done; /* true when in pipe finished */ 2866 int out_done; 2867 int err_done; 2868 unsigned short xchan; /* channel to debug xterm */ 2869 unsigned short xchan_valid; /* channel is assigned */ 2870 }; 2871 2872 struct exit_control_block 2873 { 2874 struct exit_control_block *flink; 2875 unsigned long int (*exit_routine)(void); 2876 unsigned long int arg_count; 2877 unsigned long int *status_address; 2878 unsigned long int exit_status; 2879 }; 2880 2881 typedef struct _closed_pipes Xpipe; 2882 typedef struct _closed_pipes* pXpipe; 2883 2884 struct _closed_pipes { 2885 int pid; /* PID of subprocess */ 2886 unsigned long completion; /* termination status of subprocess */ 2887 }; 2888 #define NKEEPCLOSED 50 2889 static Xpipe closed_list[NKEEPCLOSED]; 2890 static int closed_index = 0; 2891 static int closed_num = 0; 2892 2893 #define RETRY_DELAY "0 ::0.20" 2894 #define MAX_RETRY 50 2895 2896 static int pipe_ef = 0; /* first call to safe_popen inits these*/ 2897 static unsigned long mypid; 2898 static unsigned long delaytime[2]; 2899 2900 static pInfo open_pipes = NULL; 2901 static $DESCRIPTOR(nl_desc, "NL:"); 2902 2903 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */ 2904 2905 2906 2907 static unsigned long int 2908 pipe_exit_routine(void) 2909 { 2910 pInfo info; 2911 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT; 2912 int sts, did_stuff, j; 2913 2914 /* 2915 * Flush any pending i/o, but since we are in process run-down, be 2916 * careful about referencing PerlIO structures that may already have 2917 * been deallocated. We may not even have an interpreter anymore. 2918 */ 2919 info = open_pipes; 2920 while (info) { 2921 if (info->fp) { 2922 #if defined(PERL_IMPLICIT_CONTEXT) 2923 /* We need to use the Perl context of the thread that created */ 2924 /* the pipe. */ 2925 pTHX; 2926 if (info->err) 2927 aTHX = info->err->thx; 2928 else if (info->out) 2929 aTHX = info->out->thx; 2930 else if (info->in) 2931 aTHX = info->in->thx; 2932 #endif 2933 if (!info->useFILE 2934 #if defined(USE_ITHREADS) 2935 && my_perl 2936 #endif 2937 #ifdef USE_PERLIO 2938 && PL_perlio_fd_refcnt 2939 #endif 2940 ) 2941 PerlIO_flush(info->fp); 2942 else 2943 fflush((FILE *)info->fp); 2944 } 2945 info = info->next; 2946 } 2947 2948 /* 2949 next we try sending an EOF...ignore if doesn't work, make sure we 2950 don't hang 2951 */ 2952 did_stuff = 0; 2953 info = open_pipes; 2954 2955 while (info) { 2956 _ckvmssts_noperl(sys$setast(0)); 2957 if (info->in && !info->in->shut_on_empty) { 2958 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 2959 0, 0, 0, 0, 0, 0)); 2960 info->waiting = 1; 2961 did_stuff = 1; 2962 } 2963 _ckvmssts_noperl(sys$setast(1)); 2964 info = info->next; 2965 } 2966 2967 /* wait for EOF to have effect, up to ~ 30 sec [default] */ 2968 2969 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 2970 int nwait = 0; 2971 2972 info = open_pipes; 2973 while (info) { 2974 _ckvmssts_noperl(sys$setast(0)); 2975 if (info->waiting && info->done) 2976 info->waiting = 0; 2977 nwait += info->waiting; 2978 _ckvmssts_noperl(sys$setast(1)); 2979 info = info->next; 2980 } 2981 if (!nwait) break; 2982 sleep(1); 2983 } 2984 2985 did_stuff = 0; 2986 info = open_pipes; 2987 while (info) { 2988 _ckvmssts_noperl(sys$setast(0)); 2989 if (!info->done) { /* Tap them gently on the shoulder . . .*/ 2990 sts = sys$forcex(&info->pid,0,&abort); 2991 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 2992 did_stuff = 1; 2993 } 2994 _ckvmssts_noperl(sys$setast(1)); 2995 info = info->next; 2996 } 2997 2998 /* again, wait for effect */ 2999 3000 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 3001 int nwait = 0; 3002 3003 info = open_pipes; 3004 while (info) { 3005 _ckvmssts_noperl(sys$setast(0)); 3006 if (info->waiting && info->done) 3007 info->waiting = 0; 3008 nwait += info->waiting; 3009 _ckvmssts_noperl(sys$setast(1)); 3010 info = info->next; 3011 } 3012 if (!nwait) break; 3013 sleep(1); 3014 } 3015 3016 info = open_pipes; 3017 while (info) { 3018 _ckvmssts_noperl(sys$setast(0)); 3019 if (!info->done) { /* We tried to be nice . . . */ 3020 sts = sys$delprc(&info->pid,0); 3021 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 3022 info->done = 1; /* sys$delprc is as done as we're going to get. */ 3023 } 3024 _ckvmssts_noperl(sys$setast(1)); 3025 info = info->next; 3026 } 3027 3028 while(open_pipes) { 3029 3030 #if defined(PERL_IMPLICIT_CONTEXT) 3031 /* We need to use the Perl context of the thread that created */ 3032 /* the pipe. */ 3033 pTHX; 3034 if (open_pipes->err) 3035 aTHX = open_pipes->err->thx; 3036 else if (open_pipes->out) 3037 aTHX = open_pipes->out->thx; 3038 else if (open_pipes->in) 3039 aTHX = open_pipes->in->thx; 3040 #endif 3041 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno; 3042 else if (!(sts & 1)) retsts = sts; 3043 } 3044 return retsts; 3045 } 3046 3047 static struct exit_control_block pipe_exitblock = 3048 {(struct exit_control_block *) 0, 3049 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0}; 3050 3051 static void pipe_mbxtofd_ast(pPipe p); 3052 static void pipe_tochild1_ast(pPipe p); 3053 static void pipe_tochild2_ast(pPipe p); 3054 3055 static void 3056 popen_completion_ast(pInfo info) 3057 { 3058 pInfo i = open_pipes; 3059 int iss; 3060 3061 info->completion &= 0x0FFFFFFF; /* strip off "control" field */ 3062 closed_list[closed_index].pid = info->pid; 3063 closed_list[closed_index].completion = info->completion; 3064 closed_index++; 3065 if (closed_index == NKEEPCLOSED) 3066 closed_index = 0; 3067 closed_num++; 3068 3069 while (i) { 3070 if (i == info) break; 3071 i = i->next; 3072 } 3073 if (!i) return; /* unlinked, probably freed too */ 3074 3075 info->done = TRUE; 3076 3077 /* 3078 Writing to subprocess ... 3079 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe 3080 3081 chan_out may be waiting for "done" flag, or hung waiting 3082 for i/o completion to child...cancel the i/o. This will 3083 put it into "snarf mode" (done but no EOF yet) that discards 3084 input. 3085 3086 Output from subprocess (stdout, stderr) needs to be flushed and 3087 shut down. We try sending an EOF, but if the mbx is full the pipe 3088 routine should still catch the "shut_on_empty" flag, telling it to 3089 use immediate-style reads so that "mbx empty" -> EOF. 3090 3091 3092 */ 3093 if (info->in && !info->in_done) { /* only for mode=w */ 3094 if (info->in->shut_on_empty && info->in->need_wake) { 3095 info->in->need_wake = FALSE; 3096 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0)); 3097 } else { 3098 _ckvmssts_noperl(sys$cancel(info->in->chan_out)); 3099 } 3100 } 3101 3102 if (info->out && !info->out_done) { /* were we also piping output? */ 3103 info->out->shut_on_empty = TRUE; 3104 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3105 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3106 _ckvmssts_noperl(iss); 3107 } 3108 3109 if (info->err && !info->err_done) { /* we were piping stderr */ 3110 info->err->shut_on_empty = TRUE; 3111 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3112 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3113 _ckvmssts_noperl(iss); 3114 } 3115 _ckvmssts_noperl(sys$setef(pipe_ef)); 3116 3117 } 3118 3119 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd); 3120 static void vms_execfree(struct dsc$descriptor_s *vmscmd); 3121 static void pipe_infromchild_ast(pPipe p); 3122 3123 /* 3124 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate 3125 inside an AST routine without worrying about reentrancy and which Perl 3126 memory allocator is being used. 3127 3128 We read data and queue up the buffers, then spit them out one at a 3129 time to the output mailbox when the output mailbox is ready for one. 3130 3131 */ 3132 #define INITIAL_TOCHILDQUEUE 2 3133 3134 static pPipe 3135 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx) 3136 { 3137 pPipe p; 3138 pCBuf b; 3139 char mbx1[64], mbx2[64]; 3140 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3141 DSC$K_CLASS_S, mbx1}, 3142 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3143 DSC$K_CLASS_S, mbx2}; 3144 unsigned int dviitm = DVI$_DEVBUFSIZ; 3145 int j, n; 3146 3147 n = sizeof(Pipe); 3148 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3149 3150 create_mbx(&p->chan_in , &d_mbx1); 3151 create_mbx(&p->chan_out, &d_mbx2); 3152 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3153 3154 p->buf = 0; 3155 p->shut_on_empty = FALSE; 3156 p->need_wake = FALSE; 3157 p->type = 0; 3158 p->retry = 0; 3159 p->iosb.status = SS$_NORMAL; 3160 p->iosb2.status = SS$_NORMAL; 3161 p->free = RQE_ZERO; 3162 p->wait = RQE_ZERO; 3163 p->curr = 0; 3164 p->curr2 = 0; 3165 p->info = 0; 3166 #ifdef PERL_IMPLICIT_CONTEXT 3167 p->thx = aTHX; 3168 #endif 3169 3170 n = sizeof(CBuf) + p->bufsize; 3171 3172 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) { 3173 _ckvmssts_noperl(lib$get_vm(&n, &b)); 3174 b->buf = (char *) b + sizeof(CBuf); 3175 _ckvmssts_noperl(lib$insqhi(b, &p->free)); 3176 } 3177 3178 pipe_tochild2_ast(p); 3179 pipe_tochild1_ast(p); 3180 strcpy(wmbx, mbx1); 3181 strcpy(rmbx, mbx2); 3182 return p; 3183 } 3184 3185 /* reads the MBX Perl is writing, and queues */ 3186 3187 static void 3188 pipe_tochild1_ast(pPipe p) 3189 { 3190 pCBuf b = p->curr; 3191 int iss = p->iosb.status; 3192 int eof = (iss == SS$_ENDOFFILE); 3193 int sts; 3194 #ifdef PERL_IMPLICIT_CONTEXT 3195 pTHX = p->thx; 3196 #endif 3197 3198 if (p->retry) { 3199 if (eof) { 3200 p->shut_on_empty = TRUE; 3201 b->eof = TRUE; 3202 _ckvmssts_noperl(sys$dassgn(p->chan_in)); 3203 } else { 3204 _ckvmssts_noperl(iss); 3205 } 3206 3207 b->eof = eof; 3208 b->size = p->iosb.count; 3209 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait)); 3210 if (p->need_wake) { 3211 p->need_wake = FALSE; 3212 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0)); 3213 } 3214 } else { 3215 p->retry = 1; /* initial call */ 3216 } 3217 3218 if (eof) { /* flush the free queue, return when done */ 3219 int n = sizeof(CBuf) + p->bufsize; 3220 while (1) { 3221 iss = lib$remqti(&p->free, &b); 3222 if (iss == LIB$_QUEWASEMP) return; 3223 _ckvmssts_noperl(iss); 3224 _ckvmssts_noperl(lib$free_vm(&n, &b)); 3225 } 3226 } 3227 3228 iss = lib$remqti(&p->free, &b); 3229 if (iss == LIB$_QUEWASEMP) { 3230 int n = sizeof(CBuf) + p->bufsize; 3231 _ckvmssts_noperl(lib$get_vm(&n, &b)); 3232 b->buf = (char *) b + sizeof(CBuf); 3233 } else { 3234 _ckvmssts_noperl(iss); 3235 } 3236 3237 p->curr = b; 3238 iss = sys$qio(0,p->chan_in, 3239 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0), 3240 &p->iosb, 3241 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0); 3242 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL; 3243 _ckvmssts_noperl(iss); 3244 } 3245 3246 3247 /* writes queued buffers to output, waits for each to complete before 3248 doing the next */ 3249 3250 static void 3251 pipe_tochild2_ast(pPipe p) 3252 { 3253 pCBuf b = p->curr2; 3254 int iss = p->iosb2.status; 3255 int n = sizeof(CBuf) + p->bufsize; 3256 int done = (p->info && p->info->done) || 3257 iss == SS$_CANCEL || iss == SS$_ABORT; 3258 #if defined(PERL_IMPLICIT_CONTEXT) 3259 pTHX = p->thx; 3260 #endif 3261 3262 do { 3263 if (p->type) { /* type=1 has old buffer, dispose */ 3264 if (p->shut_on_empty) { 3265 _ckvmssts_noperl(lib$free_vm(&n, &b)); 3266 } else { 3267 _ckvmssts_noperl(lib$insqhi(b, &p->free)); 3268 } 3269 p->type = 0; 3270 } 3271 3272 iss = lib$remqti(&p->wait, &b); 3273 if (iss == LIB$_QUEWASEMP) { 3274 if (p->shut_on_empty) { 3275 if (done) { 3276 _ckvmssts_noperl(sys$dassgn(p->chan_out)); 3277 *p->pipe_done = TRUE; 3278 _ckvmssts_noperl(sys$setef(pipe_ef)); 3279 } else { 3280 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, 3281 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3282 } 3283 return; 3284 } 3285 p->need_wake = TRUE; 3286 return; 3287 } 3288 _ckvmssts_noperl(iss); 3289 p->type = 1; 3290 } while (done); 3291 3292 3293 p->curr2 = b; 3294 if (b->eof) { 3295 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, 3296 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3297 } else { 3298 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK, 3299 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0)); 3300 } 3301 3302 return; 3303 3304 } 3305 3306 3307 static pPipe 3308 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx) 3309 { 3310 pPipe p; 3311 char mbx1[64], mbx2[64]; 3312 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3313 DSC$K_CLASS_S, mbx1}, 3314 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3315 DSC$K_CLASS_S, mbx2}; 3316 unsigned int dviitm = DVI$_DEVBUFSIZ; 3317 3318 int n = sizeof(Pipe); 3319 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3320 create_mbx(&p->chan_in , &d_mbx1); 3321 create_mbx(&p->chan_out, &d_mbx2); 3322 3323 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3324 n = p->bufsize * sizeof(char); 3325 _ckvmssts_noperl(lib$get_vm(&n, &p->buf)); 3326 p->shut_on_empty = FALSE; 3327 p->info = 0; 3328 p->type = 0; 3329 p->iosb.status = SS$_NORMAL; 3330 #if defined(PERL_IMPLICIT_CONTEXT) 3331 p->thx = aTHX; 3332 #endif 3333 pipe_infromchild_ast(p); 3334 3335 strcpy(wmbx, mbx1); 3336 strcpy(rmbx, mbx2); 3337 return p; 3338 } 3339 3340 static void 3341 pipe_infromchild_ast(pPipe p) 3342 { 3343 int iss = p->iosb.status; 3344 int eof = (iss == SS$_ENDOFFILE); 3345 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0)); 3346 int kideof = (eof && (p->iosb.dvispec == p->info->pid)); 3347 #if defined(PERL_IMPLICIT_CONTEXT) 3348 pTHX = p->thx; 3349 #endif 3350 3351 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */ 3352 _ckvmssts_noperl(sys$dassgn(p->chan_out)); 3353 p->chan_out = 0; 3354 } 3355 3356 /* read completed: 3357 input shutdown if EOF from self (done or shut_on_empty) 3358 output shutdown if closing flag set (my_pclose) 3359 send data/eof from child or eof from self 3360 otherwise, re-read (snarf of data from child) 3361 */ 3362 3363 if (p->type == 1) { 3364 p->type = 0; 3365 if (myeof && p->chan_in) { /* input shutdown */ 3366 _ckvmssts_noperl(sys$dassgn(p->chan_in)); 3367 p->chan_in = 0; 3368 } 3369 3370 if (p->chan_out) { 3371 if (myeof || kideof) { /* pass EOF to parent */ 3372 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb, 3373 pipe_infromchild_ast, p, 3374 0, 0, 0, 0, 0, 0)); 3375 return; 3376 } else if (eof) { /* eat EOF --- fall through to read*/ 3377 3378 } else { /* transmit data */ 3379 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb, 3380 pipe_infromchild_ast,p, 3381 p->buf, p->iosb.count, 0, 0, 0, 0)); 3382 return; 3383 } 3384 } 3385 } 3386 3387 /* everything shut? flag as done */ 3388 3389 if (!p->chan_in && !p->chan_out) { 3390 *p->pipe_done = TRUE; 3391 _ckvmssts_noperl(sys$setef(pipe_ef)); 3392 return; 3393 } 3394 3395 /* write completed (or read, if snarfing from child) 3396 if still have input active, 3397 queue read...immediate mode if shut_on_empty so we get EOF if empty 3398 otherwise, 3399 check if Perl reading, generate EOFs as needed 3400 */ 3401 3402 if (p->type == 0) { 3403 p->type = 1; 3404 if (p->chan_in) { 3405 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb, 3406 pipe_infromchild_ast,p, 3407 p->buf, p->bufsize, 0, 0, 0, 0); 3408 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL; 3409 _ckvmssts_noperl(iss); 3410 } else { /* send EOFs for extra reads */ 3411 p->iosb.status = SS$_ENDOFFILE; 3412 p->iosb.dvispec = 0; 3413 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN, 3414 0, 0, 0, 3415 pipe_infromchild_ast, p, 0, 0, 0, 0)); 3416 } 3417 } 3418 } 3419 3420 static pPipe 3421 pipe_mbxtofd_setup(pTHX_ int fd, char *out) 3422 { 3423 pPipe p; 3424 char mbx[64]; 3425 unsigned long dviitm = DVI$_DEVBUFSIZ; 3426 struct stat s; 3427 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T, 3428 DSC$K_CLASS_S, mbx}; 3429 int n = sizeof(Pipe); 3430 3431 /* things like terminals and mbx's don't need this filter */ 3432 if (fd && fstat(fd,&s) == 0) { 3433 unsigned long devchar; 3434 char device[65]; 3435 unsigned short dev_len; 3436 struct dsc$descriptor_s d_dev; 3437 char * cptr; 3438 struct item_list_3 items[3]; 3439 int status; 3440 unsigned short dvi_iosb[4]; 3441 3442 cptr = getname(fd, out, 1); 3443 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV); 3444 d_dev.dsc$a_pointer = out; 3445 d_dev.dsc$w_length = strlen(out); 3446 d_dev.dsc$b_dtype = DSC$K_DTYPE_T; 3447 d_dev.dsc$b_class = DSC$K_CLASS_S; 3448 3449 items[0].len = 4; 3450 items[0].code = DVI$_DEVCHAR; 3451 items[0].bufadr = &devchar; 3452 items[0].retadr = NULL; 3453 items[1].len = 64; 3454 items[1].code = DVI$_FULLDEVNAM; 3455 items[1].bufadr = device; 3456 items[1].retadr = &dev_len; 3457 items[2].len = 0; 3458 items[2].code = 0; 3459 3460 status = sys$getdviw 3461 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL); 3462 _ckvmssts_noperl(status); 3463 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) { 3464 device[dev_len] = 0; 3465 3466 if (!(devchar & DEV$M_DIR)) { 3467 strcpy(out, device); 3468 return 0; 3469 } 3470 } 3471 } 3472 3473 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3474 p->fd_out = dup(fd); 3475 create_mbx(&p->chan_in, &d_mbx); 3476 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3477 n = (p->bufsize+1) * sizeof(char); 3478 _ckvmssts_noperl(lib$get_vm(&n, &p->buf)); 3479 p->shut_on_empty = FALSE; 3480 p->retry = 0; 3481 p->info = 0; 3482 strcpy(out, mbx); 3483 3484 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb, 3485 pipe_mbxtofd_ast, p, 3486 p->buf, p->bufsize, 0, 0, 0, 0)); 3487 3488 return p; 3489 } 3490 3491 static void 3492 pipe_mbxtofd_ast(pPipe p) 3493 { 3494 int iss = p->iosb.status; 3495 int done = p->info->done; 3496 int iss2; 3497 int eof = (iss == SS$_ENDOFFILE); 3498 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0)); 3499 int err = !(iss&1) && !eof; 3500 #if defined(PERL_IMPLICIT_CONTEXT) 3501 pTHX = p->thx; 3502 #endif 3503 3504 if (done && myeof) { /* end piping */ 3505 close(p->fd_out); 3506 sys$dassgn(p->chan_in); 3507 *p->pipe_done = TRUE; 3508 _ckvmssts_noperl(sys$setef(pipe_ef)); 3509 return; 3510 } 3511 3512 if (!err && !eof) { /* good data to send to file */ 3513 p->buf[p->iosb.count] = '\n'; 3514 iss2 = write(p->fd_out, p->buf, p->iosb.count+1); 3515 if (iss2 < 0) { 3516 p->retry++; 3517 if (p->retry < MAX_RETRY) { 3518 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p)); 3519 return; 3520 } 3521 } 3522 p->retry = 0; 3523 } else if (err) { 3524 _ckvmssts_noperl(iss); 3525 } 3526 3527 3528 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb, 3529 pipe_mbxtofd_ast, p, 3530 p->buf, p->bufsize, 0, 0, 0, 0); 3531 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL; 3532 _ckvmssts_noperl(iss); 3533 } 3534 3535 3536 typedef struct _pipeloc PLOC; 3537 typedef struct _pipeloc* pPLOC; 3538 3539 struct _pipeloc { 3540 pPLOC next; 3541 char dir[NAM$C_MAXRSS+1]; 3542 }; 3543 static pPLOC head_PLOC = 0; 3544 3545 void 3546 free_pipelocs(pTHX_ void *head) 3547 { 3548 pPLOC p, pnext; 3549 pPLOC *pHead = (pPLOC *)head; 3550 3551 p = *pHead; 3552 while (p) { 3553 pnext = p->next; 3554 PerlMem_free(p); 3555 p = pnext; 3556 } 3557 *pHead = 0; 3558 } 3559 3560 static void 3561 store_pipelocs(pTHX) 3562 { 3563 int i; 3564 pPLOC p; 3565 AV *av = 0; 3566 SV *dirsv; 3567 char *dir, *x; 3568 char *unixdir; 3569 char temp[NAM$C_MAXRSS+1]; 3570 STRLEN n_a; 3571 3572 if (head_PLOC) 3573 free_pipelocs(aTHX_ &head_PLOC); 3574 3575 /* the . directory from @INC comes last */ 3576 3577 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3578 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3579 p->next = head_PLOC; 3580 head_PLOC = p; 3581 strcpy(p->dir,"./"); 3582 3583 /* get the directory from $^X */ 3584 3585 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS); 3586 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3587 3588 #ifdef PERL_IMPLICIT_CONTEXT 3589 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3590 #else 3591 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3592 #endif 3593 my_strlcpy(temp, PL_origargv[0], sizeof(temp)); 3594 x = strrchr(temp,']'); 3595 if (x == NULL) { 3596 x = strrchr(temp,'>'); 3597 if (x == NULL) { 3598 /* It could be a UNIX path */ 3599 x = strrchr(temp,'/'); 3600 } 3601 } 3602 if (x) 3603 x[1] = '\0'; 3604 else { 3605 /* Got a bare name, so use default directory */ 3606 temp[0] = '.'; 3607 temp[1] = '\0'; 3608 } 3609 3610 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) { 3611 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3612 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3613 p->next = head_PLOC; 3614 head_PLOC = p; 3615 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3616 } 3617 } 3618 3619 /* reverse order of @INC entries, skip "." since entered above */ 3620 3621 #ifdef PERL_IMPLICIT_CONTEXT 3622 if (aTHX) 3623 #endif 3624 if (PL_incgv) av = GvAVn(PL_incgv); 3625 3626 for (i = 0; av && i <= AvFILL(av); i++) { 3627 dirsv = *av_fetch(av,i,TRUE); 3628 3629 if (SvROK(dirsv)) continue; 3630 dir = SvPVx(dirsv,n_a); 3631 if (strcmp(dir,".") == 0) continue; 3632 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL) 3633 continue; 3634 3635 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3636 p->next = head_PLOC; 3637 head_PLOC = p; 3638 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3639 } 3640 3641 /* most likely spot (ARCHLIB) put first in the list */ 3642 3643 #ifdef ARCHLIB_EXP 3644 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) { 3645 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3646 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3647 p->next = head_PLOC; 3648 head_PLOC = p; 3649 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3650 } 3651 #endif 3652 PerlMem_free(unixdir); 3653 } 3654 3655 static I32 3656 Perl_cando_by_name_int 3657 (pTHX_ I32 bit, bool effective, const char *fname, int opts); 3658 #if !defined(PERL_IMPLICIT_CONTEXT) 3659 #define cando_by_name_int Perl_cando_by_name_int 3660 #else 3661 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d) 3662 #endif 3663 3664 static char * 3665 find_vmspipe(pTHX) 3666 { 3667 static int vmspipe_file_status = 0; 3668 static char vmspipe_file[NAM$C_MAXRSS+1]; 3669 3670 /* already found? Check and use ... need read+execute permission */ 3671 3672 if (vmspipe_file_status == 1) { 3673 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3674 && cando_by_name_int 3675 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3676 return vmspipe_file; 3677 } 3678 vmspipe_file_status = 0; 3679 } 3680 3681 /* scan through stored @INC, $^X */ 3682 3683 if (vmspipe_file_status == 0) { 3684 char file[NAM$C_MAXRSS+1]; 3685 pPLOC p = head_PLOC; 3686 3687 while (p) { 3688 char * exp_res; 3689 int dirlen; 3690 dirlen = my_strlcpy(file, p->dir, sizeof(file)); 3691 my_strlcat(file, "vmspipe.com", sizeof(file)); 3692 p = p->next; 3693 3694 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0); 3695 if (!exp_res) continue; 3696 3697 if (cando_by_name_int 3698 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3699 && cando_by_name_int 3700 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3701 vmspipe_file_status = 1; 3702 return vmspipe_file; 3703 } 3704 } 3705 vmspipe_file_status = -1; /* failed, use tempfiles */ 3706 } 3707 3708 return 0; 3709 } 3710 3711 static FILE * 3712 vmspipe_tempfile(pTHX) 3713 { 3714 char file[NAM$C_MAXRSS+1]; 3715 FILE *fp; 3716 static int index = 0; 3717 Stat_t s0, s1; 3718 int cmp_result; 3719 3720 /* create a tempfile */ 3721 3722 /* we can't go from W, shr=get to R, shr=get without 3723 an intermediate vulnerable state, so don't bother trying... 3724 3725 and lib$spawn doesn't shr=put, so have to close the write 3726 3727 So... match up the creation date/time and the FID to 3728 make sure we're dealing with the same file 3729 3730 */ 3731 3732 index++; 3733 if (!decc_filename_unix_only) { 3734 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index); 3735 fp = fopen(file,"w"); 3736 if (!fp) { 3737 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index); 3738 fp = fopen(file,"w"); 3739 if (!fp) { 3740 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index); 3741 fp = fopen(file,"w"); 3742 } 3743 } 3744 } 3745 else { 3746 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index); 3747 fp = fopen(file,"w"); 3748 if (!fp) { 3749 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index); 3750 fp = fopen(file,"w"); 3751 if (!fp) { 3752 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index); 3753 fp = fopen(file,"w"); 3754 } 3755 } 3756 } 3757 if (!fp) return 0; /* we're hosed */ 3758 3759 fprintf(fp,"$! 'f$verify(0)'\n"); 3760 fprintf(fp,"$! --- protect against nonstandard definitions ---\n"); 3761 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n"); 3762 fprintf(fp,"$ perl_define = \"define/nolog\"\n"); 3763 fprintf(fp,"$ perl_on = \"set noon\"\n"); 3764 fprintf(fp,"$ perl_exit = \"exit\"\n"); 3765 fprintf(fp,"$ perl_del = \"delete\"\n"); 3766 fprintf(fp,"$ pif = \"if\"\n"); 3767 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n"); 3768 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n"); 3769 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n"); 3770 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n"); 3771 fprintf(fp,"$! --- build command line to get max possible length\n"); 3772 fprintf(fp,"$c=perl_popen_cmd0\n"); 3773 fprintf(fp,"$c=c+perl_popen_cmd1\n"); 3774 fprintf(fp,"$c=c+perl_popen_cmd2\n"); 3775 fprintf(fp,"$x=perl_popen_cmd3\n"); 3776 fprintf(fp,"$c=c+x\n"); 3777 fprintf(fp,"$ perl_on\n"); 3778 fprintf(fp,"$ 'c'\n"); 3779 fprintf(fp,"$ perl_status = $STATUS\n"); 3780 fprintf(fp,"$ perl_del 'perl_cfile'\n"); 3781 fprintf(fp,"$ perl_exit 'perl_status'\n"); 3782 fsync(fileno(fp)); 3783 3784 fgetname(fp, file, 1); 3785 fstat(fileno(fp), &s0.crtl_stat); 3786 fclose(fp); 3787 3788 if (decc_filename_unix_only) 3789 int_tounixspec(file, file, NULL); 3790 fp = fopen(file,"r","shr=get"); 3791 if (!fp) return 0; 3792 fstat(fileno(fp), &s1.crtl_stat); 3793 3794 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino); 3795 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) { 3796 fclose(fp); 3797 return 0; 3798 } 3799 3800 return fp; 3801 } 3802 3803 3804 static int vms_is_syscommand_xterm(void) 3805 { 3806 const static struct dsc$descriptor_s syscommand_dsc = 3807 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" }; 3808 3809 const static struct dsc$descriptor_s decwdisplay_dsc = 3810 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" }; 3811 3812 struct item_list_3 items[2]; 3813 unsigned short dvi_iosb[4]; 3814 unsigned long devchar; 3815 unsigned long devclass; 3816 int status; 3817 3818 /* Very simple check to guess if sys$command is a decterm? */ 3819 /* First see if the DECW$DISPLAY: device exists */ 3820 items[0].len = 4; 3821 items[0].code = DVI$_DEVCHAR; 3822 items[0].bufadr = &devchar; 3823 items[0].retadr = NULL; 3824 items[1].len = 0; 3825 items[1].code = 0; 3826 3827 status = sys$getdviw 3828 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL); 3829 3830 if ($VMS_STATUS_SUCCESS(status)) { 3831 status = dvi_iosb[0]; 3832 } 3833 3834 if (!$VMS_STATUS_SUCCESS(status)) { 3835 SETERRNO(EVMSERR, status); 3836 return -1; 3837 } 3838 3839 /* If it does, then for now assume that we are on a workstation */ 3840 /* Now verify that SYS$COMMAND is a terminal */ 3841 /* for creating the debugger DECTerm */ 3842 3843 items[0].len = 4; 3844 items[0].code = DVI$_DEVCLASS; 3845 items[0].bufadr = &devclass; 3846 items[0].retadr = NULL; 3847 items[1].len = 0; 3848 items[1].code = 0; 3849 3850 status = sys$getdviw 3851 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL); 3852 3853 if ($VMS_STATUS_SUCCESS(status)) { 3854 status = dvi_iosb[0]; 3855 } 3856 3857 if (!$VMS_STATUS_SUCCESS(status)) { 3858 SETERRNO(EVMSERR, status); 3859 return -1; 3860 } 3861 else { 3862 if (devclass == DC$_TERM) { 3863 return 0; 3864 } 3865 } 3866 return -1; 3867 } 3868 3869 /* If we are on a DECTerm, we can pretend to fork xterms when requested */ 3870 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) 3871 { 3872 int status; 3873 int ret_stat; 3874 char * ret_char; 3875 char device_name[65]; 3876 unsigned short device_name_len; 3877 struct dsc$descriptor_s customization_dsc; 3878 struct dsc$descriptor_s device_name_dsc; 3879 const char * cptr; 3880 char customization[200]; 3881 char title[40]; 3882 pInfo info = NULL; 3883 char mbx1[64]; 3884 unsigned short p_chan; 3885 int n; 3886 unsigned short iosb[4]; 3887 const char * cust_str = 3888 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n"; 3889 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3890 DSC$K_CLASS_S, mbx1}; 3891 3892 /* LIB$FIND_IMAGE_SIGNAL needs a handler */ 3893 /*---------------------------------------*/ 3894 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret); 3895 3896 3897 /* Make sure that this is from the Perl debugger */ 3898 ret_char = strstr(cmd," xterm "); 3899 if (ret_char == NULL) 3900 return NULL; 3901 cptr = ret_char + 7; 3902 ret_char = strstr(cmd,"tty"); 3903 if (ret_char == NULL) 3904 return NULL; 3905 ret_char = strstr(cmd,"sleep"); 3906 if (ret_char == NULL) 3907 return NULL; 3908 3909 if (decw_term_port == 0) { 3910 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12"); 3911 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR"); 3912 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT"); 3913 3914 status = lib$find_image_symbol 3915 (&filename1_dsc, 3916 &decw_term_port_dsc, 3917 (void *)&decw_term_port, 3918 NULL, 3919 0); 3920 3921 /* Try again with the other image name */ 3922 if (!$VMS_STATUS_SUCCESS(status)) { 3923 3924 status = lib$find_image_symbol 3925 (&filename2_dsc, 3926 &decw_term_port_dsc, 3927 (void *)&decw_term_port, 3928 NULL, 3929 0); 3930 3931 } 3932 3933 } 3934 3935 3936 /* No decw$term_port, give it up */ 3937 if (!$VMS_STATUS_SUCCESS(status)) 3938 return NULL; 3939 3940 /* Are we on a workstation? */ 3941 /* to do: capture the rows / columns and pass their properties */ 3942 ret_stat = vms_is_syscommand_xterm(); 3943 if (ret_stat < 0) 3944 return NULL; 3945 3946 /* Make the title: */ 3947 ret_char = strstr(cptr,"-title"); 3948 if (ret_char != NULL) { 3949 while ((*cptr != 0) && (*cptr != '\"')) { 3950 cptr++; 3951 } 3952 if (*cptr == '\"') 3953 cptr++; 3954 n = 0; 3955 while ((*cptr != 0) && (*cptr != '\"')) { 3956 title[n] = *cptr; 3957 n++; 3958 if (n == 39) { 3959 title[39] = 0; 3960 break; 3961 } 3962 cptr++; 3963 } 3964 title[n] = 0; 3965 } 3966 else { 3967 /* Default title */ 3968 strcpy(title,"Perl Debug DECTerm"); 3969 } 3970 sprintf(customization, cust_str, title); 3971 3972 customization_dsc.dsc$a_pointer = customization; 3973 customization_dsc.dsc$w_length = strlen(customization); 3974 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 3975 customization_dsc.dsc$b_class = DSC$K_CLASS_S; 3976 3977 device_name_dsc.dsc$a_pointer = device_name; 3978 device_name_dsc.dsc$w_length = sizeof device_name -1; 3979 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 3980 device_name_dsc.dsc$b_class = DSC$K_CLASS_S; 3981 3982 device_name_len = 0; 3983 3984 /* Try to create the window */ 3985 status = (*decw_term_port) 3986 (NULL, 3987 NULL, 3988 &customization_dsc, 3989 &device_name_dsc, 3990 &device_name_len, 3991 NULL, 3992 NULL, 3993 NULL); 3994 if (!$VMS_STATUS_SUCCESS(status)) { 3995 SETERRNO(EVMSERR, status); 3996 return NULL; 3997 } 3998 3999 device_name[device_name_len] = '\0'; 4000 4001 /* Need to set this up to look like a pipe for cleanup */ 4002 n = sizeof(Info); 4003 status = lib$get_vm(&n, &info); 4004 if (!$VMS_STATUS_SUCCESS(status)) { 4005 SETERRNO(ENOMEM, status); 4006 return NULL; 4007 } 4008 4009 info->mode = *mode; 4010 info->done = FALSE; 4011 info->completion = 0; 4012 info->closing = FALSE; 4013 info->in = 0; 4014 info->out = 0; 4015 info->err = 0; 4016 info->fp = NULL; 4017 info->useFILE = 0; 4018 info->waiting = 0; 4019 info->in_done = TRUE; 4020 info->out_done = TRUE; 4021 info->err_done = TRUE; 4022 4023 /* Assign a channel on this so that it will persist, and not login */ 4024 /* We stash this channel in the info structure for reference. */ 4025 /* The created xterm self destructs when the last channel is removed */ 4026 /* and it appears that perl5db.pl (perl debugger) does this routinely */ 4027 /* So leave this assigned. */ 4028 device_name_dsc.dsc$w_length = device_name_len; 4029 status = sys$assign(&device_name_dsc,&info->xchan,0,0); 4030 if (!$VMS_STATUS_SUCCESS(status)) { 4031 SETERRNO(EVMSERR, status); 4032 return NULL; 4033 } 4034 info->xchan_valid = 1; 4035 4036 /* Now create a mailbox to be read by the application */ 4037 4038 create_mbx(&p_chan, &d_mbx1); 4039 4040 /* write the name of the created terminal to the mailbox */ 4041 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW, 4042 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0); 4043 4044 if (!$VMS_STATUS_SUCCESS(status)) { 4045 SETERRNO(EVMSERR, status); 4046 return NULL; 4047 } 4048 4049 info->fp = PerlIO_open(mbx1, mode); 4050 4051 /* Done with this channel */ 4052 sys$dassgn(p_chan); 4053 4054 /* If any errors, then clean up */ 4055 if (!info->fp) { 4056 n = sizeof(Info); 4057 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4058 return NULL; 4059 } 4060 4061 /* All done */ 4062 return info->fp; 4063 } 4064 4065 static I32 my_pclose_pinfo(pTHX_ pInfo info); 4066 4067 static PerlIO * 4068 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) 4069 { 4070 static int handler_set_up = FALSE; 4071 PerlIO * ret_fp; 4072 unsigned long int sts, flags = CLI$M_NOWAIT; 4073 /* The use of a GLOBAL table (as was done previously) rendered 4074 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL 4075 * environment. Hence we've switched to LOCAL symbol table. 4076 */ 4077 unsigned int table = LIB$K_CLI_LOCAL_SYM; 4078 int j, wait = 0, n; 4079 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe; 4080 char *in, *out, *err, mbx[512]; 4081 FILE *tpipe = 0; 4082 char tfilebuf[NAM$C_MAXRSS+1]; 4083 pInfo info = NULL; 4084 char cmd_sym_name[20]; 4085 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T, 4086 DSC$K_CLASS_S, symbol}; 4087 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T, 4088 DSC$K_CLASS_S, 0}; 4089 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T, 4090 DSC$K_CLASS_S, cmd_sym_name}; 4091 struct dsc$descriptor_s *vmscmd; 4092 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN"); 4093 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT"); 4094 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR"); 4095 4096 /* Check here for Xterm create request. This means looking for 4097 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it 4098 * is possible to create an xterm. 4099 */ 4100 if (*in_mode == 'r') { 4101 PerlIO * xterm_fd; 4102 4103 #if defined(PERL_IMPLICIT_CONTEXT) 4104 /* Can not fork an xterm with a NULL context */ 4105 /* This probably could never happen */ 4106 xterm_fd = NULL; 4107 if (aTHX != NULL) 4108 #endif 4109 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode); 4110 if (xterm_fd != NULL) 4111 return xterm_fd; 4112 } 4113 4114 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */ 4115 4116 /* once-per-program initialization... 4117 note that the SETAST calls and the dual test of pipe_ef 4118 makes sure that only the FIRST thread through here does 4119 the initialization...all other threads wait until it's 4120 done. 4121 4122 Yeah, uglier than a pthread call, it's got all the stuff inline 4123 rather than in a separate routine. 4124 */ 4125 4126 if (!pipe_ef) { 4127 _ckvmssts_noperl(sys$setast(0)); 4128 if (!pipe_ef) { 4129 unsigned long int pidcode = JPI$_PID; 4130 $DESCRIPTOR(d_delay, RETRY_DELAY); 4131 _ckvmssts_noperl(lib$get_ef(&pipe_ef)); 4132 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4133 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime)); 4134 } 4135 if (!handler_set_up) { 4136 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock)); 4137 handler_set_up = TRUE; 4138 } 4139 _ckvmssts_noperl(sys$setast(1)); 4140 } 4141 4142 /* see if we can find a VMSPIPE.COM */ 4143 4144 tfilebuf[0] = '@'; 4145 vmspipe = find_vmspipe(aTHX); 4146 if (vmspipe) { 4147 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1; 4148 } else { /* uh, oh...we're in tempfile hell */ 4149 tpipe = vmspipe_tempfile(aTHX); 4150 if (!tpipe) { /* a fish popular in Boston */ 4151 if (ckWARN(WARN_PIPE)) { 4152 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping"); 4153 } 4154 return NULL; 4155 } 4156 fgetname(tpipe,tfilebuf+1,1); 4157 vmspipedsc.dsc$w_length = strlen(tfilebuf); 4158 } 4159 vmspipedsc.dsc$a_pointer = tfilebuf; 4160 4161 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd); 4162 if (!(sts & 1)) { 4163 switch (sts) { 4164 case RMS$_FNF: case RMS$_DNF: 4165 set_errno(ENOENT); break; 4166 case RMS$_DIR: 4167 set_errno(ENOTDIR); break; 4168 case RMS$_DEV: 4169 set_errno(ENODEV); break; 4170 case RMS$_PRV: 4171 set_errno(EACCES); break; 4172 case RMS$_SYN: 4173 set_errno(EINVAL); break; 4174 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 4175 set_errno(E2BIG); break; 4176 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 4177 _ckvmssts_noperl(sts); /* fall through */ 4178 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 4179 set_errno(EVMSERR); 4180 } 4181 set_vaxc_errno(sts); 4182 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) { 4183 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno)); 4184 } 4185 *psts = sts; 4186 return NULL; 4187 } 4188 n = sizeof(Info); 4189 _ckvmssts_noperl(lib$get_vm(&n, &info)); 4190 4191 my_strlcpy(mode, in_mode, sizeof(mode)); 4192 info->mode = *mode; 4193 info->done = FALSE; 4194 info->completion = 0; 4195 info->closing = FALSE; 4196 info->in = 0; 4197 info->out = 0; 4198 info->err = 0; 4199 info->fp = NULL; 4200 info->useFILE = 0; 4201 info->waiting = 0; 4202 info->in_done = TRUE; 4203 info->out_done = TRUE; 4204 info->err_done = TRUE; 4205 info->xchan = 0; 4206 info->xchan_valid = 0; 4207 4208 in = (char *)PerlMem_malloc(VMS_MAXRSS); 4209 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4210 out = (char *)PerlMem_malloc(VMS_MAXRSS); 4211 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4212 err = (char *)PerlMem_malloc(VMS_MAXRSS); 4213 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4214 4215 in[0] = out[0] = err[0] = '\0'; 4216 4217 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */ 4218 info->useFILE = 1; 4219 strcpy(p,p+1); 4220 } 4221 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */ 4222 wait = 1; 4223 strcpy(p,p+1); 4224 } 4225 4226 if (*mode == 'r') { /* piping from subroutine */ 4227 4228 info->out = pipe_infromchild_setup(aTHX_ mbx,out); 4229 if (info->out) { 4230 info->out->pipe_done = &info->out_done; 4231 info->out_done = FALSE; 4232 info->out->info = info; 4233 } 4234 if (!info->useFILE) { 4235 info->fp = PerlIO_open(mbx, mode); 4236 } else { 4237 info->fp = (PerlIO *) freopen(mbx, mode, stdin); 4238 vmssetuserlnm("SYS$INPUT", mbx); 4239 } 4240 4241 if (!info->fp && info->out) { 4242 sys$cancel(info->out->chan_out); 4243 4244 while (!info->out_done) { 4245 int done; 4246 _ckvmssts_noperl(sys$setast(0)); 4247 done = info->out_done; 4248 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4249 _ckvmssts_noperl(sys$setast(1)); 4250 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4251 } 4252 4253 if (info->out->buf) { 4254 n = info->out->bufsize * sizeof(char); 4255 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf)); 4256 } 4257 n = sizeof(Pipe); 4258 _ckvmssts_noperl(lib$free_vm(&n, &info->out)); 4259 n = sizeof(Info); 4260 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4261 *psts = RMS$_FNF; 4262 return NULL; 4263 } 4264 4265 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4266 if (info->err) { 4267 info->err->pipe_done = &info->err_done; 4268 info->err_done = FALSE; 4269 info->err->info = info; 4270 } 4271 4272 } else if (*mode == 'w') { /* piping to subroutine */ 4273 4274 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4275 if (info->out) { 4276 info->out->pipe_done = &info->out_done; 4277 info->out_done = FALSE; 4278 info->out->info = info; 4279 } 4280 4281 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4282 if (info->err) { 4283 info->err->pipe_done = &info->err_done; 4284 info->err_done = FALSE; 4285 info->err->info = info; 4286 } 4287 4288 info->in = pipe_tochild_setup(aTHX_ in,mbx); 4289 if (!info->useFILE) { 4290 info->fp = PerlIO_open(mbx, mode); 4291 } else { 4292 info->fp = (PerlIO *) freopen(mbx, mode, stdout); 4293 vmssetuserlnm("SYS$OUTPUT", mbx); 4294 } 4295 4296 if (info->in) { 4297 info->in->pipe_done = &info->in_done; 4298 info->in_done = FALSE; 4299 info->in->info = info; 4300 } 4301 4302 /* error cleanup */ 4303 if (!info->fp && info->in) { 4304 info->done = TRUE; 4305 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0, 4306 0, 0, 0, 0, 0, 0, 0, 0)); 4307 4308 while (!info->in_done) { 4309 int done; 4310 _ckvmssts_noperl(sys$setast(0)); 4311 done = info->in_done; 4312 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4313 _ckvmssts_noperl(sys$setast(1)); 4314 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4315 } 4316 4317 if (info->in->buf) { 4318 n = info->in->bufsize * sizeof(char); 4319 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf)); 4320 } 4321 n = sizeof(Pipe); 4322 _ckvmssts_noperl(lib$free_vm(&n, &info->in)); 4323 n = sizeof(Info); 4324 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4325 *psts = RMS$_FNF; 4326 return NULL; 4327 } 4328 4329 4330 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */ 4331 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4332 if (info->out) { 4333 info->out->pipe_done = &info->out_done; 4334 info->out_done = FALSE; 4335 info->out->info = info; 4336 } 4337 4338 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4339 if (info->err) { 4340 info->err->pipe_done = &info->err_done; 4341 info->err_done = FALSE; 4342 info->err->info = info; 4343 } 4344 } 4345 4346 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol)); 4347 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table)); 4348 4349 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol)); 4350 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table)); 4351 4352 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol)); 4353 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table)); 4354 4355 /* Done with the names for the pipes */ 4356 PerlMem_free(err); 4357 PerlMem_free(out); 4358 PerlMem_free(in); 4359 4360 p = vmscmd->dsc$a_pointer; 4361 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */ 4362 if (*p == '$') p++; /* remove leading $ */ 4363 while (*p == ' ' || *p == '\t') p++; 4364 4365 for (j = 0; j < 4; j++) { 4366 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4367 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4368 4369 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol)); 4370 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table)); 4371 4372 if (strlen(p) > MAX_DCL_SYMBOL) { 4373 p += MAX_DCL_SYMBOL; 4374 } else { 4375 p += strlen(p); 4376 } 4377 } 4378 _ckvmssts_noperl(sys$setast(0)); 4379 info->next=open_pipes; /* prepend to list */ 4380 open_pipes=info; 4381 _ckvmssts_noperl(sys$setast(1)); 4382 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT 4383 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still 4384 * have SYS$COMMAND if we need it. 4385 */ 4386 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags, 4387 0, &info->pid, &info->completion, 4388 0, popen_completion_ast,info,0,0,0)); 4389 4390 /* if we were using a tempfile, close it now */ 4391 4392 if (tpipe) fclose(tpipe); 4393 4394 /* once the subprocess is spawned, it has copied the symbols and 4395 we can get rid of ours */ 4396 4397 for (j = 0; j < 4; j++) { 4398 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4399 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4400 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table)); 4401 } 4402 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table)); 4403 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table)); 4404 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table)); 4405 vms_execfree(vmscmd); 4406 4407 #ifdef PERL_IMPLICIT_CONTEXT 4408 if (aTHX) 4409 #endif 4410 PL_forkprocess = info->pid; 4411 4412 ret_fp = info->fp; 4413 if (wait) { 4414 dSAVEDERRNO; 4415 int done = 0; 4416 while (!done) { 4417 _ckvmssts_noperl(sys$setast(0)); 4418 done = info->done; 4419 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4420 _ckvmssts_noperl(sys$setast(1)); 4421 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4422 } 4423 *psts = info->completion; 4424 /* Caller thinks it is open and tries to close it. */ 4425 /* This causes some problems, as it changes the error status */ 4426 /* my_pclose(info->fp); */ 4427 4428 /* If we did not have a file pointer open, then we have to */ 4429 /* clean up here or eventually we will run out of something */ 4430 SAVE_ERRNO; 4431 if (info->fp == NULL) { 4432 my_pclose_pinfo(aTHX_ info); 4433 } 4434 RESTORE_ERRNO; 4435 4436 } else { 4437 *psts = info->pid; 4438 } 4439 return ret_fp; 4440 } /* end of safe_popen */ 4441 4442 4443 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/ 4444 PerlIO * 4445 Perl_my_popen(pTHX_ const char *cmd, const char *mode) 4446 { 4447 int sts; 4448 TAINT_ENV(); 4449 TAINT_PROPER("popen"); 4450 PERL_FLUSHALL_FOR_CHILD; 4451 return safe_popen(aTHX_ cmd,mode,&sts); 4452 } 4453 4454 /*}}}*/ 4455 4456 4457 /* Routine to close and cleanup a pipe info structure */ 4458 4459 static I32 my_pclose_pinfo(pTHX_ pInfo info) { 4460 4461 unsigned long int retsts; 4462 int done, n; 4463 pInfo next, last; 4464 4465 /* If we were writing to a subprocess, insure that someone reading from 4466 * the mailbox gets an EOF. It looks like a simple fclose() doesn't 4467 * produce an EOF record in the mailbox. 4468 * 4469 * well, at least sometimes it *does*, so we have to watch out for 4470 * the first EOF closing the pipe (and DASSGN'ing the channel)... 4471 */ 4472 if (info->fp) { 4473 if (!info->useFILE 4474 #if defined(USE_ITHREADS) 4475 && my_perl 4476 #endif 4477 #ifdef USE_PERLIO 4478 && PL_perlio_fd_refcnt 4479 #endif 4480 ) 4481 PerlIO_flush(info->fp); 4482 else 4483 fflush((FILE *)info->fp); 4484 } 4485 4486 _ckvmssts(sys$setast(0)); 4487 info->closing = TRUE; 4488 done = info->done && info->in_done && info->out_done && info->err_done; 4489 /* hanging on write to Perl's input? cancel it */ 4490 if (info->mode == 'r' && info->out && !info->out_done) { 4491 if (info->out->chan_out) { 4492 _ckvmssts(sys$cancel(info->out->chan_out)); 4493 if (!info->out->chan_in) { /* EOF generation, need AST */ 4494 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0)); 4495 } 4496 } 4497 } 4498 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */ 4499 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 4500 0, 0, 0, 0, 0, 0)); 4501 _ckvmssts(sys$setast(1)); 4502 if (info->fp) { 4503 if (!info->useFILE 4504 #if defined(USE_ITHREADS) 4505 && my_perl 4506 #endif 4507 #ifdef USE_PERLIO 4508 && PL_perlio_fd_refcnt 4509 #endif 4510 ) 4511 PerlIO_close(info->fp); 4512 else 4513 fclose((FILE *)info->fp); 4514 } 4515 /* 4516 we have to wait until subprocess completes, but ALSO wait until all 4517 the i/o completes...otherwise we'll be freeing the "info" structure 4518 that the i/o ASTs could still be using... 4519 */ 4520 4521 while (!done) { 4522 _ckvmssts(sys$setast(0)); 4523 done = info->done && info->in_done && info->out_done && info->err_done; 4524 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4525 _ckvmssts(sys$setast(1)); 4526 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4527 } 4528 retsts = info->completion; 4529 4530 /* remove from list of open pipes */ 4531 _ckvmssts(sys$setast(0)); 4532 last = NULL; 4533 for (next = open_pipes; next != NULL; last = next, next = next->next) { 4534 if (next == info) 4535 break; 4536 } 4537 4538 if (last) 4539 last->next = info->next; 4540 else 4541 open_pipes = info->next; 4542 _ckvmssts(sys$setast(1)); 4543 4544 /* free buffers and structures */ 4545 4546 if (info->in) { 4547 if (info->in->buf) { 4548 n = info->in->bufsize * sizeof(char); 4549 _ckvmssts(lib$free_vm(&n, &info->in->buf)); 4550 } 4551 n = sizeof(Pipe); 4552 _ckvmssts(lib$free_vm(&n, &info->in)); 4553 } 4554 if (info->out) { 4555 if (info->out->buf) { 4556 n = info->out->bufsize * sizeof(char); 4557 _ckvmssts(lib$free_vm(&n, &info->out->buf)); 4558 } 4559 n = sizeof(Pipe); 4560 _ckvmssts(lib$free_vm(&n, &info->out)); 4561 } 4562 if (info->err) { 4563 if (info->err->buf) { 4564 n = info->err->bufsize * sizeof(char); 4565 _ckvmssts(lib$free_vm(&n, &info->err->buf)); 4566 } 4567 n = sizeof(Pipe); 4568 _ckvmssts(lib$free_vm(&n, &info->err)); 4569 } 4570 n = sizeof(Info); 4571 _ckvmssts(lib$free_vm(&n, &info)); 4572 4573 return retsts; 4574 } 4575 4576 4577 /*{{{ I32 my_pclose(PerlIO *fp)*/ 4578 I32 Perl_my_pclose(pTHX_ PerlIO *fp) 4579 { 4580 pInfo info, last = NULL; 4581 I32 ret_status; 4582 4583 /* Fixme - need ast and mutex protection here */ 4584 for (info = open_pipes; info != NULL; last = info, info = info->next) 4585 if (info->fp == fp) break; 4586 4587 if (info == NULL) { /* no such pipe open */ 4588 set_errno(ECHILD); /* quoth POSIX */ 4589 set_vaxc_errno(SS$_NONEXPR); 4590 return -1; 4591 } 4592 4593 ret_status = my_pclose_pinfo(aTHX_ info); 4594 4595 return ret_status; 4596 4597 } /* end of my_pclose() */ 4598 4599 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000 4600 /* Roll our own prototype because we want this regardless of whether 4601 * _VMS_WAIT is defined. 4602 */ 4603 4604 #ifdef __cplusplus 4605 extern "C" { 4606 #endif 4607 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options ); 4608 #ifdef __cplusplus 4609 } 4610 #endif 4611 4612 #endif 4613 /* sort-of waitpid; special handling of pipe clean-up for subprocesses 4614 created with popen(); otherwise partially emulate waitpid() unless 4615 we have a suitable one from the CRTL that came with VMS 7.2 and later. 4616 Also check processes not considered by the CRTL waitpid(). 4617 */ 4618 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/ 4619 Pid_t 4620 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags) 4621 { 4622 pInfo info; 4623 int done; 4624 int sts; 4625 int j; 4626 4627 if (statusp) *statusp = 0; 4628 4629 for (info = open_pipes; info != NULL; info = info->next) 4630 if (info->pid == pid) break; 4631 4632 if (info != NULL) { /* we know about this child */ 4633 while (!info->done) { 4634 _ckvmssts(sys$setast(0)); 4635 done = info->done; 4636 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4637 _ckvmssts(sys$setast(1)); 4638 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4639 } 4640 4641 if (statusp) *statusp = info->completion; 4642 return pid; 4643 } 4644 4645 /* child that already terminated? */ 4646 4647 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) { 4648 if (closed_list[j].pid == pid) { 4649 if (statusp) *statusp = closed_list[j].completion; 4650 return pid; 4651 } 4652 } 4653 4654 /* fall through if this child is not one of our own pipe children */ 4655 4656 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000 4657 4658 /* waitpid() became available in the CRTL as of VMS 7.0, but only 4659 * in 7.2 did we get a version that fills in the VMS completion 4660 * status as Perl has always tried to do. 4661 */ 4662 4663 sts = __vms_waitpid( pid, statusp, flags ); 4664 4665 if ( sts == 0 || !(sts == -1 && errno == ECHILD) ) 4666 return sts; 4667 4668 /* If the real waitpid tells us the child does not exist, we 4669 * fall through here to implement waiting for a child that 4670 * was created by some means other than exec() (say, spawned 4671 * from DCL) or to wait for a process that is not a subprocess 4672 * of the current process. 4673 */ 4674 4675 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */ 4676 4677 { 4678 $DESCRIPTOR(intdsc,"0 00:00:01"); 4679 unsigned long int ownercode = JPI$_OWNER, ownerpid; 4680 unsigned long int pidcode = JPI$_PID, mypid; 4681 unsigned long int interval[2]; 4682 unsigned int jpi_iosb[2]; 4683 struct itmlst_3 jpilist[2] = { 4684 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0}, 4685 { 0, 0, 0, 0} 4686 }; 4687 4688 if (pid <= 0) { 4689 /* Sorry folks, we don't presently implement rooting around for 4690 the first child we can find, and we definitely don't want to 4691 pass a pid of -1 to $getjpi, where it is a wildcard operation. 4692 */ 4693 set_errno(ENOTSUP); 4694 return -1; 4695 } 4696 4697 /* Get the owner of the child so I can warn if it's not mine. If the 4698 * process doesn't exist or I don't have the privs to look at it, 4699 * I can go home early. 4700 */ 4701 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL); 4702 if (sts & 1) sts = jpi_iosb[0]; 4703 if (!(sts & 1)) { 4704 switch (sts) { 4705 case SS$_NONEXPR: 4706 set_errno(ECHILD); 4707 break; 4708 case SS$_NOPRIV: 4709 set_errno(EACCES); 4710 break; 4711 default: 4712 _ckvmssts(sts); 4713 } 4714 set_vaxc_errno(sts); 4715 return -1; 4716 } 4717 4718 if (ckWARN(WARN_EXEC)) { 4719 /* remind folks they are asking for non-standard waitpid behavior */ 4720 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4721 if (ownerpid != mypid) 4722 Perl_warner(aTHX_ packWARN(WARN_EXEC), 4723 "waitpid: process %x is not a child of process %x", 4724 pid,mypid); 4725 } 4726 4727 /* simply check on it once a second until it's not there anymore. */ 4728 4729 _ckvmssts(sys$bintim(&intdsc,interval)); 4730 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) { 4731 _ckvmssts(sys$schdwk(0,0,interval,0)); 4732 _ckvmssts(sys$hiber()); 4733 } 4734 if (sts == SS$_NONEXPR) sts = SS$_NORMAL; 4735 4736 _ckvmssts(sts); 4737 return pid; 4738 } 4739 } /* end of waitpid() */ 4740 /*}}}*/ 4741 /*}}}*/ 4742 /*}}}*/ 4743 4744 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */ 4745 char * 4746 my_gconvert(double val, int ndig, int trail, char *buf) 4747 { 4748 static char __gcvtbuf[DBL_DIG+1]; 4749 char *loc; 4750 4751 loc = buf ? buf : __gcvtbuf; 4752 4753 if (val) { 4754 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG; 4755 return gcvt(val,ndig,loc); 4756 } 4757 else { 4758 loc[0] = '0'; loc[1] = '\0'; 4759 return loc; 4760 } 4761 4762 } 4763 /*}}}*/ 4764 4765 #if defined(__VAX) || !defined(NAML$C_MAXRSS) 4766 static int rms_free_search_context(struct FAB * fab) 4767 { 4768 struct NAM * nam; 4769 4770 nam = fab->fab$l_nam; 4771 nam->nam$b_nop |= NAM$M_SYNCHK; 4772 nam->nam$l_rlf = NULL; 4773 fab->fab$b_dns = 0; 4774 return sys$parse(fab, NULL, NULL); 4775 } 4776 4777 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam 4778 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0; 4779 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt) 4780 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt) 4781 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt)) 4782 #define rms_nam_esll(nam) nam.nam$b_esl 4783 #define rms_nam_esl(nam) nam.nam$b_esl 4784 #define rms_nam_name(nam) nam.nam$l_name 4785 #define rms_nam_namel(nam) nam.nam$l_name 4786 #define rms_nam_type(nam) nam.nam$l_type 4787 #define rms_nam_typel(nam) nam.nam$l_type 4788 #define rms_nam_ver(nam) nam.nam$l_ver 4789 #define rms_nam_verl(nam) nam.nam$l_ver 4790 #define rms_nam_rsll(nam) nam.nam$b_rsl 4791 #define rms_nam_rsl(nam) nam.nam$b_rsl 4792 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam 4793 #define rms_set_fna(fab, nam, name, size) \ 4794 { fab.fab$b_fns = size; fab.fab$l_fna = name; } 4795 #define rms_get_fna(fab, nam) fab.fab$l_fna 4796 #define rms_set_dna(fab, nam, name, size) \ 4797 { fab.fab$b_dns = size; fab.fab$l_dna = name; } 4798 #define rms_nam_dns(fab, nam) fab.fab$b_dns 4799 #define rms_set_esa(nam, name, size) \ 4800 { nam.nam$b_ess = size; nam.nam$l_esa = name; } 4801 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4802 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;} 4803 #define rms_set_rsa(nam, name, size) \ 4804 { nam.nam$l_rsa = name; nam.nam$b_rss = size; } 4805 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4806 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; } 4807 #define rms_nam_name_type_l_size(nam) \ 4808 (nam.nam$b_name + nam.nam$b_type) 4809 #else 4810 static int rms_free_search_context(struct FAB * fab) 4811 { 4812 struct NAML * nam; 4813 4814 nam = fab->fab$l_naml; 4815 nam->naml$b_nop |= NAM$M_SYNCHK; 4816 nam->naml$l_rlf = NULL; 4817 nam->naml$l_long_defname_size = 0; 4818 4819 fab->fab$b_dns = 0; 4820 return sys$parse(fab, NULL, NULL); 4821 } 4822 4823 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml 4824 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0; 4825 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt) 4826 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt) 4827 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt)) 4828 #define rms_nam_esll(nam) nam.naml$l_long_expand_size 4829 #define rms_nam_esl(nam) nam.naml$b_esl 4830 #define rms_nam_name(nam) nam.naml$l_name 4831 #define rms_nam_namel(nam) nam.naml$l_long_name 4832 #define rms_nam_type(nam) nam.naml$l_type 4833 #define rms_nam_typel(nam) nam.naml$l_long_type 4834 #define rms_nam_ver(nam) nam.naml$l_ver 4835 #define rms_nam_verl(nam) nam.naml$l_long_ver 4836 #define rms_nam_rsll(nam) nam.naml$l_long_result_size 4837 #define rms_nam_rsl(nam) nam.naml$b_rsl 4838 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam 4839 #define rms_set_fna(fab, nam, name, size) \ 4840 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \ 4841 nam.naml$l_long_filename_size = size; \ 4842 nam.naml$l_long_filename = name;} 4843 #define rms_get_fna(fab, nam) nam.naml$l_long_filename 4844 #define rms_set_dna(fab, nam, name, size) \ 4845 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \ 4846 nam.naml$l_long_defname_size = size; \ 4847 nam.naml$l_long_defname = name; } 4848 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size 4849 #define rms_set_esa(nam, name, size) \ 4850 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \ 4851 nam.naml$l_long_expand_alloc = size; \ 4852 nam.naml$l_long_expand = name; } 4853 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4854 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \ 4855 nam.naml$l_long_expand = l_name; \ 4856 nam.naml$l_long_expand_alloc = l_size; } 4857 #define rms_set_rsa(nam, name, size) \ 4858 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \ 4859 nam.naml$l_long_result = name; \ 4860 nam.naml$l_long_result_alloc = size; } 4861 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4862 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \ 4863 nam.naml$l_long_result = l_name; \ 4864 nam.naml$l_long_result_alloc = l_size; } 4865 #define rms_nam_name_type_l_size(nam) \ 4866 (nam.naml$l_long_name_size + nam.naml$l_long_type_size) 4867 #endif 4868 4869 4870 /* rms_erase 4871 * The CRTL for 8.3 and later can create symbolic links in any mode, 4872 * however in 8.3 the unlink/remove/delete routines will only properly handle 4873 * them if one of the PCP modes is active. 4874 */ 4875 static int rms_erase(const char * vmsname) 4876 { 4877 int status; 4878 struct FAB myfab = cc$rms_fab; 4879 rms_setup_nam(mynam); 4880 4881 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */ 4882 rms_bind_fab_nam(myfab, mynam); 4883 4884 #ifdef NAML$M_OPEN_SPECIAL 4885 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 4886 #endif 4887 4888 status = sys$erase(&myfab, 0, 0); 4889 4890 return status; 4891 } 4892 4893 4894 static int 4895 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc, 4896 const struct dsc$descriptor_s * vms_dst_dsc, 4897 unsigned long flags) 4898 { 4899 /* VMS and UNIX handle file permissions differently and the 4900 * the same ACL trick may be needed for renaming files, 4901 * especially if they are directories. 4902 */ 4903 4904 /* todo: get kill_file and rename to share common code */ 4905 /* I can not find online documentation for $change_acl 4906 * it appears to be replaced by $set_security some time ago */ 4907 4908 const unsigned int access_mode = 0; 4909 $DESCRIPTOR(obj_file_dsc,"FILE"); 4910 char *vmsname; 4911 char *rslt; 4912 unsigned long int jpicode = JPI$_UIC; 4913 int aclsts, fndsts, rnsts = -1; 4914 unsigned int ctx = 0; 4915 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 4916 struct dsc$descriptor_s * clean_dsc; 4917 4918 struct myacedef { 4919 unsigned char myace$b_length; 4920 unsigned char myace$b_type; 4921 unsigned short int myace$w_flags; 4922 unsigned long int myace$l_access; 4923 unsigned long int myace$l_ident; 4924 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 4925 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 4926 0}, 4927 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 4928 4929 struct item_list_3 4930 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0}, 4931 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0}, 4932 {0,0,0,0}}, 4933 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}}, 4934 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0}, 4935 {0,0,0,0}}; 4936 4937 4938 /* Expand the input spec using RMS, since we do not want to put 4939 * ACLs on the target of a symbolic link */ 4940 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1); 4941 if (vmsname == NULL) 4942 return SS$_INSFMEM; 4943 4944 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer, 4945 vmsname, 4946 PERL_RMSEXPAND_M_SYMLINK); 4947 if (rslt == NULL) { 4948 PerlMem_free(vmsname); 4949 return SS$_INSFMEM; 4950 } 4951 4952 /* So we get our own UIC to use as a rights identifier, 4953 * and the insert an ACE at the head of the ACL which allows us 4954 * to delete the file. 4955 */ 4956 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 4957 4958 fildsc.dsc$w_length = strlen(vmsname); 4959 fildsc.dsc$a_pointer = vmsname; 4960 ctx = 0; 4961 newace.myace$l_ident = oldace.myace$l_ident; 4962 rnsts = SS$_ABORT; 4963 4964 /* Grab any existing ACEs with this identifier in case we fail */ 4965 clean_dsc = &fildsc; 4966 aclsts = fndsts = sys$get_security(&obj_file_dsc, 4967 &fildsc, 4968 NULL, 4969 OSS$M_WLOCK, 4970 findlst, 4971 &ctx, 4972 &access_mode); 4973 4974 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) { 4975 /* Add the new ACE . . . */ 4976 4977 /* if the sys$get_security succeeded, then ctx is valid, and the 4978 * object/file descriptors will be ignored. But otherwise they 4979 * are needed 4980 */ 4981 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL, 4982 OSS$M_RELCTX, addlst, &ctx, &access_mode); 4983 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 4984 set_errno(EVMSERR); 4985 set_vaxc_errno(aclsts); 4986 PerlMem_free(vmsname); 4987 return aclsts; 4988 } 4989 4990 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc, 4991 NULL, NULL, 4992 &flags, 4993 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 4994 4995 if ($VMS_STATUS_SUCCESS(rnsts)) { 4996 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc; 4997 } 4998 4999 /* Put things back the way they were. */ 5000 ctx = 0; 5001 aclsts = sys$get_security(&obj_file_dsc, 5002 clean_dsc, 5003 NULL, 5004 OSS$M_WLOCK, 5005 findlst, 5006 &ctx, 5007 &access_mode); 5008 5009 if ($VMS_STATUS_SUCCESS(aclsts)) { 5010 int sec_flags; 5011 5012 sec_flags = 0; 5013 if (!$VMS_STATUS_SUCCESS(fndsts)) 5014 sec_flags = OSS$M_RELCTX; 5015 5016 /* Get rid of the new ACE */ 5017 aclsts = sys$set_security(NULL, NULL, NULL, 5018 sec_flags, dellst, &ctx, &access_mode); 5019 5020 /* If there was an old ACE, put it back */ 5021 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) { 5022 addlst[0].bufadr = &oldace; 5023 aclsts = sys$set_security(NULL, NULL, NULL, 5024 OSS$M_RELCTX, addlst, &ctx, &access_mode); 5025 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 5026 set_errno(EVMSERR); 5027 set_vaxc_errno(aclsts); 5028 rnsts = aclsts; 5029 } 5030 } else { 5031 int aclsts2; 5032 5033 /* Try to clear the lock on the ACL list */ 5034 aclsts2 = sys$set_security(NULL, NULL, NULL, 5035 OSS$M_RELCTX, NULL, &ctx, &access_mode); 5036 5037 /* Rename errors are most important */ 5038 if (!$VMS_STATUS_SUCCESS(rnsts)) 5039 aclsts = rnsts; 5040 set_errno(EVMSERR); 5041 set_vaxc_errno(aclsts); 5042 rnsts = aclsts; 5043 } 5044 } 5045 else { 5046 if (aclsts != SS$_ACLEMPTY) 5047 rnsts = aclsts; 5048 } 5049 } 5050 else 5051 rnsts = fndsts; 5052 5053 PerlMem_free(vmsname); 5054 return rnsts; 5055 } 5056 5057 5058 /*{{{int rename(const char *, const char * */ 5059 /* Not exactly what X/Open says to do, but doing it absolutely right 5060 * and efficiently would require a lot more work. This should be close 5061 * enough to pass all but the most strict X/Open compliance test. 5062 */ 5063 int 5064 Perl_rename(pTHX_ const char *src, const char * dst) 5065 { 5066 int retval; 5067 int pre_delete = 0; 5068 int src_sts; 5069 int dst_sts; 5070 Stat_t src_st; 5071 Stat_t dst_st; 5072 5073 /* Validate the source file */ 5074 src_sts = flex_lstat(src, &src_st); 5075 if (src_sts != 0) { 5076 5077 /* No source file or other problem */ 5078 return src_sts; 5079 } 5080 if (src_st.st_devnam[0] == 0) { 5081 /* This may be possible so fail if it is seen. */ 5082 errno = EIO; 5083 return -1; 5084 } 5085 5086 dst_sts = flex_lstat(dst, &dst_st); 5087 if (dst_sts == 0) { 5088 5089 if (dst_st.st_dev != src_st.st_dev) { 5090 /* Must be on the same device */ 5091 errno = EXDEV; 5092 return -1; 5093 } 5094 5095 /* VMS_INO_T_COMPARE is true if the inodes are different 5096 * to match the output of memcmp 5097 */ 5098 5099 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) { 5100 /* That was easy, the files are the same! */ 5101 return 0; 5102 } 5103 5104 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) { 5105 /* If source is a directory, so must be dest */ 5106 errno = EISDIR; 5107 return -1; 5108 } 5109 5110 } 5111 5112 5113 if ((dst_sts == 0) && 5114 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) { 5115 5116 /* We have issues here if vms_unlink_all_versions is set 5117 * If the destination exists, and is not a directory, then 5118 * we must delete in advance. 5119 * 5120 * If the src is a directory, then we must always pre-delete 5121 * the destination. 5122 * 5123 * If we successfully delete the dst in advance, and the rename fails 5124 * X/Open requires that errno be EIO. 5125 * 5126 */ 5127 5128 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) { 5129 int d_sts; 5130 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 5131 S_ISDIR(dst_st.st_mode)); 5132 5133 /* Need to delete all versions ? */ 5134 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) { 5135 int i = 0; 5136 5137 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) { 5138 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0); 5139 if (d_sts != 0) 5140 break; 5141 i++; 5142 5143 /* Make sure that we do not loop forever */ 5144 if (i > 32767) { 5145 errno = EIO; 5146 d_sts = -1; 5147 break; 5148 } 5149 } 5150 } 5151 5152 if (d_sts != 0) 5153 return d_sts; 5154 5155 /* We killed the destination, so only errno now is EIO */ 5156 pre_delete = 1; 5157 } 5158 } 5159 5160 /* Originally the idea was to call the CRTL rename() and only 5161 * try the lib$rename_file if it failed. 5162 * It turns out that there are too many variants in what the 5163 * the CRTL rename might do, so only use lib$rename_file 5164 */ 5165 retval = -1; 5166 5167 { 5168 /* Is the source and dest both in VMS format */ 5169 /* if the source is a directory, then need to fileify */ 5170 /* and dest must be a directory or non-existent. */ 5171 5172 char * vms_dst; 5173 int sts; 5174 char * ret_str; 5175 unsigned long flags; 5176 struct dsc$descriptor_s old_file_dsc; 5177 struct dsc$descriptor_s new_file_dsc; 5178 5179 /* We need to modify the src and dst depending 5180 * on if one or more of them are directories. 5181 */ 5182 5183 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS); 5184 if (vms_dst == NULL) 5185 _ckvmssts_noperl(SS$_INSFMEM); 5186 5187 if (S_ISDIR(src_st.st_mode)) { 5188 char * ret_str; 5189 char * vms_dir_file; 5190 5191 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS); 5192 if (vms_dir_file == NULL) 5193 _ckvmssts_noperl(SS$_INSFMEM); 5194 5195 /* If the dest is a directory, we must remove it */ 5196 if (dst_sts == 0) { 5197 int d_sts; 5198 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1); 5199 if (d_sts != 0) { 5200 PerlMem_free(vms_dst); 5201 errno = EIO; 5202 return d_sts; 5203 } 5204 5205 pre_delete = 1; 5206 } 5207 5208 /* The dest must be a VMS file specification */ 5209 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL); 5210 if (ret_str == NULL) { 5211 PerlMem_free(vms_dst); 5212 errno = EIO; 5213 return -1; 5214 } 5215 5216 /* The source must be a file specification */ 5217 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL); 5218 if (ret_str == NULL) { 5219 PerlMem_free(vms_dst); 5220 PerlMem_free(vms_dir_file); 5221 errno = EIO; 5222 return -1; 5223 } 5224 PerlMem_free(vms_dst); 5225 vms_dst = vms_dir_file; 5226 5227 } else { 5228 /* File to file or file to new dir */ 5229 5230 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) { 5231 /* VMS pathify a dir target */ 5232 ret_str = int_tovmspath(dst, vms_dst, NULL); 5233 if (ret_str == NULL) { 5234 PerlMem_free(vms_dst); 5235 errno = EIO; 5236 return -1; 5237 } 5238 } else { 5239 char * v_spec, * r_spec, * d_spec, * n_spec; 5240 char * e_spec, * vs_spec; 5241 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 5242 5243 /* fileify a target VMS file specification */ 5244 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL); 5245 if (ret_str == NULL) { 5246 PerlMem_free(vms_dst); 5247 errno = EIO; 5248 return -1; 5249 } 5250 5251 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len, 5252 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 5253 &e_len, &vs_spec, &vs_len); 5254 if (sts == 0) { 5255 if (e_len == 0) { 5256 /* Get rid of the version */ 5257 if (vs_len != 0) { 5258 *vs_spec = '\0'; 5259 } 5260 /* Need to specify a '.' so that the extension */ 5261 /* is not inherited */ 5262 strcat(vms_dst,"."); 5263 } 5264 } 5265 } 5266 } 5267 5268 old_file_dsc.dsc$a_pointer = src_st.st_devnam; 5269 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam); 5270 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5271 old_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5272 5273 new_file_dsc.dsc$a_pointer = vms_dst; 5274 new_file_dsc.dsc$w_length = strlen(vms_dst); 5275 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5276 new_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5277 5278 flags = 0; 5279 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5280 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */ 5281 #endif 5282 5283 sts = lib$rename_file(&old_file_dsc, 5284 &new_file_dsc, 5285 NULL, NULL, 5286 &flags, 5287 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 5288 if (!$VMS_STATUS_SUCCESS(sts)) { 5289 5290 /* We could have failed because VMS style permissions do not 5291 * permit renames that UNIX will allow. Just like the hack 5292 * in for kill_file. 5293 */ 5294 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags); 5295 } 5296 5297 PerlMem_free(vms_dst); 5298 if (!$VMS_STATUS_SUCCESS(sts)) { 5299 errno = EIO; 5300 return -1; 5301 } 5302 retval = 0; 5303 } 5304 5305 if (vms_unlink_all_versions) { 5306 /* Now get rid of any previous versions of the source file that 5307 * might still exist 5308 */ 5309 int i = 0; 5310 dSAVEDERRNO; 5311 SAVE_ERRNO; 5312 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam, 5313 S_ISDIR(src_st.st_mode)); 5314 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) { 5315 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam, 5316 S_ISDIR(src_st.st_mode)); 5317 if (src_sts != 0) 5318 break; 5319 i++; 5320 5321 /* Make sure that we do not loop forever */ 5322 if (i > 32767) { 5323 src_sts = -1; 5324 break; 5325 } 5326 } 5327 RESTORE_ERRNO; 5328 } 5329 5330 /* We deleted the destination, so must force the error to be EIO */ 5331 if ((retval != 0) && (pre_delete != 0)) 5332 errno = EIO; 5333 5334 return retval; 5335 } 5336 /*}}}*/ 5337 5338 5339 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/ 5340 /* Shortcut for common case of simple calls to $PARSE and $SEARCH 5341 * to expand file specification. Allows for a single default file 5342 * specification and a simple mask of options. If outbuf is non-NULL, 5343 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which 5344 * the resultant file specification is placed. If outbuf is NULL, the 5345 * resultant file specification is placed into a static buffer. 5346 * The third argument, if non-NULL, is taken to be a default file 5347 * specification string. The fourth argument is unused at present. 5348 * rmesexpand() returns the address of the resultant string if 5349 * successful, and NULL on error. 5350 * 5351 * New functionality for previously unused opts value: 5352 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format. 5353 * PERL_RMSEXPAND_M_LONG - Want output in long formst 5354 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify 5355 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target 5356 */ 5357 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 5358 5359 static char * 5360 int_rmsexpand 5361 (const char *filespec, 5362 char *outbuf, 5363 const char *defspec, 5364 unsigned opts, 5365 int * fs_utf8, 5366 int * dfs_utf8) 5367 { 5368 char * ret_spec; 5369 const char * in_spec; 5370 char * spec_buf; 5371 const char * def_spec; 5372 char * vmsfspec, *vmsdefspec; 5373 char * esa; 5374 char * esal = NULL; 5375 char * outbufl; 5376 struct FAB myfab = cc$rms_fab; 5377 rms_setup_nam(mynam); 5378 STRLEN speclen; 5379 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0; 5380 int sts; 5381 5382 /* temp hack until UTF8 is actually implemented */ 5383 if (fs_utf8 != NULL) 5384 *fs_utf8 = 0; 5385 5386 if (!filespec || !*filespec) { 5387 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL); 5388 return NULL; 5389 } 5390 5391 vmsfspec = NULL; 5392 vmsdefspec = NULL; 5393 outbufl = NULL; 5394 5395 in_spec = filespec; 5396 isunix = 0; 5397 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) { 5398 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 5399 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 5400 5401 /* If this is a UNIX file spec, convert it to VMS */ 5402 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len, 5403 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 5404 &e_len, &vs_spec, &vs_len); 5405 if (sts != 0) { 5406 isunix = 1; 5407 char * ret_spec; 5408 5409 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS); 5410 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5411 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8); 5412 if (ret_spec == NULL) { 5413 PerlMem_free(vmsfspec); 5414 return NULL; 5415 } 5416 in_spec = (const char *)vmsfspec; 5417 5418 /* Unless we are forcing to VMS format, a UNIX input means 5419 * UNIX output, and that requires long names to be used 5420 */ 5421 if ((opts & PERL_RMSEXPAND_M_VMS) == 0) 5422 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5423 opts |= PERL_RMSEXPAND_M_LONG; 5424 #else 5425 NOOP; 5426 #endif 5427 else 5428 isunix = 0; 5429 } 5430 5431 } 5432 5433 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */ 5434 rms_bind_fab_nam(myfab, mynam); 5435 5436 /* Process the default file specification if present */ 5437 def_spec = defspec; 5438 if (defspec && *defspec) { 5439 int t_isunix; 5440 t_isunix = is_unix_filespec(defspec); 5441 if (t_isunix) { 5442 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS); 5443 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5444 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8); 5445 5446 if (ret_spec == NULL) { 5447 /* Clean up and bail */ 5448 PerlMem_free(vmsdefspec); 5449 if (vmsfspec != NULL) 5450 PerlMem_free(vmsfspec); 5451 return NULL; 5452 } 5453 def_spec = (const char *)vmsdefspec; 5454 } 5455 rms_set_dna(myfab, mynam, 5456 (char *)def_spec, strlen(def_spec)); /* cast ok */ 5457 } 5458 5459 /* Now we need the expansion buffers */ 5460 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 5461 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5462 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5463 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 5464 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5465 #endif 5466 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1); 5467 5468 /* If a NAML block is used RMS always writes to the long and short 5469 * addresses unless you suppress the short name. 5470 */ 5471 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5472 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS); 5473 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5474 #endif 5475 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1)); 5476 5477 #ifdef NAM$M_NO_SHORT_UPCASE 5478 if (decc_efs_case_preserve) 5479 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE); 5480 #endif 5481 5482 /* We may not want to follow symbolic links */ 5483 #ifdef NAML$M_OPEN_SPECIAL 5484 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5485 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5486 #endif 5487 5488 /* First attempt to parse as an existing file */ 5489 retsts = sys$parse(&myfab,0,0); 5490 if (!(retsts & STS$K_SUCCESS)) { 5491 5492 /* Could not find the file, try as syntax only if error is not fatal */ 5493 rms_set_nam_nop(mynam, NAM$M_SYNCHK); 5494 if (retsts == RMS$_DNF || 5495 retsts == RMS$_DIR || 5496 retsts == RMS$_DEV || 5497 retsts == RMS$_PRV) { 5498 retsts = sys$parse(&myfab,0,0); 5499 if (retsts & STS$K_SUCCESS) goto int_expanded; 5500 } 5501 5502 /* Still could not parse the file specification */ 5503 /*----------------------------------------------*/ 5504 sts = rms_free_search_context(&myfab); /* Free search context */ 5505 if (vmsdefspec != NULL) 5506 PerlMem_free(vmsdefspec); 5507 if (vmsfspec != NULL) 5508 PerlMem_free(vmsfspec); 5509 if (outbufl != NULL) 5510 PerlMem_free(outbufl); 5511 PerlMem_free(esa); 5512 if (esal != NULL) 5513 PerlMem_free(esal); 5514 set_vaxc_errno(retsts); 5515 if (retsts == RMS$_PRV) set_errno(EACCES); 5516 else if (retsts == RMS$_DEV) set_errno(ENODEV); 5517 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 5518 else set_errno(EVMSERR); 5519 return NULL; 5520 } 5521 retsts = sys$search(&myfab,0,0); 5522 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) { 5523 sts = rms_free_search_context(&myfab); /* Free search context */ 5524 if (vmsdefspec != NULL) 5525 PerlMem_free(vmsdefspec); 5526 if (vmsfspec != NULL) 5527 PerlMem_free(vmsfspec); 5528 if (outbufl != NULL) 5529 PerlMem_free(outbufl); 5530 PerlMem_free(esa); 5531 if (esal != NULL) 5532 PerlMem_free(esal); 5533 set_vaxc_errno(retsts); 5534 if (retsts == RMS$_PRV) set_errno(EACCES); 5535 else set_errno(EVMSERR); 5536 return NULL; 5537 } 5538 5539 /* If the input filespec contained any lowercase characters, 5540 * downcase the result for compatibility with Unix-minded code. */ 5541 int_expanded: 5542 if (!decc_efs_case_preserve) { 5543 char * tbuf; 5544 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++) 5545 if (islower(*tbuf)) { haslower = 1; break; } 5546 } 5547 5548 /* Is a long or a short name expected */ 5549 /*------------------------------------*/ 5550 spec_buf = NULL; 5551 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5552 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5553 if (rms_nam_rsll(mynam)) { 5554 spec_buf = outbufl; 5555 speclen = rms_nam_rsll(mynam); 5556 } 5557 else { 5558 spec_buf = esal; /* Not esa */ 5559 speclen = rms_nam_esll(mynam); 5560 } 5561 } 5562 else { 5563 #endif 5564 if (rms_nam_rsl(mynam)) { 5565 spec_buf = outbuf; 5566 speclen = rms_nam_rsl(mynam); 5567 } 5568 else { 5569 spec_buf = esa; /* Not esal */ 5570 speclen = rms_nam_esl(mynam); 5571 } 5572 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5573 } 5574 #endif 5575 spec_buf[speclen] = '\0'; 5576 5577 /* Trim off null fields added by $PARSE 5578 * If type > 1 char, must have been specified in original or default spec 5579 * (not true for version; $SEARCH may have added version of existing file). 5580 */ 5581 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER); 5582 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5583 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5584 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1); 5585 } 5586 else { 5587 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5588 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1); 5589 } 5590 if (trimver || trimtype) { 5591 if (defspec && *defspec) { 5592 char *defesal = NULL; 5593 char *defesa = NULL; 5594 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5595 if (defesa != NULL) { 5596 struct FAB deffab = cc$rms_fab; 5597 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5598 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5599 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5600 #endif 5601 rms_setup_nam(defnam); 5602 5603 rms_bind_fab_nam(deffab, defnam); 5604 5605 /* Cast ok */ 5606 rms_set_fna 5607 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam)); 5608 5609 /* RMS needs the esa/esal as a work area if wildcards are involved */ 5610 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1); 5611 5612 rms_clear_nam_nop(defnam); 5613 rms_set_nam_nop(defnam, NAM$M_SYNCHK); 5614 #ifdef NAM$M_NO_SHORT_UPCASE 5615 if (decc_efs_case_preserve) 5616 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE); 5617 #endif 5618 #ifdef NAML$M_OPEN_SPECIAL 5619 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5620 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5621 #endif 5622 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) { 5623 if (trimver) { 5624 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER); 5625 } 5626 if (trimtype) { 5627 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE); 5628 } 5629 } 5630 if (defesal != NULL) 5631 PerlMem_free(defesal); 5632 PerlMem_free(defesa); 5633 } else { 5634 _ckvmssts_noperl(SS$_INSFMEM); 5635 } 5636 } 5637 if (trimver) { 5638 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5639 if (*(rms_nam_verl(mynam)) != '\"') 5640 speclen = rms_nam_verl(mynam) - spec_buf; 5641 } 5642 else { 5643 if (*(rms_nam_ver(mynam)) != '\"') 5644 speclen = rms_nam_ver(mynam) - spec_buf; 5645 } 5646 } 5647 if (trimtype) { 5648 /* If we didn't already trim version, copy down */ 5649 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5650 if (speclen > rms_nam_verl(mynam) - spec_buf) 5651 memmove 5652 (rms_nam_typel(mynam), 5653 rms_nam_verl(mynam), 5654 speclen - (rms_nam_verl(mynam) - spec_buf)); 5655 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam); 5656 } 5657 else { 5658 if (speclen > rms_nam_ver(mynam) - spec_buf) 5659 memmove 5660 (rms_nam_type(mynam), 5661 rms_nam_ver(mynam), 5662 speclen - (rms_nam_ver(mynam) - spec_buf)); 5663 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam); 5664 } 5665 } 5666 } 5667 5668 /* Done with these copies of the input files */ 5669 /*-------------------------------------------*/ 5670 if (vmsfspec != NULL) 5671 PerlMem_free(vmsfspec); 5672 if (vmsdefspec != NULL) 5673 PerlMem_free(vmsdefspec); 5674 5675 /* If we just had a directory spec on input, $PARSE "helpfully" 5676 * adds an empty name and type for us */ 5677 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5678 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5679 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) && 5680 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 && 5681 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5682 speclen = rms_nam_namel(mynam) - spec_buf; 5683 } 5684 else 5685 #endif 5686 { 5687 if (rms_nam_name(mynam) == rms_nam_type(mynam) && 5688 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 && 5689 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5690 speclen = rms_nam_name(mynam) - spec_buf; 5691 } 5692 5693 /* Posix format specifications must have matching quotes */ 5694 if (speclen < (VMS_MAXRSS - 1)) { 5695 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) { 5696 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) { 5697 spec_buf[speclen] = '\"'; 5698 speclen++; 5699 } 5700 } 5701 } 5702 spec_buf[speclen] = '\0'; 5703 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf); 5704 5705 /* Have we been working with an expanded, but not resultant, spec? */ 5706 /* Also, convert back to Unix syntax if necessary. */ 5707 { 5708 int rsl; 5709 5710 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5711 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5712 rsl = rms_nam_rsll(mynam); 5713 } else 5714 #endif 5715 { 5716 rsl = rms_nam_rsl(mynam); 5717 } 5718 if (!rsl) { 5719 /* rsl is not present, it means that spec_buf is either */ 5720 /* esa or esal, and needs to be copied to outbuf */ 5721 /* convert to Unix if desired */ 5722 if (isunix) { 5723 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8); 5724 } else { 5725 /* VMS file specs are not in UTF-8 */ 5726 if (fs_utf8 != NULL) 5727 *fs_utf8 = 0; 5728 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS); 5729 ret_spec = outbuf; 5730 } 5731 } 5732 else { 5733 /* Now spec_buf is either outbuf or outbufl */ 5734 /* We need the result into outbuf */ 5735 if (isunix) { 5736 /* If we need this in UNIX, then we need another buffer */ 5737 /* to keep things in order */ 5738 char * src; 5739 char * new_src = NULL; 5740 if (spec_buf == outbuf) { 5741 new_src = (char *)PerlMem_malloc(VMS_MAXRSS); 5742 my_strlcpy(new_src, spec_buf, VMS_MAXRSS); 5743 } else { 5744 src = spec_buf; 5745 } 5746 ret_spec = int_tounixspec(src, outbuf, fs_utf8); 5747 if (new_src) { 5748 PerlMem_free(new_src); 5749 } 5750 } else { 5751 /* VMS file specs are not in UTF-8 */ 5752 if (fs_utf8 != NULL) 5753 *fs_utf8 = 0; 5754 5755 /* Copy the buffer if needed */ 5756 if (outbuf != spec_buf) 5757 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS); 5758 ret_spec = outbuf; 5759 } 5760 } 5761 } 5762 5763 /* Need to clean up the search context */ 5764 rms_set_rsal(mynam, NULL, 0, NULL, 0); 5765 sts = rms_free_search_context(&myfab); /* Free search context */ 5766 5767 /* Clean up the extra buffers */ 5768 if (esal != NULL) 5769 PerlMem_free(esal); 5770 PerlMem_free(esa); 5771 if (outbufl != NULL) 5772 PerlMem_free(outbufl); 5773 5774 /* Return the result */ 5775 return ret_spec; 5776 } 5777 5778 /* Common simple case - Expand an already VMS spec */ 5779 static char * 5780 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) { 5781 opts |= PERL_RMSEXPAND_M_VMS_IN; 5782 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 5783 } 5784 5785 /* Common simple case - Expand to a VMS spec */ 5786 static char * 5787 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) { 5788 opts |= PERL_RMSEXPAND_M_VMS; 5789 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 5790 } 5791 5792 5793 /* Entry point used by perl routines */ 5794 static char * 5795 mp_do_rmsexpand 5796 (pTHX_ const char *filespec, 5797 char *outbuf, 5798 int ts, 5799 const char *defspec, 5800 unsigned opts, 5801 int * fs_utf8, 5802 int * dfs_utf8) 5803 { 5804 static char __rmsexpand_retbuf[VMS_MAXRSS]; 5805 char * expanded, *ret_spec, *ret_buf; 5806 5807 expanded = NULL; 5808 ret_buf = outbuf; 5809 if (ret_buf == NULL) { 5810 if (ts) { 5811 Newx(expanded, VMS_MAXRSS, char); 5812 if (expanded == NULL) 5813 _ckvmssts(SS$_INSFMEM); 5814 ret_buf = expanded; 5815 } else { 5816 ret_buf = __rmsexpand_retbuf; 5817 } 5818 } 5819 5820 5821 ret_spec = int_rmsexpand(filespec, ret_buf, defspec, 5822 opts, fs_utf8, dfs_utf8); 5823 5824 if (ret_spec == NULL) { 5825 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 5826 if (expanded) 5827 Safefree(expanded); 5828 } 5829 5830 return ret_spec; 5831 } 5832 /*}}}*/ 5833 /* External entry points */ 5834 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5835 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); } 5836 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5837 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); } 5838 char *Perl_rmsexpand_utf8 5839 (pTHX_ const char *spec, char *buf, const char *def, 5840 unsigned opt, int * fs_utf8, int * dfs_utf8) 5841 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); } 5842 char *Perl_rmsexpand_utf8_ts 5843 (pTHX_ const char *spec, char *buf, const char *def, 5844 unsigned opt, int * fs_utf8, int * dfs_utf8) 5845 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); } 5846 5847 5848 /* 5849 ** The following routines are provided to make life easier when 5850 ** converting among VMS-style and Unix-style directory specifications. 5851 ** All will take input specifications in either VMS or Unix syntax. On 5852 ** failure, all return NULL. If successful, the routines listed below 5853 ** return a pointer to a buffer containing the appropriately 5854 ** reformatted spec (and, therefore, subsequent calls to that routine 5855 ** will clobber the result), while the routines of the same names with 5856 ** a _ts suffix appended will return a pointer to a mallocd string 5857 ** containing the appropriately reformatted spec. 5858 ** In all cases, only explicit syntax is altered; no check is made that 5859 ** the resulting string is valid or that the directory in question 5860 ** actually exists. 5861 ** 5862 ** fileify_dirspec() - convert a directory spec into the name of the 5863 ** directory file (i.e. what you can stat() to see if it's a dir). 5864 ** The style (VMS or Unix) of the result is the same as the style 5865 ** of the parameter passed in. 5866 ** pathify_dirspec() - convert a directory spec into a path (i.e. 5867 ** what you prepend to a filename to indicate what directory it's in). 5868 ** The style (VMS or Unix) of the result is the same as the style 5869 ** of the parameter passed in. 5870 ** tounixpath() - convert a directory spec into a Unix-style path. 5871 ** tovmspath() - convert a directory spec into a VMS-style path. 5872 ** tounixspec() - convert any file spec into a Unix-style file spec. 5873 ** tovmsspec() - convert any file spec into a VMS-style spec. 5874 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec. 5875 ** 5876 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu> 5877 ** Permission is given to distribute this code as part of the Perl 5878 ** standard distribution under the terms of the GNU General Public 5879 ** License or the Perl Artistic License. Copies of each may be 5880 ** found in the Perl standard distribution. 5881 */ 5882 5883 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 5884 static char * 5885 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl) 5886 { 5887 unsigned long int dirlen, retlen, hasfilename = 0; 5888 char *cp1, *cp2, *lastdir; 5889 char *trndir, *vmsdir; 5890 unsigned short int trnlnm_iter_count; 5891 int sts; 5892 if (utf8_fl != NULL) 5893 *utf8_fl = 0; 5894 5895 if (!dir || !*dir) { 5896 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL; 5897 } 5898 dirlen = strlen(dir); 5899 while (dirlen && dir[dirlen-1] == '/') --dirlen; 5900 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */ 5901 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) { 5902 dir = "/sys$disk"; 5903 dirlen = 9; 5904 } 5905 else 5906 dirlen = 1; 5907 } 5908 if (dirlen > (VMS_MAXRSS - 1)) { 5909 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); 5910 return NULL; 5911 } 5912 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5913 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5914 if (!strpbrk(dir+1,"/]>:") && 5915 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) { 5916 strcpy(trndir,*dir == '/' ? dir + 1: dir); 5917 trnlnm_iter_count = 0; 5918 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) { 5919 trnlnm_iter_count++; 5920 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 5921 } 5922 dirlen = strlen(trndir); 5923 } 5924 else { 5925 memcpy(trndir, dir, dirlen); 5926 trndir[dirlen] = '\0'; 5927 } 5928 5929 /* At this point we are done with *dir and use *trndir which is a 5930 * copy that can be modified. *dir must not be modified. 5931 */ 5932 5933 /* If we were handed a rooted logical name or spec, treat it like a 5934 * simple directory, so that 5935 * $ Define myroot dev:[dir.] 5936 * ... do_fileify_dirspec("myroot",buf,1) ... 5937 * does something useful. 5938 */ 5939 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) { 5940 trndir[--dirlen] = '\0'; 5941 trndir[dirlen-1] = ']'; 5942 } 5943 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) { 5944 trndir[--dirlen] = '\0'; 5945 trndir[dirlen-1] = '>'; 5946 } 5947 5948 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) { 5949 /* If we've got an explicit filename, we can just shuffle the string. */ 5950 if (*(cp1+1)) hasfilename = 1; 5951 /* Similarly, we can just back up a level if we've got multiple levels 5952 of explicit directories in a VMS spec which ends with directories. */ 5953 else { 5954 for (cp2 = cp1; cp2 > trndir; cp2--) { 5955 if (*cp2 == '.') { 5956 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) { 5957 /* fix-me, can not scan EFS file specs backward like this */ 5958 *cp2 = *cp1; *cp1 = '\0'; 5959 hasfilename = 1; 5960 break; 5961 } 5962 } 5963 if (*cp2 == '[' || *cp2 == '<') break; 5964 } 5965 } 5966 } 5967 5968 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5969 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5970 cp1 = strpbrk(trndir,"]:>"); 5971 if (hasfilename || !cp1) { /* filename present or not VMS */ 5972 5973 if (trndir[0] == '.') { 5974 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) { 5975 PerlMem_free(trndir); 5976 PerlMem_free(vmsdir); 5977 return int_fileify_dirspec("[]", buf, NULL); 5978 } 5979 else if (trndir[1] == '.' && 5980 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) { 5981 PerlMem_free(trndir); 5982 PerlMem_free(vmsdir); 5983 return int_fileify_dirspec("[-]", buf, NULL); 5984 } 5985 } 5986 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */ 5987 dirlen -= 1; /* to last element */ 5988 lastdir = strrchr(trndir,'/'); 5989 } 5990 else if ((cp1 = strstr(trndir,"/.")) != NULL) { 5991 /* If we have "/." or "/..", VMSify it and let the VMS code 5992 * below expand it, rather than repeating the code to handle 5993 * relative components of a filespec here */ 5994 do { 5995 if (*(cp1+2) == '.') cp1++; 5996 if (*(cp1+2) == '/' || *(cp1+2) == '\0') { 5997 char * ret_chr; 5998 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) { 5999 PerlMem_free(trndir); 6000 PerlMem_free(vmsdir); 6001 return NULL; 6002 } 6003 if (strchr(vmsdir,'/') != NULL) { 6004 /* If int_tovmsspec() returned it, it must have VMS syntax 6005 * delimiters in it, so it's a mixed VMS/Unix spec. We take 6006 * the time to check this here only so we avoid a recursion 6007 * loop; otherwise, gigo. 6008 */ 6009 PerlMem_free(trndir); 6010 PerlMem_free(vmsdir); 6011 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); 6012 return NULL; 6013 } 6014 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) { 6015 PerlMem_free(trndir); 6016 PerlMem_free(vmsdir); 6017 return NULL; 6018 } 6019 ret_chr = int_tounixspec(trndir, buf, utf8_fl); 6020 PerlMem_free(trndir); 6021 PerlMem_free(vmsdir); 6022 return ret_chr; 6023 } 6024 cp1++; 6025 } while ((cp1 = strstr(cp1,"/.")) != NULL); 6026 lastdir = strrchr(trndir,'/'); 6027 } 6028 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) { 6029 char * ret_chr; 6030 /* Ditto for specs that end in an MFD -- let the VMS code 6031 * figure out whether it's a real device or a rooted logical. */ 6032 6033 /* This should not happen any more. Allowing the fake /000000 6034 * in a UNIX pathname causes all sorts of problems when trying 6035 * to run in UNIX emulation. So the VMS to UNIX conversions 6036 * now remove the fake /000000 directories. 6037 */ 6038 6039 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0'; 6040 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) { 6041 PerlMem_free(trndir); 6042 PerlMem_free(vmsdir); 6043 return NULL; 6044 } 6045 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) { 6046 PerlMem_free(trndir); 6047 PerlMem_free(vmsdir); 6048 return NULL; 6049 } 6050 ret_chr = int_tounixspec(trndir, buf, utf8_fl); 6051 PerlMem_free(trndir); 6052 PerlMem_free(vmsdir); 6053 return ret_chr; 6054 } 6055 else { 6056 6057 if ( !(lastdir = cp1 = strrchr(trndir,'/')) && 6058 !(lastdir = cp1 = strrchr(trndir,']')) && 6059 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir; 6060 6061 cp2 = strrchr(cp1,'.'); 6062 if (cp2) { 6063 int e_len, vs_len = 0; 6064 int is_dir = 0; 6065 char * cp3; 6066 cp3 = strchr(cp2,';'); 6067 e_len = strlen(cp2); 6068 if (cp3) { 6069 vs_len = strlen(cp3); 6070 e_len = e_len - vs_len; 6071 } 6072 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len); 6073 if (!is_dir) { 6074 if (!decc_efs_charset) { 6075 /* If this is not EFS, then not a directory */ 6076 PerlMem_free(trndir); 6077 PerlMem_free(vmsdir); 6078 set_errno(ENOTDIR); 6079 set_vaxc_errno(RMS$_DIR); 6080 return NULL; 6081 } 6082 } else { 6083 /* Ok, here we have an issue, technically if a .dir shows */ 6084 /* from inside a directory, then we should treat it as */ 6085 /* xxx^.dir.dir. But we do not have that context at this */ 6086 /* point unless this is totally restructured, so we remove */ 6087 /* The .dir for now, and fix this better later */ 6088 dirlen = cp2 - trndir; 6089 } 6090 if (decc_efs_charset && !strchr(trndir,'/')) { 6091 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */ 6092 char *cp4 = is_dir ? (cp2 - 1) : cp2; 6093 6094 for (; cp4 > cp1; cp4--) { 6095 if (*cp4 == '.') { 6096 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) { 6097 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1); 6098 *cp4 = '^'; 6099 dirlen++; 6100 } 6101 } 6102 } 6103 } 6104 } 6105 6106 } 6107 6108 retlen = dirlen + 6; 6109 memcpy(buf, trndir, dirlen); 6110 buf[dirlen] = '\0'; 6111 6112 /* We've picked up everything up to the directory file name. 6113 Now just add the type and version, and we're set. */ 6114 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) 6115 strcat(buf,".dir;1"); 6116 else 6117 strcat(buf,".DIR;1"); 6118 PerlMem_free(trndir); 6119 PerlMem_free(vmsdir); 6120 return buf; 6121 } 6122 else { /* VMS-style directory spec */ 6123 6124 char *esa, *esal, term, *cp; 6125 char *my_esa; 6126 int my_esa_len; 6127 unsigned long int cmplen, haslower = 0; 6128 struct FAB dirfab = cc$rms_fab; 6129 rms_setup_nam(savnam); 6130 rms_setup_nam(dirnam); 6131 6132 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 6133 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6134 esal = NULL; 6135 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6136 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 6137 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6138 #endif 6139 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir)); 6140 rms_bind_fab_nam(dirfab, dirnam); 6141 rms_set_dna(dirfab, dirnam, ".DIR;1", 6); 6142 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 6143 #ifdef NAM$M_NO_SHORT_UPCASE 6144 if (decc_efs_case_preserve) 6145 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6146 #endif 6147 6148 for (cp = trndir; *cp; cp++) 6149 if (islower(*cp)) { haslower = 1; break; } 6150 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) { 6151 if ((dirfab.fab$l_sts == RMS$_DIR) || 6152 (dirfab.fab$l_sts == RMS$_DNF) || 6153 (dirfab.fab$l_sts == RMS$_PRV)) { 6154 rms_set_nam_nop(dirnam, NAM$M_SYNCHK); 6155 sts = sys$parse(&dirfab); 6156 } 6157 if (!sts) { 6158 PerlMem_free(esa); 6159 if (esal != NULL) 6160 PerlMem_free(esal); 6161 PerlMem_free(trndir); 6162 PerlMem_free(vmsdir); 6163 set_errno(EVMSERR); 6164 set_vaxc_errno(dirfab.fab$l_sts); 6165 return NULL; 6166 } 6167 } 6168 else { 6169 savnam = dirnam; 6170 /* Does the file really exist? */ 6171 if (sys$search(&dirfab)& STS$K_SUCCESS) { 6172 /* Yes; fake the fnb bits so we'll check type below */ 6173 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER)); 6174 } 6175 else { /* No; just work with potential name */ 6176 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam; 6177 else { 6178 int fab_sts; 6179 fab_sts = dirfab.fab$l_sts; 6180 sts = rms_free_search_context(&dirfab); 6181 PerlMem_free(esa); 6182 if (esal != NULL) 6183 PerlMem_free(esal); 6184 PerlMem_free(trndir); 6185 PerlMem_free(vmsdir); 6186 set_errno(EVMSERR); set_vaxc_errno(fab_sts); 6187 return NULL; 6188 } 6189 } 6190 } 6191 6192 /* Make sure we are using the right buffer */ 6193 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6194 if (esal != NULL) { 6195 my_esa = esal; 6196 my_esa_len = rms_nam_esll(dirnam); 6197 } else { 6198 #endif 6199 my_esa = esa; 6200 my_esa_len = rms_nam_esl(dirnam); 6201 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6202 } 6203 #endif 6204 my_esa[my_esa_len] = '\0'; 6205 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) { 6206 cp1 = strchr(my_esa,']'); 6207 if (!cp1) cp1 = strchr(my_esa,'>'); 6208 if (cp1) { /* Should always be true */ 6209 my_esa_len -= cp1 - my_esa - 1; 6210 memmove(my_esa, cp1 + 1, my_esa_len); 6211 } 6212 } 6213 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */ 6214 /* Yep; check version while we're at it, if it's there. */ 6215 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4; 6216 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) { 6217 /* Something other than .DIR[;1]. Bzzt. */ 6218 sts = rms_free_search_context(&dirfab); 6219 PerlMem_free(esa); 6220 if (esal != NULL) 6221 PerlMem_free(esal); 6222 PerlMem_free(trndir); 6223 PerlMem_free(vmsdir); 6224 set_errno(ENOTDIR); 6225 set_vaxc_errno(RMS$_DIR); 6226 return NULL; 6227 } 6228 } 6229 6230 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) { 6231 /* They provided at least the name; we added the type, if necessary, */ 6232 my_strlcpy(buf, my_esa, VMS_MAXRSS); 6233 sts = rms_free_search_context(&dirfab); 6234 PerlMem_free(trndir); 6235 PerlMem_free(esa); 6236 if (esal != NULL) 6237 PerlMem_free(esal); 6238 PerlMem_free(vmsdir); 6239 return buf; 6240 } 6241 if ((cp1 = strstr(esa,".][000000]")) != NULL) { 6242 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2; 6243 *cp1 = '\0'; 6244 my_esa_len -= 9; 6245 } 6246 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>'); 6247 if (cp1 == NULL) { /* should never happen */ 6248 sts = rms_free_search_context(&dirfab); 6249 PerlMem_free(trndir); 6250 PerlMem_free(esa); 6251 if (esal != NULL) 6252 PerlMem_free(esal); 6253 PerlMem_free(vmsdir); 6254 return NULL; 6255 } 6256 term = *cp1; 6257 *cp1 = '\0'; 6258 retlen = strlen(my_esa); 6259 cp1 = strrchr(my_esa,'.'); 6260 /* ODS-5 directory specifications can have extra "." in them. */ 6261 /* Fix-me, can not scan EFS file specifications backwards */ 6262 while (cp1 != NULL) { 6263 if ((cp1-1 == my_esa) || (*(cp1-1) != '^')) 6264 break; 6265 else { 6266 cp1--; 6267 while ((cp1 > my_esa) && (*cp1 != '.')) 6268 cp1--; 6269 } 6270 if (cp1 == my_esa) 6271 cp1 = NULL; 6272 } 6273 6274 if ((cp1) != NULL) { 6275 /* There's more than one directory in the path. Just roll back. */ 6276 *cp1 = term; 6277 my_strlcpy(buf, my_esa, VMS_MAXRSS); 6278 } 6279 else { 6280 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) { 6281 /* Go back and expand rooted logical name */ 6282 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL); 6283 #ifdef NAM$M_NO_SHORT_UPCASE 6284 if (decc_efs_case_preserve) 6285 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6286 #endif 6287 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) { 6288 sts = rms_free_search_context(&dirfab); 6289 PerlMem_free(esa); 6290 if (esal != NULL) 6291 PerlMem_free(esal); 6292 PerlMem_free(trndir); 6293 PerlMem_free(vmsdir); 6294 set_errno(EVMSERR); 6295 set_vaxc_errno(dirfab.fab$l_sts); 6296 return NULL; 6297 } 6298 6299 /* This changes the length of the string of course */ 6300 if (esal != NULL) { 6301 my_esa_len = rms_nam_esll(dirnam); 6302 } else { 6303 my_esa_len = rms_nam_esl(dirnam); 6304 } 6305 6306 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */ 6307 cp1 = strstr(my_esa,"]["); 6308 if (!cp1) cp1 = strstr(my_esa,"]<"); 6309 dirlen = cp1 - my_esa; 6310 memcpy(buf, my_esa, dirlen); 6311 if (!strncmp(cp1+2,"000000]",7)) { 6312 buf[dirlen-1] = '\0'; 6313 /* fix-me Not full ODS-5, just extra dots in directories for now */ 6314 cp1 = buf + dirlen - 1; 6315 while (cp1 > buf) 6316 { 6317 if (*cp1 == '[') 6318 break; 6319 if (*cp1 == '.') { 6320 if (*(cp1-1) != '^') 6321 break; 6322 } 6323 cp1--; 6324 } 6325 if (*cp1 == '.') *cp1 = ']'; 6326 else { 6327 memmove(cp1+8, cp1+1, buf+dirlen-cp1); 6328 memmove(cp1+1,"000000]",7); 6329 } 6330 } 6331 else { 6332 memmove(buf+dirlen, cp1+2, retlen-dirlen); 6333 buf[retlen] = '\0'; 6334 /* Convert last '.' to ']' */ 6335 cp1 = buf+retlen-1; 6336 while (*cp != '[') { 6337 cp1--; 6338 if (*cp1 == '.') { 6339 /* Do not trip on extra dots in ODS-5 directories */ 6340 if ((cp1 == buf) || (*(cp1-1) != '^')) 6341 break; 6342 } 6343 } 6344 if (*cp1 == '.') *cp1 = ']'; 6345 else { 6346 memmove(cp1+8, cp1+1, buf+dirlen-cp1); 6347 memmove(cp1+1,"000000]",7); 6348 } 6349 } 6350 } 6351 else { /* This is a top-level dir. Add the MFD to the path. */ 6352 cp1 = my_esa; 6353 cp2 = buf; 6354 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++); 6355 strcpy(cp2,":[000000]"); 6356 cp1 += 2; 6357 strcpy(cp2+9,cp1); 6358 } 6359 } 6360 sts = rms_free_search_context(&dirfab); 6361 /* We've set up the string up through the filename. Add the 6362 type and version, and we're done. */ 6363 strcat(buf,".DIR;1"); 6364 6365 /* $PARSE may have upcased filespec, so convert output to lower 6366 * case if input contained any lowercase characters. */ 6367 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf); 6368 PerlMem_free(trndir); 6369 PerlMem_free(esa); 6370 if (esal != NULL) 6371 PerlMem_free(esal); 6372 PerlMem_free(vmsdir); 6373 return buf; 6374 } 6375 } /* end of int_fileify_dirspec() */ 6376 6377 6378 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 6379 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl) 6380 { 6381 static char __fileify_retbuf[VMS_MAXRSS]; 6382 char * fileified, *ret_spec, *ret_buf; 6383 6384 fileified = NULL; 6385 ret_buf = buf; 6386 if (ret_buf == NULL) { 6387 if (ts) { 6388 Newx(fileified, VMS_MAXRSS, char); 6389 if (fileified == NULL) 6390 _ckvmssts(SS$_INSFMEM); 6391 ret_buf = fileified; 6392 } else { 6393 ret_buf = __fileify_retbuf; 6394 } 6395 } 6396 6397 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl); 6398 6399 if (ret_spec == NULL) { 6400 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 6401 if (fileified) 6402 Safefree(fileified); 6403 } 6404 6405 return ret_spec; 6406 } /* end of do_fileify_dirspec() */ 6407 /*}}}*/ 6408 6409 /* External entry points */ 6410 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf) 6411 { return do_fileify_dirspec(dir,buf,0,NULL); } 6412 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf) 6413 { return do_fileify_dirspec(dir,buf,1,NULL); } 6414 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl) 6415 { return do_fileify_dirspec(dir,buf,0,utf8_fl); } 6416 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl) 6417 { return do_fileify_dirspec(dir,buf,1,utf8_fl); } 6418 6419 static char * int_pathify_dirspec_simple(const char * dir, char * buf, 6420 char * v_spec, int v_len, char * r_spec, int r_len, 6421 char * d_spec, int d_len, char * n_spec, int n_len, 6422 char * e_spec, int e_len, char * vs_spec, int vs_len) { 6423 6424 /* VMS specification - Try to do this the simple way */ 6425 if ((v_len + r_len > 0) || (d_len > 0)) { 6426 int is_dir; 6427 6428 /* No name or extension component, already a directory */ 6429 if ((n_len + e_len + vs_len) == 0) { 6430 strcpy(buf, dir); 6431 return buf; 6432 } 6433 6434 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */ 6435 /* This results from catfile() being used instead of catdir() */ 6436 /* So even though it should not work, we need to allow it */ 6437 6438 /* If this is .DIR;1 then do a simple conversion */ 6439 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len); 6440 if (is_dir || (e_len == 0) && (d_len > 0)) { 6441 int len; 6442 len = v_len + r_len + d_len - 1; 6443 char dclose = d_spec[d_len - 1]; 6444 memcpy(buf, dir, len); 6445 buf[len] = '.'; 6446 len++; 6447 memcpy(&buf[len], n_spec, n_len); 6448 len += n_len; 6449 buf[len] = dclose; 6450 buf[len + 1] = '\0'; 6451 return buf; 6452 } 6453 6454 #ifdef HAS_SYMLINK 6455 else if (d_len > 0) { 6456 /* In the olden days, a directory needed to have a .DIR */ 6457 /* extension to be a valid directory, but now it could */ 6458 /* be a symbolic link */ 6459 int len; 6460 len = v_len + r_len + d_len - 1; 6461 char dclose = d_spec[d_len - 1]; 6462 memcpy(buf, dir, len); 6463 buf[len] = '.'; 6464 len++; 6465 memcpy(&buf[len], n_spec, n_len); 6466 len += n_len; 6467 if (e_len > 0) { 6468 if (decc_efs_charset) { 6469 if (e_len == 4 6470 && (toupper(e_spec[1]) == 'D') 6471 && (toupper(e_spec[2]) == 'I') 6472 && (toupper(e_spec[3]) == 'R')) { 6473 6474 /* Corner case: directory spec with invalid version. 6475 * Valid would have followed is_dir path above. 6476 */ 6477 SETERRNO(ENOTDIR, RMS$_DIR); 6478 return NULL; 6479 } 6480 else { 6481 buf[len] = '^'; 6482 len++; 6483 memcpy(&buf[len], e_spec, e_len); 6484 len += e_len; 6485 } 6486 } 6487 else { 6488 SETERRNO(ENOTDIR, RMS$_DIR); 6489 return NULL; 6490 } 6491 } 6492 buf[len] = dclose; 6493 buf[len + 1] = '\0'; 6494 return buf; 6495 } 6496 #else 6497 else { 6498 set_vaxc_errno(RMS$_DIR); 6499 set_errno(ENOTDIR); 6500 return NULL; 6501 } 6502 #endif 6503 } 6504 set_vaxc_errno(RMS$_DIR); 6505 set_errno(ENOTDIR); 6506 return NULL; 6507 } 6508 6509 6510 /* Internal routine to make sure or convert a directory to be in a */ 6511 /* path specification. No utf8 flag because it is not changed or used */ 6512 static char *int_pathify_dirspec(const char *dir, char *buf) 6513 { 6514 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 6515 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 6516 char * exp_spec, *ret_spec; 6517 char * trndir; 6518 unsigned short int trnlnm_iter_count; 6519 STRLEN trnlen; 6520 int need_to_lower; 6521 6522 if (vms_debug_fileify) { 6523 if (dir == NULL) 6524 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n"); 6525 else 6526 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir); 6527 } 6528 6529 /* We may need to lower case the result if we translated */ 6530 /* a logical name or got the current working directory */ 6531 need_to_lower = 0; 6532 6533 if (!dir || !*dir) { 6534 set_errno(EINVAL); 6535 set_vaxc_errno(SS$_BADPARAM); 6536 return NULL; 6537 } 6538 6539 trndir = (char *)PerlMem_malloc(VMS_MAXRSS); 6540 if (trndir == NULL) 6541 _ckvmssts_noperl(SS$_INSFMEM); 6542 6543 /* If no directory specified use the current default */ 6544 if (*dir) 6545 my_strlcpy(trndir, dir, VMS_MAXRSS); 6546 else { 6547 getcwd(trndir, VMS_MAXRSS - 1); 6548 need_to_lower = 1; 6549 } 6550 6551 /* now deal with bare names that could be logical names */ 6552 trnlnm_iter_count = 0; 6553 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords 6554 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) { 6555 trnlnm_iter_count++; 6556 need_to_lower = 1; 6557 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) 6558 break; 6559 trnlen = strlen(trndir); 6560 6561 /* Trap simple rooted lnms, and return lnm:[000000] */ 6562 if (!strcmp(trndir+trnlen-2,".]")) { 6563 my_strlcpy(buf, dir, VMS_MAXRSS); 6564 strcat(buf, ":[000000]"); 6565 PerlMem_free(trndir); 6566 6567 if (vms_debug_fileify) { 6568 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf); 6569 } 6570 return buf; 6571 } 6572 } 6573 6574 /* At this point we do not work with *dir, but the copy in *trndir */ 6575 6576 if (need_to_lower && !decc_efs_case_preserve) { 6577 /* Legacy mode, lower case the returned value */ 6578 __mystrtolower(trndir); 6579 } 6580 6581 6582 /* Some special cases, '..', '.' */ 6583 sts = 0; 6584 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) { 6585 /* Force UNIX filespec */ 6586 sts = 1; 6587 6588 } else { 6589 /* Is this Unix or VMS format? */ 6590 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len, 6591 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 6592 &e_len, &vs_spec, &vs_len); 6593 if (sts == 0) { 6594 6595 /* Just a filename? */ 6596 if ((v_len + r_len + d_len) == 0) { 6597 6598 /* Now we have a problem, this could be Unix or VMS */ 6599 /* We have to guess. .DIR usually means VMS */ 6600 6601 /* In UNIX report mode, the .DIR extension is removed */ 6602 /* if one shows up, it is for a non-directory or a directory */ 6603 /* in EFS charset mode */ 6604 6605 /* So if we are in Unix report mode, assume that this */ 6606 /* is a relative Unix directory specification */ 6607 6608 sts = 1; 6609 if (!decc_filename_unix_report && decc_efs_charset) { 6610 int is_dir; 6611 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len); 6612 6613 if (is_dir) { 6614 /* Traditional mode, assume .DIR is directory */ 6615 buf[0] = '['; 6616 buf[1] = '.'; 6617 memcpy(&buf[2], n_spec, n_len); 6618 buf[n_len + 2] = ']'; 6619 buf[n_len + 3] = '\0'; 6620 PerlMem_free(trndir); 6621 if (vms_debug_fileify) { 6622 fprintf(stderr, 6623 "int_pathify_dirspec: buf = %s\n", 6624 buf); 6625 } 6626 return buf; 6627 } 6628 } 6629 } 6630 } 6631 } 6632 if (sts == 0) { 6633 ret_spec = int_pathify_dirspec_simple(trndir, buf, 6634 v_spec, v_len, r_spec, r_len, 6635 d_spec, d_len, n_spec, n_len, 6636 e_spec, e_len, vs_spec, vs_len); 6637 6638 if (ret_spec != NULL) { 6639 PerlMem_free(trndir); 6640 if (vms_debug_fileify) { 6641 fprintf(stderr, 6642 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6643 } 6644 return ret_spec; 6645 } 6646 6647 /* Simple way did not work, which means that a logical name */ 6648 /* was present for the directory specification. */ 6649 /* Need to use an rmsexpand variant to decode it completely */ 6650 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS); 6651 if (exp_spec == NULL) 6652 _ckvmssts_noperl(SS$_INSFMEM); 6653 6654 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG); 6655 if (ret_spec != NULL) { 6656 sts = vms_split_path(exp_spec, &v_spec, &v_len, 6657 &r_spec, &r_len, &d_spec, &d_len, 6658 &n_spec, &n_len, &e_spec, 6659 &e_len, &vs_spec, &vs_len); 6660 if (sts == 0) { 6661 ret_spec = int_pathify_dirspec_simple( 6662 exp_spec, buf, v_spec, v_len, r_spec, r_len, 6663 d_spec, d_len, n_spec, n_len, 6664 e_spec, e_len, vs_spec, vs_len); 6665 6666 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) { 6667 /* Legacy mode, lower case the returned value */ 6668 __mystrtolower(ret_spec); 6669 } 6670 } else { 6671 set_vaxc_errno(RMS$_DIR); 6672 set_errno(ENOTDIR); 6673 ret_spec = NULL; 6674 } 6675 } 6676 PerlMem_free(exp_spec); 6677 PerlMem_free(trndir); 6678 if (vms_debug_fileify) { 6679 if (ret_spec == NULL) 6680 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n"); 6681 else 6682 fprintf(stderr, 6683 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6684 } 6685 return ret_spec; 6686 6687 } else { 6688 /* Unix specification, Could be trivial conversion, */ 6689 /* but have to deal with trailing '.dir' or extra '.' */ 6690 6691 char * lastdot; 6692 char * lastslash; 6693 int is_dir; 6694 STRLEN dir_len = strlen(trndir); 6695 6696 lastslash = strrchr(trndir, '/'); 6697 if (lastslash == NULL) 6698 lastslash = trndir; 6699 else 6700 lastslash++; 6701 6702 lastdot = NULL; 6703 6704 /* '..' or '.' are valid directory components */ 6705 is_dir = 0; 6706 if (lastslash[0] == '.') { 6707 if (lastslash[1] == '\0') { 6708 is_dir = 1; 6709 } else if (lastslash[1] == '.') { 6710 if (lastslash[2] == '\0') { 6711 is_dir = 1; 6712 } else { 6713 /* And finally allow '...' */ 6714 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) { 6715 is_dir = 1; 6716 } 6717 } 6718 } 6719 } 6720 6721 if (!is_dir) { 6722 lastdot = strrchr(lastslash, '.'); 6723 } 6724 if (lastdot != NULL) { 6725 STRLEN e_len; 6726 /* '.dir' is discarded, and any other '.' is invalid */ 6727 e_len = strlen(lastdot); 6728 6729 is_dir = is_dir_ext(lastdot, e_len, NULL, 0); 6730 6731 if (is_dir) { 6732 dir_len = dir_len - 4; 6733 } 6734 } 6735 6736 my_strlcpy(buf, trndir, VMS_MAXRSS); 6737 if (buf[dir_len - 1] != '/') { 6738 buf[dir_len] = '/'; 6739 buf[dir_len + 1] = '\0'; 6740 } 6741 6742 /* Under ODS-2 rules, '.' becomes '_', so fix it up */ 6743 if (!decc_efs_charset) { 6744 int dir_start = 0; 6745 char * str = buf; 6746 if (str[0] == '.') { 6747 char * dots = str; 6748 int cnt = 1; 6749 while ((dots[cnt] == '.') && (cnt < 3)) 6750 cnt++; 6751 if (cnt <= 3) { 6752 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) { 6753 dir_start = 1; 6754 str += cnt; 6755 } 6756 } 6757 } 6758 for (; *str; ++str) { 6759 while (*str == '/') { 6760 dir_start = 1; 6761 *str++; 6762 } 6763 if (dir_start) { 6764 6765 /* Have to skip up to three dots which could be */ 6766 /* directories, 3 dots being a VMS extension for Perl */ 6767 char * dots = str; 6768 int cnt = 0; 6769 while ((dots[cnt] == '.') && (cnt < 3)) { 6770 cnt++; 6771 } 6772 if (dots[cnt] == '\0') 6773 break; 6774 if ((cnt > 1) && (dots[cnt] != '/')) { 6775 dir_start = 0; 6776 } else { 6777 str += cnt; 6778 } 6779 6780 /* too many dots? */ 6781 if ((cnt == 0) || (cnt > 3)) { 6782 dir_start = 0; 6783 } 6784 } 6785 if (!dir_start && (*str == '.')) { 6786 *str = '_'; 6787 } 6788 } 6789 } 6790 PerlMem_free(trndir); 6791 ret_spec = buf; 6792 if (vms_debug_fileify) { 6793 if (ret_spec == NULL) 6794 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n"); 6795 else 6796 fprintf(stderr, 6797 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6798 } 6799 return ret_spec; 6800 } 6801 } 6802 6803 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/ 6804 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl) 6805 { 6806 static char __pathify_retbuf[VMS_MAXRSS]; 6807 char * pathified, *ret_spec, *ret_buf; 6808 6809 pathified = NULL; 6810 ret_buf = buf; 6811 if (ret_buf == NULL) { 6812 if (ts) { 6813 Newx(pathified, VMS_MAXRSS, char); 6814 if (pathified == NULL) 6815 _ckvmssts(SS$_INSFMEM); 6816 ret_buf = pathified; 6817 } else { 6818 ret_buf = __pathify_retbuf; 6819 } 6820 } 6821 6822 ret_spec = int_pathify_dirspec(dir, ret_buf); 6823 6824 if (ret_spec == NULL) { 6825 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 6826 if (pathified) 6827 Safefree(pathified); 6828 } 6829 6830 return ret_spec; 6831 6832 } /* end of do_pathify_dirspec() */ 6833 6834 6835 /* External entry points */ 6836 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf) 6837 { return do_pathify_dirspec(dir,buf,0,NULL); } 6838 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf) 6839 { return do_pathify_dirspec(dir,buf,1,NULL); } 6840 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl) 6841 { return do_pathify_dirspec(dir,buf,0,utf8_fl); } 6842 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl) 6843 { return do_pathify_dirspec(dir,buf,1,utf8_fl); } 6844 6845 /* Internal tounixspec routine that does not use a thread context */ 6846 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/ 6847 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl) 6848 { 6849 char *dirend, *cp1, *cp3, *tmp; 6850 const char *cp2; 6851 int dirlen; 6852 unsigned short int trnlnm_iter_count; 6853 int cmp_rslt, outchars_added; 6854 if (utf8_fl != NULL) 6855 *utf8_fl = 0; 6856 6857 if (vms_debug_fileify) { 6858 if (spec == NULL) 6859 fprintf(stderr, "int_tounixspec: spec = NULL\n"); 6860 else 6861 fprintf(stderr, "int_tounixspec: spec = %s\n", spec); 6862 } 6863 6864 6865 if (spec == NULL) { 6866 set_errno(EINVAL); 6867 set_vaxc_errno(SS$_BADPARAM); 6868 return NULL; 6869 } 6870 if (strlen(spec) > (VMS_MAXRSS-1)) { 6871 set_errno(E2BIG); 6872 set_vaxc_errno(SS$_BUFFEROVF); 6873 return NULL; 6874 } 6875 6876 /* New VMS specific format needs translation 6877 * glob passes filenames with trailing '\n' and expects this preserved. 6878 */ 6879 if (decc_posix_compliant_pathnames) { 6880 if (strncmp(spec, "\"^UP^", 5) == 0) { 6881 char * uspec; 6882 char *tunix; 6883 int tunix_len; 6884 int nl_flag; 6885 6886 tunix = (char *)PerlMem_malloc(VMS_MAXRSS); 6887 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6888 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS); 6889 nl_flag = 0; 6890 if (tunix[tunix_len - 1] == '\n') { 6891 tunix[tunix_len - 1] = '\"'; 6892 tunix[tunix_len] = '\0'; 6893 tunix_len--; 6894 nl_flag = 1; 6895 } 6896 uspec = decc$translate_vms(tunix); 6897 PerlMem_free(tunix); 6898 if ((int)uspec > 0) { 6899 my_strlcpy(rslt, uspec, VMS_MAXRSS); 6900 if (nl_flag) { 6901 strcat(rslt,"\n"); 6902 } 6903 else { 6904 /* If we can not translate it, makemaker wants as-is */ 6905 my_strlcpy(rslt, spec, VMS_MAXRSS); 6906 } 6907 return rslt; 6908 } 6909 } 6910 } 6911 6912 cmp_rslt = 0; /* Presume VMS */ 6913 cp1 = strchr(spec, '/'); 6914 if (cp1 == NULL) 6915 cmp_rslt = 0; 6916 6917 /* Look for EFS ^/ */ 6918 if (decc_efs_charset) { 6919 while (cp1 != NULL) { 6920 cp2 = cp1 - 1; 6921 if (*cp2 != '^') { 6922 /* Found illegal VMS, assume UNIX */ 6923 cmp_rslt = 1; 6924 break; 6925 } 6926 cp1++; 6927 cp1 = strchr(cp1, '/'); 6928 } 6929 } 6930 6931 /* Look for "." and ".." */ 6932 if (decc_filename_unix_report) { 6933 if (spec[0] == '.') { 6934 if ((spec[1] == '\0') || (spec[1] == '\n')) { 6935 cmp_rslt = 1; 6936 } 6937 else { 6938 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) { 6939 cmp_rslt = 1; 6940 } 6941 } 6942 } 6943 } 6944 6945 cp1 = rslt; 6946 cp2 = spec; 6947 6948 /* This is already UNIX or at least nothing VMS understands, 6949 * so all we can reasonably do is unescape extended chars. 6950 */ 6951 if (cmp_rslt) { 6952 while (*cp2) { 6953 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 6954 cp1 += outchars_added; 6955 } 6956 *cp1 = '\0'; 6957 if (vms_debug_fileify) { 6958 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 6959 } 6960 return rslt; 6961 } 6962 6963 dirend = strrchr(spec,']'); 6964 if (dirend == NULL) dirend = strrchr(spec,'>'); 6965 if (dirend == NULL) dirend = strchr(spec,':'); 6966 if (dirend == NULL) { 6967 while (*cp2) { 6968 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 6969 cp1 += outchars_added; 6970 } 6971 *cp1 = '\0'; 6972 if (vms_debug_fileify) { 6973 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 6974 } 6975 return rslt; 6976 } 6977 6978 /* Special case 1 - sys$posix_root = / */ 6979 if (!decc_disable_posix_root) { 6980 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) { 6981 *cp1 = '/'; 6982 cp1++; 6983 cp2 = cp2 + 15; 6984 } 6985 } 6986 6987 /* Special case 2 - Convert NLA0: to /dev/null */ 6988 cmp_rslt = strncasecmp(spec,"NLA0:", 5); 6989 if (cmp_rslt == 0) { 6990 strcpy(rslt, "/dev/null"); 6991 cp1 = cp1 + 9; 6992 cp2 = cp2 + 5; 6993 if (spec[6] != '\0') { 6994 cp1[9] = '/'; 6995 cp1++; 6996 cp2++; 6997 } 6998 } 6999 7000 /* Also handle special case "SYS$SCRATCH:" */ 7001 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12); 7002 tmp = (char *)PerlMem_malloc(VMS_MAXRSS); 7003 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7004 if (cmp_rslt == 0) { 7005 int islnm; 7006 7007 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1); 7008 if (!islnm) { 7009 strcpy(rslt, "/tmp"); 7010 cp1 = cp1 + 4; 7011 cp2 = cp2 + 12; 7012 if (spec[12] != '\0') { 7013 cp1[4] = '/'; 7014 cp1++; 7015 cp2++; 7016 } 7017 } 7018 } 7019 7020 if (*cp2 != '[' && *cp2 != '<') { 7021 *(cp1++) = '/'; 7022 } 7023 else { /* the VMS spec begins with directories */ 7024 cp2++; 7025 if (*cp2 == ']' || *cp2 == '>') { 7026 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0'; 7027 PerlMem_free(tmp); 7028 return rslt; 7029 } 7030 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */ 7031 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) { 7032 PerlMem_free(tmp); 7033 if (vms_debug_fileify) { 7034 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7035 } 7036 return NULL; 7037 } 7038 trnlnm_iter_count = 0; 7039 do { 7040 cp3 = tmp; 7041 while (*cp3 != ':' && *cp3) cp3++; 7042 *(cp3++) = '\0'; 7043 if (strchr(cp3,']') != NULL) break; 7044 trnlnm_iter_count++; 7045 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break; 7046 } while (vmstrnenv(tmp,tmp,0,fildev,0)); 7047 cp1 = rslt; 7048 cp3 = tmp; 7049 *(cp1++) = '/'; 7050 while (*cp3) { 7051 *(cp1++) = *(cp3++); 7052 if (cp1 - rslt > (VMS_MAXRSS - 1)) { 7053 PerlMem_free(tmp); 7054 set_errno(ENAMETOOLONG); 7055 set_vaxc_errno(SS$_BUFFEROVF); 7056 if (vms_debug_fileify) { 7057 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7058 } 7059 return NULL; /* No room */ 7060 } 7061 } 7062 *(cp1++) = '/'; 7063 } 7064 if ((*cp2 == '^')) { 7065 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7066 cp1 += outchars_added; 7067 } 7068 else if ( *cp2 == '.') { 7069 if (*(cp2+1) == '.' && *(cp2+2) == '.') { 7070 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 7071 cp2 += 3; 7072 } 7073 else cp2++; 7074 } 7075 } 7076 PerlMem_free(tmp); 7077 for (; cp2 <= dirend; cp2++) { 7078 if ((*cp2 == '^')) { 7079 /* EFS file escape, pass the next character as is */ 7080 /* Fix me: HEX encoding for Unicode not implemented */ 7081 *(cp1++) = *(++cp2); 7082 /* An escaped dot stays as is -- don't convert to slash */ 7083 if (*cp2 == '.') cp2++; 7084 } 7085 if (*cp2 == ':') { 7086 *(cp1++) = '/'; 7087 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 7088 } 7089 else if (*cp2 == ']' || *cp2 == '>') { 7090 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */ 7091 } 7092 else if ((*cp2 == '.') && (*cp2-1 != '^')) { 7093 *(cp1++) = '/'; 7094 if (*(cp2+1) == ']' || *(cp2+1) == '>') { 7095 while (*(cp2+1) == ']' || *(cp2+1) == '>' || 7096 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 7097 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' || 7098 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7; 7099 } 7100 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') { 7101 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/'; 7102 cp2 += 2; 7103 } 7104 } 7105 else if (*cp2 == '-') { 7106 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') { 7107 while (*cp2 == '-') { 7108 cp2++; 7109 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 7110 } 7111 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */ 7112 /* filespecs like */ 7113 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */ 7114 if (vms_debug_fileify) { 7115 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7116 } 7117 return NULL; 7118 } 7119 } 7120 else *(cp1++) = *cp2; 7121 } 7122 else *(cp1++) = *cp2; 7123 } 7124 /* Translate the rest of the filename. */ 7125 while (*cp2) { 7126 int dot_seen = 0; 7127 switch(*cp2) { 7128 /* Fixme - for compatibility with the CRTL we should be removing */ 7129 /* spaces from the file specifications, but this may show that */ 7130 /* some tests that were appearing to pass are not really passing */ 7131 case '%': 7132 cp2++; 7133 *(cp1++) = '?'; 7134 break; 7135 case '^': 7136 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7137 cp1 += outchars_added; 7138 break; 7139 case ';': 7140 if (decc_filename_unix_no_version) { 7141 /* Easy, drop the version */ 7142 while (*cp2) 7143 cp2++; 7144 break; 7145 } else { 7146 /* Punt - passing the version as a dot will probably */ 7147 /* break perl in weird ways, but so did passing */ 7148 /* through the ; as a version. Follow the CRTL and */ 7149 /* hope for the best. */ 7150 cp2++; 7151 *(cp1++) = '.'; 7152 } 7153 break; 7154 case '.': 7155 if (dot_seen) { 7156 /* We will need to fix this properly later */ 7157 /* As Perl may be installed on an ODS-5 volume, but not */ 7158 /* have the EFS_CHARSET enabled, it still may encounter */ 7159 /* filenames with extra dots in them, and a precedent got */ 7160 /* set which allowed them to work, that we will uphold here */ 7161 /* If extra dots are present in a name and no ^ is on them */ 7162 /* VMS assumes that the first one is the extension delimiter */ 7163 /* the rest have an implied ^. */ 7164 7165 /* this is also a conflict as the . is also a version */ 7166 /* delimiter in VMS, */ 7167 7168 *(cp1++) = *(cp2++); 7169 break; 7170 } 7171 dot_seen = 1; 7172 /* This is an extension */ 7173 if (decc_readdir_dropdotnotype) { 7174 cp2++; 7175 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) { 7176 /* Drop the dot for the extension */ 7177 break; 7178 } else { 7179 *(cp1++) = '.'; 7180 } 7181 break; 7182 } 7183 default: 7184 *(cp1++) = *(cp2++); 7185 } 7186 } 7187 *cp1 = '\0'; 7188 7189 /* This still leaves /000000/ when working with a 7190 * VMS device root or concealed root. 7191 */ 7192 { 7193 int ulen; 7194 char * zeros; 7195 7196 ulen = strlen(rslt); 7197 7198 /* Get rid of "000000/ in rooted filespecs */ 7199 if (ulen > 7) { 7200 zeros = strstr(rslt, "/000000/"); 7201 if (zeros != NULL) { 7202 int mlen; 7203 mlen = ulen - (zeros - rslt) - 7; 7204 memmove(zeros, &zeros[7], mlen); 7205 ulen = ulen - 7; 7206 rslt[ulen] = '\0'; 7207 } 7208 } 7209 } 7210 7211 if (vms_debug_fileify) { 7212 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 7213 } 7214 return rslt; 7215 7216 } /* end of int_tounixspec() */ 7217 7218 7219 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/ 7220 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl) 7221 { 7222 static char __tounixspec_retbuf[VMS_MAXRSS]; 7223 char * unixspec, *ret_spec, *ret_buf; 7224 7225 unixspec = NULL; 7226 ret_buf = buf; 7227 if (ret_buf == NULL) { 7228 if (ts) { 7229 Newx(unixspec, VMS_MAXRSS, char); 7230 if (unixspec == NULL) 7231 _ckvmssts(SS$_INSFMEM); 7232 ret_buf = unixspec; 7233 } else { 7234 ret_buf = __tounixspec_retbuf; 7235 } 7236 } 7237 7238 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl); 7239 7240 if (ret_spec == NULL) { 7241 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 7242 if (unixspec) 7243 Safefree(unixspec); 7244 } 7245 7246 return ret_spec; 7247 7248 } /* end of do_tounixspec() */ 7249 /*}}}*/ 7250 /* External entry points */ 7251 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) 7252 { return do_tounixspec(spec,buf,0, NULL); } 7253 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) 7254 { return do_tounixspec(spec,buf,1, NULL); } 7255 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl) 7256 { return do_tounixspec(spec,buf,0, utf8_fl); } 7257 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl) 7258 { return do_tounixspec(spec,buf,1, utf8_fl); } 7259 7260 #if __CRTL_VER >= 70200000 && !defined(__VAX) 7261 7262 /* 7263 This procedure is used to identify if a path is based in either 7264 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and 7265 it returns the OpenVMS format directory for it. 7266 7267 It is expecting specifications of only '/' or '/xxxx/' 7268 7269 If a posix root does not exist, or 'xxxx' is not a directory 7270 in the posix root, it returns a failure. 7271 7272 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7. 7273 7274 It is used only internally by posix_to_vmsspec_hardway(). 7275 */ 7276 7277 static int posix_root_to_vms 7278 (char *vmspath, int vmspath_len, 7279 const char *unixpath, 7280 const int * utf8_fl) 7281 { 7282 int sts; 7283 struct FAB myfab = cc$rms_fab; 7284 rms_setup_nam(mynam); 7285 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 7286 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 7287 char * esa, * esal, * rsa, * rsal; 7288 int dir_flag; 7289 int unixlen; 7290 7291 dir_flag = 0; 7292 vmspath[0] = '\0'; 7293 unixlen = strlen(unixpath); 7294 if (unixlen == 0) { 7295 return RMS$_FNF; 7296 } 7297 7298 #if __CRTL_VER >= 80200000 7299 /* If not a posix spec already, convert it */ 7300 if (decc_posix_compliant_pathnames) { 7301 if (strncmp(unixpath,"\"^UP^",5) != 0) { 7302 sprintf(vmspath,"\"^UP^%s\"",unixpath); 7303 } 7304 else { 7305 /* This is already a VMS specification, no conversion */ 7306 unixlen--; 7307 my_strlcpy(vmspath, unixpath, vmspath_len + 1); 7308 } 7309 } 7310 else 7311 #endif 7312 { 7313 int path_len; 7314 int i,j; 7315 7316 /* Check to see if this is under the POSIX root */ 7317 if (decc_disable_posix_root) { 7318 return RMS$_FNF; 7319 } 7320 7321 /* Skip leading / */ 7322 if (unixpath[0] == '/') { 7323 unixpath++; 7324 unixlen--; 7325 } 7326 7327 7328 strcpy(vmspath,"SYS$POSIX_ROOT:"); 7329 7330 /* If this is only the / , or blank, then... */ 7331 if (unixpath[0] == '\0') { 7332 /* by definition, this is the answer */ 7333 return SS$_NORMAL; 7334 } 7335 7336 /* Need to look up a directory */ 7337 vmspath[15] = '['; 7338 vmspath[16] = '\0'; 7339 7340 /* Copy and add '^' escape characters as needed */ 7341 j = 16; 7342 i = 0; 7343 while (unixpath[i] != 0) { 7344 int k; 7345 7346 j += copy_expand_unix_filename_escape 7347 (&vmspath[j], &unixpath[i], &k, utf8_fl); 7348 i += k; 7349 } 7350 7351 path_len = strlen(vmspath); 7352 if (vmspath[path_len - 1] == '/') 7353 path_len--; 7354 vmspath[path_len] = ']'; 7355 path_len++; 7356 vmspath[path_len] = '\0'; 7357 7358 } 7359 vmspath[vmspath_len] = 0; 7360 if (unixpath[unixlen - 1] == '/') 7361 dir_flag = 1; 7362 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 7363 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7364 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 7365 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7366 rsal = (char *)PerlMem_malloc(VMS_MAXRSS); 7367 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7368 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 7369 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7370 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */ 7371 rms_bind_fab_nam(myfab, mynam); 7372 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1); 7373 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1); 7374 if (decc_efs_case_preserve) 7375 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE; 7376 #ifdef NAML$M_OPEN_SPECIAL 7377 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL; 7378 #endif 7379 7380 /* Set up the remaining naml fields */ 7381 sts = sys$parse(&myfab); 7382 7383 /* It failed! Try again as a UNIX filespec */ 7384 if (!(sts & 1)) { 7385 PerlMem_free(esal); 7386 PerlMem_free(esa); 7387 PerlMem_free(rsal); 7388 PerlMem_free(rsa); 7389 return sts; 7390 } 7391 7392 /* get the Device ID and the FID */ 7393 sts = sys$search(&myfab); 7394 7395 /* These are no longer needed */ 7396 PerlMem_free(esa); 7397 PerlMem_free(rsal); 7398 PerlMem_free(rsa); 7399 7400 /* on any failure, returned the POSIX ^UP^ filespec */ 7401 if (!(sts & 1)) { 7402 PerlMem_free(esal); 7403 return sts; 7404 } 7405 specdsc.dsc$a_pointer = vmspath; 7406 specdsc.dsc$w_length = vmspath_len; 7407 7408 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1]; 7409 dvidsc.dsc$w_length = mynam.naml$t_dvi[0]; 7410 sts = lib$fid_to_name 7411 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length); 7412 7413 /* on any failure, returned the POSIX ^UP^ filespec */ 7414 if (!(sts & 1)) { 7415 /* This can happen if user does not have permission to read directories */ 7416 if (strncmp(unixpath,"\"^UP^",5) != 0) 7417 sprintf(vmspath,"\"^UP^%s\"",unixpath); 7418 else 7419 my_strlcpy(vmspath, unixpath, vmspath_len + 1); 7420 } 7421 else { 7422 vmspath[specdsc.dsc$w_length] = 0; 7423 7424 /* Are we expecting a directory? */ 7425 if (dir_flag != 0) { 7426 int i; 7427 char *eptr; 7428 7429 eptr = NULL; 7430 7431 i = specdsc.dsc$w_length - 1; 7432 while (i > 0) { 7433 int zercnt; 7434 zercnt = 0; 7435 /* Version must be '1' */ 7436 if (vmspath[i--] != '1') 7437 break; 7438 /* Version delimiter is one of ".;" */ 7439 if ((vmspath[i] != '.') && (vmspath[i] != ';')) 7440 break; 7441 i--; 7442 if (vmspath[i--] != 'R') 7443 break; 7444 if (vmspath[i--] != 'I') 7445 break; 7446 if (vmspath[i--] != 'D') 7447 break; 7448 if (vmspath[i--] != '.') 7449 break; 7450 eptr = &vmspath[i+1]; 7451 while (i > 0) { 7452 if ((vmspath[i] == ']') || (vmspath[i] == '>')) { 7453 if (vmspath[i-1] != '^') { 7454 if (zercnt != 6) { 7455 *eptr = vmspath[i]; 7456 eptr[1] = '\0'; 7457 vmspath[i] = '.'; 7458 break; 7459 } 7460 else { 7461 /* Get rid of 6 imaginary zero directory filename */ 7462 vmspath[i+1] = '\0'; 7463 } 7464 } 7465 } 7466 if (vmspath[i] == '0') 7467 zercnt++; 7468 else 7469 zercnt = 10; 7470 i--; 7471 } 7472 break; 7473 } 7474 } 7475 } 7476 PerlMem_free(esal); 7477 return sts; 7478 } 7479 7480 /* /dev/mumble needs to be handled special. 7481 /dev/null becomes NLA0:, And there is the potential for other stuff 7482 like /dev/tty which may need to be mapped to something. 7483 */ 7484 7485 static int 7486 slash_dev_special_to_vms 7487 (const char * unixptr, 7488 char * vmspath, 7489 int vmspath_len) 7490 { 7491 char * nextslash; 7492 int len; 7493 int cmp; 7494 7495 unixptr += 4; 7496 nextslash = strchr(unixptr, '/'); 7497 len = strlen(unixptr); 7498 if (nextslash != NULL) 7499 len = nextslash - unixptr; 7500 cmp = strncmp("null", unixptr, 5); 7501 if (cmp == 0) { 7502 if (vmspath_len >= 6) { 7503 strcpy(vmspath, "_NLA0:"); 7504 return SS$_NORMAL; 7505 } 7506 } 7507 return 0; 7508 } 7509 7510 7511 /* The built in routines do not understand perl's special needs, so 7512 doing a manual conversion from UNIX to VMS 7513 7514 If the utf8_fl is not null and points to a non-zero value, then 7515 treat 8 bit characters as UTF-8. 7516 7517 The sequence starting with '$(' and ending with ')' will be passed 7518 through with out interpretation instead of being escaped. 7519 7520 */ 7521 static int posix_to_vmsspec_hardway 7522 (char *vmspath, int vmspath_len, 7523 const char *unixpath, 7524 int dir_flag, 7525 int * utf8_fl) { 7526 7527 char *esa; 7528 const char *unixptr; 7529 const char *unixend; 7530 char *vmsptr; 7531 const char *lastslash; 7532 const char *lastdot; 7533 int unixlen; 7534 int vmslen; 7535 int dir_start; 7536 int dir_dot; 7537 int quoted; 7538 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 7539 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 7540 7541 if (utf8_fl != NULL) 7542 *utf8_fl = 0; 7543 7544 unixptr = unixpath; 7545 dir_dot = 0; 7546 7547 /* Ignore leading "/" characters */ 7548 while((unixptr[0] == '/') && (unixptr[1] == '/')) { 7549 unixptr++; 7550 } 7551 unixlen = strlen(unixptr); 7552 7553 /* Do nothing with blank paths */ 7554 if (unixlen == 0) { 7555 vmspath[0] = '\0'; 7556 return SS$_NORMAL; 7557 } 7558 7559 quoted = 0; 7560 /* This could have a "^UP^ on the front */ 7561 if (strncmp(unixptr,"\"^UP^",5) == 0) { 7562 quoted = 1; 7563 unixptr+= 5; 7564 unixlen-= 5; 7565 } 7566 7567 lastslash = strrchr(unixptr,'/'); 7568 lastdot = strrchr(unixptr,'.'); 7569 unixend = strrchr(unixptr,'\"'); 7570 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) { 7571 unixend = unixptr + unixlen; 7572 } 7573 7574 /* last dot is last dot or past end of string */ 7575 if (lastdot == NULL) 7576 lastdot = unixptr + unixlen; 7577 7578 /* if no directories, set last slash to beginning of string */ 7579 if (lastslash == NULL) { 7580 lastslash = unixptr; 7581 } 7582 else { 7583 /* Watch out for trailing "." after last slash, still a directory */ 7584 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) { 7585 lastslash = unixptr + unixlen; 7586 } 7587 7588 /* Watch out for trailing ".." after last slash, still a directory */ 7589 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) { 7590 lastslash = unixptr + unixlen; 7591 } 7592 7593 /* dots in directories are aways escaped */ 7594 if (lastdot < lastslash) 7595 lastdot = unixptr + unixlen; 7596 } 7597 7598 /* if (unixptr < lastslash) then we are in a directory */ 7599 7600 dir_start = 0; 7601 7602 vmsptr = vmspath; 7603 vmslen = 0; 7604 7605 /* Start with the UNIX path */ 7606 if (*unixptr != '/') { 7607 /* relative paths */ 7608 7609 /* If allowing logical names on relative pathnames, then handle here */ 7610 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation && 7611 !decc_posix_compliant_pathnames) { 7612 char * nextslash; 7613 int seg_len; 7614 char * trn; 7615 int islnm; 7616 7617 /* Find the next slash */ 7618 nextslash = strchr(unixptr,'/'); 7619 7620 esa = (char *)PerlMem_malloc(vmspath_len); 7621 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7622 7623 trn = (char *)PerlMem_malloc(VMS_MAXRSS); 7624 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7625 7626 if (nextslash != NULL) { 7627 7628 seg_len = nextslash - unixptr; 7629 memcpy(esa, unixptr, seg_len); 7630 esa[seg_len] = 0; 7631 } 7632 else { 7633 seg_len = my_strlcpy(esa, unixptr, sizeof(esa)); 7634 } 7635 /* trnlnm(section) */ 7636 islnm = vmstrnenv(esa, trn, 0, fildev, 0); 7637 7638 if (islnm) { 7639 /* Now fix up the directory */ 7640 7641 /* Split up the path to find the components */ 7642 sts = vms_split_path 7643 (trn, 7644 &v_spec, 7645 &v_len, 7646 &r_spec, 7647 &r_len, 7648 &d_spec, 7649 &d_len, 7650 &n_spec, 7651 &n_len, 7652 &e_spec, 7653 &e_len, 7654 &vs_spec, 7655 &vs_len); 7656 7657 while (sts == 0) { 7658 int cmp; 7659 7660 /* A logical name must be a directory or the full 7661 specification. It is only a full specification if 7662 it is the only component */ 7663 if ((unixptr[seg_len] == '\0') || 7664 (unixptr[seg_len+1] == '\0')) { 7665 7666 /* Is a directory being required? */ 7667 if (((n_len + e_len) != 0) && (dir_flag !=0)) { 7668 /* Not a logical name */ 7669 break; 7670 } 7671 7672 7673 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) { 7674 /* This must be a directory */ 7675 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) { 7676 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1); 7677 vmsptr[vmslen] = ':'; 7678 vmslen++; 7679 vmsptr[vmslen] = '\0'; 7680 return SS$_NORMAL; 7681 } 7682 } 7683 7684 } 7685 7686 7687 /* must be dev/directory - ignore version */ 7688 if ((n_len + e_len) != 0) 7689 break; 7690 7691 /* transfer the volume */ 7692 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) { 7693 memcpy(vmsptr, v_spec, v_len); 7694 vmsptr += v_len; 7695 vmsptr[0] = '\0'; 7696 vmslen += v_len; 7697 } 7698 7699 /* unroot the rooted directory */ 7700 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) { 7701 r_spec[0] = '['; 7702 r_spec[r_len - 1] = ']'; 7703 7704 /* This should not be there, but nothing is perfect */ 7705 if (r_len > 9) { 7706 cmp = strcmp(&r_spec[1], "000000."); 7707 if (cmp == 0) { 7708 r_spec += 7; 7709 r_spec[7] = '['; 7710 r_len -= 7; 7711 if (r_len == 2) 7712 r_len = 0; 7713 } 7714 } 7715 if (r_len > 0) { 7716 memcpy(vmsptr, r_spec, r_len); 7717 vmsptr += r_len; 7718 vmslen += r_len; 7719 vmsptr[0] = '\0'; 7720 } 7721 } 7722 /* Bring over the directory. */ 7723 if ((d_len > 0) && 7724 ((d_len + vmslen) < vmspath_len)) { 7725 d_spec[0] = '['; 7726 d_spec[d_len - 1] = ']'; 7727 if (d_len > 9) { 7728 cmp = strcmp(&d_spec[1], "000000."); 7729 if (cmp == 0) { 7730 d_spec += 7; 7731 d_spec[7] = '['; 7732 d_len -= 7; 7733 if (d_len == 2) 7734 d_len = 0; 7735 } 7736 } 7737 7738 if (r_len > 0) { 7739 /* Remove the redundant root */ 7740 if (r_len > 0) { 7741 /* remove the ][ */ 7742 vmsptr--; 7743 vmslen--; 7744 d_spec++; 7745 d_len--; 7746 } 7747 memcpy(vmsptr, d_spec, d_len); 7748 vmsptr += d_len; 7749 vmslen += d_len; 7750 vmsptr[0] = '\0'; 7751 } 7752 } 7753 break; 7754 } 7755 } 7756 7757 PerlMem_free(esa); 7758 PerlMem_free(trn); 7759 } 7760 7761 if (lastslash > unixptr) { 7762 int dotdir_seen; 7763 7764 /* skip leading ./ */ 7765 dotdir_seen = 0; 7766 while ((unixptr[0] == '.') && (unixptr[1] == '/')) { 7767 dotdir_seen = 1; 7768 unixptr++; 7769 unixptr++; 7770 } 7771 7772 /* Are we still in a directory? */ 7773 if (unixptr <= lastslash) { 7774 *vmsptr++ = '['; 7775 vmslen = 1; 7776 dir_start = 1; 7777 7778 /* if not backing up, then it is relative forward. */ 7779 if (!((*unixptr == '.') && (unixptr[1] == '.') && 7780 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) { 7781 *vmsptr++ = '.'; 7782 vmslen++; 7783 dir_dot = 1; 7784 } 7785 } 7786 else { 7787 if (dotdir_seen) { 7788 /* Perl wants an empty directory here to tell the difference 7789 * between a DCL command and a filename 7790 */ 7791 *vmsptr++ = '['; 7792 *vmsptr++ = ']'; 7793 vmslen = 2; 7794 } 7795 } 7796 } 7797 else { 7798 /* Handle two special files . and .. */ 7799 if (unixptr[0] == '.') { 7800 if (&unixptr[1] == unixend) { 7801 *vmsptr++ = '['; 7802 *vmsptr++ = ']'; 7803 vmslen += 2; 7804 *vmsptr++ = '\0'; 7805 return SS$_NORMAL; 7806 } 7807 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) { 7808 *vmsptr++ = '['; 7809 *vmsptr++ = '-'; 7810 *vmsptr++ = ']'; 7811 vmslen += 3; 7812 *vmsptr++ = '\0'; 7813 return SS$_NORMAL; 7814 } 7815 } 7816 } 7817 } 7818 else { /* Absolute PATH handling */ 7819 int sts; 7820 char * nextslash; 7821 int seg_len; 7822 /* Need to find out where root is */ 7823 7824 /* In theory, this procedure should never get an absolute POSIX pathname 7825 * that can not be found on the POSIX root. 7826 * In practice, that can not be relied on, and things will show up 7827 * here that are a VMS device name or concealed logical name instead. 7828 * So to make things work, this procedure must be tolerant. 7829 */ 7830 esa = (char *)PerlMem_malloc(vmspath_len); 7831 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7832 7833 sts = SS$_NORMAL; 7834 nextslash = strchr(&unixptr[1],'/'); 7835 seg_len = 0; 7836 if (nextslash != NULL) { 7837 int cmp; 7838 seg_len = nextslash - &unixptr[1]; 7839 my_strlcpy(vmspath, unixptr, seg_len + 2); 7840 cmp = 1; 7841 if (seg_len == 3) { 7842 cmp = strncmp(vmspath, "dev", 4); 7843 if (cmp == 0) { 7844 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len); 7845 if (sts == SS$_NORMAL) 7846 return SS$_NORMAL; 7847 } 7848 } 7849 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl); 7850 } 7851 7852 if ($VMS_STATUS_SUCCESS(sts)) { 7853 /* This is verified to be a real path */ 7854 7855 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL); 7856 if ($VMS_STATUS_SUCCESS(sts)) { 7857 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1); 7858 vmsptr = vmspath + vmslen; 7859 unixptr++; 7860 if (unixptr < lastslash) { 7861 char * rptr; 7862 vmsptr--; 7863 *vmsptr++ = '.'; 7864 dir_start = 1; 7865 dir_dot = 1; 7866 if (vmslen > 7) { 7867 int cmp; 7868 rptr = vmsptr - 7; 7869 cmp = strcmp(rptr,"000000."); 7870 if (cmp == 0) { 7871 vmslen -= 7; 7872 vmsptr -= 7; 7873 vmsptr[1] = '\0'; 7874 } /* removing 6 zeros */ 7875 } /* vmslen < 7, no 6 zeros possible */ 7876 } /* Not in a directory */ 7877 } /* Posix root found */ 7878 else { 7879 /* No posix root, fall back to default directory */ 7880 strcpy(vmspath, "SYS$DISK:["); 7881 vmsptr = &vmspath[10]; 7882 vmslen = 10; 7883 if (unixptr > lastslash) { 7884 *vmsptr = ']'; 7885 vmsptr++; 7886 vmslen++; 7887 } 7888 else { 7889 dir_start = 1; 7890 } 7891 } 7892 } /* end of verified real path handling */ 7893 else { 7894 int add_6zero; 7895 int islnm; 7896 7897 /* Ok, we have a device or a concealed root that is not in POSIX 7898 * or we have garbage. Make the best of it. 7899 */ 7900 7901 /* Posix to VMS destroyed this, so copy it again */ 7902 my_strlcpy(vmspath, &unixptr[1], seg_len + 1); 7903 vmslen = strlen(vmspath); /* We know we're truncating. */ 7904 vmsptr = &vmsptr[vmslen]; 7905 islnm = 0; 7906 7907 /* Now do we need to add the fake 6 zero directory to it? */ 7908 add_6zero = 1; 7909 if ((*lastslash == '/') && (nextslash < lastslash)) { 7910 /* No there is another directory */ 7911 add_6zero = 0; 7912 } 7913 else { 7914 int trnend; 7915 int cmp; 7916 7917 /* now we have foo:bar or foo:[000000]bar to decide from */ 7918 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0); 7919 7920 if (!islnm && !decc_posix_compliant_pathnames) { 7921 7922 cmp = strncmp("bin", vmspath, 4); 7923 if (cmp == 0) { 7924 /* bin => SYS$SYSTEM: */ 7925 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0); 7926 } 7927 else { 7928 /* tmp => SYS$SCRATCH: */ 7929 cmp = strncmp("tmp", vmspath, 4); 7930 if (cmp == 0) { 7931 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0); 7932 } 7933 } 7934 } 7935 7936 trnend = islnm ? islnm - 1 : 0; 7937 7938 /* if this was a logical name, ']' or '>' must be present */ 7939 /* if not a logical name, then assume a device and hope. */ 7940 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0; 7941 7942 /* if log name and trailing '.' then rooted - treat as device */ 7943 add_6zero = islnm ? (esa[trnend-1] == '.') : 0; 7944 7945 /* Fix me, if not a logical name, a device lookup should be 7946 * done to see if the device is file structured. If the device 7947 * is not file structured, the 6 zeros should not be put on. 7948 * 7949 * As it is, perl is occasionally looking for dev:[000000]tty. 7950 * which looks a little strange. 7951 * 7952 * Not that easy to detect as "/dev" may be file structured with 7953 * special device files. 7954 */ 7955 7956 if (!islnm && (add_6zero == 0) && (*nextslash == '/') && 7957 (&nextslash[1] == unixend)) { 7958 /* No real directory present */ 7959 add_6zero = 1; 7960 } 7961 } 7962 7963 /* Put the device delimiter on */ 7964 *vmsptr++ = ':'; 7965 vmslen++; 7966 unixptr = nextslash; 7967 unixptr++; 7968 7969 /* Start directory if needed */ 7970 if (!islnm || add_6zero) { 7971 *vmsptr++ = '['; 7972 vmslen++; 7973 dir_start = 1; 7974 } 7975 7976 /* add fake 000000] if needed */ 7977 if (add_6zero) { 7978 *vmsptr++ = '0'; 7979 *vmsptr++ = '0'; 7980 *vmsptr++ = '0'; 7981 *vmsptr++ = '0'; 7982 *vmsptr++ = '0'; 7983 *vmsptr++ = '0'; 7984 *vmsptr++ = ']'; 7985 vmslen += 7; 7986 dir_start = 0; 7987 } 7988 7989 } /* non-POSIX translation */ 7990 PerlMem_free(esa); 7991 } /* End of relative/absolute path handling */ 7992 7993 while ((unixptr <= unixend) && (vmslen < vmspath_len)){ 7994 int dash_flag; 7995 int in_cnt; 7996 int out_cnt; 7997 7998 dash_flag = 0; 7999 8000 if (dir_start != 0) { 8001 8002 /* First characters in a directory are handled special */ 8003 while ((*unixptr == '/') || 8004 ((*unixptr == '.') && 8005 ((unixptr[1]=='.') || (unixptr[1]=='/') || 8006 (&unixptr[1]==unixend)))) { 8007 int loop_flag; 8008 8009 loop_flag = 0; 8010 8011 /* Skip redundant / in specification */ 8012 while ((*unixptr == '/') && (dir_start != 0)) { 8013 loop_flag = 1; 8014 unixptr++; 8015 if (unixptr == lastslash) 8016 break; 8017 } 8018 if (unixptr == lastslash) 8019 break; 8020 8021 /* Skip redundant ./ characters */ 8022 while ((*unixptr == '.') && 8023 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) { 8024 loop_flag = 1; 8025 unixptr++; 8026 if (unixptr == lastslash) 8027 break; 8028 if (*unixptr == '/') 8029 unixptr++; 8030 } 8031 if (unixptr == lastslash) 8032 break; 8033 8034 /* Skip redundant ../ characters */ 8035 while ((*unixptr == '.') && (unixptr[1] == '.') && 8036 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) { 8037 /* Set the backing up flag */ 8038 loop_flag = 1; 8039 dir_dot = 0; 8040 dash_flag = 1; 8041 *vmsptr++ = '-'; 8042 vmslen++; 8043 unixptr++; /* first . */ 8044 unixptr++; /* second . */ 8045 if (unixptr == lastslash) 8046 break; 8047 if (*unixptr == '/') /* The slash */ 8048 unixptr++; 8049 } 8050 if (unixptr == lastslash) 8051 break; 8052 8053 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 8054 /* Not needed when VMS is pretending to be UNIX. */ 8055 8056 /* Is this loop stuck because of too many dots? */ 8057 if (loop_flag == 0) { 8058 /* Exit the loop and pass the rest through */ 8059 break; 8060 } 8061 } 8062 8063 /* Are we done with directories yet? */ 8064 if (unixptr >= lastslash) { 8065 8066 /* Watch out for trailing dots */ 8067 if (dir_dot != 0) { 8068 vmslen --; 8069 vmsptr--; 8070 } 8071 *vmsptr++ = ']'; 8072 vmslen++; 8073 dash_flag = 0; 8074 dir_start = 0; 8075 if (*unixptr == '/') 8076 unixptr++; 8077 } 8078 else { 8079 /* Have we stopped backing up? */ 8080 if (dash_flag) { 8081 *vmsptr++ = '.'; 8082 vmslen++; 8083 dash_flag = 0; 8084 /* dir_start continues to be = 1 */ 8085 } 8086 if (*unixptr == '-') { 8087 *vmsptr++ = '^'; 8088 *vmsptr++ = *unixptr++; 8089 vmslen += 2; 8090 dir_start = 0; 8091 8092 /* Now are we done with directories yet? */ 8093 if (unixptr >= lastslash) { 8094 8095 /* Watch out for trailing dots */ 8096 if (dir_dot != 0) { 8097 vmslen --; 8098 vmsptr--; 8099 } 8100 8101 *vmsptr++ = ']'; 8102 vmslen++; 8103 dash_flag = 0; 8104 dir_start = 0; 8105 } 8106 } 8107 } 8108 } 8109 8110 /* All done? */ 8111 if (unixptr >= unixend) 8112 break; 8113 8114 /* Normal characters - More EFS work probably needed */ 8115 dir_start = 0; 8116 dir_dot = 0; 8117 8118 switch(*unixptr) { 8119 case '/': 8120 /* remove multiple / */ 8121 while (unixptr[1] == '/') { 8122 unixptr++; 8123 } 8124 if (unixptr == lastslash) { 8125 /* Watch out for trailing dots */ 8126 if (dir_dot != 0) { 8127 vmslen --; 8128 vmsptr--; 8129 } 8130 *vmsptr++ = ']'; 8131 } 8132 else { 8133 dir_start = 1; 8134 *vmsptr++ = '.'; 8135 dir_dot = 1; 8136 8137 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 8138 /* Not needed when VMS is pretending to be UNIX. */ 8139 8140 } 8141 dash_flag = 0; 8142 if (unixptr != unixend) 8143 unixptr++; 8144 vmslen++; 8145 break; 8146 case '.': 8147 if ((unixptr < lastdot) || (unixptr < lastslash) || 8148 (&unixptr[1] == unixend)) { 8149 *vmsptr++ = '^'; 8150 *vmsptr++ = '.'; 8151 vmslen += 2; 8152 unixptr++; 8153 8154 /* trailing dot ==> '^..' on VMS */ 8155 if (unixptr == unixend) { 8156 *vmsptr++ = '.'; 8157 vmslen++; 8158 unixptr++; 8159 } 8160 break; 8161 } 8162 8163 *vmsptr++ = *unixptr++; 8164 vmslen ++; 8165 break; 8166 case '"': 8167 if (quoted && (&unixptr[1] == unixend)) { 8168 unixptr++; 8169 break; 8170 } 8171 in_cnt = copy_expand_unix_filename_escape 8172 (vmsptr, unixptr, &out_cnt, utf8_fl); 8173 vmsptr += out_cnt; 8174 unixptr += in_cnt; 8175 break; 8176 case '~': 8177 case ';': 8178 case '\\': 8179 case '?': 8180 case ' ': 8181 default: 8182 in_cnt = copy_expand_unix_filename_escape 8183 (vmsptr, unixptr, &out_cnt, utf8_fl); 8184 vmsptr += out_cnt; 8185 unixptr += in_cnt; 8186 break; 8187 } 8188 } 8189 8190 /* Make sure directory is closed */ 8191 if (unixptr == lastslash) { 8192 char *vmsptr2; 8193 vmsptr2 = vmsptr - 1; 8194 8195 if (*vmsptr2 != ']') { 8196 *vmsptr2--; 8197 8198 /* directories do not end in a dot bracket */ 8199 if (*vmsptr2 == '.') { 8200 vmsptr2--; 8201 8202 /* ^. is allowed */ 8203 if (*vmsptr2 != '^') { 8204 vmsptr--; /* back up over the dot */ 8205 } 8206 } 8207 *vmsptr++ = ']'; 8208 } 8209 } 8210 else { 8211 char *vmsptr2; 8212 /* Add a trailing dot if a file with no extension */ 8213 vmsptr2 = vmsptr - 1; 8214 if ((vmslen > 1) && 8215 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') && 8216 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) { 8217 *vmsptr++ = '.'; 8218 vmslen++; 8219 } 8220 } 8221 8222 *vmsptr = '\0'; 8223 return SS$_NORMAL; 8224 } 8225 #endif 8226 8227 /* Eventual routine to convert a UTF-8 specification to VTF-7. */ 8228 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl) 8229 { 8230 char * result; 8231 int utf8_flag; 8232 8233 /* If a UTF8 flag is being passed, honor it */ 8234 utf8_flag = 0; 8235 if (utf8_fl != NULL) { 8236 utf8_flag = *utf8_fl; 8237 *utf8_fl = 0; 8238 } 8239 8240 if (utf8_flag) { 8241 /* If there is a possibility of UTF8, then if any UTF8 characters 8242 are present, then they must be converted to VTF-7 8243 */ 8244 result = strcpy(rslt, path); /* FIX-ME */ 8245 } 8246 else 8247 result = strcpy(rslt, path); 8248 8249 return result; 8250 } 8251 8252 /* A convenience macro for copying dots in filenames and escaping 8253 * them when they haven't already been escaped, with guards to 8254 * avoid checking before the start of the buffer or advancing 8255 * beyond the end of it (allowing room for the NUL terminator). 8256 */ 8257 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \ 8258 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \ 8259 || ((vmsefsdot) == (vmsefsbuf))) \ 8260 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \ 8261 ) { \ 8262 *((vmsefsdot)++) = '^'; \ 8263 } \ 8264 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \ 8265 *((vmsefsdot)++) = '.'; \ 8266 } STMT_END 8267 8268 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 8269 static char *int_tovmsspec 8270 (const char *path, char *rslt, int dir_flag, int * utf8_flag) { 8271 char *dirend; 8272 char *lastdot; 8273 char *cp1; 8274 const char *cp2; 8275 unsigned long int infront = 0, hasdir = 1; 8276 int rslt_len; 8277 int no_type_seen; 8278 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 8279 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 8280 8281 if (vms_debug_fileify) { 8282 if (path == NULL) 8283 fprintf(stderr, "int_tovmsspec: path = NULL\n"); 8284 else 8285 fprintf(stderr, "int_tovmsspec: path = %s\n", path); 8286 } 8287 8288 if (path == NULL) { 8289 /* If we fail, we should be setting errno */ 8290 set_errno(EINVAL); 8291 set_vaxc_errno(SS$_BADPARAM); 8292 return NULL; 8293 } 8294 rslt_len = VMS_MAXRSS-1; 8295 8296 /* '.' and '..' are "[]" and "[-]" for a quick check */ 8297 if (path[0] == '.') { 8298 if (path[1] == '\0') { 8299 strcpy(rslt,"[]"); 8300 if (utf8_flag != NULL) 8301 *utf8_flag = 0; 8302 return rslt; 8303 } 8304 else { 8305 if (path[1] == '.' && path[2] == '\0') { 8306 strcpy(rslt,"[-]"); 8307 if (utf8_flag != NULL) 8308 *utf8_flag = 0; 8309 return rslt; 8310 } 8311 } 8312 } 8313 8314 /* Posix specifications are now a native VMS format */ 8315 /*--------------------------------------------------*/ 8316 #if __CRTL_VER >= 80200000 && !defined(__VAX) 8317 if (decc_posix_compliant_pathnames) { 8318 if (strncmp(path,"\"^UP^",5) == 0) { 8319 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag); 8320 return rslt; 8321 } 8322 } 8323 #endif 8324 8325 /* This is really the only way to see if this is already in VMS format */ 8326 sts = vms_split_path 8327 (path, 8328 &v_spec, 8329 &v_len, 8330 &r_spec, 8331 &r_len, 8332 &d_spec, 8333 &d_len, 8334 &n_spec, 8335 &n_len, 8336 &e_spec, 8337 &e_len, 8338 &vs_spec, 8339 &vs_len); 8340 if (sts == 0) { 8341 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath() 8342 replacement, because the above parse just took care of most of 8343 what is needed to do vmspath when the specification is already 8344 in VMS format. 8345 8346 And if it is not already, it is easier to do the conversion as 8347 part of this routine than to call this routine and then work on 8348 the result. 8349 */ 8350 8351 /* If VMS punctuation was found, it is already VMS format */ 8352 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) { 8353 if (utf8_flag != NULL) 8354 *utf8_flag = 0; 8355 my_strlcpy(rslt, path, VMS_MAXRSS); 8356 if (vms_debug_fileify) { 8357 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8358 } 8359 return rslt; 8360 } 8361 /* Now, what to do with trailing "." cases where there is no 8362 extension? If this is a UNIX specification, and EFS characters 8363 are enabled, then the trailing "." should be converted to a "^.". 8364 But if this was already a VMS specification, then it should be 8365 left alone. 8366 8367 So in the case of ambiguity, leave the specification alone. 8368 */ 8369 8370 8371 /* If there is a possibility of UTF8, then if any UTF8 characters 8372 are present, then they must be converted to VTF-7 8373 */ 8374 if (utf8_flag != NULL) 8375 *utf8_flag = 0; 8376 my_strlcpy(rslt, path, VMS_MAXRSS); 8377 if (vms_debug_fileify) { 8378 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8379 } 8380 return rslt; 8381 } 8382 8383 dirend = strrchr(path,'/'); 8384 8385 if (dirend == NULL) { 8386 /* If we get here with no Unix directory delimiters, then this is an 8387 * ambiguous file specification, such as a Unix glob specification, a 8388 * shell or make macro, or a filespec that would be valid except for 8389 * unescaped extended characters. The safest thing if it's a macro 8390 * is to pass it through as-is. 8391 */ 8392 if (strstr(path, "$(")) { 8393 my_strlcpy(rslt, path, VMS_MAXRSS); 8394 if (vms_debug_fileify) { 8395 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8396 } 8397 return rslt; 8398 } 8399 hasdir = 0; 8400 } 8401 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */ 8402 if (!*(dirend+2)) dirend +=2; 8403 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3; 8404 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4; 8405 } 8406 8407 cp1 = rslt; 8408 cp2 = path; 8409 lastdot = strrchr(cp2,'.'); 8410 if (*cp2 == '/') { 8411 char *trndev; 8412 int islnm, rooted; 8413 STRLEN trnend; 8414 8415 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8416 if (!*(cp2+1)) { 8417 if (decc_disable_posix_root) { 8418 strcpy(rslt,"sys$disk:[000000]"); 8419 } 8420 else { 8421 strcpy(rslt,"sys$posix_root:[000000]"); 8422 } 8423 if (utf8_flag != NULL) 8424 *utf8_flag = 0; 8425 if (vms_debug_fileify) { 8426 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8427 } 8428 return rslt; 8429 } 8430 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2; 8431 *cp1 = '\0'; 8432 trndev = (char *)PerlMem_malloc(VMS_MAXRSS); 8433 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8434 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8435 8436 /* DECC special handling */ 8437 if (!islnm) { 8438 if (strcmp(rslt,"bin") == 0) { 8439 strcpy(rslt,"sys$system"); 8440 cp1 = rslt + 10; 8441 *cp1 = 0; 8442 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8443 } 8444 else if (strcmp(rslt,"tmp") == 0) { 8445 strcpy(rslt,"sys$scratch"); 8446 cp1 = rslt + 11; 8447 *cp1 = 0; 8448 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8449 } 8450 else if (!decc_disable_posix_root) { 8451 strcpy(rslt, "sys$posix_root"); 8452 cp1 = rslt + 14; 8453 *cp1 = 0; 8454 cp2 = path; 8455 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8456 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8457 } 8458 else if (strcmp(rslt,"dev") == 0) { 8459 if (strncmp(cp2,"/null", 5) == 0) { 8460 if ((cp2[5] == 0) || (cp2[5] == '/')) { 8461 strcpy(rslt,"NLA0"); 8462 cp1 = rslt + 4; 8463 *cp1 = 0; 8464 cp2 = cp2 + 5; 8465 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8466 } 8467 } 8468 } 8469 } 8470 8471 trnend = islnm ? strlen(trndev) - 1 : 0; 8472 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0; 8473 rooted = islnm ? (trndev[trnend-1] == '.') : 0; 8474 /* If the first element of the path is a logical name, determine 8475 * whether it has to be translated so we can add more directories. */ 8476 if (!islnm || rooted) { 8477 *(cp1++) = ':'; 8478 *(cp1++) = '['; 8479 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0'; 8480 else cp2++; 8481 } 8482 else { 8483 if (cp2 != dirend) { 8484 my_strlcpy(rslt, trndev, VMS_MAXRSS); 8485 cp1 = rslt + trnend; 8486 if (*cp2 != 0) { 8487 *(cp1++) = '.'; 8488 cp2++; 8489 } 8490 } 8491 else { 8492 if (decc_disable_posix_root) { 8493 *(cp1++) = ':'; 8494 hasdir = 0; 8495 } 8496 } 8497 } 8498 PerlMem_free(trndev); 8499 } 8500 else if (hasdir) { 8501 *(cp1++) = '['; 8502 if (*cp2 == '.') { 8503 if (*(cp2+1) == '/' || *(cp2+1) == '\0') { 8504 cp2 += 2; /* skip over "./" - it's redundant */ 8505 *(cp1++) = '.'; /* but it does indicate a relative dirspec */ 8506 } 8507 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8508 *(cp1++) = '-'; /* "../" --> "-" */ 8509 cp2 += 3; 8510 } 8511 else if (*(cp2+1) == '.' && *(cp2+2) == '.' && 8512 (*(cp2+3) == '/' || *(cp2+3) == '\0')) { 8513 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8514 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */ 8515 cp2 += 4; 8516 } 8517 else if ((cp2 != lastdot) || (lastdot < dirend)) { 8518 /* Escape the extra dots in EFS file specifications */ 8519 *(cp1++) = '^'; 8520 } 8521 if (cp2 > dirend) cp2 = dirend; 8522 } 8523 else *(cp1++) = '.'; 8524 } 8525 else { 8526 *(cp1++) = *cp2; 8527 } 8528 for (; cp2 < dirend; cp2++) { 8529 if (*cp2 == '/') { 8530 if (*(cp2-1) == '/') continue; 8531 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; 8532 infront = 0; 8533 } 8534 else if (!infront && *cp2 == '.') { 8535 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; } 8536 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */ 8537 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8538 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */ 8539 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-'; 8540 else { 8541 *(cp1++) = '-'; 8542 } 8543 cp2 += 2; 8544 if (cp2 == dirend) break; 8545 } 8546 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' && 8547 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) { 8548 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */ 8549 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8550 if (!*(cp2+3)) { 8551 *(cp1++) = '.'; /* Simulate trailing '/' */ 8552 cp2 += 2; /* for loop will incr this to == dirend */ 8553 } 8554 else cp2 += 3; /* Trailing '/' was there, so skip it, too */ 8555 } 8556 else { 8557 if (decc_efs_charset == 0) { 8558 if (cp1 > rslt && *(cp1-1) == '^') 8559 cp1--; /* remove the escape, if any */ 8560 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */ 8561 } 8562 else { 8563 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8564 } 8565 } 8566 } 8567 else { 8568 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.'; 8569 if (*cp2 == '.') { 8570 if (decc_efs_charset == 0) { 8571 if (cp1 > rslt && *(cp1-1) == '^') 8572 cp1--; /* remove the escape, if any */ 8573 *(cp1++) = '_'; 8574 } 8575 else { 8576 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8577 } 8578 } 8579 else *(cp1++) = *cp2; 8580 infront = 1; 8581 } 8582 } 8583 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */ 8584 if (hasdir) *(cp1++) = ']'; 8585 if (*cp2) cp2++; /* check in case we ended with trailing '..' */ 8586 /* fixme for ODS5 */ 8587 no_type_seen = 0; 8588 if (cp2 > lastdot) 8589 no_type_seen = 1; 8590 while (*cp2) { 8591 switch(*cp2) { 8592 case '?': 8593 if (decc_efs_charset == 0) 8594 *(cp1++) = '%'; 8595 else 8596 *(cp1++) = '?'; 8597 cp2++; 8598 case ' ': 8599 if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */ 8600 *(cp1)++ = '^'; 8601 *(cp1)++ = '_'; 8602 cp2++; 8603 break; 8604 case '.': 8605 if (((cp2 < lastdot) || (cp2[1] == '\0')) && 8606 decc_readdir_dropdotnotype) { 8607 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8608 cp2++; 8609 8610 /* trailing dot ==> '^..' on VMS */ 8611 if (*cp2 == '\0') { 8612 *(cp1++) = '.'; 8613 no_type_seen = 0; 8614 } 8615 } 8616 else { 8617 *(cp1++) = *(cp2++); 8618 no_type_seen = 0; 8619 } 8620 break; 8621 case '$': 8622 /* This could be a macro to be passed through */ 8623 *(cp1++) = *(cp2++); 8624 if (*cp2 == '(') { 8625 const char * save_cp2; 8626 char * save_cp1; 8627 int is_macro; 8628 8629 /* paranoid check */ 8630 save_cp2 = cp2; 8631 save_cp1 = cp1; 8632 is_macro = 0; 8633 8634 /* Test through */ 8635 *(cp1++) = *(cp2++); 8636 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8637 *(cp1++) = *(cp2++); 8638 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8639 *(cp1++) = *(cp2++); 8640 } 8641 if (*cp2 == ')') { 8642 *(cp1++) = *(cp2++); 8643 is_macro = 1; 8644 } 8645 } 8646 if (is_macro == 0) { 8647 /* Not really a macro - never mind */ 8648 cp2 = save_cp2; 8649 cp1 = save_cp1; 8650 } 8651 } 8652 break; 8653 case '\"': 8654 case '~': 8655 case '`': 8656 case '!': 8657 case '#': 8658 case '%': 8659 case '^': 8660 /* Don't escape again if following character is 8661 * already something we escape. 8662 */ 8663 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) { 8664 *(cp1++) = *(cp2++); 8665 break; 8666 } 8667 /* But otherwise fall through and escape it. */ 8668 case '&': 8669 case '(': 8670 case ')': 8671 case '=': 8672 case '+': 8673 case '\'': 8674 case '@': 8675 case '[': 8676 case ']': 8677 case '{': 8678 case '}': 8679 case ':': 8680 case '\\': 8681 case '|': 8682 case '<': 8683 case '>': 8684 if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */ 8685 *(cp1++) = '^'; 8686 *(cp1++) = *(cp2++); 8687 break; 8688 case ';': 8689 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs 8690 * which is wrong. UNIX notation should be ".dir." unless 8691 * the DECC$FILENAME_UNIX_NO_VERSION is enabled. 8692 * changing this behavior could break more things at this time. 8693 * efs character set effectively does not allow "." to be a version 8694 * delimiter as a further complication about changing this. 8695 */ 8696 if (decc_filename_unix_report != 0) { 8697 *(cp1++) = '^'; 8698 } 8699 *(cp1++) = *(cp2++); 8700 break; 8701 default: 8702 *(cp1++) = *(cp2++); 8703 } 8704 } 8705 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) { 8706 char *lcp1; 8707 lcp1 = cp1; 8708 lcp1--; 8709 /* Fix me for "^]", but that requires making sure that you do 8710 * not back up past the start of the filename 8711 */ 8712 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%')) 8713 *cp1++ = '.'; 8714 } 8715 *cp1 = '\0'; 8716 8717 if (utf8_flag != NULL) 8718 *utf8_flag = 0; 8719 if (vms_debug_fileify) { 8720 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8721 } 8722 return rslt; 8723 8724 } /* end of int_tovmsspec() */ 8725 8726 8727 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 8728 static char *mp_do_tovmsspec 8729 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) { 8730 static char __tovmsspec_retbuf[VMS_MAXRSS]; 8731 char * vmsspec, *ret_spec, *ret_buf; 8732 8733 vmsspec = NULL; 8734 ret_buf = buf; 8735 if (ret_buf == NULL) { 8736 if (ts) { 8737 Newx(vmsspec, VMS_MAXRSS, char); 8738 if (vmsspec == NULL) 8739 _ckvmssts(SS$_INSFMEM); 8740 ret_buf = vmsspec; 8741 } else { 8742 ret_buf = __tovmsspec_retbuf; 8743 } 8744 } 8745 8746 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag); 8747 8748 if (ret_spec == NULL) { 8749 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 8750 if (vmsspec) 8751 Safefree(vmsspec); 8752 } 8753 8754 return ret_spec; 8755 8756 } /* end of mp_do_tovmsspec() */ 8757 /*}}}*/ 8758 /* External entry points */ 8759 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) 8760 { return do_tovmsspec(path,buf,0,NULL); } 8761 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) 8762 { return do_tovmsspec(path,buf,1,NULL); } 8763 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8764 { return do_tovmsspec(path,buf,0,utf8_fl); } 8765 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8766 { return do_tovmsspec(path,buf,1,utf8_fl); } 8767 8768 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/ 8769 /* Internal routine for use with out an explicit context present */ 8770 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) { 8771 8772 char * ret_spec, *pathified; 8773 8774 if (path == NULL) 8775 return NULL; 8776 8777 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8778 if (pathified == NULL) 8779 _ckvmssts_noperl(SS$_INSFMEM); 8780 8781 ret_spec = int_pathify_dirspec(path, pathified); 8782 8783 if (ret_spec == NULL) { 8784 PerlMem_free(pathified); 8785 return NULL; 8786 } 8787 8788 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl); 8789 8790 PerlMem_free(pathified); 8791 return ret_spec; 8792 8793 } 8794 8795 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/ 8796 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) { 8797 static char __tovmspath_retbuf[VMS_MAXRSS]; 8798 int vmslen; 8799 char *pathified, *vmsified, *cp; 8800 8801 if (path == NULL) return NULL; 8802 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8803 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8804 if (int_pathify_dirspec(path, pathified) == NULL) { 8805 PerlMem_free(pathified); 8806 return NULL; 8807 } 8808 8809 vmsified = NULL; 8810 if (buf == NULL) 8811 Newx(vmsified, VMS_MAXRSS, char); 8812 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) { 8813 PerlMem_free(pathified); 8814 if (vmsified) Safefree(vmsified); 8815 return NULL; 8816 } 8817 PerlMem_free(pathified); 8818 if (buf) { 8819 return buf; 8820 } 8821 else if (ts) { 8822 vmslen = strlen(vmsified); 8823 Newx(cp,vmslen+1,char); 8824 memcpy(cp,vmsified,vmslen); 8825 cp[vmslen] = '\0'; 8826 Safefree(vmsified); 8827 return cp; 8828 } 8829 else { 8830 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf)); 8831 Safefree(vmsified); 8832 return __tovmspath_retbuf; 8833 } 8834 8835 } /* end of do_tovmspath() */ 8836 /*}}}*/ 8837 /* External entry points */ 8838 char *Perl_tovmspath(pTHX_ const char *path, char *buf) 8839 { return do_tovmspath(path,buf,0, NULL); } 8840 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) 8841 { return do_tovmspath(path,buf,1, NULL); } 8842 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl) 8843 { return do_tovmspath(path,buf,0,utf8_fl); } 8844 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl) 8845 { return do_tovmspath(path,buf,1,utf8_fl); } 8846 8847 8848 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/ 8849 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) { 8850 static char __tounixpath_retbuf[VMS_MAXRSS]; 8851 int unixlen; 8852 char *pathified, *unixified, *cp; 8853 8854 if (path == NULL) return NULL; 8855 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8856 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8857 if (int_pathify_dirspec(path, pathified) == NULL) { 8858 PerlMem_free(pathified); 8859 return NULL; 8860 } 8861 8862 unixified = NULL; 8863 if (buf == NULL) { 8864 Newx(unixified, VMS_MAXRSS, char); 8865 } 8866 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) { 8867 PerlMem_free(pathified); 8868 if (unixified) Safefree(unixified); 8869 return NULL; 8870 } 8871 PerlMem_free(pathified); 8872 if (buf) { 8873 return buf; 8874 } 8875 else if (ts) { 8876 unixlen = strlen(unixified); 8877 Newx(cp,unixlen+1,char); 8878 memcpy(cp,unixified,unixlen); 8879 cp[unixlen] = '\0'; 8880 Safefree(unixified); 8881 return cp; 8882 } 8883 else { 8884 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf)); 8885 Safefree(unixified); 8886 return __tounixpath_retbuf; 8887 } 8888 8889 } /* end of do_tounixpath() */ 8890 /*}}}*/ 8891 /* External entry points */ 8892 char *Perl_tounixpath(pTHX_ const char *path, char *buf) 8893 { return do_tounixpath(path,buf,0,NULL); } 8894 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) 8895 { return do_tounixpath(path,buf,1,NULL); } 8896 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8897 { return do_tounixpath(path,buf,0,utf8_fl); } 8898 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8899 { return do_tounixpath(path,buf,1,utf8_fl); } 8900 8901 /* 8902 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com) 8903 * 8904 ***************************************************************************** 8905 * * 8906 * Copyright (C) 1989-1994, 2007 by * 8907 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 * 8908 * * 8909 * Permission is hereby granted for the reproduction of this software * 8910 * on condition that this copyright notice is included in source * 8911 * distributions of the software. The code may be modified and * 8912 * distributed under the same terms as Perl itself. * 8913 * * 8914 * 27-Aug-1994 Modified for inclusion in perl5 * 8915 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) * 8916 ***************************************************************************** 8917 */ 8918 8919 /* 8920 * getredirection() is intended to aid in porting C programs 8921 * to VMS (Vax-11 C). The native VMS environment does not support 8922 * '>' and '<' I/O redirection, or command line wild card expansion, 8923 * or a command line pipe mechanism using the '|' AND background 8924 * command execution '&'. All of these capabilities are provided to any 8925 * C program which calls this procedure as the first thing in the 8926 * main program. 8927 * The piping mechanism will probably work with almost any 'filter' type 8928 * of program. With suitable modification, it may useful for other 8929 * portability problems as well. 8930 * 8931 * Author: Mark Pizzolato (mark AT infocomm DOT com) 8932 */ 8933 struct list_item 8934 { 8935 struct list_item *next; 8936 char *value; 8937 }; 8938 8939 static void add_item(struct list_item **head, 8940 struct list_item **tail, 8941 char *value, 8942 int *count); 8943 8944 static void mp_expand_wild_cards(pTHX_ char *item, 8945 struct list_item **head, 8946 struct list_item **tail, 8947 int *count); 8948 8949 static int background_process(pTHX_ int argc, char **argv); 8950 8951 static void pipe_and_fork(pTHX_ char **cmargv); 8952 8953 /*{{{ void getredirection(int *ac, char ***av)*/ 8954 static void 8955 mp_getredirection(pTHX_ int *ac, char ***av) 8956 /* 8957 * Process vms redirection arg's. Exit if any error is seen. 8958 * If getredirection() processes an argument, it is erased 8959 * from the vector. getredirection() returns a new argc and argv value. 8960 * In the event that a background command is requested (by a trailing "&"), 8961 * this routine creates a background subprocess, and simply exits the program. 8962 * 8963 * Warning: do not try to simplify the code for vms. The code 8964 * presupposes that getredirection() is called before any data is 8965 * read from stdin or written to stdout. 8966 * 8967 * Normal usage is as follows: 8968 * 8969 * main(argc, argv) 8970 * int argc; 8971 * char *argv[]; 8972 * { 8973 * getredirection(&argc, &argv); 8974 * } 8975 */ 8976 { 8977 int argc = *ac; /* Argument Count */ 8978 char **argv = *av; /* Argument Vector */ 8979 char *ap; /* Argument pointer */ 8980 int j; /* argv[] index */ 8981 int item_count = 0; /* Count of Items in List */ 8982 struct list_item *list_head = 0; /* First Item in List */ 8983 struct list_item *list_tail; /* Last Item in List */ 8984 char *in = NULL; /* Input File Name */ 8985 char *out = NULL; /* Output File Name */ 8986 char *outmode = "w"; /* Mode to Open Output File */ 8987 char *err = NULL; /* Error File Name */ 8988 char *errmode = "w"; /* Mode to Open Error File */ 8989 int cmargc = 0; /* Piped Command Arg Count */ 8990 char **cmargv = NULL;/* Piped Command Arg Vector */ 8991 8992 /* 8993 * First handle the case where the last thing on the line ends with 8994 * a '&'. This indicates the desire for the command to be run in a 8995 * subprocess, so we satisfy that desire. 8996 */ 8997 ap = argv[argc-1]; 8998 if (0 == strcmp("&", ap)) 8999 exit(background_process(aTHX_ --argc, argv)); 9000 if (*ap && '&' == ap[strlen(ap)-1]) 9001 { 9002 ap[strlen(ap)-1] = '\0'; 9003 exit(background_process(aTHX_ argc, argv)); 9004 } 9005 /* 9006 * Now we handle the general redirection cases that involve '>', '>>', 9007 * '<', and pipes '|'. 9008 */ 9009 for (j = 0; j < argc; ++j) 9010 { 9011 if (0 == strcmp("<", argv[j])) 9012 { 9013 if (j+1 >= argc) 9014 { 9015 fprintf(stderr,"No input file after < on command line"); 9016 exit(LIB$_WRONUMARG); 9017 } 9018 in = argv[++j]; 9019 continue; 9020 } 9021 if ('<' == *(ap = argv[j])) 9022 { 9023 in = 1 + ap; 9024 continue; 9025 } 9026 if (0 == strcmp(">", ap)) 9027 { 9028 if (j+1 >= argc) 9029 { 9030 fprintf(stderr,"No output file after > on command line"); 9031 exit(LIB$_WRONUMARG); 9032 } 9033 out = argv[++j]; 9034 continue; 9035 } 9036 if ('>' == *ap) 9037 { 9038 if ('>' == ap[1]) 9039 { 9040 outmode = "a"; 9041 if ('\0' == ap[2]) 9042 out = argv[++j]; 9043 else 9044 out = 2 + ap; 9045 } 9046 else 9047 out = 1 + ap; 9048 if (j >= argc) 9049 { 9050 fprintf(stderr,"No output file after > or >> on command line"); 9051 exit(LIB$_WRONUMARG); 9052 } 9053 continue; 9054 } 9055 if (('2' == *ap) && ('>' == ap[1])) 9056 { 9057 if ('>' == ap[2]) 9058 { 9059 errmode = "a"; 9060 if ('\0' == ap[3]) 9061 err = argv[++j]; 9062 else 9063 err = 3 + ap; 9064 } 9065 else 9066 if ('\0' == ap[2]) 9067 err = argv[++j]; 9068 else 9069 err = 2 + ap; 9070 if (j >= argc) 9071 { 9072 fprintf(stderr,"No output file after 2> or 2>> on command line"); 9073 exit(LIB$_WRONUMARG); 9074 } 9075 continue; 9076 } 9077 if (0 == strcmp("|", argv[j])) 9078 { 9079 if (j+1 >= argc) 9080 { 9081 fprintf(stderr,"No command into which to pipe on command line"); 9082 exit(LIB$_WRONUMARG); 9083 } 9084 cmargc = argc-(j+1); 9085 cmargv = &argv[j+1]; 9086 argc = j; 9087 continue; 9088 } 9089 if ('|' == *(ap = argv[j])) 9090 { 9091 ++argv[j]; 9092 cmargc = argc-j; 9093 cmargv = &argv[j]; 9094 argc = j; 9095 continue; 9096 } 9097 expand_wild_cards(ap, &list_head, &list_tail, &item_count); 9098 } 9099 /* 9100 * Allocate and fill in the new argument vector, Some Unix's terminate 9101 * the list with an extra null pointer. 9102 */ 9103 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *)); 9104 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9105 *av = argv; 9106 for (j = 0; j < item_count; ++j, list_head = list_head->next) 9107 argv[j] = list_head->value; 9108 *ac = item_count; 9109 if (cmargv != NULL) 9110 { 9111 if (out != NULL) 9112 { 9113 fprintf(stderr,"'|' and '>' may not both be specified on command line"); 9114 exit(LIB$_INVARGORD); 9115 } 9116 pipe_and_fork(aTHX_ cmargv); 9117 } 9118 9119 /* Check for input from a pipe (mailbox) */ 9120 9121 if (in == NULL && 1 == isapipe(0)) 9122 { 9123 char mbxname[L_tmpnam]; 9124 long int bufsize; 9125 long int dvi_item = DVI$_DEVBUFSIZ; 9126 $DESCRIPTOR(mbxnam, ""); 9127 $DESCRIPTOR(mbxdevnam, ""); 9128 9129 /* Input from a pipe, reopen it in binary mode to disable */ 9130 /* carriage control processing. */ 9131 9132 fgetname(stdin, mbxname, 1); 9133 mbxnam.dsc$a_pointer = mbxname; 9134 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer); 9135 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0); 9136 mbxdevnam.dsc$a_pointer = mbxname; 9137 mbxdevnam.dsc$w_length = sizeof(mbxname); 9138 dvi_item = DVI$_DEVNAM; 9139 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length); 9140 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0'; 9141 set_errno(0); 9142 set_vaxc_errno(1); 9143 freopen(mbxname, "rb", stdin); 9144 if (errno != 0) 9145 { 9146 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname); 9147 exit(vaxc$errno); 9148 } 9149 } 9150 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) 9151 { 9152 fprintf(stderr,"Can't open input file %s as stdin",in); 9153 exit(vaxc$errno); 9154 } 9155 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) 9156 { 9157 fprintf(stderr,"Can't open output file %s as stdout",out); 9158 exit(vaxc$errno); 9159 } 9160 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out); 9161 9162 if (err != NULL) { 9163 if (strcmp(err,"&1") == 0) { 9164 dup2(fileno(stdout), fileno(stderr)); 9165 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT"); 9166 } else { 9167 FILE *tmperr; 9168 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2"))) 9169 { 9170 fprintf(stderr,"Can't open error file %s as stderr",err); 9171 exit(vaxc$errno); 9172 } 9173 fclose(tmperr); 9174 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2")) 9175 { 9176 exit(vaxc$errno); 9177 } 9178 vmssetuserlnm("SYS$ERROR", err); 9179 } 9180 } 9181 #ifdef ARGPROC_DEBUG 9182 PerlIO_printf(Perl_debug_log, "Arglist:\n"); 9183 for (j = 0; j < *ac; ++j) 9184 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]); 9185 #endif 9186 /* Clear errors we may have hit expanding wildcards, so they don't 9187 show up in Perl's $! later */ 9188 set_errno(0); set_vaxc_errno(1); 9189 } /* end of getredirection() */ 9190 /*}}}*/ 9191 9192 static void add_item(struct list_item **head, 9193 struct list_item **tail, 9194 char *value, 9195 int *count) 9196 { 9197 if (*head == 0) 9198 { 9199 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 9200 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9201 *tail = *head; 9202 } 9203 else { 9204 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 9205 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9206 *tail = (*tail)->next; 9207 } 9208 (*tail)->value = value; 9209 ++(*count); 9210 } 9211 9212 static void mp_expand_wild_cards(pTHX_ char *item, 9213 struct list_item **head, 9214 struct list_item **tail, 9215 int *count) 9216 { 9217 int expcount = 0; 9218 unsigned long int context = 0; 9219 int isunix = 0; 9220 int item_len = 0; 9221 char *had_version; 9222 char *had_device; 9223 int had_directory; 9224 char *devdir,*cp; 9225 char *vmsspec; 9226 $DESCRIPTOR(filespec, ""); 9227 $DESCRIPTOR(defaultspec, "SYS$DISK:[]"); 9228 $DESCRIPTOR(resultspec, ""); 9229 unsigned long int lff_flags = 0; 9230 int sts; 9231 int rms_sts; 9232 9233 #ifdef VMS_LONGNAME_SUPPORT 9234 lff_flags = LIB$M_FIL_LONG_NAMES; 9235 #endif 9236 9237 for (cp = item; *cp; cp++) { 9238 if (*cp == '*' || *cp == '%' || isspace(*cp)) break; 9239 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break; 9240 } 9241 if (!*cp || isspace(*cp)) 9242 { 9243 add_item(head, tail, item, count); 9244 return; 9245 } 9246 else 9247 { 9248 /* "double quoted" wild card expressions pass as is */ 9249 /* From DCL that means using e.g.: */ 9250 /* perl program """perl.*""" */ 9251 item_len = strlen(item); 9252 if ( '"' == *item && '"' == item[item_len-1] ) 9253 { 9254 item++; 9255 item[item_len-2] = '\0'; 9256 add_item(head, tail, item, count); 9257 return; 9258 } 9259 } 9260 resultspec.dsc$b_dtype = DSC$K_DTYPE_T; 9261 resultspec.dsc$b_class = DSC$K_CLASS_D; 9262 resultspec.dsc$a_pointer = NULL; 9263 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS); 9264 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9265 if ((isunix = (int) strchr(item,'/')) != (int) NULL) 9266 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL); 9267 if (!isunix || !filespec.dsc$a_pointer) 9268 filespec.dsc$a_pointer = item; 9269 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer); 9270 /* 9271 * Only return version specs, if the caller specified a version 9272 */ 9273 had_version = strchr(item, ';'); 9274 /* 9275 * Only return device and directory specs, if the caller specified either. 9276 */ 9277 had_device = strchr(item, ':'); 9278 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<')); 9279 9280 while ($VMS_STATUS_SUCCESS(sts = lib$find_file 9281 (&filespec, &resultspec, &context, 9282 &defaultspec, 0, &rms_sts, &lff_flags))) 9283 { 9284 char *string; 9285 char *c; 9286 9287 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1); 9288 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9289 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1); 9290 if (NULL == had_version) 9291 *(strrchr(string, ';')) = '\0'; 9292 if ((!had_directory) && (had_device == NULL)) 9293 { 9294 if (NULL == (devdir = strrchr(string, ']'))) 9295 devdir = strrchr(string, '>'); 9296 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1); 9297 } 9298 /* 9299 * Be consistent with what the C RTL has already done to the rest of 9300 * the argv items and lowercase all of these names. 9301 */ 9302 if (!decc_efs_case_preserve) { 9303 for (c = string; *c; ++c) 9304 if (isupper(*c)) 9305 *c = tolower(*c); 9306 } 9307 if (isunix) trim_unixpath(string,item,1); 9308 add_item(head, tail, string, count); 9309 ++expcount; 9310 } 9311 PerlMem_free(vmsspec); 9312 if (sts != RMS$_NMF) 9313 { 9314 set_vaxc_errno(sts); 9315 switch (sts) 9316 { 9317 case RMS$_FNF: case RMS$_DNF: 9318 set_errno(ENOENT); break; 9319 case RMS$_DIR: 9320 set_errno(ENOTDIR); break; 9321 case RMS$_DEV: 9322 set_errno(ENODEV); break; 9323 case RMS$_FNM: case RMS$_SYN: 9324 set_errno(EINVAL); break; 9325 case RMS$_PRV: 9326 set_errno(EACCES); break; 9327 default: 9328 _ckvmssts_noperl(sts); 9329 } 9330 } 9331 if (expcount == 0) 9332 add_item(head, tail, item, count); 9333 _ckvmssts_noperl(lib$sfree1_dd(&resultspec)); 9334 _ckvmssts_noperl(lib$find_file_end(&context)); 9335 } 9336 9337 static int child_st[2];/* Event Flag set when child process completes */ 9338 9339 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */ 9340 9341 static unsigned long int exit_handler(void) 9342 { 9343 short iosb[4]; 9344 9345 if (0 == child_st[0]) 9346 { 9347 #ifdef ARGPROC_DEBUG 9348 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n"); 9349 #endif 9350 fflush(stdout); /* Have to flush pipe for binary data to */ 9351 /* terminate properly -- <tp@mccall.com> */ 9352 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0); 9353 sys$dassgn(child_chan); 9354 fclose(stdout); 9355 sys$synch(0, child_st); 9356 } 9357 return(1); 9358 } 9359 9360 static void sig_child(int chan) 9361 { 9362 #ifdef ARGPROC_DEBUG 9363 PerlIO_printf(Perl_debug_log, "Child Completion AST\n"); 9364 #endif 9365 if (child_st[0] == 0) 9366 child_st[0] = 1; 9367 } 9368 9369 static struct exit_control_block exit_block = 9370 { 9371 0, 9372 exit_handler, 9373 1, 9374 &exit_block.exit_status, 9375 0 9376 }; 9377 9378 static void 9379 pipe_and_fork(pTHX_ char **cmargv) 9380 { 9381 PerlIO *fp; 9382 struct dsc$descriptor_s *vmscmd; 9383 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q; 9384 int sts, j, l, ismcr, quote, tquote = 0; 9385 9386 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd); 9387 vms_execfree(vmscmd); 9388 9389 j = l = 0; 9390 p = subcmd; 9391 q = cmargv[0]; 9392 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C' 9393 && toupper(*(q+2)) == 'R' && !*(q+3); 9394 9395 while (q && l < MAX_DCL_LINE_LENGTH) { 9396 if (!*q) { 9397 if (j > 0 && quote) { 9398 *p++ = '"'; 9399 l++; 9400 } 9401 q = cmargv[++j]; 9402 if (q) { 9403 if (ismcr && j > 1) quote = 1; 9404 tquote = (strchr(q,' ')) != NULL || *q == '\0'; 9405 *p++ = ' '; 9406 l++; 9407 if (quote || tquote) { 9408 *p++ = '"'; 9409 l++; 9410 } 9411 } 9412 } else { 9413 if ((quote||tquote) && *q == '"') { 9414 *p++ = '"'; 9415 l++; 9416 } 9417 *p++ = *q++; 9418 l++; 9419 } 9420 } 9421 *p = '\0'; 9422 9423 fp = safe_popen(aTHX_ subcmd,"wbF",&sts); 9424 if (fp == NULL) { 9425 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts); 9426 } 9427 } 9428 9429 static int background_process(pTHX_ int argc, char **argv) 9430 { 9431 char command[MAX_DCL_SYMBOL + 1] = "$"; 9432 $DESCRIPTOR(value, ""); 9433 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND"); 9434 static $DESCRIPTOR(null, "NLA0:"); 9435 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID"); 9436 char pidstring[80]; 9437 $DESCRIPTOR(pidstr, ""); 9438 int pid; 9439 unsigned long int flags = 17, one = 1, retsts; 9440 int len; 9441 9442 len = my_strlcat(command, argv[0], sizeof(command)); 9443 while (--argc && (len < MAX_DCL_SYMBOL)) 9444 { 9445 my_strlcat(command, " \"", sizeof(command)); 9446 my_strlcat(command, *(++argv), sizeof(command)); 9447 len = my_strlcat(command, "\"", sizeof(command)); 9448 } 9449 value.dsc$a_pointer = command; 9450 value.dsc$w_length = strlen(value.dsc$a_pointer); 9451 _ckvmssts_noperl(lib$set_symbol(&cmd, &value)); 9452 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid); 9453 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */ 9454 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid)); 9455 } 9456 else { 9457 _ckvmssts_noperl(retsts); 9458 } 9459 #ifdef ARGPROC_DEBUG 9460 PerlIO_printf(Perl_debug_log, "%s\n", command); 9461 #endif 9462 sprintf(pidstring, "%08X", pid); 9463 PerlIO_printf(Perl_debug_log, "%s\n", pidstring); 9464 pidstr.dsc$a_pointer = pidstring; 9465 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer); 9466 lib$set_symbol(&pidsymbol, &pidstr); 9467 return(SS$_NORMAL); 9468 } 9469 /*}}}*/ 9470 /***** End of code taken from Mark Pizzolato's argproc.c package *****/ 9471 9472 9473 /* OS-specific initialization at image activation (not thread startup) */ 9474 /* Older VAXC header files lack these constants */ 9475 #ifndef JPI$_RIGHTS_SIZE 9476 # define JPI$_RIGHTS_SIZE 817 9477 #endif 9478 #ifndef KGB$M_SUBSYSTEM 9479 # define KGB$M_SUBSYSTEM 0x8 9480 #endif 9481 9482 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */ 9483 9484 /*{{{void vms_image_init(int *, char ***)*/ 9485 void 9486 vms_image_init(int *argcp, char ***argvp) 9487 { 9488 int status; 9489 char eqv[LNM$C_NAMLENGTH+1] = ""; 9490 unsigned int len, tabct = 8, tabidx = 0; 9491 unsigned long int *mask, iosb[2], i, rlst[128], rsz; 9492 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)]; 9493 unsigned short int dummy, rlen; 9494 struct dsc$descriptor_s **tabvec; 9495 #if defined(PERL_IMPLICIT_CONTEXT) 9496 pTHX = NULL; 9497 #endif 9498 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy}, 9499 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen}, 9500 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy}, 9501 { 0, 0, 0, 0} }; 9502 9503 #ifdef KILL_BY_SIGPRC 9504 Perl_csighandler_init(); 9505 #endif 9506 9507 #if __CRTL_VER >= 70300000 && !defined(__VAX) 9508 /* This was moved from the pre-image init handler because on threaded */ 9509 /* Perl it was always returning 0 for the default value. */ 9510 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH); 9511 if (status > 0) { 9512 int s; 9513 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT"); 9514 if (s > 0) { 9515 int initial; 9516 initial = decc$feature_get_value(s, 4); 9517 if (initial > 0) { 9518 /* initial is: 0 if nothing has set the feature */ 9519 /* -1 if initialized to default */ 9520 /* 1 if set by logical name */ 9521 /* 2 if set by decc$feature_set_value */ 9522 decc_disable_posix_root = decc$feature_get_value(s, 1); 9523 9524 /* If the value is not valid, force the feature off */ 9525 if (decc_disable_posix_root < 0) { 9526 decc$feature_set_value(s, 1, 1); 9527 decc_disable_posix_root = 1; 9528 } 9529 } 9530 else { 9531 /* Nothing has asked for it explicitly, so use our own default. */ 9532 decc_disable_posix_root = 1; 9533 decc$feature_set_value(s, 1, 1); 9534 } 9535 } 9536 } 9537 #endif 9538 9539 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL)); 9540 _ckvmssts_noperl(iosb[0]); 9541 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) { 9542 if (iprv[i]) { /* Running image installed with privs? */ 9543 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */ 9544 will_taint = TRUE; 9545 break; 9546 } 9547 } 9548 /* Rights identifiers might trigger tainting as well. */ 9549 if (!will_taint && (rlen || rsz)) { 9550 while (rlen < rsz) { 9551 /* We didn't get all the identifiers on the first pass. Allocate a 9552 * buffer much larger than $GETJPI wants (rsz is size in bytes that 9553 * were needed to hold all identifiers at time of last call; we'll 9554 * allocate that many unsigned long ints), and go back and get 'em. 9555 * If it gave us less than it wanted to despite ample buffer space, 9556 * something's broken. Is your system missing a system identifier? 9557 */ 9558 if (rsz <= jpilist[1].buflen) { 9559 /* Perl_croak accvios when used this early in startup. */ 9560 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s", 9561 rsz, (unsigned long) jpilist[1].buflen, 9562 "Check your rights database for corruption.\n"); 9563 exit(SS$_ABORT); 9564 } 9565 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr); 9566 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int)); 9567 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9568 jpilist[1].buflen = rsz * sizeof(unsigned long int); 9569 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL)); 9570 _ckvmssts_noperl(iosb[0]); 9571 } 9572 mask = (unsigned long int *)jpilist[1].bufadr; 9573 /* Check attribute flags for each identifier (2nd longword); protected 9574 * subsystem identifiers trigger tainting. 9575 */ 9576 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) { 9577 if (mask[i] & KGB$M_SUBSYSTEM) { 9578 will_taint = TRUE; 9579 break; 9580 } 9581 } 9582 if (mask != rlst) PerlMem_free(mask); 9583 } 9584 9585 /* When Perl is in decc_filename_unix_report mode and is run from a concealed 9586 * logical, some versions of the CRTL will add a phanthom /000000/ 9587 * directory. This needs to be removed. 9588 */ 9589 if (decc_filename_unix_report) { 9590 char * zeros; 9591 int ulen; 9592 ulen = strlen(argvp[0][0]); 9593 if (ulen > 7) { 9594 zeros = strstr(argvp[0][0], "/000000/"); 9595 if (zeros != NULL) { 9596 int mlen; 9597 mlen = ulen - (zeros - argvp[0][0]) - 7; 9598 memmove(zeros, &zeros[7], mlen); 9599 ulen = ulen - 7; 9600 argvp[0][0][ulen] = '\0'; 9601 } 9602 } 9603 /* It also may have a trailing dot that needs to be removed otherwise 9604 * it will be converted to VMS mode incorrectly. 9605 */ 9606 ulen--; 9607 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype)) 9608 argvp[0][0][ulen] = '\0'; 9609 } 9610 9611 /* We need to use this hack to tell Perl it should run with tainting, 9612 * since its tainting flag may be part of the PL_curinterp struct, which 9613 * hasn't been allocated when vms_image_init() is called. 9614 */ 9615 if (will_taint) { 9616 char **newargv, **oldargv; 9617 oldargv = *argvp; 9618 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *)); 9619 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9620 newargv[0] = oldargv[0]; 9621 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char)); 9622 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9623 strcpy(newargv[1], "-T"); 9624 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **); 9625 (*argcp)++; 9626 newargv[*argcp] = NULL; 9627 /* We orphan the old argv, since we don't know where it's come from, 9628 * so we don't know how to free it. 9629 */ 9630 *argvp = newargv; 9631 } 9632 else { /* Did user explicitly request tainting? */ 9633 int i; 9634 char *cp, **av = *argvp; 9635 for (i = 1; i < *argcp; i++) { 9636 if (*av[i] != '-') break; 9637 for (cp = av[i]+1; *cp; cp++) { 9638 if (*cp == 'T') { will_taint = 1; break; } 9639 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' || 9640 strchr("DFIiMmx",*cp)) break; 9641 } 9642 if (will_taint) break; 9643 } 9644 } 9645 9646 for (tabidx = 0; 9647 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx); 9648 tabidx++) { 9649 if (!tabidx) { 9650 tabvec = (struct dsc$descriptor_s **) 9651 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *)); 9652 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9653 } 9654 else if (tabidx >= tabct) { 9655 tabct += 8; 9656 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *)); 9657 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9658 } 9659 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 9660 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9661 tabvec[tabidx]->dsc$w_length = len; 9662 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T; 9663 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S; 9664 tabvec[tabidx]->dsc$a_pointer = PerlMem_malloc(len + 1); 9665 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9666 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1); 9667 } 9668 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; } 9669 9670 getredirection(argcp,argvp); 9671 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) ) 9672 { 9673 # include <reentrancy.h> 9674 decc$set_reentrancy(C$C_MULTITHREAD); 9675 } 9676 #endif 9677 return; 9678 } 9679 /*}}}*/ 9680 9681 9682 /* trim_unixpath() 9683 * Trim Unix-style prefix off filespec, so it looks like what a shell 9684 * glob expansion would return (i.e. from specified prefix on, not 9685 * full path). Note that returned filespec is Unix-style, regardless 9686 * of whether input filespec was VMS-style or Unix-style. 9687 * 9688 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to 9689 * determine prefix (both may be in VMS or Unix syntax). opts is a bit 9690 * vector of options; at present, only bit 0 is used, and if set tells 9691 * trim unixpath to try the current default directory as a prefix when 9692 * presented with a possibly ambiguous ... wildcard. 9693 * 9694 * Returns !=0 on success, with trimmed filespec replacing contents of 9695 * fspec, and 0 on failure, with contents of fpsec unchanged. 9696 */ 9697 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/ 9698 int 9699 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts) 9700 { 9701 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2; 9702 int tmplen, reslen = 0, dirs = 0; 9703 9704 if (!wildspec || !fspec) return 0; 9705 9706 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS); 9707 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9708 tplate = unixwild; 9709 if (strpbrk(wildspec,"]>:") != NULL) { 9710 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) { 9711 PerlMem_free(unixwild); 9712 return 0; 9713 } 9714 } 9715 else { 9716 my_strlcpy(unixwild, wildspec, VMS_MAXRSS); 9717 } 9718 unixified = (char *)PerlMem_malloc(VMS_MAXRSS); 9719 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9720 if (strpbrk(fspec,"]>:") != NULL) { 9721 if (int_tounixspec(fspec, unixified, NULL) == NULL) { 9722 PerlMem_free(unixwild); 9723 PerlMem_free(unixified); 9724 return 0; 9725 } 9726 else base = unixified; 9727 /* reslen != 0 ==> we had to unixify resultant filespec, so we must 9728 * check to see that final result fits into (isn't longer than) fspec */ 9729 reslen = strlen(fspec); 9730 } 9731 else base = fspec; 9732 9733 /* No prefix or absolute path on wildcard, so nothing to remove */ 9734 if (!*tplate || *tplate == '/') { 9735 PerlMem_free(unixwild); 9736 if (base == fspec) { 9737 PerlMem_free(unixified); 9738 return 1; 9739 } 9740 tmplen = strlen(unixified); 9741 if (tmplen > reslen) { 9742 PerlMem_free(unixified); 9743 return 0; /* not enough space */ 9744 } 9745 /* Copy unixified resultant, including trailing NUL */ 9746 memmove(fspec,unixified,tmplen+1); 9747 PerlMem_free(unixified); 9748 return 1; 9749 } 9750 9751 for (end = base; *end; end++) ; /* Find end of resultant filespec */ 9752 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */ 9753 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++; 9754 for (cp1 = end ;cp1 >= base; cp1--) 9755 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */ 9756 { cp1++; break; } 9757 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1); 9758 PerlMem_free(unixified); 9759 PerlMem_free(unixwild); 9760 return 1; 9761 } 9762 else { 9763 char *tpl, *lcres; 9764 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1; 9765 int ells = 1, totells, segdirs, match; 9766 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, 9767 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 9768 9769 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;} 9770 totells = ells; 9771 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++; 9772 tpl = (char *)PerlMem_malloc(VMS_MAXRSS); 9773 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9774 if (ellipsis == tplate && opts & 1) { 9775 /* Template begins with an ellipsis. Since we can't tell how many 9776 * directory names at the front of the resultant to keep for an 9777 * arbitrary starting point, we arbitrarily choose the current 9778 * default directory as a starting point. If it's there as a prefix, 9779 * clip it off. If not, fall through and act as if the leading 9780 * ellipsis weren't there (i.e. return shortest possible path that 9781 * could match template). 9782 */ 9783 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) { 9784 PerlMem_free(tpl); 9785 PerlMem_free(unixified); 9786 PerlMem_free(unixwild); 9787 return 0; 9788 } 9789 if (!decc_efs_case_preserve) { 9790 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9791 if (_tolower(*cp1) != _tolower(*cp2)) break; 9792 } 9793 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9794 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--; 9795 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) { 9796 memmove(fspec,cp2+1,end - cp2); 9797 PerlMem_free(tpl); 9798 PerlMem_free(unixified); 9799 PerlMem_free(unixwild); 9800 return 1; 9801 } 9802 } 9803 /* First off, back up over constant elements at end of path */ 9804 if (dirs) { 9805 for (front = end ; front >= base; front--) 9806 if (*front == '/' && !dirs--) { front++; break; } 9807 } 9808 lcres = (char *)PerlMem_malloc(VMS_MAXRSS); 9809 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9810 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1); 9811 cp1++,cp2++) { 9812 if (!decc_efs_case_preserve) { 9813 *cp2 = _tolower(*cp1); /* Make lc copy for match */ 9814 } 9815 else { 9816 *cp2 = *cp1; 9817 } 9818 } 9819 if (cp1 != '\0') { 9820 PerlMem_free(tpl); 9821 PerlMem_free(unixified); 9822 PerlMem_free(unixwild); 9823 PerlMem_free(lcres); 9824 return 0; /* Path too long. */ 9825 } 9826 lcend = cp2; 9827 *cp2 = '\0'; /* Pick up with memcpy later */ 9828 lcfront = lcres + (front - base); 9829 /* Now skip over each ellipsis and try to match the path in front of it. */ 9830 while (ells--) { 9831 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--) 9832 if (*(cp1) == '.' && *(cp1+1) == '.' && 9833 *(cp1+2) == '.' && *(cp1+3) == '/' ) break; 9834 if (cp1 < tplate) break; /* template started with an ellipsis */ 9835 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */ 9836 ellipsis = cp1; continue; 9837 } 9838 wilddsc.dsc$a_pointer = tpl; 9839 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1; 9840 nextell = cp1; 9841 for (segdirs = 0, cp2 = tpl; 9842 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1); 9843 cp1++, cp2++) { 9844 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */ 9845 else { 9846 if (!decc_efs_case_preserve) { 9847 *cp2 = _tolower(*cp1); /* else lowercase for match */ 9848 } 9849 else { 9850 *cp2 = *cp1; /* else preserve case for match */ 9851 } 9852 } 9853 if (*cp2 == '/') segdirs++; 9854 } 9855 if (cp1 != ellipsis - 1) { 9856 PerlMem_free(tpl); 9857 PerlMem_free(unixified); 9858 PerlMem_free(unixwild); 9859 PerlMem_free(lcres); 9860 return 0; /* Path too long */ 9861 } 9862 /* Back up at least as many dirs as in template before matching */ 9863 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--) 9864 if (*cp1 == '/' && !segdirs--) { cp1++; break; } 9865 for (match = 0; cp1 > lcres;) { 9866 resdsc.dsc$a_pointer = cp1; 9867 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) { 9868 match++; 9869 if (match == 1) lcfront = cp1; 9870 } 9871 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; } 9872 } 9873 if (!match) { 9874 PerlMem_free(tpl); 9875 PerlMem_free(unixified); 9876 PerlMem_free(unixwild); 9877 PerlMem_free(lcres); 9878 return 0; /* Can't find prefix ??? */ 9879 } 9880 if (match > 1 && opts & 1) { 9881 /* This ... wildcard could cover more than one set of dirs (i.e. 9882 * a set of similar dir names is repeated). If the template 9883 * contains more than 1 ..., upstream elements could resolve the 9884 * ambiguity, but it's not worth a full backtracking setup here. 9885 * As a quick heuristic, clip off the current default directory 9886 * if it's present to find the trimmed spec, else use the 9887 * shortest string that this ... could cover. 9888 */ 9889 char def[NAM$C_MAXRSS+1], *st; 9890 9891 if (getcwd(def, sizeof def,0) == NULL) { 9892 PerlMem_free(unixified); 9893 PerlMem_free(unixwild); 9894 PerlMem_free(lcres); 9895 PerlMem_free(tpl); 9896 return 0; 9897 } 9898 if (!decc_efs_case_preserve) { 9899 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9900 if (_tolower(*cp1) != _tolower(*cp2)) break; 9901 } 9902 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9903 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--; 9904 if (*cp1 == '\0' && *cp2 == '/') { 9905 memmove(fspec,cp2+1,end - cp2); 9906 PerlMem_free(tpl); 9907 PerlMem_free(unixified); 9908 PerlMem_free(unixwild); 9909 PerlMem_free(lcres); 9910 return 1; 9911 } 9912 /* Nope -- stick with lcfront from above and keep going. */ 9913 } 9914 } 9915 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1); 9916 PerlMem_free(tpl); 9917 PerlMem_free(unixified); 9918 PerlMem_free(unixwild); 9919 PerlMem_free(lcres); 9920 return 1; 9921 } 9922 9923 } /* end of trim_unixpath() */ 9924 /*}}}*/ 9925 9926 9927 /* 9928 * VMS readdir() routines. 9929 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990. 9930 * 9931 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu 9932 * Minor modifications to original routines. 9933 */ 9934 9935 /* readdir may have been redefined by reentr.h, so make sure we get 9936 * the local version for what we do here. 9937 */ 9938 #ifdef readdir 9939 # undef readdir 9940 #endif 9941 #if !defined(PERL_IMPLICIT_CONTEXT) 9942 # define readdir Perl_readdir 9943 #else 9944 # define readdir(a) Perl_readdir(aTHX_ a) 9945 #endif 9946 9947 /* Number of elements in vms_versions array */ 9948 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0]) 9949 9950 /* 9951 * Open a directory, return a handle for later use. 9952 */ 9953 /*{{{ DIR *opendir(char*name) */ 9954 DIR * 9955 Perl_opendir(pTHX_ const char *name) 9956 { 9957 DIR *dd; 9958 char *dir; 9959 Stat_t sb; 9960 9961 Newx(dir, VMS_MAXRSS, char); 9962 if (int_tovmspath(name, dir, NULL) == NULL) { 9963 Safefree(dir); 9964 return NULL; 9965 } 9966 /* Check access before stat; otherwise stat does not 9967 * accurately report whether it's a directory. 9968 */ 9969 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) { 9970 /* cando_by_name has already set errno */ 9971 Safefree(dir); 9972 return NULL; 9973 } 9974 if (flex_stat(dir,&sb) == -1) return NULL; 9975 if (!S_ISDIR(sb.st_mode)) { 9976 Safefree(dir); 9977 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR); 9978 return NULL; 9979 } 9980 /* Get memory for the handle, and the pattern. */ 9981 Newx(dd,1,DIR); 9982 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char); 9983 9984 /* Fill in the fields; mainly playing with the descriptor. */ 9985 sprintf(dd->pattern, "%s*.*",dir); 9986 Safefree(dir); 9987 dd->context = 0; 9988 dd->count = 0; 9989 dd->flags = 0; 9990 /* By saying we want the result of readdir() in unix format, we are really 9991 * saying we want all the escapes removed, translating characters that 9992 * must be escaped in a VMS-format name to their unescaped form, which is 9993 * presumably allowed in a Unix-format name. 9994 */ 9995 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0; 9996 dd->pat.dsc$a_pointer = dd->pattern; 9997 dd->pat.dsc$w_length = strlen(dd->pattern); 9998 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T; 9999 dd->pat.dsc$b_class = DSC$K_CLASS_S; 10000 #if defined(USE_ITHREADS) 10001 Newx(dd->mutex,1,perl_mutex); 10002 MUTEX_INIT( (perl_mutex *) dd->mutex ); 10003 #else 10004 dd->mutex = NULL; 10005 #endif 10006 10007 return dd; 10008 } /* end of opendir() */ 10009 /*}}}*/ 10010 10011 /* 10012 * Set the flag to indicate we want versions or not. 10013 */ 10014 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/ 10015 void 10016 vmsreaddirversions(DIR *dd, int flag) 10017 { 10018 if (flag) 10019 dd->flags |= PERL_VMSDIR_M_VERSIONS; 10020 else 10021 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 10022 } 10023 /*}}}*/ 10024 10025 /* 10026 * Free up an opened directory. 10027 */ 10028 /*{{{ void closedir(DIR *dd)*/ 10029 void 10030 Perl_closedir(DIR *dd) 10031 { 10032 int sts; 10033 10034 sts = lib$find_file_end(&dd->context); 10035 Safefree(dd->pattern); 10036 #if defined(USE_ITHREADS) 10037 MUTEX_DESTROY( (perl_mutex *) dd->mutex ); 10038 Safefree(dd->mutex); 10039 #endif 10040 Safefree(dd); 10041 } 10042 /*}}}*/ 10043 10044 /* 10045 * Collect all the version numbers for the current file. 10046 */ 10047 static void 10048 collectversions(pTHX_ DIR *dd) 10049 { 10050 struct dsc$descriptor_s pat; 10051 struct dsc$descriptor_s res; 10052 struct dirent *e; 10053 char *p, *text, *buff; 10054 int i; 10055 unsigned long context, tmpsts; 10056 10057 /* Convenient shorthand. */ 10058 e = &dd->entry; 10059 10060 /* Add the version wildcard, ignoring the "*.*" put on before */ 10061 i = strlen(dd->pattern); 10062 Newx(text,i + e->d_namlen + 3,char); 10063 my_strlcpy(text, dd->pattern, i + 1); 10064 sprintf(&text[i - 3], "%s;*", e->d_name); 10065 10066 /* Set up the pattern descriptor. */ 10067 pat.dsc$a_pointer = text; 10068 pat.dsc$w_length = i + e->d_namlen - 1; 10069 pat.dsc$b_dtype = DSC$K_DTYPE_T; 10070 pat.dsc$b_class = DSC$K_CLASS_S; 10071 10072 /* Set up result descriptor. */ 10073 Newx(buff, VMS_MAXRSS, char); 10074 res.dsc$a_pointer = buff; 10075 res.dsc$w_length = VMS_MAXRSS - 1; 10076 res.dsc$b_dtype = DSC$K_DTYPE_T; 10077 res.dsc$b_class = DSC$K_CLASS_S; 10078 10079 /* Read files, collecting versions. */ 10080 for (context = 0, e->vms_verscount = 0; 10081 e->vms_verscount < VERSIZE(e); 10082 e->vms_verscount++) { 10083 unsigned long rsts; 10084 unsigned long flags = 0; 10085 10086 #ifdef VMS_LONGNAME_SUPPORT 10087 flags = LIB$M_FIL_LONG_NAMES; 10088 #endif 10089 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags); 10090 if (tmpsts == RMS$_NMF || context == 0) break; 10091 _ckvmssts(tmpsts); 10092 buff[VMS_MAXRSS - 1] = '\0'; 10093 if ((p = strchr(buff, ';'))) 10094 e->vms_versions[e->vms_verscount] = atoi(p + 1); 10095 else 10096 e->vms_versions[e->vms_verscount] = -1; 10097 } 10098 10099 _ckvmssts(lib$find_file_end(&context)); 10100 Safefree(text); 10101 Safefree(buff); 10102 10103 } /* end of collectversions() */ 10104 10105 /* 10106 * Read the next entry from the directory. 10107 */ 10108 /*{{{ struct dirent *readdir(DIR *dd)*/ 10109 struct dirent * 10110 Perl_readdir(pTHX_ DIR *dd) 10111 { 10112 struct dsc$descriptor_s res; 10113 char *p, *buff; 10114 unsigned long int tmpsts; 10115 unsigned long rsts; 10116 unsigned long flags = 0; 10117 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 10118 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 10119 10120 /* Set up result descriptor, and get next file. */ 10121 Newx(buff, VMS_MAXRSS, char); 10122 res.dsc$a_pointer = buff; 10123 res.dsc$w_length = VMS_MAXRSS - 1; 10124 res.dsc$b_dtype = DSC$K_DTYPE_T; 10125 res.dsc$b_class = DSC$K_CLASS_S; 10126 10127 #ifdef VMS_LONGNAME_SUPPORT 10128 flags = LIB$M_FIL_LONG_NAMES; 10129 #endif 10130 10131 tmpsts = lib$find_file 10132 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags); 10133 if (dd->context == 0) 10134 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */ 10135 10136 if (!(tmpsts & 1)) { 10137 switch (tmpsts) { 10138 case RMS$_NMF: 10139 break; /* no more files considered success */ 10140 case RMS$_PRV: 10141 SETERRNO(EACCES, tmpsts); break; 10142 case RMS$_DEV: 10143 SETERRNO(ENODEV, tmpsts); break; 10144 case RMS$_DIR: 10145 SETERRNO(ENOTDIR, tmpsts); break; 10146 case RMS$_FNF: case RMS$_DNF: 10147 SETERRNO(ENOENT, tmpsts); break; 10148 default: 10149 SETERRNO(EVMSERR, tmpsts); 10150 } 10151 Safefree(buff); 10152 return NULL; 10153 } 10154 dd->count++; 10155 /* Force the buffer to end with a NUL, and downcase name to match C convention. */ 10156 buff[res.dsc$w_length] = '\0'; 10157 p = buff + res.dsc$w_length; 10158 while (--p >= buff) if (!isspace(*p)) break; 10159 *p = '\0'; 10160 if (!decc_efs_case_preserve) { 10161 for (p = buff; *p; p++) *p = _tolower(*p); 10162 } 10163 10164 /* Skip any directory component and just copy the name. */ 10165 sts = vms_split_path 10166 (buff, 10167 &v_spec, 10168 &v_len, 10169 &r_spec, 10170 &r_len, 10171 &d_spec, 10172 &d_len, 10173 &n_spec, 10174 &n_len, 10175 &e_spec, 10176 &e_len, 10177 &vs_spec, 10178 &vs_len); 10179 10180 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 10181 10182 /* In Unix report mode, remove the ".dir;1" from the name */ 10183 /* if it is a real directory. */ 10184 if (decc_filename_unix_report && decc_efs_charset) { 10185 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 10186 Stat_t statbuf; 10187 int ret_sts; 10188 10189 ret_sts = flex_lstat(buff, &statbuf); 10190 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) { 10191 e_len = 0; 10192 e_spec[0] = 0; 10193 } 10194 } 10195 } 10196 10197 /* Drop NULL extensions on UNIX file specification */ 10198 if ((e_len == 1) && decc_readdir_dropdotnotype) { 10199 e_len = 0; 10200 e_spec[0] = '\0'; 10201 } 10202 } 10203 10204 memcpy(dd->entry.d_name, n_spec, n_len + e_len); 10205 dd->entry.d_name[n_len + e_len] = '\0'; 10206 dd->entry.d_namlen = n_len + e_len; 10207 10208 /* Convert the filename to UNIX format if needed */ 10209 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 10210 10211 /* Translate the encoded characters. */ 10212 /* Fixme: Unicode handling could result in embedded 0 characters */ 10213 if (strchr(dd->entry.d_name, '^') != NULL) { 10214 char new_name[256]; 10215 char * q; 10216 p = dd->entry.d_name; 10217 q = new_name; 10218 while (*p != 0) { 10219 int inchars_read, outchars_added; 10220 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added); 10221 p += inchars_read; 10222 q += outchars_added; 10223 /* fix-me */ 10224 /* if outchars_added > 1, then this is a wide file specification */ 10225 /* Wide file specifications need to be passed in Perl */ 10226 /* counted strings apparently with a Unicode flag */ 10227 } 10228 *q = 0; 10229 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name)); 10230 } 10231 } 10232 10233 dd->entry.vms_verscount = 0; 10234 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd); 10235 Safefree(buff); 10236 return &dd->entry; 10237 10238 } /* end of readdir() */ 10239 /*}}}*/ 10240 10241 /* 10242 * Read the next entry from the directory -- thread-safe version. 10243 */ 10244 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/ 10245 int 10246 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result) 10247 { 10248 int retval; 10249 10250 MUTEX_LOCK( (perl_mutex *) dd->mutex ); 10251 10252 entry = readdir(dd); 10253 *result = entry; 10254 retval = ( *result == NULL ? errno : 0 ); 10255 10256 MUTEX_UNLOCK( (perl_mutex *) dd->mutex ); 10257 10258 return retval; 10259 10260 } /* end of readdir_r() */ 10261 /*}}}*/ 10262 10263 /* 10264 * Return something that can be used in a seekdir later. 10265 */ 10266 /*{{{ long telldir(DIR *dd)*/ 10267 long 10268 Perl_telldir(DIR *dd) 10269 { 10270 return dd->count; 10271 } 10272 /*}}}*/ 10273 10274 /* 10275 * Return to a spot where we used to be. Brute force. 10276 */ 10277 /*{{{ void seekdir(DIR *dd,long count)*/ 10278 void 10279 Perl_seekdir(pTHX_ DIR *dd, long count) 10280 { 10281 int old_flags; 10282 10283 /* If we haven't done anything yet... */ 10284 if (dd->count == 0) 10285 return; 10286 10287 /* Remember some state, and clear it. */ 10288 old_flags = dd->flags; 10289 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 10290 _ckvmssts(lib$find_file_end(&dd->context)); 10291 dd->context = 0; 10292 10293 /* The increment is in readdir(). */ 10294 for (dd->count = 0; dd->count < count; ) 10295 readdir(dd); 10296 10297 dd->flags = old_flags; 10298 10299 } /* end of seekdir() */ 10300 /*}}}*/ 10301 10302 /* VMS subprocess management 10303 * 10304 * my_vfork() - just a vfork(), after setting a flag to record that 10305 * the current script is trying a Unix-style fork/exec. 10306 * 10307 * vms_do_aexec() and vms_do_exec() are called in response to the 10308 * perl 'exec' function. If this follows a vfork call, then they 10309 * call out the regular perl routines in doio.c which do an 10310 * execvp (for those who really want to try this under VMS). 10311 * Otherwise, they do exactly what the perl docs say exec should 10312 * do - terminate the current script and invoke a new command 10313 * (See below for notes on command syntax.) 10314 * 10315 * do_aspawn() and do_spawn() implement the VMS side of the perl 10316 * 'system' function. 10317 * 10318 * Note on command arguments to perl 'exec' and 'system': When handled 10319 * in 'VMSish fashion' (i.e. not after a call to vfork) The args 10320 * are concatenated to form a DCL command string. If the first non-numeric 10321 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such), 10322 * the command string is handed off to DCL directly. Otherwise, 10323 * the first token of the command is taken as the filespec of an image 10324 * to run. The filespec is expanded using a default type of '.EXE' and 10325 * the process defaults for device, directory, etc., and if found, the resultant 10326 * filespec is invoked using the DCL verb 'MCR', and passed the rest of 10327 * the command string as parameters. This is perhaps a bit complicated, 10328 * but I hope it will form a happy medium between what VMS folks expect 10329 * from lib$spawn and what Unix folks expect from exec. 10330 */ 10331 10332 static int vfork_called; 10333 10334 /*{{{int my_vfork(void)*/ 10335 int 10336 my_vfork(void) 10337 { 10338 vfork_called++; 10339 return vfork(); 10340 } 10341 /*}}}*/ 10342 10343 10344 static void 10345 vms_execfree(struct dsc$descriptor_s *vmscmd) 10346 { 10347 if (vmscmd) { 10348 if (vmscmd->dsc$a_pointer) { 10349 PerlMem_free(vmscmd->dsc$a_pointer); 10350 } 10351 PerlMem_free(vmscmd); 10352 } 10353 } 10354 10355 static char * 10356 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp) 10357 { 10358 char *junk, *tmps = NULL; 10359 size_t cmdlen = 0; 10360 size_t rlen; 10361 SV **idx; 10362 STRLEN n_a; 10363 10364 idx = mark; 10365 if (really) { 10366 tmps = SvPV(really,rlen); 10367 if (*tmps) { 10368 cmdlen += rlen + 1; 10369 idx++; 10370 } 10371 } 10372 10373 for (idx++; idx <= sp; idx++) { 10374 if (*idx) { 10375 junk = SvPVx(*idx,rlen); 10376 cmdlen += rlen ? rlen + 1 : 0; 10377 } 10378 } 10379 Newx(PL_Cmd, cmdlen+1, char); 10380 10381 if (tmps && *tmps) { 10382 my_strlcpy(PL_Cmd, tmps, cmdlen + 1); 10383 mark++; 10384 } 10385 else *PL_Cmd = '\0'; 10386 while (++mark <= sp) { 10387 if (*mark) { 10388 char *s = SvPVx(*mark,n_a); 10389 if (!*s) continue; 10390 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1); 10391 my_strlcat(PL_Cmd, s, cmdlen+1); 10392 } 10393 } 10394 return PL_Cmd; 10395 10396 } /* end of setup_argstr() */ 10397 10398 10399 static unsigned long int 10400 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote, 10401 struct dsc$descriptor_s **pvmscmd) 10402 { 10403 char * vmsspec; 10404 char * resspec; 10405 char image_name[NAM$C_MAXRSS+1]; 10406 char image_argv[NAM$C_MAXRSS+1]; 10407 $DESCRIPTOR(defdsc,".EXE"); 10408 $DESCRIPTOR(defdsc2,"."); 10409 struct dsc$descriptor_s resdsc; 10410 struct dsc$descriptor_s *vmscmd; 10411 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10412 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL; 10413 char *s, *rest, *cp, *wordbreak; 10414 char * cmd; 10415 int cmdlen; 10416 int isdcl; 10417 10418 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 10419 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10420 10421 /* vmsspec is a DCL command buffer, not just a filename */ 10422 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1); 10423 if (vmsspec == NULL) 10424 _ckvmssts_noperl(SS$_INSFMEM); 10425 10426 resspec = (char *)PerlMem_malloc(VMS_MAXRSS); 10427 if (resspec == NULL) 10428 _ckvmssts_noperl(SS$_INSFMEM); 10429 10430 /* Make a copy for modification */ 10431 cmdlen = strlen(incmd); 10432 cmd = (char *)PerlMem_malloc(cmdlen+1); 10433 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10434 my_strlcpy(cmd, incmd, cmdlen + 1); 10435 image_name[0] = 0; 10436 image_argv[0] = 0; 10437 10438 resdsc.dsc$a_pointer = resspec; 10439 resdsc.dsc$b_dtype = DSC$K_DTYPE_T; 10440 resdsc.dsc$b_class = DSC$K_CLASS_S; 10441 resdsc.dsc$w_length = VMS_MAXRSS - 1; 10442 10443 vmscmd->dsc$a_pointer = NULL; 10444 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T; 10445 vmscmd->dsc$b_class = DSC$K_CLASS_S; 10446 vmscmd->dsc$w_length = 0; 10447 if (pvmscmd) *pvmscmd = vmscmd; 10448 10449 if (suggest_quote) *suggest_quote = 0; 10450 10451 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) { 10452 PerlMem_free(cmd); 10453 PerlMem_free(vmsspec); 10454 PerlMem_free(resspec); 10455 return CLI$_BUFOVF; /* continuation lines currently unsupported */ 10456 } 10457 10458 s = cmd; 10459 10460 while (*s && isspace(*s)) s++; 10461 10462 if (*s == '@' || *s == '$') { 10463 vmsspec[0] = *s; rest = s + 1; 10464 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest; 10465 } 10466 else { cp = vmsspec; rest = s; } 10467 10468 /* If the first word is quoted, then we need to unquote it and 10469 * escape spaces within it. We'll expand into the resspec buffer, 10470 * then copy back into the cmd buffer, expanding the latter if 10471 * necessary. 10472 */ 10473 if (*rest == '"') { 10474 char *cp2; 10475 char *r = rest; 10476 bool in_quote = 0; 10477 int clen = cmdlen; 10478 int soff = s - cmd; 10479 10480 for (cp2 = resspec; 10481 *rest && cp2 - resspec < (VMS_MAXRSS - 1); 10482 rest++) { 10483 10484 if (*rest == ' ') { /* Escape ' ' to '^_'. */ 10485 *cp2 = '^'; 10486 *(++cp2) = '_'; 10487 cp2++; 10488 clen++; 10489 } 10490 else if (*rest == '"') { 10491 clen--; 10492 if (in_quote) { /* Must be closing quote. */ 10493 rest++; 10494 break; 10495 } 10496 in_quote = 1; 10497 } 10498 else { 10499 *cp2 = *rest; 10500 cp2++; 10501 } 10502 } 10503 *cp2 = '\0'; 10504 10505 /* Expand the command buffer if necessary. */ 10506 if (clen > cmdlen) { 10507 cmd = (char *)PerlMem_realloc(cmd, clen); 10508 if (cmd == NULL) 10509 _ckvmssts_noperl(SS$_INSFMEM); 10510 /* Where we are may have changed, so recompute offsets */ 10511 r = cmd + (r - s - soff); 10512 rest = cmd + (rest - s - soff); 10513 s = cmd + soff; 10514 } 10515 10516 /* Shift the non-verb portion of the command (if any) up or 10517 * down as necessary. 10518 */ 10519 if (*rest) 10520 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest); 10521 10522 /* Copy the unquoted and escaped command verb into place. */ 10523 memcpy(r, resspec, cp2 - resspec); 10524 cmd[clen] = '\0'; 10525 cmdlen = clen; 10526 rest = r; /* Rewind for subsequent operations. */ 10527 } 10528 10529 if (*rest == '.' || *rest == '/') { 10530 char *cp2; 10531 for (cp2 = resspec; 10532 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1); 10533 rest++, cp2++) *cp2 = *rest; 10534 *cp2 = '\0'; 10535 if (int_tovmsspec(resspec, cp, 0, NULL)) { 10536 s = vmsspec; 10537 10538 /* When a UNIX spec with no file type is translated to VMS, */ 10539 /* A trailing '.' is appended under ODS-5 rules. */ 10540 /* Here we do not want that trailing "." as it prevents */ 10541 /* Looking for a implied ".exe" type. */ 10542 if (decc_efs_charset) { 10543 int i; 10544 i = strlen(vmsspec); 10545 if (vmsspec[i-1] == '.') { 10546 vmsspec[i-1] = '\0'; 10547 } 10548 } 10549 10550 if (*rest) { 10551 for (cp2 = vmsspec + strlen(vmsspec); 10552 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH; 10553 rest++, cp2++) *cp2 = *rest; 10554 *cp2 = '\0'; 10555 } 10556 } 10557 } 10558 /* Intuit whether verb (first word of cmd) is a DCL command: 10559 * - if first nonspace char is '@', it's a DCL indirection 10560 * otherwise 10561 * - if verb contains a filespec separator, it's not a DCL command 10562 * - if it doesn't, caller tells us whether to default to a DCL 10563 * command, or to a local image unless told it's DCL (by leading '$') 10564 */ 10565 if (*s == '@') { 10566 isdcl = 1; 10567 if (suggest_quote) *suggest_quote = 1; 10568 } else { 10569 char *filespec = strpbrk(s,":<[.;"); 10570 rest = wordbreak = strpbrk(s," \"\t/"); 10571 if (!wordbreak) wordbreak = s + strlen(s); 10572 if (*s == '$') check_img = 0; 10573 if (filespec && (filespec < wordbreak)) isdcl = 0; 10574 else isdcl = !check_img; 10575 } 10576 10577 if (!isdcl) { 10578 int rsts; 10579 imgdsc.dsc$a_pointer = s; 10580 imgdsc.dsc$w_length = wordbreak - s; 10581 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 10582 if (!(retsts&1)) { 10583 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10584 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 10585 if (!(retsts & 1) && *s == '$') { 10586 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10587 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--; 10588 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 10589 if (!(retsts&1)) { 10590 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10591 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 10592 } 10593 } 10594 } 10595 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10596 10597 if (retsts & 1) { 10598 FILE *fp; 10599 s = resspec; 10600 while (*s && !isspace(*s)) s++; 10601 *s = '\0'; 10602 10603 /* check that it's really not DCL with no file extension */ 10604 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get"); 10605 if (fp) { 10606 char b[256] = {0,0,0,0}; 10607 read(fileno(fp), b, 256); 10608 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]); 10609 if (isdcl) { 10610 int shebang_len; 10611 10612 /* Check for script */ 10613 shebang_len = 0; 10614 if ((b[0] == '#') && (b[1] == '!')) 10615 shebang_len = 2; 10616 #ifdef ALTERNATE_SHEBANG 10617 else { 10618 shebang_len = strlen(ALTERNATE_SHEBANG); 10619 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) { 10620 char * perlstr; 10621 perlstr = strstr("perl",b); 10622 if (perlstr == NULL) 10623 shebang_len = 0; 10624 } 10625 else 10626 shebang_len = 0; 10627 } 10628 #endif 10629 10630 if (shebang_len > 0) { 10631 int i; 10632 int j; 10633 char tmpspec[NAM$C_MAXRSS + 1]; 10634 10635 i = shebang_len; 10636 /* Image is following after white space */ 10637 /*--------------------------------------*/ 10638 while (isprint(b[i]) && isspace(b[i])) 10639 i++; 10640 10641 j = 0; 10642 while (isprint(b[i]) && !isspace(b[i])) { 10643 tmpspec[j++] = b[i++]; 10644 if (j >= NAM$C_MAXRSS) 10645 break; 10646 } 10647 tmpspec[j] = '\0'; 10648 10649 /* There may be some default parameters to the image */ 10650 /*---------------------------------------------------*/ 10651 j = 0; 10652 while (isprint(b[i])) { 10653 image_argv[j++] = b[i++]; 10654 if (j >= NAM$C_MAXRSS) 10655 break; 10656 } 10657 while ((j > 0) && !isprint(image_argv[j-1])) 10658 j--; 10659 image_argv[j] = 0; 10660 10661 /* It will need to be converted to VMS format and validated */ 10662 if (tmpspec[0] != '\0') { 10663 char * iname; 10664 10665 /* Try to find the exact program requested to be run */ 10666 /*---------------------------------------------------*/ 10667 iname = int_rmsexpand 10668 (tmpspec, image_name, ".exe", 10669 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10670 if (iname != NULL) { 10671 if (cando_by_name_int 10672 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) { 10673 /* MCR prefix needed */ 10674 isdcl = 0; 10675 } 10676 else { 10677 /* Try again with a null type */ 10678 /*----------------------------*/ 10679 iname = int_rmsexpand 10680 (tmpspec, image_name, ".", 10681 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10682 if (iname != NULL) { 10683 if (cando_by_name_int 10684 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) { 10685 /* MCR prefix needed */ 10686 isdcl = 0; 10687 } 10688 } 10689 } 10690 10691 /* Did we find the image to run the script? */ 10692 /*------------------------------------------*/ 10693 if (isdcl) { 10694 char *tchr; 10695 10696 /* Assume DCL or foreign command exists */ 10697 /*--------------------------------------*/ 10698 tchr = strrchr(tmpspec, '/'); 10699 if (tchr != NULL) { 10700 tchr++; 10701 } 10702 else { 10703 tchr = tmpspec; 10704 } 10705 my_strlcpy(image_name, tchr, sizeof(image_name)); 10706 } 10707 } 10708 } 10709 } 10710 } 10711 fclose(fp); 10712 } 10713 if (check_img && isdcl) { 10714 PerlMem_free(cmd); 10715 PerlMem_free(resspec); 10716 PerlMem_free(vmsspec); 10717 return RMS$_FNF; 10718 } 10719 10720 if (cando_by_name(S_IXUSR,0,resspec)) { 10721 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH); 10722 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10723 if (!isdcl) { 10724 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH); 10725 if (image_name[0] != 0) { 10726 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH); 10727 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10728 } 10729 } else if (image_name[0] != 0) { 10730 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH); 10731 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10732 } else { 10733 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH); 10734 } 10735 if (suggest_quote) *suggest_quote = 1; 10736 10737 /* If there is an image name, use original command */ 10738 if (image_name[0] == 0) 10739 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH); 10740 else { 10741 rest = cmd; 10742 while (*rest && isspace(*rest)) rest++; 10743 } 10744 10745 if (image_argv[0] != 0) { 10746 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH); 10747 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10748 } 10749 if (rest) { 10750 int rest_len; 10751 int vmscmd_len; 10752 10753 rest_len = strlen(rest); 10754 vmscmd_len = strlen(vmscmd->dsc$a_pointer); 10755 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH) 10756 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH); 10757 else 10758 retsts = CLI$_BUFOVF; 10759 } 10760 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer); 10761 PerlMem_free(cmd); 10762 PerlMem_free(vmsspec); 10763 PerlMem_free(resspec); 10764 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10765 } 10766 else 10767 retsts = RMS$_PRV; 10768 } 10769 } 10770 /* It's either a DCL command or we couldn't find a suitable image */ 10771 vmscmd->dsc$w_length = strlen(cmd); 10772 10773 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1); 10774 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1); 10775 10776 PerlMem_free(cmd); 10777 PerlMem_free(resspec); 10778 PerlMem_free(vmsspec); 10779 10780 /* check if it's a symbol (for quoting purposes) */ 10781 if (suggest_quote && !*suggest_quote) { 10782 int iss; 10783 char equiv[LNM$C_NAMLENGTH]; 10784 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10785 eqvdsc.dsc$a_pointer = equiv; 10786 10787 iss = lib$get_symbol(vmscmd,&eqvdsc); 10788 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1; 10789 } 10790 if (!(retsts & 1)) { 10791 /* just hand off status values likely to be due to user error */ 10792 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV || 10793 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN || 10794 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts; 10795 else { _ckvmssts_noperl(retsts); } 10796 } 10797 10798 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10799 10800 } /* end of setup_cmddsc() */ 10801 10802 10803 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */ 10804 bool 10805 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp) 10806 { 10807 bool exec_sts; 10808 char * cmd; 10809 10810 if (sp > mark) { 10811 if (vfork_called) { /* this follows a vfork - act Unixish */ 10812 vfork_called--; 10813 if (vfork_called < 0) { 10814 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10815 vfork_called = 0; 10816 } 10817 else return do_aexec(really,mark,sp); 10818 } 10819 /* no vfork - act VMSish */ 10820 cmd = setup_argstr(aTHX_ really,mark,sp); 10821 exec_sts = vms_do_exec(cmd); 10822 Safefree(cmd); /* Clean up from setup_argstr() */ 10823 return exec_sts; 10824 } 10825 10826 return FALSE; 10827 } /* end of vms_do_aexec() */ 10828 /*}}}*/ 10829 10830 /* {{{bool vms_do_exec(char *cmd) */ 10831 bool 10832 Perl_vms_do_exec(pTHX_ const char *cmd) 10833 { 10834 struct dsc$descriptor_s *vmscmd; 10835 10836 if (vfork_called) { /* this follows a vfork - act Unixish */ 10837 vfork_called--; 10838 if (vfork_called < 0) { 10839 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10840 vfork_called = 0; 10841 } 10842 else return do_exec(cmd); 10843 } 10844 10845 { /* no vfork - act VMSish */ 10846 unsigned long int retsts; 10847 10848 TAINT_ENV(); 10849 TAINT_PROPER("exec"); 10850 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1) 10851 retsts = lib$do_command(vmscmd); 10852 10853 switch (retsts) { 10854 case RMS$_FNF: case RMS$_DNF: 10855 set_errno(ENOENT); break; 10856 case RMS$_DIR: 10857 set_errno(ENOTDIR); break; 10858 case RMS$_DEV: 10859 set_errno(ENODEV); break; 10860 case RMS$_PRV: 10861 set_errno(EACCES); break; 10862 case RMS$_SYN: 10863 set_errno(EINVAL); break; 10864 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 10865 set_errno(E2BIG); break; 10866 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 10867 _ckvmssts_noperl(retsts); /* fall through */ 10868 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 10869 set_errno(EVMSERR); 10870 } 10871 set_vaxc_errno(retsts); 10872 if (ckWARN(WARN_EXEC)) { 10873 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s", 10874 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno)); 10875 } 10876 vms_execfree(vmscmd); 10877 } 10878 10879 return FALSE; 10880 10881 } /* end of vms_do_exec() */ 10882 /*}}}*/ 10883 10884 int do_spawn2(pTHX_ const char *, int); 10885 10886 int 10887 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp) 10888 { 10889 unsigned long int sts; 10890 char * cmd; 10891 int flags = 0; 10892 10893 if (sp > mark) { 10894 10895 /* We'll copy the (undocumented?) Win32 behavior and allow a 10896 * numeric first argument. But the only value we'll support 10897 * through do_aspawn is a value of 1, which means spawn without 10898 * waiting for completion -- other values are ignored. 10899 */ 10900 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) { 10901 ++mark; 10902 flags = SvIVx(*mark); 10903 } 10904 10905 if (flags && flags == 1) /* the Win32 P_NOWAIT value */ 10906 flags = CLI$M_NOWAIT; 10907 else 10908 flags = 0; 10909 10910 cmd = setup_argstr(aTHX_ really, mark, sp); 10911 sts = do_spawn2(aTHX_ cmd, flags); 10912 /* pp_sys will clean up cmd */ 10913 return sts; 10914 } 10915 return SS$_ABORT; 10916 } /* end of do_aspawn() */ 10917 /*}}}*/ 10918 10919 10920 /* {{{int do_spawn(char* cmd) */ 10921 int 10922 Perl_do_spawn(pTHX_ char* cmd) 10923 { 10924 PERL_ARGS_ASSERT_DO_SPAWN; 10925 10926 return do_spawn2(aTHX_ cmd, 0); 10927 } 10928 /*}}}*/ 10929 10930 /* {{{int do_spawn_nowait(char* cmd) */ 10931 int 10932 Perl_do_spawn_nowait(pTHX_ char* cmd) 10933 { 10934 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT; 10935 10936 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT); 10937 } 10938 /*}}}*/ 10939 10940 /* {{{int do_spawn2(char *cmd) */ 10941 int 10942 do_spawn2(pTHX_ const char *cmd, int flags) 10943 { 10944 unsigned long int sts, substs; 10945 10946 /* The caller of this routine expects to Safefree(PL_Cmd) */ 10947 Newx(PL_Cmd,10,char); 10948 10949 TAINT_ENV(); 10950 TAINT_PROPER("spawn"); 10951 if (!cmd || !*cmd) { 10952 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0); 10953 if (!(sts & 1)) { 10954 switch (sts) { 10955 case RMS$_FNF: case RMS$_DNF: 10956 set_errno(ENOENT); break; 10957 case RMS$_DIR: 10958 set_errno(ENOTDIR); break; 10959 case RMS$_DEV: 10960 set_errno(ENODEV); break; 10961 case RMS$_PRV: 10962 set_errno(EACCES); break; 10963 case RMS$_SYN: 10964 set_errno(EINVAL); break; 10965 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 10966 set_errno(E2BIG); break; 10967 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 10968 _ckvmssts_noperl(sts); /* fall through */ 10969 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 10970 set_errno(EVMSERR); 10971 } 10972 set_vaxc_errno(sts); 10973 if (ckWARN(WARN_EXEC)) { 10974 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s", 10975 Strerror(errno)); 10976 } 10977 } 10978 sts = substs; 10979 } 10980 else { 10981 char mode[3]; 10982 PerlIO * fp; 10983 if (flags & CLI$M_NOWAIT) 10984 strcpy(mode, "n"); 10985 else 10986 strcpy(mode, "nW"); 10987 10988 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts); 10989 if (fp != NULL) 10990 my_pclose(fp); 10991 /* sts will be the pid in the nowait case */ 10992 } 10993 return sts; 10994 } /* end of do_spawn2() */ 10995 /*}}}*/ 10996 10997 10998 static unsigned int *sockflags, sockflagsize; 10999 11000 /* 11001 * Shim fdopen to identify sockets for my_fwrite later, since the stdio 11002 * routines found in some versions of the CRTL can't deal with sockets. 11003 * We don't shim the other file open routines since a socket isn't 11004 * likely to be opened by a name. 11005 */ 11006 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/ 11007 FILE *my_fdopen(int fd, const char *mode) 11008 { 11009 FILE *fp = fdopen(fd, mode); 11010 11011 if (fp) { 11012 unsigned int fdoff = fd / sizeof(unsigned int); 11013 Stat_t sbuf; /* native stat; we don't need flex_stat */ 11014 if (!sockflagsize || fdoff > sockflagsize) { 11015 if (sockflags) Renew( sockflags,fdoff+2,unsigned int); 11016 else Newx (sockflags,fdoff+2,unsigned int); 11017 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize); 11018 sockflagsize = fdoff + 2; 11019 } 11020 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode)) 11021 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int)); 11022 } 11023 return fp; 11024 11025 } 11026 /*}}}*/ 11027 11028 11029 /* 11030 * Clear the corresponding bit when the (possibly) socket stream is closed. 11031 * There still a small hole: we miss an implicit close which might occur 11032 * via freopen(). >> Todo 11033 */ 11034 /*{{{ int my_fclose(FILE *fp)*/ 11035 int my_fclose(FILE *fp) { 11036 if (fp) { 11037 unsigned int fd = fileno(fp); 11038 unsigned int fdoff = fd / sizeof(unsigned int); 11039 11040 if (sockflagsize && fdoff < sockflagsize) 11041 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int)); 11042 } 11043 return fclose(fp); 11044 } 11045 /*}}}*/ 11046 11047 11048 /* 11049 * A simple fwrite replacement which outputs itmsz*nitm chars without 11050 * introducing record boundaries every itmsz chars. 11051 * We are using fputs, which depends on a terminating null. We may 11052 * well be writing binary data, so we need to accommodate not only 11053 * data with nulls sprinkled in the middle but also data with no null 11054 * byte at the end. 11055 */ 11056 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/ 11057 int 11058 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest) 11059 { 11060 char *cp, *end, *cpd; 11061 char *data; 11062 unsigned int fd = fileno(dest); 11063 unsigned int fdoff = fd / sizeof(unsigned int); 11064 int retval; 11065 int bufsize = itmsz * nitm + 1; 11066 11067 if (fdoff < sockflagsize && 11068 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) { 11069 if (write(fd, src, itmsz * nitm) == EOF) return EOF; 11070 return nitm; 11071 } 11072 11073 _ckvmssts_noperl(lib$get_vm(&bufsize, &data)); 11074 memcpy( data, src, itmsz*nitm ); 11075 data[itmsz*nitm] = '\0'; 11076 11077 end = data + itmsz * nitm; 11078 retval = (int) nitm; /* on success return # items written */ 11079 11080 cpd = data; 11081 while (cpd <= end) { 11082 for (cp = cpd; cp <= end; cp++) if (!*cp) break; 11083 if (fputs(cpd,dest) == EOF) { retval = EOF; break; } 11084 if (cp < end) 11085 if (fputc('\0',dest) == EOF) { retval = EOF; break; } 11086 cpd = cp + 1; 11087 } 11088 11089 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data)); 11090 return retval; 11091 11092 } /* end of my_fwrite() */ 11093 /*}}}*/ 11094 11095 /*{{{ int my_flush(FILE *fp)*/ 11096 int 11097 Perl_my_flush(pTHX_ FILE *fp) 11098 { 11099 int res; 11100 if ((res = fflush(fp)) == 0 && fp) { 11101 #ifdef VMS_DO_SOCKETS 11102 Stat_t s; 11103 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode)) 11104 #endif 11105 res = fsync(fileno(fp)); 11106 } 11107 /* 11108 * If the flush succeeded but set end-of-file, we need to clear 11109 * the error because our caller may check ferror(). BTW, this 11110 * probably means we just flushed an empty file. 11111 */ 11112 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp); 11113 11114 return res; 11115 } 11116 /*}}}*/ 11117 11118 /* fgetname() is not returning the correct file specifications when 11119 * decc_filename_unix_report mode is active. So we have to have it 11120 * aways return filenames in VMS mode and convert it ourselves. 11121 */ 11122 11123 /*{{{ char * my_fgetname(FILE *fp, buf)*/ 11124 char * 11125 Perl_my_fgetname(FILE *fp, char * buf) { 11126 char * retname; 11127 char * vms_name; 11128 11129 retname = fgetname(fp, buf, 1); 11130 11131 /* If we are in VMS mode, then we are done */ 11132 if (!decc_filename_unix_report || (retname == NULL)) { 11133 return retname; 11134 } 11135 11136 /* Convert this to Unix format */ 11137 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS); 11138 my_strlcpy(vms_name, retname, VMS_MAXRSS); 11139 retname = int_tounixspec(vms_name, buf, NULL); 11140 PerlMem_free(vms_name); 11141 11142 return retname; 11143 } 11144 /*}}}*/ 11145 11146 /* 11147 * Here are replacements for the following Unix routines in the VMS environment: 11148 * getpwuid Get information for a particular UIC or UID 11149 * getpwnam Get information for a named user 11150 * getpwent Get information for each user in the rights database 11151 * setpwent Reset search to the start of the rights database 11152 * endpwent Finish searching for users in the rights database 11153 * 11154 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure 11155 * (defined in pwd.h), which contains the following fields:- 11156 * struct passwd { 11157 * char *pw_name; Username (in lower case) 11158 * char *pw_passwd; Hashed password 11159 * unsigned int pw_uid; UIC 11160 * unsigned int pw_gid; UIC group number 11161 * char *pw_unixdir; Default device/directory (VMS-style) 11162 * char *pw_gecos; Owner name 11163 * char *pw_dir; Default device/directory (Unix-style) 11164 * char *pw_shell; Default CLI name (eg. DCL) 11165 * }; 11166 * If the specified user does not exist, getpwuid and getpwnam return NULL. 11167 * 11168 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid). 11169 * not the UIC member number (eg. what's returned by getuid()), 11170 * getpwuid() can accept either as input (if uid is specified, the caller's 11171 * UIC group is used), though it won't recognise gid=0. 11172 * 11173 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return 11174 * information about other users in your group or in other groups, respectively. 11175 * If the required privilege is not available, then these routines fill only 11176 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty 11177 * string). 11178 * 11179 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995. 11180 */ 11181 11182 /* sizes of various UAF record fields */ 11183 #define UAI$S_USERNAME 12 11184 #define UAI$S_IDENT 31 11185 #define UAI$S_OWNER 31 11186 #define UAI$S_DEFDEV 31 11187 #define UAI$S_DEFDIR 63 11188 #define UAI$S_DEFCLI 31 11189 #define UAI$S_PWD 8 11190 11191 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \ 11192 (uic).uic$v_member != UIC$K_WILD_MEMBER && \ 11193 (uic).uic$v_group != UIC$K_WILD_GROUP) 11194 11195 static char __empty[]= ""; 11196 static struct passwd __passwd_empty= 11197 {(char *) __empty, (char *) __empty, 0, 0, 11198 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty}; 11199 static int contxt= 0; 11200 static struct passwd __pwdcache; 11201 static char __pw_namecache[UAI$S_IDENT+1]; 11202 11203 /* 11204 * This routine does most of the work extracting the user information. 11205 */ 11206 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd) 11207 { 11208 static struct { 11209 unsigned char length; 11210 char pw_gecos[UAI$S_OWNER+1]; 11211 } owner; 11212 static union uicdef uic; 11213 static struct { 11214 unsigned char length; 11215 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1]; 11216 } defdev; 11217 static struct { 11218 unsigned char length; 11219 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1]; 11220 } defdir; 11221 static struct { 11222 unsigned char length; 11223 char pw_shell[UAI$S_DEFCLI+1]; 11224 } defcli; 11225 static char pw_passwd[UAI$S_PWD+1]; 11226 11227 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd; 11228 struct dsc$descriptor_s name_desc; 11229 unsigned long int sts; 11230 11231 static struct itmlst_3 itmlst[]= { 11232 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner}, 11233 {sizeof(uic), UAI$_UIC, &uic, &luic}, 11234 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev}, 11235 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir}, 11236 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli}, 11237 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd}, 11238 {0, 0, NULL, NULL}}; 11239 11240 name_desc.dsc$w_length= strlen(name); 11241 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 11242 name_desc.dsc$b_class= DSC$K_CLASS_S; 11243 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */ 11244 11245 /* Note that sys$getuai returns many fields as counted strings. */ 11246 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0); 11247 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) { 11248 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES); 11249 } 11250 else { _ckvmssts(sts); } 11251 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */ 11252 11253 if ((int) owner.length < lowner) lowner= (int) owner.length; 11254 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length; 11255 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length; 11256 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length; 11257 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir); 11258 owner.pw_gecos[lowner]= '\0'; 11259 defdev.pw_dir[ldefdev+ldefdir]= '\0'; 11260 defcli.pw_shell[ldefcli]= '\0'; 11261 if (valid_uic(uic)) { 11262 pwd->pw_uid= uic.uic$l_uic; 11263 pwd->pw_gid= uic.uic$v_group; 11264 } 11265 else 11266 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\""); 11267 pwd->pw_passwd= pw_passwd; 11268 pwd->pw_gecos= owner.pw_gecos; 11269 pwd->pw_dir= defdev.pw_dir; 11270 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL); 11271 pwd->pw_shell= defcli.pw_shell; 11272 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) { 11273 int ldir; 11274 ldir= strlen(pwd->pw_unixdir) - 1; 11275 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0'; 11276 } 11277 else 11278 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir)); 11279 if (!decc_efs_case_preserve) 11280 __mystrtolower(pwd->pw_unixdir); 11281 return 1; 11282 } 11283 11284 /* 11285 * Get information for a named user. 11286 */ 11287 /*{{{struct passwd *getpwnam(char *name)*/ 11288 struct passwd *Perl_my_getpwnam(pTHX_ const char *name) 11289 { 11290 struct dsc$descriptor_s name_desc; 11291 union uicdef uic; 11292 unsigned long int sts; 11293 11294 __pwdcache = __passwd_empty; 11295 if (!fillpasswd(aTHX_ name, &__pwdcache)) { 11296 /* We still may be able to determine pw_uid and pw_gid */ 11297 name_desc.dsc$w_length= strlen(name); 11298 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 11299 name_desc.dsc$b_class= DSC$K_CLASS_S; 11300 name_desc.dsc$a_pointer= (char *) name; 11301 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) { 11302 __pwdcache.pw_uid= uic.uic$l_uic; 11303 __pwdcache.pw_gid= uic.uic$v_group; 11304 } 11305 else { 11306 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) { 11307 set_vaxc_errno(sts); 11308 set_errno(sts == RMS$_PRV ? EACCES : EINVAL); 11309 return NULL; 11310 } 11311 else { _ckvmssts(sts); } 11312 } 11313 } 11314 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache)); 11315 __pwdcache.pw_name= __pw_namecache; 11316 return &__pwdcache; 11317 } /* end of my_getpwnam() */ 11318 /*}}}*/ 11319 11320 /* 11321 * Get information for a particular UIC or UID. 11322 * Called by my_getpwent with uid=-1 to list all users. 11323 */ 11324 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/ 11325 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid) 11326 { 11327 const $DESCRIPTOR(name_desc,__pw_namecache); 11328 unsigned short lname; 11329 union uicdef uic; 11330 unsigned long int status; 11331 11332 if (uid == (unsigned int) -1) { 11333 do { 11334 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt); 11335 if (status == SS$_NOSUCHID || status == RMS$_PRV) { 11336 set_vaxc_errno(status); 11337 set_errno(status == RMS$_PRV ? EACCES : EINVAL); 11338 my_endpwent(); 11339 return NULL; 11340 } 11341 else { _ckvmssts(status); } 11342 } while (!valid_uic (uic)); 11343 } 11344 else { 11345 uic.uic$l_uic= uid; 11346 if (!uic.uic$v_group) 11347 uic.uic$v_group= PerlProc_getgid(); 11348 if (valid_uic(uic)) 11349 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0); 11350 else status = SS$_IVIDENT; 11351 if (status == SS$_IVIDENT || status == SS$_NOSUCHID || 11352 status == RMS$_PRV) { 11353 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL); 11354 return NULL; 11355 } 11356 else { _ckvmssts(status); } 11357 } 11358 __pw_namecache[lname]= '\0'; 11359 __mystrtolower(__pw_namecache); 11360 11361 __pwdcache = __passwd_empty; 11362 __pwdcache.pw_name = __pw_namecache; 11363 11364 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege). 11365 The identifier's value is usually the UIC, but it doesn't have to be, 11366 so if we can, we let fillpasswd update this. */ 11367 __pwdcache.pw_uid = uic.uic$l_uic; 11368 __pwdcache.pw_gid = uic.uic$v_group; 11369 11370 fillpasswd(aTHX_ __pw_namecache, &__pwdcache); 11371 return &__pwdcache; 11372 11373 } /* end of my_getpwuid() */ 11374 /*}}}*/ 11375 11376 /* 11377 * Get information for next user. 11378 */ 11379 /*{{{struct passwd *my_getpwent()*/ 11380 struct passwd *Perl_my_getpwent(pTHX) 11381 { 11382 return (my_getpwuid((unsigned int) -1)); 11383 } 11384 /*}}}*/ 11385 11386 /* 11387 * Finish searching rights database for users. 11388 */ 11389 /*{{{void my_endpwent()*/ 11390 void Perl_my_endpwent(pTHX) 11391 { 11392 if (contxt) { 11393 _ckvmssts(sys$finish_rdb(&contxt)); 11394 contxt= 0; 11395 } 11396 } 11397 /*}}}*/ 11398 11399 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(), 11400 * my_utime(), and flex_stat(), all of which operate on UTC unless 11401 * VMSISH_TIMES is true. 11402 */ 11403 /* method used to handle UTC conversions: 11404 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction 11405 */ 11406 static int gmtime_emulation_type; 11407 /* number of secs to add to UTC POSIX-style time to get local time */ 11408 static long int utc_offset_secs; 11409 11410 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc. 11411 * in vmsish.h. #undef them here so we can call the CRTL routines 11412 * directly. 11413 */ 11414 #undef gmtime 11415 #undef localtime 11416 #undef time 11417 11418 11419 static time_t toutc_dst(time_t loc) { 11420 struct tm *rsltmp; 11421 11422 if ((rsltmp = localtime(&loc)) == NULL) return -1u; 11423 loc -= utc_offset_secs; 11424 if (rsltmp->tm_isdst) loc -= 3600; 11425 return loc; 11426 } 11427 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 11428 ((gmtime_emulation_type || my_time(NULL)), \ 11429 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \ 11430 ((secs) - utc_offset_secs)))) 11431 11432 static time_t toloc_dst(time_t utc) { 11433 struct tm *rsltmp; 11434 11435 utc += utc_offset_secs; 11436 if ((rsltmp = localtime(&utc)) == NULL) return -1u; 11437 if (rsltmp->tm_isdst) utc += 3600; 11438 return utc; 11439 } 11440 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 11441 ((gmtime_emulation_type || my_time(NULL)), \ 11442 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \ 11443 ((secs) + utc_offset_secs)))) 11444 11445 /* my_time(), my_localtime(), my_gmtime() 11446 * By default traffic in UTC time values, using CRTL gmtime() or 11447 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone. 11448 * Note: We need to use these functions even when the CRTL has working 11449 * UTC support, since they also handle C<use vmsish qw(times);> 11450 * 11451 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu> 11452 * Modified by Charles Bailey <bailey@newman.upenn.edu> 11453 */ 11454 11455 /*{{{time_t my_time(time_t *timep)*/ 11456 time_t Perl_my_time(pTHX_ time_t *timep) 11457 { 11458 time_t when; 11459 struct tm *tm_p; 11460 11461 if (gmtime_emulation_type == 0) { 11462 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */ 11463 /* results of calls to gmtime() and localtime() */ 11464 /* for same &base */ 11465 11466 gmtime_emulation_type++; 11467 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */ 11468 char off[LNM$C_NAMLENGTH+1];; 11469 11470 gmtime_emulation_type++; 11471 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) { 11472 gmtime_emulation_type++; 11473 utc_offset_secs = 0; 11474 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC"); 11475 } 11476 else { utc_offset_secs = atol(off); } 11477 } 11478 else { /* We've got a working gmtime() */ 11479 struct tm gmt, local; 11480 11481 gmt = *tm_p; 11482 tm_p = localtime(&base); 11483 local = *tm_p; 11484 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400; 11485 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600; 11486 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60; 11487 utc_offset_secs += (local.tm_sec - gmt.tm_sec); 11488 } 11489 } 11490 11491 when = time(NULL); 11492 # ifdef VMSISH_TIME 11493 if (VMSISH_TIME) when = _toloc(when); 11494 # endif 11495 if (timep != NULL) *timep = when; 11496 return when; 11497 11498 } /* end of my_time() */ 11499 /*}}}*/ 11500 11501 11502 /*{{{struct tm *my_gmtime(const time_t *timep)*/ 11503 struct tm * 11504 Perl_my_gmtime(pTHX_ const time_t *timep) 11505 { 11506 time_t when; 11507 struct tm *rsltmp; 11508 11509 if (timep == NULL) { 11510 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11511 return NULL; 11512 } 11513 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11514 11515 when = *timep; 11516 # ifdef VMSISH_TIME 11517 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */ 11518 # endif 11519 return gmtime(&when); 11520 } /* end of my_gmtime() */ 11521 /*}}}*/ 11522 11523 11524 /*{{{struct tm *my_localtime(const time_t *timep)*/ 11525 struct tm * 11526 Perl_my_localtime(pTHX_ const time_t *timep) 11527 { 11528 time_t when; 11529 11530 if (timep == NULL) { 11531 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11532 return NULL; 11533 } 11534 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11535 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */ 11536 11537 when = *timep; 11538 # ifdef VMSISH_TIME 11539 if (VMSISH_TIME) when = _toutc(when); 11540 # endif 11541 /* CRTL localtime() wants UTC as input, does tz correction itself */ 11542 return localtime(&when); 11543 } /* end of my_localtime() */ 11544 /*}}}*/ 11545 11546 /* Reset definitions for later calls */ 11547 #define gmtime(t) my_gmtime(t) 11548 #define localtime(t) my_localtime(t) 11549 #define time(t) my_time(t) 11550 11551 11552 /* my_utime - update modification/access time of a file 11553 * 11554 * VMS 7.3 and later implementation 11555 * Only the UTC translation is home-grown. The rest is handled by the 11556 * CRTL utime(), which will take into account the relevant feature 11557 * logicals and ODS-5 volume characteristics for true access times. 11558 * 11559 * pre VMS 7.3 implementation: 11560 * The calling sequence is identical to POSIX utime(), but under 11561 * VMS with ODS-2, only the modification time is changed; ODS-2 does 11562 * not maintain access times. Restrictions differ from the POSIX 11563 * definition in that the time can be changed as long as the 11564 * caller has permission to execute the necessary IO$_MODIFY $QIO; 11565 * no separate checks are made to insure that the caller is the 11566 * owner of the file or has special privs enabled. 11567 * Code here is based on Joe Meadows' FILE utility. 11568 * 11569 */ 11570 11571 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00) 11572 * to VMS epoch (01-JAN-1858 00:00:00.00) 11573 * in 100 ns intervals. 11574 */ 11575 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 }; 11576 11577 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/ 11578 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes) 11579 { 11580 #if __CRTL_VER >= 70300000 11581 struct utimbuf utc_utimes, *utc_utimesp; 11582 11583 if (utimes != NULL) { 11584 utc_utimes.actime = utimes->actime; 11585 utc_utimes.modtime = utimes->modtime; 11586 # ifdef VMSISH_TIME 11587 /* If input was local; convert to UTC for sys svc */ 11588 if (VMSISH_TIME) { 11589 utc_utimes.actime = _toutc(utimes->actime); 11590 utc_utimes.modtime = _toutc(utimes->modtime); 11591 } 11592 # endif 11593 utc_utimesp = &utc_utimes; 11594 } 11595 else { 11596 utc_utimesp = NULL; 11597 } 11598 11599 return utime(file, utc_utimesp); 11600 11601 #else /* __CRTL_VER < 70300000 */ 11602 11603 int i; 11604 int sts; 11605 long int bintime[2], len = 2, lowbit, unixtime, 11606 secscale = 10000000; /* seconds --> 100 ns intervals */ 11607 unsigned long int chan, iosb[2], retsts; 11608 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS]; 11609 struct FAB myfab = cc$rms_fab; 11610 struct NAM mynam = cc$rms_nam; 11611 #if defined (__DECC) && defined (__VAX) 11612 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr, 11613 * at least through VMS V6.1, which causes a type-conversion warning. 11614 */ 11615 # pragma message save 11616 # pragma message disable cvtdiftypes 11617 #endif 11618 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}}; 11619 struct fibdef myfib; 11620 #if defined (__DECC) && defined (__VAX) 11621 /* This should be right after the declaration of myatr, but due 11622 * to a bug in VAX DEC C, this takes effect a statement early. 11623 */ 11624 # pragma message restore 11625 #endif 11626 /* cast ok for read only parameter */ 11627 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib}, 11628 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0}, 11629 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0}; 11630 11631 if (file == NULL || *file == '\0') { 11632 SETERRNO(ENOENT, LIB$_INVARG); 11633 return -1; 11634 } 11635 11636 /* Convert to VMS format ensuring that it will fit in 255 characters */ 11637 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) { 11638 SETERRNO(ENOENT, LIB$_INVARG); 11639 return -1; 11640 } 11641 if (utimes != NULL) { 11642 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00) 11643 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00). 11644 * Since time_t is unsigned long int, and lib$emul takes a signed long int 11645 * as input, we force the sign bit to be clear by shifting unixtime right 11646 * one bit, then multiplying by an extra factor of 2 in lib$emul(). 11647 */ 11648 lowbit = (utimes->modtime & 1) ? secscale : 0; 11649 unixtime = (long int) utimes->modtime; 11650 # ifdef VMSISH_TIME 11651 /* If input was UTC; convert to local for sys svc */ 11652 if (!VMSISH_TIME) unixtime = _toloc(unixtime); 11653 # endif 11654 unixtime >>= 1; secscale <<= 1; 11655 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime); 11656 if (!(retsts & 1)) { 11657 SETERRNO(EVMSERR, retsts); 11658 return -1; 11659 } 11660 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len); 11661 if (!(retsts & 1)) { 11662 SETERRNO(EVMSERR, retsts); 11663 return -1; 11664 } 11665 } 11666 else { 11667 /* Just get the current time in VMS format directly */ 11668 retsts = sys$gettim(bintime); 11669 if (!(retsts & 1)) { 11670 SETERRNO(EVMSERR, retsts); 11671 return -1; 11672 } 11673 } 11674 11675 myfab.fab$l_fna = vmsspec; 11676 myfab.fab$b_fns = (unsigned char) strlen(vmsspec); 11677 myfab.fab$l_nam = &mynam; 11678 mynam.nam$l_esa = esa; 11679 mynam.nam$b_ess = (unsigned char) sizeof esa; 11680 mynam.nam$l_rsa = rsa; 11681 mynam.nam$b_rss = (unsigned char) sizeof rsa; 11682 if (decc_efs_case_preserve) 11683 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE; 11684 11685 /* Look for the file to be affected, letting RMS parse the file 11686 * specification for us as well. I have set errno using only 11687 * values documented in the utime() man page for VMS POSIX. 11688 */ 11689 retsts = sys$parse(&myfab,0,0); 11690 if (!(retsts & 1)) { 11691 set_vaxc_errno(retsts); 11692 if (retsts == RMS$_PRV) set_errno(EACCES); 11693 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 11694 else set_errno(EVMSERR); 11695 return -1; 11696 } 11697 retsts = sys$search(&myfab,0,0); 11698 if (!(retsts & 1)) { 11699 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11700 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11701 set_vaxc_errno(retsts); 11702 if (retsts == RMS$_PRV) set_errno(EACCES); 11703 else if (retsts == RMS$_FNF) set_errno(ENOENT); 11704 else set_errno(EVMSERR); 11705 return -1; 11706 } 11707 11708 devdsc.dsc$w_length = mynam.nam$b_dev; 11709 /* cast ok for read only parameter */ 11710 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev; 11711 11712 retsts = sys$assign(&devdsc,&chan,0,0); 11713 if (!(retsts & 1)) { 11714 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11715 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11716 set_vaxc_errno(retsts); 11717 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR); 11718 else if (retsts == SS$_NOPRIV) set_errno(EACCES); 11719 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR); 11720 else set_errno(EVMSERR); 11721 return -1; 11722 } 11723 11724 fnmdsc.dsc$a_pointer = mynam.nam$l_name; 11725 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver; 11726 11727 memset((void *) &myfib, 0, sizeof myfib); 11728 #if defined(__DECC) || defined(__DECCXX) 11729 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i]; 11730 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i]; 11731 /* This prevents the revision time of the file being reset to the current 11732 * time as a result of our IO$_MODIFY $QIO. */ 11733 myfib.fib$l_acctl = FIB$M_NORECORD; 11734 #else 11735 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i]; 11736 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i]; 11737 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD; 11738 #endif 11739 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0); 11740 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11741 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11742 _ckvmssts(sys$dassgn(chan)); 11743 if (retsts & 1) retsts = iosb[0]; 11744 if (!(retsts & 1)) { 11745 set_vaxc_errno(retsts); 11746 if (retsts == SS$_NOPRIV) set_errno(EACCES); 11747 else set_errno(EVMSERR); 11748 return -1; 11749 } 11750 11751 return 0; 11752 11753 #endif /* #if __CRTL_VER >= 70300000 */ 11754 11755 } /* end of my_utime() */ 11756 /*}}}*/ 11757 11758 /* 11759 * flex_stat, flex_lstat, flex_fstat 11760 * basic stat, but gets it right when asked to stat 11761 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3) 11762 */ 11763 11764 #ifndef _USE_STD_STAT 11765 /* encode_dev packs a VMS device name string into an integer to allow 11766 * simple comparisons. This can be used, for example, to check whether two 11767 * files are located on the same device, by comparing their encoded device 11768 * names. Even a string comparison would not do, because stat() reuses the 11769 * device name buffer for each call; so without encode_dev, it would be 11770 * necessary to save the buffer and use strcmp (this would mean a number of 11771 * changes to the standard Perl code, to say nothing of what a Perl script 11772 * would have to do. 11773 * 11774 * The device lock id, if it exists, should be unique (unless perhaps compared 11775 * with lock ids transferred from other nodes). We have a lock id if the disk is 11776 * mounted cluster-wide, which is when we tend to get long (host-qualified) 11777 * device names. Thus we use the lock id in preference, and only if that isn't 11778 * available, do we try to pack the device name into an integer (flagged by 11779 * the sign bit (LOCKID_MASK) being set). 11780 * 11781 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device 11782 * name and its encoded form, but it seems very unlikely that we will find 11783 * two files on different disks that share the same encoded device names, 11784 * and even more remote that they will share the same file id (if the test 11785 * is to check for the same file). 11786 * 11787 * A better method might be to use sys$device_scan on the first call, and to 11788 * search for the device, returning an index into the cached array. 11789 * The number returned would be more intelligible. 11790 * This is probably not worth it, and anyway would take quite a bit longer 11791 * on the first call. 11792 */ 11793 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */ 11794 static mydev_t encode_dev (pTHX_ const char *dev) 11795 { 11796 int i; 11797 unsigned long int f; 11798 mydev_t enc; 11799 char c; 11800 const char *q; 11801 11802 if (!dev || !dev[0]) return 0; 11803 11804 #if LOCKID_MASK 11805 { 11806 struct dsc$descriptor_s dev_desc; 11807 unsigned long int status, lockid = 0, item = DVI$_LOCKID; 11808 11809 /* For cluster-mounted disks, the disk lock identifier is unique, so we 11810 can try that first. */ 11811 dev_desc.dsc$w_length = strlen (dev); 11812 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T; 11813 dev_desc.dsc$b_class = DSC$K_CLASS_S; 11814 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */ 11815 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0); 11816 if (!$VMS_STATUS_SUCCESS(status)) { 11817 switch (status) { 11818 case SS$_NOSUCHDEV: 11819 SETERRNO(ENODEV, status); 11820 return 0; 11821 default: 11822 _ckvmssts(status); 11823 } 11824 } 11825 if (lockid) return (lockid & ~LOCKID_MASK); 11826 } 11827 #endif 11828 11829 /* Otherwise we try to encode the device name */ 11830 enc = 0; 11831 f = 1; 11832 i = 0; 11833 for (q = dev + strlen(dev); q--; q >= dev) { 11834 if (*q == ':') 11835 break; 11836 if (isdigit (*q)) 11837 c= (*q) - '0'; 11838 else if (isalpha (toupper (*q))) 11839 c= toupper (*q) - 'A' + (char)10; 11840 else 11841 continue; /* Skip '$'s */ 11842 i++; 11843 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */ 11844 if (i>1) f *= 36; 11845 enc += f * (unsigned long int) c; 11846 } 11847 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */ 11848 11849 } /* end of encode_dev() */ 11850 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11851 device_no = encode_dev(aTHX_ devname) 11852 #else 11853 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11854 device_no = new_dev_no 11855 #endif 11856 11857 static int 11858 is_null_device(const char *name) 11859 { 11860 if (decc_bug_devnull != 0) { 11861 if (strncmp("/dev/null", name, 9) == 0) 11862 return 1; 11863 } 11864 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:". 11865 The underscore prefix, controller letter, and unit number are 11866 independently optional; for our purposes, the colon punctuation 11867 is not. The colon can be trailed by optional directory and/or 11868 filename, but two consecutive colons indicates a nodename rather 11869 than a device. [pr] */ 11870 if (*name == '_') ++name; 11871 if (tolower(*name++) != 'n') return 0; 11872 if (tolower(*name++) != 'l') return 0; 11873 if (tolower(*name) == 'a') ++name; 11874 if (*name == '0') ++name; 11875 return (*name++ == ':') && (*name != ':'); 11876 } 11877 11878 static int 11879 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag); 11880 11881 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c) 11882 11883 static I32 11884 Perl_cando_by_name_int 11885 (pTHX_ I32 bit, bool effective, const char *fname, int opts) 11886 { 11887 char usrname[L_cuserid]; 11888 struct dsc$descriptor_s usrdsc = 11889 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname}; 11890 char *vmsname = NULL, *fileified = NULL; 11891 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags; 11892 unsigned short int retlen, trnlnm_iter_count; 11893 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11894 union prvdef curprv; 11895 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen}, 11896 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen}, 11897 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}}; 11898 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen}, 11899 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length}, 11900 {0,0,0,0}}; 11901 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen}, 11902 {0,0,0,0}}; 11903 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11904 Stat_t st; 11905 static int profile_context = -1; 11906 11907 if (!fname || !*fname) return FALSE; 11908 11909 /* Make sure we expand logical names, since sys$check_access doesn't */ 11910 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 11911 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11912 if (!strpbrk(fname,"/]>:")) { 11913 my_strlcpy(fileified, fname, VMS_MAXRSS); 11914 trnlnm_iter_count = 0; 11915 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) { 11916 trnlnm_iter_count++; 11917 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 11918 } 11919 fname = fileified; 11920 } 11921 11922 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS); 11923 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11924 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) { 11925 /* Don't know if already in VMS format, so make sure */ 11926 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) { 11927 PerlMem_free(fileified); 11928 PerlMem_free(vmsname); 11929 return FALSE; 11930 } 11931 } 11932 else { 11933 my_strlcpy(vmsname, fname, VMS_MAXRSS); 11934 } 11935 11936 /* sys$check_access needs a file spec, not a directory spec. 11937 * flex_stat now will handle a null thread context during startup. 11938 */ 11939 11940 retlen = namdsc.dsc$w_length = strlen(vmsname); 11941 if (vmsname[retlen-1] == ']' 11942 || vmsname[retlen-1] == '>' 11943 || vmsname[retlen-1] == ':' 11944 || (!flex_stat_int(vmsname, &st, 1) && 11945 S_ISDIR(st.st_mode))) { 11946 11947 if (!int_fileify_dirspec(vmsname, fileified, NULL)) { 11948 PerlMem_free(fileified); 11949 PerlMem_free(vmsname); 11950 return FALSE; 11951 } 11952 fname = fileified; 11953 } 11954 else { 11955 fname = vmsname; 11956 } 11957 11958 retlen = namdsc.dsc$w_length = strlen(fname); 11959 namdsc.dsc$a_pointer = (char *)fname; 11960 11961 switch (bit) { 11962 case S_IXUSR: case S_IXGRP: case S_IXOTH: 11963 access = ARM$M_EXECUTE; 11964 flags = CHP$M_READ; 11965 break; 11966 case S_IRUSR: case S_IRGRP: case S_IROTH: 11967 access = ARM$M_READ; 11968 flags = CHP$M_READ | CHP$M_USEREADALL; 11969 break; 11970 case S_IWUSR: case S_IWGRP: case S_IWOTH: 11971 access = ARM$M_WRITE; 11972 flags = CHP$M_READ | CHP$M_WRITE; 11973 break; 11974 case S_IDUSR: case S_IDGRP: case S_IDOTH: 11975 access = ARM$M_DELETE; 11976 flags = CHP$M_READ | CHP$M_WRITE; 11977 break; 11978 default: 11979 if (fileified != NULL) 11980 PerlMem_free(fileified); 11981 if (vmsname != NULL) 11982 PerlMem_free(vmsname); 11983 return FALSE; 11984 } 11985 11986 /* Before we call $check_access, create a user profile with the current 11987 * process privs since otherwise it just uses the default privs from the 11988 * UAF and might give false positives or negatives. This only works on 11989 * VMS versions v6.0 and later since that's when sys$create_user_profile 11990 * became available. 11991 */ 11992 11993 /* get current process privs and username */ 11994 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0)); 11995 _ckvmssts_noperl(iosb[0]); 11996 11997 /* find out the space required for the profile */ 11998 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0, 11999 &usrprodsc.dsc$w_length,&profile_context)); 12000 12001 /* allocate space for the profile and get it filled in */ 12002 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length); 12003 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12004 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer, 12005 &usrprodsc.dsc$w_length,&profile_context)); 12006 12007 /* use the profile to check access to the file; free profile & analyze results */ 12008 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc); 12009 PerlMem_free(usrprodsc.dsc$a_pointer); 12010 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */ 12011 12012 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT || 12013 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN || 12014 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) { 12015 set_vaxc_errno(retsts); 12016 if (retsts == SS$_NOPRIV) set_errno(EACCES); 12017 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL); 12018 else set_errno(ENOENT); 12019 if (fileified != NULL) 12020 PerlMem_free(fileified); 12021 if (vmsname != NULL) 12022 PerlMem_free(vmsname); 12023 return FALSE; 12024 } 12025 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) { 12026 if (fileified != NULL) 12027 PerlMem_free(fileified); 12028 if (vmsname != NULL) 12029 PerlMem_free(vmsname); 12030 return TRUE; 12031 } 12032 _ckvmssts_noperl(retsts); 12033 12034 if (fileified != NULL) 12035 PerlMem_free(fileified); 12036 if (vmsname != NULL) 12037 PerlMem_free(vmsname); 12038 return FALSE; /* Should never get here */ 12039 12040 } 12041 12042 /* Do the permissions allow some operation? Assumes PL_statcache already set. */ 12043 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a 12044 * subset of the applicable information. 12045 */ 12046 bool 12047 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp) 12048 { 12049 return cando_by_name_int 12050 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN); 12051 } /* end of cando() */ 12052 /*}}}*/ 12053 12054 12055 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/ 12056 I32 12057 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname) 12058 { 12059 return cando_by_name_int(bit, effective, fname, 0); 12060 12061 } /* end of cando_by_name() */ 12062 /*}}}*/ 12063 12064 12065 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/ 12066 int 12067 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp) 12068 { 12069 dSAVE_ERRNO; /* fstat may set this even on success */ 12070 if (!fstat(fd, &statbufp->crtl_stat)) { 12071 char *cptr; 12072 char *vms_filename; 12073 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS); 12074 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM); 12075 12076 /* Save name for cando by name in VMS format */ 12077 cptr = getname(fd, vms_filename, 1); 12078 12079 /* This should not happen, but just in case */ 12080 if (cptr == NULL) { 12081 statbufp->st_devnam[0] = 0; 12082 } 12083 else { 12084 /* Make sure that the saved name fits in 255 characters */ 12085 cptr = int_rmsexpand_vms 12086 (vms_filename, 12087 statbufp->st_devnam, 12088 0); 12089 if (cptr == NULL) 12090 statbufp->st_devnam[0] = 0; 12091 } 12092 PerlMem_free(vms_filename); 12093 12094 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 12095 VMS_DEVICE_ENCODE 12096 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 12097 12098 # ifdef VMSISH_TIME 12099 if (VMSISH_TIME) { 12100 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12101 statbufp->st_atime = _toloc(statbufp->st_atime); 12102 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12103 } 12104 # endif 12105 RESTORE_ERRNO; 12106 return 0; 12107 } 12108 return -1; 12109 12110 } /* end of flex_fstat() */ 12111 /*}}}*/ 12112 12113 static int 12114 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) 12115 { 12116 char *temp_fspec = NULL; 12117 char *fileified = NULL; 12118 const char *save_spec; 12119 char *ret_spec; 12120 int retval = -1; 12121 char efs_hack = 0; 12122 char already_fileified = 0; 12123 dSAVEDERRNO; 12124 12125 if (!fspec) { 12126 errno = EINVAL; 12127 return retval; 12128 } 12129 12130 if (decc_bug_devnull != 0) { 12131 if (is_null_device(fspec)) { /* Fake a stat() for the null device */ 12132 memset(statbufp,0,sizeof *statbufp); 12133 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0); 12134 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC; 12135 statbufp->st_uid = 0x00010001; 12136 statbufp->st_gid = 0x0001; 12137 time((time_t *)&statbufp->st_mtime); 12138 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime; 12139 return 0; 12140 } 12141 } 12142 12143 SAVE_ERRNO; 12144 12145 #if __CRTL_VER >= 80200000 && !defined(__VAX) 12146 /* 12147 * If we are in POSIX filespec mode, accept the filename as is. 12148 */ 12149 if (decc_posix_compliant_pathnames == 0) { 12150 #endif 12151 12152 /* Try for a simple stat first. If fspec contains a filename without 12153 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir 12154 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here. 12155 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir, 12156 * not sea:[wine.dark]., if the latter exists. If the intended target is 12157 * the file with null type, specify this by calling flex_stat() with 12158 * a '.' at the end of fspec. 12159 */ 12160 12161 if (lstat_flag == 0) 12162 retval = stat(fspec, &statbufp->crtl_stat); 12163 else 12164 retval = lstat(fspec, &statbufp->crtl_stat); 12165 12166 if (!retval) { 12167 save_spec = fspec; 12168 } 12169 else { 12170 /* In the odd case where we have write but not read access 12171 * to a directory, stat('foo.DIR') works but stat('foo') doesn't. 12172 */ 12173 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 12174 if (fileified == NULL) 12175 _ckvmssts_noperl(SS$_INSFMEM); 12176 12177 ret_spec = int_fileify_dirspec(fspec, fileified, NULL); 12178 if (ret_spec != NULL) { 12179 if (lstat_flag == 0) 12180 retval = stat(fileified, &statbufp->crtl_stat); 12181 else 12182 retval = lstat(fileified, &statbufp->crtl_stat); 12183 save_spec = fileified; 12184 already_fileified = 1; 12185 } 12186 } 12187 12188 if (retval && vms_bug_stat_filename) { 12189 12190 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS); 12191 if (temp_fspec == NULL) 12192 _ckvmssts_noperl(SS$_INSFMEM); 12193 12194 /* We should try again as a vmsified file specification. */ 12195 12196 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL); 12197 if (ret_spec != NULL) { 12198 if (lstat_flag == 0) 12199 retval = stat(temp_fspec, &statbufp->crtl_stat); 12200 else 12201 retval = lstat(temp_fspec, &statbufp->crtl_stat); 12202 save_spec = temp_fspec; 12203 } 12204 } 12205 12206 if (retval) { 12207 /* Last chance - allow multiple dots without EFS CHARSET */ 12208 /* The CRTL stat() falls down hard on multi-dot filenames in unix 12209 * format unless * DECC$EFS_CHARSET is in effect, so temporarily 12210 * enable it if it isn't already. 12211 */ 12212 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12213 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 12214 decc$feature_set_value(decc_efs_charset_index, 1, 1); 12215 #endif 12216 if (lstat_flag == 0) 12217 retval = stat(fspec, &statbufp->crtl_stat); 12218 else 12219 retval = lstat(fspec, &statbufp->crtl_stat); 12220 save_spec = fspec; 12221 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12222 if (!decc_efs_charset && (decc_efs_charset_index > 0)) { 12223 decc$feature_set_value(decc_efs_charset_index, 1, 0); 12224 efs_hack = 1; 12225 } 12226 #endif 12227 } 12228 12229 #if __CRTL_VER >= 80200000 && !defined(__VAX) 12230 } else { 12231 if (lstat_flag == 0) 12232 retval = stat(temp_fspec, &statbufp->crtl_stat); 12233 else 12234 retval = lstat(temp_fspec, &statbufp->crtl_stat); 12235 save_spec = temp_fspec; 12236 } 12237 #endif 12238 12239 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12240 /* As you were... */ 12241 if (!decc_efs_charset) 12242 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0); 12243 #endif 12244 12245 if (!retval) { 12246 char *cptr; 12247 int rmsex_flags = PERL_RMSEXPAND_M_VMS; 12248 12249 /* If this is an lstat, do not follow the link */ 12250 if (lstat_flag) 12251 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK; 12252 12253 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12254 /* If we used the efs_hack above, we must also use it here for */ 12255 /* perl_cando to work */ 12256 if (efs_hack && (decc_efs_charset_index > 0)) { 12257 decc$feature_set_value(decc_efs_charset_index, 1, 1); 12258 } 12259 #endif 12260 12261 /* If we've got a directory, save a fileified, expanded version of it 12262 * in st_devnam. If not a directory, just an expanded version. 12263 */ 12264 if (S_ISDIR(statbufp->st_mode) && !already_fileified) { 12265 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 12266 if (fileified == NULL) 12267 _ckvmssts_noperl(SS$_INSFMEM); 12268 12269 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL); 12270 if (cptr != NULL) 12271 save_spec = fileified; 12272 } 12273 12274 cptr = int_rmsexpand(save_spec, 12275 statbufp->st_devnam, 12276 NULL, 12277 rmsex_flags, 12278 0, 12279 0); 12280 12281 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12282 if (efs_hack && (decc_efs_charset_index > 0)) { 12283 decc$feature_set_value(decc_efs_charset, 1, 0); 12284 } 12285 #endif 12286 12287 /* Fix me: If this is NULL then stat found a file, and we could */ 12288 /* not convert the specification to VMS - Should never happen */ 12289 if (cptr == NULL) 12290 statbufp->st_devnam[0] = 0; 12291 12292 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 12293 VMS_DEVICE_ENCODE 12294 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 12295 # ifdef VMSISH_TIME 12296 if (VMSISH_TIME) { 12297 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12298 statbufp->st_atime = _toloc(statbufp->st_atime); 12299 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12300 } 12301 # endif 12302 } 12303 /* If we were successful, leave errno where we found it */ 12304 if (retval == 0) RESTORE_ERRNO; 12305 if (temp_fspec) 12306 PerlMem_free(temp_fspec); 12307 if (fileified) 12308 PerlMem_free(fileified); 12309 return retval; 12310 12311 } /* end of flex_stat_int() */ 12312 12313 12314 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/ 12315 int 12316 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp) 12317 { 12318 return flex_stat_int(fspec, statbufp, 0); 12319 } 12320 /*}}}*/ 12321 12322 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/ 12323 int 12324 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp) 12325 { 12326 return flex_stat_int(fspec, statbufp, 1); 12327 } 12328 /*}}}*/ 12329 12330 12331 /*{{{char *my_getlogin()*/ 12332 /* VMS cuserid == Unix getlogin, except calling sequence */ 12333 char * 12334 my_getlogin(void) 12335 { 12336 static char user[L_cuserid]; 12337 return cuserid(user); 12338 } 12339 /*}}}*/ 12340 12341 12342 /* rmscopy - copy a file using VMS RMS routines 12343 * 12344 * Copies contents and attributes of spec_in to spec_out, except owner 12345 * and protection information. Name and type of spec_in are used as 12346 * defaults for spec_out. The third parameter specifies whether rmscopy() 12347 * should try to propagate timestamps from the input file to the output file. 12348 * If it is less than 0, no timestamps are preserved. If it is 0, then 12349 * rmscopy() will behave similarly to the DCL COPY command: timestamps are 12350 * propagated to the output file at creation iff the output file specification 12351 * did not contain an explicit name or type, and the revision date is always 12352 * updated at the end of the copy operation. If it is greater than 0, then 12353 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps 12354 * other than the revision date should be propagated, and bit 1 indicates 12355 * that the revision date should be propagated. 12356 * 12357 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure. 12358 * 12359 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>. 12360 * Incorporates, with permission, some code from EZCOPY by Tim Adye 12361 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code 12362 * as part of the Perl standard distribution under the terms of the 12363 * GNU General Public License or the Perl Artistic License. Copies 12364 * of each may be found in the Perl standard distribution. 12365 */ /* FIXME */ 12366 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/ 12367 int 12368 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates) 12369 { 12370 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out, 12371 *rsa, *rsal, *rsa_out, *rsal_out, *ubf; 12372 unsigned long int sts; 12373 int dna_len; 12374 struct FAB fab_in, fab_out; 12375 struct RAB rab_in, rab_out; 12376 rms_setup_nam(nam); 12377 rms_setup_nam(nam_out); 12378 struct XABDAT xabdat; 12379 struct XABFHC xabfhc; 12380 struct XABRDT xabrdt; 12381 struct XABSUM xabsum; 12382 12383 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS); 12384 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12385 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS); 12386 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12387 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) || 12388 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) { 12389 PerlMem_free(vmsin); 12390 PerlMem_free(vmsout); 12391 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12392 return 0; 12393 } 12394 12395 esa = (char *)PerlMem_malloc(VMS_MAXRSS); 12396 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12397 esal = NULL; 12398 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12399 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 12400 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12401 #endif 12402 fab_in = cc$rms_fab; 12403 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin)); 12404 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI; 12405 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET; 12406 fab_in.fab$l_fop = FAB$M_SQO; 12407 rms_bind_fab_nam(fab_in, nam); 12408 fab_in.fab$l_xab = (void *) &xabdat; 12409 12410 rsa = (char *)PerlMem_malloc(VMS_MAXRSS); 12411 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12412 rsal = NULL; 12413 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12414 rsal = (char *)PerlMem_malloc(VMS_MAXRSS); 12415 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12416 #endif 12417 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1)); 12418 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 12419 rms_nam_esl(nam) = 0; 12420 rms_nam_rsl(nam) = 0; 12421 rms_nam_esll(nam) = 0; 12422 rms_nam_rsll(nam) = 0; 12423 #ifdef NAM$M_NO_SHORT_UPCASE 12424 if (decc_efs_case_preserve) 12425 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE); 12426 #endif 12427 12428 xabdat = cc$rms_xabdat; /* To get creation date */ 12429 xabdat.xab$l_nxt = (void *) &xabfhc; 12430 12431 xabfhc = cc$rms_xabfhc; /* To get record length */ 12432 xabfhc.xab$l_nxt = (void *) &xabsum; 12433 12434 xabsum = cc$rms_xabsum; /* To get key and area information */ 12435 12436 if (!((sts = sys$open(&fab_in)) & 1)) { 12437 PerlMem_free(vmsin); 12438 PerlMem_free(vmsout); 12439 PerlMem_free(esa); 12440 if (esal != NULL) 12441 PerlMem_free(esal); 12442 PerlMem_free(rsa); 12443 if (rsal != NULL) 12444 PerlMem_free(rsal); 12445 set_vaxc_errno(sts); 12446 switch (sts) { 12447 case RMS$_FNF: case RMS$_DNF: 12448 set_errno(ENOENT); break; 12449 case RMS$_DIR: 12450 set_errno(ENOTDIR); break; 12451 case RMS$_DEV: 12452 set_errno(ENODEV); break; 12453 case RMS$_SYN: 12454 set_errno(EINVAL); break; 12455 case RMS$_PRV: 12456 set_errno(EACCES); break; 12457 default: 12458 set_errno(EVMSERR); 12459 } 12460 return 0; 12461 } 12462 12463 nam_out = nam; 12464 fab_out = fab_in; 12465 fab_out.fab$w_ifi = 0; 12466 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT; 12467 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI; 12468 fab_out.fab$l_fop = FAB$M_SQO; 12469 rms_bind_fab_nam(fab_out, nam_out); 12470 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout)); 12471 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0; 12472 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len); 12473 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 12474 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12475 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 12476 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12477 esal_out = NULL; 12478 rsal_out = NULL; 12479 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12480 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS); 12481 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12482 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS); 12483 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12484 #endif 12485 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1)); 12486 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1)); 12487 12488 if (preserve_dates == 0) { /* Act like DCL COPY */ 12489 rms_set_nam_nop(nam_out, NAM$M_SYNCHK); 12490 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */ 12491 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) { 12492 PerlMem_free(vmsin); 12493 PerlMem_free(vmsout); 12494 PerlMem_free(esa); 12495 if (esal != NULL) 12496 PerlMem_free(esal); 12497 PerlMem_free(rsa); 12498 if (rsal != NULL) 12499 PerlMem_free(rsal); 12500 PerlMem_free(esa_out); 12501 if (esal_out != NULL) 12502 PerlMem_free(esal_out); 12503 PerlMem_free(rsa_out); 12504 if (rsal_out != NULL) 12505 PerlMem_free(rsal_out); 12506 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR); 12507 set_vaxc_errno(sts); 12508 return 0; 12509 } 12510 fab_out.fab$l_xab = (void *) &xabdat; 12511 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) 12512 preserve_dates = 1; 12513 } 12514 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */ 12515 preserve_dates =0; /* bitmask from this point forward */ 12516 12517 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc; 12518 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) { 12519 PerlMem_free(vmsin); 12520 PerlMem_free(vmsout); 12521 PerlMem_free(esa); 12522 if (esal != NULL) 12523 PerlMem_free(esal); 12524 PerlMem_free(rsa); 12525 if (rsal != NULL) 12526 PerlMem_free(rsal); 12527 PerlMem_free(esa_out); 12528 if (esal_out != NULL) 12529 PerlMem_free(esal_out); 12530 PerlMem_free(rsa_out); 12531 if (rsal_out != NULL) 12532 PerlMem_free(rsal_out); 12533 set_vaxc_errno(sts); 12534 switch (sts) { 12535 case RMS$_DNF: 12536 set_errno(ENOENT); break; 12537 case RMS$_DIR: 12538 set_errno(ENOTDIR); break; 12539 case RMS$_DEV: 12540 set_errno(ENODEV); break; 12541 case RMS$_SYN: 12542 set_errno(EINVAL); break; 12543 case RMS$_PRV: 12544 set_errno(EACCES); break; 12545 default: 12546 set_errno(EVMSERR); 12547 } 12548 return 0; 12549 } 12550 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */ 12551 if (preserve_dates & 2) { 12552 /* sys$close() will process xabrdt, not xabdat */ 12553 xabrdt = cc$rms_xabrdt; 12554 #ifndef __GNUC__ 12555 xabrdt.xab$q_rdt = xabdat.xab$q_rdt; 12556 #else 12557 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt 12558 * is unsigned long[2], while DECC & VAXC use a struct */ 12559 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt); 12560 #endif 12561 fab_out.fab$l_xab = (void *) &xabrdt; 12562 } 12563 12564 ubf = (char *)PerlMem_malloc(32256); 12565 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12566 rab_in = cc$rms_rab; 12567 rab_in.rab$l_fab = &fab_in; 12568 rab_in.rab$l_rop = RAB$M_BIO; 12569 rab_in.rab$l_ubf = ubf; 12570 rab_in.rab$w_usz = 32256; 12571 if (!((sts = sys$connect(&rab_in)) & 1)) { 12572 sys$close(&fab_in); sys$close(&fab_out); 12573 PerlMem_free(vmsin); 12574 PerlMem_free(vmsout); 12575 PerlMem_free(ubf); 12576 PerlMem_free(esa); 12577 if (esal != NULL) 12578 PerlMem_free(esal); 12579 PerlMem_free(rsa); 12580 if (rsal != NULL) 12581 PerlMem_free(rsal); 12582 PerlMem_free(esa_out); 12583 if (esal_out != NULL) 12584 PerlMem_free(esal_out); 12585 PerlMem_free(rsa_out); 12586 if (rsal_out != NULL) 12587 PerlMem_free(rsal_out); 12588 set_errno(EVMSERR); set_vaxc_errno(sts); 12589 return 0; 12590 } 12591 12592 rab_out = cc$rms_rab; 12593 rab_out.rab$l_fab = &fab_out; 12594 rab_out.rab$l_rbf = ubf; 12595 if (!((sts = sys$connect(&rab_out)) & 1)) { 12596 sys$close(&fab_in); sys$close(&fab_out); 12597 PerlMem_free(vmsin); 12598 PerlMem_free(vmsout); 12599 PerlMem_free(ubf); 12600 PerlMem_free(esa); 12601 if (esal != NULL) 12602 PerlMem_free(esal); 12603 PerlMem_free(rsa); 12604 if (rsal != NULL) 12605 PerlMem_free(rsal); 12606 PerlMem_free(esa_out); 12607 if (esal_out != NULL) 12608 PerlMem_free(esal_out); 12609 PerlMem_free(rsa_out); 12610 if (rsal_out != NULL) 12611 PerlMem_free(rsal_out); 12612 set_errno(EVMSERR); set_vaxc_errno(sts); 12613 return 0; 12614 } 12615 12616 while ((sts = sys$read(&rab_in))) { /* always true */ 12617 if (sts == RMS$_EOF) break; 12618 rab_out.rab$w_rsz = rab_in.rab$w_rsz; 12619 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) { 12620 sys$close(&fab_in); sys$close(&fab_out); 12621 PerlMem_free(vmsin); 12622 PerlMem_free(vmsout); 12623 PerlMem_free(ubf); 12624 PerlMem_free(esa); 12625 if (esal != NULL) 12626 PerlMem_free(esal); 12627 PerlMem_free(rsa); 12628 if (rsal != NULL) 12629 PerlMem_free(rsal); 12630 PerlMem_free(esa_out); 12631 if (esal_out != NULL) 12632 PerlMem_free(esal_out); 12633 PerlMem_free(rsa_out); 12634 if (rsal_out != NULL) 12635 PerlMem_free(rsal_out); 12636 set_errno(EVMSERR); set_vaxc_errno(sts); 12637 return 0; 12638 } 12639 } 12640 12641 12642 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */ 12643 sys$close(&fab_in); sys$close(&fab_out); 12644 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts; 12645 12646 PerlMem_free(vmsin); 12647 PerlMem_free(vmsout); 12648 PerlMem_free(ubf); 12649 PerlMem_free(esa); 12650 if (esal != NULL) 12651 PerlMem_free(esal); 12652 PerlMem_free(rsa); 12653 if (rsal != NULL) 12654 PerlMem_free(rsal); 12655 PerlMem_free(esa_out); 12656 if (esal_out != NULL) 12657 PerlMem_free(esal_out); 12658 PerlMem_free(rsa_out); 12659 if (rsal_out != NULL) 12660 PerlMem_free(rsal_out); 12661 12662 if (!(sts & 1)) { 12663 set_errno(EVMSERR); set_vaxc_errno(sts); 12664 return 0; 12665 } 12666 12667 return 1; 12668 12669 } /* end of rmscopy() */ 12670 /*}}}*/ 12671 12672 12673 /*** The following glue provides 'hooks' to make some of the routines 12674 * from this file available from Perl. These routines are sufficiently 12675 * basic, and are required sufficiently early in the build process, 12676 * that's it's nice to have them available to miniperl as well as the 12677 * full Perl, so they're set up here instead of in an extension. The 12678 * Perl code which handles importation of these names into a given 12679 * package lives in [.VMS]Filespec.pm in @INC. 12680 */ 12681 12682 void 12683 rmsexpand_fromperl(pTHX_ CV *cv) 12684 { 12685 dXSARGS; 12686 char *fspec, *defspec = NULL, *rslt; 12687 STRLEN n_a; 12688 int fs_utf8, dfs_utf8; 12689 12690 fs_utf8 = 0; 12691 dfs_utf8 = 0; 12692 if (!items || items > 2) 12693 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])"); 12694 fspec = SvPV(ST(0),n_a); 12695 fs_utf8 = SvUTF8(ST(0)); 12696 if (!fspec || !*fspec) XSRETURN_UNDEF; 12697 if (items == 2) { 12698 defspec = SvPV(ST(1),n_a); 12699 dfs_utf8 = SvUTF8(ST(1)); 12700 } 12701 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8); 12702 ST(0) = sv_newmortal(); 12703 if (rslt != NULL) { 12704 sv_usepvn(ST(0),rslt,strlen(rslt)); 12705 if (fs_utf8) { 12706 SvUTF8_on(ST(0)); 12707 } 12708 } 12709 XSRETURN(1); 12710 } 12711 12712 void 12713 vmsify_fromperl(pTHX_ CV *cv) 12714 { 12715 dXSARGS; 12716 char *vmsified; 12717 STRLEN n_a; 12718 int utf8_fl; 12719 12720 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)"); 12721 utf8_fl = SvUTF8(ST(0)); 12722 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12723 ST(0) = sv_newmortal(); 12724 if (vmsified != NULL) { 12725 sv_usepvn(ST(0),vmsified,strlen(vmsified)); 12726 if (utf8_fl) { 12727 SvUTF8_on(ST(0)); 12728 } 12729 } 12730 XSRETURN(1); 12731 } 12732 12733 void 12734 unixify_fromperl(pTHX_ CV *cv) 12735 { 12736 dXSARGS; 12737 char *unixified; 12738 STRLEN n_a; 12739 int utf8_fl; 12740 12741 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)"); 12742 utf8_fl = SvUTF8(ST(0)); 12743 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12744 ST(0) = sv_newmortal(); 12745 if (unixified != NULL) { 12746 sv_usepvn(ST(0),unixified,strlen(unixified)); 12747 if (utf8_fl) { 12748 SvUTF8_on(ST(0)); 12749 } 12750 } 12751 XSRETURN(1); 12752 } 12753 12754 void 12755 fileify_fromperl(pTHX_ CV *cv) 12756 { 12757 dXSARGS; 12758 char *fileified; 12759 STRLEN n_a; 12760 int utf8_fl; 12761 12762 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)"); 12763 utf8_fl = SvUTF8(ST(0)); 12764 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12765 ST(0) = sv_newmortal(); 12766 if (fileified != NULL) { 12767 sv_usepvn(ST(0),fileified,strlen(fileified)); 12768 if (utf8_fl) { 12769 SvUTF8_on(ST(0)); 12770 } 12771 } 12772 XSRETURN(1); 12773 } 12774 12775 void 12776 pathify_fromperl(pTHX_ CV *cv) 12777 { 12778 dXSARGS; 12779 char *pathified; 12780 STRLEN n_a; 12781 int utf8_fl; 12782 12783 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)"); 12784 utf8_fl = SvUTF8(ST(0)); 12785 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12786 ST(0) = sv_newmortal(); 12787 if (pathified != NULL) { 12788 sv_usepvn(ST(0),pathified,strlen(pathified)); 12789 if (utf8_fl) { 12790 SvUTF8_on(ST(0)); 12791 } 12792 } 12793 XSRETURN(1); 12794 } 12795 12796 void 12797 vmspath_fromperl(pTHX_ CV *cv) 12798 { 12799 dXSARGS; 12800 char *vmspath; 12801 STRLEN n_a; 12802 int utf8_fl; 12803 12804 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)"); 12805 utf8_fl = SvUTF8(ST(0)); 12806 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12807 ST(0) = sv_newmortal(); 12808 if (vmspath != NULL) { 12809 sv_usepvn(ST(0),vmspath,strlen(vmspath)); 12810 if (utf8_fl) { 12811 SvUTF8_on(ST(0)); 12812 } 12813 } 12814 XSRETURN(1); 12815 } 12816 12817 void 12818 unixpath_fromperl(pTHX_ CV *cv) 12819 { 12820 dXSARGS; 12821 char *unixpath; 12822 STRLEN n_a; 12823 int utf8_fl; 12824 12825 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)"); 12826 utf8_fl = SvUTF8(ST(0)); 12827 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12828 ST(0) = sv_newmortal(); 12829 if (unixpath != NULL) { 12830 sv_usepvn(ST(0),unixpath,strlen(unixpath)); 12831 if (utf8_fl) { 12832 SvUTF8_on(ST(0)); 12833 } 12834 } 12835 XSRETURN(1); 12836 } 12837 12838 void 12839 candelete_fromperl(pTHX_ CV *cv) 12840 { 12841 dXSARGS; 12842 char *fspec, *fsp; 12843 SV *mysv; 12844 IO *io; 12845 STRLEN n_a; 12846 12847 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)"); 12848 12849 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12850 Newx(fspec, VMS_MAXRSS, char); 12851 if (fspec == NULL) _ckvmssts(SS$_INSFMEM); 12852 if (isGV_with_GP(mysv)) { 12853 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) { 12854 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12855 ST(0) = &PL_sv_no; 12856 Safefree(fspec); 12857 XSRETURN(1); 12858 } 12859 fsp = fspec; 12860 } 12861 else { 12862 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) { 12863 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12864 ST(0) = &PL_sv_no; 12865 Safefree(fspec); 12866 XSRETURN(1); 12867 } 12868 } 12869 12870 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp)); 12871 Safefree(fspec); 12872 XSRETURN(1); 12873 } 12874 12875 void 12876 rmscopy_fromperl(pTHX_ CV *cv) 12877 { 12878 dXSARGS; 12879 char *inspec, *outspec, *inp, *outp; 12880 int date_flag; 12881 SV *mysv; 12882 IO *io; 12883 STRLEN n_a; 12884 12885 if (items < 2 || items > 3) 12886 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])"); 12887 12888 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12889 Newx(inspec, VMS_MAXRSS, char); 12890 if (isGV_with_GP(mysv)) { 12891 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) { 12892 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12893 ST(0) = sv_2mortal(newSViv(0)); 12894 Safefree(inspec); 12895 XSRETURN(1); 12896 } 12897 inp = inspec; 12898 } 12899 else { 12900 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) { 12901 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12902 ST(0) = sv_2mortal(newSViv(0)); 12903 Safefree(inspec); 12904 XSRETURN(1); 12905 } 12906 } 12907 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1); 12908 Newx(outspec, VMS_MAXRSS, char); 12909 if (isGV_with_GP(mysv)) { 12910 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) { 12911 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12912 ST(0) = sv_2mortal(newSViv(0)); 12913 Safefree(inspec); 12914 Safefree(outspec); 12915 XSRETURN(1); 12916 } 12917 outp = outspec; 12918 } 12919 else { 12920 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) { 12921 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12922 ST(0) = sv_2mortal(newSViv(0)); 12923 Safefree(inspec); 12924 Safefree(outspec); 12925 XSRETURN(1); 12926 } 12927 } 12928 date_flag = (items == 3) ? SvIV(ST(2)) : 0; 12929 12930 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag))); 12931 Safefree(inspec); 12932 Safefree(outspec); 12933 XSRETURN(1); 12934 } 12935 12936 /* The mod2fname is limited to shorter filenames by design, so it should 12937 * not be modified to support longer EFS pathnames 12938 */ 12939 void 12940 mod2fname(pTHX_ CV *cv) 12941 { 12942 dXSARGS; 12943 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1], 12944 workbuff[NAM$C_MAXRSS*1 + 1]; 12945 int counter, num_entries; 12946 /* ODS-5 ups this, but we want to be consistent, so... */ 12947 int max_name_len = 39; 12948 AV *in_array = (AV *)SvRV(ST(0)); 12949 12950 num_entries = av_len(in_array); 12951 12952 /* All the names start with PL_. */ 12953 strcpy(ultimate_name, "PL_"); 12954 12955 /* Clean up our working buffer */ 12956 Zero(work_name, sizeof(work_name), char); 12957 12958 /* Run through the entries and build up a working name */ 12959 for(counter = 0; counter <= num_entries; counter++) { 12960 /* If it's not the first name then tack on a __ */ 12961 if (counter) { 12962 my_strlcat(work_name, "__", sizeof(work_name)); 12963 } 12964 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name)); 12965 } 12966 12967 /* Check to see if we actually have to bother...*/ 12968 if (strlen(work_name) + 3 <= max_name_len) { 12969 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name)); 12970 } else { 12971 /* It's too darned big, so we need to go strip. We use the same */ 12972 /* algorithm as xsubpp does. First, strip out doubled __ */ 12973 char *source, *dest, last; 12974 dest = workbuff; 12975 last = 0; 12976 for (source = work_name; *source; source++) { 12977 if (last == *source && last == '_') { 12978 continue; 12979 } 12980 *dest++ = *source; 12981 last = *source; 12982 } 12983 /* Go put it back */ 12984 my_strlcpy(work_name, workbuff, sizeof(work_name)); 12985 /* Is it still too big? */ 12986 if (strlen(work_name) + 3 > max_name_len) { 12987 /* Strip duplicate letters */ 12988 last = 0; 12989 dest = workbuff; 12990 for (source = work_name; *source; source++) { 12991 if (last == toupper(*source)) { 12992 continue; 12993 } 12994 *dest++ = *source; 12995 last = toupper(*source); 12996 } 12997 my_strlcpy(work_name, workbuff, sizeof(work_name)); 12998 } 12999 13000 /* Is it *still* too big? */ 13001 if (strlen(work_name) + 3 > max_name_len) { 13002 /* Too bad, we truncate */ 13003 work_name[max_name_len - 2] = 0; 13004 } 13005 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name)); 13006 } 13007 13008 /* Okay, return it */ 13009 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0)); 13010 XSRETURN(1); 13011 } 13012 13013 void 13014 hushexit_fromperl(pTHX_ CV *cv) 13015 { 13016 dXSARGS; 13017 13018 if (items > 0) { 13019 VMSISH_HUSHED = SvTRUE(ST(0)); 13020 } 13021 ST(0) = boolSV(VMSISH_HUSHED); 13022 XSRETURN(1); 13023 } 13024 13025 13026 PerlIO * 13027 Perl_vms_start_glob 13028 (pTHX_ SV *tmpglob, 13029 IO *io) 13030 { 13031 PerlIO *fp; 13032 struct vs_str_st *rslt; 13033 char *vmsspec; 13034 char *rstr; 13035 char *begin, *cp; 13036 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;"); 13037 PerlIO *tmpfp; 13038 STRLEN i; 13039 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13040 struct dsc$descriptor_vs rsdsc; 13041 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0; 13042 unsigned long hasver = 0, isunix = 0; 13043 unsigned long int lff_flags = 0; 13044 int rms_sts; 13045 int vms_old_glob = 1; 13046 13047 if (!SvOK(tmpglob)) { 13048 SETERRNO(ENOENT,RMS$_FNF); 13049 return NULL; 13050 } 13051 13052 vms_old_glob = !decc_filename_unix_report; 13053 13054 #ifdef VMS_LONGNAME_SUPPORT 13055 lff_flags = LIB$M_FIL_LONG_NAMES; 13056 #endif 13057 /* The Newx macro will not allow me to assign a smaller array 13058 * to the rslt pointer, so we will assign it to the begin char pointer 13059 * and then copy the value into the rslt pointer. 13060 */ 13061 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char); 13062 rslt = (struct vs_str_st *)begin; 13063 rslt->length = 0; 13064 rstr = &rslt->str[0]; 13065 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */ 13066 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int); 13067 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT; 13068 rsdsc.dsc$b_class = DSC$K_CLASS_VS; 13069 13070 Newx(vmsspec, VMS_MAXRSS, char); 13071 13072 /* We could find out if there's an explicit dev/dir or version 13073 by peeking into lib$find_file's internal context at 13074 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb 13075 but that's unsupported, so I don't want to do it now and 13076 have it bite someone in the future. */ 13077 /* Fix-me: vms_split_path() is the only way to do this, the 13078 existing method will fail with many legal EFS or UNIX specifications 13079 */ 13080 13081 cp = SvPV(tmpglob,i); 13082 13083 for (; i; i--) { 13084 if (cp[i] == ';') hasver = 1; 13085 if (cp[i] == '.') { 13086 if (sts) hasver = 1; 13087 else sts = 1; 13088 } 13089 if (cp[i] == '/') { 13090 hasdir = isunix = 1; 13091 break; 13092 } 13093 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') { 13094 hasdir = 1; 13095 break; 13096 } 13097 } 13098 13099 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */ 13100 if ((hasdir == 0) && decc_filename_unix_report) { 13101 isunix = 1; 13102 } 13103 13104 if ((tmpfp = PerlIO_tmpfile()) != NULL) { 13105 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec; 13106 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len; 13107 int wildstar = 0; 13108 int wildquery = 0; 13109 int found = 0; 13110 Stat_t st; 13111 int stat_sts; 13112 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st); 13113 if (!stat_sts && S_ISDIR(st.st_mode)) { 13114 char * vms_dir; 13115 const char * fname; 13116 STRLEN fname_len; 13117 13118 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */ 13119 /* path delimiter of ':>]', if so, then the old behavior has */ 13120 /* obviously been specifically requested */ 13121 13122 fname = SvPVX_const(tmpglob); 13123 fname_len = strlen(fname); 13124 vms_dir = strpbrk(&fname[fname_len - 1], ":>]"); 13125 if (vms_old_glob || (vms_dir != NULL)) { 13126 wilddsc.dsc$a_pointer = tovmspath_utf8( 13127 SvPVX(tmpglob),vmsspec,NULL); 13128 ok = (wilddsc.dsc$a_pointer != NULL); 13129 /* maybe passed 'foo' rather than '[.foo]', thus not 13130 detected above */ 13131 hasdir = 1; 13132 } else { 13133 /* Operate just on the directory, the special stat/fstat for */ 13134 /* leaves the fileified specification in the st_devnam */ 13135 /* member. */ 13136 wilddsc.dsc$a_pointer = st.st_devnam; 13137 ok = 1; 13138 } 13139 } 13140 else { 13141 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL); 13142 ok = (wilddsc.dsc$a_pointer != NULL); 13143 } 13144 if (ok) 13145 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer); 13146 13147 /* If not extended character set, replace ? with % */ 13148 /* With extended character set, ? is a wildcard single character */ 13149 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) { 13150 if (*cp == '?') { 13151 wildquery = 1; 13152 if (!decc_efs_charset) 13153 *cp = '%'; 13154 } else if (*cp == '%') { 13155 wildquery = 1; 13156 } else if (*cp == '*') { 13157 wildstar = 1; 13158 } 13159 } 13160 13161 if (ok) { 13162 wv_sts = vms_split_path( 13163 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len, 13164 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len, 13165 &wvs_spec, &wvs_len); 13166 } else { 13167 wn_spec = NULL; 13168 wn_len = 0; 13169 we_spec = NULL; 13170 we_len = 0; 13171 } 13172 13173 sts = SS$_NORMAL; 13174 while (ok && $VMS_STATUS_SUCCESS(sts)) { 13175 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13176 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13177 int valid_find; 13178 13179 valid_find = 0; 13180 sts = lib$find_file(&wilddsc,&rsdsc,&cxt, 13181 &dfltdsc,NULL,&rms_sts,&lff_flags); 13182 if (!$VMS_STATUS_SUCCESS(sts)) 13183 break; 13184 13185 /* with varying string, 1st word of buffer contains result length */ 13186 rstr[rslt->length] = '\0'; 13187 13188 /* Find where all the components are */ 13189 v_sts = vms_split_path 13190 (rstr, 13191 &v_spec, 13192 &v_len, 13193 &r_spec, 13194 &r_len, 13195 &d_spec, 13196 &d_len, 13197 &n_spec, 13198 &n_len, 13199 &e_spec, 13200 &e_len, 13201 &vs_spec, 13202 &vs_len); 13203 13204 /* If no version on input, truncate the version on output */ 13205 if (!hasver && (vs_len > 0)) { 13206 *vs_spec = '\0'; 13207 vs_len = 0; 13208 } 13209 13210 if (isunix) { 13211 13212 /* In Unix report mode, remove the ".dir;1" from the name */ 13213 /* if it is a real directory */ 13214 if (decc_filename_unix_report && decc_efs_charset) { 13215 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 13216 Stat_t statbuf; 13217 int ret_sts; 13218 13219 ret_sts = flex_lstat(rstr, &statbuf); 13220 if ((ret_sts == 0) && 13221 S_ISDIR(statbuf.st_mode)) { 13222 e_len = 0; 13223 e_spec[0] = 0; 13224 } 13225 } 13226 } 13227 13228 /* No version & a null extension on UNIX handling */ 13229 if ((e_len == 1) && decc_readdir_dropdotnotype) { 13230 e_len = 0; 13231 *e_spec = '\0'; 13232 } 13233 } 13234 13235 if (!decc_efs_case_preserve) { 13236 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp); 13237 } 13238 13239 /* Find File treats a Null extension as return all extensions */ 13240 /* This is contrary to Perl expectations */ 13241 13242 if (wildstar || wildquery || vms_old_glob) { 13243 /* really need to see if the returned file name matched */ 13244 /* but for now will assume that it matches */ 13245 valid_find = 1; 13246 } else { 13247 /* Exact Match requested */ 13248 /* How are directories handled? - like a file */ 13249 if ((e_len == we_len) && (n_len == wn_len)) { 13250 int t1; 13251 t1 = e_len; 13252 if (t1 > 0) 13253 t1 = strncmp(e_spec, we_spec, e_len); 13254 if (t1 == 0) { 13255 t1 = n_len; 13256 if (t1 > 0) 13257 t1 = strncmp(n_spec, we_spec, n_len); 13258 if (t1 == 0) 13259 valid_find = 1; 13260 } 13261 } 13262 } 13263 13264 if (valid_find) { 13265 found++; 13266 13267 if (hasdir) { 13268 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1); 13269 begin = rstr; 13270 } 13271 else { 13272 /* Start with the name */ 13273 begin = n_spec; 13274 } 13275 strcat(begin,"\n"); 13276 ok = (PerlIO_puts(tmpfp,begin) != EOF); 13277 } 13278 } 13279 if (cxt) (void)lib$find_file_end(&cxt); 13280 13281 if (!found) { 13282 /* Be POSIXish: return the input pattern when no matches */ 13283 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS); 13284 strcat(rstr,"\n"); 13285 ok = (PerlIO_puts(tmpfp,rstr) != EOF); 13286 } 13287 13288 if (ok && sts != RMS$_NMF && 13289 sts != RMS$_DNF && sts != RMS_FNF) ok = 0; 13290 if (!ok) { 13291 if (!(sts & 1)) { 13292 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts); 13293 } 13294 PerlIO_close(tmpfp); 13295 fp = NULL; 13296 } 13297 else { 13298 PerlIO_rewind(tmpfp); 13299 IoTYPE(io) = IoTYPE_RDONLY; 13300 IoIFP(io) = fp = tmpfp; 13301 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */ 13302 } 13303 } 13304 Safefree(vmsspec); 13305 Safefree(rslt); 13306 return fp; 13307 } 13308 13309 13310 static char * 13311 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, 13312 int *utf8_fl); 13313 13314 void 13315 unixrealpath_fromperl(pTHX_ CV *cv) 13316 { 13317 dXSARGS; 13318 char *fspec, *rslt_spec, *rslt; 13319 STRLEN n_a; 13320 13321 if (!items || items != 1) 13322 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)"); 13323 13324 fspec = SvPV(ST(0),n_a); 13325 if (!fspec || !*fspec) XSRETURN_UNDEF; 13326 13327 Newx(rslt_spec, VMS_MAXRSS + 1, char); 13328 rslt = do_vms_realpath(fspec, rslt_spec, NULL); 13329 13330 ST(0) = sv_newmortal(); 13331 if (rslt != NULL) 13332 sv_usepvn(ST(0),rslt,strlen(rslt)); 13333 else 13334 Safefree(rslt_spec); 13335 XSRETURN(1); 13336 } 13337 13338 static char * 13339 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec, 13340 int *utf8_fl); 13341 13342 void 13343 vmsrealpath_fromperl(pTHX_ CV *cv) 13344 { 13345 dXSARGS; 13346 char *fspec, *rslt_spec, *rslt; 13347 STRLEN n_a; 13348 13349 if (!items || items != 1) 13350 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)"); 13351 13352 fspec = SvPV(ST(0),n_a); 13353 if (!fspec || !*fspec) XSRETURN_UNDEF; 13354 13355 Newx(rslt_spec, VMS_MAXRSS + 1, char); 13356 rslt = do_vms_realname(fspec, rslt_spec, NULL); 13357 13358 ST(0) = sv_newmortal(); 13359 if (rslt != NULL) 13360 sv_usepvn(ST(0),rslt,strlen(rslt)); 13361 else 13362 Safefree(rslt_spec); 13363 XSRETURN(1); 13364 } 13365 13366 #ifdef HAS_SYMLINK 13367 /* 13368 * A thin wrapper around decc$symlink to make sure we follow the 13369 * standard and do not create a symlink with a zero-length name, 13370 * and convert the target to Unix format, as the CRTL can't handle 13371 * targets in VMS format. 13372 */ 13373 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/ 13374 int 13375 Perl_my_symlink(pTHX_ const char *contents, const char *link_name) 13376 { 13377 int sts; 13378 char * utarget; 13379 13380 if (!link_name || !*link_name) { 13381 SETERRNO(ENOENT, SS$_NOSUCHFILE); 13382 return -1; 13383 } 13384 13385 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 13386 /* An untranslatable filename should be passed through. */ 13387 (void) int_tounixspec(contents, utarget, NULL); 13388 sts = symlink(utarget, link_name); 13389 PerlMem_free(utarget); 13390 return sts; 13391 } 13392 /*}}}*/ 13393 13394 #endif /* HAS_SYMLINK */ 13395 13396 int do_vms_case_tolerant(void); 13397 13398 void 13399 case_tolerant_process_fromperl(pTHX_ CV *cv) 13400 { 13401 dXSARGS; 13402 ST(0) = boolSV(do_vms_case_tolerant()); 13403 XSRETURN(1); 13404 } 13405 13406 #ifdef USE_ITHREADS 13407 13408 void 13409 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, 13410 struct interp_intern *dst) 13411 { 13412 PERL_ARGS_ASSERT_SYS_INTERN_DUP; 13413 13414 memcpy(dst,src,sizeof(struct interp_intern)); 13415 } 13416 13417 #endif 13418 13419 void 13420 Perl_sys_intern_clear(pTHX) 13421 { 13422 } 13423 13424 void 13425 Perl_sys_intern_init(pTHX) 13426 { 13427 unsigned int ix = RAND_MAX; 13428 double x; 13429 13430 VMSISH_HUSHED = 0; 13431 13432 MY_POSIX_EXIT = vms_posix_exit; 13433 13434 x = (float)ix; 13435 MY_INV_RAND_MAX = 1./x; 13436 } 13437 13438 void 13439 init_os_extras(void) 13440 { 13441 dTHX; 13442 char* file = __FILE__; 13443 if (decc_disable_to_vms_logname_translation) { 13444 no_translate_barewords = TRUE; 13445 } else { 13446 no_translate_barewords = FALSE; 13447 } 13448 13449 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$"); 13450 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$"); 13451 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$"); 13452 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$"); 13453 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$"); 13454 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$"); 13455 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$"); 13456 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$"); 13457 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$"); 13458 newXS("File::Copy::rmscopy",rmscopy_fromperl,file); 13459 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$"); 13460 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$"); 13461 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$"); 13462 newXSproto("VMS::Filespec::case_tolerant_process", 13463 case_tolerant_process_fromperl,file,""); 13464 13465 store_pipelocs(aTHX); /* will redo any earlier attempts */ 13466 13467 return; 13468 } 13469 13470 #if __CRTL_VER == 80200000 13471 /* This missed getting in to the DECC SDK for 8.2 */ 13472 char *realpath(const char *file_name, char * resolved_name, ...); 13473 #endif 13474 13475 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/ 13476 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK. 13477 * The perl fallback routine to provide realpath() is not as efficient 13478 * on OpenVMS. 13479 */ 13480 13481 #ifdef __cplusplus 13482 extern "C" { 13483 #endif 13484 13485 /* Hack, use old stat() as fastest way of getting ino_t and device */ 13486 int decc$stat(const char *name, void * statbuf); 13487 #if !defined(__VAX) && __CRTL_VER >= 80200000 13488 int decc$lstat(const char *name, void * statbuf); 13489 #else 13490 #define decc$lstat decc$stat 13491 #endif 13492 13493 #ifdef __cplusplus 13494 } 13495 #endif 13496 13497 13498 /* Realpath is fragile. In 8.3 it does not work if the feature 13499 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic 13500 * links are implemented in RMS, not the CRTL. It also can fail if the 13501 * user does not have read/execute access to some of the directories. 13502 * So in order for Do What I Mean mode to work, if realpath() fails, 13503 * fall back to looking up the filename by the device name and FID. 13504 */ 13505 13506 int vms_fid_to_name(char * outname, int outlen, 13507 const char * name, int lstat_flag, mode_t * mode) 13508 { 13509 #pragma message save 13510 #pragma message disable MISALGNDSTRCT 13511 #pragma message disable MISALGNDMEM 13512 #pragma member_alignment save 13513 #pragma nomember_alignment 13514 struct statbuf_t { 13515 char * st_dev; 13516 unsigned short st_ino[3]; 13517 unsigned short old_st_mode; 13518 unsigned long padl[30]; /* plenty of room */ 13519 } statbuf; 13520 #pragma message restore 13521 #pragma member_alignment restore 13522 13523 int sts; 13524 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13525 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13526 char *fileified; 13527 char *temp_fspec; 13528 char *ret_spec; 13529 13530 /* Need to follow the mostly the same rules as flex_stat_int, or we may get 13531 * unexpected answers 13532 */ 13533 13534 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 13535 if (fileified == NULL) 13536 _ckvmssts_noperl(SS$_INSFMEM); 13537 13538 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS); 13539 if (temp_fspec == NULL) 13540 _ckvmssts_noperl(SS$_INSFMEM); 13541 13542 sts = -1; 13543 /* First need to try as a directory */ 13544 ret_spec = int_tovmspath(name, temp_fspec, NULL); 13545 if (ret_spec != NULL) { 13546 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL); 13547 if (ret_spec != NULL) { 13548 if (lstat_flag == 0) 13549 sts = decc$stat(fileified, &statbuf); 13550 else 13551 sts = decc$lstat(fileified, &statbuf); 13552 } 13553 } 13554 13555 /* Then as a VMS file spec */ 13556 if (sts != 0) { 13557 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL); 13558 if (ret_spec != NULL) { 13559 if (lstat_flag == 0) { 13560 sts = decc$stat(temp_fspec, &statbuf); 13561 } else { 13562 sts = decc$lstat(temp_fspec, &statbuf); 13563 } 13564 } 13565 } 13566 13567 if (sts) { 13568 /* Next try - allow multiple dots with out EFS CHARSET */ 13569 /* The CRTL stat() falls down hard on multi-dot filenames in unix 13570 * format unless * DECC$EFS_CHARSET is in effect, so temporarily 13571 * enable it if it isn't already. 13572 */ 13573 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13574 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 13575 decc$feature_set_value(decc_efs_charset_index, 1, 1); 13576 #endif 13577 ret_spec = int_tovmspath(name, temp_fspec, NULL); 13578 if (lstat_flag == 0) { 13579 sts = decc$stat(name, &statbuf); 13580 } else { 13581 sts = decc$lstat(name, &statbuf); 13582 } 13583 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13584 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 13585 decc$feature_set_value(decc_efs_charset_index, 1, 0); 13586 #endif 13587 } 13588 13589 13590 /* and then because the Perl Unix to VMS conversion is not perfect */ 13591 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */ 13592 /* characters from filenames so we need to try it as-is */ 13593 if (sts) { 13594 if (lstat_flag == 0) { 13595 sts = decc$stat(name, &statbuf); 13596 } else { 13597 sts = decc$lstat(name, &statbuf); 13598 } 13599 } 13600 13601 if (sts == 0) { 13602 int vms_sts; 13603 13604 dvidsc.dsc$a_pointer=statbuf.st_dev; 13605 dvidsc.dsc$w_length=strlen(statbuf.st_dev); 13606 13607 specdsc.dsc$a_pointer = outname; 13608 specdsc.dsc$w_length = outlen-1; 13609 13610 vms_sts = lib$fid_to_name 13611 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length); 13612 if ($VMS_STATUS_SUCCESS(vms_sts)) { 13613 outname[specdsc.dsc$w_length] = 0; 13614 13615 /* Return the mode */ 13616 if (mode) { 13617 *mode = statbuf.old_st_mode; 13618 } 13619 } 13620 } 13621 PerlMem_free(temp_fspec); 13622 PerlMem_free(fileified); 13623 return sts; 13624 } 13625 13626 13627 13628 static char * 13629 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, 13630 int *utf8_fl) 13631 { 13632 char * rslt = NULL; 13633 13634 #ifdef HAS_SYMLINK 13635 if (decc_posix_compliant_pathnames > 0 ) { 13636 /* realpath currently only works if posix compliant pathnames are 13637 * enabled. It may start working when they are not, but in that 13638 * case we still want the fallback behavior for backwards compatibility 13639 */ 13640 rslt = realpath(filespec, outbuf); 13641 } 13642 #endif 13643 13644 if (rslt == NULL) { 13645 char * vms_spec; 13646 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13647 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13648 mode_t my_mode; 13649 13650 /* Fall back to fid_to_name */ 13651 13652 Newx(vms_spec, VMS_MAXRSS + 1, char); 13653 13654 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode); 13655 if (sts == 0) { 13656 13657 13658 /* Now need to trim the version off */ 13659 sts = vms_split_path 13660 (vms_spec, 13661 &v_spec, 13662 &v_len, 13663 &r_spec, 13664 &r_len, 13665 &d_spec, 13666 &d_len, 13667 &n_spec, 13668 &n_len, 13669 &e_spec, 13670 &e_len, 13671 &vs_spec, 13672 &vs_len); 13673 13674 13675 if (sts == 0) { 13676 int haslower = 0; 13677 const char *cp; 13678 13679 /* Trim off the version */ 13680 int file_len = v_len + r_len + d_len + n_len + e_len; 13681 vms_spec[file_len] = 0; 13682 13683 /* Trim off the .DIR if this is a directory */ 13684 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 13685 if (S_ISDIR(my_mode)) { 13686 e_len = 0; 13687 e_spec[0] = 0; 13688 } 13689 } 13690 13691 /* Drop NULL extensions on UNIX file specification */ 13692 if ((e_len == 1) && decc_readdir_dropdotnotype) { 13693 e_len = 0; 13694 e_spec[0] = '\0'; 13695 } 13696 13697 /* The result is expected to be in UNIX format */ 13698 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl); 13699 13700 /* Downcase if input had any lower case letters and 13701 * case preservation is not in effect. 13702 */ 13703 if (!decc_efs_case_preserve) { 13704 for (cp = filespec; *cp; cp++) 13705 if (islower(*cp)) { haslower = 1; break; } 13706 13707 if (haslower) __mystrtolower(rslt); 13708 } 13709 } 13710 } else { 13711 13712 /* Now for some hacks to deal with backwards and forward */ 13713 /* compatibility */ 13714 if (!decc_efs_charset) { 13715 13716 /* 1. ODS-2 mode wants to do a syntax only translation */ 13717 rslt = int_rmsexpand(filespec, outbuf, 13718 NULL, 0, NULL, utf8_fl); 13719 13720 } else { 13721 if (decc_filename_unix_report) { 13722 char * dir_name; 13723 char * vms_dir_name; 13724 char * file_name; 13725 13726 /* 2. ODS-5 / UNIX report mode should return a failure */ 13727 /* if the parent directory also does not exist */ 13728 /* Otherwise, get the real path for the parent */ 13729 /* and add the child to it. */ 13730 13731 /* basename / dirname only available for VMS 7.0+ */ 13732 /* So we may need to implement them as common routines */ 13733 13734 Newx(dir_name, VMS_MAXRSS + 1, char); 13735 Newx(vms_dir_name, VMS_MAXRSS + 1, char); 13736 dir_name[0] = '\0'; 13737 file_name = NULL; 13738 13739 /* First try a VMS parse */ 13740 sts = vms_split_path 13741 (filespec, 13742 &v_spec, 13743 &v_len, 13744 &r_spec, 13745 &r_len, 13746 &d_spec, 13747 &d_len, 13748 &n_spec, 13749 &n_len, 13750 &e_spec, 13751 &e_len, 13752 &vs_spec, 13753 &vs_len); 13754 13755 if (sts == 0) { 13756 /* This is VMS */ 13757 13758 int dir_len = v_len + r_len + d_len + n_len; 13759 if (dir_len > 0) { 13760 memcpy(dir_name, filespec, dir_len); 13761 dir_name[dir_len] = '\0'; 13762 file_name = (char *)&filespec[dir_len + 1]; 13763 } 13764 } else { 13765 /* This must be UNIX */ 13766 char * tchar; 13767 13768 tchar = strrchr(filespec, '/'); 13769 13770 if (tchar != NULL) { 13771 int dir_len = tchar - filespec; 13772 memcpy(dir_name, filespec, dir_len); 13773 dir_name[dir_len] = '\0'; 13774 file_name = (char *) &filespec[dir_len + 1]; 13775 } 13776 } 13777 13778 /* Dir name is defaulted */ 13779 if (dir_name[0] == 0) { 13780 dir_name[0] = '.'; 13781 dir_name[1] = '\0'; 13782 } 13783 13784 /* Need realpath for the directory */ 13785 sts = vms_fid_to_name(vms_dir_name, 13786 VMS_MAXRSS + 1, 13787 dir_name, 0, NULL); 13788 13789 if (sts == 0) { 13790 /* Now need to pathify it. */ 13791 char *tdir = int_pathify_dirspec(vms_dir_name, 13792 outbuf); 13793 13794 /* And now add the original filespec to it */ 13795 if (file_name != NULL) { 13796 my_strlcat(outbuf, file_name, VMS_MAXRSS); 13797 } 13798 return outbuf; 13799 } 13800 Safefree(vms_dir_name); 13801 Safefree(dir_name); 13802 } 13803 } 13804 } 13805 Safefree(vms_spec); 13806 } 13807 return rslt; 13808 } 13809 13810 static char * 13811 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf, 13812 int *utf8_fl) 13813 { 13814 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13815 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13816 13817 /* Fall back to fid_to_name */ 13818 13819 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL); 13820 if (sts != 0) { 13821 return NULL; 13822 } 13823 else { 13824 13825 13826 /* Now need to trim the version off */ 13827 sts = vms_split_path 13828 (outbuf, 13829 &v_spec, 13830 &v_len, 13831 &r_spec, 13832 &r_len, 13833 &d_spec, 13834 &d_len, 13835 &n_spec, 13836 &n_len, 13837 &e_spec, 13838 &e_len, 13839 &vs_spec, 13840 &vs_len); 13841 13842 13843 if (sts == 0) { 13844 int haslower = 0; 13845 const char *cp; 13846 13847 /* Trim off the version */ 13848 int file_len = v_len + r_len + d_len + n_len + e_len; 13849 outbuf[file_len] = 0; 13850 13851 /* Downcase if input had any lower case letters and 13852 * case preservation is not in effect. 13853 */ 13854 if (!decc_efs_case_preserve) { 13855 for (cp = filespec; *cp; cp++) 13856 if (islower(*cp)) { haslower = 1; break; } 13857 13858 if (haslower) __mystrtolower(outbuf); 13859 } 13860 } 13861 } 13862 return outbuf; 13863 } 13864 13865 13866 /*}}}*/ 13867 /* External entry points */ 13868 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13869 { return do_vms_realpath(filespec, outbuf, utf8_fl); } 13870 13871 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13872 { return do_vms_realname(filespec, outbuf, utf8_fl); } 13873 13874 /* case_tolerant */ 13875 13876 /*{{{int do_vms_case_tolerant(void)*/ 13877 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is 13878 * controlled by a process setting. 13879 */ 13880 int do_vms_case_tolerant(void) 13881 { 13882 return vms_process_case_tolerant; 13883 } 13884 /*}}}*/ 13885 /* External entry points */ 13886 #if __CRTL_VER >= 70301000 && !defined(__VAX) 13887 int Perl_vms_case_tolerant(void) 13888 { return do_vms_case_tolerant(); } 13889 #else 13890 int Perl_vms_case_tolerant(void) 13891 { return vms_process_case_tolerant; } 13892 #endif 13893 13894 13895 /* Start of DECC RTL Feature handling */ 13896 13897 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13898 13899 static int 13900 set_feature_default(const char *name, int value) 13901 { 13902 int status; 13903 int index; 13904 char val_str[10]; 13905 13906 /* If the feature has been explicitly disabled in the environment, 13907 * then don't enable it here. 13908 */ 13909 if (value > 0) { 13910 status = simple_trnlnm(name, val_str, sizeof(val_str)); 13911 if ($VMS_STATUS_SUCCESS(status)) { 13912 val_str[0] = _toupper(val_str[0]); 13913 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F') 13914 return 0; 13915 } 13916 } 13917 13918 index = decc$feature_get_index(name); 13919 13920 status = decc$feature_set_value(index, 1, value); 13921 if (index == -1 || (status == -1)) { 13922 return -1; 13923 } 13924 13925 status = decc$feature_get_value(index, 1); 13926 if (status != value) { 13927 return -1; 13928 } 13929 13930 /* Various things may check for an environment setting 13931 * rather than the feature directly, so set that too. 13932 */ 13933 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE"); 13934 13935 return 0; 13936 } 13937 #endif 13938 13939 13940 /* C RTL Feature settings */ 13941 13942 #if defined(__DECC) || defined(__DECCXX) 13943 13944 #ifdef __cplusplus 13945 extern "C" { 13946 #endif 13947 13948 extern void 13949 vmsperl_set_features(void) 13950 { 13951 int status; 13952 int s; 13953 char val_str[10]; 13954 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX) 13955 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM; 13956 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE; 13957 unsigned long case_perm; 13958 unsigned long case_image; 13959 #endif 13960 13961 /* Allow an exception to bring Perl into the VMS debugger */ 13962 vms_debug_on_exception = 0; 13963 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str)); 13964 if ($VMS_STATUS_SUCCESS(status)) { 13965 val_str[0] = _toupper(val_str[0]); 13966 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13967 vms_debug_on_exception = 1; 13968 else 13969 vms_debug_on_exception = 0; 13970 } 13971 13972 /* Debug unix/vms file translation routines */ 13973 vms_debug_fileify = 0; 13974 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str)); 13975 if ($VMS_STATUS_SUCCESS(status)) { 13976 val_str[0] = _toupper(val_str[0]); 13977 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13978 vms_debug_fileify = 1; 13979 else 13980 vms_debug_fileify = 0; 13981 } 13982 13983 13984 /* Historically PERL has been doing vmsify / stat differently than */ 13985 /* the CRTL. In particular, under some conditions the CRTL will */ 13986 /* remove some illegal characters like spaces from filenames */ 13987 /* resulting in some differences. The stat()/lstat() wrapper has */ 13988 /* been reporting such file names as invalid and fails to stat them */ 13989 /* fixing this bug so that stat()/lstat() accept these like the */ 13990 /* CRTL does will result in several tests failing. */ 13991 /* This should really be fixed, but for now, set up a feature to */ 13992 /* enable it so that the impact can be studied. */ 13993 vms_bug_stat_filename = 0; 13994 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str)); 13995 if ($VMS_STATUS_SUCCESS(status)) { 13996 val_str[0] = _toupper(val_str[0]); 13997 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13998 vms_bug_stat_filename = 1; 13999 else 14000 vms_bug_stat_filename = 0; 14001 } 14002 14003 14004 /* Create VTF-7 filenames from Unicode instead of UTF-8 */ 14005 vms_vtf7_filenames = 0; 14006 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str)); 14007 if ($VMS_STATUS_SUCCESS(status)) { 14008 val_str[0] = _toupper(val_str[0]); 14009 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14010 vms_vtf7_filenames = 1; 14011 else 14012 vms_vtf7_filenames = 0; 14013 } 14014 14015 /* unlink all versions on unlink() or rename() */ 14016 vms_unlink_all_versions = 0; 14017 status = simple_trnlnm 14018 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str)); 14019 if ($VMS_STATUS_SUCCESS(status)) { 14020 val_str[0] = _toupper(val_str[0]); 14021 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14022 vms_unlink_all_versions = 1; 14023 else 14024 vms_unlink_all_versions = 0; 14025 } 14026 14027 #if __CRTL_VER >= 70300000 && !defined(__VAX) 14028 /* Detect running under GNV Bash or other UNIX like shell */ 14029 gnv_unix_shell = 0; 14030 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str)); 14031 if ($VMS_STATUS_SUCCESS(status)) { 14032 gnv_unix_shell = 1; 14033 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1); 14034 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1); 14035 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1); 14036 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0); 14037 vms_unlink_all_versions = 1; 14038 vms_posix_exit = 1; 14039 } 14040 /* Some reasonable defaults that are not CRTL defaults */ 14041 set_feature_default("DECC$EFS_CASE_PRESERVE", 1); 14042 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */ 14043 set_feature_default("DECC$EFS_CHARSET", 1); 14044 #endif 14045 14046 /* hacks to see if known bugs are still present for testing */ 14047 14048 /* PCP mode requires creating /dev/null special device file */ 14049 decc_bug_devnull = 0; 14050 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str)); 14051 if ($VMS_STATUS_SUCCESS(status)) { 14052 val_str[0] = _toupper(val_str[0]); 14053 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14054 decc_bug_devnull = 1; 14055 else 14056 decc_bug_devnull = 0; 14057 } 14058 14059 #if __CRTL_VER >= 70300000 && !defined(__VAX) 14060 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION"); 14061 if (s >= 0) { 14062 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1); 14063 if (decc_disable_to_vms_logname_translation < 0) 14064 decc_disable_to_vms_logname_translation = 0; 14065 } 14066 14067 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE"); 14068 if (s >= 0) { 14069 decc_efs_case_preserve = decc$feature_get_value(s, 1); 14070 if (decc_efs_case_preserve < 0) 14071 decc_efs_case_preserve = 0; 14072 } 14073 14074 s = decc$feature_get_index("DECC$EFS_CHARSET"); 14075 decc_efs_charset_index = s; 14076 if (s >= 0) { 14077 decc_efs_charset = decc$feature_get_value(s, 1); 14078 if (decc_efs_charset < 0) 14079 decc_efs_charset = 0; 14080 } 14081 14082 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT"); 14083 if (s >= 0) { 14084 decc_filename_unix_report = decc$feature_get_value(s, 1); 14085 if (decc_filename_unix_report > 0) { 14086 decc_filename_unix_report = 1; 14087 vms_posix_exit = 1; 14088 } 14089 else 14090 decc_filename_unix_report = 0; 14091 } 14092 14093 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY"); 14094 if (s >= 0) { 14095 decc_filename_unix_only = decc$feature_get_value(s, 1); 14096 if (decc_filename_unix_only > 0) { 14097 decc_filename_unix_only = 1; 14098 } 14099 else { 14100 decc_filename_unix_only = 0; 14101 } 14102 } 14103 14104 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION"); 14105 if (s >= 0) { 14106 decc_filename_unix_no_version = decc$feature_get_value(s, 1); 14107 if (decc_filename_unix_no_version < 0) 14108 decc_filename_unix_no_version = 0; 14109 } 14110 14111 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE"); 14112 if (s >= 0) { 14113 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1); 14114 if (decc_readdir_dropdotnotype < 0) 14115 decc_readdir_dropdotnotype = 0; 14116 } 14117 14118 #if __CRTL_VER >= 80200000 14119 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES"); 14120 if (s >= 0) { 14121 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1); 14122 if (decc_posix_compliant_pathnames < 0) 14123 decc_posix_compliant_pathnames = 0; 14124 if (decc_posix_compliant_pathnames > 4) 14125 decc_posix_compliant_pathnames = 0; 14126 } 14127 14128 #endif 14129 #else 14130 status = simple_trnlnm 14131 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str)); 14132 if ($VMS_STATUS_SUCCESS(status)) { 14133 val_str[0] = _toupper(val_str[0]); 14134 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14135 decc_disable_to_vms_logname_translation = 1; 14136 } 14137 } 14138 14139 #ifndef __VAX 14140 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str)); 14141 if ($VMS_STATUS_SUCCESS(status)) { 14142 val_str[0] = _toupper(val_str[0]); 14143 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14144 decc_efs_case_preserve = 1; 14145 } 14146 } 14147 #endif 14148 14149 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str)); 14150 if ($VMS_STATUS_SUCCESS(status)) { 14151 val_str[0] = _toupper(val_str[0]); 14152 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14153 decc_filename_unix_report = 1; 14154 } 14155 } 14156 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str)); 14157 if ($VMS_STATUS_SUCCESS(status)) { 14158 val_str[0] = _toupper(val_str[0]); 14159 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14160 decc_filename_unix_only = 1; 14161 decc_filename_unix_report = 1; 14162 } 14163 } 14164 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str)); 14165 if ($VMS_STATUS_SUCCESS(status)) { 14166 val_str[0] = _toupper(val_str[0]); 14167 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14168 decc_filename_unix_no_version = 1; 14169 } 14170 } 14171 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str)); 14172 if ($VMS_STATUS_SUCCESS(status)) { 14173 val_str[0] = _toupper(val_str[0]); 14174 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14175 decc_readdir_dropdotnotype = 1; 14176 } 14177 } 14178 #endif 14179 14180 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX) 14181 14182 /* Report true case tolerance */ 14183 /*----------------------------*/ 14184 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0); 14185 if (!$VMS_STATUS_SUCCESS(status)) 14186 case_perm = PPROP$K_CASE_BLIND; 14187 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0); 14188 if (!$VMS_STATUS_SUCCESS(status)) 14189 case_image = PPROP$K_CASE_BLIND; 14190 if ((case_perm == PPROP$K_CASE_SENSITIVE) || 14191 (case_image == PPROP$K_CASE_SENSITIVE)) 14192 vms_process_case_tolerant = 0; 14193 14194 #endif 14195 14196 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */ 14197 /* for strict backward compatibility */ 14198 status = simple_trnlnm 14199 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str)); 14200 if ($VMS_STATUS_SUCCESS(status)) { 14201 val_str[0] = _toupper(val_str[0]); 14202 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14203 vms_posix_exit = 1; 14204 else 14205 vms_posix_exit = 0; 14206 } 14207 } 14208 14209 /* Use 32-bit pointers because that's what the image activator 14210 * assumes for the LIB$INITIALZE psect. 14211 */ 14212 #if __INITIAL_POINTER_SIZE 14213 #pragma pointer_size save 14214 #pragma pointer_size 32 14215 #endif 14216 14217 /* Create a reference to the LIB$INITIALIZE function. */ 14218 extern void LIB$INITIALIZE(void); 14219 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE; 14220 14221 /* Create an array of pointers to the init functions in the special 14222 * LIB$INITIALIZE section. In our case, the array only has one entry. 14223 */ 14224 #pragma extern_model save 14225 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long 14226 extern void (* const vmsperl_unused_global_2[])() = 14227 { 14228 vmsperl_set_features, 14229 }; 14230 #pragma extern_model restore 14231 14232 #if __INITIAL_POINTER_SIZE 14233 #pragma pointer_size restore 14234 #endif 14235 14236 #ifdef __cplusplus 14237 } 14238 #endif 14239 14240 #endif /* defined(__DECC) || defined(__DECCXX) */ 14241 /* End of vms.c */ 14242