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 /* Routine to remove the 2-byte prefix from the translation of a 882 * process-permanent file (PPF). 883 */ 884 static inline unsigned short int 885 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen) 886 { 887 if (*((int *)lnm) == *((int *)"SYS$") && 888 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 && 889 ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT")) || 890 (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT")) || 891 (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR")) || 892 (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) ) ) { 893 894 memmove(eqv, eqv+4, eqvlen-4); 895 eqvlen -= 4; 896 } 897 return eqvlen; 898 } 899 900 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */ 901 int 902 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, 903 struct dsc$descriptor_s **tabvec, unsigned long int flags) 904 { 905 const char *cp1; 906 char uplnm[LNM$C_NAMLENGTH+1], *cp2; 907 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure; 908 unsigned long int retsts, attr = LNM$M_CASE_BLIND; 909 int midx; 910 unsigned char acmode; 911 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 912 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 913 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0}, 914 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen}, 915 {0, 0, 0, 0}}; 916 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 917 #if defined(PERL_IMPLICIT_CONTEXT) 918 pTHX = NULL; 919 if (PL_curinterp) { 920 aTHX = PERL_GET_INTERP; 921 } else { 922 aTHX = NULL; 923 } 924 #endif 925 926 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) { 927 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0; 928 } 929 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 930 *cp2 = _toupper(*cp1); 931 if (cp1 - lnm > LNM$C_NAMLENGTH) { 932 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 933 return 0; 934 } 935 } 936 lnmdsc.dsc$w_length = cp1 - lnm; 937 lnmdsc.dsc$a_pointer = uplnm; 938 uplnm[lnmdsc.dsc$w_length] = '\0'; 939 secure = flags & PERL__TRNENV_SECURE; 940 acmode = secure ? PSL$C_EXEC : PSL$C_USER; 941 if (!tabvec || !*tabvec) tabvec = env_tables; 942 943 for (curtab = 0; tabvec[curtab]; curtab++) { 944 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) { 945 if (!ivenv && !secure) { 946 char *eq; 947 int i; 948 if (!environ) { 949 ivenv = 1; 950 #if defined(PERL_IMPLICIT_CONTEXT) 951 if (aTHX == NULL) { 952 fprintf(stderr, 953 "Can't read CRTL environ\n"); 954 } else 955 #endif 956 Perl_warn(aTHX_ "Can't read CRTL environ\n"); 957 continue; 958 } 959 retsts = SS$_NOLOGNAM; 960 for (i = 0; environ[i]; i++) { 961 if ((eq = strchr(environ[i],'=')) && 962 lnmdsc.dsc$w_length == (eq - environ[i]) && 963 !strncmp(environ[i],uplnm,eq - environ[i])) { 964 eq++; 965 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen]; 966 if (!eqvlen) continue; 967 retsts = SS$_NORMAL; 968 break; 969 } 970 } 971 if (retsts != SS$_NOLOGNAM) break; 972 } 973 } 974 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 975 !str$case_blind_compare(&tmpdsc,&clisym)) { 976 if (!ivsym && !secure) { 977 unsigned short int deflen = LNM$C_NAMLENGTH; 978 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 979 /* dynamic dsc to accommodate possible long value */ 980 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc)); 981 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0); 982 if (retsts & 1) { 983 if (eqvlen > MAX_DCL_SYMBOL) { 984 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU); 985 eqvlen = MAX_DCL_SYMBOL; 986 /* Special hack--we might be called before the interpreter's */ 987 /* fully initialized, in which case either thr or PL_curcop */ 988 /* might be bogus. We have to check, since ckWARN needs them */ 989 /* both to be valid if running threaded */ 990 #if defined(PERL_IMPLICIT_CONTEXT) 991 if (aTHX == NULL) { 992 fprintf(stderr, 993 "Value of CLI symbol \"%s\" too long",lnm); 994 } else 995 #endif 996 if (ckWARN(WARN_MISC)) { 997 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm); 998 } 999 } 1000 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen); 1001 } 1002 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc)); 1003 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 1004 if (retsts == LIB$_NOSUCHSYM) continue; 1005 break; 1006 } 1007 } 1008 else if (!ivlnm) { 1009 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) { 1010 midx = my_maxidx(lnm); 1011 for (idx = 0, cp2 = eqv; idx <= midx; idx++) { 1012 lnmlst[1].bufadr = cp2; 1013 eqvlen = 0; 1014 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 1015 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; } 1016 if (retsts == SS$_NOLOGNAM) break; 1017 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen); 1018 cp2 += eqvlen; 1019 *cp2 = '\0'; 1020 } 1021 if ((retsts == SS$_IVLOGNAM) || 1022 (retsts == SS$_NOLOGNAM)) { continue; } 1023 eqvlen = strlen(eqv); 1024 } 1025 else { 1026 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 1027 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1028 if (retsts == SS$_NOLOGNAM) continue; 1029 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen); 1030 eqv[eqvlen] = '\0'; 1031 } 1032 break; 1033 } 1034 } 1035 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; } 1036 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM || 1037 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB || 1038 retsts == SS$_NOLOGNAM) { 1039 set_errno(EINVAL); set_vaxc_errno(retsts); 1040 } 1041 else _ckvmssts_noperl(retsts); 1042 return 0; 1043 } /* end of vmstrnenv */ 1044 /*}}}*/ 1045 1046 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/ 1047 /* Define as a function so we can access statics. */ 1048 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx) 1049 { 1050 int flags = 0; 1051 1052 #if defined(PERL_IMPLICIT_CONTEXT) 1053 if (aTHX != NULL) 1054 #endif 1055 #ifdef SECURE_INTERNAL_GETENV 1056 flags = (PL_curinterp ? TAINTING_get : will_taint) ? 1057 PERL__TRNENV_SECURE : 0; 1058 #endif 1059 1060 return vmstrnenv(lnm, eqv, idx, fildev, flags); 1061 } 1062 /*}}}*/ 1063 1064 /* my_getenv 1065 * Note: Uses Perl temp to store result so char * can be returned to 1066 * caller; this pointer will be invalidated at next Perl statement 1067 * transition. 1068 * We define this as a function rather than a macro in terms of my_getenv_len() 1069 * so that it'll work when PL_curinterp is undefined (and we therefore can't 1070 * allocate SVs). 1071 */ 1072 /*{{{ char *my_getenv(const char *lnm, bool sys)*/ 1073 char * 1074 Perl_my_getenv(pTHX_ const char *lnm, bool sys) 1075 { 1076 const char *cp1; 1077 static char *__my_getenv_eqv = NULL; 1078 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv; 1079 unsigned long int idx = 0; 1080 int success, secure, saverr, savvmserr; 1081 int midx, flags; 1082 SV *tmpsv; 1083 1084 midx = my_maxidx(lnm) + 1; 1085 1086 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1087 /* Set up a temporary buffer for the return value; Perl will 1088 * clean it up at the next statement transition */ 1089 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1090 if (!tmpsv) return NULL; 1091 eqv = SvPVX(tmpsv); 1092 } 1093 else { 1094 /* Assume no interpreter ==> single thread */ 1095 if (__my_getenv_eqv != NULL) { 1096 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1097 } 1098 else { 1099 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1100 } 1101 eqv = __my_getenv_eqv; 1102 } 1103 1104 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1105 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) { 1106 int len; 1107 getcwd(eqv,LNM$C_NAMLENGTH); 1108 1109 len = strlen(eqv); 1110 1111 /* Get rid of "000000/ in rooted filespecs */ 1112 if (len > 7) { 1113 char * zeros; 1114 zeros = strstr(eqv, "/000000/"); 1115 if (zeros != NULL) { 1116 int mlen; 1117 mlen = len - (zeros - eqv) - 7; 1118 memmove(zeros, &zeros[7], mlen); 1119 len = len - 7; 1120 eqv[len] = '\0'; 1121 } 1122 } 1123 return eqv; 1124 } 1125 else { 1126 /* Impose security constraints only if tainting */ 1127 if (sys) { 1128 /* Impose security constraints only if tainting */ 1129 secure = PL_curinterp ? TAINTING_get : will_taint; 1130 saverr = errno; savvmserr = vaxc$errno; 1131 } 1132 else { 1133 secure = 0; 1134 } 1135 1136 flags = 1137 #ifdef SECURE_INTERNAL_GETENV 1138 secure ? PERL__TRNENV_SECURE : 0 1139 #else 1140 0 1141 #endif 1142 ; 1143 1144 /* For the getenv interface we combine all the equivalence names 1145 * of a search list logical into one value to acquire a maximum 1146 * value length of 255*128 (assuming %ENV is using logicals). 1147 */ 1148 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1149 1150 /* If the name contains a semicolon-delimited index, parse it 1151 * off and make sure we only retrieve the equivalence name for 1152 * that index. */ 1153 if ((cp2 = strchr(lnm,';')) != NULL) { 1154 my_strlcpy(uplnm, lnm, cp2 - lnm + 1); 1155 idx = strtoul(cp2+1,NULL,0); 1156 lnm = uplnm; 1157 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1158 } 1159 1160 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags); 1161 1162 /* Discard NOLOGNAM on internal calls since we're often looking 1163 * for an optional name, and this "error" often shows up as the 1164 * (bogus) exit status for a die() call later on. */ 1165 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr); 1166 return success ? eqv : NULL; 1167 } 1168 1169 } /* end of my_getenv() */ 1170 /*}}}*/ 1171 1172 1173 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/ 1174 char * 1175 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys) 1176 { 1177 const char *cp1; 1178 char *buf, *cp2; 1179 unsigned long idx = 0; 1180 int midx, flags; 1181 static char *__my_getenv_len_eqv = NULL; 1182 int secure, saverr, savvmserr; 1183 SV *tmpsv; 1184 1185 midx = my_maxidx(lnm) + 1; 1186 1187 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1188 /* Set up a temporary buffer for the return value; Perl will 1189 * clean it up at the next statement transition */ 1190 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1191 if (!tmpsv) return NULL; 1192 buf = SvPVX(tmpsv); 1193 } 1194 else { 1195 /* Assume no interpreter ==> single thread */ 1196 if (__my_getenv_len_eqv != NULL) { 1197 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1198 } 1199 else { 1200 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1201 } 1202 buf = __my_getenv_len_eqv; 1203 } 1204 1205 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1206 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) { 1207 char * zeros; 1208 1209 getcwd(buf,LNM$C_NAMLENGTH); 1210 *len = strlen(buf); 1211 1212 /* Get rid of "000000/ in rooted filespecs */ 1213 if (*len > 7) { 1214 zeros = strstr(buf, "/000000/"); 1215 if (zeros != NULL) { 1216 int mlen; 1217 mlen = *len - (zeros - buf) - 7; 1218 memmove(zeros, &zeros[7], mlen); 1219 *len = *len - 7; 1220 buf[*len] = '\0'; 1221 } 1222 } 1223 return buf; 1224 } 1225 else { 1226 if (sys) { 1227 /* Impose security constraints only if tainting */ 1228 secure = PL_curinterp ? TAINTING_get : will_taint; 1229 saverr = errno; savvmserr = vaxc$errno; 1230 } 1231 else { 1232 secure = 0; 1233 } 1234 1235 flags = 1236 #ifdef SECURE_INTERNAL_GETENV 1237 secure ? PERL__TRNENV_SECURE : 0 1238 #else 1239 0 1240 #endif 1241 ; 1242 1243 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1244 1245 if ((cp2 = strchr(lnm,';')) != NULL) { 1246 my_strlcpy(buf, lnm, cp2 - lnm + 1); 1247 idx = strtoul(cp2+1,NULL,0); 1248 lnm = buf; 1249 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1250 } 1251 1252 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags); 1253 1254 /* Get rid of "000000/ in rooted filespecs */ 1255 if (*len > 7) { 1256 char * zeros; 1257 zeros = strstr(buf, "/000000/"); 1258 if (zeros != NULL) { 1259 int mlen; 1260 mlen = *len - (zeros - buf) - 7; 1261 memmove(zeros, &zeros[7], mlen); 1262 *len = *len - 7; 1263 buf[*len] = '\0'; 1264 } 1265 } 1266 1267 /* Discard NOLOGNAM on internal calls since we're often looking 1268 * for an optional name, and this "error" often shows up as the 1269 * (bogus) exit status for a die() call later on. */ 1270 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr); 1271 return *len ? buf : NULL; 1272 } 1273 1274 } /* end of my_getenv_len() */ 1275 /*}}}*/ 1276 1277 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *); 1278 1279 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); } 1280 1281 /*{{{ void prime_env_iter() */ 1282 void 1283 prime_env_iter(void) 1284 /* Fill the %ENV associative array with all logical names we can 1285 * find, in preparation for iterating over it. 1286 */ 1287 { 1288 static int primed = 0; 1289 HV *seenhv = NULL, *envhv; 1290 SV *sv = NULL; 1291 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL; 1292 unsigned short int chan; 1293 #ifndef CLI$M_TRUSTED 1294 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */ 1295 #endif 1296 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED; 1297 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0; 1298 long int i; 1299 bool have_sym = FALSE, have_lnm = FALSE; 1300 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1301 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:"); 1302 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES"); 1303 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1304 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 1305 #if defined(PERL_IMPLICIT_CONTEXT) 1306 pTHX; 1307 #endif 1308 #if defined(USE_ITHREADS) 1309 static perl_mutex primenv_mutex; 1310 MUTEX_INIT(&primenv_mutex); 1311 #endif 1312 1313 #if defined(PERL_IMPLICIT_CONTEXT) 1314 /* We jump through these hoops because we can be called at */ 1315 /* platform-specific initialization time, which is before anything is */ 1316 /* set up--we can't even do a plain dTHX since that relies on the */ 1317 /* interpreter structure to be initialized */ 1318 if (PL_curinterp) { 1319 aTHX = PERL_GET_INTERP; 1320 } else { 1321 /* we never get here because the NULL pointer will cause the */ 1322 /* several of the routines called by this routine to access violate */ 1323 1324 /* This routine is only called by hv.c/hv_iterinit which has a */ 1325 /* context, so the real fix may be to pass it through instead of */ 1326 /* the hoops above */ 1327 aTHX = NULL; 1328 } 1329 #endif 1330 1331 if (primed || !PL_envgv) return; 1332 MUTEX_LOCK(&primenv_mutex); 1333 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; } 1334 envhv = GvHVn(PL_envgv); 1335 /* Perform a dummy fetch as an lval to insure that the hash table is 1336 * set up. Otherwise, the hv_store() will turn into a nullop. */ 1337 (void) hv_fetch(envhv,"DEFAULT",7,TRUE); 1338 1339 for (i = 0; env_tables[i]; i++) { 1340 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1341 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1; 1342 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1; 1343 } 1344 if (have_sym || have_lnm) { 1345 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM; 1346 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0)); 1347 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0)); 1348 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length)); 1349 } 1350 1351 for (i--; i >= 0; i--) { 1352 if (!str$case_blind_compare(env_tables[i],&crtlenv)) { 1353 char *start; 1354 int j; 1355 /* Start at the end, so if there is a duplicate we keep the first one. */ 1356 for (j = 0; environ[j]; j++); 1357 for (j--; j >= 0; j--) { 1358 if (!(start = strchr(environ[j],'='))) { 1359 if (ckWARN(WARN_INTERNAL)) 1360 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]); 1361 } 1362 else { 1363 start++; 1364 sv = newSVpv(start,0); 1365 SvTAINTED_on(sv); 1366 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0); 1367 } 1368 } 1369 continue; 1370 } 1371 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1372 !str$case_blind_compare(&tmpdsc,&clisym)) { 1373 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd)); 1374 cmddsc.dsc$w_length = 20; 1375 if (env_tables[i]->dsc$w_length == 12 && 1376 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) && 1377 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12); 1378 flags = defflags | CLI$M_NOLOGNAM; 1379 } 1380 else { 1381 my_strlcpy(cmd, "Show Logical *", sizeof(cmd)); 1382 if (str$case_blind_compare(env_tables[i],&fildevdsc)) { 1383 my_strlcat(cmd," /Table=", sizeof(cmd)); 1384 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd)); 1385 } 1386 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */ 1387 flags = defflags | CLI$M_NOCLISYM; 1388 } 1389 1390 /* Create a new subprocess to execute each command, to exclude the 1391 * remote possibility that someone could subvert a mbx or file used 1392 * to write multiple commands to a single subprocess. 1393 */ 1394 do { 1395 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs, 1396 0,&riseandshine,0,0,&clidsc,&clitabdsc); 1397 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */ 1398 defflags &= ~CLI$M_TRUSTED; 1399 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED)); 1400 _ckvmssts(retsts); 1401 if (!buf) Newx(buf,mbxbufsiz + 1,char); 1402 if (seenhv) SvREFCNT_dec(seenhv); 1403 seenhv = newHV(); 1404 while (1) { 1405 char *cp1, *cp2, *key; 1406 unsigned long int sts, iosb[2], retlen, keylen; 1407 U32 hash; 1408 1409 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0); 1410 if (sts & 1) sts = iosb[0] & 0xffff; 1411 if (sts == SS$_ENDOFFILE) { 1412 int wakect = 0; 1413 while (substs == 0) { sys$hiber(); wakect++;} 1414 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */ 1415 _ckvmssts(substs); 1416 break; 1417 } 1418 _ckvmssts(sts); 1419 retlen = iosb[0] >> 16; 1420 if (!retlen) continue; /* blank line */ 1421 buf[retlen] = '\0'; 1422 if (iosb[1] != subpid) { 1423 if (iosb[1]) { 1424 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf); 1425 } 1426 continue; 1427 } 1428 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL)) 1429 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf); 1430 1431 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ; 1432 if (*cp1 == '(' || /* Logical name table name */ 1433 *cp1 == '=' /* Next eqv of searchlist */) continue; 1434 if (*cp1 == '"') cp1++; 1435 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ; 1436 key = cp1; keylen = cp2 - cp1; 1437 if (keylen && hv_exists(seenhv,key,keylen)) continue; 1438 while (*cp2 && *cp2 != '=') cp2++; 1439 while (*cp2 && *cp2 == '=') cp2++; 1440 while (*cp2 && *cp2 == ' ') cp2++; 1441 if (*cp2 == '"') { /* String translation; may embed "" */ 1442 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ; 1443 cp2++; cp1--; /* Skip "" surrounding translation */ 1444 } 1445 else { /* Numeric translation */ 1446 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ; 1447 cp1--; /* stop on last non-space char */ 1448 } 1449 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) { 1450 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf); 1451 continue; 1452 } 1453 PERL_HASH(hash,key,keylen); 1454 1455 if (cp1 == cp2 && *cp2 == '.') { 1456 /* A single dot usually means an unprintable character, such as a null 1457 * to indicate a zero-length value. Get the actual value to make sure. 1458 */ 1459 char lnm[LNM$C_NAMLENGTH+1]; 1460 char eqv[MAX_DCL_SYMBOL+1]; 1461 int trnlen; 1462 strncpy(lnm, key, keylen); 1463 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0); 1464 sv = newSVpvn(eqv, strlen(eqv)); 1465 } 1466 else { 1467 sv = newSVpvn(cp2,cp1 - cp2 + 1); 1468 } 1469 1470 SvTAINTED_on(sv); 1471 hv_store(envhv,key,keylen,sv,hash); 1472 hv_store(seenhv,key,keylen,&PL_sv_yes,hash); 1473 } 1474 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */ 1475 /* get the PPFs for this process, not the subprocess */ 1476 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL}; 1477 char eqv[LNM$C_NAMLENGTH+1]; 1478 int trnlen, i; 1479 for (i = 0; ppfs[i]; i++) { 1480 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0); 1481 sv = newSVpv(eqv,trnlen); 1482 SvTAINTED_on(sv); 1483 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0); 1484 } 1485 } 1486 } 1487 primed = 1; 1488 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan)); 1489 if (buf) Safefree(buf); 1490 if (seenhv) SvREFCNT_dec(seenhv); 1491 MUTEX_UNLOCK(&primenv_mutex); 1492 return; 1493 1494 } /* end of prime_env_iter */ 1495 /*}}}*/ 1496 1497 1498 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/ 1499 /* Define or delete an element in the same "environment" as 1500 * vmstrnenv(). If an element is to be deleted, it's removed from 1501 * the first place it's found. If it's to be set, it's set in the 1502 * place designated by the first element of the table vector. 1503 * Like setenv() returns 0 for success, non-zero on error. 1504 */ 1505 int 1506 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec) 1507 { 1508 const char *cp1; 1509 char uplnm[LNM$C_NAMLENGTH], *cp2, *c; 1510 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0; 1511 int nseg = 0, j; 1512 unsigned long int retsts, usermode = PSL$C_USER; 1513 struct itmlst_3 *ile, *ilist; 1514 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm}, 1515 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 1516 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1517 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1518 $DESCRIPTOR(local,"_LOCAL"); 1519 1520 if (!lnm) { 1521 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1522 return SS$_IVLOGNAM; 1523 } 1524 1525 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 1526 *cp2 = _toupper(*cp1); 1527 if (cp1 - lnm > LNM$C_NAMLENGTH) { 1528 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1529 return SS$_IVLOGNAM; 1530 } 1531 } 1532 lnmdsc.dsc$w_length = cp1 - lnm; 1533 if (!tabvec || !*tabvec) tabvec = env_tables; 1534 1535 if (!eqv) { /* we're deleting n element */ 1536 for (curtab = 0; tabvec[curtab]; curtab++) { 1537 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) { 1538 int i; 1539 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */ 1540 if ((cp1 = strchr(environ[i],'=')) && 1541 lnmdsc.dsc$w_length == (cp1 - environ[i]) && 1542 !strncmp(environ[i],lnm,cp1 - environ[i])) { 1543 #ifdef HAS_SETENV 1544 return setenv(lnm,"",1) ? vaxc$errno : 0; 1545 } 1546 } 1547 ivenv = 1; retsts = SS$_NOLOGNAM; 1548 #else 1549 if (ckWARN(WARN_INTERNAL)) 1550 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm); 1551 ivenv = 1; retsts = SS$_NOSUCHPGM; 1552 break; 1553 } 1554 } 1555 #endif 1556 } 1557 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 1558 !str$case_blind_compare(&tmpdsc,&clisym)) { 1559 unsigned int symtype; 1560 if (tabvec[curtab]->dsc$w_length == 12 && 1561 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) && 1562 !str$case_blind_compare(&tmpdsc,&local)) 1563 symtype = LIB$K_CLI_LOCAL_SYM; 1564 else symtype = LIB$K_CLI_GLOBAL_SYM; 1565 retsts = lib$delete_symbol(&lnmdsc,&symtype); 1566 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 1567 if (retsts == LIB$_NOSUCHSYM) continue; 1568 break; 1569 } 1570 else if (!ivlnm) { 1571 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */ 1572 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1573 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1574 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */ 1575 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1576 } 1577 } 1578 } 1579 else { /* we're defining a value */ 1580 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) { 1581 #ifdef HAS_SETENV 1582 return setenv(lnm,eqv,1) ? vaxc$errno : 0; 1583 #else 1584 if (ckWARN(WARN_INTERNAL)) 1585 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv); 1586 retsts = SS$_NOSUCHPGM; 1587 #endif 1588 } 1589 else { 1590 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */ 1591 eqvdsc.dsc$w_length = strlen(eqv); 1592 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) && 1593 !str$case_blind_compare(&tmpdsc,&clisym)) { 1594 unsigned int symtype; 1595 if (tabvec[0]->dsc$w_length == 12 && 1596 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) && 1597 !str$case_blind_compare(&tmpdsc,&local)) 1598 symtype = LIB$K_CLI_LOCAL_SYM; 1599 else symtype = LIB$K_CLI_GLOBAL_SYM; 1600 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype); 1601 } 1602 else { 1603 if (!*eqv) eqvdsc.dsc$w_length = 1; 1604 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) { 1605 1606 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH; 1607 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) { 1608 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes", 1609 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1)); 1610 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1); 1611 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1; 1612 } 1613 1614 Newx(ilist,nseg+1,struct itmlst_3); 1615 ile = ilist; 1616 if (!ile) { 1617 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM); 1618 return SS$_INSFMEM; 1619 } 1620 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1))); 1621 1622 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) { 1623 ile->itmcode = LNM$_STRING; 1624 ile->bufadr = c; 1625 if ((j+1) == nseg) { 1626 ile->buflen = strlen(c); 1627 /* in case we are truncating one that's too long */ 1628 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH; 1629 } 1630 else { 1631 ile->buflen = LNM$C_NAMLENGTH; 1632 } 1633 } 1634 1635 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist); 1636 Safefree (ilist); 1637 } 1638 else { 1639 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0); 1640 } 1641 } 1642 } 1643 } 1644 if (!(retsts & 1)) { 1645 switch (retsts) { 1646 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM: 1647 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB: 1648 set_errno(EVMSERR); break; 1649 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 1650 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM: 1651 set_errno(EINVAL); break; 1652 case SS$_NOPRIV: 1653 set_errno(EACCES); break; 1654 default: 1655 _ckvmssts(retsts); 1656 set_errno(EVMSERR); 1657 } 1658 set_vaxc_errno(retsts); 1659 return (int) retsts || 44; /* retsts should never be 0, but just in case */ 1660 } 1661 else { 1662 /* We reset error values on success because Perl does an hv_fetch() 1663 * before each hv_store(), and if the thing we're setting didn't 1664 * previously exist, we've got a leftover error message. (Of course, 1665 * this fails in the face of 1666 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo'; 1667 * in that the error reported in $! isn't spurious, 1668 * but it's right more often than not.) 1669 */ 1670 set_errno(0); set_vaxc_errno(retsts); 1671 return 0; 1672 } 1673 1674 } /* end of vmssetenv() */ 1675 /*}}}*/ 1676 1677 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/ 1678 /* This has to be a function since there's a prototype for it in proto.h */ 1679 void 1680 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv) 1681 { 1682 if (lnm && *lnm) { 1683 int len = strlen(lnm); 1684 if (len == 7) { 1685 char uplnm[8]; 1686 int i; 1687 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]); 1688 if (!strcmp(uplnm,"DEFAULT")) { 1689 if (eqv && *eqv) my_chdir(eqv); 1690 return; 1691 } 1692 } 1693 } 1694 (void) vmssetenv(lnm,eqv,NULL); 1695 } 1696 /*}}}*/ 1697 1698 /*{{{static void vmssetuserlnm(char *name, char *eqv); */ 1699 /* vmssetuserlnm 1700 * sets a user-mode logical in the process logical name table 1701 * used for redirection of sys$error 1702 */ 1703 void 1704 Perl_vmssetuserlnm(const char *name, const char *eqv) 1705 { 1706 $DESCRIPTOR(d_tab, "LNM$PROCESS"); 1707 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 1708 unsigned long int iss, attr = LNM$M_CONFINE; 1709 unsigned char acmode = PSL$C_USER; 1710 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0}, 1711 {0, 0, 0, 0}}; 1712 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */ 1713 d_name.dsc$w_length = strlen(name); 1714 1715 lnmlst[0].buflen = strlen(eqv); 1716 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */ 1717 1718 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst); 1719 if (!(iss&1)) lib$signal(iss); 1720 } 1721 /*}}}*/ 1722 1723 1724 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/ 1725 /* my_crypt - VMS password hashing 1726 * my_crypt() provides an interface compatible with the Unix crypt() 1727 * C library function, and uses sys$hash_password() to perform VMS 1728 * password hashing. The quadword hashed password value is returned 1729 * as a NUL-terminated 8 character string. my_crypt() does not change 1730 * the case of its string arguments; in order to match the behavior 1731 * of LOGINOUT et al., alphabetic characters in both arguments must 1732 * be upcased by the caller. 1733 * 1734 * - fix me to call ACM services when available 1735 */ 1736 char * 1737 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname) 1738 { 1739 # ifndef UAI$C_PREFERRED_ALGORITHM 1740 # define UAI$C_PREFERRED_ALGORITHM 127 1741 # endif 1742 unsigned char alg = UAI$C_PREFERRED_ALGORITHM; 1743 unsigned short int salt = 0; 1744 unsigned long int sts; 1745 struct const_dsc { 1746 unsigned short int dsc$w_length; 1747 unsigned char dsc$b_type; 1748 unsigned char dsc$b_class; 1749 const char * dsc$a_pointer; 1750 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}, 1751 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1752 struct itmlst_3 uailst[3] = { 1753 { sizeof alg, UAI$_ENCRYPT, &alg, 0}, 1754 { sizeof salt, UAI$_SALT, &salt, 0}, 1755 { 0, 0, NULL, NULL}}; 1756 static char hash[9]; 1757 1758 usrdsc.dsc$w_length = strlen(usrname); 1759 usrdsc.dsc$a_pointer = usrname; 1760 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) { 1761 switch (sts) { 1762 case SS$_NOGRPPRV: case SS$_NOSYSPRV: 1763 set_errno(EACCES); 1764 break; 1765 case RMS$_RNF: 1766 set_errno(ESRCH); /* There isn't a Unix no-such-user error */ 1767 break; 1768 default: 1769 set_errno(EVMSERR); 1770 } 1771 set_vaxc_errno(sts); 1772 if (sts != RMS$_RNF) return NULL; 1773 } 1774 1775 txtdsc.dsc$w_length = strlen(textpasswd); 1776 txtdsc.dsc$a_pointer = textpasswd; 1777 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) { 1778 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL; 1779 } 1780 1781 return (char *) hash; 1782 1783 } /* end of my_crypt() */ 1784 /*}}}*/ 1785 1786 1787 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *); 1788 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *); 1789 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *); 1790 1791 /* fixup barenames that are directories for internal use. 1792 * There have been problems with the consistent handling of UNIX 1793 * style directory names when routines are presented with a name that 1794 * has no directory delimiters at all. So this routine will eventually 1795 * fix the issue. 1796 */ 1797 static char * fixup_bare_dirnames(const char * name) 1798 { 1799 if (decc_disable_to_vms_logname_translation) { 1800 /* fix me */ 1801 } 1802 return NULL; 1803 } 1804 1805 /* 8.3, remove() is now broken on symbolic links */ 1806 static int rms_erase(const char * vmsname); 1807 1808 1809 /* mp_do_kill_file 1810 * A little hack to get around a bug in some implementation of remove() 1811 * that do not know how to delete a directory 1812 * 1813 * Delete any file to which user has control access, regardless of whether 1814 * delete access is explicitly allowed. 1815 * Limitations: User must have write access to parent directory. 1816 * Does not block signals or ASTs; if interrupted in midstream 1817 * may leave file with an altered ACL. 1818 * HANDLE WITH CARE! 1819 */ 1820 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/ 1821 static int 1822 mp_do_kill_file(pTHX_ const char *name, int dirflag) 1823 { 1824 char *vmsname; 1825 char *rslt; 1826 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; 1827 unsigned long int cxt = 0, aclsts, fndsts; 1828 int rmsts = -1; 1829 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1830 struct myacedef { 1831 unsigned char myace$b_length; 1832 unsigned char myace$b_type; 1833 unsigned short int myace$w_flags; 1834 unsigned long int myace$l_access; 1835 unsigned long int myace$l_ident; 1836 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 1837 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0}, 1838 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 1839 struct itmlst_3 1840 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0}, 1841 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}}, 1842 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}}, 1843 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}}, 1844 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}}, 1845 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}}; 1846 1847 /* Expand the input spec using RMS, since the CRTL remove() and 1848 * system services won't do this by themselves, so we may miss 1849 * a file "hiding" behind a logical name or search list. */ 1850 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1); 1851 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM); 1852 1853 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK); 1854 if (rslt == NULL) { 1855 PerlMem_free(vmsname); 1856 return -1; 1857 } 1858 1859 /* Erase the file */ 1860 rmsts = rms_erase(vmsname); 1861 1862 /* Did it succeed */ 1863 if ($VMS_STATUS_SUCCESS(rmsts)) { 1864 PerlMem_free(vmsname); 1865 return 0; 1866 } 1867 1868 /* If not, can changing protections help? */ 1869 if (rmsts != RMS$_PRV) { 1870 set_vaxc_errno(rmsts); 1871 PerlMem_free(vmsname); 1872 return -1; 1873 } 1874 1875 /* No, so we get our own UIC to use as a rights identifier, 1876 * and the insert an ACE at the head of the ACL which allows us 1877 * to delete the file. 1878 */ 1879 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 1880 fildsc.dsc$w_length = strlen(vmsname); 1881 fildsc.dsc$a_pointer = vmsname; 1882 cxt = 0; 1883 newace.myace$l_ident = oldace.myace$l_ident; 1884 rmsts = -1; 1885 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) { 1886 switch (aclsts) { 1887 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT: 1888 set_errno(ENOENT); break; 1889 case RMS$_DIR: 1890 set_errno(ENOTDIR); break; 1891 case RMS$_DEV: 1892 set_errno(ENODEV); break; 1893 case RMS$_SYN: case SS$_INVFILFOROP: 1894 set_errno(EINVAL); break; 1895 case RMS$_PRV: 1896 set_errno(EACCES); break; 1897 default: 1898 _ckvmssts_noperl(aclsts); 1899 } 1900 set_vaxc_errno(aclsts); 1901 PerlMem_free(vmsname); 1902 return -1; 1903 } 1904 /* Grab any existing ACEs with this identifier in case we fail */ 1905 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt); 1906 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY 1907 || fndsts == SS$_NOMOREACE ) { 1908 /* Add the new ACE . . . */ 1909 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1)) 1910 goto yourroom; 1911 1912 rmsts = rms_erase(vmsname); 1913 if ($VMS_STATUS_SUCCESS(rmsts)) { 1914 rmsts = 0; 1915 } 1916 else { 1917 rmsts = -1; 1918 /* We blew it - dir with files in it, no write priv for 1919 * parent directory, etc. Put things back the way they were. */ 1920 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1)) 1921 goto yourroom; 1922 if (fndsts & 1) { 1923 addlst[0].bufadr = &oldace; 1924 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1)) 1925 goto yourroom; 1926 } 1927 } 1928 } 1929 1930 yourroom: 1931 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0); 1932 /* We just deleted it, so of course it's not there. Some versions of 1933 * VMS seem to return success on the unlock operation anyhow (after all 1934 * the unlock is successful), but others don't. 1935 */ 1936 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL; 1937 if (aclsts & 1) aclsts = fndsts; 1938 if (!(aclsts & 1)) { 1939 set_errno(EVMSERR); 1940 set_vaxc_errno(aclsts); 1941 } 1942 1943 PerlMem_free(vmsname); 1944 return rmsts; 1945 1946 } /* end of kill_file() */ 1947 /*}}}*/ 1948 1949 1950 /*{{{int do_rmdir(char *name)*/ 1951 int 1952 Perl_do_rmdir(pTHX_ const char *name) 1953 { 1954 char * dirfile; 1955 int retval; 1956 Stat_t st; 1957 1958 /* lstat returns a VMS fileified specification of the name */ 1959 /* that is looked up, and also lets verifies that this is a directory */ 1960 1961 retval = flex_lstat(name, &st); 1962 if (retval != 0) { 1963 char * ret_spec; 1964 1965 /* Due to a historical feature, flex_stat/lstat can not see some */ 1966 /* Unix format file names that the rest of the CRTL can see */ 1967 /* Fixing that feature will cause some perl tests to fail */ 1968 /* So try this one more time. */ 1969 1970 retval = lstat(name, &st.crtl_stat); 1971 if (retval != 0) 1972 return -1; 1973 1974 /* force it to a file spec for the kill file to work. */ 1975 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL); 1976 if (ret_spec == NULL) { 1977 errno = EIO; 1978 return -1; 1979 } 1980 } 1981 1982 if (!S_ISDIR(st.st_mode)) { 1983 errno = ENOTDIR; 1984 retval = -1; 1985 } 1986 else { 1987 dirfile = st.st_devnam; 1988 1989 /* It may be possible for flex_stat to find a file and vmsify() to */ 1990 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */ 1991 /* with that case, so fail it */ 1992 if (dirfile[0] == 0) { 1993 errno = EIO; 1994 return -1; 1995 } 1996 1997 retval = mp_do_kill_file(aTHX_ dirfile, 1); 1998 } 1999 2000 return retval; 2001 2002 } /* end of do_rmdir */ 2003 /*}}}*/ 2004 2005 /* kill_file 2006 * Delete any file to which user has control access, regardless of whether 2007 * delete access is explicitly allowed. 2008 * Limitations: User must have write access to parent directory. 2009 * Does not block signals or ASTs; if interrupted in midstream 2010 * may leave file with an altered ACL. 2011 * HANDLE WITH CARE! 2012 */ 2013 /*{{{int kill_file(char *name)*/ 2014 int 2015 Perl_kill_file(pTHX_ const char *name) 2016 { 2017 char * vmsfile; 2018 Stat_t st; 2019 int rmsts; 2020 2021 /* Convert the filename to VMS format and see if it is a directory */ 2022 /* flex_lstat returns a vmsified file specification */ 2023 rmsts = flex_lstat(name, &st); 2024 if (rmsts != 0) { 2025 2026 /* Due to a historical feature, flex_stat/lstat can not see some */ 2027 /* Unix format file names that the rest of the CRTL can see when */ 2028 /* ODS-2 file specifications are in use. */ 2029 /* Fixing that feature will cause some perl tests to fail */ 2030 /* [.lib.ExtUtils.t]Manifest.t is one of them */ 2031 st.st_mode = 0; 2032 vmsfile = (char *) name; /* cast ok */ 2033 2034 } else { 2035 vmsfile = st.st_devnam; 2036 if (vmsfile[0] == 0) { 2037 /* It may be possible for flex_stat to find a file and vmsify() */ 2038 /* to fail with ODS-2 specifications. mp_do_kill_file can not */ 2039 /* deal with that case, so fail it */ 2040 errno = EIO; 2041 return -1; 2042 } 2043 } 2044 2045 /* Remove() is allowed to delete directories, according to the X/Open 2046 * specifications. 2047 * This may need special handling to work with the ACL hacks. 2048 */ 2049 if (S_ISDIR(st.st_mode)) { 2050 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1); 2051 return rmsts; 2052 } 2053 2054 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0); 2055 2056 /* Need to delete all versions ? */ 2057 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) { 2058 int i = 0; 2059 2060 /* Just use lstat() here as do not need st_dev */ 2061 /* and we know that the file is in VMS format or that */ 2062 /* because of a historical bug, flex_stat can not see the file */ 2063 while (lstat(vmsfile, (stat_t *)&st) == 0) { 2064 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0); 2065 if (rmsts != 0) 2066 break; 2067 i++; 2068 2069 /* Make sure that we do not loop forever */ 2070 if (i > 32767) { 2071 errno = EIO; 2072 rmsts = -1; 2073 break; 2074 } 2075 } 2076 } 2077 2078 return rmsts; 2079 2080 } /* end of kill_file() */ 2081 /*}}}*/ 2082 2083 2084 /*{{{int my_mkdir(char *,Mode_t)*/ 2085 int 2086 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode) 2087 { 2088 STRLEN dirlen = strlen(dir); 2089 2090 /* zero length string sometimes gives ACCVIO */ 2091 if (dirlen == 0) return -1; 2092 2093 /* CRTL mkdir() doesn't tolerate trailing /, since that implies 2094 * null file name/type. However, it's commonplace under Unix, 2095 * so we'll allow it for a gain in portability. 2096 */ 2097 if (dir[dirlen-1] == '/') { 2098 char *newdir = savepvn(dir,dirlen-1); 2099 int ret = mkdir(newdir,mode); 2100 Safefree(newdir); 2101 return ret; 2102 } 2103 else return mkdir(dir,mode); 2104 } /* end of my_mkdir */ 2105 /*}}}*/ 2106 2107 /*{{{int my_chdir(char *)*/ 2108 int 2109 Perl_my_chdir(pTHX_ const char *dir) 2110 { 2111 STRLEN dirlen = strlen(dir); 2112 const char *dir1 = dir; 2113 2114 /* zero length string sometimes gives ACCVIO */ 2115 if (dirlen == 0) { 2116 SETERRNO(EINVAL, SS$_BADPARAM); 2117 return -1; 2118 } 2119 2120 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces. 2121 * This does not work if DECC$EFS_CHARSET is active. Hack it here 2122 * so that existing scripts do not need to be changed. 2123 */ 2124 while ((dirlen > 0) && (*dir1 == ' ')) { 2125 dir1++; 2126 dirlen--; 2127 } 2128 2129 /* some versions of CRTL chdir() doesn't tolerate trailing /, since 2130 * that implies 2131 * null file name/type. However, it's commonplace under Unix, 2132 * so we'll allow it for a gain in portability. 2133 * 2134 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active. 2135 */ 2136 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) { 2137 char *newdir; 2138 int ret; 2139 newdir = (char *)PerlMem_malloc(dirlen); 2140 if (newdir ==NULL) 2141 _ckvmssts_noperl(SS$_INSFMEM); 2142 memcpy(newdir, dir1, dirlen-1); 2143 newdir[dirlen-1] = '\0'; 2144 ret = chdir(newdir); 2145 PerlMem_free(newdir); 2146 return ret; 2147 } 2148 else return chdir(dir1); 2149 } /* end of my_chdir */ 2150 /*}}}*/ 2151 2152 2153 /*{{{int my_chmod(char *, mode_t)*/ 2154 int 2155 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode) 2156 { 2157 Stat_t st; 2158 int ret = -1; 2159 char * changefile; 2160 STRLEN speclen = strlen(file_spec); 2161 2162 /* zero length string sometimes gives ACCVIO */ 2163 if (speclen == 0) return -1; 2164 2165 /* some versions of CRTL chmod() doesn't tolerate trailing /, since 2166 * that implies null file name/type. However, it's commonplace under Unix, 2167 * so we'll allow it for a gain in portability. 2168 * 2169 * Tests are showing that chmod() on VMS 8.3 is only accepting directories 2170 * in VMS file.dir notation. 2171 */ 2172 changefile = (char *) file_spec; /* cast ok */ 2173 ret = flex_lstat(file_spec, &st); 2174 if (ret != 0) { 2175 2176 /* Due to a historical feature, flex_stat/lstat can not see some */ 2177 /* Unix format file names that the rest of the CRTL can see when */ 2178 /* ODS-2 file specifications are in use. */ 2179 /* Fixing that feature will cause some perl tests to fail */ 2180 /* [.lib.ExtUtils.t]Manifest.t is one of them */ 2181 st.st_mode = 0; 2182 2183 } else { 2184 /* It may be possible to get here with nothing in st_devname */ 2185 /* chmod still may work though */ 2186 if (st.st_devnam[0] != 0) { 2187 changefile = st.st_devnam; 2188 } 2189 } 2190 ret = chmod(changefile, mode); 2191 return ret; 2192 } /* end of my_chmod */ 2193 /*}}}*/ 2194 2195 2196 /*{{{FILE *my_tmpfile()*/ 2197 FILE * 2198 my_tmpfile(void) 2199 { 2200 FILE *fp; 2201 char *cp; 2202 2203 if ((fp = tmpfile())) return fp; 2204 2205 cp = (char *)PerlMem_malloc(L_tmpnam+24); 2206 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 2207 2208 if (decc_filename_unix_only == 0) 2209 strcpy(cp,"Sys$Scratch:"); 2210 else 2211 strcpy(cp,"/tmp/"); 2212 tmpnam(cp+strlen(cp)); 2213 strcat(cp,".Perltmp"); 2214 fp = fopen(cp,"w+","fop=dlt"); 2215 PerlMem_free(cp); 2216 return fp; 2217 } 2218 /*}}}*/ 2219 2220 2221 /* 2222 * The C RTL's sigaction fails to check for invalid signal numbers so we 2223 * help it out a bit. The docs are correct, but the actual routine doesn't 2224 * do what the docs say it will. 2225 */ 2226 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/ 2227 int 2228 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act, 2229 struct sigaction* oact) 2230 { 2231 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) { 2232 SETERRNO(EINVAL, SS$_INVARG); 2233 return -1; 2234 } 2235 return sigaction(sig, act, oact); 2236 } 2237 /*}}}*/ 2238 2239 #ifdef KILL_BY_SIGPRC 2240 #include <errnodef.h> 2241 2242 /* We implement our own kill() using the undocumented system service 2243 sys$sigprc for one of two reasons: 2244 2245 1.) If the kill() in an older CRTL uses sys$forcex, causing the 2246 target process to do a sys$exit, which usually can't be handled 2247 gracefully...certainly not by Perl and the %SIG{} mechanism. 2248 2249 2.) If the kill() in the CRTL can't be called from a signal 2250 handler without disappearing into the ether, i.e., the signal 2251 it purportedly sends is never trapped. Still true as of VMS 7.3. 2252 2253 sys$sigprc has the same parameters as sys$forcex, but throws an exception 2254 in the target process rather than calling sys$exit. 2255 2256 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg 2257 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't 2258 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc 2259 with condition codes C$_SIG0+nsig*8, catching the exception on the 2260 target process and resignaling with appropriate arguments. 2261 2262 But we don't have that VMS 7.0+ exception handler, so if you 2263 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well. 2264 2265 Also note that SIGTERM is listed in the docs as being "unimplemented", 2266 yet always seems to be signaled with a VMS condition code of 4 (and 2267 correctly handled for that code). So we hardwire it in. 2268 2269 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal 2270 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather 2271 than signalling with an unrecognized (and unhandled by CRTL) code. 2272 */ 2273 2274 #define _MY_SIG_MAX 28 2275 2276 static unsigned int 2277 Perl_sig_to_vmscondition_int(int sig) 2278 { 2279 static unsigned int sig_code[_MY_SIG_MAX+1] = 2280 { 2281 0, /* 0 ZERO */ 2282 SS$_HANGUP, /* 1 SIGHUP */ 2283 SS$_CONTROLC, /* 2 SIGINT */ 2284 SS$_CONTROLY, /* 3 SIGQUIT */ 2285 SS$_RADRMOD, /* 4 SIGILL */ 2286 SS$_BREAK, /* 5 SIGTRAP */ 2287 SS$_OPCCUS, /* 6 SIGABRT */ 2288 SS$_COMPAT, /* 7 SIGEMT */ 2289 #ifdef __VAX 2290 SS$_FLTOVF, /* 8 SIGFPE VAX */ 2291 #else 2292 SS$_HPARITH, /* 8 SIGFPE AXP */ 2293 #endif 2294 SS$_ABORT, /* 9 SIGKILL */ 2295 SS$_ACCVIO, /* 10 SIGBUS */ 2296 SS$_ACCVIO, /* 11 SIGSEGV */ 2297 SS$_BADPARAM, /* 12 SIGSYS */ 2298 SS$_NOMBX, /* 13 SIGPIPE */ 2299 SS$_ASTFLT, /* 14 SIGALRM */ 2300 4, /* 15 SIGTERM */ 2301 0, /* 16 SIGUSR1 */ 2302 0, /* 17 SIGUSR2 */ 2303 0, /* 18 */ 2304 0, /* 19 */ 2305 0, /* 20 SIGCHLD */ 2306 0, /* 21 SIGCONT */ 2307 0, /* 22 SIGSTOP */ 2308 0, /* 23 SIGTSTP */ 2309 0, /* 24 SIGTTIN */ 2310 0, /* 25 SIGTTOU */ 2311 0, /* 26 */ 2312 0, /* 27 */ 2313 0 /* 28 SIGWINCH */ 2314 }; 2315 2316 static int initted = 0; 2317 if (!initted) { 2318 initted = 1; 2319 sig_code[16] = C$_SIGUSR1; 2320 sig_code[17] = C$_SIGUSR2; 2321 sig_code[20] = C$_SIGCHLD; 2322 #if __CRTL_VER >= 70300000 2323 sig_code[28] = C$_SIGWINCH; 2324 #endif 2325 } 2326 2327 if (sig < _SIG_MIN) return 0; 2328 if (sig > _MY_SIG_MAX) return 0; 2329 return sig_code[sig]; 2330 } 2331 2332 unsigned int 2333 Perl_sig_to_vmscondition(int sig) 2334 { 2335 #ifdef SS$_DEBUG 2336 if (vms_debug_on_exception != 0) 2337 lib$signal(SS$_DEBUG); 2338 #endif 2339 return Perl_sig_to_vmscondition_int(sig); 2340 } 2341 2342 2343 #define sys$sigprc SYS$SIGPRC 2344 #ifdef __cplusplus 2345 extern "C" { 2346 #endif 2347 int sys$sigprc(unsigned int *pidadr, 2348 struct dsc$descriptor_s *prcname, 2349 unsigned int code); 2350 #ifdef __cplusplus 2351 } 2352 #endif 2353 2354 int 2355 Perl_my_kill(int pid, int sig) 2356 { 2357 int iss; 2358 unsigned int code; 2359 2360 /* sig 0 means validate the PID */ 2361 /*------------------------------*/ 2362 if (sig == 0) { 2363 const unsigned long int jpicode = JPI$_PID; 2364 pid_t ret_pid; 2365 int status; 2366 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL); 2367 if ($VMS_STATUS_SUCCESS(status)) 2368 return 0; 2369 switch (status) { 2370 case SS$_NOSUCHNODE: 2371 case SS$_UNREACHABLE: 2372 case SS$_NONEXPR: 2373 errno = ESRCH; 2374 break; 2375 case SS$_NOPRIV: 2376 errno = EPERM; 2377 break; 2378 default: 2379 errno = EVMSERR; 2380 } 2381 vaxc$errno=status; 2382 return -1; 2383 } 2384 2385 code = Perl_sig_to_vmscondition_int(sig); 2386 2387 if (!code) { 2388 SETERRNO(EINVAL, SS$_BADPARAM); 2389 return -1; 2390 } 2391 2392 /* Fixme: Per official UNIX specification: If pid = 0, or negative then 2393 * signals are to be sent to multiple processes. 2394 * pid = 0 - all processes in group except ones that the system exempts 2395 * pid = -1 - all processes except ones that the system exempts 2396 * pid = -n - all processes in group (abs(n)) except ... 2397 * For now, just report as not supported. 2398 */ 2399 2400 if (pid <= 0) { 2401 SETERRNO(ENOTSUP, SS$_UNSUPPORTED); 2402 return -1; 2403 } 2404 2405 iss = sys$sigprc((unsigned int *)&pid,0,code); 2406 if (iss&1) return 0; 2407 2408 switch (iss) { 2409 case SS$_NOPRIV: 2410 set_errno(EPERM); break; 2411 case SS$_NONEXPR: 2412 case SS$_NOSUCHNODE: 2413 case SS$_UNREACHABLE: 2414 set_errno(ESRCH); break; 2415 case SS$_INSFMEM: 2416 set_errno(ENOMEM); break; 2417 default: 2418 _ckvmssts_noperl(iss); 2419 set_errno(EVMSERR); 2420 } 2421 set_vaxc_errno(iss); 2422 2423 return -1; 2424 } 2425 #endif 2426 2427 /* Routine to convert a VMS status code to a UNIX status code. 2428 ** More tricky than it appears because of conflicting conventions with 2429 ** existing code. 2430 ** 2431 ** VMS status codes are a bit mask, with the least significant bit set for 2432 ** success. 2433 ** 2434 ** Special UNIX status of EVMSERR indicates that no translation is currently 2435 ** available, and programs should check the VMS status code. 2436 ** 2437 ** Programs compiled with _POSIX_EXIT have a special encoding that requires 2438 ** decoding. 2439 */ 2440 2441 #ifndef C_FACILITY_NO 2442 #define C_FACILITY_NO 0x350000 2443 #endif 2444 #ifndef DCL_IVVERB 2445 #define DCL_IVVERB 0x38090 2446 #endif 2447 2448 int Perl_vms_status_to_unix(int vms_status, int child_flag) 2449 { 2450 int facility; 2451 int fac_sp; 2452 int msg_no; 2453 int msg_status; 2454 int unix_status; 2455 2456 /* Assume the best or the worst */ 2457 if (vms_status & STS$M_SUCCESS) 2458 unix_status = 0; 2459 else 2460 unix_status = EVMSERR; 2461 2462 msg_status = vms_status & ~STS$M_CONTROL; 2463 2464 facility = vms_status & STS$M_FAC_NO; 2465 fac_sp = vms_status & STS$M_FAC_SP; 2466 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY); 2467 2468 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) { 2469 switch(msg_no) { 2470 case SS$_NORMAL: 2471 unix_status = 0; 2472 break; 2473 case SS$_ACCVIO: 2474 unix_status = EFAULT; 2475 break; 2476 case SS$_DEVOFFLINE: 2477 unix_status = EBUSY; 2478 break; 2479 case SS$_CLEARED: 2480 unix_status = ENOTCONN; 2481 break; 2482 case SS$_IVCHAN: 2483 case SS$_IVLOGNAM: 2484 case SS$_BADPARAM: 2485 case SS$_IVLOGTAB: 2486 case SS$_NOLOGNAM: 2487 case SS$_NOLOGTAB: 2488 case SS$_INVFILFOROP: 2489 case SS$_INVARG: 2490 case SS$_NOSUCHID: 2491 case SS$_IVIDENT: 2492 unix_status = EINVAL; 2493 break; 2494 case SS$_UNSUPPORTED: 2495 unix_status = ENOTSUP; 2496 break; 2497 case SS$_FILACCERR: 2498 case SS$_NOGRPPRV: 2499 case SS$_NOSYSPRV: 2500 unix_status = EACCES; 2501 break; 2502 case SS$_DEVICEFULL: 2503 unix_status = ENOSPC; 2504 break; 2505 case SS$_NOSUCHDEV: 2506 unix_status = ENODEV; 2507 break; 2508 case SS$_NOSUCHFILE: 2509 case SS$_NOSUCHOBJECT: 2510 unix_status = ENOENT; 2511 break; 2512 case SS$_ABORT: /* Fatal case */ 2513 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */ 2514 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */ 2515 unix_status = EINTR; 2516 break; 2517 case SS$_BUFFEROVF: 2518 unix_status = E2BIG; 2519 break; 2520 case SS$_INSFMEM: 2521 unix_status = ENOMEM; 2522 break; 2523 case SS$_NOPRIV: 2524 unix_status = EPERM; 2525 break; 2526 case SS$_NOSUCHNODE: 2527 case SS$_UNREACHABLE: 2528 unix_status = ESRCH; 2529 break; 2530 case SS$_NONEXPR: 2531 unix_status = ECHILD; 2532 break; 2533 default: 2534 if ((facility == 0) && (msg_no < 8)) { 2535 /* These are not real VMS status codes so assume that they are 2536 ** already UNIX status codes 2537 */ 2538 unix_status = msg_no; 2539 break; 2540 } 2541 } 2542 } 2543 else { 2544 /* Translate a POSIX exit code to a UNIX exit code */ 2545 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) { 2546 unix_status = (msg_no & 0x07F8) >> 3; 2547 } 2548 else { 2549 2550 /* Documented traditional behavior for handling VMS child exits */ 2551 /*--------------------------------------------------------------*/ 2552 if (child_flag != 0) { 2553 2554 /* Success / Informational return 0 */ 2555 /*----------------------------------*/ 2556 if (msg_no & STS$K_SUCCESS) 2557 return 0; 2558 2559 /* Warning returns 1 */ 2560 /*-------------------*/ 2561 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0) 2562 return 1; 2563 2564 /* Everything else pass through the severity bits */ 2565 /*------------------------------------------------*/ 2566 return (msg_no & STS$M_SEVERITY); 2567 } 2568 2569 /* Normal VMS status to ERRNO mapping attempt */ 2570 /*--------------------------------------------*/ 2571 switch(msg_status) { 2572 /* case RMS$_EOF: */ /* End of File */ 2573 case RMS$_FNF: /* File Not Found */ 2574 case RMS$_DNF: /* Dir Not Found */ 2575 unix_status = ENOENT; 2576 break; 2577 case RMS$_RNF: /* Record Not Found */ 2578 unix_status = ESRCH; 2579 break; 2580 case RMS$_DIR: 2581 unix_status = ENOTDIR; 2582 break; 2583 case RMS$_DEV: 2584 unix_status = ENODEV; 2585 break; 2586 case RMS$_IFI: 2587 case RMS$_FAC: 2588 case RMS$_ISI: 2589 unix_status = EBADF; 2590 break; 2591 case RMS$_FEX: 2592 unix_status = EEXIST; 2593 break; 2594 case RMS$_SYN: 2595 case RMS$_FNM: 2596 case LIB$_INVSTRDES: 2597 case LIB$_INVARG: 2598 case LIB$_NOSUCHSYM: 2599 case LIB$_INVSYMNAM: 2600 case DCL_IVVERB: 2601 unix_status = EINVAL; 2602 break; 2603 case CLI$_BUFOVF: 2604 case RMS$_RTB: 2605 case CLI$_TKNOVF: 2606 case CLI$_RSLOVF: 2607 unix_status = E2BIG; 2608 break; 2609 case RMS$_PRV: /* No privilege */ 2610 case RMS$_ACC: /* ACP file access failed */ 2611 case RMS$_WLK: /* Device write locked */ 2612 unix_status = EACCES; 2613 break; 2614 case RMS$_MKD: /* Failed to mark for delete */ 2615 unix_status = EPERM; 2616 break; 2617 /* case RMS$_NMF: */ /* No more files */ 2618 } 2619 } 2620 } 2621 2622 return unix_status; 2623 } 2624 2625 /* Try to guess at what VMS error status should go with a UNIX errno 2626 * value. This is hard to do as there could be many possible VMS 2627 * error statuses that caused the errno value to be set. 2628 */ 2629 2630 int Perl_unix_status_to_vms(int unix_status) 2631 { 2632 int test_unix_status; 2633 2634 /* Trivial cases first */ 2635 /*---------------------*/ 2636 if (unix_status == EVMSERR) 2637 return vaxc$errno; 2638 2639 /* Is vaxc$errno sane? */ 2640 /*---------------------*/ 2641 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0); 2642 if (test_unix_status == unix_status) 2643 return vaxc$errno; 2644 2645 /* If way out of range, must be VMS code already */ 2646 /*-----------------------------------------------*/ 2647 if (unix_status > EVMSERR) 2648 return unix_status; 2649 2650 /* If out of range, punt */ 2651 /*-----------------------*/ 2652 if (unix_status > __ERRNO_MAX) 2653 return SS$_ABORT; 2654 2655 2656 /* Ok, now we have to do it the hard way. */ 2657 /*----------------------------------------*/ 2658 switch(unix_status) { 2659 case 0: return SS$_NORMAL; 2660 case EPERM: return SS$_NOPRIV; 2661 case ENOENT: return SS$_NOSUCHOBJECT; 2662 case ESRCH: return SS$_UNREACHABLE; 2663 case EINTR: return SS$_ABORT; 2664 /* case EIO: */ 2665 /* case ENXIO: */ 2666 case E2BIG: return SS$_BUFFEROVF; 2667 /* case ENOEXEC */ 2668 case EBADF: return RMS$_IFI; 2669 case ECHILD: return SS$_NONEXPR; 2670 /* case EAGAIN */ 2671 case ENOMEM: return SS$_INSFMEM; 2672 case EACCES: return SS$_FILACCERR; 2673 case EFAULT: return SS$_ACCVIO; 2674 /* case ENOTBLK */ 2675 case EBUSY: return SS$_DEVOFFLINE; 2676 case EEXIST: return RMS$_FEX; 2677 /* case EXDEV */ 2678 case ENODEV: return SS$_NOSUCHDEV; 2679 case ENOTDIR: return RMS$_DIR; 2680 /* case EISDIR */ 2681 case EINVAL: return SS$_INVARG; 2682 /* case ENFILE */ 2683 /* case EMFILE */ 2684 /* case ENOTTY */ 2685 /* case ETXTBSY */ 2686 /* case EFBIG */ 2687 case ENOSPC: return SS$_DEVICEFULL; 2688 case ESPIPE: return LIB$_INVARG; 2689 /* case EROFS: */ 2690 /* case EMLINK: */ 2691 /* case EPIPE: */ 2692 /* case EDOM */ 2693 case ERANGE: return LIB$_INVARG; 2694 /* case EWOULDBLOCK */ 2695 /* case EINPROGRESS */ 2696 /* case EALREADY */ 2697 /* case ENOTSOCK */ 2698 /* case EDESTADDRREQ */ 2699 /* case EMSGSIZE */ 2700 /* case EPROTOTYPE */ 2701 /* case ENOPROTOOPT */ 2702 /* case EPROTONOSUPPORT */ 2703 /* case ESOCKTNOSUPPORT */ 2704 /* case EOPNOTSUPP */ 2705 /* case EPFNOSUPPORT */ 2706 /* case EAFNOSUPPORT */ 2707 /* case EADDRINUSE */ 2708 /* case EADDRNOTAVAIL */ 2709 /* case ENETDOWN */ 2710 /* case ENETUNREACH */ 2711 /* case ENETRESET */ 2712 /* case ECONNABORTED */ 2713 /* case ECONNRESET */ 2714 /* case ENOBUFS */ 2715 /* case EISCONN */ 2716 case ENOTCONN: return SS$_CLEARED; 2717 /* case ESHUTDOWN */ 2718 /* case ETOOMANYREFS */ 2719 /* case ETIMEDOUT */ 2720 /* case ECONNREFUSED */ 2721 /* case ELOOP */ 2722 /* case ENAMETOOLONG */ 2723 /* case EHOSTDOWN */ 2724 /* case EHOSTUNREACH */ 2725 /* case ENOTEMPTY */ 2726 /* case EPROCLIM */ 2727 /* case EUSERS */ 2728 /* case EDQUOT */ 2729 /* case ENOMSG */ 2730 /* case EIDRM */ 2731 /* case EALIGN */ 2732 /* case ESTALE */ 2733 /* case EREMOTE */ 2734 /* case ENOLCK */ 2735 /* case ENOSYS */ 2736 /* case EFTYPE */ 2737 /* case ECANCELED */ 2738 /* case EFAIL */ 2739 /* case EINPROG */ 2740 case ENOTSUP: 2741 return SS$_UNSUPPORTED; 2742 /* case EDEADLK */ 2743 /* case ENWAIT */ 2744 /* case EILSEQ */ 2745 /* case EBADCAT */ 2746 /* case EBADMSG */ 2747 /* case EABANDONED */ 2748 default: 2749 return SS$_ABORT; /* punt */ 2750 } 2751 } 2752 2753 2754 /* default piping mailbox size */ 2755 #ifdef __VAX 2756 # define PERL_BUFSIZ 512 2757 #else 2758 # define PERL_BUFSIZ 8192 2759 #endif 2760 2761 2762 static void 2763 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc) 2764 { 2765 unsigned long int mbxbufsiz; 2766 static unsigned long int syssize = 0; 2767 unsigned long int dviitm = DVI$_DEVNAM; 2768 char csize[LNM$C_NAMLENGTH+1]; 2769 int sts; 2770 2771 if (!syssize) { 2772 unsigned long syiitm = SYI$_MAXBUF; 2773 /* 2774 * Get the SYSGEN parameter MAXBUF 2775 * 2776 * If the logical 'PERL_MBX_SIZE' is defined 2777 * use the value of the logical instead of PERL_BUFSIZ, but 2778 * keep the size between 128 and MAXBUF. 2779 * 2780 */ 2781 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0)); 2782 } 2783 2784 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) { 2785 mbxbufsiz = atoi(csize); 2786 } else { 2787 mbxbufsiz = PERL_BUFSIZ; 2788 } 2789 if (mbxbufsiz < 128) mbxbufsiz = 128; 2790 if (mbxbufsiz > syssize) mbxbufsiz = syssize; 2791 2792 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0)); 2793 2794 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length); 2795 _ckvmssts_noperl(sts); 2796 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0'; 2797 2798 } /* end of create_mbx() */ 2799 2800 2801 /*{{{ my_popen and my_pclose*/ 2802 2803 typedef struct _iosb IOSB; 2804 typedef struct _iosb* pIOSB; 2805 typedef struct _pipe Pipe; 2806 typedef struct _pipe* pPipe; 2807 typedef struct pipe_details Info; 2808 typedef struct pipe_details* pInfo; 2809 typedef struct _srqp RQE; 2810 typedef struct _srqp* pRQE; 2811 typedef struct _tochildbuf CBuf; 2812 typedef struct _tochildbuf* pCBuf; 2813 2814 struct _iosb { 2815 unsigned short status; 2816 unsigned short count; 2817 unsigned long dvispec; 2818 }; 2819 2820 #pragma member_alignment save 2821 #pragma nomember_alignment quadword 2822 struct _srqp { /* VMS self-relative queue entry */ 2823 unsigned long qptr[2]; 2824 }; 2825 #pragma member_alignment restore 2826 static RQE RQE_ZERO = {0,0}; 2827 2828 struct _tochildbuf { 2829 RQE q; 2830 int eof; 2831 unsigned short size; 2832 char *buf; 2833 }; 2834 2835 struct _pipe { 2836 RQE free; 2837 RQE wait; 2838 int fd_out; 2839 unsigned short chan_in; 2840 unsigned short chan_out; 2841 char *buf; 2842 unsigned int bufsize; 2843 IOSB iosb; 2844 IOSB iosb2; 2845 int *pipe_done; 2846 int retry; 2847 int type; 2848 int shut_on_empty; 2849 int need_wake; 2850 pPipe *home; 2851 pInfo info; 2852 pCBuf curr; 2853 pCBuf curr2; 2854 #if defined(PERL_IMPLICIT_CONTEXT) 2855 void *thx; /* Either a thread or an interpreter */ 2856 /* pointer, depending on how we're built */ 2857 #endif 2858 }; 2859 2860 2861 struct pipe_details 2862 { 2863 pInfo next; 2864 PerlIO *fp; /* file pointer to pipe mailbox */ 2865 int useFILE; /* using stdio, not perlio */ 2866 int pid; /* PID of subprocess */ 2867 int mode; /* == 'r' if pipe open for reading */ 2868 int done; /* subprocess has completed */ 2869 int waiting; /* waiting for completion/closure */ 2870 int closing; /* my_pclose is closing this pipe */ 2871 unsigned long completion; /* termination status of subprocess */ 2872 pPipe in; /* pipe in to sub */ 2873 pPipe out; /* pipe out of sub */ 2874 pPipe err; /* pipe of sub's sys$error */ 2875 int in_done; /* true when in pipe finished */ 2876 int out_done; 2877 int err_done; 2878 unsigned short xchan; /* channel to debug xterm */ 2879 unsigned short xchan_valid; /* channel is assigned */ 2880 }; 2881 2882 struct exit_control_block 2883 { 2884 struct exit_control_block *flink; 2885 unsigned long int (*exit_routine)(void); 2886 unsigned long int arg_count; 2887 unsigned long int *status_address; 2888 unsigned long int exit_status; 2889 }; 2890 2891 typedef struct _closed_pipes Xpipe; 2892 typedef struct _closed_pipes* pXpipe; 2893 2894 struct _closed_pipes { 2895 int pid; /* PID of subprocess */ 2896 unsigned long completion; /* termination status of subprocess */ 2897 }; 2898 #define NKEEPCLOSED 50 2899 static Xpipe closed_list[NKEEPCLOSED]; 2900 static int closed_index = 0; 2901 static int closed_num = 0; 2902 2903 #define RETRY_DELAY "0 ::0.20" 2904 #define MAX_RETRY 50 2905 2906 static int pipe_ef = 0; /* first call to safe_popen inits these*/ 2907 static unsigned long mypid; 2908 static unsigned long delaytime[2]; 2909 2910 static pInfo open_pipes = NULL; 2911 static $DESCRIPTOR(nl_desc, "NL:"); 2912 2913 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */ 2914 2915 2916 2917 static unsigned long int 2918 pipe_exit_routine(void) 2919 { 2920 pInfo info; 2921 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT; 2922 int sts, did_stuff, j; 2923 2924 /* 2925 * Flush any pending i/o, but since we are in process run-down, be 2926 * careful about referencing PerlIO structures that may already have 2927 * been deallocated. We may not even have an interpreter anymore. 2928 */ 2929 info = open_pipes; 2930 while (info) { 2931 if (info->fp) { 2932 #if defined(PERL_IMPLICIT_CONTEXT) 2933 /* We need to use the Perl context of the thread that created */ 2934 /* the pipe. */ 2935 pTHX; 2936 if (info->err) 2937 aTHX = info->err->thx; 2938 else if (info->out) 2939 aTHX = info->out->thx; 2940 else if (info->in) 2941 aTHX = info->in->thx; 2942 #endif 2943 if (!info->useFILE 2944 #if defined(USE_ITHREADS) 2945 && my_perl 2946 #endif 2947 #ifdef USE_PERLIO 2948 && PL_perlio_fd_refcnt 2949 #endif 2950 ) 2951 PerlIO_flush(info->fp); 2952 else 2953 fflush((FILE *)info->fp); 2954 } 2955 info = info->next; 2956 } 2957 2958 /* 2959 next we try sending an EOF...ignore if doesn't work, make sure we 2960 don't hang 2961 */ 2962 did_stuff = 0; 2963 info = open_pipes; 2964 2965 while (info) { 2966 _ckvmssts_noperl(sys$setast(0)); 2967 if (info->in && !info->in->shut_on_empty) { 2968 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 2969 0, 0, 0, 0, 0, 0)); 2970 info->waiting = 1; 2971 did_stuff = 1; 2972 } 2973 _ckvmssts_noperl(sys$setast(1)); 2974 info = info->next; 2975 } 2976 2977 /* wait for EOF to have effect, up to ~ 30 sec [default] */ 2978 2979 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 2980 int nwait = 0; 2981 2982 info = open_pipes; 2983 while (info) { 2984 _ckvmssts_noperl(sys$setast(0)); 2985 if (info->waiting && info->done) 2986 info->waiting = 0; 2987 nwait += info->waiting; 2988 _ckvmssts_noperl(sys$setast(1)); 2989 info = info->next; 2990 } 2991 if (!nwait) break; 2992 sleep(1); 2993 } 2994 2995 did_stuff = 0; 2996 info = open_pipes; 2997 while (info) { 2998 _ckvmssts_noperl(sys$setast(0)); 2999 if (!info->done) { /* Tap them gently on the shoulder . . .*/ 3000 sts = sys$forcex(&info->pid,0,&abort); 3001 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 3002 did_stuff = 1; 3003 } 3004 _ckvmssts_noperl(sys$setast(1)); 3005 info = info->next; 3006 } 3007 3008 /* again, wait for effect */ 3009 3010 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 3011 int nwait = 0; 3012 3013 info = open_pipes; 3014 while (info) { 3015 _ckvmssts_noperl(sys$setast(0)); 3016 if (info->waiting && info->done) 3017 info->waiting = 0; 3018 nwait += info->waiting; 3019 _ckvmssts_noperl(sys$setast(1)); 3020 info = info->next; 3021 } 3022 if (!nwait) break; 3023 sleep(1); 3024 } 3025 3026 info = open_pipes; 3027 while (info) { 3028 _ckvmssts_noperl(sys$setast(0)); 3029 if (!info->done) { /* We tried to be nice . . . */ 3030 sts = sys$delprc(&info->pid,0); 3031 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 3032 info->done = 1; /* sys$delprc is as done as we're going to get. */ 3033 } 3034 _ckvmssts_noperl(sys$setast(1)); 3035 info = info->next; 3036 } 3037 3038 while(open_pipes) { 3039 3040 #if defined(PERL_IMPLICIT_CONTEXT) 3041 /* We need to use the Perl context of the thread that created */ 3042 /* the pipe. */ 3043 pTHX; 3044 if (open_pipes->err) 3045 aTHX = open_pipes->err->thx; 3046 else if (open_pipes->out) 3047 aTHX = open_pipes->out->thx; 3048 else if (open_pipes->in) 3049 aTHX = open_pipes->in->thx; 3050 #endif 3051 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno; 3052 else if (!(sts & 1)) retsts = sts; 3053 } 3054 return retsts; 3055 } 3056 3057 static struct exit_control_block pipe_exitblock = 3058 {(struct exit_control_block *) 0, 3059 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0}; 3060 3061 static void pipe_mbxtofd_ast(pPipe p); 3062 static void pipe_tochild1_ast(pPipe p); 3063 static void pipe_tochild2_ast(pPipe p); 3064 3065 static void 3066 popen_completion_ast(pInfo info) 3067 { 3068 pInfo i = open_pipes; 3069 int iss; 3070 3071 info->completion &= 0x0FFFFFFF; /* strip off "control" field */ 3072 closed_list[closed_index].pid = info->pid; 3073 closed_list[closed_index].completion = info->completion; 3074 closed_index++; 3075 if (closed_index == NKEEPCLOSED) 3076 closed_index = 0; 3077 closed_num++; 3078 3079 while (i) { 3080 if (i == info) break; 3081 i = i->next; 3082 } 3083 if (!i) return; /* unlinked, probably freed too */ 3084 3085 info->done = TRUE; 3086 3087 /* 3088 Writing to subprocess ... 3089 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe 3090 3091 chan_out may be waiting for "done" flag, or hung waiting 3092 for i/o completion to child...cancel the i/o. This will 3093 put it into "snarf mode" (done but no EOF yet) that discards 3094 input. 3095 3096 Output from subprocess (stdout, stderr) needs to be flushed and 3097 shut down. We try sending an EOF, but if the mbx is full the pipe 3098 routine should still catch the "shut_on_empty" flag, telling it to 3099 use immediate-style reads so that "mbx empty" -> EOF. 3100 3101 3102 */ 3103 if (info->in && !info->in_done) { /* only for mode=w */ 3104 if (info->in->shut_on_empty && info->in->need_wake) { 3105 info->in->need_wake = FALSE; 3106 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0)); 3107 } else { 3108 _ckvmssts_noperl(sys$cancel(info->in->chan_out)); 3109 } 3110 } 3111 3112 if (info->out && !info->out_done) { /* were we also piping output? */ 3113 info->out->shut_on_empty = TRUE; 3114 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3115 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3116 _ckvmssts_noperl(iss); 3117 } 3118 3119 if (info->err && !info->err_done) { /* we were piping stderr */ 3120 info->err->shut_on_empty = TRUE; 3121 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3122 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3123 _ckvmssts_noperl(iss); 3124 } 3125 _ckvmssts_noperl(sys$setef(pipe_ef)); 3126 3127 } 3128 3129 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd); 3130 static void vms_execfree(struct dsc$descriptor_s *vmscmd); 3131 static void pipe_infromchild_ast(pPipe p); 3132 3133 /* 3134 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate 3135 inside an AST routine without worrying about reentrancy and which Perl 3136 memory allocator is being used. 3137 3138 We read data and queue up the buffers, then spit them out one at a 3139 time to the output mailbox when the output mailbox is ready for one. 3140 3141 */ 3142 #define INITIAL_TOCHILDQUEUE 2 3143 3144 static pPipe 3145 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx) 3146 { 3147 pPipe p; 3148 pCBuf b; 3149 char mbx1[64], mbx2[64]; 3150 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3151 DSC$K_CLASS_S, mbx1}, 3152 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3153 DSC$K_CLASS_S, mbx2}; 3154 unsigned int dviitm = DVI$_DEVBUFSIZ; 3155 int j, n; 3156 3157 n = sizeof(Pipe); 3158 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3159 3160 create_mbx(&p->chan_in , &d_mbx1); 3161 create_mbx(&p->chan_out, &d_mbx2); 3162 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3163 3164 p->buf = 0; 3165 p->shut_on_empty = FALSE; 3166 p->need_wake = FALSE; 3167 p->type = 0; 3168 p->retry = 0; 3169 p->iosb.status = SS$_NORMAL; 3170 p->iosb2.status = SS$_NORMAL; 3171 p->free = RQE_ZERO; 3172 p->wait = RQE_ZERO; 3173 p->curr = 0; 3174 p->curr2 = 0; 3175 p->info = 0; 3176 #ifdef PERL_IMPLICIT_CONTEXT 3177 p->thx = aTHX; 3178 #endif 3179 3180 n = sizeof(CBuf) + p->bufsize; 3181 3182 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) { 3183 _ckvmssts_noperl(lib$get_vm(&n, &b)); 3184 b->buf = (char *) b + sizeof(CBuf); 3185 _ckvmssts_noperl(lib$insqhi(b, &p->free)); 3186 } 3187 3188 pipe_tochild2_ast(p); 3189 pipe_tochild1_ast(p); 3190 strcpy(wmbx, mbx1); 3191 strcpy(rmbx, mbx2); 3192 return p; 3193 } 3194 3195 /* reads the MBX Perl is writing, and queues */ 3196 3197 static void 3198 pipe_tochild1_ast(pPipe p) 3199 { 3200 pCBuf b = p->curr; 3201 int iss = p->iosb.status; 3202 int eof = (iss == SS$_ENDOFFILE); 3203 int sts; 3204 #ifdef PERL_IMPLICIT_CONTEXT 3205 pTHX = p->thx; 3206 #endif 3207 3208 if (p->retry) { 3209 if (eof) { 3210 p->shut_on_empty = TRUE; 3211 b->eof = TRUE; 3212 _ckvmssts_noperl(sys$dassgn(p->chan_in)); 3213 } else { 3214 _ckvmssts_noperl(iss); 3215 } 3216 3217 b->eof = eof; 3218 b->size = p->iosb.count; 3219 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait)); 3220 if (p->need_wake) { 3221 p->need_wake = FALSE; 3222 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0)); 3223 } 3224 } else { 3225 p->retry = 1; /* initial call */ 3226 } 3227 3228 if (eof) { /* flush the free queue, return when done */ 3229 int n = sizeof(CBuf) + p->bufsize; 3230 while (1) { 3231 iss = lib$remqti(&p->free, &b); 3232 if (iss == LIB$_QUEWASEMP) return; 3233 _ckvmssts_noperl(iss); 3234 _ckvmssts_noperl(lib$free_vm(&n, &b)); 3235 } 3236 } 3237 3238 iss = lib$remqti(&p->free, &b); 3239 if (iss == LIB$_QUEWASEMP) { 3240 int n = sizeof(CBuf) + p->bufsize; 3241 _ckvmssts_noperl(lib$get_vm(&n, &b)); 3242 b->buf = (char *) b + sizeof(CBuf); 3243 } else { 3244 _ckvmssts_noperl(iss); 3245 } 3246 3247 p->curr = b; 3248 iss = sys$qio(0,p->chan_in, 3249 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0), 3250 &p->iosb, 3251 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0); 3252 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL; 3253 _ckvmssts_noperl(iss); 3254 } 3255 3256 3257 /* writes queued buffers to output, waits for each to complete before 3258 doing the next */ 3259 3260 static void 3261 pipe_tochild2_ast(pPipe p) 3262 { 3263 pCBuf b = p->curr2; 3264 int iss = p->iosb2.status; 3265 int n = sizeof(CBuf) + p->bufsize; 3266 int done = (p->info && p->info->done) || 3267 iss == SS$_CANCEL || iss == SS$_ABORT; 3268 #if defined(PERL_IMPLICIT_CONTEXT) 3269 pTHX = p->thx; 3270 #endif 3271 3272 do { 3273 if (p->type) { /* type=1 has old buffer, dispose */ 3274 if (p->shut_on_empty) { 3275 _ckvmssts_noperl(lib$free_vm(&n, &b)); 3276 } else { 3277 _ckvmssts_noperl(lib$insqhi(b, &p->free)); 3278 } 3279 p->type = 0; 3280 } 3281 3282 iss = lib$remqti(&p->wait, &b); 3283 if (iss == LIB$_QUEWASEMP) { 3284 if (p->shut_on_empty) { 3285 if (done) { 3286 _ckvmssts_noperl(sys$dassgn(p->chan_out)); 3287 *p->pipe_done = TRUE; 3288 _ckvmssts_noperl(sys$setef(pipe_ef)); 3289 } else { 3290 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, 3291 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3292 } 3293 return; 3294 } 3295 p->need_wake = TRUE; 3296 return; 3297 } 3298 _ckvmssts_noperl(iss); 3299 p->type = 1; 3300 } while (done); 3301 3302 3303 p->curr2 = b; 3304 if (b->eof) { 3305 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, 3306 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3307 } else { 3308 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK, 3309 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0)); 3310 } 3311 3312 return; 3313 3314 } 3315 3316 3317 static pPipe 3318 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx) 3319 { 3320 pPipe p; 3321 char mbx1[64], mbx2[64]; 3322 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3323 DSC$K_CLASS_S, mbx1}, 3324 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3325 DSC$K_CLASS_S, mbx2}; 3326 unsigned int dviitm = DVI$_DEVBUFSIZ; 3327 3328 int n = sizeof(Pipe); 3329 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3330 create_mbx(&p->chan_in , &d_mbx1); 3331 create_mbx(&p->chan_out, &d_mbx2); 3332 3333 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3334 n = p->bufsize * sizeof(char); 3335 _ckvmssts_noperl(lib$get_vm(&n, &p->buf)); 3336 p->shut_on_empty = FALSE; 3337 p->info = 0; 3338 p->type = 0; 3339 p->iosb.status = SS$_NORMAL; 3340 #if defined(PERL_IMPLICIT_CONTEXT) 3341 p->thx = aTHX; 3342 #endif 3343 pipe_infromchild_ast(p); 3344 3345 strcpy(wmbx, mbx1); 3346 strcpy(rmbx, mbx2); 3347 return p; 3348 } 3349 3350 static void 3351 pipe_infromchild_ast(pPipe p) 3352 { 3353 int iss = p->iosb.status; 3354 int eof = (iss == SS$_ENDOFFILE); 3355 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0)); 3356 int kideof = (eof && (p->iosb.dvispec == p->info->pid)); 3357 #if defined(PERL_IMPLICIT_CONTEXT) 3358 pTHX = p->thx; 3359 #endif 3360 3361 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */ 3362 _ckvmssts_noperl(sys$dassgn(p->chan_out)); 3363 p->chan_out = 0; 3364 } 3365 3366 /* read completed: 3367 input shutdown if EOF from self (done or shut_on_empty) 3368 output shutdown if closing flag set (my_pclose) 3369 send data/eof from child or eof from self 3370 otherwise, re-read (snarf of data from child) 3371 */ 3372 3373 if (p->type == 1) { 3374 p->type = 0; 3375 if (myeof && p->chan_in) { /* input shutdown */ 3376 _ckvmssts_noperl(sys$dassgn(p->chan_in)); 3377 p->chan_in = 0; 3378 } 3379 3380 if (p->chan_out) { 3381 if (myeof || kideof) { /* pass EOF to parent */ 3382 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb, 3383 pipe_infromchild_ast, p, 3384 0, 0, 0, 0, 0, 0)); 3385 return; 3386 } else if (eof) { /* eat EOF --- fall through to read*/ 3387 3388 } else { /* transmit data */ 3389 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb, 3390 pipe_infromchild_ast,p, 3391 p->buf, p->iosb.count, 0, 0, 0, 0)); 3392 return; 3393 } 3394 } 3395 } 3396 3397 /* everything shut? flag as done */ 3398 3399 if (!p->chan_in && !p->chan_out) { 3400 *p->pipe_done = TRUE; 3401 _ckvmssts_noperl(sys$setef(pipe_ef)); 3402 return; 3403 } 3404 3405 /* write completed (or read, if snarfing from child) 3406 if still have input active, 3407 queue read...immediate mode if shut_on_empty so we get EOF if empty 3408 otherwise, 3409 check if Perl reading, generate EOFs as needed 3410 */ 3411 3412 if (p->type == 0) { 3413 p->type = 1; 3414 if (p->chan_in) { 3415 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb, 3416 pipe_infromchild_ast,p, 3417 p->buf, p->bufsize, 0, 0, 0, 0); 3418 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL; 3419 _ckvmssts_noperl(iss); 3420 } else { /* send EOFs for extra reads */ 3421 p->iosb.status = SS$_ENDOFFILE; 3422 p->iosb.dvispec = 0; 3423 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN, 3424 0, 0, 0, 3425 pipe_infromchild_ast, p, 0, 0, 0, 0)); 3426 } 3427 } 3428 } 3429 3430 static pPipe 3431 pipe_mbxtofd_setup(pTHX_ int fd, char *out) 3432 { 3433 pPipe p; 3434 char mbx[64]; 3435 unsigned long dviitm = DVI$_DEVBUFSIZ; 3436 struct stat s; 3437 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T, 3438 DSC$K_CLASS_S, mbx}; 3439 int n = sizeof(Pipe); 3440 3441 /* things like terminals and mbx's don't need this filter */ 3442 if (fd && fstat(fd,&s) == 0) { 3443 unsigned long devchar; 3444 char device[65]; 3445 unsigned short dev_len; 3446 struct dsc$descriptor_s d_dev; 3447 char * cptr; 3448 struct item_list_3 items[3]; 3449 int status; 3450 unsigned short dvi_iosb[4]; 3451 3452 cptr = getname(fd, out, 1); 3453 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV); 3454 d_dev.dsc$a_pointer = out; 3455 d_dev.dsc$w_length = strlen(out); 3456 d_dev.dsc$b_dtype = DSC$K_DTYPE_T; 3457 d_dev.dsc$b_class = DSC$K_CLASS_S; 3458 3459 items[0].len = 4; 3460 items[0].code = DVI$_DEVCHAR; 3461 items[0].bufadr = &devchar; 3462 items[0].retadr = NULL; 3463 items[1].len = 64; 3464 items[1].code = DVI$_FULLDEVNAM; 3465 items[1].bufadr = device; 3466 items[1].retadr = &dev_len; 3467 items[2].len = 0; 3468 items[2].code = 0; 3469 3470 status = sys$getdviw 3471 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL); 3472 _ckvmssts_noperl(status); 3473 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) { 3474 device[dev_len] = 0; 3475 3476 if (!(devchar & DEV$M_DIR)) { 3477 strcpy(out, device); 3478 return 0; 3479 } 3480 } 3481 } 3482 3483 _ckvmssts_noperl(lib$get_vm(&n, &p)); 3484 p->fd_out = dup(fd); 3485 create_mbx(&p->chan_in, &d_mbx); 3486 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3487 n = (p->bufsize+1) * sizeof(char); 3488 _ckvmssts_noperl(lib$get_vm(&n, &p->buf)); 3489 p->shut_on_empty = FALSE; 3490 p->retry = 0; 3491 p->info = 0; 3492 strcpy(out, mbx); 3493 3494 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb, 3495 pipe_mbxtofd_ast, p, 3496 p->buf, p->bufsize, 0, 0, 0, 0)); 3497 3498 return p; 3499 } 3500 3501 static void 3502 pipe_mbxtofd_ast(pPipe p) 3503 { 3504 int iss = p->iosb.status; 3505 int done = p->info->done; 3506 int iss2; 3507 int eof = (iss == SS$_ENDOFFILE); 3508 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0)); 3509 int err = !(iss&1) && !eof; 3510 #if defined(PERL_IMPLICIT_CONTEXT) 3511 pTHX = p->thx; 3512 #endif 3513 3514 if (done && myeof) { /* end piping */ 3515 close(p->fd_out); 3516 sys$dassgn(p->chan_in); 3517 *p->pipe_done = TRUE; 3518 _ckvmssts_noperl(sys$setef(pipe_ef)); 3519 return; 3520 } 3521 3522 if (!err && !eof) { /* good data to send to file */ 3523 p->buf[p->iosb.count] = '\n'; 3524 iss2 = write(p->fd_out, p->buf, p->iosb.count+1); 3525 if (iss2 < 0) { 3526 p->retry++; 3527 if (p->retry < MAX_RETRY) { 3528 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p)); 3529 return; 3530 } 3531 } 3532 p->retry = 0; 3533 } else if (err) { 3534 _ckvmssts_noperl(iss); 3535 } 3536 3537 3538 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb, 3539 pipe_mbxtofd_ast, p, 3540 p->buf, p->bufsize, 0, 0, 0, 0); 3541 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL; 3542 _ckvmssts_noperl(iss); 3543 } 3544 3545 3546 typedef struct _pipeloc PLOC; 3547 typedef struct _pipeloc* pPLOC; 3548 3549 struct _pipeloc { 3550 pPLOC next; 3551 char dir[NAM$C_MAXRSS+1]; 3552 }; 3553 static pPLOC head_PLOC = 0; 3554 3555 void 3556 free_pipelocs(pTHX_ void *head) 3557 { 3558 pPLOC p, pnext; 3559 pPLOC *pHead = (pPLOC *)head; 3560 3561 p = *pHead; 3562 while (p) { 3563 pnext = p->next; 3564 PerlMem_free(p); 3565 p = pnext; 3566 } 3567 *pHead = 0; 3568 } 3569 3570 static void 3571 store_pipelocs(pTHX) 3572 { 3573 int i; 3574 pPLOC p; 3575 AV *av = 0; 3576 SV *dirsv; 3577 char *dir, *x; 3578 char *unixdir; 3579 char temp[NAM$C_MAXRSS+1]; 3580 STRLEN n_a; 3581 3582 if (head_PLOC) 3583 free_pipelocs(aTHX_ &head_PLOC); 3584 3585 /* the . directory from @INC comes last */ 3586 3587 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3588 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3589 p->next = head_PLOC; 3590 head_PLOC = p; 3591 strcpy(p->dir,"./"); 3592 3593 /* get the directory from $^X */ 3594 3595 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS); 3596 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3597 3598 #ifdef PERL_IMPLICIT_CONTEXT 3599 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3600 #else 3601 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3602 #endif 3603 my_strlcpy(temp, PL_origargv[0], sizeof(temp)); 3604 x = strrchr(temp,']'); 3605 if (x == NULL) { 3606 x = strrchr(temp,'>'); 3607 if (x == NULL) { 3608 /* It could be a UNIX path */ 3609 x = strrchr(temp,'/'); 3610 } 3611 } 3612 if (x) 3613 x[1] = '\0'; 3614 else { 3615 /* Got a bare name, so use default directory */ 3616 temp[0] = '.'; 3617 temp[1] = '\0'; 3618 } 3619 3620 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) { 3621 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3622 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3623 p->next = head_PLOC; 3624 head_PLOC = p; 3625 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3626 } 3627 } 3628 3629 /* reverse order of @INC entries, skip "." since entered above */ 3630 3631 #ifdef PERL_IMPLICIT_CONTEXT 3632 if (aTHX) 3633 #endif 3634 if (PL_incgv) av = GvAVn(PL_incgv); 3635 3636 for (i = 0; av && i <= AvFILL(av); i++) { 3637 dirsv = *av_fetch(av,i,TRUE); 3638 3639 if (SvROK(dirsv)) continue; 3640 dir = SvPVx(dirsv,n_a); 3641 if (strcmp(dir,".") == 0) continue; 3642 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL) 3643 continue; 3644 3645 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3646 p->next = head_PLOC; 3647 head_PLOC = p; 3648 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3649 } 3650 3651 /* most likely spot (ARCHLIB) put first in the list */ 3652 3653 #ifdef ARCHLIB_EXP 3654 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) { 3655 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3656 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM); 3657 p->next = head_PLOC; 3658 head_PLOC = p; 3659 my_strlcpy(p->dir, unixdir, sizeof(p->dir)); 3660 } 3661 #endif 3662 PerlMem_free(unixdir); 3663 } 3664 3665 static I32 3666 Perl_cando_by_name_int 3667 (pTHX_ I32 bit, bool effective, const char *fname, int opts); 3668 #if !defined(PERL_IMPLICIT_CONTEXT) 3669 #define cando_by_name_int Perl_cando_by_name_int 3670 #else 3671 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d) 3672 #endif 3673 3674 static char * 3675 find_vmspipe(pTHX) 3676 { 3677 static int vmspipe_file_status = 0; 3678 static char vmspipe_file[NAM$C_MAXRSS+1]; 3679 3680 /* already found? Check and use ... need read+execute permission */ 3681 3682 if (vmspipe_file_status == 1) { 3683 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3684 && cando_by_name_int 3685 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3686 return vmspipe_file; 3687 } 3688 vmspipe_file_status = 0; 3689 } 3690 3691 /* scan through stored @INC, $^X */ 3692 3693 if (vmspipe_file_status == 0) { 3694 char file[NAM$C_MAXRSS+1]; 3695 pPLOC p = head_PLOC; 3696 3697 while (p) { 3698 char * exp_res; 3699 int dirlen; 3700 dirlen = my_strlcpy(file, p->dir, sizeof(file)); 3701 my_strlcat(file, "vmspipe.com", sizeof(file)); 3702 p = p->next; 3703 3704 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0); 3705 if (!exp_res) continue; 3706 3707 if (cando_by_name_int 3708 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3709 && cando_by_name_int 3710 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3711 vmspipe_file_status = 1; 3712 return vmspipe_file; 3713 } 3714 } 3715 vmspipe_file_status = -1; /* failed, use tempfiles */ 3716 } 3717 3718 return 0; 3719 } 3720 3721 static FILE * 3722 vmspipe_tempfile(pTHX) 3723 { 3724 char file[NAM$C_MAXRSS+1]; 3725 FILE *fp; 3726 static int index = 0; 3727 Stat_t s0, s1; 3728 int cmp_result; 3729 3730 /* create a tempfile */ 3731 3732 /* we can't go from W, shr=get to R, shr=get without 3733 an intermediate vulnerable state, so don't bother trying... 3734 3735 and lib$spawn doesn't shr=put, so have to close the write 3736 3737 So... match up the creation date/time and the FID to 3738 make sure we're dealing with the same file 3739 3740 */ 3741 3742 index++; 3743 if (!decc_filename_unix_only) { 3744 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index); 3745 fp = fopen(file,"w"); 3746 if (!fp) { 3747 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index); 3748 fp = fopen(file,"w"); 3749 if (!fp) { 3750 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index); 3751 fp = fopen(file,"w"); 3752 } 3753 } 3754 } 3755 else { 3756 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index); 3757 fp = fopen(file,"w"); 3758 if (!fp) { 3759 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index); 3760 fp = fopen(file,"w"); 3761 if (!fp) { 3762 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index); 3763 fp = fopen(file,"w"); 3764 } 3765 } 3766 } 3767 if (!fp) return 0; /* we're hosed */ 3768 3769 fprintf(fp,"$! 'f$verify(0)'\n"); 3770 fprintf(fp,"$! --- protect against nonstandard definitions ---\n"); 3771 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n"); 3772 fprintf(fp,"$ perl_define = \"define/nolog\"\n"); 3773 fprintf(fp,"$ perl_on = \"set noon\"\n"); 3774 fprintf(fp,"$ perl_exit = \"exit\"\n"); 3775 fprintf(fp,"$ perl_del = \"delete\"\n"); 3776 fprintf(fp,"$ pif = \"if\"\n"); 3777 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n"); 3778 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n"); 3779 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n"); 3780 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n"); 3781 fprintf(fp,"$! --- build command line to get max possible length\n"); 3782 fprintf(fp,"$c=perl_popen_cmd0\n"); 3783 fprintf(fp,"$c=c+perl_popen_cmd1\n"); 3784 fprintf(fp,"$c=c+perl_popen_cmd2\n"); 3785 fprintf(fp,"$x=perl_popen_cmd3\n"); 3786 fprintf(fp,"$c=c+x\n"); 3787 fprintf(fp,"$ perl_on\n"); 3788 fprintf(fp,"$ 'c'\n"); 3789 fprintf(fp,"$ perl_status = $STATUS\n"); 3790 fprintf(fp,"$ perl_del 'perl_cfile'\n"); 3791 fprintf(fp,"$ perl_exit 'perl_status'\n"); 3792 fsync(fileno(fp)); 3793 3794 fgetname(fp, file, 1); 3795 fstat(fileno(fp), &s0.crtl_stat); 3796 fclose(fp); 3797 3798 if (decc_filename_unix_only) 3799 int_tounixspec(file, file, NULL); 3800 fp = fopen(file,"r","shr=get"); 3801 if (!fp) return 0; 3802 fstat(fileno(fp), &s1.crtl_stat); 3803 3804 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino); 3805 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) { 3806 fclose(fp); 3807 return 0; 3808 } 3809 3810 return fp; 3811 } 3812 3813 3814 static int vms_is_syscommand_xterm(void) 3815 { 3816 const static struct dsc$descriptor_s syscommand_dsc = 3817 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" }; 3818 3819 const static struct dsc$descriptor_s decwdisplay_dsc = 3820 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" }; 3821 3822 struct item_list_3 items[2]; 3823 unsigned short dvi_iosb[4]; 3824 unsigned long devchar; 3825 unsigned long devclass; 3826 int status; 3827 3828 /* Very simple check to guess if sys$command is a decterm? */ 3829 /* First see if the DECW$DISPLAY: device exists */ 3830 items[0].len = 4; 3831 items[0].code = DVI$_DEVCHAR; 3832 items[0].bufadr = &devchar; 3833 items[0].retadr = NULL; 3834 items[1].len = 0; 3835 items[1].code = 0; 3836 3837 status = sys$getdviw 3838 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL); 3839 3840 if ($VMS_STATUS_SUCCESS(status)) { 3841 status = dvi_iosb[0]; 3842 } 3843 3844 if (!$VMS_STATUS_SUCCESS(status)) { 3845 SETERRNO(EVMSERR, status); 3846 return -1; 3847 } 3848 3849 /* If it does, then for now assume that we are on a workstation */ 3850 /* Now verify that SYS$COMMAND is a terminal */ 3851 /* for creating the debugger DECTerm */ 3852 3853 items[0].len = 4; 3854 items[0].code = DVI$_DEVCLASS; 3855 items[0].bufadr = &devclass; 3856 items[0].retadr = NULL; 3857 items[1].len = 0; 3858 items[1].code = 0; 3859 3860 status = sys$getdviw 3861 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL); 3862 3863 if ($VMS_STATUS_SUCCESS(status)) { 3864 status = dvi_iosb[0]; 3865 } 3866 3867 if (!$VMS_STATUS_SUCCESS(status)) { 3868 SETERRNO(EVMSERR, status); 3869 return -1; 3870 } 3871 else { 3872 if (devclass == DC$_TERM) { 3873 return 0; 3874 } 3875 } 3876 return -1; 3877 } 3878 3879 /* If we are on a DECTerm, we can pretend to fork xterms when requested */ 3880 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) 3881 { 3882 int status; 3883 int ret_stat; 3884 char * ret_char; 3885 char device_name[65]; 3886 unsigned short device_name_len; 3887 struct dsc$descriptor_s customization_dsc; 3888 struct dsc$descriptor_s device_name_dsc; 3889 const char * cptr; 3890 char customization[200]; 3891 char title[40]; 3892 pInfo info = NULL; 3893 char mbx1[64]; 3894 unsigned short p_chan; 3895 int n; 3896 unsigned short iosb[4]; 3897 const char * cust_str = 3898 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n"; 3899 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3900 DSC$K_CLASS_S, mbx1}; 3901 3902 /* LIB$FIND_IMAGE_SIGNAL needs a handler */ 3903 /*---------------------------------------*/ 3904 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret); 3905 3906 3907 /* Make sure that this is from the Perl debugger */ 3908 ret_char = strstr(cmd," xterm "); 3909 if (ret_char == NULL) 3910 return NULL; 3911 cptr = ret_char + 7; 3912 ret_char = strstr(cmd,"tty"); 3913 if (ret_char == NULL) 3914 return NULL; 3915 ret_char = strstr(cmd,"sleep"); 3916 if (ret_char == NULL) 3917 return NULL; 3918 3919 if (decw_term_port == 0) { 3920 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12"); 3921 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR"); 3922 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT"); 3923 3924 status = lib$find_image_symbol 3925 (&filename1_dsc, 3926 &decw_term_port_dsc, 3927 (void *)&decw_term_port, 3928 NULL, 3929 0); 3930 3931 /* Try again with the other image name */ 3932 if (!$VMS_STATUS_SUCCESS(status)) { 3933 3934 status = lib$find_image_symbol 3935 (&filename2_dsc, 3936 &decw_term_port_dsc, 3937 (void *)&decw_term_port, 3938 NULL, 3939 0); 3940 3941 } 3942 3943 } 3944 3945 3946 /* No decw$term_port, give it up */ 3947 if (!$VMS_STATUS_SUCCESS(status)) 3948 return NULL; 3949 3950 /* Are we on a workstation? */ 3951 /* to do: capture the rows / columns and pass their properties */ 3952 ret_stat = vms_is_syscommand_xterm(); 3953 if (ret_stat < 0) 3954 return NULL; 3955 3956 /* Make the title: */ 3957 ret_char = strstr(cptr,"-title"); 3958 if (ret_char != NULL) { 3959 while ((*cptr != 0) && (*cptr != '\"')) { 3960 cptr++; 3961 } 3962 if (*cptr == '\"') 3963 cptr++; 3964 n = 0; 3965 while ((*cptr != 0) && (*cptr != '\"')) { 3966 title[n] = *cptr; 3967 n++; 3968 if (n == 39) { 3969 title[39] = 0; 3970 break; 3971 } 3972 cptr++; 3973 } 3974 title[n] = 0; 3975 } 3976 else { 3977 /* Default title */ 3978 strcpy(title,"Perl Debug DECTerm"); 3979 } 3980 sprintf(customization, cust_str, title); 3981 3982 customization_dsc.dsc$a_pointer = customization; 3983 customization_dsc.dsc$w_length = strlen(customization); 3984 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 3985 customization_dsc.dsc$b_class = DSC$K_CLASS_S; 3986 3987 device_name_dsc.dsc$a_pointer = device_name; 3988 device_name_dsc.dsc$w_length = sizeof device_name -1; 3989 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 3990 device_name_dsc.dsc$b_class = DSC$K_CLASS_S; 3991 3992 device_name_len = 0; 3993 3994 /* Try to create the window */ 3995 status = (*decw_term_port) 3996 (NULL, 3997 NULL, 3998 &customization_dsc, 3999 &device_name_dsc, 4000 &device_name_len, 4001 NULL, 4002 NULL, 4003 NULL); 4004 if (!$VMS_STATUS_SUCCESS(status)) { 4005 SETERRNO(EVMSERR, status); 4006 return NULL; 4007 } 4008 4009 device_name[device_name_len] = '\0'; 4010 4011 /* Need to set this up to look like a pipe for cleanup */ 4012 n = sizeof(Info); 4013 status = lib$get_vm(&n, &info); 4014 if (!$VMS_STATUS_SUCCESS(status)) { 4015 SETERRNO(ENOMEM, status); 4016 return NULL; 4017 } 4018 4019 info->mode = *mode; 4020 info->done = FALSE; 4021 info->completion = 0; 4022 info->closing = FALSE; 4023 info->in = 0; 4024 info->out = 0; 4025 info->err = 0; 4026 info->fp = NULL; 4027 info->useFILE = 0; 4028 info->waiting = 0; 4029 info->in_done = TRUE; 4030 info->out_done = TRUE; 4031 info->err_done = TRUE; 4032 4033 /* Assign a channel on this so that it will persist, and not login */ 4034 /* We stash this channel in the info structure for reference. */ 4035 /* The created xterm self destructs when the last channel is removed */ 4036 /* and it appears that perl5db.pl (perl debugger) does this routinely */ 4037 /* So leave this assigned. */ 4038 device_name_dsc.dsc$w_length = device_name_len; 4039 status = sys$assign(&device_name_dsc,&info->xchan,0,0); 4040 if (!$VMS_STATUS_SUCCESS(status)) { 4041 SETERRNO(EVMSERR, status); 4042 return NULL; 4043 } 4044 info->xchan_valid = 1; 4045 4046 /* Now create a mailbox to be read by the application */ 4047 4048 create_mbx(&p_chan, &d_mbx1); 4049 4050 /* write the name of the created terminal to the mailbox */ 4051 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW, 4052 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0); 4053 4054 if (!$VMS_STATUS_SUCCESS(status)) { 4055 SETERRNO(EVMSERR, status); 4056 return NULL; 4057 } 4058 4059 info->fp = PerlIO_open(mbx1, mode); 4060 4061 /* Done with this channel */ 4062 sys$dassgn(p_chan); 4063 4064 /* If any errors, then clean up */ 4065 if (!info->fp) { 4066 n = sizeof(Info); 4067 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4068 return NULL; 4069 } 4070 4071 /* All done */ 4072 return info->fp; 4073 } 4074 4075 static I32 my_pclose_pinfo(pTHX_ pInfo info); 4076 4077 static PerlIO * 4078 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) 4079 { 4080 static int handler_set_up = FALSE; 4081 PerlIO * ret_fp; 4082 unsigned long int sts, flags = CLI$M_NOWAIT; 4083 /* The use of a GLOBAL table (as was done previously) rendered 4084 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL 4085 * environment. Hence we've switched to LOCAL symbol table. 4086 */ 4087 unsigned int table = LIB$K_CLI_LOCAL_SYM; 4088 int j, wait = 0, n; 4089 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe; 4090 char *in, *out, *err, mbx[512]; 4091 FILE *tpipe = 0; 4092 char tfilebuf[NAM$C_MAXRSS+1]; 4093 pInfo info = NULL; 4094 char cmd_sym_name[20]; 4095 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T, 4096 DSC$K_CLASS_S, symbol}; 4097 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T, 4098 DSC$K_CLASS_S, 0}; 4099 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T, 4100 DSC$K_CLASS_S, cmd_sym_name}; 4101 struct dsc$descriptor_s *vmscmd; 4102 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN"); 4103 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT"); 4104 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR"); 4105 4106 /* Check here for Xterm create request. This means looking for 4107 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it 4108 * is possible to create an xterm. 4109 */ 4110 if (*in_mode == 'r') { 4111 PerlIO * xterm_fd; 4112 4113 #if defined(PERL_IMPLICIT_CONTEXT) 4114 /* Can not fork an xterm with a NULL context */ 4115 /* This probably could never happen */ 4116 xterm_fd = NULL; 4117 if (aTHX != NULL) 4118 #endif 4119 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode); 4120 if (xterm_fd != NULL) 4121 return xterm_fd; 4122 } 4123 4124 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */ 4125 4126 /* once-per-program initialization... 4127 note that the SETAST calls and the dual test of pipe_ef 4128 makes sure that only the FIRST thread through here does 4129 the initialization...all other threads wait until it's 4130 done. 4131 4132 Yeah, uglier than a pthread call, it's got all the stuff inline 4133 rather than in a separate routine. 4134 */ 4135 4136 if (!pipe_ef) { 4137 _ckvmssts_noperl(sys$setast(0)); 4138 if (!pipe_ef) { 4139 unsigned long int pidcode = JPI$_PID; 4140 $DESCRIPTOR(d_delay, RETRY_DELAY); 4141 _ckvmssts_noperl(lib$get_ef(&pipe_ef)); 4142 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4143 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime)); 4144 } 4145 if (!handler_set_up) { 4146 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock)); 4147 handler_set_up = TRUE; 4148 } 4149 _ckvmssts_noperl(sys$setast(1)); 4150 } 4151 4152 /* see if we can find a VMSPIPE.COM */ 4153 4154 tfilebuf[0] = '@'; 4155 vmspipe = find_vmspipe(aTHX); 4156 if (vmspipe) { 4157 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1; 4158 } else { /* uh, oh...we're in tempfile hell */ 4159 tpipe = vmspipe_tempfile(aTHX); 4160 if (!tpipe) { /* a fish popular in Boston */ 4161 if (ckWARN(WARN_PIPE)) { 4162 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping"); 4163 } 4164 return NULL; 4165 } 4166 fgetname(tpipe,tfilebuf+1,1); 4167 vmspipedsc.dsc$w_length = strlen(tfilebuf); 4168 } 4169 vmspipedsc.dsc$a_pointer = tfilebuf; 4170 4171 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd); 4172 if (!(sts & 1)) { 4173 switch (sts) { 4174 case RMS$_FNF: case RMS$_DNF: 4175 set_errno(ENOENT); break; 4176 case RMS$_DIR: 4177 set_errno(ENOTDIR); break; 4178 case RMS$_DEV: 4179 set_errno(ENODEV); break; 4180 case RMS$_PRV: 4181 set_errno(EACCES); break; 4182 case RMS$_SYN: 4183 set_errno(EINVAL); break; 4184 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 4185 set_errno(E2BIG); break; 4186 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 4187 _ckvmssts_noperl(sts); /* fall through */ 4188 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 4189 set_errno(EVMSERR); 4190 } 4191 set_vaxc_errno(sts); 4192 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) { 4193 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno)); 4194 } 4195 *psts = sts; 4196 return NULL; 4197 } 4198 n = sizeof(Info); 4199 _ckvmssts_noperl(lib$get_vm(&n, &info)); 4200 4201 my_strlcpy(mode, in_mode, sizeof(mode)); 4202 info->mode = *mode; 4203 info->done = FALSE; 4204 info->completion = 0; 4205 info->closing = FALSE; 4206 info->in = 0; 4207 info->out = 0; 4208 info->err = 0; 4209 info->fp = NULL; 4210 info->useFILE = 0; 4211 info->waiting = 0; 4212 info->in_done = TRUE; 4213 info->out_done = TRUE; 4214 info->err_done = TRUE; 4215 info->xchan = 0; 4216 info->xchan_valid = 0; 4217 4218 in = (char *)PerlMem_malloc(VMS_MAXRSS); 4219 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4220 out = (char *)PerlMem_malloc(VMS_MAXRSS); 4221 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4222 err = (char *)PerlMem_malloc(VMS_MAXRSS); 4223 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM); 4224 4225 in[0] = out[0] = err[0] = '\0'; 4226 4227 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */ 4228 info->useFILE = 1; 4229 strcpy(p,p+1); 4230 } 4231 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */ 4232 wait = 1; 4233 strcpy(p,p+1); 4234 } 4235 4236 if (*mode == 'r') { /* piping from subroutine */ 4237 4238 info->out = pipe_infromchild_setup(aTHX_ mbx,out); 4239 if (info->out) { 4240 info->out->pipe_done = &info->out_done; 4241 info->out_done = FALSE; 4242 info->out->info = info; 4243 } 4244 if (!info->useFILE) { 4245 info->fp = PerlIO_open(mbx, mode); 4246 } else { 4247 info->fp = (PerlIO *) freopen(mbx, mode, stdin); 4248 vmssetuserlnm("SYS$INPUT", mbx); 4249 } 4250 4251 if (!info->fp && info->out) { 4252 sys$cancel(info->out->chan_out); 4253 4254 while (!info->out_done) { 4255 int done; 4256 _ckvmssts_noperl(sys$setast(0)); 4257 done = info->out_done; 4258 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4259 _ckvmssts_noperl(sys$setast(1)); 4260 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4261 } 4262 4263 if (info->out->buf) { 4264 n = info->out->bufsize * sizeof(char); 4265 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf)); 4266 } 4267 n = sizeof(Pipe); 4268 _ckvmssts_noperl(lib$free_vm(&n, &info->out)); 4269 n = sizeof(Info); 4270 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4271 *psts = RMS$_FNF; 4272 return NULL; 4273 } 4274 4275 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4276 if (info->err) { 4277 info->err->pipe_done = &info->err_done; 4278 info->err_done = FALSE; 4279 info->err->info = info; 4280 } 4281 4282 } else if (*mode == 'w') { /* piping to subroutine */ 4283 4284 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4285 if (info->out) { 4286 info->out->pipe_done = &info->out_done; 4287 info->out_done = FALSE; 4288 info->out->info = info; 4289 } 4290 4291 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4292 if (info->err) { 4293 info->err->pipe_done = &info->err_done; 4294 info->err_done = FALSE; 4295 info->err->info = info; 4296 } 4297 4298 info->in = pipe_tochild_setup(aTHX_ in,mbx); 4299 if (!info->useFILE) { 4300 info->fp = PerlIO_open(mbx, mode); 4301 } else { 4302 info->fp = (PerlIO *) freopen(mbx, mode, stdout); 4303 vmssetuserlnm("SYS$OUTPUT", mbx); 4304 } 4305 4306 if (info->in) { 4307 info->in->pipe_done = &info->in_done; 4308 info->in_done = FALSE; 4309 info->in->info = info; 4310 } 4311 4312 /* error cleanup */ 4313 if (!info->fp && info->in) { 4314 info->done = TRUE; 4315 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0, 4316 0, 0, 0, 0, 0, 0, 0, 0)); 4317 4318 while (!info->in_done) { 4319 int done; 4320 _ckvmssts_noperl(sys$setast(0)); 4321 done = info->in_done; 4322 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4323 _ckvmssts_noperl(sys$setast(1)); 4324 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4325 } 4326 4327 if (info->in->buf) { 4328 n = info->in->bufsize * sizeof(char); 4329 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf)); 4330 } 4331 n = sizeof(Pipe); 4332 _ckvmssts_noperl(lib$free_vm(&n, &info->in)); 4333 n = sizeof(Info); 4334 _ckvmssts_noperl(lib$free_vm(&n, &info)); 4335 *psts = RMS$_FNF; 4336 return NULL; 4337 } 4338 4339 4340 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */ 4341 /* Let the child inherit standard input, unless it's a directory. */ 4342 Stat_t st; 4343 if (my_trnlnm("SYS$INPUT", in, 0)) { 4344 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode)) 4345 *in = '\0'; 4346 } 4347 4348 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4349 if (info->out) { 4350 info->out->pipe_done = &info->out_done; 4351 info->out_done = FALSE; 4352 info->out->info = info; 4353 } 4354 4355 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4356 if (info->err) { 4357 info->err->pipe_done = &info->err_done; 4358 info->err_done = FALSE; 4359 info->err->info = info; 4360 } 4361 } 4362 4363 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol)); 4364 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table)); 4365 4366 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol)); 4367 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table)); 4368 4369 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol)); 4370 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table)); 4371 4372 /* Done with the names for the pipes */ 4373 PerlMem_free(err); 4374 PerlMem_free(out); 4375 PerlMem_free(in); 4376 4377 p = vmscmd->dsc$a_pointer; 4378 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */ 4379 if (*p == '$') p++; /* remove leading $ */ 4380 while (*p == ' ' || *p == '\t') p++; 4381 4382 for (j = 0; j < 4; j++) { 4383 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4384 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4385 4386 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol)); 4387 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table)); 4388 4389 if (strlen(p) > MAX_DCL_SYMBOL) { 4390 p += MAX_DCL_SYMBOL; 4391 } else { 4392 p += strlen(p); 4393 } 4394 } 4395 _ckvmssts_noperl(sys$setast(0)); 4396 info->next=open_pipes; /* prepend to list */ 4397 open_pipes=info; 4398 _ckvmssts_noperl(sys$setast(1)); 4399 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT 4400 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still 4401 * have SYS$COMMAND if we need it. 4402 */ 4403 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags, 4404 0, &info->pid, &info->completion, 4405 0, popen_completion_ast,info,0,0,0)); 4406 4407 /* if we were using a tempfile, close it now */ 4408 4409 if (tpipe) fclose(tpipe); 4410 4411 /* once the subprocess is spawned, it has copied the symbols and 4412 we can get rid of ours */ 4413 4414 for (j = 0; j < 4; j++) { 4415 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4416 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4417 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table)); 4418 } 4419 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table)); 4420 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table)); 4421 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table)); 4422 vms_execfree(vmscmd); 4423 4424 #ifdef PERL_IMPLICIT_CONTEXT 4425 if (aTHX) 4426 #endif 4427 PL_forkprocess = info->pid; 4428 4429 ret_fp = info->fp; 4430 if (wait) { 4431 dSAVEDERRNO; 4432 int done = 0; 4433 while (!done) { 4434 _ckvmssts_noperl(sys$setast(0)); 4435 done = info->done; 4436 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef)); 4437 _ckvmssts_noperl(sys$setast(1)); 4438 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef)); 4439 } 4440 *psts = info->completion; 4441 /* Caller thinks it is open and tries to close it. */ 4442 /* This causes some problems, as it changes the error status */ 4443 /* my_pclose(info->fp); */ 4444 4445 /* If we did not have a file pointer open, then we have to */ 4446 /* clean up here or eventually we will run out of something */ 4447 SAVE_ERRNO; 4448 if (info->fp == NULL) { 4449 my_pclose_pinfo(aTHX_ info); 4450 } 4451 RESTORE_ERRNO; 4452 4453 } else { 4454 *psts = info->pid; 4455 } 4456 return ret_fp; 4457 } /* end of safe_popen */ 4458 4459 4460 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/ 4461 PerlIO * 4462 Perl_my_popen(pTHX_ const char *cmd, const char *mode) 4463 { 4464 int sts; 4465 TAINT_ENV(); 4466 TAINT_PROPER("popen"); 4467 PERL_FLUSHALL_FOR_CHILD; 4468 return safe_popen(aTHX_ cmd,mode,&sts); 4469 } 4470 4471 /*}}}*/ 4472 4473 4474 /* Routine to close and cleanup a pipe info structure */ 4475 4476 static I32 my_pclose_pinfo(pTHX_ pInfo info) { 4477 4478 unsigned long int retsts; 4479 int done, n; 4480 pInfo next, last; 4481 4482 /* If we were writing to a subprocess, insure that someone reading from 4483 * the mailbox gets an EOF. It looks like a simple fclose() doesn't 4484 * produce an EOF record in the mailbox. 4485 * 4486 * well, at least sometimes it *does*, so we have to watch out for 4487 * the first EOF closing the pipe (and DASSGN'ing the channel)... 4488 */ 4489 if (info->fp) { 4490 if (!info->useFILE 4491 #if defined(USE_ITHREADS) 4492 && my_perl 4493 #endif 4494 #ifdef USE_PERLIO 4495 && PL_perlio_fd_refcnt 4496 #endif 4497 ) 4498 PerlIO_flush(info->fp); 4499 else 4500 fflush((FILE *)info->fp); 4501 } 4502 4503 _ckvmssts(sys$setast(0)); 4504 info->closing = TRUE; 4505 done = info->done && info->in_done && info->out_done && info->err_done; 4506 /* hanging on write to Perl's input? cancel it */ 4507 if (info->mode == 'r' && info->out && !info->out_done) { 4508 if (info->out->chan_out) { 4509 _ckvmssts(sys$cancel(info->out->chan_out)); 4510 if (!info->out->chan_in) { /* EOF generation, need AST */ 4511 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0)); 4512 } 4513 } 4514 } 4515 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */ 4516 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 4517 0, 0, 0, 0, 0, 0)); 4518 _ckvmssts(sys$setast(1)); 4519 if (info->fp) { 4520 if (!info->useFILE 4521 #if defined(USE_ITHREADS) 4522 && my_perl 4523 #endif 4524 #ifdef USE_PERLIO 4525 && PL_perlio_fd_refcnt 4526 #endif 4527 ) 4528 PerlIO_close(info->fp); 4529 else 4530 fclose((FILE *)info->fp); 4531 } 4532 /* 4533 we have to wait until subprocess completes, but ALSO wait until all 4534 the i/o completes...otherwise we'll be freeing the "info" structure 4535 that the i/o ASTs could still be using... 4536 */ 4537 4538 while (!done) { 4539 _ckvmssts(sys$setast(0)); 4540 done = info->done && info->in_done && info->out_done && info->err_done; 4541 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4542 _ckvmssts(sys$setast(1)); 4543 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4544 } 4545 retsts = info->completion; 4546 4547 /* remove from list of open pipes */ 4548 _ckvmssts(sys$setast(0)); 4549 last = NULL; 4550 for (next = open_pipes; next != NULL; last = next, next = next->next) { 4551 if (next == info) 4552 break; 4553 } 4554 4555 if (last) 4556 last->next = info->next; 4557 else 4558 open_pipes = info->next; 4559 _ckvmssts(sys$setast(1)); 4560 4561 /* free buffers and structures */ 4562 4563 if (info->in) { 4564 if (info->in->buf) { 4565 n = info->in->bufsize * sizeof(char); 4566 _ckvmssts(lib$free_vm(&n, &info->in->buf)); 4567 } 4568 n = sizeof(Pipe); 4569 _ckvmssts(lib$free_vm(&n, &info->in)); 4570 } 4571 if (info->out) { 4572 if (info->out->buf) { 4573 n = info->out->bufsize * sizeof(char); 4574 _ckvmssts(lib$free_vm(&n, &info->out->buf)); 4575 } 4576 n = sizeof(Pipe); 4577 _ckvmssts(lib$free_vm(&n, &info->out)); 4578 } 4579 if (info->err) { 4580 if (info->err->buf) { 4581 n = info->err->bufsize * sizeof(char); 4582 _ckvmssts(lib$free_vm(&n, &info->err->buf)); 4583 } 4584 n = sizeof(Pipe); 4585 _ckvmssts(lib$free_vm(&n, &info->err)); 4586 } 4587 n = sizeof(Info); 4588 _ckvmssts(lib$free_vm(&n, &info)); 4589 4590 return retsts; 4591 } 4592 4593 4594 /*{{{ I32 my_pclose(PerlIO *fp)*/ 4595 I32 Perl_my_pclose(pTHX_ PerlIO *fp) 4596 { 4597 pInfo info, last = NULL; 4598 I32 ret_status; 4599 4600 /* Fixme - need ast and mutex protection here */ 4601 for (info = open_pipes; info != NULL; last = info, info = info->next) 4602 if (info->fp == fp) break; 4603 4604 if (info == NULL) { /* no such pipe open */ 4605 set_errno(ECHILD); /* quoth POSIX */ 4606 set_vaxc_errno(SS$_NONEXPR); 4607 return -1; 4608 } 4609 4610 ret_status = my_pclose_pinfo(aTHX_ info); 4611 4612 return ret_status; 4613 4614 } /* end of my_pclose() */ 4615 4616 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000 4617 /* Roll our own prototype because we want this regardless of whether 4618 * _VMS_WAIT is defined. 4619 */ 4620 4621 #ifdef __cplusplus 4622 extern "C" { 4623 #endif 4624 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options ); 4625 #ifdef __cplusplus 4626 } 4627 #endif 4628 4629 #endif 4630 /* sort-of waitpid; special handling of pipe clean-up for subprocesses 4631 created with popen(); otherwise partially emulate waitpid() unless 4632 we have a suitable one from the CRTL that came with VMS 7.2 and later. 4633 Also check processes not considered by the CRTL waitpid(). 4634 */ 4635 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/ 4636 Pid_t 4637 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags) 4638 { 4639 pInfo info; 4640 int done; 4641 int sts; 4642 int j; 4643 4644 if (statusp) *statusp = 0; 4645 4646 for (info = open_pipes; info != NULL; info = info->next) 4647 if (info->pid == pid) break; 4648 4649 if (info != NULL) { /* we know about this child */ 4650 while (!info->done) { 4651 _ckvmssts(sys$setast(0)); 4652 done = info->done; 4653 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4654 _ckvmssts(sys$setast(1)); 4655 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4656 } 4657 4658 if (statusp) *statusp = info->completion; 4659 return pid; 4660 } 4661 4662 /* child that already terminated? */ 4663 4664 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) { 4665 if (closed_list[j].pid == pid) { 4666 if (statusp) *statusp = closed_list[j].completion; 4667 return pid; 4668 } 4669 } 4670 4671 /* fall through if this child is not one of our own pipe children */ 4672 4673 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000 4674 4675 /* waitpid() became available in the CRTL as of VMS 7.0, but only 4676 * in 7.2 did we get a version that fills in the VMS completion 4677 * status as Perl has always tried to do. 4678 */ 4679 4680 sts = __vms_waitpid( pid, statusp, flags ); 4681 4682 if ( sts == 0 || !(sts == -1 && errno == ECHILD) ) 4683 return sts; 4684 4685 /* If the real waitpid tells us the child does not exist, we 4686 * fall through here to implement waiting for a child that 4687 * was created by some means other than exec() (say, spawned 4688 * from DCL) or to wait for a process that is not a subprocess 4689 * of the current process. 4690 */ 4691 4692 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */ 4693 4694 { 4695 $DESCRIPTOR(intdsc,"0 00:00:01"); 4696 unsigned long int ownercode = JPI$_OWNER, ownerpid; 4697 unsigned long int pidcode = JPI$_PID, mypid; 4698 unsigned long int interval[2]; 4699 unsigned int jpi_iosb[2]; 4700 struct itmlst_3 jpilist[2] = { 4701 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0}, 4702 { 0, 0, 0, 0} 4703 }; 4704 4705 if (pid <= 0) { 4706 /* Sorry folks, we don't presently implement rooting around for 4707 the first child we can find, and we definitely don't want to 4708 pass a pid of -1 to $getjpi, where it is a wildcard operation. 4709 */ 4710 set_errno(ENOTSUP); 4711 return -1; 4712 } 4713 4714 /* Get the owner of the child so I can warn if it's not mine. If the 4715 * process doesn't exist or I don't have the privs to look at it, 4716 * I can go home early. 4717 */ 4718 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL); 4719 if (sts & 1) sts = jpi_iosb[0]; 4720 if (!(sts & 1)) { 4721 switch (sts) { 4722 case SS$_NONEXPR: 4723 set_errno(ECHILD); 4724 break; 4725 case SS$_NOPRIV: 4726 set_errno(EACCES); 4727 break; 4728 default: 4729 _ckvmssts(sts); 4730 } 4731 set_vaxc_errno(sts); 4732 return -1; 4733 } 4734 4735 if (ckWARN(WARN_EXEC)) { 4736 /* remind folks they are asking for non-standard waitpid behavior */ 4737 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4738 if (ownerpid != mypid) 4739 Perl_warner(aTHX_ packWARN(WARN_EXEC), 4740 "waitpid: process %x is not a child of process %x", 4741 pid,mypid); 4742 } 4743 4744 /* simply check on it once a second until it's not there anymore. */ 4745 4746 _ckvmssts(sys$bintim(&intdsc,interval)); 4747 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) { 4748 _ckvmssts(sys$schdwk(0,0,interval,0)); 4749 _ckvmssts(sys$hiber()); 4750 } 4751 if (sts == SS$_NONEXPR) sts = SS$_NORMAL; 4752 4753 _ckvmssts(sts); 4754 return pid; 4755 } 4756 } /* end of waitpid() */ 4757 /*}}}*/ 4758 /*}}}*/ 4759 /*}}}*/ 4760 4761 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */ 4762 char * 4763 my_gconvert(double val, int ndig, int trail, char *buf) 4764 { 4765 static char __gcvtbuf[DBL_DIG+1]; 4766 char *loc; 4767 4768 loc = buf ? buf : __gcvtbuf; 4769 4770 if (val) { 4771 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG; 4772 return gcvt(val,ndig,loc); 4773 } 4774 else { 4775 loc[0] = '0'; loc[1] = '\0'; 4776 return loc; 4777 } 4778 4779 } 4780 /*}}}*/ 4781 4782 #if defined(__VAX) || !defined(NAML$C_MAXRSS) 4783 static int rms_free_search_context(struct FAB * fab) 4784 { 4785 struct NAM * nam; 4786 4787 nam = fab->fab$l_nam; 4788 nam->nam$b_nop |= NAM$M_SYNCHK; 4789 nam->nam$l_rlf = NULL; 4790 fab->fab$b_dns = 0; 4791 return sys$parse(fab, NULL, NULL); 4792 } 4793 4794 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam 4795 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0; 4796 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt) 4797 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt) 4798 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt)) 4799 #define rms_nam_esll(nam) nam.nam$b_esl 4800 #define rms_nam_esl(nam) nam.nam$b_esl 4801 #define rms_nam_name(nam) nam.nam$l_name 4802 #define rms_nam_namel(nam) nam.nam$l_name 4803 #define rms_nam_type(nam) nam.nam$l_type 4804 #define rms_nam_typel(nam) nam.nam$l_type 4805 #define rms_nam_ver(nam) nam.nam$l_ver 4806 #define rms_nam_verl(nam) nam.nam$l_ver 4807 #define rms_nam_rsll(nam) nam.nam$b_rsl 4808 #define rms_nam_rsl(nam) nam.nam$b_rsl 4809 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam 4810 #define rms_set_fna(fab, nam, name, size) \ 4811 { fab.fab$b_fns = size; fab.fab$l_fna = name; } 4812 #define rms_get_fna(fab, nam) fab.fab$l_fna 4813 #define rms_set_dna(fab, nam, name, size) \ 4814 { fab.fab$b_dns = size; fab.fab$l_dna = name; } 4815 #define rms_nam_dns(fab, nam) fab.fab$b_dns 4816 #define rms_set_esa(nam, name, size) \ 4817 { nam.nam$b_ess = size; nam.nam$l_esa = name; } 4818 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4819 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;} 4820 #define rms_set_rsa(nam, name, size) \ 4821 { nam.nam$l_rsa = name; nam.nam$b_rss = size; } 4822 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4823 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; } 4824 #define rms_nam_name_type_l_size(nam) \ 4825 (nam.nam$b_name + nam.nam$b_type) 4826 #else 4827 static int rms_free_search_context(struct FAB * fab) 4828 { 4829 struct NAML * nam; 4830 4831 nam = fab->fab$l_naml; 4832 nam->naml$b_nop |= NAM$M_SYNCHK; 4833 nam->naml$l_rlf = NULL; 4834 nam->naml$l_long_defname_size = 0; 4835 4836 fab->fab$b_dns = 0; 4837 return sys$parse(fab, NULL, NULL); 4838 } 4839 4840 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml 4841 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0; 4842 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt) 4843 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt) 4844 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt)) 4845 #define rms_nam_esll(nam) nam.naml$l_long_expand_size 4846 #define rms_nam_esl(nam) nam.naml$b_esl 4847 #define rms_nam_name(nam) nam.naml$l_name 4848 #define rms_nam_namel(nam) nam.naml$l_long_name 4849 #define rms_nam_type(nam) nam.naml$l_type 4850 #define rms_nam_typel(nam) nam.naml$l_long_type 4851 #define rms_nam_ver(nam) nam.naml$l_ver 4852 #define rms_nam_verl(nam) nam.naml$l_long_ver 4853 #define rms_nam_rsll(nam) nam.naml$l_long_result_size 4854 #define rms_nam_rsl(nam) nam.naml$b_rsl 4855 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam 4856 #define rms_set_fna(fab, nam, name, size) \ 4857 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \ 4858 nam.naml$l_long_filename_size = size; \ 4859 nam.naml$l_long_filename = name;} 4860 #define rms_get_fna(fab, nam) nam.naml$l_long_filename 4861 #define rms_set_dna(fab, nam, name, size) \ 4862 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \ 4863 nam.naml$l_long_defname_size = size; \ 4864 nam.naml$l_long_defname = name; } 4865 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size 4866 #define rms_set_esa(nam, name, size) \ 4867 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \ 4868 nam.naml$l_long_expand_alloc = size; \ 4869 nam.naml$l_long_expand = name; } 4870 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4871 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \ 4872 nam.naml$l_long_expand = l_name; \ 4873 nam.naml$l_long_expand_alloc = l_size; } 4874 #define rms_set_rsa(nam, name, size) \ 4875 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \ 4876 nam.naml$l_long_result = name; \ 4877 nam.naml$l_long_result_alloc = size; } 4878 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4879 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \ 4880 nam.naml$l_long_result = l_name; \ 4881 nam.naml$l_long_result_alloc = l_size; } 4882 #define rms_nam_name_type_l_size(nam) \ 4883 (nam.naml$l_long_name_size + nam.naml$l_long_type_size) 4884 #endif 4885 4886 4887 /* rms_erase 4888 * The CRTL for 8.3 and later can create symbolic links in any mode, 4889 * however in 8.3 the unlink/remove/delete routines will only properly handle 4890 * them if one of the PCP modes is active. 4891 */ 4892 static int rms_erase(const char * vmsname) 4893 { 4894 int status; 4895 struct FAB myfab = cc$rms_fab; 4896 rms_setup_nam(mynam); 4897 4898 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */ 4899 rms_bind_fab_nam(myfab, mynam); 4900 4901 #ifdef NAML$M_OPEN_SPECIAL 4902 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 4903 #endif 4904 4905 status = sys$erase(&myfab, 0, 0); 4906 4907 return status; 4908 } 4909 4910 4911 static int 4912 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc, 4913 const struct dsc$descriptor_s * vms_dst_dsc, 4914 unsigned long flags) 4915 { 4916 /* VMS and UNIX handle file permissions differently and the 4917 * the same ACL trick may be needed for renaming files, 4918 * especially if they are directories. 4919 */ 4920 4921 /* todo: get kill_file and rename to share common code */ 4922 /* I can not find online documentation for $change_acl 4923 * it appears to be replaced by $set_security some time ago */ 4924 4925 const unsigned int access_mode = 0; 4926 $DESCRIPTOR(obj_file_dsc,"FILE"); 4927 char *vmsname; 4928 char *rslt; 4929 unsigned long int jpicode = JPI$_UIC; 4930 int aclsts, fndsts, rnsts = -1; 4931 unsigned int ctx = 0; 4932 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 4933 struct dsc$descriptor_s * clean_dsc; 4934 4935 struct myacedef { 4936 unsigned char myace$b_length; 4937 unsigned char myace$b_type; 4938 unsigned short int myace$w_flags; 4939 unsigned long int myace$l_access; 4940 unsigned long int myace$l_ident; 4941 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 4942 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 4943 0}, 4944 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 4945 4946 struct item_list_3 4947 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0}, 4948 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0}, 4949 {0,0,0,0}}, 4950 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}}, 4951 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0}, 4952 {0,0,0,0}}; 4953 4954 4955 /* Expand the input spec using RMS, since we do not want to put 4956 * ACLs on the target of a symbolic link */ 4957 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1); 4958 if (vmsname == NULL) 4959 return SS$_INSFMEM; 4960 4961 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer, 4962 vmsname, 4963 PERL_RMSEXPAND_M_SYMLINK); 4964 if (rslt == NULL) { 4965 PerlMem_free(vmsname); 4966 return SS$_INSFMEM; 4967 } 4968 4969 /* So we get our own UIC to use as a rights identifier, 4970 * and the insert an ACE at the head of the ACL which allows us 4971 * to delete the file. 4972 */ 4973 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 4974 4975 fildsc.dsc$w_length = strlen(vmsname); 4976 fildsc.dsc$a_pointer = vmsname; 4977 ctx = 0; 4978 newace.myace$l_ident = oldace.myace$l_ident; 4979 rnsts = SS$_ABORT; 4980 4981 /* Grab any existing ACEs with this identifier in case we fail */ 4982 clean_dsc = &fildsc; 4983 aclsts = fndsts = sys$get_security(&obj_file_dsc, 4984 &fildsc, 4985 NULL, 4986 OSS$M_WLOCK, 4987 findlst, 4988 &ctx, 4989 &access_mode); 4990 4991 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) { 4992 /* Add the new ACE . . . */ 4993 4994 /* if the sys$get_security succeeded, then ctx is valid, and the 4995 * object/file descriptors will be ignored. But otherwise they 4996 * are needed 4997 */ 4998 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL, 4999 OSS$M_RELCTX, addlst, &ctx, &access_mode); 5000 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 5001 set_errno(EVMSERR); 5002 set_vaxc_errno(aclsts); 5003 PerlMem_free(vmsname); 5004 return aclsts; 5005 } 5006 5007 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc, 5008 NULL, NULL, 5009 &flags, 5010 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 5011 5012 if ($VMS_STATUS_SUCCESS(rnsts)) { 5013 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc; 5014 } 5015 5016 /* Put things back the way they were. */ 5017 ctx = 0; 5018 aclsts = sys$get_security(&obj_file_dsc, 5019 clean_dsc, 5020 NULL, 5021 OSS$M_WLOCK, 5022 findlst, 5023 &ctx, 5024 &access_mode); 5025 5026 if ($VMS_STATUS_SUCCESS(aclsts)) { 5027 int sec_flags; 5028 5029 sec_flags = 0; 5030 if (!$VMS_STATUS_SUCCESS(fndsts)) 5031 sec_flags = OSS$M_RELCTX; 5032 5033 /* Get rid of the new ACE */ 5034 aclsts = sys$set_security(NULL, NULL, NULL, 5035 sec_flags, dellst, &ctx, &access_mode); 5036 5037 /* If there was an old ACE, put it back */ 5038 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) { 5039 addlst[0].bufadr = &oldace; 5040 aclsts = sys$set_security(NULL, NULL, NULL, 5041 OSS$M_RELCTX, addlst, &ctx, &access_mode); 5042 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 5043 set_errno(EVMSERR); 5044 set_vaxc_errno(aclsts); 5045 rnsts = aclsts; 5046 } 5047 } else { 5048 int aclsts2; 5049 5050 /* Try to clear the lock on the ACL list */ 5051 aclsts2 = sys$set_security(NULL, NULL, NULL, 5052 OSS$M_RELCTX, NULL, &ctx, &access_mode); 5053 5054 /* Rename errors are most important */ 5055 if (!$VMS_STATUS_SUCCESS(rnsts)) 5056 aclsts = rnsts; 5057 set_errno(EVMSERR); 5058 set_vaxc_errno(aclsts); 5059 rnsts = aclsts; 5060 } 5061 } 5062 else { 5063 if (aclsts != SS$_ACLEMPTY) 5064 rnsts = aclsts; 5065 } 5066 } 5067 else 5068 rnsts = fndsts; 5069 5070 PerlMem_free(vmsname); 5071 return rnsts; 5072 } 5073 5074 5075 /*{{{int rename(const char *, const char * */ 5076 /* Not exactly what X/Open says to do, but doing it absolutely right 5077 * and efficiently would require a lot more work. This should be close 5078 * enough to pass all but the most strict X/Open compliance test. 5079 */ 5080 int 5081 Perl_rename(pTHX_ const char *src, const char * dst) 5082 { 5083 int retval; 5084 int pre_delete = 0; 5085 int src_sts; 5086 int dst_sts; 5087 Stat_t src_st; 5088 Stat_t dst_st; 5089 5090 /* Validate the source file */ 5091 src_sts = flex_lstat(src, &src_st); 5092 if (src_sts != 0) { 5093 5094 /* No source file or other problem */ 5095 return src_sts; 5096 } 5097 if (src_st.st_devnam[0] == 0) { 5098 /* This may be possible so fail if it is seen. */ 5099 errno = EIO; 5100 return -1; 5101 } 5102 5103 dst_sts = flex_lstat(dst, &dst_st); 5104 if (dst_sts == 0) { 5105 5106 if (dst_st.st_dev != src_st.st_dev) { 5107 /* Must be on the same device */ 5108 errno = EXDEV; 5109 return -1; 5110 } 5111 5112 /* VMS_INO_T_COMPARE is true if the inodes are different 5113 * to match the output of memcmp 5114 */ 5115 5116 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) { 5117 /* That was easy, the files are the same! */ 5118 return 0; 5119 } 5120 5121 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) { 5122 /* If source is a directory, so must be dest */ 5123 errno = EISDIR; 5124 return -1; 5125 } 5126 5127 } 5128 5129 5130 if ((dst_sts == 0) && 5131 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) { 5132 5133 /* We have issues here if vms_unlink_all_versions is set 5134 * If the destination exists, and is not a directory, then 5135 * we must delete in advance. 5136 * 5137 * If the src is a directory, then we must always pre-delete 5138 * the destination. 5139 * 5140 * If we successfully delete the dst in advance, and the rename fails 5141 * X/Open requires that errno be EIO. 5142 * 5143 */ 5144 5145 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) { 5146 int d_sts; 5147 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 5148 S_ISDIR(dst_st.st_mode)); 5149 5150 /* Need to delete all versions ? */ 5151 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) { 5152 int i = 0; 5153 5154 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) { 5155 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0); 5156 if (d_sts != 0) 5157 break; 5158 i++; 5159 5160 /* Make sure that we do not loop forever */ 5161 if (i > 32767) { 5162 errno = EIO; 5163 d_sts = -1; 5164 break; 5165 } 5166 } 5167 } 5168 5169 if (d_sts != 0) 5170 return d_sts; 5171 5172 /* We killed the destination, so only errno now is EIO */ 5173 pre_delete = 1; 5174 } 5175 } 5176 5177 /* Originally the idea was to call the CRTL rename() and only 5178 * try the lib$rename_file if it failed. 5179 * It turns out that there are too many variants in what the 5180 * the CRTL rename might do, so only use lib$rename_file 5181 */ 5182 retval = -1; 5183 5184 { 5185 /* Is the source and dest both in VMS format */ 5186 /* if the source is a directory, then need to fileify */ 5187 /* and dest must be a directory or non-existent. */ 5188 5189 char * vms_dst; 5190 int sts; 5191 char * ret_str; 5192 unsigned long flags; 5193 struct dsc$descriptor_s old_file_dsc; 5194 struct dsc$descriptor_s new_file_dsc; 5195 5196 /* We need to modify the src and dst depending 5197 * on if one or more of them are directories. 5198 */ 5199 5200 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS); 5201 if (vms_dst == NULL) 5202 _ckvmssts_noperl(SS$_INSFMEM); 5203 5204 if (S_ISDIR(src_st.st_mode)) { 5205 char * ret_str; 5206 char * vms_dir_file; 5207 5208 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS); 5209 if (vms_dir_file == NULL) 5210 _ckvmssts_noperl(SS$_INSFMEM); 5211 5212 /* If the dest is a directory, we must remove it */ 5213 if (dst_sts == 0) { 5214 int d_sts; 5215 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1); 5216 if (d_sts != 0) { 5217 PerlMem_free(vms_dst); 5218 errno = EIO; 5219 return d_sts; 5220 } 5221 5222 pre_delete = 1; 5223 } 5224 5225 /* The dest must be a VMS file specification */ 5226 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL); 5227 if (ret_str == NULL) { 5228 PerlMem_free(vms_dst); 5229 errno = EIO; 5230 return -1; 5231 } 5232 5233 /* The source must be a file specification */ 5234 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL); 5235 if (ret_str == NULL) { 5236 PerlMem_free(vms_dst); 5237 PerlMem_free(vms_dir_file); 5238 errno = EIO; 5239 return -1; 5240 } 5241 PerlMem_free(vms_dst); 5242 vms_dst = vms_dir_file; 5243 5244 } else { 5245 /* File to file or file to new dir */ 5246 5247 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) { 5248 /* VMS pathify a dir target */ 5249 ret_str = int_tovmspath(dst, vms_dst, NULL); 5250 if (ret_str == NULL) { 5251 PerlMem_free(vms_dst); 5252 errno = EIO; 5253 return -1; 5254 } 5255 } else { 5256 char * v_spec, * r_spec, * d_spec, * n_spec; 5257 char * e_spec, * vs_spec; 5258 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 5259 5260 /* fileify a target VMS file specification */ 5261 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL); 5262 if (ret_str == NULL) { 5263 PerlMem_free(vms_dst); 5264 errno = EIO; 5265 return -1; 5266 } 5267 5268 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len, 5269 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 5270 &e_len, &vs_spec, &vs_len); 5271 if (sts == 0) { 5272 if (e_len == 0) { 5273 /* Get rid of the version */ 5274 if (vs_len != 0) { 5275 *vs_spec = '\0'; 5276 } 5277 /* Need to specify a '.' so that the extension */ 5278 /* is not inherited */ 5279 strcat(vms_dst,"."); 5280 } 5281 } 5282 } 5283 } 5284 5285 old_file_dsc.dsc$a_pointer = src_st.st_devnam; 5286 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam); 5287 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5288 old_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5289 5290 new_file_dsc.dsc$a_pointer = vms_dst; 5291 new_file_dsc.dsc$w_length = strlen(vms_dst); 5292 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5293 new_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5294 5295 flags = 0; 5296 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5297 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */ 5298 #endif 5299 5300 sts = lib$rename_file(&old_file_dsc, 5301 &new_file_dsc, 5302 NULL, NULL, 5303 &flags, 5304 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 5305 if (!$VMS_STATUS_SUCCESS(sts)) { 5306 5307 /* We could have failed because VMS style permissions do not 5308 * permit renames that UNIX will allow. Just like the hack 5309 * in for kill_file. 5310 */ 5311 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags); 5312 } 5313 5314 PerlMem_free(vms_dst); 5315 if (!$VMS_STATUS_SUCCESS(sts)) { 5316 errno = EIO; 5317 return -1; 5318 } 5319 retval = 0; 5320 } 5321 5322 if (vms_unlink_all_versions) { 5323 /* Now get rid of any previous versions of the source file that 5324 * might still exist 5325 */ 5326 int i = 0; 5327 dSAVEDERRNO; 5328 SAVE_ERRNO; 5329 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam, 5330 S_ISDIR(src_st.st_mode)); 5331 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) { 5332 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam, 5333 S_ISDIR(src_st.st_mode)); 5334 if (src_sts != 0) 5335 break; 5336 i++; 5337 5338 /* Make sure that we do not loop forever */ 5339 if (i > 32767) { 5340 src_sts = -1; 5341 break; 5342 } 5343 } 5344 RESTORE_ERRNO; 5345 } 5346 5347 /* We deleted the destination, so must force the error to be EIO */ 5348 if ((retval != 0) && (pre_delete != 0)) 5349 errno = EIO; 5350 5351 return retval; 5352 } 5353 /*}}}*/ 5354 5355 5356 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/ 5357 /* Shortcut for common case of simple calls to $PARSE and $SEARCH 5358 * to expand file specification. Allows for a single default file 5359 * specification and a simple mask of options. If outbuf is non-NULL, 5360 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which 5361 * the resultant file specification is placed. If outbuf is NULL, the 5362 * resultant file specification is placed into a static buffer. 5363 * The third argument, if non-NULL, is taken to be a default file 5364 * specification string. The fourth argument is unused at present. 5365 * rmesexpand() returns the address of the resultant string if 5366 * successful, and NULL on error. 5367 * 5368 * New functionality for previously unused opts value: 5369 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format. 5370 * PERL_RMSEXPAND_M_LONG - Want output in long formst 5371 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify 5372 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target 5373 */ 5374 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 5375 5376 static char * 5377 int_rmsexpand 5378 (const char *filespec, 5379 char *outbuf, 5380 const char *defspec, 5381 unsigned opts, 5382 int * fs_utf8, 5383 int * dfs_utf8) 5384 { 5385 char * ret_spec; 5386 const char * in_spec; 5387 char * spec_buf; 5388 const char * def_spec; 5389 char * vmsfspec, *vmsdefspec; 5390 char * esa; 5391 char * esal = NULL; 5392 char * outbufl; 5393 struct FAB myfab = cc$rms_fab; 5394 rms_setup_nam(mynam); 5395 STRLEN speclen; 5396 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0; 5397 int sts; 5398 5399 /* temp hack until UTF8 is actually implemented */ 5400 if (fs_utf8 != NULL) 5401 *fs_utf8 = 0; 5402 5403 if (!filespec || !*filespec) { 5404 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL); 5405 return NULL; 5406 } 5407 5408 vmsfspec = NULL; 5409 vmsdefspec = NULL; 5410 outbufl = NULL; 5411 5412 in_spec = filespec; 5413 isunix = 0; 5414 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) { 5415 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 5416 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 5417 5418 /* If this is a UNIX file spec, convert it to VMS */ 5419 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len, 5420 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 5421 &e_len, &vs_spec, &vs_len); 5422 if (sts != 0) { 5423 isunix = 1; 5424 char * ret_spec; 5425 5426 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS); 5427 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5428 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8); 5429 if (ret_spec == NULL) { 5430 PerlMem_free(vmsfspec); 5431 return NULL; 5432 } 5433 in_spec = (const char *)vmsfspec; 5434 5435 /* Unless we are forcing to VMS format, a UNIX input means 5436 * UNIX output, and that requires long names to be used 5437 */ 5438 if ((opts & PERL_RMSEXPAND_M_VMS) == 0) 5439 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5440 opts |= PERL_RMSEXPAND_M_LONG; 5441 #else 5442 NOOP; 5443 #endif 5444 else 5445 isunix = 0; 5446 } 5447 5448 } 5449 5450 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */ 5451 rms_bind_fab_nam(myfab, mynam); 5452 5453 /* Process the default file specification if present */ 5454 def_spec = defspec; 5455 if (defspec && *defspec) { 5456 int t_isunix; 5457 t_isunix = is_unix_filespec(defspec); 5458 if (t_isunix) { 5459 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS); 5460 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5461 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8); 5462 5463 if (ret_spec == NULL) { 5464 /* Clean up and bail */ 5465 PerlMem_free(vmsdefspec); 5466 if (vmsfspec != NULL) 5467 PerlMem_free(vmsfspec); 5468 return NULL; 5469 } 5470 def_spec = (const char *)vmsdefspec; 5471 } 5472 rms_set_dna(myfab, mynam, 5473 (char *)def_spec, strlen(def_spec)); /* cast ok */ 5474 } 5475 5476 /* Now we need the expansion buffers */ 5477 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 5478 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5479 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5480 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 5481 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5482 #endif 5483 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1); 5484 5485 /* If a NAML block is used RMS always writes to the long and short 5486 * addresses unless you suppress the short name. 5487 */ 5488 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5489 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS); 5490 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5491 #endif 5492 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1)); 5493 5494 #ifdef NAM$M_NO_SHORT_UPCASE 5495 if (decc_efs_case_preserve) 5496 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE); 5497 #endif 5498 5499 /* We may not want to follow symbolic links */ 5500 #ifdef NAML$M_OPEN_SPECIAL 5501 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5502 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5503 #endif 5504 5505 /* First attempt to parse as an existing file */ 5506 retsts = sys$parse(&myfab,0,0); 5507 if (!(retsts & STS$K_SUCCESS)) { 5508 5509 /* Could not find the file, try as syntax only if error is not fatal */ 5510 rms_set_nam_nop(mynam, NAM$M_SYNCHK); 5511 if (retsts == RMS$_DNF || 5512 retsts == RMS$_DIR || 5513 retsts == RMS$_DEV || 5514 retsts == RMS$_PRV) { 5515 retsts = sys$parse(&myfab,0,0); 5516 if (retsts & STS$K_SUCCESS) goto int_expanded; 5517 } 5518 5519 /* Still could not parse the file specification */ 5520 /*----------------------------------------------*/ 5521 sts = rms_free_search_context(&myfab); /* Free search context */ 5522 if (vmsdefspec != NULL) 5523 PerlMem_free(vmsdefspec); 5524 if (vmsfspec != NULL) 5525 PerlMem_free(vmsfspec); 5526 if (outbufl != NULL) 5527 PerlMem_free(outbufl); 5528 PerlMem_free(esa); 5529 if (esal != NULL) 5530 PerlMem_free(esal); 5531 set_vaxc_errno(retsts); 5532 if (retsts == RMS$_PRV) set_errno(EACCES); 5533 else if (retsts == RMS$_DEV) set_errno(ENODEV); 5534 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 5535 else set_errno(EVMSERR); 5536 return NULL; 5537 } 5538 retsts = sys$search(&myfab,0,0); 5539 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) { 5540 sts = rms_free_search_context(&myfab); /* Free search context */ 5541 if (vmsdefspec != NULL) 5542 PerlMem_free(vmsdefspec); 5543 if (vmsfspec != NULL) 5544 PerlMem_free(vmsfspec); 5545 if (outbufl != NULL) 5546 PerlMem_free(outbufl); 5547 PerlMem_free(esa); 5548 if (esal != NULL) 5549 PerlMem_free(esal); 5550 set_vaxc_errno(retsts); 5551 if (retsts == RMS$_PRV) set_errno(EACCES); 5552 else set_errno(EVMSERR); 5553 return NULL; 5554 } 5555 5556 /* If the input filespec contained any lowercase characters, 5557 * downcase the result for compatibility with Unix-minded code. */ 5558 int_expanded: 5559 if (!decc_efs_case_preserve) { 5560 char * tbuf; 5561 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++) 5562 if (islower(*tbuf)) { haslower = 1; break; } 5563 } 5564 5565 /* Is a long or a short name expected */ 5566 /*------------------------------------*/ 5567 spec_buf = NULL; 5568 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5569 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5570 if (rms_nam_rsll(mynam)) { 5571 spec_buf = outbufl; 5572 speclen = rms_nam_rsll(mynam); 5573 } 5574 else { 5575 spec_buf = esal; /* Not esa */ 5576 speclen = rms_nam_esll(mynam); 5577 } 5578 } 5579 else { 5580 #endif 5581 if (rms_nam_rsl(mynam)) { 5582 spec_buf = outbuf; 5583 speclen = rms_nam_rsl(mynam); 5584 } 5585 else { 5586 spec_buf = esa; /* Not esal */ 5587 speclen = rms_nam_esl(mynam); 5588 } 5589 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5590 } 5591 #endif 5592 spec_buf[speclen] = '\0'; 5593 5594 /* Trim off null fields added by $PARSE 5595 * If type > 1 char, must have been specified in original or default spec 5596 * (not true for version; $SEARCH may have added version of existing file). 5597 */ 5598 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER); 5599 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5600 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5601 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1); 5602 } 5603 else { 5604 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5605 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1); 5606 } 5607 if (trimver || trimtype) { 5608 if (defspec && *defspec) { 5609 char *defesal = NULL; 5610 char *defesa = NULL; 5611 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5612 if (defesa != NULL) { 5613 struct FAB deffab = cc$rms_fab; 5614 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5615 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5616 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5617 #endif 5618 rms_setup_nam(defnam); 5619 5620 rms_bind_fab_nam(deffab, defnam); 5621 5622 /* Cast ok */ 5623 rms_set_fna 5624 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam)); 5625 5626 /* RMS needs the esa/esal as a work area if wildcards are involved */ 5627 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1); 5628 5629 rms_clear_nam_nop(defnam); 5630 rms_set_nam_nop(defnam, NAM$M_SYNCHK); 5631 #ifdef NAM$M_NO_SHORT_UPCASE 5632 if (decc_efs_case_preserve) 5633 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE); 5634 #endif 5635 #ifdef NAML$M_OPEN_SPECIAL 5636 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5637 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5638 #endif 5639 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) { 5640 if (trimver) { 5641 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER); 5642 } 5643 if (trimtype) { 5644 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE); 5645 } 5646 } 5647 if (defesal != NULL) 5648 PerlMem_free(defesal); 5649 PerlMem_free(defesa); 5650 } else { 5651 _ckvmssts_noperl(SS$_INSFMEM); 5652 } 5653 } 5654 if (trimver) { 5655 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5656 if (*(rms_nam_verl(mynam)) != '\"') 5657 speclen = rms_nam_verl(mynam) - spec_buf; 5658 } 5659 else { 5660 if (*(rms_nam_ver(mynam)) != '\"') 5661 speclen = rms_nam_ver(mynam) - spec_buf; 5662 } 5663 } 5664 if (trimtype) { 5665 /* If we didn't already trim version, copy down */ 5666 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5667 if (speclen > rms_nam_verl(mynam) - spec_buf) 5668 memmove 5669 (rms_nam_typel(mynam), 5670 rms_nam_verl(mynam), 5671 speclen - (rms_nam_verl(mynam) - spec_buf)); 5672 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam); 5673 } 5674 else { 5675 if (speclen > rms_nam_ver(mynam) - spec_buf) 5676 memmove 5677 (rms_nam_type(mynam), 5678 rms_nam_ver(mynam), 5679 speclen - (rms_nam_ver(mynam) - spec_buf)); 5680 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam); 5681 } 5682 } 5683 } 5684 5685 /* Done with these copies of the input files */ 5686 /*-------------------------------------------*/ 5687 if (vmsfspec != NULL) 5688 PerlMem_free(vmsfspec); 5689 if (vmsdefspec != NULL) 5690 PerlMem_free(vmsdefspec); 5691 5692 /* If we just had a directory spec on input, $PARSE "helpfully" 5693 * adds an empty name and type for us */ 5694 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5695 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5696 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) && 5697 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 && 5698 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5699 speclen = rms_nam_namel(mynam) - spec_buf; 5700 } 5701 else 5702 #endif 5703 { 5704 if (rms_nam_name(mynam) == rms_nam_type(mynam) && 5705 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 && 5706 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5707 speclen = rms_nam_name(mynam) - spec_buf; 5708 } 5709 5710 /* Posix format specifications must have matching quotes */ 5711 if (speclen < (VMS_MAXRSS - 1)) { 5712 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) { 5713 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) { 5714 spec_buf[speclen] = '\"'; 5715 speclen++; 5716 } 5717 } 5718 } 5719 spec_buf[speclen] = '\0'; 5720 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf); 5721 5722 /* Have we been working with an expanded, but not resultant, spec? */ 5723 /* Also, convert back to Unix syntax if necessary. */ 5724 { 5725 int rsl; 5726 5727 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5728 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5729 rsl = rms_nam_rsll(mynam); 5730 } else 5731 #endif 5732 { 5733 rsl = rms_nam_rsl(mynam); 5734 } 5735 if (!rsl) { 5736 /* rsl is not present, it means that spec_buf is either */ 5737 /* esa or esal, and needs to be copied to outbuf */ 5738 /* convert to Unix if desired */ 5739 if (isunix) { 5740 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8); 5741 } else { 5742 /* VMS file specs are not in UTF-8 */ 5743 if (fs_utf8 != NULL) 5744 *fs_utf8 = 0; 5745 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS); 5746 ret_spec = outbuf; 5747 } 5748 } 5749 else { 5750 /* Now spec_buf is either outbuf or outbufl */ 5751 /* We need the result into outbuf */ 5752 if (isunix) { 5753 /* If we need this in UNIX, then we need another buffer */ 5754 /* to keep things in order */ 5755 char * src; 5756 char * new_src = NULL; 5757 if (spec_buf == outbuf) { 5758 new_src = (char *)PerlMem_malloc(VMS_MAXRSS); 5759 my_strlcpy(new_src, spec_buf, VMS_MAXRSS); 5760 } else { 5761 src = spec_buf; 5762 } 5763 ret_spec = int_tounixspec(src, outbuf, fs_utf8); 5764 if (new_src) { 5765 PerlMem_free(new_src); 5766 } 5767 } else { 5768 /* VMS file specs are not in UTF-8 */ 5769 if (fs_utf8 != NULL) 5770 *fs_utf8 = 0; 5771 5772 /* Copy the buffer if needed */ 5773 if (outbuf != spec_buf) 5774 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS); 5775 ret_spec = outbuf; 5776 } 5777 } 5778 } 5779 5780 /* Need to clean up the search context */ 5781 rms_set_rsal(mynam, NULL, 0, NULL, 0); 5782 sts = rms_free_search_context(&myfab); /* Free search context */ 5783 5784 /* Clean up the extra buffers */ 5785 if (esal != NULL) 5786 PerlMem_free(esal); 5787 PerlMem_free(esa); 5788 if (outbufl != NULL) 5789 PerlMem_free(outbufl); 5790 5791 /* Return the result */ 5792 return ret_spec; 5793 } 5794 5795 /* Common simple case - Expand an already VMS spec */ 5796 static char * 5797 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) { 5798 opts |= PERL_RMSEXPAND_M_VMS_IN; 5799 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 5800 } 5801 5802 /* Common simple case - Expand to a VMS spec */ 5803 static char * 5804 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) { 5805 opts |= PERL_RMSEXPAND_M_VMS; 5806 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 5807 } 5808 5809 5810 /* Entry point used by perl routines */ 5811 static char * 5812 mp_do_rmsexpand 5813 (pTHX_ const char *filespec, 5814 char *outbuf, 5815 int ts, 5816 const char *defspec, 5817 unsigned opts, 5818 int * fs_utf8, 5819 int * dfs_utf8) 5820 { 5821 static char __rmsexpand_retbuf[VMS_MAXRSS]; 5822 char * expanded, *ret_spec, *ret_buf; 5823 5824 expanded = NULL; 5825 ret_buf = outbuf; 5826 if (ret_buf == NULL) { 5827 if (ts) { 5828 Newx(expanded, VMS_MAXRSS, char); 5829 if (expanded == NULL) 5830 _ckvmssts(SS$_INSFMEM); 5831 ret_buf = expanded; 5832 } else { 5833 ret_buf = __rmsexpand_retbuf; 5834 } 5835 } 5836 5837 5838 ret_spec = int_rmsexpand(filespec, ret_buf, defspec, 5839 opts, fs_utf8, dfs_utf8); 5840 5841 if (ret_spec == NULL) { 5842 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 5843 if (expanded) 5844 Safefree(expanded); 5845 } 5846 5847 return ret_spec; 5848 } 5849 /*}}}*/ 5850 /* External entry points */ 5851 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5852 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); } 5853 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5854 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); } 5855 char *Perl_rmsexpand_utf8 5856 (pTHX_ const char *spec, char *buf, const char *def, 5857 unsigned opt, int * fs_utf8, int * dfs_utf8) 5858 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); } 5859 char *Perl_rmsexpand_utf8_ts 5860 (pTHX_ const char *spec, char *buf, const char *def, 5861 unsigned opt, int * fs_utf8, int * dfs_utf8) 5862 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); } 5863 5864 5865 /* 5866 ** The following routines are provided to make life easier when 5867 ** converting among VMS-style and Unix-style directory specifications. 5868 ** All will take input specifications in either VMS or Unix syntax. On 5869 ** failure, all return NULL. If successful, the routines listed below 5870 ** return a pointer to a buffer containing the appropriately 5871 ** reformatted spec (and, therefore, subsequent calls to that routine 5872 ** will clobber the result), while the routines of the same names with 5873 ** a _ts suffix appended will return a pointer to a mallocd string 5874 ** containing the appropriately reformatted spec. 5875 ** In all cases, only explicit syntax is altered; no check is made that 5876 ** the resulting string is valid or that the directory in question 5877 ** actually exists. 5878 ** 5879 ** fileify_dirspec() - convert a directory spec into the name of the 5880 ** directory file (i.e. what you can stat() to see if it's a dir). 5881 ** The style (VMS or Unix) of the result is the same as the style 5882 ** of the parameter passed in. 5883 ** pathify_dirspec() - convert a directory spec into a path (i.e. 5884 ** what you prepend to a filename to indicate what directory it's in). 5885 ** The style (VMS or Unix) of the result is the same as the style 5886 ** of the parameter passed in. 5887 ** tounixpath() - convert a directory spec into a Unix-style path. 5888 ** tovmspath() - convert a directory spec into a VMS-style path. 5889 ** tounixspec() - convert any file spec into a Unix-style file spec. 5890 ** tovmsspec() - convert any file spec into a VMS-style spec. 5891 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec. 5892 ** 5893 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu> 5894 ** Permission is given to distribute this code as part of the Perl 5895 ** standard distribution under the terms of the GNU General Public 5896 ** License or the Perl Artistic License. Copies of each may be 5897 ** found in the Perl standard distribution. 5898 */ 5899 5900 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 5901 static char * 5902 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl) 5903 { 5904 unsigned long int dirlen, retlen, hasfilename = 0; 5905 char *cp1, *cp2, *lastdir; 5906 char *trndir, *vmsdir; 5907 unsigned short int trnlnm_iter_count; 5908 int sts; 5909 if (utf8_fl != NULL) 5910 *utf8_fl = 0; 5911 5912 if (!dir || !*dir) { 5913 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL; 5914 } 5915 dirlen = strlen(dir); 5916 while (dirlen && dir[dirlen-1] == '/') --dirlen; 5917 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */ 5918 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) { 5919 dir = "/sys$disk"; 5920 dirlen = 9; 5921 } 5922 else 5923 dirlen = 1; 5924 } 5925 if (dirlen > (VMS_MAXRSS - 1)) { 5926 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); 5927 return NULL; 5928 } 5929 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5930 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5931 if (!strpbrk(dir+1,"/]>:") && 5932 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) { 5933 strcpy(trndir,*dir == '/' ? dir + 1: dir); 5934 trnlnm_iter_count = 0; 5935 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) { 5936 trnlnm_iter_count++; 5937 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 5938 } 5939 dirlen = strlen(trndir); 5940 } 5941 else { 5942 memcpy(trndir, dir, dirlen); 5943 trndir[dirlen] = '\0'; 5944 } 5945 5946 /* At this point we are done with *dir and use *trndir which is a 5947 * copy that can be modified. *dir must not be modified. 5948 */ 5949 5950 /* If we were handed a rooted logical name or spec, treat it like a 5951 * simple directory, so that 5952 * $ Define myroot dev:[dir.] 5953 * ... do_fileify_dirspec("myroot",buf,1) ... 5954 * does something useful. 5955 */ 5956 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) { 5957 trndir[--dirlen] = '\0'; 5958 trndir[dirlen-1] = ']'; 5959 } 5960 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) { 5961 trndir[--dirlen] = '\0'; 5962 trndir[dirlen-1] = '>'; 5963 } 5964 5965 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) { 5966 /* If we've got an explicit filename, we can just shuffle the string. */ 5967 if (*(cp1+1)) hasfilename = 1; 5968 /* Similarly, we can just back up a level if we've got multiple levels 5969 of explicit directories in a VMS spec which ends with directories. */ 5970 else { 5971 for (cp2 = cp1; cp2 > trndir; cp2--) { 5972 if (*cp2 == '.') { 5973 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) { 5974 /* fix-me, can not scan EFS file specs backward like this */ 5975 *cp2 = *cp1; *cp1 = '\0'; 5976 hasfilename = 1; 5977 break; 5978 } 5979 } 5980 if (*cp2 == '[' || *cp2 == '<') break; 5981 } 5982 } 5983 } 5984 5985 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 5986 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM); 5987 cp1 = strpbrk(trndir,"]:>"); 5988 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */ 5989 cp1 = strpbrk(cp1+2,"]:>"); 5990 5991 if (hasfilename || !cp1) { /* filename present or not VMS */ 5992 5993 if (trndir[0] == '.') { 5994 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) { 5995 PerlMem_free(trndir); 5996 PerlMem_free(vmsdir); 5997 return int_fileify_dirspec("[]", buf, NULL); 5998 } 5999 else if (trndir[1] == '.' && 6000 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) { 6001 PerlMem_free(trndir); 6002 PerlMem_free(vmsdir); 6003 return int_fileify_dirspec("[-]", buf, NULL); 6004 } 6005 } 6006 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */ 6007 dirlen -= 1; /* to last element */ 6008 lastdir = strrchr(trndir,'/'); 6009 } 6010 else if ((cp1 = strstr(trndir,"/.")) != NULL) { 6011 /* If we have "/." or "/..", VMSify it and let the VMS code 6012 * below expand it, rather than repeating the code to handle 6013 * relative components of a filespec here */ 6014 do { 6015 if (*(cp1+2) == '.') cp1++; 6016 if (*(cp1+2) == '/' || *(cp1+2) == '\0') { 6017 char * ret_chr; 6018 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) { 6019 PerlMem_free(trndir); 6020 PerlMem_free(vmsdir); 6021 return NULL; 6022 } 6023 if (strchr(vmsdir,'/') != NULL) { 6024 /* If int_tovmsspec() returned it, it must have VMS syntax 6025 * delimiters in it, so it's a mixed VMS/Unix spec. We take 6026 * the time to check this here only so we avoid a recursion 6027 * loop; otherwise, gigo. 6028 */ 6029 PerlMem_free(trndir); 6030 PerlMem_free(vmsdir); 6031 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); 6032 return NULL; 6033 } 6034 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) { 6035 PerlMem_free(trndir); 6036 PerlMem_free(vmsdir); 6037 return NULL; 6038 } 6039 ret_chr = int_tounixspec(trndir, buf, utf8_fl); 6040 PerlMem_free(trndir); 6041 PerlMem_free(vmsdir); 6042 return ret_chr; 6043 } 6044 cp1++; 6045 } while ((cp1 = strstr(cp1,"/.")) != NULL); 6046 lastdir = strrchr(trndir,'/'); 6047 } 6048 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) { 6049 char * ret_chr; 6050 /* Ditto for specs that end in an MFD -- let the VMS code 6051 * figure out whether it's a real device or a rooted logical. */ 6052 6053 /* This should not happen any more. Allowing the fake /000000 6054 * in a UNIX pathname causes all sorts of problems when trying 6055 * to run in UNIX emulation. So the VMS to UNIX conversions 6056 * now remove the fake /000000 directories. 6057 */ 6058 6059 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0'; 6060 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) { 6061 PerlMem_free(trndir); 6062 PerlMem_free(vmsdir); 6063 return NULL; 6064 } 6065 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) { 6066 PerlMem_free(trndir); 6067 PerlMem_free(vmsdir); 6068 return NULL; 6069 } 6070 ret_chr = int_tounixspec(trndir, buf, utf8_fl); 6071 PerlMem_free(trndir); 6072 PerlMem_free(vmsdir); 6073 return ret_chr; 6074 } 6075 else { 6076 6077 if ( !(lastdir = cp1 = strrchr(trndir,'/')) && 6078 !(lastdir = cp1 = strrchr(trndir,']')) && 6079 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir; 6080 6081 cp2 = strrchr(cp1,'.'); 6082 if (cp2) { 6083 int e_len, vs_len = 0; 6084 int is_dir = 0; 6085 char * cp3; 6086 cp3 = strchr(cp2,';'); 6087 e_len = strlen(cp2); 6088 if (cp3) { 6089 vs_len = strlen(cp3); 6090 e_len = e_len - vs_len; 6091 } 6092 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len); 6093 if (!is_dir) { 6094 if (!decc_efs_charset) { 6095 /* If this is not EFS, then not a directory */ 6096 PerlMem_free(trndir); 6097 PerlMem_free(vmsdir); 6098 set_errno(ENOTDIR); 6099 set_vaxc_errno(RMS$_DIR); 6100 return NULL; 6101 } 6102 } else { 6103 /* Ok, here we have an issue, technically if a .dir shows */ 6104 /* from inside a directory, then we should treat it as */ 6105 /* xxx^.dir.dir. But we do not have that context at this */ 6106 /* point unless this is totally restructured, so we remove */ 6107 /* The .dir for now, and fix this better later */ 6108 dirlen = cp2 - trndir; 6109 } 6110 if (decc_efs_charset && !strchr(trndir,'/')) { 6111 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */ 6112 char *cp4 = is_dir ? (cp2 - 1) : cp2; 6113 6114 for (; cp4 > cp1; cp4--) { 6115 if (*cp4 == '.') { 6116 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) { 6117 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1); 6118 *cp4 = '^'; 6119 dirlen++; 6120 } 6121 } 6122 } 6123 } 6124 } 6125 6126 } 6127 6128 retlen = dirlen + 6; 6129 memcpy(buf, trndir, dirlen); 6130 buf[dirlen] = '\0'; 6131 6132 /* We've picked up everything up to the directory file name. 6133 Now just add the type and version, and we're set. */ 6134 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) 6135 strcat(buf,".dir"); 6136 else 6137 strcat(buf,".DIR"); 6138 if (!decc_filename_unix_no_version) 6139 strcat(buf,";1"); 6140 PerlMem_free(trndir); 6141 PerlMem_free(vmsdir); 6142 return buf; 6143 } 6144 else { /* VMS-style directory spec */ 6145 6146 char *esa, *esal, term, *cp; 6147 char *my_esa; 6148 int my_esa_len; 6149 unsigned long int cmplen, haslower = 0; 6150 struct FAB dirfab = cc$rms_fab; 6151 rms_setup_nam(savnam); 6152 rms_setup_nam(dirnam); 6153 6154 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 6155 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6156 esal = NULL; 6157 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6158 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 6159 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6160 #endif 6161 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir)); 6162 rms_bind_fab_nam(dirfab, dirnam); 6163 rms_set_dna(dirfab, dirnam, ".DIR;1", 6); 6164 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 6165 #ifdef NAM$M_NO_SHORT_UPCASE 6166 if (decc_efs_case_preserve) 6167 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6168 #endif 6169 6170 for (cp = trndir; *cp; cp++) 6171 if (islower(*cp)) { haslower = 1; break; } 6172 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) { 6173 if ((dirfab.fab$l_sts == RMS$_DIR) || 6174 (dirfab.fab$l_sts == RMS$_DNF) || 6175 (dirfab.fab$l_sts == RMS$_PRV)) { 6176 rms_set_nam_nop(dirnam, NAM$M_SYNCHK); 6177 sts = sys$parse(&dirfab); 6178 } 6179 if (!sts) { 6180 PerlMem_free(esa); 6181 if (esal != NULL) 6182 PerlMem_free(esal); 6183 PerlMem_free(trndir); 6184 PerlMem_free(vmsdir); 6185 set_errno(EVMSERR); 6186 set_vaxc_errno(dirfab.fab$l_sts); 6187 return NULL; 6188 } 6189 } 6190 else { 6191 savnam = dirnam; 6192 /* Does the file really exist? */ 6193 if (sys$search(&dirfab)& STS$K_SUCCESS) { 6194 /* Yes; fake the fnb bits so we'll check type below */ 6195 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER)); 6196 } 6197 else { /* No; just work with potential name */ 6198 if (dirfab.fab$l_sts == RMS$_FNF 6199 || dirfab.fab$l_sts == RMS$_DNF 6200 || dirfab.fab$l_sts == RMS$_FND) 6201 dirnam = savnam; 6202 else { 6203 int fab_sts; 6204 fab_sts = dirfab.fab$l_sts; 6205 sts = rms_free_search_context(&dirfab); 6206 PerlMem_free(esa); 6207 if (esal != NULL) 6208 PerlMem_free(esal); 6209 PerlMem_free(trndir); 6210 PerlMem_free(vmsdir); 6211 set_errno(EVMSERR); set_vaxc_errno(fab_sts); 6212 return NULL; 6213 } 6214 } 6215 } 6216 6217 /* Make sure we are using the right buffer */ 6218 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6219 if (esal != NULL) { 6220 my_esa = esal; 6221 my_esa_len = rms_nam_esll(dirnam); 6222 } else { 6223 #endif 6224 my_esa = esa; 6225 my_esa_len = rms_nam_esl(dirnam); 6226 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6227 } 6228 #endif 6229 my_esa[my_esa_len] = '\0'; 6230 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) { 6231 cp1 = strchr(my_esa,']'); 6232 if (!cp1) cp1 = strchr(my_esa,'>'); 6233 if (cp1) { /* Should always be true */ 6234 my_esa_len -= cp1 - my_esa - 1; 6235 memmove(my_esa, cp1 + 1, my_esa_len); 6236 } 6237 } 6238 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */ 6239 /* Yep; check version while we're at it, if it's there. */ 6240 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4; 6241 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) { 6242 /* Something other than .DIR[;1]. Bzzt. */ 6243 sts = rms_free_search_context(&dirfab); 6244 PerlMem_free(esa); 6245 if (esal != NULL) 6246 PerlMem_free(esal); 6247 PerlMem_free(trndir); 6248 PerlMem_free(vmsdir); 6249 set_errno(ENOTDIR); 6250 set_vaxc_errno(RMS$_DIR); 6251 return NULL; 6252 } 6253 } 6254 6255 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) { 6256 /* They provided at least the name; we added the type, if necessary, */ 6257 my_strlcpy(buf, my_esa, VMS_MAXRSS); 6258 sts = rms_free_search_context(&dirfab); 6259 PerlMem_free(trndir); 6260 PerlMem_free(esa); 6261 if (esal != NULL) 6262 PerlMem_free(esal); 6263 PerlMem_free(vmsdir); 6264 return buf; 6265 } 6266 if ((cp1 = strstr(esa,".][000000]")) != NULL) { 6267 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2; 6268 *cp1 = '\0'; 6269 my_esa_len -= 9; 6270 } 6271 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>'); 6272 if (cp1 == NULL) { /* should never happen */ 6273 sts = rms_free_search_context(&dirfab); 6274 PerlMem_free(trndir); 6275 PerlMem_free(esa); 6276 if (esal != NULL) 6277 PerlMem_free(esal); 6278 PerlMem_free(vmsdir); 6279 return NULL; 6280 } 6281 term = *cp1; 6282 *cp1 = '\0'; 6283 retlen = strlen(my_esa); 6284 cp1 = strrchr(my_esa,'.'); 6285 /* ODS-5 directory specifications can have extra "." in them. */ 6286 /* Fix-me, can not scan EFS file specifications backwards */ 6287 while (cp1 != NULL) { 6288 if ((cp1-1 == my_esa) || (*(cp1-1) != '^')) 6289 break; 6290 else { 6291 cp1--; 6292 while ((cp1 > my_esa) && (*cp1 != '.')) 6293 cp1--; 6294 } 6295 if (cp1 == my_esa) 6296 cp1 = NULL; 6297 } 6298 6299 if ((cp1) != NULL) { 6300 /* There's more than one directory in the path. Just roll back. */ 6301 *cp1 = term; 6302 my_strlcpy(buf, my_esa, VMS_MAXRSS); 6303 } 6304 else { 6305 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) { 6306 /* Go back and expand rooted logical name */ 6307 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL); 6308 #ifdef NAM$M_NO_SHORT_UPCASE 6309 if (decc_efs_case_preserve) 6310 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6311 #endif 6312 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) { 6313 sts = rms_free_search_context(&dirfab); 6314 PerlMem_free(esa); 6315 if (esal != NULL) 6316 PerlMem_free(esal); 6317 PerlMem_free(trndir); 6318 PerlMem_free(vmsdir); 6319 set_errno(EVMSERR); 6320 set_vaxc_errno(dirfab.fab$l_sts); 6321 return NULL; 6322 } 6323 6324 /* This changes the length of the string of course */ 6325 if (esal != NULL) { 6326 my_esa_len = rms_nam_esll(dirnam); 6327 } else { 6328 my_esa_len = rms_nam_esl(dirnam); 6329 } 6330 6331 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */ 6332 cp1 = strstr(my_esa,"]["); 6333 if (!cp1) cp1 = strstr(my_esa,"]<"); 6334 dirlen = cp1 - my_esa; 6335 memcpy(buf, my_esa, dirlen); 6336 if (!strncmp(cp1+2,"000000]",7)) { 6337 buf[dirlen-1] = '\0'; 6338 /* fix-me Not full ODS-5, just extra dots in directories for now */ 6339 cp1 = buf + dirlen - 1; 6340 while (cp1 > buf) 6341 { 6342 if (*cp1 == '[') 6343 break; 6344 if (*cp1 == '.') { 6345 if (*(cp1-1) != '^') 6346 break; 6347 } 6348 cp1--; 6349 } 6350 if (*cp1 == '.') *cp1 = ']'; 6351 else { 6352 memmove(cp1+8, cp1+1, buf+dirlen-cp1); 6353 memmove(cp1+1,"000000]",7); 6354 } 6355 } 6356 else { 6357 memmove(buf+dirlen, cp1+2, retlen-dirlen); 6358 buf[retlen] = '\0'; 6359 /* Convert last '.' to ']' */ 6360 cp1 = buf+retlen-1; 6361 while (*cp != '[') { 6362 cp1--; 6363 if (*cp1 == '.') { 6364 /* Do not trip on extra dots in ODS-5 directories */ 6365 if ((cp1 == buf) || (*(cp1-1) != '^')) 6366 break; 6367 } 6368 } 6369 if (*cp1 == '.') *cp1 = ']'; 6370 else { 6371 memmove(cp1+8, cp1+1, buf+dirlen-cp1); 6372 memmove(cp1+1,"000000]",7); 6373 } 6374 } 6375 } 6376 else { /* This is a top-level dir. Add the MFD to the path. */ 6377 cp1 = strrchr(my_esa, ':'); 6378 assert(cp1); 6379 memmove(buf, my_esa, cp1 - my_esa + 1); 6380 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8); 6381 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2)); 6382 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */ 6383 } 6384 } 6385 sts = rms_free_search_context(&dirfab); 6386 /* We've set up the string up through the filename. Add the 6387 type and version, and we're done. */ 6388 strcat(buf,".DIR;1"); 6389 6390 /* $PARSE may have upcased filespec, so convert output to lower 6391 * case if input contained any lowercase characters. */ 6392 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf); 6393 PerlMem_free(trndir); 6394 PerlMem_free(esa); 6395 if (esal != NULL) 6396 PerlMem_free(esal); 6397 PerlMem_free(vmsdir); 6398 return buf; 6399 } 6400 } /* end of int_fileify_dirspec() */ 6401 6402 6403 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 6404 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl) 6405 { 6406 static char __fileify_retbuf[VMS_MAXRSS]; 6407 char * fileified, *ret_spec, *ret_buf; 6408 6409 fileified = NULL; 6410 ret_buf = buf; 6411 if (ret_buf == NULL) { 6412 if (ts) { 6413 Newx(fileified, VMS_MAXRSS, char); 6414 if (fileified == NULL) 6415 _ckvmssts(SS$_INSFMEM); 6416 ret_buf = fileified; 6417 } else { 6418 ret_buf = __fileify_retbuf; 6419 } 6420 } 6421 6422 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl); 6423 6424 if (ret_spec == NULL) { 6425 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 6426 if (fileified) 6427 Safefree(fileified); 6428 } 6429 6430 return ret_spec; 6431 } /* end of do_fileify_dirspec() */ 6432 /*}}}*/ 6433 6434 /* External entry points */ 6435 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf) 6436 { return do_fileify_dirspec(dir,buf,0,NULL); } 6437 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf) 6438 { return do_fileify_dirspec(dir,buf,1,NULL); } 6439 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl) 6440 { return do_fileify_dirspec(dir,buf,0,utf8_fl); } 6441 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl) 6442 { return do_fileify_dirspec(dir,buf,1,utf8_fl); } 6443 6444 static char * int_pathify_dirspec_simple(const char * dir, char * buf, 6445 char * v_spec, int v_len, char * r_spec, int r_len, 6446 char * d_spec, int d_len, char * n_spec, int n_len, 6447 char * e_spec, int e_len, char * vs_spec, int vs_len) { 6448 6449 /* VMS specification - Try to do this the simple way */ 6450 if ((v_len + r_len > 0) || (d_len > 0)) { 6451 int is_dir; 6452 6453 /* No name or extension component, already a directory */ 6454 if ((n_len + e_len + vs_len) == 0) { 6455 strcpy(buf, dir); 6456 return buf; 6457 } 6458 6459 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */ 6460 /* This results from catfile() being used instead of catdir() */ 6461 /* So even though it should not work, we need to allow it */ 6462 6463 /* If this is .DIR;1 then do a simple conversion */ 6464 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len); 6465 if (is_dir || (e_len == 0) && (d_len > 0)) { 6466 int len; 6467 len = v_len + r_len + d_len - 1; 6468 char dclose = d_spec[d_len - 1]; 6469 memcpy(buf, dir, len); 6470 buf[len] = '.'; 6471 len++; 6472 memcpy(&buf[len], n_spec, n_len); 6473 len += n_len; 6474 buf[len] = dclose; 6475 buf[len + 1] = '\0'; 6476 return buf; 6477 } 6478 6479 #ifdef HAS_SYMLINK 6480 else if (d_len > 0) { 6481 /* In the olden days, a directory needed to have a .DIR */ 6482 /* extension to be a valid directory, but now it could */ 6483 /* be a symbolic link */ 6484 int len; 6485 len = v_len + r_len + d_len - 1; 6486 char dclose = d_spec[d_len - 1]; 6487 memcpy(buf, dir, len); 6488 buf[len] = '.'; 6489 len++; 6490 memcpy(&buf[len], n_spec, n_len); 6491 len += n_len; 6492 if (e_len > 0) { 6493 if (decc_efs_charset) { 6494 if (e_len == 4 6495 && (toupper(e_spec[1]) == 'D') 6496 && (toupper(e_spec[2]) == 'I') 6497 && (toupper(e_spec[3]) == 'R')) { 6498 6499 /* Corner case: directory spec with invalid version. 6500 * Valid would have followed is_dir path above. 6501 */ 6502 SETERRNO(ENOTDIR, RMS$_DIR); 6503 return NULL; 6504 } 6505 else { 6506 buf[len] = '^'; 6507 len++; 6508 memcpy(&buf[len], e_spec, e_len); 6509 len += e_len; 6510 } 6511 } 6512 else { 6513 SETERRNO(ENOTDIR, RMS$_DIR); 6514 return NULL; 6515 } 6516 } 6517 buf[len] = dclose; 6518 buf[len + 1] = '\0'; 6519 return buf; 6520 } 6521 #else 6522 else { 6523 set_vaxc_errno(RMS$_DIR); 6524 set_errno(ENOTDIR); 6525 return NULL; 6526 } 6527 #endif 6528 } 6529 set_vaxc_errno(RMS$_DIR); 6530 set_errno(ENOTDIR); 6531 return NULL; 6532 } 6533 6534 6535 /* Internal routine to make sure or convert a directory to be in a */ 6536 /* path specification. No utf8 flag because it is not changed or used */ 6537 static char *int_pathify_dirspec(const char *dir, char *buf) 6538 { 6539 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 6540 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 6541 char * exp_spec, *ret_spec; 6542 char * trndir; 6543 unsigned short int trnlnm_iter_count; 6544 STRLEN trnlen; 6545 int need_to_lower; 6546 6547 if (vms_debug_fileify) { 6548 if (dir == NULL) 6549 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n"); 6550 else 6551 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir); 6552 } 6553 6554 /* We may need to lower case the result if we translated */ 6555 /* a logical name or got the current working directory */ 6556 need_to_lower = 0; 6557 6558 if (!dir || !*dir) { 6559 set_errno(EINVAL); 6560 set_vaxc_errno(SS$_BADPARAM); 6561 return NULL; 6562 } 6563 6564 trndir = (char *)PerlMem_malloc(VMS_MAXRSS); 6565 if (trndir == NULL) 6566 _ckvmssts_noperl(SS$_INSFMEM); 6567 6568 /* If no directory specified use the current default */ 6569 if (*dir) 6570 my_strlcpy(trndir, dir, VMS_MAXRSS); 6571 else { 6572 getcwd(trndir, VMS_MAXRSS - 1); 6573 need_to_lower = 1; 6574 } 6575 6576 /* now deal with bare names that could be logical names */ 6577 trnlnm_iter_count = 0; 6578 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords 6579 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) { 6580 trnlnm_iter_count++; 6581 need_to_lower = 1; 6582 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) 6583 break; 6584 trnlen = strlen(trndir); 6585 6586 /* Trap simple rooted lnms, and return lnm:[000000] */ 6587 if (!strcmp(trndir+trnlen-2,".]")) { 6588 my_strlcpy(buf, dir, VMS_MAXRSS); 6589 strcat(buf, ":[000000]"); 6590 PerlMem_free(trndir); 6591 6592 if (vms_debug_fileify) { 6593 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf); 6594 } 6595 return buf; 6596 } 6597 } 6598 6599 /* At this point we do not work with *dir, but the copy in *trndir */ 6600 6601 if (need_to_lower && !decc_efs_case_preserve) { 6602 /* Legacy mode, lower case the returned value */ 6603 __mystrtolower(trndir); 6604 } 6605 6606 6607 /* Some special cases, '..', '.' */ 6608 sts = 0; 6609 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) { 6610 /* Force UNIX filespec */ 6611 sts = 1; 6612 6613 } else { 6614 /* Is this Unix or VMS format? */ 6615 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len, 6616 &d_spec, &d_len, &n_spec, &n_len, &e_spec, 6617 &e_len, &vs_spec, &vs_len); 6618 if (sts == 0) { 6619 6620 /* Just a filename? */ 6621 if ((v_len + r_len + d_len) == 0) { 6622 6623 /* Now we have a problem, this could be Unix or VMS */ 6624 /* We have to guess. .DIR usually means VMS */ 6625 6626 /* In UNIX report mode, the .DIR extension is removed */ 6627 /* if one shows up, it is for a non-directory or a directory */ 6628 /* in EFS charset mode */ 6629 6630 /* So if we are in Unix report mode, assume that this */ 6631 /* is a relative Unix directory specification */ 6632 6633 sts = 1; 6634 if (!decc_filename_unix_report && decc_efs_charset) { 6635 int is_dir; 6636 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len); 6637 6638 if (is_dir) { 6639 /* Traditional mode, assume .DIR is directory */ 6640 buf[0] = '['; 6641 buf[1] = '.'; 6642 memcpy(&buf[2], n_spec, n_len); 6643 buf[n_len + 2] = ']'; 6644 buf[n_len + 3] = '\0'; 6645 PerlMem_free(trndir); 6646 if (vms_debug_fileify) { 6647 fprintf(stderr, 6648 "int_pathify_dirspec: buf = %s\n", 6649 buf); 6650 } 6651 return buf; 6652 } 6653 } 6654 } 6655 } 6656 } 6657 if (sts == 0) { 6658 ret_spec = int_pathify_dirspec_simple(trndir, buf, 6659 v_spec, v_len, r_spec, r_len, 6660 d_spec, d_len, n_spec, n_len, 6661 e_spec, e_len, vs_spec, vs_len); 6662 6663 if (ret_spec != NULL) { 6664 PerlMem_free(trndir); 6665 if (vms_debug_fileify) { 6666 fprintf(stderr, 6667 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6668 } 6669 return ret_spec; 6670 } 6671 6672 /* Simple way did not work, which means that a logical name */ 6673 /* was present for the directory specification. */ 6674 /* Need to use an rmsexpand variant to decode it completely */ 6675 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS); 6676 if (exp_spec == NULL) 6677 _ckvmssts_noperl(SS$_INSFMEM); 6678 6679 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG); 6680 if (ret_spec != NULL) { 6681 sts = vms_split_path(exp_spec, &v_spec, &v_len, 6682 &r_spec, &r_len, &d_spec, &d_len, 6683 &n_spec, &n_len, &e_spec, 6684 &e_len, &vs_spec, &vs_len); 6685 if (sts == 0) { 6686 ret_spec = int_pathify_dirspec_simple( 6687 exp_spec, buf, v_spec, v_len, r_spec, r_len, 6688 d_spec, d_len, n_spec, n_len, 6689 e_spec, e_len, vs_spec, vs_len); 6690 6691 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) { 6692 /* Legacy mode, lower case the returned value */ 6693 __mystrtolower(ret_spec); 6694 } 6695 } else { 6696 set_vaxc_errno(RMS$_DIR); 6697 set_errno(ENOTDIR); 6698 ret_spec = NULL; 6699 } 6700 } 6701 PerlMem_free(exp_spec); 6702 PerlMem_free(trndir); 6703 if (vms_debug_fileify) { 6704 if (ret_spec == NULL) 6705 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n"); 6706 else 6707 fprintf(stderr, 6708 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6709 } 6710 return ret_spec; 6711 6712 } else { 6713 /* Unix specification, Could be trivial conversion, */ 6714 /* but have to deal with trailing '.dir' or extra '.' */ 6715 6716 char * lastdot; 6717 char * lastslash; 6718 int is_dir; 6719 STRLEN dir_len = strlen(trndir); 6720 6721 lastslash = strrchr(trndir, '/'); 6722 if (lastslash == NULL) 6723 lastslash = trndir; 6724 else 6725 lastslash++; 6726 6727 lastdot = NULL; 6728 6729 /* '..' or '.' are valid directory components */ 6730 is_dir = 0; 6731 if (lastslash[0] == '.') { 6732 if (lastslash[1] == '\0') { 6733 is_dir = 1; 6734 } else if (lastslash[1] == '.') { 6735 if (lastslash[2] == '\0') { 6736 is_dir = 1; 6737 } else { 6738 /* And finally allow '...' */ 6739 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) { 6740 is_dir = 1; 6741 } 6742 } 6743 } 6744 } 6745 6746 if (!is_dir) { 6747 lastdot = strrchr(lastslash, '.'); 6748 } 6749 if (lastdot != NULL) { 6750 STRLEN e_len; 6751 /* '.dir' is discarded, and any other '.' is invalid */ 6752 e_len = strlen(lastdot); 6753 6754 is_dir = is_dir_ext(lastdot, e_len, NULL, 0); 6755 6756 if (is_dir) { 6757 dir_len = dir_len - 4; 6758 } 6759 } 6760 6761 my_strlcpy(buf, trndir, VMS_MAXRSS); 6762 if (buf[dir_len - 1] != '/') { 6763 buf[dir_len] = '/'; 6764 buf[dir_len + 1] = '\0'; 6765 } 6766 6767 /* Under ODS-2 rules, '.' becomes '_', so fix it up */ 6768 if (!decc_efs_charset) { 6769 int dir_start = 0; 6770 char * str = buf; 6771 if (str[0] == '.') { 6772 char * dots = str; 6773 int cnt = 1; 6774 while ((dots[cnt] == '.') && (cnt < 3)) 6775 cnt++; 6776 if (cnt <= 3) { 6777 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) { 6778 dir_start = 1; 6779 str += cnt; 6780 } 6781 } 6782 } 6783 for (; *str; ++str) { 6784 while (*str == '/') { 6785 dir_start = 1; 6786 *str++; 6787 } 6788 if (dir_start) { 6789 6790 /* Have to skip up to three dots which could be */ 6791 /* directories, 3 dots being a VMS extension for Perl */ 6792 char * dots = str; 6793 int cnt = 0; 6794 while ((dots[cnt] == '.') && (cnt < 3)) { 6795 cnt++; 6796 } 6797 if (dots[cnt] == '\0') 6798 break; 6799 if ((cnt > 1) && (dots[cnt] != '/')) { 6800 dir_start = 0; 6801 } else { 6802 str += cnt; 6803 } 6804 6805 /* too many dots? */ 6806 if ((cnt == 0) || (cnt > 3)) { 6807 dir_start = 0; 6808 } 6809 } 6810 if (!dir_start && (*str == '.')) { 6811 *str = '_'; 6812 } 6813 } 6814 } 6815 PerlMem_free(trndir); 6816 ret_spec = buf; 6817 if (vms_debug_fileify) { 6818 if (ret_spec == NULL) 6819 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n"); 6820 else 6821 fprintf(stderr, 6822 "int_pathify_dirspec: ret_spec = %s\n", ret_spec); 6823 } 6824 return ret_spec; 6825 } 6826 } 6827 6828 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/ 6829 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl) 6830 { 6831 static char __pathify_retbuf[VMS_MAXRSS]; 6832 char * pathified, *ret_spec, *ret_buf; 6833 6834 pathified = NULL; 6835 ret_buf = buf; 6836 if (ret_buf == NULL) { 6837 if (ts) { 6838 Newx(pathified, VMS_MAXRSS, char); 6839 if (pathified == NULL) 6840 _ckvmssts(SS$_INSFMEM); 6841 ret_buf = pathified; 6842 } else { 6843 ret_buf = __pathify_retbuf; 6844 } 6845 } 6846 6847 ret_spec = int_pathify_dirspec(dir, ret_buf); 6848 6849 if (ret_spec == NULL) { 6850 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 6851 if (pathified) 6852 Safefree(pathified); 6853 } 6854 6855 return ret_spec; 6856 6857 } /* end of do_pathify_dirspec() */ 6858 6859 6860 /* External entry points */ 6861 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf) 6862 { return do_pathify_dirspec(dir,buf,0,NULL); } 6863 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf) 6864 { return do_pathify_dirspec(dir,buf,1,NULL); } 6865 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl) 6866 { return do_pathify_dirspec(dir,buf,0,utf8_fl); } 6867 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl) 6868 { return do_pathify_dirspec(dir,buf,1,utf8_fl); } 6869 6870 /* Internal tounixspec routine that does not use a thread context */ 6871 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/ 6872 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl) 6873 { 6874 char *dirend, *cp1, *cp3, *tmp; 6875 const char *cp2; 6876 int dirlen; 6877 unsigned short int trnlnm_iter_count; 6878 int cmp_rslt, outchars_added; 6879 if (utf8_fl != NULL) 6880 *utf8_fl = 0; 6881 6882 if (vms_debug_fileify) { 6883 if (spec == NULL) 6884 fprintf(stderr, "int_tounixspec: spec = NULL\n"); 6885 else 6886 fprintf(stderr, "int_tounixspec: spec = %s\n", spec); 6887 } 6888 6889 6890 if (spec == NULL) { 6891 set_errno(EINVAL); 6892 set_vaxc_errno(SS$_BADPARAM); 6893 return NULL; 6894 } 6895 if (strlen(spec) > (VMS_MAXRSS-1)) { 6896 set_errno(E2BIG); 6897 set_vaxc_errno(SS$_BUFFEROVF); 6898 return NULL; 6899 } 6900 6901 /* New VMS specific format needs translation 6902 * glob passes filenames with trailing '\n' and expects this preserved. 6903 */ 6904 if (decc_posix_compliant_pathnames) { 6905 if (strncmp(spec, "\"^UP^", 5) == 0) { 6906 char * uspec; 6907 char *tunix; 6908 int tunix_len; 6909 int nl_flag; 6910 6911 tunix = (char *)PerlMem_malloc(VMS_MAXRSS); 6912 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6913 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS); 6914 nl_flag = 0; 6915 if (tunix[tunix_len - 1] == '\n') { 6916 tunix[tunix_len - 1] = '\"'; 6917 tunix[tunix_len] = '\0'; 6918 tunix_len--; 6919 nl_flag = 1; 6920 } 6921 uspec = decc$translate_vms(tunix); 6922 PerlMem_free(tunix); 6923 if ((int)uspec > 0) { 6924 my_strlcpy(rslt, uspec, VMS_MAXRSS); 6925 if (nl_flag) { 6926 strcat(rslt,"\n"); 6927 } 6928 else { 6929 /* If we can not translate it, makemaker wants as-is */ 6930 my_strlcpy(rslt, spec, VMS_MAXRSS); 6931 } 6932 return rslt; 6933 } 6934 } 6935 } 6936 6937 cmp_rslt = 0; /* Presume VMS */ 6938 cp1 = strchr(spec, '/'); 6939 if (cp1 == NULL) 6940 cmp_rslt = 0; 6941 6942 /* Look for EFS ^/ */ 6943 if (decc_efs_charset) { 6944 while (cp1 != NULL) { 6945 cp2 = cp1 - 1; 6946 if (*cp2 != '^') { 6947 /* Found illegal VMS, assume UNIX */ 6948 cmp_rslt = 1; 6949 break; 6950 } 6951 cp1++; 6952 cp1 = strchr(cp1, '/'); 6953 } 6954 } 6955 6956 /* Look for "." and ".." */ 6957 if (decc_filename_unix_report) { 6958 if (spec[0] == '.') { 6959 if ((spec[1] == '\0') || (spec[1] == '\n')) { 6960 cmp_rslt = 1; 6961 } 6962 else { 6963 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) { 6964 cmp_rslt = 1; 6965 } 6966 } 6967 } 6968 } 6969 6970 cp1 = rslt; 6971 cp2 = spec; 6972 6973 /* This is already UNIX or at least nothing VMS understands, 6974 * so all we can reasonably do is unescape extended chars. 6975 */ 6976 if (cmp_rslt) { 6977 while (*cp2) { 6978 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 6979 cp1 += outchars_added; 6980 } 6981 *cp1 = '\0'; 6982 if (vms_debug_fileify) { 6983 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 6984 } 6985 return rslt; 6986 } 6987 6988 dirend = strrchr(spec,']'); 6989 if (dirend == NULL) dirend = strrchr(spec,'>'); 6990 if (dirend == NULL) dirend = strchr(spec,':'); 6991 if (dirend == NULL) { 6992 while (*cp2) { 6993 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 6994 cp1 += outchars_added; 6995 } 6996 *cp1 = '\0'; 6997 if (vms_debug_fileify) { 6998 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 6999 } 7000 return rslt; 7001 } 7002 7003 /* Special case 1 - sys$posix_root = / */ 7004 if (!decc_disable_posix_root) { 7005 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) { 7006 *cp1 = '/'; 7007 cp1++; 7008 cp2 = cp2 + 15; 7009 } 7010 } 7011 7012 /* Special case 2 - Convert NLA0: to /dev/null */ 7013 cmp_rslt = strncasecmp(spec,"NLA0:", 5); 7014 if (cmp_rslt == 0) { 7015 strcpy(rslt, "/dev/null"); 7016 cp1 = cp1 + 9; 7017 cp2 = cp2 + 5; 7018 if (spec[6] != '\0') { 7019 cp1[9] = '/'; 7020 cp1++; 7021 cp2++; 7022 } 7023 } 7024 7025 /* Also handle special case "SYS$SCRATCH:" */ 7026 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12); 7027 tmp = (char *)PerlMem_malloc(VMS_MAXRSS); 7028 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7029 if (cmp_rslt == 0) { 7030 int islnm; 7031 7032 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1); 7033 if (!islnm) { 7034 strcpy(rslt, "/tmp"); 7035 cp1 = cp1 + 4; 7036 cp2 = cp2 + 12; 7037 if (spec[12] != '\0') { 7038 cp1[4] = '/'; 7039 cp1++; 7040 cp2++; 7041 } 7042 } 7043 } 7044 7045 if (*cp2 != '[' && *cp2 != '<') { 7046 *(cp1++) = '/'; 7047 } 7048 else { /* the VMS spec begins with directories */ 7049 cp2++; 7050 if (*cp2 == ']' || *cp2 == '>') { 7051 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0'; 7052 PerlMem_free(tmp); 7053 return rslt; 7054 } 7055 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */ 7056 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) { 7057 PerlMem_free(tmp); 7058 if (vms_debug_fileify) { 7059 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7060 } 7061 return NULL; 7062 } 7063 trnlnm_iter_count = 0; 7064 do { 7065 cp3 = tmp; 7066 while (*cp3 != ':' && *cp3) cp3++; 7067 *(cp3++) = '\0'; 7068 if (strchr(cp3,']') != NULL) break; 7069 trnlnm_iter_count++; 7070 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break; 7071 } while (vmstrnenv(tmp,tmp,0,fildev,0)); 7072 cp1 = rslt; 7073 cp3 = tmp; 7074 *(cp1++) = '/'; 7075 while (*cp3) { 7076 *(cp1++) = *(cp3++); 7077 if (cp1 - rslt > (VMS_MAXRSS - 1)) { 7078 PerlMem_free(tmp); 7079 set_errno(ENAMETOOLONG); 7080 set_vaxc_errno(SS$_BUFFEROVF); 7081 if (vms_debug_fileify) { 7082 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7083 } 7084 return NULL; /* No room */ 7085 } 7086 } 7087 *(cp1++) = '/'; 7088 } 7089 if ((*cp2 == '^')) { 7090 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7091 cp1 += outchars_added; 7092 } 7093 else if ( *cp2 == '.') { 7094 if (*(cp2+1) == '.' && *(cp2+2) == '.') { 7095 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 7096 cp2 += 3; 7097 } 7098 else cp2++; 7099 } 7100 } 7101 PerlMem_free(tmp); 7102 for (; cp2 <= dirend; cp2++) { 7103 if ((*cp2 == '^')) { 7104 /* EFS file escape, pass the next character as is */ 7105 /* Fix me: HEX encoding for Unicode not implemented */ 7106 *(cp1++) = *(++cp2); 7107 /* An escaped dot stays as is -- don't convert to slash */ 7108 if (*cp2 == '.') cp2++; 7109 } 7110 if (*cp2 == ':') { 7111 *(cp1++) = '/'; 7112 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 7113 } 7114 else if (*cp2 == ']' || *cp2 == '>') { 7115 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */ 7116 } 7117 else if ((*cp2 == '.') && (*cp2-1 != '^')) { 7118 *(cp1++) = '/'; 7119 if (*(cp2+1) == ']' || *(cp2+1) == '>') { 7120 while (*(cp2+1) == ']' || *(cp2+1) == '>' || 7121 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 7122 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' || 7123 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7; 7124 } 7125 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') { 7126 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/'; 7127 cp2 += 2; 7128 } 7129 } 7130 else if (*cp2 == '-') { 7131 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') { 7132 while (*cp2 == '-') { 7133 cp2++; 7134 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 7135 } 7136 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */ 7137 /* filespecs like */ 7138 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */ 7139 if (vms_debug_fileify) { 7140 fprintf(stderr, "int_tounixspec: rslt = NULL\n"); 7141 } 7142 return NULL; 7143 } 7144 } 7145 else *(cp1++) = *cp2; 7146 } 7147 else *(cp1++) = *cp2; 7148 } 7149 /* Translate the rest of the filename. */ 7150 while (*cp2) { 7151 int dot_seen = 0; 7152 switch(*cp2) { 7153 /* Fixme - for compatibility with the CRTL we should be removing */ 7154 /* spaces from the file specifications, but this may show that */ 7155 /* some tests that were appearing to pass are not really passing */ 7156 case '%': 7157 cp2++; 7158 *(cp1++) = '?'; 7159 break; 7160 case '^': 7161 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added); 7162 cp1 += outchars_added; 7163 break; 7164 case ';': 7165 if (decc_filename_unix_no_version) { 7166 /* Easy, drop the version */ 7167 while (*cp2) 7168 cp2++; 7169 break; 7170 } else { 7171 /* Punt - passing the version as a dot will probably */ 7172 /* break perl in weird ways, but so did passing */ 7173 /* through the ; as a version. Follow the CRTL and */ 7174 /* hope for the best. */ 7175 cp2++; 7176 *(cp1++) = '.'; 7177 } 7178 break; 7179 case '.': 7180 if (dot_seen) { 7181 /* We will need to fix this properly later */ 7182 /* As Perl may be installed on an ODS-5 volume, but not */ 7183 /* have the EFS_CHARSET enabled, it still may encounter */ 7184 /* filenames with extra dots in them, and a precedent got */ 7185 /* set which allowed them to work, that we will uphold here */ 7186 /* If extra dots are present in a name and no ^ is on them */ 7187 /* VMS assumes that the first one is the extension delimiter */ 7188 /* the rest have an implied ^. */ 7189 7190 /* this is also a conflict as the . is also a version */ 7191 /* delimiter in VMS, */ 7192 7193 *(cp1++) = *(cp2++); 7194 break; 7195 } 7196 dot_seen = 1; 7197 /* This is an extension */ 7198 if (decc_readdir_dropdotnotype) { 7199 cp2++; 7200 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) { 7201 /* Drop the dot for the extension */ 7202 break; 7203 } else { 7204 *(cp1++) = '.'; 7205 } 7206 break; 7207 } 7208 default: 7209 *(cp1++) = *(cp2++); 7210 } 7211 } 7212 *cp1 = '\0'; 7213 7214 /* This still leaves /000000/ when working with a 7215 * VMS device root or concealed root. 7216 */ 7217 { 7218 int ulen; 7219 char * zeros; 7220 7221 ulen = strlen(rslt); 7222 7223 /* Get rid of "000000/ in rooted filespecs */ 7224 if (ulen > 7) { 7225 zeros = strstr(rslt, "/000000/"); 7226 if (zeros != NULL) { 7227 int mlen; 7228 mlen = ulen - (zeros - rslt) - 7; 7229 memmove(zeros, &zeros[7], mlen); 7230 ulen = ulen - 7; 7231 rslt[ulen] = '\0'; 7232 } 7233 } 7234 } 7235 7236 if (vms_debug_fileify) { 7237 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt); 7238 } 7239 return rslt; 7240 7241 } /* end of int_tounixspec() */ 7242 7243 7244 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/ 7245 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl) 7246 { 7247 static char __tounixspec_retbuf[VMS_MAXRSS]; 7248 char * unixspec, *ret_spec, *ret_buf; 7249 7250 unixspec = NULL; 7251 ret_buf = buf; 7252 if (ret_buf == NULL) { 7253 if (ts) { 7254 Newx(unixspec, VMS_MAXRSS, char); 7255 if (unixspec == NULL) 7256 _ckvmssts(SS$_INSFMEM); 7257 ret_buf = unixspec; 7258 } else { 7259 ret_buf = __tounixspec_retbuf; 7260 } 7261 } 7262 7263 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl); 7264 7265 if (ret_spec == NULL) { 7266 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 7267 if (unixspec) 7268 Safefree(unixspec); 7269 } 7270 7271 return ret_spec; 7272 7273 } /* end of do_tounixspec() */ 7274 /*}}}*/ 7275 /* External entry points */ 7276 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) 7277 { return do_tounixspec(spec,buf,0, NULL); } 7278 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) 7279 { return do_tounixspec(spec,buf,1, NULL); } 7280 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl) 7281 { return do_tounixspec(spec,buf,0, utf8_fl); } 7282 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl) 7283 { return do_tounixspec(spec,buf,1, utf8_fl); } 7284 7285 #if __CRTL_VER >= 70200000 && !defined(__VAX) 7286 7287 /* 7288 This procedure is used to identify if a path is based in either 7289 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and 7290 it returns the OpenVMS format directory for it. 7291 7292 It is expecting specifications of only '/' or '/xxxx/' 7293 7294 If a posix root does not exist, or 'xxxx' is not a directory 7295 in the posix root, it returns a failure. 7296 7297 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7. 7298 7299 It is used only internally by posix_to_vmsspec_hardway(). 7300 */ 7301 7302 static int posix_root_to_vms 7303 (char *vmspath, int vmspath_len, 7304 const char *unixpath, 7305 const int * utf8_fl) 7306 { 7307 int sts; 7308 struct FAB myfab = cc$rms_fab; 7309 rms_setup_nam(mynam); 7310 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 7311 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 7312 char * esa, * esal, * rsa, * rsal; 7313 int dir_flag; 7314 int unixlen; 7315 7316 dir_flag = 0; 7317 vmspath[0] = '\0'; 7318 unixlen = strlen(unixpath); 7319 if (unixlen == 0) { 7320 return RMS$_FNF; 7321 } 7322 7323 #if __CRTL_VER >= 80200000 7324 /* If not a posix spec already, convert it */ 7325 if (decc_posix_compliant_pathnames) { 7326 if (strncmp(unixpath,"\"^UP^",5) != 0) { 7327 sprintf(vmspath,"\"^UP^%s\"",unixpath); 7328 } 7329 else { 7330 /* This is already a VMS specification, no conversion */ 7331 unixlen--; 7332 my_strlcpy(vmspath, unixpath, vmspath_len + 1); 7333 } 7334 } 7335 else 7336 #endif 7337 { 7338 int path_len; 7339 int i,j; 7340 7341 /* Check to see if this is under the POSIX root */ 7342 if (decc_disable_posix_root) { 7343 return RMS$_FNF; 7344 } 7345 7346 /* Skip leading / */ 7347 if (unixpath[0] == '/') { 7348 unixpath++; 7349 unixlen--; 7350 } 7351 7352 7353 strcpy(vmspath,"SYS$POSIX_ROOT:"); 7354 7355 /* If this is only the / , or blank, then... */ 7356 if (unixpath[0] == '\0') { 7357 /* by definition, this is the answer */ 7358 return SS$_NORMAL; 7359 } 7360 7361 /* Need to look up a directory */ 7362 vmspath[15] = '['; 7363 vmspath[16] = '\0'; 7364 7365 /* Copy and add '^' escape characters as needed */ 7366 j = 16; 7367 i = 0; 7368 while (unixpath[i] != 0) { 7369 int k; 7370 7371 j += copy_expand_unix_filename_escape 7372 (&vmspath[j], &unixpath[i], &k, utf8_fl); 7373 i += k; 7374 } 7375 7376 path_len = strlen(vmspath); 7377 if (vmspath[path_len - 1] == '/') 7378 path_len--; 7379 vmspath[path_len] = ']'; 7380 path_len++; 7381 vmspath[path_len] = '\0'; 7382 7383 } 7384 vmspath[vmspath_len] = 0; 7385 if (unixpath[unixlen - 1] == '/') 7386 dir_flag = 1; 7387 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 7388 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7389 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 7390 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7391 rsal = (char *)PerlMem_malloc(VMS_MAXRSS); 7392 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7393 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 7394 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7395 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */ 7396 rms_bind_fab_nam(myfab, mynam); 7397 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1); 7398 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1); 7399 if (decc_efs_case_preserve) 7400 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE; 7401 #ifdef NAML$M_OPEN_SPECIAL 7402 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL; 7403 #endif 7404 7405 /* Set up the remaining naml fields */ 7406 sts = sys$parse(&myfab); 7407 7408 /* It failed! Try again as a UNIX filespec */ 7409 if (!(sts & 1)) { 7410 PerlMem_free(esal); 7411 PerlMem_free(esa); 7412 PerlMem_free(rsal); 7413 PerlMem_free(rsa); 7414 return sts; 7415 } 7416 7417 /* get the Device ID and the FID */ 7418 sts = sys$search(&myfab); 7419 7420 /* These are no longer needed */ 7421 PerlMem_free(esa); 7422 PerlMem_free(rsal); 7423 PerlMem_free(rsa); 7424 7425 /* on any failure, returned the POSIX ^UP^ filespec */ 7426 if (!(sts & 1)) { 7427 PerlMem_free(esal); 7428 return sts; 7429 } 7430 specdsc.dsc$a_pointer = vmspath; 7431 specdsc.dsc$w_length = vmspath_len; 7432 7433 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1]; 7434 dvidsc.dsc$w_length = mynam.naml$t_dvi[0]; 7435 sts = lib$fid_to_name 7436 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length); 7437 7438 /* on any failure, returned the POSIX ^UP^ filespec */ 7439 if (!(sts & 1)) { 7440 /* This can happen if user does not have permission to read directories */ 7441 if (strncmp(unixpath,"\"^UP^",5) != 0) 7442 sprintf(vmspath,"\"^UP^%s\"",unixpath); 7443 else 7444 my_strlcpy(vmspath, unixpath, vmspath_len + 1); 7445 } 7446 else { 7447 vmspath[specdsc.dsc$w_length] = 0; 7448 7449 /* Are we expecting a directory? */ 7450 if (dir_flag != 0) { 7451 int i; 7452 char *eptr; 7453 7454 eptr = NULL; 7455 7456 i = specdsc.dsc$w_length - 1; 7457 while (i > 0) { 7458 int zercnt; 7459 zercnt = 0; 7460 /* Version must be '1' */ 7461 if (vmspath[i--] != '1') 7462 break; 7463 /* Version delimiter is one of ".;" */ 7464 if ((vmspath[i] != '.') && (vmspath[i] != ';')) 7465 break; 7466 i--; 7467 if (vmspath[i--] != 'R') 7468 break; 7469 if (vmspath[i--] != 'I') 7470 break; 7471 if (vmspath[i--] != 'D') 7472 break; 7473 if (vmspath[i--] != '.') 7474 break; 7475 eptr = &vmspath[i+1]; 7476 while (i > 0) { 7477 if ((vmspath[i] == ']') || (vmspath[i] == '>')) { 7478 if (vmspath[i-1] != '^') { 7479 if (zercnt != 6) { 7480 *eptr = vmspath[i]; 7481 eptr[1] = '\0'; 7482 vmspath[i] = '.'; 7483 break; 7484 } 7485 else { 7486 /* Get rid of 6 imaginary zero directory filename */ 7487 vmspath[i+1] = '\0'; 7488 } 7489 } 7490 } 7491 if (vmspath[i] == '0') 7492 zercnt++; 7493 else 7494 zercnt = 10; 7495 i--; 7496 } 7497 break; 7498 } 7499 } 7500 } 7501 PerlMem_free(esal); 7502 return sts; 7503 } 7504 7505 /* /dev/mumble needs to be handled special. 7506 /dev/null becomes NLA0:, And there is the potential for other stuff 7507 like /dev/tty which may need to be mapped to something. 7508 */ 7509 7510 static int 7511 slash_dev_special_to_vms 7512 (const char * unixptr, 7513 char * vmspath, 7514 int vmspath_len) 7515 { 7516 char * nextslash; 7517 int len; 7518 int cmp; 7519 7520 unixptr += 4; 7521 nextslash = strchr(unixptr, '/'); 7522 len = strlen(unixptr); 7523 if (nextslash != NULL) 7524 len = nextslash - unixptr; 7525 cmp = strncmp("null", unixptr, 5); 7526 if (cmp == 0) { 7527 if (vmspath_len >= 6) { 7528 strcpy(vmspath, "_NLA0:"); 7529 return SS$_NORMAL; 7530 } 7531 } 7532 return 0; 7533 } 7534 7535 7536 /* The built in routines do not understand perl's special needs, so 7537 doing a manual conversion from UNIX to VMS 7538 7539 If the utf8_fl is not null and points to a non-zero value, then 7540 treat 8 bit characters as UTF-8. 7541 7542 The sequence starting with '$(' and ending with ')' will be passed 7543 through with out interpretation instead of being escaped. 7544 7545 */ 7546 static int posix_to_vmsspec_hardway 7547 (char *vmspath, int vmspath_len, 7548 const char *unixpath, 7549 int dir_flag, 7550 int * utf8_fl) { 7551 7552 char *esa; 7553 const char *unixptr; 7554 const char *unixend; 7555 char *vmsptr; 7556 const char *lastslash; 7557 const char *lastdot; 7558 int unixlen; 7559 int vmslen; 7560 int dir_start; 7561 int dir_dot; 7562 int quoted; 7563 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 7564 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 7565 7566 if (utf8_fl != NULL) 7567 *utf8_fl = 0; 7568 7569 unixptr = unixpath; 7570 dir_dot = 0; 7571 7572 /* Ignore leading "/" characters */ 7573 while((unixptr[0] == '/') && (unixptr[1] == '/')) { 7574 unixptr++; 7575 } 7576 unixlen = strlen(unixptr); 7577 7578 /* Do nothing with blank paths */ 7579 if (unixlen == 0) { 7580 vmspath[0] = '\0'; 7581 return SS$_NORMAL; 7582 } 7583 7584 quoted = 0; 7585 /* This could have a "^UP^ on the front */ 7586 if (strncmp(unixptr,"\"^UP^",5) == 0) { 7587 quoted = 1; 7588 unixptr+= 5; 7589 unixlen-= 5; 7590 } 7591 7592 lastslash = strrchr(unixptr,'/'); 7593 lastdot = strrchr(unixptr,'.'); 7594 unixend = strrchr(unixptr,'\"'); 7595 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) { 7596 unixend = unixptr + unixlen; 7597 } 7598 7599 /* last dot is last dot or past end of string */ 7600 if (lastdot == NULL) 7601 lastdot = unixptr + unixlen; 7602 7603 /* if no directories, set last slash to beginning of string */ 7604 if (lastslash == NULL) { 7605 lastslash = unixptr; 7606 } 7607 else { 7608 /* Watch out for trailing "." after last slash, still a directory */ 7609 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) { 7610 lastslash = unixptr + unixlen; 7611 } 7612 7613 /* Watch out for trailing ".." after last slash, still a directory */ 7614 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) { 7615 lastslash = unixptr + unixlen; 7616 } 7617 7618 /* dots in directories are aways escaped */ 7619 if (lastdot < lastslash) 7620 lastdot = unixptr + unixlen; 7621 } 7622 7623 /* if (unixptr < lastslash) then we are in a directory */ 7624 7625 dir_start = 0; 7626 7627 vmsptr = vmspath; 7628 vmslen = 0; 7629 7630 /* Start with the UNIX path */ 7631 if (*unixptr != '/') { 7632 /* relative paths */ 7633 7634 /* If allowing logical names on relative pathnames, then handle here */ 7635 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation && 7636 !decc_posix_compliant_pathnames) { 7637 char * nextslash; 7638 int seg_len; 7639 char * trn; 7640 int islnm; 7641 7642 /* Find the next slash */ 7643 nextslash = strchr(unixptr,'/'); 7644 7645 esa = (char *)PerlMem_malloc(vmspath_len); 7646 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7647 7648 trn = (char *)PerlMem_malloc(VMS_MAXRSS); 7649 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7650 7651 if (nextslash != NULL) { 7652 7653 seg_len = nextslash - unixptr; 7654 memcpy(esa, unixptr, seg_len); 7655 esa[seg_len] = 0; 7656 } 7657 else { 7658 seg_len = my_strlcpy(esa, unixptr, sizeof(esa)); 7659 } 7660 /* trnlnm(section) */ 7661 islnm = vmstrnenv(esa, trn, 0, fildev, 0); 7662 7663 if (islnm) { 7664 /* Now fix up the directory */ 7665 7666 /* Split up the path to find the components */ 7667 sts = vms_split_path 7668 (trn, 7669 &v_spec, 7670 &v_len, 7671 &r_spec, 7672 &r_len, 7673 &d_spec, 7674 &d_len, 7675 &n_spec, 7676 &n_len, 7677 &e_spec, 7678 &e_len, 7679 &vs_spec, 7680 &vs_len); 7681 7682 while (sts == 0) { 7683 int cmp; 7684 7685 /* A logical name must be a directory or the full 7686 specification. It is only a full specification if 7687 it is the only component */ 7688 if ((unixptr[seg_len] == '\0') || 7689 (unixptr[seg_len+1] == '\0')) { 7690 7691 /* Is a directory being required? */ 7692 if (((n_len + e_len) != 0) && (dir_flag !=0)) { 7693 /* Not a logical name */ 7694 break; 7695 } 7696 7697 7698 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) { 7699 /* This must be a directory */ 7700 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) { 7701 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1); 7702 vmsptr[vmslen] = ':'; 7703 vmslen++; 7704 vmsptr[vmslen] = '\0'; 7705 return SS$_NORMAL; 7706 } 7707 } 7708 7709 } 7710 7711 7712 /* must be dev/directory - ignore version */ 7713 if ((n_len + e_len) != 0) 7714 break; 7715 7716 /* transfer the volume */ 7717 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) { 7718 memcpy(vmsptr, v_spec, v_len); 7719 vmsptr += v_len; 7720 vmsptr[0] = '\0'; 7721 vmslen += v_len; 7722 } 7723 7724 /* unroot the rooted directory */ 7725 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) { 7726 r_spec[0] = '['; 7727 r_spec[r_len - 1] = ']'; 7728 7729 /* This should not be there, but nothing is perfect */ 7730 if (r_len > 9) { 7731 cmp = strcmp(&r_spec[1], "000000."); 7732 if (cmp == 0) { 7733 r_spec += 7; 7734 r_spec[7] = '['; 7735 r_len -= 7; 7736 if (r_len == 2) 7737 r_len = 0; 7738 } 7739 } 7740 if (r_len > 0) { 7741 memcpy(vmsptr, r_spec, r_len); 7742 vmsptr += r_len; 7743 vmslen += r_len; 7744 vmsptr[0] = '\0'; 7745 } 7746 } 7747 /* Bring over the directory. */ 7748 if ((d_len > 0) && 7749 ((d_len + vmslen) < vmspath_len)) { 7750 d_spec[0] = '['; 7751 d_spec[d_len - 1] = ']'; 7752 if (d_len > 9) { 7753 cmp = strcmp(&d_spec[1], "000000."); 7754 if (cmp == 0) { 7755 d_spec += 7; 7756 d_spec[7] = '['; 7757 d_len -= 7; 7758 if (d_len == 2) 7759 d_len = 0; 7760 } 7761 } 7762 7763 if (r_len > 0) { 7764 /* Remove the redundant root */ 7765 if (r_len > 0) { 7766 /* remove the ][ */ 7767 vmsptr--; 7768 vmslen--; 7769 d_spec++; 7770 d_len--; 7771 } 7772 memcpy(vmsptr, d_spec, d_len); 7773 vmsptr += d_len; 7774 vmslen += d_len; 7775 vmsptr[0] = '\0'; 7776 } 7777 } 7778 break; 7779 } 7780 } 7781 7782 PerlMem_free(esa); 7783 PerlMem_free(trn); 7784 } 7785 7786 if (lastslash > unixptr) { 7787 int dotdir_seen; 7788 7789 /* skip leading ./ */ 7790 dotdir_seen = 0; 7791 while ((unixptr[0] == '.') && (unixptr[1] == '/')) { 7792 dotdir_seen = 1; 7793 unixptr++; 7794 unixptr++; 7795 } 7796 7797 /* Are we still in a directory? */ 7798 if (unixptr <= lastslash) { 7799 *vmsptr++ = '['; 7800 vmslen = 1; 7801 dir_start = 1; 7802 7803 /* if not backing up, then it is relative forward. */ 7804 if (!((*unixptr == '.') && (unixptr[1] == '.') && 7805 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) { 7806 *vmsptr++ = '.'; 7807 vmslen++; 7808 dir_dot = 1; 7809 } 7810 } 7811 else { 7812 if (dotdir_seen) { 7813 /* Perl wants an empty directory here to tell the difference 7814 * between a DCL command and a filename 7815 */ 7816 *vmsptr++ = '['; 7817 *vmsptr++ = ']'; 7818 vmslen = 2; 7819 } 7820 } 7821 } 7822 else { 7823 /* Handle two special files . and .. */ 7824 if (unixptr[0] == '.') { 7825 if (&unixptr[1] == unixend) { 7826 *vmsptr++ = '['; 7827 *vmsptr++ = ']'; 7828 vmslen += 2; 7829 *vmsptr++ = '\0'; 7830 return SS$_NORMAL; 7831 } 7832 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) { 7833 *vmsptr++ = '['; 7834 *vmsptr++ = '-'; 7835 *vmsptr++ = ']'; 7836 vmslen += 3; 7837 *vmsptr++ = '\0'; 7838 return SS$_NORMAL; 7839 } 7840 } 7841 } 7842 } 7843 else { /* Absolute PATH handling */ 7844 int sts; 7845 char * nextslash; 7846 int seg_len; 7847 /* Need to find out where root is */ 7848 7849 /* In theory, this procedure should never get an absolute POSIX pathname 7850 * that can not be found on the POSIX root. 7851 * In practice, that can not be relied on, and things will show up 7852 * here that are a VMS device name or concealed logical name instead. 7853 * So to make things work, this procedure must be tolerant. 7854 */ 7855 esa = (char *)PerlMem_malloc(vmspath_len); 7856 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7857 7858 sts = SS$_NORMAL; 7859 nextslash = strchr(&unixptr[1],'/'); 7860 seg_len = 0; 7861 if (nextslash != NULL) { 7862 int cmp; 7863 seg_len = nextslash - &unixptr[1]; 7864 my_strlcpy(vmspath, unixptr, seg_len + 2); 7865 cmp = 1; 7866 if (seg_len == 3) { 7867 cmp = strncmp(vmspath, "dev", 4); 7868 if (cmp == 0) { 7869 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len); 7870 if (sts == SS$_NORMAL) 7871 return SS$_NORMAL; 7872 } 7873 } 7874 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl); 7875 } 7876 7877 if ($VMS_STATUS_SUCCESS(sts)) { 7878 /* This is verified to be a real path */ 7879 7880 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL); 7881 if ($VMS_STATUS_SUCCESS(sts)) { 7882 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1); 7883 vmsptr = vmspath + vmslen; 7884 unixptr++; 7885 if (unixptr < lastslash) { 7886 char * rptr; 7887 vmsptr--; 7888 *vmsptr++ = '.'; 7889 dir_start = 1; 7890 dir_dot = 1; 7891 if (vmslen > 7) { 7892 int cmp; 7893 rptr = vmsptr - 7; 7894 cmp = strcmp(rptr,"000000."); 7895 if (cmp == 0) { 7896 vmslen -= 7; 7897 vmsptr -= 7; 7898 vmsptr[1] = '\0'; 7899 } /* removing 6 zeros */ 7900 } /* vmslen < 7, no 6 zeros possible */ 7901 } /* Not in a directory */ 7902 } /* Posix root found */ 7903 else { 7904 /* No posix root, fall back to default directory */ 7905 strcpy(vmspath, "SYS$DISK:["); 7906 vmsptr = &vmspath[10]; 7907 vmslen = 10; 7908 if (unixptr > lastslash) { 7909 *vmsptr = ']'; 7910 vmsptr++; 7911 vmslen++; 7912 } 7913 else { 7914 dir_start = 1; 7915 } 7916 } 7917 } /* end of verified real path handling */ 7918 else { 7919 int add_6zero; 7920 int islnm; 7921 7922 /* Ok, we have a device or a concealed root that is not in POSIX 7923 * or we have garbage. Make the best of it. 7924 */ 7925 7926 /* Posix to VMS destroyed this, so copy it again */ 7927 my_strlcpy(vmspath, &unixptr[1], seg_len + 1); 7928 vmslen = strlen(vmspath); /* We know we're truncating. */ 7929 vmsptr = &vmsptr[vmslen]; 7930 islnm = 0; 7931 7932 /* Now do we need to add the fake 6 zero directory to it? */ 7933 add_6zero = 1; 7934 if ((*lastslash == '/') && (nextslash < lastslash)) { 7935 /* No there is another directory */ 7936 add_6zero = 0; 7937 } 7938 else { 7939 int trnend; 7940 int cmp; 7941 7942 /* now we have foo:bar or foo:[000000]bar to decide from */ 7943 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0); 7944 7945 if (!islnm && !decc_posix_compliant_pathnames) { 7946 7947 cmp = strncmp("bin", vmspath, 4); 7948 if (cmp == 0) { 7949 /* bin => SYS$SYSTEM: */ 7950 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0); 7951 } 7952 else { 7953 /* tmp => SYS$SCRATCH: */ 7954 cmp = strncmp("tmp", vmspath, 4); 7955 if (cmp == 0) { 7956 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0); 7957 } 7958 } 7959 } 7960 7961 trnend = islnm ? islnm - 1 : 0; 7962 7963 /* if this was a logical name, ']' or '>' must be present */ 7964 /* if not a logical name, then assume a device and hope. */ 7965 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0; 7966 7967 /* if log name and trailing '.' then rooted - treat as device */ 7968 add_6zero = islnm ? (esa[trnend-1] == '.') : 0; 7969 7970 /* Fix me, if not a logical name, a device lookup should be 7971 * done to see if the device is file structured. If the device 7972 * is not file structured, the 6 zeros should not be put on. 7973 * 7974 * As it is, perl is occasionally looking for dev:[000000]tty. 7975 * which looks a little strange. 7976 * 7977 * Not that easy to detect as "/dev" may be file structured with 7978 * special device files. 7979 */ 7980 7981 if (!islnm && (add_6zero == 0) && (*nextslash == '/') && 7982 (&nextslash[1] == unixend)) { 7983 /* No real directory present */ 7984 add_6zero = 1; 7985 } 7986 } 7987 7988 /* Put the device delimiter on */ 7989 *vmsptr++ = ':'; 7990 vmslen++; 7991 unixptr = nextslash; 7992 unixptr++; 7993 7994 /* Start directory if needed */ 7995 if (!islnm || add_6zero) { 7996 *vmsptr++ = '['; 7997 vmslen++; 7998 dir_start = 1; 7999 } 8000 8001 /* add fake 000000] if needed */ 8002 if (add_6zero) { 8003 *vmsptr++ = '0'; 8004 *vmsptr++ = '0'; 8005 *vmsptr++ = '0'; 8006 *vmsptr++ = '0'; 8007 *vmsptr++ = '0'; 8008 *vmsptr++ = '0'; 8009 *vmsptr++ = ']'; 8010 vmslen += 7; 8011 dir_start = 0; 8012 } 8013 8014 } /* non-POSIX translation */ 8015 PerlMem_free(esa); 8016 } /* End of relative/absolute path handling */ 8017 8018 while ((unixptr <= unixend) && (vmslen < vmspath_len)){ 8019 int dash_flag; 8020 int in_cnt; 8021 int out_cnt; 8022 8023 dash_flag = 0; 8024 8025 if (dir_start != 0) { 8026 8027 /* First characters in a directory are handled special */ 8028 while ((*unixptr == '/') || 8029 ((*unixptr == '.') && 8030 ((unixptr[1]=='.') || (unixptr[1]=='/') || 8031 (&unixptr[1]==unixend)))) { 8032 int loop_flag; 8033 8034 loop_flag = 0; 8035 8036 /* Skip redundant / in specification */ 8037 while ((*unixptr == '/') && (dir_start != 0)) { 8038 loop_flag = 1; 8039 unixptr++; 8040 if (unixptr == lastslash) 8041 break; 8042 } 8043 if (unixptr == lastslash) 8044 break; 8045 8046 /* Skip redundant ./ characters */ 8047 while ((*unixptr == '.') && 8048 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) { 8049 loop_flag = 1; 8050 unixptr++; 8051 if (unixptr == lastslash) 8052 break; 8053 if (*unixptr == '/') 8054 unixptr++; 8055 } 8056 if (unixptr == lastslash) 8057 break; 8058 8059 /* Skip redundant ../ characters */ 8060 while ((*unixptr == '.') && (unixptr[1] == '.') && 8061 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) { 8062 /* Set the backing up flag */ 8063 loop_flag = 1; 8064 dir_dot = 0; 8065 dash_flag = 1; 8066 *vmsptr++ = '-'; 8067 vmslen++; 8068 unixptr++; /* first . */ 8069 unixptr++; /* second . */ 8070 if (unixptr == lastslash) 8071 break; 8072 if (*unixptr == '/') /* The slash */ 8073 unixptr++; 8074 } 8075 if (unixptr == lastslash) 8076 break; 8077 8078 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 8079 /* Not needed when VMS is pretending to be UNIX. */ 8080 8081 /* Is this loop stuck because of too many dots? */ 8082 if (loop_flag == 0) { 8083 /* Exit the loop and pass the rest through */ 8084 break; 8085 } 8086 } 8087 8088 /* Are we done with directories yet? */ 8089 if (unixptr >= lastslash) { 8090 8091 /* Watch out for trailing dots */ 8092 if (dir_dot != 0) { 8093 vmslen --; 8094 vmsptr--; 8095 } 8096 *vmsptr++ = ']'; 8097 vmslen++; 8098 dash_flag = 0; 8099 dir_start = 0; 8100 if (*unixptr == '/') 8101 unixptr++; 8102 } 8103 else { 8104 /* Have we stopped backing up? */ 8105 if (dash_flag) { 8106 *vmsptr++ = '.'; 8107 vmslen++; 8108 dash_flag = 0; 8109 /* dir_start continues to be = 1 */ 8110 } 8111 if (*unixptr == '-') { 8112 *vmsptr++ = '^'; 8113 *vmsptr++ = *unixptr++; 8114 vmslen += 2; 8115 dir_start = 0; 8116 8117 /* Now are we done with directories yet? */ 8118 if (unixptr >= lastslash) { 8119 8120 /* Watch out for trailing dots */ 8121 if (dir_dot != 0) { 8122 vmslen --; 8123 vmsptr--; 8124 } 8125 8126 *vmsptr++ = ']'; 8127 vmslen++; 8128 dash_flag = 0; 8129 dir_start = 0; 8130 } 8131 } 8132 } 8133 } 8134 8135 /* All done? */ 8136 if (unixptr >= unixend) 8137 break; 8138 8139 /* Normal characters - More EFS work probably needed */ 8140 dir_start = 0; 8141 dir_dot = 0; 8142 8143 switch(*unixptr) { 8144 case '/': 8145 /* remove multiple / */ 8146 while (unixptr[1] == '/') { 8147 unixptr++; 8148 } 8149 if (unixptr == lastslash) { 8150 /* Watch out for trailing dots */ 8151 if (dir_dot != 0) { 8152 vmslen --; 8153 vmsptr--; 8154 } 8155 *vmsptr++ = ']'; 8156 } 8157 else { 8158 dir_start = 1; 8159 *vmsptr++ = '.'; 8160 dir_dot = 1; 8161 8162 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 8163 /* Not needed when VMS is pretending to be UNIX. */ 8164 8165 } 8166 dash_flag = 0; 8167 if (unixptr != unixend) 8168 unixptr++; 8169 vmslen++; 8170 break; 8171 case '.': 8172 if ((unixptr < lastdot) || (unixptr < lastslash) || 8173 (&unixptr[1] == unixend)) { 8174 *vmsptr++ = '^'; 8175 *vmsptr++ = '.'; 8176 vmslen += 2; 8177 unixptr++; 8178 8179 /* trailing dot ==> '^..' on VMS */ 8180 if (unixptr == unixend) { 8181 *vmsptr++ = '.'; 8182 vmslen++; 8183 unixptr++; 8184 } 8185 break; 8186 } 8187 8188 *vmsptr++ = *unixptr++; 8189 vmslen ++; 8190 break; 8191 case '"': 8192 if (quoted && (&unixptr[1] == unixend)) { 8193 unixptr++; 8194 break; 8195 } 8196 in_cnt = copy_expand_unix_filename_escape 8197 (vmsptr, unixptr, &out_cnt, utf8_fl); 8198 vmsptr += out_cnt; 8199 unixptr += in_cnt; 8200 break; 8201 case '~': 8202 case ';': 8203 case '\\': 8204 case '?': 8205 case ' ': 8206 default: 8207 in_cnt = copy_expand_unix_filename_escape 8208 (vmsptr, unixptr, &out_cnt, utf8_fl); 8209 vmsptr += out_cnt; 8210 unixptr += in_cnt; 8211 break; 8212 } 8213 } 8214 8215 /* Make sure directory is closed */ 8216 if (unixptr == lastslash) { 8217 char *vmsptr2; 8218 vmsptr2 = vmsptr - 1; 8219 8220 if (*vmsptr2 != ']') { 8221 *vmsptr2--; 8222 8223 /* directories do not end in a dot bracket */ 8224 if (*vmsptr2 == '.') { 8225 vmsptr2--; 8226 8227 /* ^. is allowed */ 8228 if (*vmsptr2 != '^') { 8229 vmsptr--; /* back up over the dot */ 8230 } 8231 } 8232 *vmsptr++ = ']'; 8233 } 8234 } 8235 else { 8236 char *vmsptr2; 8237 /* Add a trailing dot if a file with no extension */ 8238 vmsptr2 = vmsptr - 1; 8239 if ((vmslen > 1) && 8240 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') && 8241 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) { 8242 *vmsptr++ = '.'; 8243 vmslen++; 8244 } 8245 } 8246 8247 *vmsptr = '\0'; 8248 return SS$_NORMAL; 8249 } 8250 #endif 8251 8252 /* Eventual routine to convert a UTF-8 specification to VTF-7. */ 8253 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl) 8254 { 8255 char * result; 8256 int utf8_flag; 8257 8258 /* If a UTF8 flag is being passed, honor it */ 8259 utf8_flag = 0; 8260 if (utf8_fl != NULL) { 8261 utf8_flag = *utf8_fl; 8262 *utf8_fl = 0; 8263 } 8264 8265 if (utf8_flag) { 8266 /* If there is a possibility of UTF8, then if any UTF8 characters 8267 are present, then they must be converted to VTF-7 8268 */ 8269 result = strcpy(rslt, path); /* FIX-ME */ 8270 } 8271 else 8272 result = strcpy(rslt, path); 8273 8274 return result; 8275 } 8276 8277 /* A convenience macro for copying dots in filenames and escaping 8278 * them when they haven't already been escaped, with guards to 8279 * avoid checking before the start of the buffer or advancing 8280 * beyond the end of it (allowing room for the NUL terminator). 8281 */ 8282 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \ 8283 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \ 8284 || ((vmsefsdot) == (vmsefsbuf))) \ 8285 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \ 8286 ) { \ 8287 *((vmsefsdot)++) = '^'; \ 8288 } \ 8289 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \ 8290 *((vmsefsdot)++) = '.'; \ 8291 } STMT_END 8292 8293 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 8294 static char *int_tovmsspec 8295 (const char *path, char *rslt, int dir_flag, int * utf8_flag) { 8296 char *dirend; 8297 char *lastdot; 8298 char *cp1; 8299 const char *cp2; 8300 unsigned long int infront = 0, hasdir = 1; 8301 int rslt_len; 8302 int no_type_seen; 8303 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 8304 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 8305 8306 if (vms_debug_fileify) { 8307 if (path == NULL) 8308 fprintf(stderr, "int_tovmsspec: path = NULL\n"); 8309 else 8310 fprintf(stderr, "int_tovmsspec: path = %s\n", path); 8311 } 8312 8313 if (path == NULL) { 8314 /* If we fail, we should be setting errno */ 8315 set_errno(EINVAL); 8316 set_vaxc_errno(SS$_BADPARAM); 8317 return NULL; 8318 } 8319 rslt_len = VMS_MAXRSS-1; 8320 8321 /* '.' and '..' are "[]" and "[-]" for a quick check */ 8322 if (path[0] == '.') { 8323 if (path[1] == '\0') { 8324 strcpy(rslt,"[]"); 8325 if (utf8_flag != NULL) 8326 *utf8_flag = 0; 8327 return rslt; 8328 } 8329 else { 8330 if (path[1] == '.' && path[2] == '\0') { 8331 strcpy(rslt,"[-]"); 8332 if (utf8_flag != NULL) 8333 *utf8_flag = 0; 8334 return rslt; 8335 } 8336 } 8337 } 8338 8339 /* Posix specifications are now a native VMS format */ 8340 /*--------------------------------------------------*/ 8341 #if __CRTL_VER >= 80200000 && !defined(__VAX) 8342 if (decc_posix_compliant_pathnames) { 8343 if (strncmp(path,"\"^UP^",5) == 0) { 8344 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag); 8345 return rslt; 8346 } 8347 } 8348 #endif 8349 8350 /* This is really the only way to see if this is already in VMS format */ 8351 sts = vms_split_path 8352 (path, 8353 &v_spec, 8354 &v_len, 8355 &r_spec, 8356 &r_len, 8357 &d_spec, 8358 &d_len, 8359 &n_spec, 8360 &n_len, 8361 &e_spec, 8362 &e_len, 8363 &vs_spec, 8364 &vs_len); 8365 if (sts == 0) { 8366 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath() 8367 replacement, because the above parse just took care of most of 8368 what is needed to do vmspath when the specification is already 8369 in VMS format. 8370 8371 And if it is not already, it is easier to do the conversion as 8372 part of this routine than to call this routine and then work on 8373 the result. 8374 */ 8375 8376 /* If VMS punctuation was found, it is already VMS format */ 8377 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) { 8378 if (utf8_flag != NULL) 8379 *utf8_flag = 0; 8380 my_strlcpy(rslt, path, VMS_MAXRSS); 8381 if (vms_debug_fileify) { 8382 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8383 } 8384 return rslt; 8385 } 8386 /* Now, what to do with trailing "." cases where there is no 8387 extension? If this is a UNIX specification, and EFS characters 8388 are enabled, then the trailing "." should be converted to a "^.". 8389 But if this was already a VMS specification, then it should be 8390 left alone. 8391 8392 So in the case of ambiguity, leave the specification alone. 8393 */ 8394 8395 8396 /* If there is a possibility of UTF8, then if any UTF8 characters 8397 are present, then they must be converted to VTF-7 8398 */ 8399 if (utf8_flag != NULL) 8400 *utf8_flag = 0; 8401 my_strlcpy(rslt, path, VMS_MAXRSS); 8402 if (vms_debug_fileify) { 8403 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8404 } 8405 return rslt; 8406 } 8407 8408 dirend = strrchr(path,'/'); 8409 8410 if (dirend == NULL) { 8411 /* If we get here with no Unix directory delimiters, then this is an 8412 * ambiguous file specification, such as a Unix glob specification, a 8413 * shell or make macro, or a filespec that would be valid except for 8414 * unescaped extended characters. The safest thing if it's a macro 8415 * is to pass it through as-is. 8416 */ 8417 if (strstr(path, "$(")) { 8418 my_strlcpy(rslt, path, VMS_MAXRSS); 8419 if (vms_debug_fileify) { 8420 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8421 } 8422 return rslt; 8423 } 8424 hasdir = 0; 8425 } 8426 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */ 8427 if (!*(dirend+2)) dirend +=2; 8428 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3; 8429 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4; 8430 } 8431 8432 cp1 = rslt; 8433 cp2 = path; 8434 lastdot = strrchr(cp2,'.'); 8435 if (*cp2 == '/') { 8436 char *trndev; 8437 int islnm, rooted; 8438 STRLEN trnend; 8439 8440 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8441 if (!*(cp2+1)) { 8442 if (decc_disable_posix_root) { 8443 strcpy(rslt,"sys$disk:[000000]"); 8444 } 8445 else { 8446 strcpy(rslt,"sys$posix_root:[000000]"); 8447 } 8448 if (utf8_flag != NULL) 8449 *utf8_flag = 0; 8450 if (vms_debug_fileify) { 8451 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8452 } 8453 return rslt; 8454 } 8455 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2; 8456 *cp1 = '\0'; 8457 trndev = (char *)PerlMem_malloc(VMS_MAXRSS); 8458 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8459 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8460 8461 /* DECC special handling */ 8462 if (!islnm) { 8463 if (strcmp(rslt,"bin") == 0) { 8464 strcpy(rslt,"sys$system"); 8465 cp1 = rslt + 10; 8466 *cp1 = 0; 8467 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8468 } 8469 else if (strcmp(rslt,"tmp") == 0) { 8470 strcpy(rslt,"sys$scratch"); 8471 cp1 = rslt + 11; 8472 *cp1 = 0; 8473 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8474 } 8475 else if (!decc_disable_posix_root) { 8476 strcpy(rslt, "sys$posix_root"); 8477 cp1 = rslt + 14; 8478 *cp1 = 0; 8479 cp2 = path; 8480 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8481 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8482 } 8483 else if (strcmp(rslt,"dev") == 0) { 8484 if (strncmp(cp2,"/null", 5) == 0) { 8485 if ((cp2[5] == 0) || (cp2[5] == '/')) { 8486 strcpy(rslt,"NLA0"); 8487 cp1 = rslt + 4; 8488 *cp1 = 0; 8489 cp2 = cp2 + 5; 8490 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1); 8491 } 8492 } 8493 } 8494 } 8495 8496 trnend = islnm ? strlen(trndev) - 1 : 0; 8497 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0; 8498 rooted = islnm ? (trndev[trnend-1] == '.') : 0; 8499 /* If the first element of the path is a logical name, determine 8500 * whether it has to be translated so we can add more directories. */ 8501 if (!islnm || rooted) { 8502 *(cp1++) = ':'; 8503 *(cp1++) = '['; 8504 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0'; 8505 else cp2++; 8506 } 8507 else { 8508 if (cp2 != dirend) { 8509 my_strlcpy(rslt, trndev, VMS_MAXRSS); 8510 cp1 = rslt + trnend; 8511 if (*cp2 != 0) { 8512 *(cp1++) = '.'; 8513 cp2++; 8514 } 8515 } 8516 else { 8517 if (decc_disable_posix_root) { 8518 *(cp1++) = ':'; 8519 hasdir = 0; 8520 } 8521 } 8522 } 8523 PerlMem_free(trndev); 8524 } 8525 else if (hasdir) { 8526 *(cp1++) = '['; 8527 if (*cp2 == '.') { 8528 if (*(cp2+1) == '/' || *(cp2+1) == '\0') { 8529 cp2 += 2; /* skip over "./" - it's redundant */ 8530 *(cp1++) = '.'; /* but it does indicate a relative dirspec */ 8531 } 8532 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8533 *(cp1++) = '-'; /* "../" --> "-" */ 8534 cp2 += 3; 8535 } 8536 else if (*(cp2+1) == '.' && *(cp2+2) == '.' && 8537 (*(cp2+3) == '/' || *(cp2+3) == '\0')) { 8538 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8539 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */ 8540 cp2 += 4; 8541 } 8542 else if ((cp2 != lastdot) || (lastdot < dirend)) { 8543 /* Escape the extra dots in EFS file specifications */ 8544 *(cp1++) = '^'; 8545 } 8546 if (cp2 > dirend) cp2 = dirend; 8547 } 8548 else *(cp1++) = '.'; 8549 } 8550 for (; cp2 < dirend; cp2++) { 8551 if (*cp2 == '/') { 8552 if (*(cp2-1) == '/') continue; 8553 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; 8554 infront = 0; 8555 } 8556 else if (!infront && *cp2 == '.') { 8557 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; } 8558 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */ 8559 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8560 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */ 8561 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-'; 8562 else { 8563 *(cp1++) = '-'; 8564 } 8565 cp2 += 2; 8566 if (cp2 == dirend) break; 8567 } 8568 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' && 8569 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) { 8570 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */ 8571 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8572 if (!*(cp2+3)) { 8573 *(cp1++) = '.'; /* Simulate trailing '/' */ 8574 cp2 += 2; /* for loop will incr this to == dirend */ 8575 } 8576 else cp2 += 3; /* Trailing '/' was there, so skip it, too */ 8577 } 8578 else { 8579 if (decc_efs_charset == 0) { 8580 if (cp1 > rslt && *(cp1-1) == '^') 8581 cp1--; /* remove the escape, if any */ 8582 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */ 8583 } 8584 else { 8585 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8586 } 8587 } 8588 } 8589 else { 8590 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.'; 8591 if (*cp2 == '.') { 8592 if (decc_efs_charset == 0) { 8593 if (cp1 > rslt && *(cp1-1) == '^') 8594 cp1--; /* remove the escape, if any */ 8595 *(cp1++) = '_'; 8596 } 8597 else { 8598 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8599 } 8600 } 8601 else *(cp1++) = *cp2; 8602 infront = 1; 8603 } 8604 } 8605 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */ 8606 if (hasdir) *(cp1++) = ']'; 8607 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */ 8608 no_type_seen = 0; 8609 if (cp2 > lastdot) 8610 no_type_seen = 1; 8611 while (*cp2) { 8612 switch(*cp2) { 8613 case '?': 8614 if (decc_efs_charset == 0) 8615 *(cp1++) = '%'; 8616 else 8617 *(cp1++) = '?'; 8618 cp2++; 8619 case ' ': 8620 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */ 8621 *(cp1)++ = '^'; 8622 *(cp1)++ = '_'; 8623 cp2++; 8624 break; 8625 case '.': 8626 if (((cp2 < lastdot) || (cp2[1] == '\0')) && 8627 decc_readdir_dropdotnotype) { 8628 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS); 8629 cp2++; 8630 8631 /* trailing dot ==> '^..' on VMS */ 8632 if (*cp2 == '\0') { 8633 *(cp1++) = '.'; 8634 no_type_seen = 0; 8635 } 8636 } 8637 else { 8638 *(cp1++) = *(cp2++); 8639 no_type_seen = 0; 8640 } 8641 break; 8642 case '$': 8643 /* This could be a macro to be passed through */ 8644 *(cp1++) = *(cp2++); 8645 if (*cp2 == '(') { 8646 const char * save_cp2; 8647 char * save_cp1; 8648 int is_macro; 8649 8650 /* paranoid check */ 8651 save_cp2 = cp2; 8652 save_cp1 = cp1; 8653 is_macro = 0; 8654 8655 /* Test through */ 8656 *(cp1++) = *(cp2++); 8657 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8658 *(cp1++) = *(cp2++); 8659 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8660 *(cp1++) = *(cp2++); 8661 } 8662 if (*cp2 == ')') { 8663 *(cp1++) = *(cp2++); 8664 is_macro = 1; 8665 } 8666 } 8667 if (is_macro == 0) { 8668 /* Not really a macro - never mind */ 8669 cp2 = save_cp2; 8670 cp1 = save_cp1; 8671 } 8672 } 8673 break; 8674 case '\"': 8675 case '~': 8676 case '`': 8677 case '!': 8678 case '#': 8679 case '%': 8680 case '^': 8681 /* Don't escape again if following character is 8682 * already something we escape. 8683 */ 8684 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) { 8685 *(cp1++) = *(cp2++); 8686 break; 8687 } 8688 /* But otherwise fall through and escape it. */ 8689 case '&': 8690 case '(': 8691 case ')': 8692 case '=': 8693 case '+': 8694 case '\'': 8695 case '@': 8696 case '[': 8697 case ']': 8698 case '{': 8699 case '}': 8700 case ':': 8701 case '\\': 8702 case '|': 8703 case '<': 8704 case '>': 8705 if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */ 8706 *(cp1++) = '^'; 8707 *(cp1++) = *(cp2++); 8708 break; 8709 case ';': 8710 /* If it doesn't look like the beginning of a version number, 8711 * or we've been promised there are no version numbers, then 8712 * escape it. 8713 */ 8714 if (decc_filename_unix_no_version) { 8715 *(cp1++) = '^'; 8716 } 8717 else { 8718 size_t all_nums = strspn(cp2+1, "0123456789"); 8719 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0') 8720 *(cp1++) = '^'; 8721 } 8722 *(cp1++) = *(cp2++); 8723 break; 8724 default: 8725 *(cp1++) = *(cp2++); 8726 } 8727 } 8728 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) { 8729 char *lcp1; 8730 lcp1 = cp1; 8731 lcp1--; 8732 /* Fix me for "^]", but that requires making sure that you do 8733 * not back up past the start of the filename 8734 */ 8735 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%')) 8736 *cp1++ = '.'; 8737 } 8738 *cp1 = '\0'; 8739 8740 if (utf8_flag != NULL) 8741 *utf8_flag = 0; 8742 if (vms_debug_fileify) { 8743 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt); 8744 } 8745 return rslt; 8746 8747 } /* end of int_tovmsspec() */ 8748 8749 8750 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 8751 static char *mp_do_tovmsspec 8752 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) { 8753 static char __tovmsspec_retbuf[VMS_MAXRSS]; 8754 char * vmsspec, *ret_spec, *ret_buf; 8755 8756 vmsspec = NULL; 8757 ret_buf = buf; 8758 if (ret_buf == NULL) { 8759 if (ts) { 8760 Newx(vmsspec, VMS_MAXRSS, char); 8761 if (vmsspec == NULL) 8762 _ckvmssts(SS$_INSFMEM); 8763 ret_buf = vmsspec; 8764 } else { 8765 ret_buf = __tovmsspec_retbuf; 8766 } 8767 } 8768 8769 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag); 8770 8771 if (ret_spec == NULL) { 8772 /* Cleanup on isle 5, if this is thread specific we need to deallocate */ 8773 if (vmsspec) 8774 Safefree(vmsspec); 8775 } 8776 8777 return ret_spec; 8778 8779 } /* end of mp_do_tovmsspec() */ 8780 /*}}}*/ 8781 /* External entry points */ 8782 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) 8783 { return do_tovmsspec(path,buf,0,NULL); } 8784 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) 8785 { return do_tovmsspec(path,buf,1,NULL); } 8786 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8787 { return do_tovmsspec(path,buf,0,utf8_fl); } 8788 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8789 { return do_tovmsspec(path,buf,1,utf8_fl); } 8790 8791 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/ 8792 /* Internal routine for use with out an explicit context present */ 8793 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) { 8794 8795 char * ret_spec, *pathified; 8796 8797 if (path == NULL) 8798 return NULL; 8799 8800 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8801 if (pathified == NULL) 8802 _ckvmssts_noperl(SS$_INSFMEM); 8803 8804 ret_spec = int_pathify_dirspec(path, pathified); 8805 8806 if (ret_spec == NULL) { 8807 PerlMem_free(pathified); 8808 return NULL; 8809 } 8810 8811 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl); 8812 8813 PerlMem_free(pathified); 8814 return ret_spec; 8815 8816 } 8817 8818 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/ 8819 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) { 8820 static char __tovmspath_retbuf[VMS_MAXRSS]; 8821 int vmslen; 8822 char *pathified, *vmsified, *cp; 8823 8824 if (path == NULL) return NULL; 8825 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8826 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8827 if (int_pathify_dirspec(path, pathified) == NULL) { 8828 PerlMem_free(pathified); 8829 return NULL; 8830 } 8831 8832 vmsified = NULL; 8833 if (buf == NULL) 8834 Newx(vmsified, VMS_MAXRSS, char); 8835 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) { 8836 PerlMem_free(pathified); 8837 if (vmsified) Safefree(vmsified); 8838 return NULL; 8839 } 8840 PerlMem_free(pathified); 8841 if (buf) { 8842 return buf; 8843 } 8844 else if (ts) { 8845 vmslen = strlen(vmsified); 8846 Newx(cp,vmslen+1,char); 8847 memcpy(cp,vmsified,vmslen); 8848 cp[vmslen] = '\0'; 8849 Safefree(vmsified); 8850 return cp; 8851 } 8852 else { 8853 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf)); 8854 Safefree(vmsified); 8855 return __tovmspath_retbuf; 8856 } 8857 8858 } /* end of do_tovmspath() */ 8859 /*}}}*/ 8860 /* External entry points */ 8861 char *Perl_tovmspath(pTHX_ const char *path, char *buf) 8862 { return do_tovmspath(path,buf,0, NULL); } 8863 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) 8864 { return do_tovmspath(path,buf,1, NULL); } 8865 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl) 8866 { return do_tovmspath(path,buf,0,utf8_fl); } 8867 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl) 8868 { return do_tovmspath(path,buf,1,utf8_fl); } 8869 8870 8871 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/ 8872 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) { 8873 static char __tounixpath_retbuf[VMS_MAXRSS]; 8874 int unixlen; 8875 char *pathified, *unixified, *cp; 8876 8877 if (path == NULL) return NULL; 8878 pathified = (char *)PerlMem_malloc(VMS_MAXRSS); 8879 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8880 if (int_pathify_dirspec(path, pathified) == NULL) { 8881 PerlMem_free(pathified); 8882 return NULL; 8883 } 8884 8885 unixified = NULL; 8886 if (buf == NULL) { 8887 Newx(unixified, VMS_MAXRSS, char); 8888 } 8889 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) { 8890 PerlMem_free(pathified); 8891 if (unixified) Safefree(unixified); 8892 return NULL; 8893 } 8894 PerlMem_free(pathified); 8895 if (buf) { 8896 return buf; 8897 } 8898 else if (ts) { 8899 unixlen = strlen(unixified); 8900 Newx(cp,unixlen+1,char); 8901 memcpy(cp,unixified,unixlen); 8902 cp[unixlen] = '\0'; 8903 Safefree(unixified); 8904 return cp; 8905 } 8906 else { 8907 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf)); 8908 Safefree(unixified); 8909 return __tounixpath_retbuf; 8910 } 8911 8912 } /* end of do_tounixpath() */ 8913 /*}}}*/ 8914 /* External entry points */ 8915 char *Perl_tounixpath(pTHX_ const char *path, char *buf) 8916 { return do_tounixpath(path,buf,0,NULL); } 8917 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) 8918 { return do_tounixpath(path,buf,1,NULL); } 8919 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8920 { return do_tounixpath(path,buf,0,utf8_fl); } 8921 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8922 { return do_tounixpath(path,buf,1,utf8_fl); } 8923 8924 /* 8925 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com) 8926 * 8927 ***************************************************************************** 8928 * * 8929 * Copyright (C) 1989-1994, 2007 by * 8930 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 * 8931 * * 8932 * Permission is hereby granted for the reproduction of this software * 8933 * on condition that this copyright notice is included in source * 8934 * distributions of the software. The code may be modified and * 8935 * distributed under the same terms as Perl itself. * 8936 * * 8937 * 27-Aug-1994 Modified for inclusion in perl5 * 8938 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) * 8939 ***************************************************************************** 8940 */ 8941 8942 /* 8943 * getredirection() is intended to aid in porting C programs 8944 * to VMS (Vax-11 C). The native VMS environment does not support 8945 * '>' and '<' I/O redirection, or command line wild card expansion, 8946 * or a command line pipe mechanism using the '|' AND background 8947 * command execution '&'. All of these capabilities are provided to any 8948 * C program which calls this procedure as the first thing in the 8949 * main program. 8950 * The piping mechanism will probably work with almost any 'filter' type 8951 * of program. With suitable modification, it may useful for other 8952 * portability problems as well. 8953 * 8954 * Author: Mark Pizzolato (mark AT infocomm DOT com) 8955 */ 8956 struct list_item 8957 { 8958 struct list_item *next; 8959 char *value; 8960 }; 8961 8962 static void add_item(struct list_item **head, 8963 struct list_item **tail, 8964 char *value, 8965 int *count); 8966 8967 static void mp_expand_wild_cards(pTHX_ char *item, 8968 struct list_item **head, 8969 struct list_item **tail, 8970 int *count); 8971 8972 static int background_process(pTHX_ int argc, char **argv); 8973 8974 static void pipe_and_fork(pTHX_ char **cmargv); 8975 8976 /*{{{ void getredirection(int *ac, char ***av)*/ 8977 static void 8978 mp_getredirection(pTHX_ int *ac, char ***av) 8979 /* 8980 * Process vms redirection arg's. Exit if any error is seen. 8981 * If getredirection() processes an argument, it is erased 8982 * from the vector. getredirection() returns a new argc and argv value. 8983 * In the event that a background command is requested (by a trailing "&"), 8984 * this routine creates a background subprocess, and simply exits the program. 8985 * 8986 * Warning: do not try to simplify the code for vms. The code 8987 * presupposes that getredirection() is called before any data is 8988 * read from stdin or written to stdout. 8989 * 8990 * Normal usage is as follows: 8991 * 8992 * main(argc, argv) 8993 * int argc; 8994 * char *argv[]; 8995 * { 8996 * getredirection(&argc, &argv); 8997 * } 8998 */ 8999 { 9000 int argc = *ac; /* Argument Count */ 9001 char **argv = *av; /* Argument Vector */ 9002 char *ap; /* Argument pointer */ 9003 int j; /* argv[] index */ 9004 int item_count = 0; /* Count of Items in List */ 9005 struct list_item *list_head = 0; /* First Item in List */ 9006 struct list_item *list_tail; /* Last Item in List */ 9007 char *in = NULL; /* Input File Name */ 9008 char *out = NULL; /* Output File Name */ 9009 char *outmode = "w"; /* Mode to Open Output File */ 9010 char *err = NULL; /* Error File Name */ 9011 char *errmode = "w"; /* Mode to Open Error File */ 9012 int cmargc = 0; /* Piped Command Arg Count */ 9013 char **cmargv = NULL;/* Piped Command Arg Vector */ 9014 9015 /* 9016 * First handle the case where the last thing on the line ends with 9017 * a '&'. This indicates the desire for the command to be run in a 9018 * subprocess, so we satisfy that desire. 9019 */ 9020 ap = argv[argc-1]; 9021 if (0 == strcmp("&", ap)) 9022 exit(background_process(aTHX_ --argc, argv)); 9023 if (*ap && '&' == ap[strlen(ap)-1]) 9024 { 9025 ap[strlen(ap)-1] = '\0'; 9026 exit(background_process(aTHX_ argc, argv)); 9027 } 9028 /* 9029 * Now we handle the general redirection cases that involve '>', '>>', 9030 * '<', and pipes '|'. 9031 */ 9032 for (j = 0; j < argc; ++j) 9033 { 9034 if (0 == strcmp("<", argv[j])) 9035 { 9036 if (j+1 >= argc) 9037 { 9038 fprintf(stderr,"No input file after < on command line"); 9039 exit(LIB$_WRONUMARG); 9040 } 9041 in = argv[++j]; 9042 continue; 9043 } 9044 if ('<' == *(ap = argv[j])) 9045 { 9046 in = 1 + ap; 9047 continue; 9048 } 9049 if (0 == strcmp(">", ap)) 9050 { 9051 if (j+1 >= argc) 9052 { 9053 fprintf(stderr,"No output file after > on command line"); 9054 exit(LIB$_WRONUMARG); 9055 } 9056 out = argv[++j]; 9057 continue; 9058 } 9059 if ('>' == *ap) 9060 { 9061 if ('>' == ap[1]) 9062 { 9063 outmode = "a"; 9064 if ('\0' == ap[2]) 9065 out = argv[++j]; 9066 else 9067 out = 2 + ap; 9068 } 9069 else 9070 out = 1 + ap; 9071 if (j >= argc) 9072 { 9073 fprintf(stderr,"No output file after > or >> on command line"); 9074 exit(LIB$_WRONUMARG); 9075 } 9076 continue; 9077 } 9078 if (('2' == *ap) && ('>' == ap[1])) 9079 { 9080 if ('>' == ap[2]) 9081 { 9082 errmode = "a"; 9083 if ('\0' == ap[3]) 9084 err = argv[++j]; 9085 else 9086 err = 3 + ap; 9087 } 9088 else 9089 if ('\0' == ap[2]) 9090 err = argv[++j]; 9091 else 9092 err = 2 + ap; 9093 if (j >= argc) 9094 { 9095 fprintf(stderr,"No output file after 2> or 2>> on command line"); 9096 exit(LIB$_WRONUMARG); 9097 } 9098 continue; 9099 } 9100 if (0 == strcmp("|", argv[j])) 9101 { 9102 if (j+1 >= argc) 9103 { 9104 fprintf(stderr,"No command into which to pipe on command line"); 9105 exit(LIB$_WRONUMARG); 9106 } 9107 cmargc = argc-(j+1); 9108 cmargv = &argv[j+1]; 9109 argc = j; 9110 continue; 9111 } 9112 if ('|' == *(ap = argv[j])) 9113 { 9114 ++argv[j]; 9115 cmargc = argc-j; 9116 cmargv = &argv[j]; 9117 argc = j; 9118 continue; 9119 } 9120 expand_wild_cards(ap, &list_head, &list_tail, &item_count); 9121 } 9122 /* 9123 * Allocate and fill in the new argument vector, Some Unix's terminate 9124 * the list with an extra null pointer. 9125 */ 9126 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *)); 9127 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9128 *av = argv; 9129 for (j = 0; j < item_count; ++j, list_head = list_head->next) 9130 argv[j] = list_head->value; 9131 *ac = item_count; 9132 if (cmargv != NULL) 9133 { 9134 if (out != NULL) 9135 { 9136 fprintf(stderr,"'|' and '>' may not both be specified on command line"); 9137 exit(LIB$_INVARGORD); 9138 } 9139 pipe_and_fork(aTHX_ cmargv); 9140 } 9141 9142 /* Check for input from a pipe (mailbox) */ 9143 9144 if (in == NULL && 1 == isapipe(0)) 9145 { 9146 char mbxname[L_tmpnam]; 9147 long int bufsize; 9148 long int dvi_item = DVI$_DEVBUFSIZ; 9149 $DESCRIPTOR(mbxnam, ""); 9150 $DESCRIPTOR(mbxdevnam, ""); 9151 9152 /* Input from a pipe, reopen it in binary mode to disable */ 9153 /* carriage control processing. */ 9154 9155 fgetname(stdin, mbxname, 1); 9156 mbxnam.dsc$a_pointer = mbxname; 9157 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer); 9158 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0); 9159 mbxdevnam.dsc$a_pointer = mbxname; 9160 mbxdevnam.dsc$w_length = sizeof(mbxname); 9161 dvi_item = DVI$_DEVNAM; 9162 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length); 9163 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0'; 9164 set_errno(0); 9165 set_vaxc_errno(1); 9166 freopen(mbxname, "rb", stdin); 9167 if (errno != 0) 9168 { 9169 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname); 9170 exit(vaxc$errno); 9171 } 9172 } 9173 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) 9174 { 9175 fprintf(stderr,"Can't open input file %s as stdin",in); 9176 exit(vaxc$errno); 9177 } 9178 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) 9179 { 9180 fprintf(stderr,"Can't open output file %s as stdout",out); 9181 exit(vaxc$errno); 9182 } 9183 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out); 9184 9185 if (err != NULL) { 9186 if (strcmp(err,"&1") == 0) { 9187 dup2(fileno(stdout), fileno(stderr)); 9188 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT"); 9189 } else { 9190 FILE *tmperr; 9191 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2"))) 9192 { 9193 fprintf(stderr,"Can't open error file %s as stderr",err); 9194 exit(vaxc$errno); 9195 } 9196 fclose(tmperr); 9197 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2")) 9198 { 9199 exit(vaxc$errno); 9200 } 9201 vmssetuserlnm("SYS$ERROR", err); 9202 } 9203 } 9204 #ifdef ARGPROC_DEBUG 9205 PerlIO_printf(Perl_debug_log, "Arglist:\n"); 9206 for (j = 0; j < *ac; ++j) 9207 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]); 9208 #endif 9209 /* Clear errors we may have hit expanding wildcards, so they don't 9210 show up in Perl's $! later */ 9211 set_errno(0); set_vaxc_errno(1); 9212 } /* end of getredirection() */ 9213 /*}}}*/ 9214 9215 static void add_item(struct list_item **head, 9216 struct list_item **tail, 9217 char *value, 9218 int *count) 9219 { 9220 if (*head == 0) 9221 { 9222 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 9223 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9224 *tail = *head; 9225 } 9226 else { 9227 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 9228 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9229 *tail = (*tail)->next; 9230 } 9231 (*tail)->value = value; 9232 ++(*count); 9233 } 9234 9235 static void mp_expand_wild_cards(pTHX_ char *item, 9236 struct list_item **head, 9237 struct list_item **tail, 9238 int *count) 9239 { 9240 int expcount = 0; 9241 unsigned long int context = 0; 9242 int isunix = 0; 9243 int item_len = 0; 9244 char *had_version; 9245 char *had_device; 9246 int had_directory; 9247 char *devdir,*cp; 9248 char *vmsspec; 9249 $DESCRIPTOR(filespec, ""); 9250 $DESCRIPTOR(defaultspec, "SYS$DISK:[]"); 9251 $DESCRIPTOR(resultspec, ""); 9252 unsigned long int lff_flags = 0; 9253 int sts; 9254 int rms_sts; 9255 9256 #ifdef VMS_LONGNAME_SUPPORT 9257 lff_flags = LIB$M_FIL_LONG_NAMES; 9258 #endif 9259 9260 for (cp = item; *cp; cp++) { 9261 if (*cp == '*' || *cp == '%' || isspace(*cp)) break; 9262 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break; 9263 } 9264 if (!*cp || isspace(*cp)) 9265 { 9266 add_item(head, tail, item, count); 9267 return; 9268 } 9269 else 9270 { 9271 /* "double quoted" wild card expressions pass as is */ 9272 /* From DCL that means using e.g.: */ 9273 /* perl program """perl.*""" */ 9274 item_len = strlen(item); 9275 if ( '"' == *item && '"' == item[item_len-1] ) 9276 { 9277 item++; 9278 item[item_len-2] = '\0'; 9279 add_item(head, tail, item, count); 9280 return; 9281 } 9282 } 9283 resultspec.dsc$b_dtype = DSC$K_DTYPE_T; 9284 resultspec.dsc$b_class = DSC$K_CLASS_D; 9285 resultspec.dsc$a_pointer = NULL; 9286 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS); 9287 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9288 if ((isunix = (int) strchr(item,'/')) != (int) NULL) 9289 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL); 9290 if (!isunix || !filespec.dsc$a_pointer) 9291 filespec.dsc$a_pointer = item; 9292 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer); 9293 /* 9294 * Only return version specs, if the caller specified a version 9295 */ 9296 had_version = strchr(item, ';'); 9297 /* 9298 * Only return device and directory specs, if the caller specified either. 9299 */ 9300 had_device = strchr(item, ':'); 9301 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<')); 9302 9303 while ($VMS_STATUS_SUCCESS(sts = lib$find_file 9304 (&filespec, &resultspec, &context, 9305 &defaultspec, 0, &rms_sts, &lff_flags))) 9306 { 9307 char *string; 9308 char *c; 9309 9310 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1); 9311 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9312 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1); 9313 if (NULL == had_version) 9314 *(strrchr(string, ';')) = '\0'; 9315 if ((!had_directory) && (had_device == NULL)) 9316 { 9317 if (NULL == (devdir = strrchr(string, ']'))) 9318 devdir = strrchr(string, '>'); 9319 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1); 9320 } 9321 /* 9322 * Be consistent with what the C RTL has already done to the rest of 9323 * the argv items and lowercase all of these names. 9324 */ 9325 if (!decc_efs_case_preserve) { 9326 for (c = string; *c; ++c) 9327 if (isupper(*c)) 9328 *c = tolower(*c); 9329 } 9330 if (isunix) trim_unixpath(string,item,1); 9331 add_item(head, tail, string, count); 9332 ++expcount; 9333 } 9334 PerlMem_free(vmsspec); 9335 if (sts != RMS$_NMF) 9336 { 9337 set_vaxc_errno(sts); 9338 switch (sts) 9339 { 9340 case RMS$_FNF: case RMS$_DNF: 9341 set_errno(ENOENT); break; 9342 case RMS$_DIR: 9343 set_errno(ENOTDIR); break; 9344 case RMS$_DEV: 9345 set_errno(ENODEV); break; 9346 case RMS$_FNM: case RMS$_SYN: 9347 set_errno(EINVAL); break; 9348 case RMS$_PRV: 9349 set_errno(EACCES); break; 9350 default: 9351 _ckvmssts_noperl(sts); 9352 } 9353 } 9354 if (expcount == 0) 9355 add_item(head, tail, item, count); 9356 _ckvmssts_noperl(lib$sfree1_dd(&resultspec)); 9357 _ckvmssts_noperl(lib$find_file_end(&context)); 9358 } 9359 9360 static int child_st[2];/* Event Flag set when child process completes */ 9361 9362 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */ 9363 9364 static unsigned long int exit_handler(void) 9365 { 9366 short iosb[4]; 9367 9368 if (0 == child_st[0]) 9369 { 9370 #ifdef ARGPROC_DEBUG 9371 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n"); 9372 #endif 9373 fflush(stdout); /* Have to flush pipe for binary data to */ 9374 /* terminate properly -- <tp@mccall.com> */ 9375 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0); 9376 sys$dassgn(child_chan); 9377 fclose(stdout); 9378 sys$synch(0, child_st); 9379 } 9380 return(1); 9381 } 9382 9383 static void sig_child(int chan) 9384 { 9385 #ifdef ARGPROC_DEBUG 9386 PerlIO_printf(Perl_debug_log, "Child Completion AST\n"); 9387 #endif 9388 if (child_st[0] == 0) 9389 child_st[0] = 1; 9390 } 9391 9392 static struct exit_control_block exit_block = 9393 { 9394 0, 9395 exit_handler, 9396 1, 9397 &exit_block.exit_status, 9398 0 9399 }; 9400 9401 static void 9402 pipe_and_fork(pTHX_ char **cmargv) 9403 { 9404 PerlIO *fp; 9405 struct dsc$descriptor_s *vmscmd; 9406 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q; 9407 int sts, j, l, ismcr, quote, tquote = 0; 9408 9409 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd); 9410 vms_execfree(vmscmd); 9411 9412 j = l = 0; 9413 p = subcmd; 9414 q = cmargv[0]; 9415 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C' 9416 && toupper(*(q+2)) == 'R' && !*(q+3); 9417 9418 while (q && l < MAX_DCL_LINE_LENGTH) { 9419 if (!*q) { 9420 if (j > 0 && quote) { 9421 *p++ = '"'; 9422 l++; 9423 } 9424 q = cmargv[++j]; 9425 if (q) { 9426 if (ismcr && j > 1) quote = 1; 9427 tquote = (strchr(q,' ')) != NULL || *q == '\0'; 9428 *p++ = ' '; 9429 l++; 9430 if (quote || tquote) { 9431 *p++ = '"'; 9432 l++; 9433 } 9434 } 9435 } else { 9436 if ((quote||tquote) && *q == '"') { 9437 *p++ = '"'; 9438 l++; 9439 } 9440 *p++ = *q++; 9441 l++; 9442 } 9443 } 9444 *p = '\0'; 9445 9446 fp = safe_popen(aTHX_ subcmd,"wbF",&sts); 9447 if (fp == NULL) { 9448 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts); 9449 } 9450 } 9451 9452 static int background_process(pTHX_ int argc, char **argv) 9453 { 9454 char command[MAX_DCL_SYMBOL + 1] = "$"; 9455 $DESCRIPTOR(value, ""); 9456 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND"); 9457 static $DESCRIPTOR(null, "NLA0:"); 9458 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID"); 9459 char pidstring[80]; 9460 $DESCRIPTOR(pidstr, ""); 9461 int pid; 9462 unsigned long int flags = 17, one = 1, retsts; 9463 int len; 9464 9465 len = my_strlcat(command, argv[0], sizeof(command)); 9466 while (--argc && (len < MAX_DCL_SYMBOL)) 9467 { 9468 my_strlcat(command, " \"", sizeof(command)); 9469 my_strlcat(command, *(++argv), sizeof(command)); 9470 len = my_strlcat(command, "\"", sizeof(command)); 9471 } 9472 value.dsc$a_pointer = command; 9473 value.dsc$w_length = strlen(value.dsc$a_pointer); 9474 _ckvmssts_noperl(lib$set_symbol(&cmd, &value)); 9475 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid); 9476 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */ 9477 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid)); 9478 } 9479 else { 9480 _ckvmssts_noperl(retsts); 9481 } 9482 #ifdef ARGPROC_DEBUG 9483 PerlIO_printf(Perl_debug_log, "%s\n", command); 9484 #endif 9485 sprintf(pidstring, "%08X", pid); 9486 PerlIO_printf(Perl_debug_log, "%s\n", pidstring); 9487 pidstr.dsc$a_pointer = pidstring; 9488 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer); 9489 lib$set_symbol(&pidsymbol, &pidstr); 9490 return(SS$_NORMAL); 9491 } 9492 /*}}}*/ 9493 /***** End of code taken from Mark Pizzolato's argproc.c package *****/ 9494 9495 9496 /* OS-specific initialization at image activation (not thread startup) */ 9497 /* Older VAXC header files lack these constants */ 9498 #ifndef JPI$_RIGHTS_SIZE 9499 # define JPI$_RIGHTS_SIZE 817 9500 #endif 9501 #ifndef KGB$M_SUBSYSTEM 9502 # define KGB$M_SUBSYSTEM 0x8 9503 #endif 9504 9505 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */ 9506 9507 /*{{{void vms_image_init(int *, char ***)*/ 9508 void 9509 vms_image_init(int *argcp, char ***argvp) 9510 { 9511 int status; 9512 char eqv[LNM$C_NAMLENGTH+1] = ""; 9513 unsigned int len, tabct = 8, tabidx = 0; 9514 unsigned long int *mask, iosb[2], i, rlst[128], rsz; 9515 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)]; 9516 unsigned short int dummy, rlen; 9517 struct dsc$descriptor_s **tabvec; 9518 #if defined(PERL_IMPLICIT_CONTEXT) 9519 pTHX = NULL; 9520 #endif 9521 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy}, 9522 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen}, 9523 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy}, 9524 { 0, 0, 0, 0} }; 9525 9526 #ifdef KILL_BY_SIGPRC 9527 Perl_csighandler_init(); 9528 #endif 9529 9530 #if __CRTL_VER >= 70300000 && !defined(__VAX) 9531 /* This was moved from the pre-image init handler because on threaded */ 9532 /* Perl it was always returning 0 for the default value. */ 9533 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH); 9534 if (status > 0) { 9535 int s; 9536 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT"); 9537 if (s > 0) { 9538 int initial; 9539 initial = decc$feature_get_value(s, 4); 9540 if (initial > 0) { 9541 /* initial is: 0 if nothing has set the feature */ 9542 /* -1 if initialized to default */ 9543 /* 1 if set by logical name */ 9544 /* 2 if set by decc$feature_set_value */ 9545 decc_disable_posix_root = decc$feature_get_value(s, 1); 9546 9547 /* If the value is not valid, force the feature off */ 9548 if (decc_disable_posix_root < 0) { 9549 decc$feature_set_value(s, 1, 1); 9550 decc_disable_posix_root = 1; 9551 } 9552 } 9553 else { 9554 /* Nothing has asked for it explicitly, so use our own default. */ 9555 decc_disable_posix_root = 1; 9556 decc$feature_set_value(s, 1, 1); 9557 } 9558 } 9559 } 9560 #endif 9561 9562 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL)); 9563 _ckvmssts_noperl(iosb[0]); 9564 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) { 9565 if (iprv[i]) { /* Running image installed with privs? */ 9566 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */ 9567 will_taint = TRUE; 9568 break; 9569 } 9570 } 9571 /* Rights identifiers might trigger tainting as well. */ 9572 if (!will_taint && (rlen || rsz)) { 9573 while (rlen < rsz) { 9574 /* We didn't get all the identifiers on the first pass. Allocate a 9575 * buffer much larger than $GETJPI wants (rsz is size in bytes that 9576 * were needed to hold all identifiers at time of last call; we'll 9577 * allocate that many unsigned long ints), and go back and get 'em. 9578 * If it gave us less than it wanted to despite ample buffer space, 9579 * something's broken. Is your system missing a system identifier? 9580 */ 9581 if (rsz <= jpilist[1].buflen) { 9582 /* Perl_croak accvios when used this early in startup. */ 9583 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s", 9584 rsz, (unsigned long) jpilist[1].buflen, 9585 "Check your rights database for corruption.\n"); 9586 exit(SS$_ABORT); 9587 } 9588 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr); 9589 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int)); 9590 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9591 jpilist[1].buflen = rsz * sizeof(unsigned long int); 9592 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL)); 9593 _ckvmssts_noperl(iosb[0]); 9594 } 9595 mask = (unsigned long int *)jpilist[1].bufadr; 9596 /* Check attribute flags for each identifier (2nd longword); protected 9597 * subsystem identifiers trigger tainting. 9598 */ 9599 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) { 9600 if (mask[i] & KGB$M_SUBSYSTEM) { 9601 will_taint = TRUE; 9602 break; 9603 } 9604 } 9605 if (mask != rlst) PerlMem_free(mask); 9606 } 9607 9608 /* When Perl is in decc_filename_unix_report mode and is run from a concealed 9609 * logical, some versions of the CRTL will add a phanthom /000000/ 9610 * directory. This needs to be removed. 9611 */ 9612 if (decc_filename_unix_report) { 9613 char * zeros; 9614 int ulen; 9615 ulen = strlen(argvp[0][0]); 9616 if (ulen > 7) { 9617 zeros = strstr(argvp[0][0], "/000000/"); 9618 if (zeros != NULL) { 9619 int mlen; 9620 mlen = ulen - (zeros - argvp[0][0]) - 7; 9621 memmove(zeros, &zeros[7], mlen); 9622 ulen = ulen - 7; 9623 argvp[0][0][ulen] = '\0'; 9624 } 9625 } 9626 /* It also may have a trailing dot that needs to be removed otherwise 9627 * it will be converted to VMS mode incorrectly. 9628 */ 9629 ulen--; 9630 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype)) 9631 argvp[0][0][ulen] = '\0'; 9632 } 9633 9634 /* We need to use this hack to tell Perl it should run with tainting, 9635 * since its tainting flag may be part of the PL_curinterp struct, which 9636 * hasn't been allocated when vms_image_init() is called. 9637 */ 9638 if (will_taint) { 9639 char **newargv, **oldargv; 9640 oldargv = *argvp; 9641 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *)); 9642 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9643 newargv[0] = oldargv[0]; 9644 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char)); 9645 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9646 strcpy(newargv[1], "-T"); 9647 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **); 9648 (*argcp)++; 9649 newargv[*argcp] = NULL; 9650 /* We orphan the old argv, since we don't know where it's come from, 9651 * so we don't know how to free it. 9652 */ 9653 *argvp = newargv; 9654 } 9655 else { /* Did user explicitly request tainting? */ 9656 int i; 9657 char *cp, **av = *argvp; 9658 for (i = 1; i < *argcp; i++) { 9659 if (*av[i] != '-') break; 9660 for (cp = av[i]+1; *cp; cp++) { 9661 if (*cp == 'T') { will_taint = 1; break; } 9662 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' || 9663 strchr("DFIiMmx",*cp)) break; 9664 } 9665 if (will_taint) break; 9666 } 9667 } 9668 9669 for (tabidx = 0; 9670 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx); 9671 tabidx++) { 9672 if (!tabidx) { 9673 tabvec = (struct dsc$descriptor_s **) 9674 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *)); 9675 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9676 } 9677 else if (tabidx >= tabct) { 9678 tabct += 8; 9679 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *)); 9680 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9681 } 9682 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 9683 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9684 tabvec[tabidx]->dsc$w_length = len; 9685 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T; 9686 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S; 9687 tabvec[tabidx]->dsc$a_pointer = PerlMem_malloc(len + 1); 9688 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9689 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1); 9690 } 9691 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; } 9692 9693 getredirection(argcp,argvp); 9694 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) ) 9695 { 9696 # include <reentrancy.h> 9697 decc$set_reentrancy(C$C_MULTITHREAD); 9698 } 9699 #endif 9700 return; 9701 } 9702 /*}}}*/ 9703 9704 9705 /* trim_unixpath() 9706 * Trim Unix-style prefix off filespec, so it looks like what a shell 9707 * glob expansion would return (i.e. from specified prefix on, not 9708 * full path). Note that returned filespec is Unix-style, regardless 9709 * of whether input filespec was VMS-style or Unix-style. 9710 * 9711 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to 9712 * determine prefix (both may be in VMS or Unix syntax). opts is a bit 9713 * vector of options; at present, only bit 0 is used, and if set tells 9714 * trim unixpath to try the current default directory as a prefix when 9715 * presented with a possibly ambiguous ... wildcard. 9716 * 9717 * Returns !=0 on success, with trimmed filespec replacing contents of 9718 * fspec, and 0 on failure, with contents of fpsec unchanged. 9719 */ 9720 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/ 9721 int 9722 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts) 9723 { 9724 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2; 9725 int tmplen, reslen = 0, dirs = 0; 9726 9727 if (!wildspec || !fspec) return 0; 9728 9729 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS); 9730 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9731 tplate = unixwild; 9732 if (strpbrk(wildspec,"]>:") != NULL) { 9733 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) { 9734 PerlMem_free(unixwild); 9735 return 0; 9736 } 9737 } 9738 else { 9739 my_strlcpy(unixwild, wildspec, VMS_MAXRSS); 9740 } 9741 unixified = (char *)PerlMem_malloc(VMS_MAXRSS); 9742 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9743 if (strpbrk(fspec,"]>:") != NULL) { 9744 if (int_tounixspec(fspec, unixified, NULL) == NULL) { 9745 PerlMem_free(unixwild); 9746 PerlMem_free(unixified); 9747 return 0; 9748 } 9749 else base = unixified; 9750 /* reslen != 0 ==> we had to unixify resultant filespec, so we must 9751 * check to see that final result fits into (isn't longer than) fspec */ 9752 reslen = strlen(fspec); 9753 } 9754 else base = fspec; 9755 9756 /* No prefix or absolute path on wildcard, so nothing to remove */ 9757 if (!*tplate || *tplate == '/') { 9758 PerlMem_free(unixwild); 9759 if (base == fspec) { 9760 PerlMem_free(unixified); 9761 return 1; 9762 } 9763 tmplen = strlen(unixified); 9764 if (tmplen > reslen) { 9765 PerlMem_free(unixified); 9766 return 0; /* not enough space */ 9767 } 9768 /* Copy unixified resultant, including trailing NUL */ 9769 memmove(fspec,unixified,tmplen+1); 9770 PerlMem_free(unixified); 9771 return 1; 9772 } 9773 9774 for (end = base; *end; end++) ; /* Find end of resultant filespec */ 9775 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */ 9776 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++; 9777 for (cp1 = end ;cp1 >= base; cp1--) 9778 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */ 9779 { cp1++; break; } 9780 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1); 9781 PerlMem_free(unixified); 9782 PerlMem_free(unixwild); 9783 return 1; 9784 } 9785 else { 9786 char *tpl, *lcres; 9787 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1; 9788 int ells = 1, totells, segdirs, match; 9789 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, 9790 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 9791 9792 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;} 9793 totells = ells; 9794 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++; 9795 tpl = (char *)PerlMem_malloc(VMS_MAXRSS); 9796 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9797 if (ellipsis == tplate && opts & 1) { 9798 /* Template begins with an ellipsis. Since we can't tell how many 9799 * directory names at the front of the resultant to keep for an 9800 * arbitrary starting point, we arbitrarily choose the current 9801 * default directory as a starting point. If it's there as a prefix, 9802 * clip it off. If not, fall through and act as if the leading 9803 * ellipsis weren't there (i.e. return shortest possible path that 9804 * could match template). 9805 */ 9806 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) { 9807 PerlMem_free(tpl); 9808 PerlMem_free(unixified); 9809 PerlMem_free(unixwild); 9810 return 0; 9811 } 9812 if (!decc_efs_case_preserve) { 9813 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9814 if (_tolower(*cp1) != _tolower(*cp2)) break; 9815 } 9816 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9817 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--; 9818 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) { 9819 memmove(fspec,cp2+1,end - cp2); 9820 PerlMem_free(tpl); 9821 PerlMem_free(unixified); 9822 PerlMem_free(unixwild); 9823 return 1; 9824 } 9825 } 9826 /* First off, back up over constant elements at end of path */ 9827 if (dirs) { 9828 for (front = end ; front >= base; front--) 9829 if (*front == '/' && !dirs--) { front++; break; } 9830 } 9831 lcres = (char *)PerlMem_malloc(VMS_MAXRSS); 9832 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9833 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1); 9834 cp1++,cp2++) { 9835 if (!decc_efs_case_preserve) { 9836 *cp2 = _tolower(*cp1); /* Make lc copy for match */ 9837 } 9838 else { 9839 *cp2 = *cp1; 9840 } 9841 } 9842 if (cp1 != '\0') { 9843 PerlMem_free(tpl); 9844 PerlMem_free(unixified); 9845 PerlMem_free(unixwild); 9846 PerlMem_free(lcres); 9847 return 0; /* Path too long. */ 9848 } 9849 lcend = cp2; 9850 *cp2 = '\0'; /* Pick up with memcpy later */ 9851 lcfront = lcres + (front - base); 9852 /* Now skip over each ellipsis and try to match the path in front of it. */ 9853 while (ells--) { 9854 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--) 9855 if (*(cp1) == '.' && *(cp1+1) == '.' && 9856 *(cp1+2) == '.' && *(cp1+3) == '/' ) break; 9857 if (cp1 < tplate) break; /* template started with an ellipsis */ 9858 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */ 9859 ellipsis = cp1; continue; 9860 } 9861 wilddsc.dsc$a_pointer = tpl; 9862 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1; 9863 nextell = cp1; 9864 for (segdirs = 0, cp2 = tpl; 9865 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1); 9866 cp1++, cp2++) { 9867 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */ 9868 else { 9869 if (!decc_efs_case_preserve) { 9870 *cp2 = _tolower(*cp1); /* else lowercase for match */ 9871 } 9872 else { 9873 *cp2 = *cp1; /* else preserve case for match */ 9874 } 9875 } 9876 if (*cp2 == '/') segdirs++; 9877 } 9878 if (cp1 != ellipsis - 1) { 9879 PerlMem_free(tpl); 9880 PerlMem_free(unixified); 9881 PerlMem_free(unixwild); 9882 PerlMem_free(lcres); 9883 return 0; /* Path too long */ 9884 } 9885 /* Back up at least as many dirs as in template before matching */ 9886 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--) 9887 if (*cp1 == '/' && !segdirs--) { cp1++; break; } 9888 for (match = 0; cp1 > lcres;) { 9889 resdsc.dsc$a_pointer = cp1; 9890 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) { 9891 match++; 9892 if (match == 1) lcfront = cp1; 9893 } 9894 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; } 9895 } 9896 if (!match) { 9897 PerlMem_free(tpl); 9898 PerlMem_free(unixified); 9899 PerlMem_free(unixwild); 9900 PerlMem_free(lcres); 9901 return 0; /* Can't find prefix ??? */ 9902 } 9903 if (match > 1 && opts & 1) { 9904 /* This ... wildcard could cover more than one set of dirs (i.e. 9905 * a set of similar dir names is repeated). If the template 9906 * contains more than 1 ..., upstream elements could resolve the 9907 * ambiguity, but it's not worth a full backtracking setup here. 9908 * As a quick heuristic, clip off the current default directory 9909 * if it's present to find the trimmed spec, else use the 9910 * shortest string that this ... could cover. 9911 */ 9912 char def[NAM$C_MAXRSS+1], *st; 9913 9914 if (getcwd(def, sizeof def,0) == NULL) { 9915 PerlMem_free(unixified); 9916 PerlMem_free(unixwild); 9917 PerlMem_free(lcres); 9918 PerlMem_free(tpl); 9919 return 0; 9920 } 9921 if (!decc_efs_case_preserve) { 9922 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9923 if (_tolower(*cp1) != _tolower(*cp2)) break; 9924 } 9925 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9926 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--; 9927 if (*cp1 == '\0' && *cp2 == '/') { 9928 memmove(fspec,cp2+1,end - cp2); 9929 PerlMem_free(tpl); 9930 PerlMem_free(unixified); 9931 PerlMem_free(unixwild); 9932 PerlMem_free(lcres); 9933 return 1; 9934 } 9935 /* Nope -- stick with lcfront from above and keep going. */ 9936 } 9937 } 9938 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1); 9939 PerlMem_free(tpl); 9940 PerlMem_free(unixified); 9941 PerlMem_free(unixwild); 9942 PerlMem_free(lcres); 9943 return 1; 9944 } 9945 9946 } /* end of trim_unixpath() */ 9947 /*}}}*/ 9948 9949 9950 /* 9951 * VMS readdir() routines. 9952 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990. 9953 * 9954 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu 9955 * Minor modifications to original routines. 9956 */ 9957 9958 /* readdir may have been redefined by reentr.h, so make sure we get 9959 * the local version for what we do here. 9960 */ 9961 #ifdef readdir 9962 # undef readdir 9963 #endif 9964 #if !defined(PERL_IMPLICIT_CONTEXT) 9965 # define readdir Perl_readdir 9966 #else 9967 # define readdir(a) Perl_readdir(aTHX_ a) 9968 #endif 9969 9970 /* Number of elements in vms_versions array */ 9971 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0]) 9972 9973 /* 9974 * Open a directory, return a handle for later use. 9975 */ 9976 /*{{{ DIR *opendir(char*name) */ 9977 DIR * 9978 Perl_opendir(pTHX_ const char *name) 9979 { 9980 DIR *dd; 9981 char *dir; 9982 Stat_t sb; 9983 9984 Newx(dir, VMS_MAXRSS, char); 9985 if (int_tovmspath(name, dir, NULL) == NULL) { 9986 Safefree(dir); 9987 return NULL; 9988 } 9989 /* Check access before stat; otherwise stat does not 9990 * accurately report whether it's a directory. 9991 */ 9992 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */ 9993 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) { 9994 /* cando_by_name has already set errno */ 9995 Safefree(dir); 9996 return NULL; 9997 } 9998 if (flex_stat(dir,&sb) == -1) return NULL; 9999 if (!S_ISDIR(sb.st_mode)) { 10000 Safefree(dir); 10001 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR); 10002 return NULL; 10003 } 10004 /* Get memory for the handle, and the pattern. */ 10005 Newx(dd,1,DIR); 10006 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char); 10007 10008 /* Fill in the fields; mainly playing with the descriptor. */ 10009 sprintf(dd->pattern, "%s*.*",dir); 10010 Safefree(dir); 10011 dd->context = 0; 10012 dd->count = 0; 10013 dd->flags = 0; 10014 /* By saying we want the result of readdir() in unix format, we are really 10015 * saying we want all the escapes removed, translating characters that 10016 * must be escaped in a VMS-format name to their unescaped form, which is 10017 * presumably allowed in a Unix-format name. 10018 */ 10019 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0; 10020 dd->pat.dsc$a_pointer = dd->pattern; 10021 dd->pat.dsc$w_length = strlen(dd->pattern); 10022 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T; 10023 dd->pat.dsc$b_class = DSC$K_CLASS_S; 10024 #if defined(USE_ITHREADS) 10025 Newx(dd->mutex,1,perl_mutex); 10026 MUTEX_INIT( (perl_mutex *) dd->mutex ); 10027 #else 10028 dd->mutex = NULL; 10029 #endif 10030 10031 return dd; 10032 } /* end of opendir() */ 10033 /*}}}*/ 10034 10035 /* 10036 * Set the flag to indicate we want versions or not. 10037 */ 10038 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/ 10039 void 10040 vmsreaddirversions(DIR *dd, int flag) 10041 { 10042 if (flag) 10043 dd->flags |= PERL_VMSDIR_M_VERSIONS; 10044 else 10045 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 10046 } 10047 /*}}}*/ 10048 10049 /* 10050 * Free up an opened directory. 10051 */ 10052 /*{{{ void closedir(DIR *dd)*/ 10053 void 10054 Perl_closedir(DIR *dd) 10055 { 10056 int sts; 10057 10058 sts = lib$find_file_end(&dd->context); 10059 Safefree(dd->pattern); 10060 #if defined(USE_ITHREADS) 10061 MUTEX_DESTROY( (perl_mutex *) dd->mutex ); 10062 Safefree(dd->mutex); 10063 #endif 10064 Safefree(dd); 10065 } 10066 /*}}}*/ 10067 10068 /* 10069 * Collect all the version numbers for the current file. 10070 */ 10071 static void 10072 collectversions(pTHX_ DIR *dd) 10073 { 10074 struct dsc$descriptor_s pat; 10075 struct dsc$descriptor_s res; 10076 struct dirent *e; 10077 char *p, *text, *buff; 10078 int i; 10079 unsigned long context, tmpsts; 10080 10081 /* Convenient shorthand. */ 10082 e = &dd->entry; 10083 10084 /* Add the version wildcard, ignoring the "*.*" put on before */ 10085 i = strlen(dd->pattern); 10086 Newx(text,i + e->d_namlen + 3,char); 10087 my_strlcpy(text, dd->pattern, i + 1); 10088 sprintf(&text[i - 3], "%s;*", e->d_name); 10089 10090 /* Set up the pattern descriptor. */ 10091 pat.dsc$a_pointer = text; 10092 pat.dsc$w_length = i + e->d_namlen - 1; 10093 pat.dsc$b_dtype = DSC$K_DTYPE_T; 10094 pat.dsc$b_class = DSC$K_CLASS_S; 10095 10096 /* Set up result descriptor. */ 10097 Newx(buff, VMS_MAXRSS, char); 10098 res.dsc$a_pointer = buff; 10099 res.dsc$w_length = VMS_MAXRSS - 1; 10100 res.dsc$b_dtype = DSC$K_DTYPE_T; 10101 res.dsc$b_class = DSC$K_CLASS_S; 10102 10103 /* Read files, collecting versions. */ 10104 for (context = 0, e->vms_verscount = 0; 10105 e->vms_verscount < VERSIZE(e); 10106 e->vms_verscount++) { 10107 unsigned long rsts; 10108 unsigned long flags = 0; 10109 10110 #ifdef VMS_LONGNAME_SUPPORT 10111 flags = LIB$M_FIL_LONG_NAMES; 10112 #endif 10113 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags); 10114 if (tmpsts == RMS$_NMF || context == 0) break; 10115 _ckvmssts(tmpsts); 10116 buff[VMS_MAXRSS - 1] = '\0'; 10117 if ((p = strchr(buff, ';'))) 10118 e->vms_versions[e->vms_verscount] = atoi(p + 1); 10119 else 10120 e->vms_versions[e->vms_verscount] = -1; 10121 } 10122 10123 _ckvmssts(lib$find_file_end(&context)); 10124 Safefree(text); 10125 Safefree(buff); 10126 10127 } /* end of collectversions() */ 10128 10129 /* 10130 * Read the next entry from the directory. 10131 */ 10132 /*{{{ struct dirent *readdir(DIR *dd)*/ 10133 struct dirent * 10134 Perl_readdir(pTHX_ DIR *dd) 10135 { 10136 struct dsc$descriptor_s res; 10137 char *p, *buff; 10138 unsigned long int tmpsts; 10139 unsigned long rsts; 10140 unsigned long flags = 0; 10141 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 10142 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 10143 10144 /* Set up result descriptor, and get next file. */ 10145 Newx(buff, VMS_MAXRSS, char); 10146 res.dsc$a_pointer = buff; 10147 res.dsc$w_length = VMS_MAXRSS - 1; 10148 res.dsc$b_dtype = DSC$K_DTYPE_T; 10149 res.dsc$b_class = DSC$K_CLASS_S; 10150 10151 #ifdef VMS_LONGNAME_SUPPORT 10152 flags = LIB$M_FIL_LONG_NAMES; 10153 #endif 10154 10155 tmpsts = lib$find_file 10156 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags); 10157 if (dd->context == 0) 10158 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */ 10159 10160 if (!(tmpsts & 1)) { 10161 switch (tmpsts) { 10162 case RMS$_NMF: 10163 break; /* no more files considered success */ 10164 case RMS$_PRV: 10165 SETERRNO(EACCES, tmpsts); break; 10166 case RMS$_DEV: 10167 SETERRNO(ENODEV, tmpsts); break; 10168 case RMS$_DIR: 10169 SETERRNO(ENOTDIR, tmpsts); break; 10170 case RMS$_FNF: case RMS$_DNF: 10171 SETERRNO(ENOENT, tmpsts); break; 10172 default: 10173 SETERRNO(EVMSERR, tmpsts); 10174 } 10175 Safefree(buff); 10176 return NULL; 10177 } 10178 dd->count++; 10179 /* Force the buffer to end with a NUL, and downcase name to match C convention. */ 10180 buff[res.dsc$w_length] = '\0'; 10181 p = buff + res.dsc$w_length; 10182 while (--p >= buff) if (!isspace(*p)) break; 10183 *p = '\0'; 10184 if (!decc_efs_case_preserve) { 10185 for (p = buff; *p; p++) *p = _tolower(*p); 10186 } 10187 10188 /* Skip any directory component and just copy the name. */ 10189 sts = vms_split_path 10190 (buff, 10191 &v_spec, 10192 &v_len, 10193 &r_spec, 10194 &r_len, 10195 &d_spec, 10196 &d_len, 10197 &n_spec, 10198 &n_len, 10199 &e_spec, 10200 &e_len, 10201 &vs_spec, 10202 &vs_len); 10203 10204 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 10205 10206 /* In Unix report mode, remove the ".dir;1" from the name */ 10207 /* if it is a real directory. */ 10208 if (decc_filename_unix_report && decc_efs_charset) { 10209 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 10210 Stat_t statbuf; 10211 int ret_sts; 10212 10213 ret_sts = flex_lstat(buff, &statbuf); 10214 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) { 10215 e_len = 0; 10216 e_spec[0] = 0; 10217 } 10218 } 10219 } 10220 10221 /* Drop NULL extensions on UNIX file specification */ 10222 if ((e_len == 1) && decc_readdir_dropdotnotype) { 10223 e_len = 0; 10224 e_spec[0] = '\0'; 10225 } 10226 } 10227 10228 memcpy(dd->entry.d_name, n_spec, n_len + e_len); 10229 dd->entry.d_name[n_len + e_len] = '\0'; 10230 dd->entry.d_namlen = n_len + e_len; 10231 10232 /* Convert the filename to UNIX format if needed */ 10233 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 10234 10235 /* Translate the encoded characters. */ 10236 /* Fixme: Unicode handling could result in embedded 0 characters */ 10237 if (strchr(dd->entry.d_name, '^') != NULL) { 10238 char new_name[256]; 10239 char * q; 10240 p = dd->entry.d_name; 10241 q = new_name; 10242 while (*p != 0) { 10243 int inchars_read, outchars_added; 10244 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added); 10245 p += inchars_read; 10246 q += outchars_added; 10247 /* fix-me */ 10248 /* if outchars_added > 1, then this is a wide file specification */ 10249 /* Wide file specifications need to be passed in Perl */ 10250 /* counted strings apparently with a Unicode flag */ 10251 } 10252 *q = 0; 10253 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name)); 10254 } 10255 } 10256 10257 dd->entry.vms_verscount = 0; 10258 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd); 10259 Safefree(buff); 10260 return &dd->entry; 10261 10262 } /* end of readdir() */ 10263 /*}}}*/ 10264 10265 /* 10266 * Read the next entry from the directory -- thread-safe version. 10267 */ 10268 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/ 10269 int 10270 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result) 10271 { 10272 int retval; 10273 10274 MUTEX_LOCK( (perl_mutex *) dd->mutex ); 10275 10276 entry = readdir(dd); 10277 *result = entry; 10278 retval = ( *result == NULL ? errno : 0 ); 10279 10280 MUTEX_UNLOCK( (perl_mutex *) dd->mutex ); 10281 10282 return retval; 10283 10284 } /* end of readdir_r() */ 10285 /*}}}*/ 10286 10287 /* 10288 * Return something that can be used in a seekdir later. 10289 */ 10290 /*{{{ long telldir(DIR *dd)*/ 10291 long 10292 Perl_telldir(DIR *dd) 10293 { 10294 return dd->count; 10295 } 10296 /*}}}*/ 10297 10298 /* 10299 * Return to a spot where we used to be. Brute force. 10300 */ 10301 /*{{{ void seekdir(DIR *dd,long count)*/ 10302 void 10303 Perl_seekdir(pTHX_ DIR *dd, long count) 10304 { 10305 int old_flags; 10306 10307 /* If we haven't done anything yet... */ 10308 if (dd->count == 0) 10309 return; 10310 10311 /* Remember some state, and clear it. */ 10312 old_flags = dd->flags; 10313 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 10314 _ckvmssts(lib$find_file_end(&dd->context)); 10315 dd->context = 0; 10316 10317 /* The increment is in readdir(). */ 10318 for (dd->count = 0; dd->count < count; ) 10319 readdir(dd); 10320 10321 dd->flags = old_flags; 10322 10323 } /* end of seekdir() */ 10324 /*}}}*/ 10325 10326 /* VMS subprocess management 10327 * 10328 * my_vfork() - just a vfork(), after setting a flag to record that 10329 * the current script is trying a Unix-style fork/exec. 10330 * 10331 * vms_do_aexec() and vms_do_exec() are called in response to the 10332 * perl 'exec' function. If this follows a vfork call, then they 10333 * call out the regular perl routines in doio.c which do an 10334 * execvp (for those who really want to try this under VMS). 10335 * Otherwise, they do exactly what the perl docs say exec should 10336 * do - terminate the current script and invoke a new command 10337 * (See below for notes on command syntax.) 10338 * 10339 * do_aspawn() and do_spawn() implement the VMS side of the perl 10340 * 'system' function. 10341 * 10342 * Note on command arguments to perl 'exec' and 'system': When handled 10343 * in 'VMSish fashion' (i.e. not after a call to vfork) The args 10344 * are concatenated to form a DCL command string. If the first non-numeric 10345 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such), 10346 * the command string is handed off to DCL directly. Otherwise, 10347 * the first token of the command is taken as the filespec of an image 10348 * to run. The filespec is expanded using a default type of '.EXE' and 10349 * the process defaults for device, directory, etc., and if found, the resultant 10350 * filespec is invoked using the DCL verb 'MCR', and passed the rest of 10351 * the command string as parameters. This is perhaps a bit complicated, 10352 * but I hope it will form a happy medium between what VMS folks expect 10353 * from lib$spawn and what Unix folks expect from exec. 10354 */ 10355 10356 static int vfork_called; 10357 10358 /*{{{int my_vfork(void)*/ 10359 int 10360 my_vfork(void) 10361 { 10362 vfork_called++; 10363 return vfork(); 10364 } 10365 /*}}}*/ 10366 10367 10368 static void 10369 vms_execfree(struct dsc$descriptor_s *vmscmd) 10370 { 10371 if (vmscmd) { 10372 if (vmscmd->dsc$a_pointer) { 10373 PerlMem_free(vmscmd->dsc$a_pointer); 10374 } 10375 PerlMem_free(vmscmd); 10376 } 10377 } 10378 10379 static char * 10380 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp) 10381 { 10382 char *junk, *tmps = NULL; 10383 size_t cmdlen = 0; 10384 size_t rlen; 10385 SV **idx; 10386 STRLEN n_a; 10387 10388 idx = mark; 10389 if (really) { 10390 tmps = SvPV(really,rlen); 10391 if (*tmps) { 10392 cmdlen += rlen + 1; 10393 idx++; 10394 } 10395 } 10396 10397 for (idx++; idx <= sp; idx++) { 10398 if (*idx) { 10399 junk = SvPVx(*idx,rlen); 10400 cmdlen += rlen ? rlen + 1 : 0; 10401 } 10402 } 10403 Newx(PL_Cmd, cmdlen+1, char); 10404 10405 if (tmps && *tmps) { 10406 my_strlcpy(PL_Cmd, tmps, cmdlen + 1); 10407 mark++; 10408 } 10409 else *PL_Cmd = '\0'; 10410 while (++mark <= sp) { 10411 if (*mark) { 10412 char *s = SvPVx(*mark,n_a); 10413 if (!*s) continue; 10414 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1); 10415 my_strlcat(PL_Cmd, s, cmdlen+1); 10416 } 10417 } 10418 return PL_Cmd; 10419 10420 } /* end of setup_argstr() */ 10421 10422 10423 static unsigned long int 10424 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote, 10425 struct dsc$descriptor_s **pvmscmd) 10426 { 10427 char * vmsspec; 10428 char * resspec; 10429 char image_name[NAM$C_MAXRSS+1]; 10430 char image_argv[NAM$C_MAXRSS+1]; 10431 $DESCRIPTOR(defdsc,".EXE"); 10432 $DESCRIPTOR(defdsc2,"."); 10433 struct dsc$descriptor_s resdsc; 10434 struct dsc$descriptor_s *vmscmd; 10435 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10436 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL; 10437 char *s, *rest, *cp, *wordbreak; 10438 char * cmd; 10439 int cmdlen; 10440 int isdcl; 10441 10442 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 10443 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10444 10445 /* vmsspec is a DCL command buffer, not just a filename */ 10446 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1); 10447 if (vmsspec == NULL) 10448 _ckvmssts_noperl(SS$_INSFMEM); 10449 10450 resspec = (char *)PerlMem_malloc(VMS_MAXRSS); 10451 if (resspec == NULL) 10452 _ckvmssts_noperl(SS$_INSFMEM); 10453 10454 /* Make a copy for modification */ 10455 cmdlen = strlen(incmd); 10456 cmd = (char *)PerlMem_malloc(cmdlen+1); 10457 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10458 my_strlcpy(cmd, incmd, cmdlen + 1); 10459 image_name[0] = 0; 10460 image_argv[0] = 0; 10461 10462 resdsc.dsc$a_pointer = resspec; 10463 resdsc.dsc$b_dtype = DSC$K_DTYPE_T; 10464 resdsc.dsc$b_class = DSC$K_CLASS_S; 10465 resdsc.dsc$w_length = VMS_MAXRSS - 1; 10466 10467 vmscmd->dsc$a_pointer = NULL; 10468 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T; 10469 vmscmd->dsc$b_class = DSC$K_CLASS_S; 10470 vmscmd->dsc$w_length = 0; 10471 if (pvmscmd) *pvmscmd = vmscmd; 10472 10473 if (suggest_quote) *suggest_quote = 0; 10474 10475 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) { 10476 PerlMem_free(cmd); 10477 PerlMem_free(vmsspec); 10478 PerlMem_free(resspec); 10479 return CLI$_BUFOVF; /* continuation lines currently unsupported */ 10480 } 10481 10482 s = cmd; 10483 10484 while (*s && isspace(*s)) s++; 10485 10486 if (*s == '@' || *s == '$') { 10487 vmsspec[0] = *s; rest = s + 1; 10488 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest; 10489 } 10490 else { cp = vmsspec; rest = s; } 10491 10492 /* If the first word is quoted, then we need to unquote it and 10493 * escape spaces within it. We'll expand into the resspec buffer, 10494 * then copy back into the cmd buffer, expanding the latter if 10495 * necessary. 10496 */ 10497 if (*rest == '"') { 10498 char *cp2; 10499 char *r = rest; 10500 bool in_quote = 0; 10501 int clen = cmdlen; 10502 int soff = s - cmd; 10503 10504 for (cp2 = resspec; 10505 *rest && cp2 - resspec < (VMS_MAXRSS - 1); 10506 rest++) { 10507 10508 if (*rest == ' ') { /* Escape ' ' to '^_'. */ 10509 *cp2 = '^'; 10510 *(++cp2) = '_'; 10511 cp2++; 10512 clen++; 10513 } 10514 else if (*rest == '"') { 10515 clen--; 10516 if (in_quote) { /* Must be closing quote. */ 10517 rest++; 10518 break; 10519 } 10520 in_quote = 1; 10521 } 10522 else { 10523 *cp2 = *rest; 10524 cp2++; 10525 } 10526 } 10527 *cp2 = '\0'; 10528 10529 /* Expand the command buffer if necessary. */ 10530 if (clen > cmdlen) { 10531 cmd = (char *)PerlMem_realloc(cmd, clen); 10532 if (cmd == NULL) 10533 _ckvmssts_noperl(SS$_INSFMEM); 10534 /* Where we are may have changed, so recompute offsets */ 10535 r = cmd + (r - s - soff); 10536 rest = cmd + (rest - s - soff); 10537 s = cmd + soff; 10538 } 10539 10540 /* Shift the non-verb portion of the command (if any) up or 10541 * down as necessary. 10542 */ 10543 if (*rest) 10544 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest); 10545 10546 /* Copy the unquoted and escaped command verb into place. */ 10547 memcpy(r, resspec, cp2 - resspec); 10548 cmd[clen] = '\0'; 10549 cmdlen = clen; 10550 rest = r; /* Rewind for subsequent operations. */ 10551 } 10552 10553 if (*rest == '.' || *rest == '/') { 10554 char *cp2; 10555 for (cp2 = resspec; 10556 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1); 10557 rest++, cp2++) *cp2 = *rest; 10558 *cp2 = '\0'; 10559 if (int_tovmsspec(resspec, cp, 0, NULL)) { 10560 s = vmsspec; 10561 10562 /* When a UNIX spec with no file type is translated to VMS, */ 10563 /* A trailing '.' is appended under ODS-5 rules. */ 10564 /* Here we do not want that trailing "." as it prevents */ 10565 /* Looking for a implied ".exe" type. */ 10566 if (decc_efs_charset) { 10567 int i; 10568 i = strlen(vmsspec); 10569 if (vmsspec[i-1] == '.') { 10570 vmsspec[i-1] = '\0'; 10571 } 10572 } 10573 10574 if (*rest) { 10575 for (cp2 = vmsspec + strlen(vmsspec); 10576 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH; 10577 rest++, cp2++) *cp2 = *rest; 10578 *cp2 = '\0'; 10579 } 10580 } 10581 } 10582 /* Intuit whether verb (first word of cmd) is a DCL command: 10583 * - if first nonspace char is '@', it's a DCL indirection 10584 * otherwise 10585 * - if verb contains a filespec separator, it's not a DCL command 10586 * - if it doesn't, caller tells us whether to default to a DCL 10587 * command, or to a local image unless told it's DCL (by leading '$') 10588 */ 10589 if (*s == '@') { 10590 isdcl = 1; 10591 if (suggest_quote) *suggest_quote = 1; 10592 } else { 10593 char *filespec = strpbrk(s,":<[.;"); 10594 rest = wordbreak = strpbrk(s," \"\t/"); 10595 if (!wordbreak) wordbreak = s + strlen(s); 10596 if (*s == '$') check_img = 0; 10597 if (filespec && (filespec < wordbreak)) isdcl = 0; 10598 else isdcl = !check_img; 10599 } 10600 10601 if (!isdcl) { 10602 int rsts; 10603 imgdsc.dsc$a_pointer = s; 10604 imgdsc.dsc$w_length = wordbreak - s; 10605 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 10606 if (!(retsts&1)) { 10607 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10608 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 10609 if (!(retsts & 1) && *s == '$') { 10610 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10611 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--; 10612 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 10613 if (!(retsts&1)) { 10614 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10615 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 10616 } 10617 } 10618 } 10619 _ckvmssts_noperl(lib$find_file_end(&cxt)); 10620 10621 if (retsts & 1) { 10622 FILE *fp; 10623 s = resspec; 10624 while (*s && !isspace(*s)) s++; 10625 *s = '\0'; 10626 10627 /* check that it's really not DCL with no file extension */ 10628 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get"); 10629 if (fp) { 10630 char b[256] = {0,0,0,0}; 10631 read(fileno(fp), b, 256); 10632 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]); 10633 if (isdcl) { 10634 int shebang_len; 10635 10636 /* Check for script */ 10637 shebang_len = 0; 10638 if ((b[0] == '#') && (b[1] == '!')) 10639 shebang_len = 2; 10640 #ifdef ALTERNATE_SHEBANG 10641 else { 10642 shebang_len = strlen(ALTERNATE_SHEBANG); 10643 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) { 10644 char * perlstr; 10645 perlstr = strstr("perl",b); 10646 if (perlstr == NULL) 10647 shebang_len = 0; 10648 } 10649 else 10650 shebang_len = 0; 10651 } 10652 #endif 10653 10654 if (shebang_len > 0) { 10655 int i; 10656 int j; 10657 char tmpspec[NAM$C_MAXRSS + 1]; 10658 10659 i = shebang_len; 10660 /* Image is following after white space */ 10661 /*--------------------------------------*/ 10662 while (isprint(b[i]) && isspace(b[i])) 10663 i++; 10664 10665 j = 0; 10666 while (isprint(b[i]) && !isspace(b[i])) { 10667 tmpspec[j++] = b[i++]; 10668 if (j >= NAM$C_MAXRSS) 10669 break; 10670 } 10671 tmpspec[j] = '\0'; 10672 10673 /* There may be some default parameters to the image */ 10674 /*---------------------------------------------------*/ 10675 j = 0; 10676 while (isprint(b[i])) { 10677 image_argv[j++] = b[i++]; 10678 if (j >= NAM$C_MAXRSS) 10679 break; 10680 } 10681 while ((j > 0) && !isprint(image_argv[j-1])) 10682 j--; 10683 image_argv[j] = 0; 10684 10685 /* It will need to be converted to VMS format and validated */ 10686 if (tmpspec[0] != '\0') { 10687 char * iname; 10688 10689 /* Try to find the exact program requested to be run */ 10690 /*---------------------------------------------------*/ 10691 iname = int_rmsexpand 10692 (tmpspec, image_name, ".exe", 10693 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10694 if (iname != NULL) { 10695 if (cando_by_name_int 10696 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) { 10697 /* MCR prefix needed */ 10698 isdcl = 0; 10699 } 10700 else { 10701 /* Try again with a null type */ 10702 /*----------------------------*/ 10703 iname = int_rmsexpand 10704 (tmpspec, image_name, ".", 10705 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10706 if (iname != NULL) { 10707 if (cando_by_name_int 10708 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) { 10709 /* MCR prefix needed */ 10710 isdcl = 0; 10711 } 10712 } 10713 } 10714 10715 /* Did we find the image to run the script? */ 10716 /*------------------------------------------*/ 10717 if (isdcl) { 10718 char *tchr; 10719 10720 /* Assume DCL or foreign command exists */ 10721 /*--------------------------------------*/ 10722 tchr = strrchr(tmpspec, '/'); 10723 if (tchr != NULL) { 10724 tchr++; 10725 } 10726 else { 10727 tchr = tmpspec; 10728 } 10729 my_strlcpy(image_name, tchr, sizeof(image_name)); 10730 } 10731 } 10732 } 10733 } 10734 } 10735 fclose(fp); 10736 } 10737 if (check_img && isdcl) { 10738 PerlMem_free(cmd); 10739 PerlMem_free(resspec); 10740 PerlMem_free(vmsspec); 10741 return RMS$_FNF; 10742 } 10743 10744 if (cando_by_name(S_IXUSR,0,resspec)) { 10745 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH); 10746 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 10747 if (!isdcl) { 10748 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH); 10749 if (image_name[0] != 0) { 10750 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH); 10751 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10752 } 10753 } else if (image_name[0] != 0) { 10754 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH); 10755 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10756 } else { 10757 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH); 10758 } 10759 if (suggest_quote) *suggest_quote = 1; 10760 10761 /* If there is an image name, use original command */ 10762 if (image_name[0] == 0) 10763 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH); 10764 else { 10765 rest = cmd; 10766 while (*rest && isspace(*rest)) rest++; 10767 } 10768 10769 if (image_argv[0] != 0) { 10770 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH); 10771 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH); 10772 } 10773 if (rest) { 10774 int rest_len; 10775 int vmscmd_len; 10776 10777 rest_len = strlen(rest); 10778 vmscmd_len = strlen(vmscmd->dsc$a_pointer); 10779 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH) 10780 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH); 10781 else 10782 retsts = CLI$_BUFOVF; 10783 } 10784 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer); 10785 PerlMem_free(cmd); 10786 PerlMem_free(vmsspec); 10787 PerlMem_free(resspec); 10788 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10789 } 10790 else 10791 retsts = RMS$_PRV; 10792 } 10793 } 10794 /* It's either a DCL command or we couldn't find a suitable image */ 10795 vmscmd->dsc$w_length = strlen(cmd); 10796 10797 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1); 10798 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1); 10799 10800 PerlMem_free(cmd); 10801 PerlMem_free(resspec); 10802 PerlMem_free(vmsspec); 10803 10804 /* check if it's a symbol (for quoting purposes) */ 10805 if (suggest_quote && !*suggest_quote) { 10806 int iss; 10807 char equiv[LNM$C_NAMLENGTH]; 10808 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10809 eqvdsc.dsc$a_pointer = equiv; 10810 10811 iss = lib$get_symbol(vmscmd,&eqvdsc); 10812 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1; 10813 } 10814 if (!(retsts & 1)) { 10815 /* just hand off status values likely to be due to user error */ 10816 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV || 10817 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN || 10818 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts; 10819 else { _ckvmssts_noperl(retsts); } 10820 } 10821 10822 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10823 10824 } /* end of setup_cmddsc() */ 10825 10826 10827 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */ 10828 bool 10829 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp) 10830 { 10831 bool exec_sts; 10832 char * cmd; 10833 10834 if (sp > mark) { 10835 if (vfork_called) { /* this follows a vfork - act Unixish */ 10836 vfork_called--; 10837 if (vfork_called < 0) { 10838 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10839 vfork_called = 0; 10840 } 10841 else return do_aexec(really,mark,sp); 10842 } 10843 /* no vfork - act VMSish */ 10844 cmd = setup_argstr(aTHX_ really,mark,sp); 10845 exec_sts = vms_do_exec(cmd); 10846 Safefree(cmd); /* Clean up from setup_argstr() */ 10847 return exec_sts; 10848 } 10849 10850 return FALSE; 10851 } /* end of vms_do_aexec() */ 10852 /*}}}*/ 10853 10854 /* {{{bool vms_do_exec(char *cmd) */ 10855 bool 10856 Perl_vms_do_exec(pTHX_ const char *cmd) 10857 { 10858 struct dsc$descriptor_s *vmscmd; 10859 10860 if (vfork_called) { /* this follows a vfork - act Unixish */ 10861 vfork_called--; 10862 if (vfork_called < 0) { 10863 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10864 vfork_called = 0; 10865 } 10866 else return do_exec(cmd); 10867 } 10868 10869 { /* no vfork - act VMSish */ 10870 unsigned long int retsts; 10871 10872 TAINT_ENV(); 10873 TAINT_PROPER("exec"); 10874 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1) 10875 retsts = lib$do_command(vmscmd); 10876 10877 switch (retsts) { 10878 case RMS$_FNF: case RMS$_DNF: 10879 set_errno(ENOENT); break; 10880 case RMS$_DIR: 10881 set_errno(ENOTDIR); break; 10882 case RMS$_DEV: 10883 set_errno(ENODEV); break; 10884 case RMS$_PRV: 10885 set_errno(EACCES); break; 10886 case RMS$_SYN: 10887 set_errno(EINVAL); break; 10888 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 10889 set_errno(E2BIG); break; 10890 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 10891 _ckvmssts_noperl(retsts); /* fall through */ 10892 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 10893 set_errno(EVMSERR); 10894 } 10895 set_vaxc_errno(retsts); 10896 if (ckWARN(WARN_EXEC)) { 10897 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s", 10898 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno)); 10899 } 10900 vms_execfree(vmscmd); 10901 } 10902 10903 return FALSE; 10904 10905 } /* end of vms_do_exec() */ 10906 /*}}}*/ 10907 10908 int do_spawn2(pTHX_ const char *, int); 10909 10910 int 10911 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp) 10912 { 10913 unsigned long int sts; 10914 char * cmd; 10915 int flags = 0; 10916 10917 if (sp > mark) { 10918 10919 /* We'll copy the (undocumented?) Win32 behavior and allow a 10920 * numeric first argument. But the only value we'll support 10921 * through do_aspawn is a value of 1, which means spawn without 10922 * waiting for completion -- other values are ignored. 10923 */ 10924 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) { 10925 ++mark; 10926 flags = SvIVx(*mark); 10927 } 10928 10929 if (flags && flags == 1) /* the Win32 P_NOWAIT value */ 10930 flags = CLI$M_NOWAIT; 10931 else 10932 flags = 0; 10933 10934 cmd = setup_argstr(aTHX_ really, mark, sp); 10935 sts = do_spawn2(aTHX_ cmd, flags); 10936 /* pp_sys will clean up cmd */ 10937 return sts; 10938 } 10939 return SS$_ABORT; 10940 } /* end of do_aspawn() */ 10941 /*}}}*/ 10942 10943 10944 /* {{{int do_spawn(char* cmd) */ 10945 int 10946 Perl_do_spawn(pTHX_ char* cmd) 10947 { 10948 PERL_ARGS_ASSERT_DO_SPAWN; 10949 10950 return do_spawn2(aTHX_ cmd, 0); 10951 } 10952 /*}}}*/ 10953 10954 /* {{{int do_spawn_nowait(char* cmd) */ 10955 int 10956 Perl_do_spawn_nowait(pTHX_ char* cmd) 10957 { 10958 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT; 10959 10960 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT); 10961 } 10962 /*}}}*/ 10963 10964 /* {{{int do_spawn2(char *cmd) */ 10965 int 10966 do_spawn2(pTHX_ const char *cmd, int flags) 10967 { 10968 unsigned long int sts, substs; 10969 10970 /* The caller of this routine expects to Safefree(PL_Cmd) */ 10971 Newx(PL_Cmd,10,char); 10972 10973 TAINT_ENV(); 10974 TAINT_PROPER("spawn"); 10975 if (!cmd || !*cmd) { 10976 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0); 10977 if (!(sts & 1)) { 10978 switch (sts) { 10979 case RMS$_FNF: case RMS$_DNF: 10980 set_errno(ENOENT); break; 10981 case RMS$_DIR: 10982 set_errno(ENOTDIR); break; 10983 case RMS$_DEV: 10984 set_errno(ENODEV); break; 10985 case RMS$_PRV: 10986 set_errno(EACCES); break; 10987 case RMS$_SYN: 10988 set_errno(EINVAL); break; 10989 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 10990 set_errno(E2BIG); break; 10991 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 10992 _ckvmssts_noperl(sts); /* fall through */ 10993 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 10994 set_errno(EVMSERR); 10995 } 10996 set_vaxc_errno(sts); 10997 if (ckWARN(WARN_EXEC)) { 10998 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s", 10999 Strerror(errno)); 11000 } 11001 } 11002 sts = substs; 11003 } 11004 else { 11005 char mode[3]; 11006 PerlIO * fp; 11007 if (flags & CLI$M_NOWAIT) 11008 strcpy(mode, "n"); 11009 else 11010 strcpy(mode, "nW"); 11011 11012 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts); 11013 if (fp != NULL) 11014 my_pclose(fp); 11015 /* sts will be the pid in the nowait case */ 11016 } 11017 return sts; 11018 } /* end of do_spawn2() */ 11019 /*}}}*/ 11020 11021 11022 static unsigned int *sockflags, sockflagsize; 11023 11024 /* 11025 * Shim fdopen to identify sockets for my_fwrite later, since the stdio 11026 * routines found in some versions of the CRTL can't deal with sockets. 11027 * We don't shim the other file open routines since a socket isn't 11028 * likely to be opened by a name. 11029 */ 11030 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/ 11031 FILE *my_fdopen(int fd, const char *mode) 11032 { 11033 FILE *fp = fdopen(fd, mode); 11034 11035 if (fp) { 11036 unsigned int fdoff = fd / sizeof(unsigned int); 11037 Stat_t sbuf; /* native stat; we don't need flex_stat */ 11038 if (!sockflagsize || fdoff > sockflagsize) { 11039 if (sockflags) Renew( sockflags,fdoff+2,unsigned int); 11040 else Newx (sockflags,fdoff+2,unsigned int); 11041 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize); 11042 sockflagsize = fdoff + 2; 11043 } 11044 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode)) 11045 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int)); 11046 } 11047 return fp; 11048 11049 } 11050 /*}}}*/ 11051 11052 11053 /* 11054 * Clear the corresponding bit when the (possibly) socket stream is closed. 11055 * There still a small hole: we miss an implicit close which might occur 11056 * via freopen(). >> Todo 11057 */ 11058 /*{{{ int my_fclose(FILE *fp)*/ 11059 int my_fclose(FILE *fp) { 11060 if (fp) { 11061 unsigned int fd = fileno(fp); 11062 unsigned int fdoff = fd / sizeof(unsigned int); 11063 11064 if (sockflagsize && fdoff < sockflagsize) 11065 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int)); 11066 } 11067 return fclose(fp); 11068 } 11069 /*}}}*/ 11070 11071 11072 /* 11073 * A simple fwrite replacement which outputs itmsz*nitm chars without 11074 * introducing record boundaries every itmsz chars. 11075 * We are using fputs, which depends on a terminating null. We may 11076 * well be writing binary data, so we need to accommodate not only 11077 * data with nulls sprinkled in the middle but also data with no null 11078 * byte at the end. 11079 */ 11080 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/ 11081 int 11082 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest) 11083 { 11084 char *cp, *end, *cpd; 11085 char *data; 11086 unsigned int fd = fileno(dest); 11087 unsigned int fdoff = fd / sizeof(unsigned int); 11088 int retval; 11089 int bufsize = itmsz * nitm + 1; 11090 11091 if (fdoff < sockflagsize && 11092 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) { 11093 if (write(fd, src, itmsz * nitm) == EOF) return EOF; 11094 return nitm; 11095 } 11096 11097 _ckvmssts_noperl(lib$get_vm(&bufsize, &data)); 11098 memcpy( data, src, itmsz*nitm ); 11099 data[itmsz*nitm] = '\0'; 11100 11101 end = data + itmsz * nitm; 11102 retval = (int) nitm; /* on success return # items written */ 11103 11104 cpd = data; 11105 while (cpd <= end) { 11106 for (cp = cpd; cp <= end; cp++) if (!*cp) break; 11107 if (fputs(cpd,dest) == EOF) { retval = EOF; break; } 11108 if (cp < end) 11109 if (fputc('\0',dest) == EOF) { retval = EOF; break; } 11110 cpd = cp + 1; 11111 } 11112 11113 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data)); 11114 return retval; 11115 11116 } /* end of my_fwrite() */ 11117 /*}}}*/ 11118 11119 /*{{{ int my_flush(FILE *fp)*/ 11120 int 11121 Perl_my_flush(pTHX_ FILE *fp) 11122 { 11123 int res; 11124 if ((res = fflush(fp)) == 0 && fp) { 11125 #ifdef VMS_DO_SOCKETS 11126 Stat_t s; 11127 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode)) 11128 #endif 11129 res = fsync(fileno(fp)); 11130 } 11131 /* 11132 * If the flush succeeded but set end-of-file, we need to clear 11133 * the error because our caller may check ferror(). BTW, this 11134 * probably means we just flushed an empty file. 11135 */ 11136 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp); 11137 11138 return res; 11139 } 11140 /*}}}*/ 11141 11142 /* fgetname() is not returning the correct file specifications when 11143 * decc_filename_unix_report mode is active. So we have to have it 11144 * aways return filenames in VMS mode and convert it ourselves. 11145 */ 11146 11147 /*{{{ char * my_fgetname(FILE *fp, buf)*/ 11148 char * 11149 Perl_my_fgetname(FILE *fp, char * buf) { 11150 char * retname; 11151 char * vms_name; 11152 11153 retname = fgetname(fp, buf, 1); 11154 11155 /* If we are in VMS mode, then we are done */ 11156 if (!decc_filename_unix_report || (retname == NULL)) { 11157 return retname; 11158 } 11159 11160 /* Convert this to Unix format */ 11161 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS); 11162 my_strlcpy(vms_name, retname, VMS_MAXRSS); 11163 retname = int_tounixspec(vms_name, buf, NULL); 11164 PerlMem_free(vms_name); 11165 11166 return retname; 11167 } 11168 /*}}}*/ 11169 11170 /* 11171 * Here are replacements for the following Unix routines in the VMS environment: 11172 * getpwuid Get information for a particular UIC or UID 11173 * getpwnam Get information for a named user 11174 * getpwent Get information for each user in the rights database 11175 * setpwent Reset search to the start of the rights database 11176 * endpwent Finish searching for users in the rights database 11177 * 11178 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure 11179 * (defined in pwd.h), which contains the following fields:- 11180 * struct passwd { 11181 * char *pw_name; Username (in lower case) 11182 * char *pw_passwd; Hashed password 11183 * unsigned int pw_uid; UIC 11184 * unsigned int pw_gid; UIC group number 11185 * char *pw_unixdir; Default device/directory (VMS-style) 11186 * char *pw_gecos; Owner name 11187 * char *pw_dir; Default device/directory (Unix-style) 11188 * char *pw_shell; Default CLI name (eg. DCL) 11189 * }; 11190 * If the specified user does not exist, getpwuid and getpwnam return NULL. 11191 * 11192 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid). 11193 * not the UIC member number (eg. what's returned by getuid()), 11194 * getpwuid() can accept either as input (if uid is specified, the caller's 11195 * UIC group is used), though it won't recognise gid=0. 11196 * 11197 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return 11198 * information about other users in your group or in other groups, respectively. 11199 * If the required privilege is not available, then these routines fill only 11200 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty 11201 * string). 11202 * 11203 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995. 11204 */ 11205 11206 /* sizes of various UAF record fields */ 11207 #define UAI$S_USERNAME 12 11208 #define UAI$S_IDENT 31 11209 #define UAI$S_OWNER 31 11210 #define UAI$S_DEFDEV 31 11211 #define UAI$S_DEFDIR 63 11212 #define UAI$S_DEFCLI 31 11213 #define UAI$S_PWD 8 11214 11215 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \ 11216 (uic).uic$v_member != UIC$K_WILD_MEMBER && \ 11217 (uic).uic$v_group != UIC$K_WILD_GROUP) 11218 11219 static char __empty[]= ""; 11220 static struct passwd __passwd_empty= 11221 {(char *) __empty, (char *) __empty, 0, 0, 11222 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty}; 11223 static int contxt= 0; 11224 static struct passwd __pwdcache; 11225 static char __pw_namecache[UAI$S_IDENT+1]; 11226 11227 /* 11228 * This routine does most of the work extracting the user information. 11229 */ 11230 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd) 11231 { 11232 static struct { 11233 unsigned char length; 11234 char pw_gecos[UAI$S_OWNER+1]; 11235 } owner; 11236 static union uicdef uic; 11237 static struct { 11238 unsigned char length; 11239 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1]; 11240 } defdev; 11241 static struct { 11242 unsigned char length; 11243 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1]; 11244 } defdir; 11245 static struct { 11246 unsigned char length; 11247 char pw_shell[UAI$S_DEFCLI+1]; 11248 } defcli; 11249 static char pw_passwd[UAI$S_PWD+1]; 11250 11251 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd; 11252 struct dsc$descriptor_s name_desc; 11253 unsigned long int sts; 11254 11255 static struct itmlst_3 itmlst[]= { 11256 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner}, 11257 {sizeof(uic), UAI$_UIC, &uic, &luic}, 11258 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev}, 11259 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir}, 11260 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli}, 11261 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd}, 11262 {0, 0, NULL, NULL}}; 11263 11264 name_desc.dsc$w_length= strlen(name); 11265 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 11266 name_desc.dsc$b_class= DSC$K_CLASS_S; 11267 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */ 11268 11269 /* Note that sys$getuai returns many fields as counted strings. */ 11270 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0); 11271 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) { 11272 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES); 11273 } 11274 else { _ckvmssts(sts); } 11275 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */ 11276 11277 if ((int) owner.length < lowner) lowner= (int) owner.length; 11278 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length; 11279 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length; 11280 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length; 11281 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir); 11282 owner.pw_gecos[lowner]= '\0'; 11283 defdev.pw_dir[ldefdev+ldefdir]= '\0'; 11284 defcli.pw_shell[ldefcli]= '\0'; 11285 if (valid_uic(uic)) { 11286 pwd->pw_uid= uic.uic$l_uic; 11287 pwd->pw_gid= uic.uic$v_group; 11288 } 11289 else 11290 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\""); 11291 pwd->pw_passwd= pw_passwd; 11292 pwd->pw_gecos= owner.pw_gecos; 11293 pwd->pw_dir= defdev.pw_dir; 11294 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL); 11295 pwd->pw_shell= defcli.pw_shell; 11296 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) { 11297 int ldir; 11298 ldir= strlen(pwd->pw_unixdir) - 1; 11299 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0'; 11300 } 11301 else 11302 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir)); 11303 if (!decc_efs_case_preserve) 11304 __mystrtolower(pwd->pw_unixdir); 11305 return 1; 11306 } 11307 11308 /* 11309 * Get information for a named user. 11310 */ 11311 /*{{{struct passwd *getpwnam(char *name)*/ 11312 struct passwd *Perl_my_getpwnam(pTHX_ const char *name) 11313 { 11314 struct dsc$descriptor_s name_desc; 11315 union uicdef uic; 11316 unsigned long int sts; 11317 11318 __pwdcache = __passwd_empty; 11319 if (!fillpasswd(aTHX_ name, &__pwdcache)) { 11320 /* We still may be able to determine pw_uid and pw_gid */ 11321 name_desc.dsc$w_length= strlen(name); 11322 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 11323 name_desc.dsc$b_class= DSC$K_CLASS_S; 11324 name_desc.dsc$a_pointer= (char *) name; 11325 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) { 11326 __pwdcache.pw_uid= uic.uic$l_uic; 11327 __pwdcache.pw_gid= uic.uic$v_group; 11328 } 11329 else { 11330 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) { 11331 set_vaxc_errno(sts); 11332 set_errno(sts == RMS$_PRV ? EACCES : EINVAL); 11333 return NULL; 11334 } 11335 else { _ckvmssts(sts); } 11336 } 11337 } 11338 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache)); 11339 __pwdcache.pw_name= __pw_namecache; 11340 return &__pwdcache; 11341 } /* end of my_getpwnam() */ 11342 /*}}}*/ 11343 11344 /* 11345 * Get information for a particular UIC or UID. 11346 * Called by my_getpwent with uid=-1 to list all users. 11347 */ 11348 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/ 11349 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid) 11350 { 11351 const $DESCRIPTOR(name_desc,__pw_namecache); 11352 unsigned short lname; 11353 union uicdef uic; 11354 unsigned long int status; 11355 11356 if (uid == (unsigned int) -1) { 11357 do { 11358 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt); 11359 if (status == SS$_NOSUCHID || status == RMS$_PRV) { 11360 set_vaxc_errno(status); 11361 set_errno(status == RMS$_PRV ? EACCES : EINVAL); 11362 my_endpwent(); 11363 return NULL; 11364 } 11365 else { _ckvmssts(status); } 11366 } while (!valid_uic (uic)); 11367 } 11368 else { 11369 uic.uic$l_uic= uid; 11370 if (!uic.uic$v_group) 11371 uic.uic$v_group= PerlProc_getgid(); 11372 if (valid_uic(uic)) 11373 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0); 11374 else status = SS$_IVIDENT; 11375 if (status == SS$_IVIDENT || status == SS$_NOSUCHID || 11376 status == RMS$_PRV) { 11377 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL); 11378 return NULL; 11379 } 11380 else { _ckvmssts(status); } 11381 } 11382 __pw_namecache[lname]= '\0'; 11383 __mystrtolower(__pw_namecache); 11384 11385 __pwdcache = __passwd_empty; 11386 __pwdcache.pw_name = __pw_namecache; 11387 11388 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege). 11389 The identifier's value is usually the UIC, but it doesn't have to be, 11390 so if we can, we let fillpasswd update this. */ 11391 __pwdcache.pw_uid = uic.uic$l_uic; 11392 __pwdcache.pw_gid = uic.uic$v_group; 11393 11394 fillpasswd(aTHX_ __pw_namecache, &__pwdcache); 11395 return &__pwdcache; 11396 11397 } /* end of my_getpwuid() */ 11398 /*}}}*/ 11399 11400 /* 11401 * Get information for next user. 11402 */ 11403 /*{{{struct passwd *my_getpwent()*/ 11404 struct passwd *Perl_my_getpwent(pTHX) 11405 { 11406 return (my_getpwuid((unsigned int) -1)); 11407 } 11408 /*}}}*/ 11409 11410 /* 11411 * Finish searching rights database for users. 11412 */ 11413 /*{{{void my_endpwent()*/ 11414 void Perl_my_endpwent(pTHX) 11415 { 11416 if (contxt) { 11417 _ckvmssts(sys$finish_rdb(&contxt)); 11418 contxt= 0; 11419 } 11420 } 11421 /*}}}*/ 11422 11423 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(), 11424 * my_utime(), and flex_stat(), all of which operate on UTC unless 11425 * VMSISH_TIMES is true. 11426 */ 11427 /* method used to handle UTC conversions: 11428 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction 11429 */ 11430 static int gmtime_emulation_type; 11431 /* number of secs to add to UTC POSIX-style time to get local time */ 11432 static long int utc_offset_secs; 11433 11434 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc. 11435 * in vmsish.h. #undef them here so we can call the CRTL routines 11436 * directly. 11437 */ 11438 #undef gmtime 11439 #undef localtime 11440 #undef time 11441 11442 11443 static time_t toutc_dst(time_t loc) { 11444 struct tm *rsltmp; 11445 11446 if ((rsltmp = localtime(&loc)) == NULL) return -1u; 11447 loc -= utc_offset_secs; 11448 if (rsltmp->tm_isdst) loc -= 3600; 11449 return loc; 11450 } 11451 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 11452 ((gmtime_emulation_type || my_time(NULL)), \ 11453 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \ 11454 ((secs) - utc_offset_secs)))) 11455 11456 static time_t toloc_dst(time_t utc) { 11457 struct tm *rsltmp; 11458 11459 utc += utc_offset_secs; 11460 if ((rsltmp = localtime(&utc)) == NULL) return -1u; 11461 if (rsltmp->tm_isdst) utc += 3600; 11462 return utc; 11463 } 11464 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 11465 ((gmtime_emulation_type || my_time(NULL)), \ 11466 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \ 11467 ((secs) + utc_offset_secs)))) 11468 11469 /* my_time(), my_localtime(), my_gmtime() 11470 * By default traffic in UTC time values, using CRTL gmtime() or 11471 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone. 11472 * Note: We need to use these functions even when the CRTL has working 11473 * UTC support, since they also handle C<use vmsish qw(times);> 11474 * 11475 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu> 11476 * Modified by Charles Bailey <bailey@newman.upenn.edu> 11477 */ 11478 11479 /*{{{time_t my_time(time_t *timep)*/ 11480 time_t Perl_my_time(pTHX_ time_t *timep) 11481 { 11482 time_t when; 11483 struct tm *tm_p; 11484 11485 if (gmtime_emulation_type == 0) { 11486 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */ 11487 /* results of calls to gmtime() and localtime() */ 11488 /* for same &base */ 11489 11490 gmtime_emulation_type++; 11491 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */ 11492 char off[LNM$C_NAMLENGTH+1];; 11493 11494 gmtime_emulation_type++; 11495 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) { 11496 gmtime_emulation_type++; 11497 utc_offset_secs = 0; 11498 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC"); 11499 } 11500 else { utc_offset_secs = atol(off); } 11501 } 11502 else { /* We've got a working gmtime() */ 11503 struct tm gmt, local; 11504 11505 gmt = *tm_p; 11506 tm_p = localtime(&base); 11507 local = *tm_p; 11508 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400; 11509 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600; 11510 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60; 11511 utc_offset_secs += (local.tm_sec - gmt.tm_sec); 11512 } 11513 } 11514 11515 when = time(NULL); 11516 # ifdef VMSISH_TIME 11517 if (VMSISH_TIME) when = _toloc(when); 11518 # endif 11519 if (timep != NULL) *timep = when; 11520 return when; 11521 11522 } /* end of my_time() */ 11523 /*}}}*/ 11524 11525 11526 /*{{{struct tm *my_gmtime(const time_t *timep)*/ 11527 struct tm * 11528 Perl_my_gmtime(pTHX_ const time_t *timep) 11529 { 11530 time_t when; 11531 struct tm *rsltmp; 11532 11533 if (timep == NULL) { 11534 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11535 return NULL; 11536 } 11537 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11538 11539 when = *timep; 11540 # ifdef VMSISH_TIME 11541 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */ 11542 # endif 11543 return gmtime(&when); 11544 } /* end of my_gmtime() */ 11545 /*}}}*/ 11546 11547 11548 /*{{{struct tm *my_localtime(const time_t *timep)*/ 11549 struct tm * 11550 Perl_my_localtime(pTHX_ const time_t *timep) 11551 { 11552 time_t when; 11553 11554 if (timep == NULL) { 11555 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11556 return NULL; 11557 } 11558 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11559 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */ 11560 11561 when = *timep; 11562 # ifdef VMSISH_TIME 11563 if (VMSISH_TIME) when = _toutc(when); 11564 # endif 11565 /* CRTL localtime() wants UTC as input, does tz correction itself */ 11566 return localtime(&when); 11567 } /* end of my_localtime() */ 11568 /*}}}*/ 11569 11570 /* Reset definitions for later calls */ 11571 #define gmtime(t) my_gmtime(t) 11572 #define localtime(t) my_localtime(t) 11573 #define time(t) my_time(t) 11574 11575 11576 /* my_utime - update modification/access time of a file 11577 * 11578 * VMS 7.3 and later implementation 11579 * Only the UTC translation is home-grown. The rest is handled by the 11580 * CRTL utime(), which will take into account the relevant feature 11581 * logicals and ODS-5 volume characteristics for true access times. 11582 * 11583 * pre VMS 7.3 implementation: 11584 * The calling sequence is identical to POSIX utime(), but under 11585 * VMS with ODS-2, only the modification time is changed; ODS-2 does 11586 * not maintain access times. Restrictions differ from the POSIX 11587 * definition in that the time can be changed as long as the 11588 * caller has permission to execute the necessary IO$_MODIFY $QIO; 11589 * no separate checks are made to insure that the caller is the 11590 * owner of the file or has special privs enabled. 11591 * Code here is based on Joe Meadows' FILE utility. 11592 * 11593 */ 11594 11595 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00) 11596 * to VMS epoch (01-JAN-1858 00:00:00.00) 11597 * in 100 ns intervals. 11598 */ 11599 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 }; 11600 11601 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/ 11602 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes) 11603 { 11604 #if __CRTL_VER >= 70300000 11605 struct utimbuf utc_utimes, *utc_utimesp; 11606 11607 if (utimes != NULL) { 11608 utc_utimes.actime = utimes->actime; 11609 utc_utimes.modtime = utimes->modtime; 11610 # ifdef VMSISH_TIME 11611 /* If input was local; convert to UTC for sys svc */ 11612 if (VMSISH_TIME) { 11613 utc_utimes.actime = _toutc(utimes->actime); 11614 utc_utimes.modtime = _toutc(utimes->modtime); 11615 } 11616 # endif 11617 utc_utimesp = &utc_utimes; 11618 } 11619 else { 11620 utc_utimesp = NULL; 11621 } 11622 11623 return utime(file, utc_utimesp); 11624 11625 #else /* __CRTL_VER < 70300000 */ 11626 11627 int i; 11628 int sts; 11629 long int bintime[2], len = 2, lowbit, unixtime, 11630 secscale = 10000000; /* seconds --> 100 ns intervals */ 11631 unsigned long int chan, iosb[2], retsts; 11632 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS]; 11633 struct FAB myfab = cc$rms_fab; 11634 struct NAM mynam = cc$rms_nam; 11635 #if defined (__DECC) && defined (__VAX) 11636 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr, 11637 * at least through VMS V6.1, which causes a type-conversion warning. 11638 */ 11639 # pragma message save 11640 # pragma message disable cvtdiftypes 11641 #endif 11642 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}}; 11643 struct fibdef myfib; 11644 #if defined (__DECC) && defined (__VAX) 11645 /* This should be right after the declaration of myatr, but due 11646 * to a bug in VAX DEC C, this takes effect a statement early. 11647 */ 11648 # pragma message restore 11649 #endif 11650 /* cast ok for read only parameter */ 11651 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib}, 11652 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0}, 11653 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0}; 11654 11655 if (file == NULL || *file == '\0') { 11656 SETERRNO(ENOENT, LIB$_INVARG); 11657 return -1; 11658 } 11659 11660 /* Convert to VMS format ensuring that it will fit in 255 characters */ 11661 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) { 11662 SETERRNO(ENOENT, LIB$_INVARG); 11663 return -1; 11664 } 11665 if (utimes != NULL) { 11666 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00) 11667 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00). 11668 * Since time_t is unsigned long int, and lib$emul takes a signed long int 11669 * as input, we force the sign bit to be clear by shifting unixtime right 11670 * one bit, then multiplying by an extra factor of 2 in lib$emul(). 11671 */ 11672 lowbit = (utimes->modtime & 1) ? secscale : 0; 11673 unixtime = (long int) utimes->modtime; 11674 # ifdef VMSISH_TIME 11675 /* If input was UTC; convert to local for sys svc */ 11676 if (!VMSISH_TIME) unixtime = _toloc(unixtime); 11677 # endif 11678 unixtime >>= 1; secscale <<= 1; 11679 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime); 11680 if (!(retsts & 1)) { 11681 SETERRNO(EVMSERR, retsts); 11682 return -1; 11683 } 11684 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len); 11685 if (!(retsts & 1)) { 11686 SETERRNO(EVMSERR, retsts); 11687 return -1; 11688 } 11689 } 11690 else { 11691 /* Just get the current time in VMS format directly */ 11692 retsts = sys$gettim(bintime); 11693 if (!(retsts & 1)) { 11694 SETERRNO(EVMSERR, retsts); 11695 return -1; 11696 } 11697 } 11698 11699 myfab.fab$l_fna = vmsspec; 11700 myfab.fab$b_fns = (unsigned char) strlen(vmsspec); 11701 myfab.fab$l_nam = &mynam; 11702 mynam.nam$l_esa = esa; 11703 mynam.nam$b_ess = (unsigned char) sizeof esa; 11704 mynam.nam$l_rsa = rsa; 11705 mynam.nam$b_rss = (unsigned char) sizeof rsa; 11706 if (decc_efs_case_preserve) 11707 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE; 11708 11709 /* Look for the file to be affected, letting RMS parse the file 11710 * specification for us as well. I have set errno using only 11711 * values documented in the utime() man page for VMS POSIX. 11712 */ 11713 retsts = sys$parse(&myfab,0,0); 11714 if (!(retsts & 1)) { 11715 set_vaxc_errno(retsts); 11716 if (retsts == RMS$_PRV) set_errno(EACCES); 11717 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 11718 else set_errno(EVMSERR); 11719 return -1; 11720 } 11721 retsts = sys$search(&myfab,0,0); 11722 if (!(retsts & 1)) { 11723 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11724 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11725 set_vaxc_errno(retsts); 11726 if (retsts == RMS$_PRV) set_errno(EACCES); 11727 else if (retsts == RMS$_FNF) set_errno(ENOENT); 11728 else set_errno(EVMSERR); 11729 return -1; 11730 } 11731 11732 devdsc.dsc$w_length = mynam.nam$b_dev; 11733 /* cast ok for read only parameter */ 11734 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev; 11735 11736 retsts = sys$assign(&devdsc,&chan,0,0); 11737 if (!(retsts & 1)) { 11738 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11739 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11740 set_vaxc_errno(retsts); 11741 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR); 11742 else if (retsts == SS$_NOPRIV) set_errno(EACCES); 11743 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR); 11744 else set_errno(EVMSERR); 11745 return -1; 11746 } 11747 11748 fnmdsc.dsc$a_pointer = mynam.nam$l_name; 11749 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver; 11750 11751 memset((void *) &myfib, 0, sizeof myfib); 11752 #if defined(__DECC) || defined(__DECCXX) 11753 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i]; 11754 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i]; 11755 /* This prevents the revision time of the file being reset to the current 11756 * time as a result of our IO$_MODIFY $QIO. */ 11757 myfib.fib$l_acctl = FIB$M_NORECORD; 11758 #else 11759 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i]; 11760 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i]; 11761 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD; 11762 #endif 11763 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0); 11764 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11765 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11766 _ckvmssts(sys$dassgn(chan)); 11767 if (retsts & 1) retsts = iosb[0]; 11768 if (!(retsts & 1)) { 11769 set_vaxc_errno(retsts); 11770 if (retsts == SS$_NOPRIV) set_errno(EACCES); 11771 else set_errno(EVMSERR); 11772 return -1; 11773 } 11774 11775 return 0; 11776 11777 #endif /* #if __CRTL_VER >= 70300000 */ 11778 11779 } /* end of my_utime() */ 11780 /*}}}*/ 11781 11782 /* 11783 * flex_stat, flex_lstat, flex_fstat 11784 * basic stat, but gets it right when asked to stat 11785 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3) 11786 */ 11787 11788 #ifndef _USE_STD_STAT 11789 /* encode_dev packs a VMS device name string into an integer to allow 11790 * simple comparisons. This can be used, for example, to check whether two 11791 * files are located on the same device, by comparing their encoded device 11792 * names. Even a string comparison would not do, because stat() reuses the 11793 * device name buffer for each call; so without encode_dev, it would be 11794 * necessary to save the buffer and use strcmp (this would mean a number of 11795 * changes to the standard Perl code, to say nothing of what a Perl script 11796 * would have to do. 11797 * 11798 * The device lock id, if it exists, should be unique (unless perhaps compared 11799 * with lock ids transferred from other nodes). We have a lock id if the disk is 11800 * mounted cluster-wide, which is when we tend to get long (host-qualified) 11801 * device names. Thus we use the lock id in preference, and only if that isn't 11802 * available, do we try to pack the device name into an integer (flagged by 11803 * the sign bit (LOCKID_MASK) being set). 11804 * 11805 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device 11806 * name and its encoded form, but it seems very unlikely that we will find 11807 * two files on different disks that share the same encoded device names, 11808 * and even more remote that they will share the same file id (if the test 11809 * is to check for the same file). 11810 * 11811 * A better method might be to use sys$device_scan on the first call, and to 11812 * search for the device, returning an index into the cached array. 11813 * The number returned would be more intelligible. 11814 * This is probably not worth it, and anyway would take quite a bit longer 11815 * on the first call. 11816 */ 11817 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */ 11818 static mydev_t encode_dev (pTHX_ const char *dev) 11819 { 11820 int i; 11821 unsigned long int f; 11822 mydev_t enc; 11823 char c; 11824 const char *q; 11825 11826 if (!dev || !dev[0]) return 0; 11827 11828 #if LOCKID_MASK 11829 { 11830 struct dsc$descriptor_s dev_desc; 11831 unsigned long int status, lockid = 0, item = DVI$_LOCKID; 11832 11833 /* For cluster-mounted disks, the disk lock identifier is unique, so we 11834 can try that first. */ 11835 dev_desc.dsc$w_length = strlen (dev); 11836 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T; 11837 dev_desc.dsc$b_class = DSC$K_CLASS_S; 11838 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */ 11839 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0); 11840 if (!$VMS_STATUS_SUCCESS(status)) { 11841 switch (status) { 11842 case SS$_NOSUCHDEV: 11843 SETERRNO(ENODEV, status); 11844 return 0; 11845 default: 11846 _ckvmssts(status); 11847 } 11848 } 11849 if (lockid) return (lockid & ~LOCKID_MASK); 11850 } 11851 #endif 11852 11853 /* Otherwise we try to encode the device name */ 11854 enc = 0; 11855 f = 1; 11856 i = 0; 11857 for (q = dev + strlen(dev); q--; q >= dev) { 11858 if (*q == ':') 11859 break; 11860 if (isdigit (*q)) 11861 c= (*q) - '0'; 11862 else if (isalpha (toupper (*q))) 11863 c= toupper (*q) - 'A' + (char)10; 11864 else 11865 continue; /* Skip '$'s */ 11866 i++; 11867 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */ 11868 if (i>1) f *= 36; 11869 enc += f * (unsigned long int) c; 11870 } 11871 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */ 11872 11873 } /* end of encode_dev() */ 11874 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11875 device_no = encode_dev(aTHX_ devname) 11876 #else 11877 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11878 device_no = new_dev_no 11879 #endif 11880 11881 static int 11882 is_null_device(const char *name) 11883 { 11884 if (decc_bug_devnull != 0) { 11885 if (strncmp("/dev/null", name, 9) == 0) 11886 return 1; 11887 } 11888 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:". 11889 The underscore prefix, controller letter, and unit number are 11890 independently optional; for our purposes, the colon punctuation 11891 is not. The colon can be trailed by optional directory and/or 11892 filename, but two consecutive colons indicates a nodename rather 11893 than a device. [pr] */ 11894 if (*name == '_') ++name; 11895 if (tolower(*name++) != 'n') return 0; 11896 if (tolower(*name++) != 'l') return 0; 11897 if (tolower(*name) == 'a') ++name; 11898 if (*name == '0') ++name; 11899 return (*name++ == ':') && (*name != ':'); 11900 } 11901 11902 static int 11903 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag); 11904 11905 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c) 11906 11907 static I32 11908 Perl_cando_by_name_int 11909 (pTHX_ I32 bit, bool effective, const char *fname, int opts) 11910 { 11911 char usrname[L_cuserid]; 11912 struct dsc$descriptor_s usrdsc = 11913 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname}; 11914 char *vmsname = NULL, *fileified = NULL; 11915 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags; 11916 unsigned short int retlen, trnlnm_iter_count; 11917 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11918 union prvdef curprv; 11919 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen}, 11920 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen}, 11921 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}}; 11922 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen}, 11923 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length}, 11924 {0,0,0,0}}; 11925 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen}, 11926 {0,0,0,0}}; 11927 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11928 Stat_t st; 11929 static int profile_context = -1; 11930 11931 if (!fname || !*fname) return FALSE; 11932 11933 /* Make sure we expand logical names, since sys$check_access doesn't */ 11934 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 11935 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11936 if (!strpbrk(fname,"/]>:")) { 11937 my_strlcpy(fileified, fname, VMS_MAXRSS); 11938 trnlnm_iter_count = 0; 11939 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) { 11940 trnlnm_iter_count++; 11941 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 11942 } 11943 fname = fileified; 11944 } 11945 11946 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS); 11947 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM); 11948 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) { 11949 /* Don't know if already in VMS format, so make sure */ 11950 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) { 11951 PerlMem_free(fileified); 11952 PerlMem_free(vmsname); 11953 return FALSE; 11954 } 11955 } 11956 else { 11957 my_strlcpy(vmsname, fname, VMS_MAXRSS); 11958 } 11959 11960 /* sys$check_access needs a file spec, not a directory spec. 11961 * flex_stat now will handle a null thread context during startup. 11962 */ 11963 11964 retlen = namdsc.dsc$w_length = strlen(vmsname); 11965 if (vmsname[retlen-1] == ']' 11966 || vmsname[retlen-1] == '>' 11967 || vmsname[retlen-1] == ':' 11968 || (!flex_stat_int(vmsname, &st, 1) && 11969 S_ISDIR(st.st_mode))) { 11970 11971 if (!int_fileify_dirspec(vmsname, fileified, NULL)) { 11972 PerlMem_free(fileified); 11973 PerlMem_free(vmsname); 11974 return FALSE; 11975 } 11976 fname = fileified; 11977 } 11978 else { 11979 fname = vmsname; 11980 } 11981 11982 retlen = namdsc.dsc$w_length = strlen(fname); 11983 namdsc.dsc$a_pointer = (char *)fname; 11984 11985 switch (bit) { 11986 case S_IXUSR: case S_IXGRP: case S_IXOTH: 11987 access = ARM$M_EXECUTE; 11988 flags = CHP$M_READ; 11989 break; 11990 case S_IRUSR: case S_IRGRP: case S_IROTH: 11991 access = ARM$M_READ; 11992 flags = CHP$M_READ | CHP$M_USEREADALL; 11993 break; 11994 case S_IWUSR: case S_IWGRP: case S_IWOTH: 11995 access = ARM$M_WRITE; 11996 flags = CHP$M_READ | CHP$M_WRITE; 11997 break; 11998 case S_IDUSR: case S_IDGRP: case S_IDOTH: 11999 access = ARM$M_DELETE; 12000 flags = CHP$M_READ | CHP$M_WRITE; 12001 break; 12002 default: 12003 if (fileified != NULL) 12004 PerlMem_free(fileified); 12005 if (vmsname != NULL) 12006 PerlMem_free(vmsname); 12007 return FALSE; 12008 } 12009 12010 /* Before we call $check_access, create a user profile with the current 12011 * process privs since otherwise it just uses the default privs from the 12012 * UAF and might give false positives or negatives. This only works on 12013 * VMS versions v6.0 and later since that's when sys$create_user_profile 12014 * became available. 12015 */ 12016 12017 /* get current process privs and username */ 12018 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0)); 12019 _ckvmssts_noperl(iosb[0]); 12020 12021 /* find out the space required for the profile */ 12022 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0, 12023 &usrprodsc.dsc$w_length,&profile_context)); 12024 12025 /* allocate space for the profile and get it filled in */ 12026 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length); 12027 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12028 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer, 12029 &usrprodsc.dsc$w_length,&profile_context)); 12030 12031 /* use the profile to check access to the file; free profile & analyze results */ 12032 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc); 12033 PerlMem_free(usrprodsc.dsc$a_pointer); 12034 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */ 12035 12036 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT || 12037 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN || 12038 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) { 12039 set_vaxc_errno(retsts); 12040 if (retsts == SS$_NOPRIV) set_errno(EACCES); 12041 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL); 12042 else set_errno(ENOENT); 12043 if (fileified != NULL) 12044 PerlMem_free(fileified); 12045 if (vmsname != NULL) 12046 PerlMem_free(vmsname); 12047 return FALSE; 12048 } 12049 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) { 12050 if (fileified != NULL) 12051 PerlMem_free(fileified); 12052 if (vmsname != NULL) 12053 PerlMem_free(vmsname); 12054 return TRUE; 12055 } 12056 _ckvmssts_noperl(retsts); 12057 12058 if (fileified != NULL) 12059 PerlMem_free(fileified); 12060 if (vmsname != NULL) 12061 PerlMem_free(vmsname); 12062 return FALSE; /* Should never get here */ 12063 12064 } 12065 12066 /* Do the permissions allow some operation? Assumes PL_statcache already set. */ 12067 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a 12068 * subset of the applicable information. 12069 */ 12070 bool 12071 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp) 12072 { 12073 return cando_by_name_int 12074 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN); 12075 } /* end of cando() */ 12076 /*}}}*/ 12077 12078 12079 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/ 12080 I32 12081 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname) 12082 { 12083 return cando_by_name_int(bit, effective, fname, 0); 12084 12085 } /* end of cando_by_name() */ 12086 /*}}}*/ 12087 12088 12089 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/ 12090 int 12091 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp) 12092 { 12093 dSAVE_ERRNO; /* fstat may set this even on success */ 12094 if (!fstat(fd, &statbufp->crtl_stat)) { 12095 char *cptr; 12096 char *vms_filename; 12097 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS); 12098 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM); 12099 12100 /* Save name for cando by name in VMS format */ 12101 cptr = getname(fd, vms_filename, 1); 12102 12103 /* This should not happen, but just in case */ 12104 if (cptr == NULL) { 12105 statbufp->st_devnam[0] = 0; 12106 } 12107 else { 12108 /* Make sure that the saved name fits in 255 characters */ 12109 cptr = int_rmsexpand_vms 12110 (vms_filename, 12111 statbufp->st_devnam, 12112 0); 12113 if (cptr == NULL) 12114 statbufp->st_devnam[0] = 0; 12115 } 12116 PerlMem_free(vms_filename); 12117 12118 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 12119 VMS_DEVICE_ENCODE 12120 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 12121 12122 # ifdef VMSISH_TIME 12123 if (VMSISH_TIME) { 12124 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12125 statbufp->st_atime = _toloc(statbufp->st_atime); 12126 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12127 } 12128 # endif 12129 RESTORE_ERRNO; 12130 return 0; 12131 } 12132 return -1; 12133 12134 } /* end of flex_fstat() */ 12135 /*}}}*/ 12136 12137 static int 12138 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) 12139 { 12140 char *temp_fspec = NULL; 12141 char *fileified = NULL; 12142 const char *save_spec; 12143 char *ret_spec; 12144 int retval = -1; 12145 char efs_hack = 0; 12146 char already_fileified = 0; 12147 dSAVEDERRNO; 12148 12149 if (!fspec) { 12150 errno = EINVAL; 12151 return retval; 12152 } 12153 12154 if (decc_bug_devnull != 0) { 12155 if (is_null_device(fspec)) { /* Fake a stat() for the null device */ 12156 memset(statbufp,0,sizeof *statbufp); 12157 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0); 12158 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC; 12159 statbufp->st_uid = 0x00010001; 12160 statbufp->st_gid = 0x0001; 12161 time((time_t *)&statbufp->st_mtime); 12162 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime; 12163 return 0; 12164 } 12165 } 12166 12167 SAVE_ERRNO; 12168 12169 #if __CRTL_VER >= 80200000 && !defined(__VAX) 12170 /* 12171 * If we are in POSIX filespec mode, accept the filename as is. 12172 */ 12173 if (decc_posix_compliant_pathnames == 0) { 12174 #endif 12175 12176 /* Try for a simple stat first. If fspec contains a filename without 12177 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir 12178 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here. 12179 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir, 12180 * not sea:[wine.dark]., if the latter exists. If the intended target is 12181 * the file with null type, specify this by calling flex_stat() with 12182 * a '.' at the end of fspec. 12183 */ 12184 12185 if (lstat_flag == 0) 12186 retval = stat(fspec, &statbufp->crtl_stat); 12187 else 12188 retval = lstat(fspec, &statbufp->crtl_stat); 12189 12190 if (!retval) { 12191 save_spec = fspec; 12192 } 12193 else { 12194 /* In the odd case where we have write but not read access 12195 * to a directory, stat('foo.DIR') works but stat('foo') doesn't. 12196 */ 12197 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 12198 if (fileified == NULL) 12199 _ckvmssts_noperl(SS$_INSFMEM); 12200 12201 ret_spec = int_fileify_dirspec(fspec, fileified, NULL); 12202 if (ret_spec != NULL) { 12203 if (lstat_flag == 0) 12204 retval = stat(fileified, &statbufp->crtl_stat); 12205 else 12206 retval = lstat(fileified, &statbufp->crtl_stat); 12207 save_spec = fileified; 12208 already_fileified = 1; 12209 } 12210 } 12211 12212 if (retval && vms_bug_stat_filename) { 12213 12214 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS); 12215 if (temp_fspec == NULL) 12216 _ckvmssts_noperl(SS$_INSFMEM); 12217 12218 /* We should try again as a vmsified file specification. */ 12219 12220 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL); 12221 if (ret_spec != NULL) { 12222 if (lstat_flag == 0) 12223 retval = stat(temp_fspec, &statbufp->crtl_stat); 12224 else 12225 retval = lstat(temp_fspec, &statbufp->crtl_stat); 12226 save_spec = temp_fspec; 12227 } 12228 } 12229 12230 if (retval) { 12231 /* Last chance - allow multiple dots without EFS CHARSET */ 12232 /* The CRTL stat() falls down hard on multi-dot filenames in unix 12233 * format unless * DECC$EFS_CHARSET is in effect, so temporarily 12234 * enable it if it isn't already. 12235 */ 12236 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12237 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 12238 decc$feature_set_value(decc_efs_charset_index, 1, 1); 12239 #endif 12240 if (lstat_flag == 0) 12241 retval = stat(fspec, &statbufp->crtl_stat); 12242 else 12243 retval = lstat(fspec, &statbufp->crtl_stat); 12244 save_spec = fspec; 12245 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12246 if (!decc_efs_charset && (decc_efs_charset_index > 0)) { 12247 decc$feature_set_value(decc_efs_charset_index, 1, 0); 12248 efs_hack = 1; 12249 } 12250 #endif 12251 } 12252 12253 #if __CRTL_VER >= 80200000 && !defined(__VAX) 12254 } else { 12255 if (lstat_flag == 0) 12256 retval = stat(temp_fspec, &statbufp->crtl_stat); 12257 else 12258 retval = lstat(temp_fspec, &statbufp->crtl_stat); 12259 save_spec = temp_fspec; 12260 } 12261 #endif 12262 12263 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12264 /* As you were... */ 12265 if (!decc_efs_charset) 12266 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0); 12267 #endif 12268 12269 if (!retval) { 12270 char *cptr; 12271 int rmsex_flags = PERL_RMSEXPAND_M_VMS; 12272 12273 /* If this is an lstat, do not follow the link */ 12274 if (lstat_flag) 12275 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK; 12276 12277 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12278 /* If we used the efs_hack above, we must also use it here for */ 12279 /* perl_cando to work */ 12280 if (efs_hack && (decc_efs_charset_index > 0)) { 12281 decc$feature_set_value(decc_efs_charset_index, 1, 1); 12282 } 12283 #endif 12284 12285 /* If we've got a directory, save a fileified, expanded version of it 12286 * in st_devnam. If not a directory, just an expanded version. 12287 */ 12288 if (S_ISDIR(statbufp->st_mode) && !already_fileified) { 12289 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 12290 if (fileified == NULL) 12291 _ckvmssts_noperl(SS$_INSFMEM); 12292 12293 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL); 12294 if (cptr != NULL) 12295 save_spec = fileified; 12296 } 12297 12298 cptr = int_rmsexpand(save_spec, 12299 statbufp->st_devnam, 12300 NULL, 12301 rmsex_flags, 12302 0, 12303 0); 12304 12305 #if __CRTL_VER >= 70300000 && !defined(__VAX) 12306 if (efs_hack && (decc_efs_charset_index > 0)) { 12307 decc$feature_set_value(decc_efs_charset, 1, 0); 12308 } 12309 #endif 12310 12311 /* Fix me: If this is NULL then stat found a file, and we could */ 12312 /* not convert the specification to VMS - Should never happen */ 12313 if (cptr == NULL) 12314 statbufp->st_devnam[0] = 0; 12315 12316 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 12317 VMS_DEVICE_ENCODE 12318 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 12319 # ifdef VMSISH_TIME 12320 if (VMSISH_TIME) { 12321 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12322 statbufp->st_atime = _toloc(statbufp->st_atime); 12323 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12324 } 12325 # endif 12326 } 12327 /* If we were successful, leave errno where we found it */ 12328 if (retval == 0) RESTORE_ERRNO; 12329 if (temp_fspec) 12330 PerlMem_free(temp_fspec); 12331 if (fileified) 12332 PerlMem_free(fileified); 12333 return retval; 12334 12335 } /* end of flex_stat_int() */ 12336 12337 12338 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/ 12339 int 12340 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp) 12341 { 12342 return flex_stat_int(fspec, statbufp, 0); 12343 } 12344 /*}}}*/ 12345 12346 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/ 12347 int 12348 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp) 12349 { 12350 return flex_stat_int(fspec, statbufp, 1); 12351 } 12352 /*}}}*/ 12353 12354 12355 /*{{{char *my_getlogin()*/ 12356 /* VMS cuserid == Unix getlogin, except calling sequence */ 12357 char * 12358 my_getlogin(void) 12359 { 12360 static char user[L_cuserid]; 12361 return cuserid(user); 12362 } 12363 /*}}}*/ 12364 12365 12366 /* rmscopy - copy a file using VMS RMS routines 12367 * 12368 * Copies contents and attributes of spec_in to spec_out, except owner 12369 * and protection information. Name and type of spec_in are used as 12370 * defaults for spec_out. The third parameter specifies whether rmscopy() 12371 * should try to propagate timestamps from the input file to the output file. 12372 * If it is less than 0, no timestamps are preserved. If it is 0, then 12373 * rmscopy() will behave similarly to the DCL COPY command: timestamps are 12374 * propagated to the output file at creation iff the output file specification 12375 * did not contain an explicit name or type, and the revision date is always 12376 * updated at the end of the copy operation. If it is greater than 0, then 12377 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps 12378 * other than the revision date should be propagated, and bit 1 indicates 12379 * that the revision date should be propagated. 12380 * 12381 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure. 12382 * 12383 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>. 12384 * Incorporates, with permission, some code from EZCOPY by Tim Adye 12385 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code 12386 * as part of the Perl standard distribution under the terms of the 12387 * GNU General Public License or the Perl Artistic License. Copies 12388 * of each may be found in the Perl standard distribution. 12389 */ /* FIXME */ 12390 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/ 12391 int 12392 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates) 12393 { 12394 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out, 12395 *rsa, *rsal, *rsa_out, *rsal_out, *ubf; 12396 unsigned long int sts; 12397 int dna_len; 12398 struct FAB fab_in, fab_out; 12399 struct RAB rab_in, rab_out; 12400 rms_setup_nam(nam); 12401 rms_setup_nam(nam_out); 12402 struct XABDAT xabdat; 12403 struct XABFHC xabfhc; 12404 struct XABRDT xabrdt; 12405 struct XABSUM xabsum; 12406 12407 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS); 12408 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12409 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS); 12410 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12411 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) || 12412 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) { 12413 PerlMem_free(vmsin); 12414 PerlMem_free(vmsout); 12415 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12416 return 0; 12417 } 12418 12419 esa = (char *)PerlMem_malloc(VMS_MAXRSS); 12420 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12421 esal = NULL; 12422 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12423 esal = (char *)PerlMem_malloc(VMS_MAXRSS); 12424 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12425 #endif 12426 fab_in = cc$rms_fab; 12427 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin)); 12428 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI; 12429 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET; 12430 fab_in.fab$l_fop = FAB$M_SQO; 12431 rms_bind_fab_nam(fab_in, nam); 12432 fab_in.fab$l_xab = (void *) &xabdat; 12433 12434 rsa = (char *)PerlMem_malloc(VMS_MAXRSS); 12435 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12436 rsal = NULL; 12437 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12438 rsal = (char *)PerlMem_malloc(VMS_MAXRSS); 12439 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12440 #endif 12441 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1)); 12442 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 12443 rms_nam_esl(nam) = 0; 12444 rms_nam_rsl(nam) = 0; 12445 rms_nam_esll(nam) = 0; 12446 rms_nam_rsll(nam) = 0; 12447 #ifdef NAM$M_NO_SHORT_UPCASE 12448 if (decc_efs_case_preserve) 12449 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE); 12450 #endif 12451 12452 xabdat = cc$rms_xabdat; /* To get creation date */ 12453 xabdat.xab$l_nxt = (void *) &xabfhc; 12454 12455 xabfhc = cc$rms_xabfhc; /* To get record length */ 12456 xabfhc.xab$l_nxt = (void *) &xabsum; 12457 12458 xabsum = cc$rms_xabsum; /* To get key and area information */ 12459 12460 if (!((sts = sys$open(&fab_in)) & 1)) { 12461 PerlMem_free(vmsin); 12462 PerlMem_free(vmsout); 12463 PerlMem_free(esa); 12464 if (esal != NULL) 12465 PerlMem_free(esal); 12466 PerlMem_free(rsa); 12467 if (rsal != NULL) 12468 PerlMem_free(rsal); 12469 set_vaxc_errno(sts); 12470 switch (sts) { 12471 case RMS$_FNF: case RMS$_DNF: 12472 set_errno(ENOENT); break; 12473 case RMS$_DIR: 12474 set_errno(ENOTDIR); break; 12475 case RMS$_DEV: 12476 set_errno(ENODEV); break; 12477 case RMS$_SYN: 12478 set_errno(EINVAL); break; 12479 case RMS$_PRV: 12480 set_errno(EACCES); break; 12481 default: 12482 set_errno(EVMSERR); 12483 } 12484 return 0; 12485 } 12486 12487 nam_out = nam; 12488 fab_out = fab_in; 12489 fab_out.fab$w_ifi = 0; 12490 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT; 12491 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI; 12492 fab_out.fab$l_fop = FAB$M_SQO; 12493 rms_bind_fab_nam(fab_out, nam_out); 12494 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout)); 12495 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0; 12496 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len); 12497 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 12498 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12499 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1); 12500 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12501 esal_out = NULL; 12502 rsal_out = NULL; 12503 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12504 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS); 12505 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12506 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS); 12507 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12508 #endif 12509 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1)); 12510 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1)); 12511 12512 if (preserve_dates == 0) { /* Act like DCL COPY */ 12513 rms_set_nam_nop(nam_out, NAM$M_SYNCHK); 12514 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */ 12515 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) { 12516 PerlMem_free(vmsin); 12517 PerlMem_free(vmsout); 12518 PerlMem_free(esa); 12519 if (esal != NULL) 12520 PerlMem_free(esal); 12521 PerlMem_free(rsa); 12522 if (rsal != NULL) 12523 PerlMem_free(rsal); 12524 PerlMem_free(esa_out); 12525 if (esal_out != NULL) 12526 PerlMem_free(esal_out); 12527 PerlMem_free(rsa_out); 12528 if (rsal_out != NULL) 12529 PerlMem_free(rsal_out); 12530 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR); 12531 set_vaxc_errno(sts); 12532 return 0; 12533 } 12534 fab_out.fab$l_xab = (void *) &xabdat; 12535 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) 12536 preserve_dates = 1; 12537 } 12538 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */ 12539 preserve_dates =0; /* bitmask from this point forward */ 12540 12541 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc; 12542 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) { 12543 PerlMem_free(vmsin); 12544 PerlMem_free(vmsout); 12545 PerlMem_free(esa); 12546 if (esal != NULL) 12547 PerlMem_free(esal); 12548 PerlMem_free(rsa); 12549 if (rsal != NULL) 12550 PerlMem_free(rsal); 12551 PerlMem_free(esa_out); 12552 if (esal_out != NULL) 12553 PerlMem_free(esal_out); 12554 PerlMem_free(rsa_out); 12555 if (rsal_out != NULL) 12556 PerlMem_free(rsal_out); 12557 set_vaxc_errno(sts); 12558 switch (sts) { 12559 case RMS$_DNF: 12560 set_errno(ENOENT); break; 12561 case RMS$_DIR: 12562 set_errno(ENOTDIR); break; 12563 case RMS$_DEV: 12564 set_errno(ENODEV); break; 12565 case RMS$_SYN: 12566 set_errno(EINVAL); break; 12567 case RMS$_PRV: 12568 set_errno(EACCES); break; 12569 default: 12570 set_errno(EVMSERR); 12571 } 12572 return 0; 12573 } 12574 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */ 12575 if (preserve_dates & 2) { 12576 /* sys$close() will process xabrdt, not xabdat */ 12577 xabrdt = cc$rms_xabrdt; 12578 #ifndef __GNUC__ 12579 xabrdt.xab$q_rdt = xabdat.xab$q_rdt; 12580 #else 12581 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt 12582 * is unsigned long[2], while DECC & VAXC use a struct */ 12583 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt); 12584 #endif 12585 fab_out.fab$l_xab = (void *) &xabrdt; 12586 } 12587 12588 ubf = (char *)PerlMem_malloc(32256); 12589 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM); 12590 rab_in = cc$rms_rab; 12591 rab_in.rab$l_fab = &fab_in; 12592 rab_in.rab$l_rop = RAB$M_BIO; 12593 rab_in.rab$l_ubf = ubf; 12594 rab_in.rab$w_usz = 32256; 12595 if (!((sts = sys$connect(&rab_in)) & 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 rab_out = cc$rms_rab; 12617 rab_out.rab$l_fab = &fab_out; 12618 rab_out.rab$l_rbf = ubf; 12619 if (!((sts = sys$connect(&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 while ((sts = sys$read(&rab_in))) { /* always true */ 12641 if (sts == RMS$_EOF) break; 12642 rab_out.rab$w_rsz = rab_in.rab$w_rsz; 12643 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) { 12644 sys$close(&fab_in); sys$close(&fab_out); 12645 PerlMem_free(vmsin); 12646 PerlMem_free(vmsout); 12647 PerlMem_free(ubf); 12648 PerlMem_free(esa); 12649 if (esal != NULL) 12650 PerlMem_free(esal); 12651 PerlMem_free(rsa); 12652 if (rsal != NULL) 12653 PerlMem_free(rsal); 12654 PerlMem_free(esa_out); 12655 if (esal_out != NULL) 12656 PerlMem_free(esal_out); 12657 PerlMem_free(rsa_out); 12658 if (rsal_out != NULL) 12659 PerlMem_free(rsal_out); 12660 set_errno(EVMSERR); set_vaxc_errno(sts); 12661 return 0; 12662 } 12663 } 12664 12665 12666 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */ 12667 sys$close(&fab_in); sys$close(&fab_out); 12668 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts; 12669 12670 PerlMem_free(vmsin); 12671 PerlMem_free(vmsout); 12672 PerlMem_free(ubf); 12673 PerlMem_free(esa); 12674 if (esal != NULL) 12675 PerlMem_free(esal); 12676 PerlMem_free(rsa); 12677 if (rsal != NULL) 12678 PerlMem_free(rsal); 12679 PerlMem_free(esa_out); 12680 if (esal_out != NULL) 12681 PerlMem_free(esal_out); 12682 PerlMem_free(rsa_out); 12683 if (rsal_out != NULL) 12684 PerlMem_free(rsal_out); 12685 12686 if (!(sts & 1)) { 12687 set_errno(EVMSERR); set_vaxc_errno(sts); 12688 return 0; 12689 } 12690 12691 return 1; 12692 12693 } /* end of rmscopy() */ 12694 /*}}}*/ 12695 12696 12697 /*** The following glue provides 'hooks' to make some of the routines 12698 * from this file available from Perl. These routines are sufficiently 12699 * basic, and are required sufficiently early in the build process, 12700 * that's it's nice to have them available to miniperl as well as the 12701 * full Perl, so they're set up here instead of in an extension. The 12702 * Perl code which handles importation of these names into a given 12703 * package lives in [.VMS]Filespec.pm in @INC. 12704 */ 12705 12706 void 12707 rmsexpand_fromperl(pTHX_ CV *cv) 12708 { 12709 dXSARGS; 12710 char *fspec, *defspec = NULL, *rslt; 12711 STRLEN n_a; 12712 int fs_utf8, dfs_utf8; 12713 12714 fs_utf8 = 0; 12715 dfs_utf8 = 0; 12716 if (!items || items > 2) 12717 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])"); 12718 fspec = SvPV(ST(0),n_a); 12719 fs_utf8 = SvUTF8(ST(0)); 12720 if (!fspec || !*fspec) XSRETURN_UNDEF; 12721 if (items == 2) { 12722 defspec = SvPV(ST(1),n_a); 12723 dfs_utf8 = SvUTF8(ST(1)); 12724 } 12725 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8); 12726 ST(0) = sv_newmortal(); 12727 if (rslt != NULL) { 12728 sv_usepvn(ST(0),rslt,strlen(rslt)); 12729 if (fs_utf8) { 12730 SvUTF8_on(ST(0)); 12731 } 12732 } 12733 XSRETURN(1); 12734 } 12735 12736 void 12737 vmsify_fromperl(pTHX_ CV *cv) 12738 { 12739 dXSARGS; 12740 char *vmsified; 12741 STRLEN n_a; 12742 int utf8_fl; 12743 12744 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)"); 12745 utf8_fl = SvUTF8(ST(0)); 12746 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12747 ST(0) = sv_newmortal(); 12748 if (vmsified != NULL) { 12749 sv_usepvn(ST(0),vmsified,strlen(vmsified)); 12750 if (utf8_fl) { 12751 SvUTF8_on(ST(0)); 12752 } 12753 } 12754 XSRETURN(1); 12755 } 12756 12757 void 12758 unixify_fromperl(pTHX_ CV *cv) 12759 { 12760 dXSARGS; 12761 char *unixified; 12762 STRLEN n_a; 12763 int utf8_fl; 12764 12765 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)"); 12766 utf8_fl = SvUTF8(ST(0)); 12767 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12768 ST(0) = sv_newmortal(); 12769 if (unixified != NULL) { 12770 sv_usepvn(ST(0),unixified,strlen(unixified)); 12771 if (utf8_fl) { 12772 SvUTF8_on(ST(0)); 12773 } 12774 } 12775 XSRETURN(1); 12776 } 12777 12778 void 12779 fileify_fromperl(pTHX_ CV *cv) 12780 { 12781 dXSARGS; 12782 char *fileified; 12783 STRLEN n_a; 12784 int utf8_fl; 12785 12786 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)"); 12787 utf8_fl = SvUTF8(ST(0)); 12788 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12789 ST(0) = sv_newmortal(); 12790 if (fileified != NULL) { 12791 sv_usepvn(ST(0),fileified,strlen(fileified)); 12792 if (utf8_fl) { 12793 SvUTF8_on(ST(0)); 12794 } 12795 } 12796 XSRETURN(1); 12797 } 12798 12799 void 12800 pathify_fromperl(pTHX_ CV *cv) 12801 { 12802 dXSARGS; 12803 char *pathified; 12804 STRLEN n_a; 12805 int utf8_fl; 12806 12807 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)"); 12808 utf8_fl = SvUTF8(ST(0)); 12809 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12810 ST(0) = sv_newmortal(); 12811 if (pathified != NULL) { 12812 sv_usepvn(ST(0),pathified,strlen(pathified)); 12813 if (utf8_fl) { 12814 SvUTF8_on(ST(0)); 12815 } 12816 } 12817 XSRETURN(1); 12818 } 12819 12820 void 12821 vmspath_fromperl(pTHX_ CV *cv) 12822 { 12823 dXSARGS; 12824 char *vmspath; 12825 STRLEN n_a; 12826 int utf8_fl; 12827 12828 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)"); 12829 utf8_fl = SvUTF8(ST(0)); 12830 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12831 ST(0) = sv_newmortal(); 12832 if (vmspath != NULL) { 12833 sv_usepvn(ST(0),vmspath,strlen(vmspath)); 12834 if (utf8_fl) { 12835 SvUTF8_on(ST(0)); 12836 } 12837 } 12838 XSRETURN(1); 12839 } 12840 12841 void 12842 unixpath_fromperl(pTHX_ CV *cv) 12843 { 12844 dXSARGS; 12845 char *unixpath; 12846 STRLEN n_a; 12847 int utf8_fl; 12848 12849 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)"); 12850 utf8_fl = SvUTF8(ST(0)); 12851 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12852 ST(0) = sv_newmortal(); 12853 if (unixpath != NULL) { 12854 sv_usepvn(ST(0),unixpath,strlen(unixpath)); 12855 if (utf8_fl) { 12856 SvUTF8_on(ST(0)); 12857 } 12858 } 12859 XSRETURN(1); 12860 } 12861 12862 void 12863 candelete_fromperl(pTHX_ CV *cv) 12864 { 12865 dXSARGS; 12866 char *fspec, *fsp; 12867 SV *mysv; 12868 IO *io; 12869 STRLEN n_a; 12870 12871 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)"); 12872 12873 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12874 Newx(fspec, VMS_MAXRSS, char); 12875 if (fspec == NULL) _ckvmssts(SS$_INSFMEM); 12876 if (isGV_with_GP(mysv)) { 12877 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) { 12878 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12879 ST(0) = &PL_sv_no; 12880 Safefree(fspec); 12881 XSRETURN(1); 12882 } 12883 fsp = fspec; 12884 } 12885 else { 12886 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) { 12887 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12888 ST(0) = &PL_sv_no; 12889 Safefree(fspec); 12890 XSRETURN(1); 12891 } 12892 } 12893 12894 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp)); 12895 Safefree(fspec); 12896 XSRETURN(1); 12897 } 12898 12899 void 12900 rmscopy_fromperl(pTHX_ CV *cv) 12901 { 12902 dXSARGS; 12903 char *inspec, *outspec, *inp, *outp; 12904 int date_flag; 12905 SV *mysv; 12906 IO *io; 12907 STRLEN n_a; 12908 12909 if (items < 2 || items > 3) 12910 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])"); 12911 12912 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12913 Newx(inspec, VMS_MAXRSS, char); 12914 if (isGV_with_GP(mysv)) { 12915 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) { 12916 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12917 ST(0) = sv_2mortal(newSViv(0)); 12918 Safefree(inspec); 12919 XSRETURN(1); 12920 } 12921 inp = inspec; 12922 } 12923 else { 12924 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) { 12925 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12926 ST(0) = sv_2mortal(newSViv(0)); 12927 Safefree(inspec); 12928 XSRETURN(1); 12929 } 12930 } 12931 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1); 12932 Newx(outspec, VMS_MAXRSS, char); 12933 if (isGV_with_GP(mysv)) { 12934 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) { 12935 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12936 ST(0) = sv_2mortal(newSViv(0)); 12937 Safefree(inspec); 12938 Safefree(outspec); 12939 XSRETURN(1); 12940 } 12941 outp = outspec; 12942 } 12943 else { 12944 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) { 12945 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12946 ST(0) = sv_2mortal(newSViv(0)); 12947 Safefree(inspec); 12948 Safefree(outspec); 12949 XSRETURN(1); 12950 } 12951 } 12952 date_flag = (items == 3) ? SvIV(ST(2)) : 0; 12953 12954 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag))); 12955 Safefree(inspec); 12956 Safefree(outspec); 12957 XSRETURN(1); 12958 } 12959 12960 /* The mod2fname is limited to shorter filenames by design, so it should 12961 * not be modified to support longer EFS pathnames 12962 */ 12963 void 12964 mod2fname(pTHX_ CV *cv) 12965 { 12966 dXSARGS; 12967 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1], 12968 workbuff[NAM$C_MAXRSS*1 + 1]; 12969 SSize_t counter, num_entries; 12970 /* ODS-5 ups this, but we want to be consistent, so... */ 12971 int max_name_len = 39; 12972 AV *in_array = (AV *)SvRV(ST(0)); 12973 12974 num_entries = av_tindex(in_array); 12975 12976 /* All the names start with PL_. */ 12977 strcpy(ultimate_name, "PL_"); 12978 12979 /* Clean up our working buffer */ 12980 Zero(work_name, sizeof(work_name), char); 12981 12982 /* Run through the entries and build up a working name */ 12983 for(counter = 0; counter <= num_entries; counter++) { 12984 /* If it's not the first name then tack on a __ */ 12985 if (counter) { 12986 my_strlcat(work_name, "__", sizeof(work_name)); 12987 } 12988 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name)); 12989 } 12990 12991 /* Check to see if we actually have to bother...*/ 12992 if (strlen(work_name) + 3 <= max_name_len) { 12993 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name)); 12994 } else { 12995 /* It's too darned big, so we need to go strip. We use the same */ 12996 /* algorithm as xsubpp does. First, strip out doubled __ */ 12997 char *source, *dest, last; 12998 dest = workbuff; 12999 last = 0; 13000 for (source = work_name; *source; source++) { 13001 if (last == *source && last == '_') { 13002 continue; 13003 } 13004 *dest++ = *source; 13005 last = *source; 13006 } 13007 /* Go put it back */ 13008 my_strlcpy(work_name, workbuff, sizeof(work_name)); 13009 /* Is it still too big? */ 13010 if (strlen(work_name) + 3 > max_name_len) { 13011 /* Strip duplicate letters */ 13012 last = 0; 13013 dest = workbuff; 13014 for (source = work_name; *source; source++) { 13015 if (last == toupper(*source)) { 13016 continue; 13017 } 13018 *dest++ = *source; 13019 last = toupper(*source); 13020 } 13021 my_strlcpy(work_name, workbuff, sizeof(work_name)); 13022 } 13023 13024 /* Is it *still* too big? */ 13025 if (strlen(work_name) + 3 > max_name_len) { 13026 /* Too bad, we truncate */ 13027 work_name[max_name_len - 2] = 0; 13028 } 13029 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name)); 13030 } 13031 13032 /* Okay, return it */ 13033 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0)); 13034 XSRETURN(1); 13035 } 13036 13037 void 13038 hushexit_fromperl(pTHX_ CV *cv) 13039 { 13040 dXSARGS; 13041 13042 if (items > 0) { 13043 VMSISH_HUSHED = SvTRUE(ST(0)); 13044 } 13045 ST(0) = boolSV(VMSISH_HUSHED); 13046 XSRETURN(1); 13047 } 13048 13049 13050 PerlIO * 13051 Perl_vms_start_glob 13052 (pTHX_ SV *tmpglob, 13053 IO *io) 13054 { 13055 PerlIO *fp; 13056 struct vs_str_st *rslt; 13057 char *vmsspec; 13058 char *rstr; 13059 char *begin, *cp; 13060 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;"); 13061 PerlIO *tmpfp; 13062 STRLEN i; 13063 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13064 struct dsc$descriptor_vs rsdsc; 13065 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0; 13066 unsigned long hasver = 0, isunix = 0; 13067 unsigned long int lff_flags = 0; 13068 int rms_sts; 13069 int vms_old_glob = 1; 13070 13071 if (!SvOK(tmpglob)) { 13072 SETERRNO(ENOENT,RMS$_FNF); 13073 return NULL; 13074 } 13075 13076 vms_old_glob = !decc_filename_unix_report; 13077 13078 #ifdef VMS_LONGNAME_SUPPORT 13079 lff_flags = LIB$M_FIL_LONG_NAMES; 13080 #endif 13081 /* The Newx macro will not allow me to assign a smaller array 13082 * to the rslt pointer, so we will assign it to the begin char pointer 13083 * and then copy the value into the rslt pointer. 13084 */ 13085 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char); 13086 rslt = (struct vs_str_st *)begin; 13087 rslt->length = 0; 13088 rstr = &rslt->str[0]; 13089 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */ 13090 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int); 13091 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT; 13092 rsdsc.dsc$b_class = DSC$K_CLASS_VS; 13093 13094 Newx(vmsspec, VMS_MAXRSS, char); 13095 13096 /* We could find out if there's an explicit dev/dir or version 13097 by peeking into lib$find_file's internal context at 13098 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb 13099 but that's unsupported, so I don't want to do it now and 13100 have it bite someone in the future. */ 13101 /* Fix-me: vms_split_path() is the only way to do this, the 13102 existing method will fail with many legal EFS or UNIX specifications 13103 */ 13104 13105 cp = SvPV(tmpglob,i); 13106 13107 for (; i; i--) { 13108 if (cp[i] == ';') hasver = 1; 13109 if (cp[i] == '.') { 13110 if (sts) hasver = 1; 13111 else sts = 1; 13112 } 13113 if (cp[i] == '/') { 13114 hasdir = isunix = 1; 13115 break; 13116 } 13117 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') { 13118 hasdir = 1; 13119 break; 13120 } 13121 } 13122 13123 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */ 13124 if ((hasdir == 0) && decc_filename_unix_report) { 13125 isunix = 1; 13126 } 13127 13128 if ((tmpfp = PerlIO_tmpfile()) != NULL) { 13129 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec; 13130 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len; 13131 int wildstar = 0; 13132 int wildquery = 0; 13133 int found = 0; 13134 Stat_t st; 13135 int stat_sts; 13136 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st); 13137 if (!stat_sts && S_ISDIR(st.st_mode)) { 13138 char * vms_dir; 13139 const char * fname; 13140 STRLEN fname_len; 13141 13142 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */ 13143 /* path delimiter of ':>]', if so, then the old behavior has */ 13144 /* obviously been specifically requested */ 13145 13146 fname = SvPVX_const(tmpglob); 13147 fname_len = strlen(fname); 13148 vms_dir = strpbrk(&fname[fname_len - 1], ":>]"); 13149 if (vms_old_glob || (vms_dir != NULL)) { 13150 wilddsc.dsc$a_pointer = tovmspath_utf8( 13151 SvPVX(tmpglob),vmsspec,NULL); 13152 ok = (wilddsc.dsc$a_pointer != NULL); 13153 /* maybe passed 'foo' rather than '[.foo]', thus not 13154 detected above */ 13155 hasdir = 1; 13156 } else { 13157 /* Operate just on the directory, the special stat/fstat for */ 13158 /* leaves the fileified specification in the st_devnam */ 13159 /* member. */ 13160 wilddsc.dsc$a_pointer = st.st_devnam; 13161 ok = 1; 13162 } 13163 } 13164 else { 13165 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL); 13166 ok = (wilddsc.dsc$a_pointer != NULL); 13167 } 13168 if (ok) 13169 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer); 13170 13171 /* If not extended character set, replace ? with % */ 13172 /* With extended character set, ? is a wildcard single character */ 13173 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) { 13174 if (*cp == '?') { 13175 wildquery = 1; 13176 if (!decc_efs_charset) 13177 *cp = '%'; 13178 } else if (*cp == '%') { 13179 wildquery = 1; 13180 } else if (*cp == '*') { 13181 wildstar = 1; 13182 } 13183 } 13184 13185 if (ok) { 13186 wv_sts = vms_split_path( 13187 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len, 13188 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len, 13189 &wvs_spec, &wvs_len); 13190 } else { 13191 wn_spec = NULL; 13192 wn_len = 0; 13193 we_spec = NULL; 13194 we_len = 0; 13195 } 13196 13197 sts = SS$_NORMAL; 13198 while (ok && $VMS_STATUS_SUCCESS(sts)) { 13199 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13200 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13201 int valid_find; 13202 13203 valid_find = 0; 13204 sts = lib$find_file(&wilddsc,&rsdsc,&cxt, 13205 &dfltdsc,NULL,&rms_sts,&lff_flags); 13206 if (!$VMS_STATUS_SUCCESS(sts)) 13207 break; 13208 13209 /* with varying string, 1st word of buffer contains result length */ 13210 rstr[rslt->length] = '\0'; 13211 13212 /* Find where all the components are */ 13213 v_sts = vms_split_path 13214 (rstr, 13215 &v_spec, 13216 &v_len, 13217 &r_spec, 13218 &r_len, 13219 &d_spec, 13220 &d_len, 13221 &n_spec, 13222 &n_len, 13223 &e_spec, 13224 &e_len, 13225 &vs_spec, 13226 &vs_len); 13227 13228 /* If no version on input, truncate the version on output */ 13229 if (!hasver && (vs_len > 0)) { 13230 *vs_spec = '\0'; 13231 vs_len = 0; 13232 } 13233 13234 if (isunix) { 13235 13236 /* In Unix report mode, remove the ".dir;1" from the name */ 13237 /* if it is a real directory */ 13238 if (decc_filename_unix_report && decc_efs_charset) { 13239 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 13240 Stat_t statbuf; 13241 int ret_sts; 13242 13243 ret_sts = flex_lstat(rstr, &statbuf); 13244 if ((ret_sts == 0) && 13245 S_ISDIR(statbuf.st_mode)) { 13246 e_len = 0; 13247 e_spec[0] = 0; 13248 } 13249 } 13250 } 13251 13252 /* No version & a null extension on UNIX handling */ 13253 if ((e_len == 1) && decc_readdir_dropdotnotype) { 13254 e_len = 0; 13255 *e_spec = '\0'; 13256 } 13257 } 13258 13259 if (!decc_efs_case_preserve) { 13260 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp); 13261 } 13262 13263 /* Find File treats a Null extension as return all extensions */ 13264 /* This is contrary to Perl expectations */ 13265 13266 if (wildstar || wildquery || vms_old_glob) { 13267 /* really need to see if the returned file name matched */ 13268 /* but for now will assume that it matches */ 13269 valid_find = 1; 13270 } else { 13271 /* Exact Match requested */ 13272 /* How are directories handled? - like a file */ 13273 if ((e_len == we_len) && (n_len == wn_len)) { 13274 int t1; 13275 t1 = e_len; 13276 if (t1 > 0) 13277 t1 = strncmp(e_spec, we_spec, e_len); 13278 if (t1 == 0) { 13279 t1 = n_len; 13280 if (t1 > 0) 13281 t1 = strncmp(n_spec, we_spec, n_len); 13282 if (t1 == 0) 13283 valid_find = 1; 13284 } 13285 } 13286 } 13287 13288 if (valid_find) { 13289 found++; 13290 13291 if (hasdir) { 13292 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1); 13293 begin = rstr; 13294 } 13295 else { 13296 /* Start with the name */ 13297 begin = n_spec; 13298 } 13299 strcat(begin,"\n"); 13300 ok = (PerlIO_puts(tmpfp,begin) != EOF); 13301 } 13302 } 13303 if (cxt) (void)lib$find_file_end(&cxt); 13304 13305 if (!found) { 13306 /* Be POSIXish: return the input pattern when no matches */ 13307 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS); 13308 strcat(rstr,"\n"); 13309 ok = (PerlIO_puts(tmpfp,rstr) != EOF); 13310 } 13311 13312 if (ok && sts != RMS$_NMF && 13313 sts != RMS$_DNF && sts != RMS_FNF) ok = 0; 13314 if (!ok) { 13315 if (!(sts & 1)) { 13316 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts); 13317 } 13318 PerlIO_close(tmpfp); 13319 fp = NULL; 13320 } 13321 else { 13322 PerlIO_rewind(tmpfp); 13323 IoTYPE(io) = IoTYPE_RDONLY; 13324 IoIFP(io) = fp = tmpfp; 13325 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */ 13326 } 13327 } 13328 Safefree(vmsspec); 13329 Safefree(rslt); 13330 return fp; 13331 } 13332 13333 13334 static char * 13335 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, 13336 int *utf8_fl); 13337 13338 void 13339 unixrealpath_fromperl(pTHX_ CV *cv) 13340 { 13341 dXSARGS; 13342 char *fspec, *rslt_spec, *rslt; 13343 STRLEN n_a; 13344 13345 if (!items || items != 1) 13346 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)"); 13347 13348 fspec = SvPV(ST(0),n_a); 13349 if (!fspec || !*fspec) XSRETURN_UNDEF; 13350 13351 Newx(rslt_spec, VMS_MAXRSS + 1, char); 13352 rslt = do_vms_realpath(fspec, rslt_spec, NULL); 13353 13354 ST(0) = sv_newmortal(); 13355 if (rslt != NULL) 13356 sv_usepvn(ST(0),rslt,strlen(rslt)); 13357 else 13358 Safefree(rslt_spec); 13359 XSRETURN(1); 13360 } 13361 13362 static char * 13363 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec, 13364 int *utf8_fl); 13365 13366 void 13367 vmsrealpath_fromperl(pTHX_ CV *cv) 13368 { 13369 dXSARGS; 13370 char *fspec, *rslt_spec, *rslt; 13371 STRLEN n_a; 13372 13373 if (!items || items != 1) 13374 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)"); 13375 13376 fspec = SvPV(ST(0),n_a); 13377 if (!fspec || !*fspec) XSRETURN_UNDEF; 13378 13379 Newx(rslt_spec, VMS_MAXRSS + 1, char); 13380 rslt = do_vms_realname(fspec, rslt_spec, NULL); 13381 13382 ST(0) = sv_newmortal(); 13383 if (rslt != NULL) 13384 sv_usepvn(ST(0),rslt,strlen(rslt)); 13385 else 13386 Safefree(rslt_spec); 13387 XSRETURN(1); 13388 } 13389 13390 #ifdef HAS_SYMLINK 13391 /* 13392 * A thin wrapper around decc$symlink to make sure we follow the 13393 * standard and do not create a symlink with a zero-length name, 13394 * and convert the target to Unix format, as the CRTL can't handle 13395 * targets in VMS format. 13396 */ 13397 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/ 13398 int 13399 Perl_my_symlink(pTHX_ const char *contents, const char *link_name) 13400 { 13401 int sts; 13402 char * utarget; 13403 13404 if (!link_name || !*link_name) { 13405 SETERRNO(ENOENT, SS$_NOSUCHFILE); 13406 return -1; 13407 } 13408 13409 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1); 13410 /* An untranslatable filename should be passed through. */ 13411 (void) int_tounixspec(contents, utarget, NULL); 13412 sts = symlink(utarget, link_name); 13413 PerlMem_free(utarget); 13414 return sts; 13415 } 13416 /*}}}*/ 13417 13418 #endif /* HAS_SYMLINK */ 13419 13420 int do_vms_case_tolerant(void); 13421 13422 void 13423 case_tolerant_process_fromperl(pTHX_ CV *cv) 13424 { 13425 dXSARGS; 13426 ST(0) = boolSV(do_vms_case_tolerant()); 13427 XSRETURN(1); 13428 } 13429 13430 #ifdef USE_ITHREADS 13431 13432 void 13433 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, 13434 struct interp_intern *dst) 13435 { 13436 PERL_ARGS_ASSERT_SYS_INTERN_DUP; 13437 13438 memcpy(dst,src,sizeof(struct interp_intern)); 13439 } 13440 13441 #endif 13442 13443 void 13444 Perl_sys_intern_clear(pTHX) 13445 { 13446 } 13447 13448 void 13449 Perl_sys_intern_init(pTHX) 13450 { 13451 unsigned int ix = RAND_MAX; 13452 double x; 13453 13454 VMSISH_HUSHED = 0; 13455 13456 MY_POSIX_EXIT = vms_posix_exit; 13457 13458 x = (float)ix; 13459 MY_INV_RAND_MAX = 1./x; 13460 } 13461 13462 void 13463 init_os_extras(void) 13464 { 13465 dTHX; 13466 char* file = __FILE__; 13467 if (decc_disable_to_vms_logname_translation) { 13468 no_translate_barewords = TRUE; 13469 } else { 13470 no_translate_barewords = FALSE; 13471 } 13472 13473 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$"); 13474 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$"); 13475 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$"); 13476 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$"); 13477 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$"); 13478 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$"); 13479 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$"); 13480 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$"); 13481 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$"); 13482 newXS("File::Copy::rmscopy",rmscopy_fromperl,file); 13483 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$"); 13484 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$"); 13485 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$"); 13486 newXSproto("VMS::Filespec::case_tolerant_process", 13487 case_tolerant_process_fromperl,file,""); 13488 13489 store_pipelocs(aTHX); /* will redo any earlier attempts */ 13490 13491 return; 13492 } 13493 13494 #if __CRTL_VER == 80200000 13495 /* This missed getting in to the DECC SDK for 8.2 */ 13496 char *realpath(const char *file_name, char * resolved_name, ...); 13497 #endif 13498 13499 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/ 13500 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK. 13501 * The perl fallback routine to provide realpath() is not as efficient 13502 * on OpenVMS. 13503 */ 13504 13505 #ifdef __cplusplus 13506 extern "C" { 13507 #endif 13508 13509 /* Hack, use old stat() as fastest way of getting ino_t and device */ 13510 int decc$stat(const char *name, void * statbuf); 13511 #if !defined(__VAX) && __CRTL_VER >= 80200000 13512 int decc$lstat(const char *name, void * statbuf); 13513 #else 13514 #define decc$lstat decc$stat 13515 #endif 13516 13517 #ifdef __cplusplus 13518 } 13519 #endif 13520 13521 13522 /* Realpath is fragile. In 8.3 it does not work if the feature 13523 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic 13524 * links are implemented in RMS, not the CRTL. It also can fail if the 13525 * user does not have read/execute access to some of the directories. 13526 * So in order for Do What I Mean mode to work, if realpath() fails, 13527 * fall back to looking up the filename by the device name and FID. 13528 */ 13529 13530 int vms_fid_to_name(char * outname, int outlen, 13531 const char * name, int lstat_flag, mode_t * mode) 13532 { 13533 #pragma message save 13534 #pragma message disable MISALGNDSTRCT 13535 #pragma message disable MISALGNDMEM 13536 #pragma member_alignment save 13537 #pragma nomember_alignment 13538 struct statbuf_t { 13539 char * st_dev; 13540 unsigned short st_ino[3]; 13541 unsigned short old_st_mode; 13542 unsigned long padl[30]; /* plenty of room */ 13543 } statbuf; 13544 #pragma message restore 13545 #pragma member_alignment restore 13546 13547 int sts; 13548 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13549 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13550 char *fileified; 13551 char *temp_fspec; 13552 char *ret_spec; 13553 13554 /* Need to follow the mostly the same rules as flex_stat_int, or we may get 13555 * unexpected answers 13556 */ 13557 13558 fileified = (char *)PerlMem_malloc(VMS_MAXRSS); 13559 if (fileified == NULL) 13560 _ckvmssts_noperl(SS$_INSFMEM); 13561 13562 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS); 13563 if (temp_fspec == NULL) 13564 _ckvmssts_noperl(SS$_INSFMEM); 13565 13566 sts = -1; 13567 /* First need to try as a directory */ 13568 ret_spec = int_tovmspath(name, temp_fspec, NULL); 13569 if (ret_spec != NULL) { 13570 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL); 13571 if (ret_spec != NULL) { 13572 if (lstat_flag == 0) 13573 sts = decc$stat(fileified, &statbuf); 13574 else 13575 sts = decc$lstat(fileified, &statbuf); 13576 } 13577 } 13578 13579 /* Then as a VMS file spec */ 13580 if (sts != 0) { 13581 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL); 13582 if (ret_spec != NULL) { 13583 if (lstat_flag == 0) { 13584 sts = decc$stat(temp_fspec, &statbuf); 13585 } else { 13586 sts = decc$lstat(temp_fspec, &statbuf); 13587 } 13588 } 13589 } 13590 13591 if (sts) { 13592 /* Next try - allow multiple dots with out EFS CHARSET */ 13593 /* The CRTL stat() falls down hard on multi-dot filenames in unix 13594 * format unless * DECC$EFS_CHARSET is in effect, so temporarily 13595 * enable it if it isn't already. 13596 */ 13597 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13598 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 13599 decc$feature_set_value(decc_efs_charset_index, 1, 1); 13600 #endif 13601 ret_spec = int_tovmspath(name, temp_fspec, NULL); 13602 if (lstat_flag == 0) { 13603 sts = decc$stat(name, &statbuf); 13604 } else { 13605 sts = decc$lstat(name, &statbuf); 13606 } 13607 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13608 if (!decc_efs_charset && (decc_efs_charset_index > 0)) 13609 decc$feature_set_value(decc_efs_charset_index, 1, 0); 13610 #endif 13611 } 13612 13613 13614 /* and then because the Perl Unix to VMS conversion is not perfect */ 13615 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */ 13616 /* characters from filenames so we need to try it as-is */ 13617 if (sts) { 13618 if (lstat_flag == 0) { 13619 sts = decc$stat(name, &statbuf); 13620 } else { 13621 sts = decc$lstat(name, &statbuf); 13622 } 13623 } 13624 13625 if (sts == 0) { 13626 int vms_sts; 13627 13628 dvidsc.dsc$a_pointer=statbuf.st_dev; 13629 dvidsc.dsc$w_length=strlen(statbuf.st_dev); 13630 13631 specdsc.dsc$a_pointer = outname; 13632 specdsc.dsc$w_length = outlen-1; 13633 13634 vms_sts = lib$fid_to_name 13635 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length); 13636 if ($VMS_STATUS_SUCCESS(vms_sts)) { 13637 outname[specdsc.dsc$w_length] = 0; 13638 13639 /* Return the mode */ 13640 if (mode) { 13641 *mode = statbuf.old_st_mode; 13642 } 13643 } 13644 } 13645 PerlMem_free(temp_fspec); 13646 PerlMem_free(fileified); 13647 return sts; 13648 } 13649 13650 13651 13652 static char * 13653 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, 13654 int *utf8_fl) 13655 { 13656 char * rslt = NULL; 13657 13658 #ifdef HAS_SYMLINK 13659 if (decc_posix_compliant_pathnames > 0 ) { 13660 /* realpath currently only works if posix compliant pathnames are 13661 * enabled. It may start working when they are not, but in that 13662 * case we still want the fallback behavior for backwards compatibility 13663 */ 13664 rslt = realpath(filespec, outbuf); 13665 } 13666 #endif 13667 13668 if (rslt == NULL) { 13669 char * vms_spec; 13670 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13671 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13672 mode_t my_mode; 13673 13674 /* Fall back to fid_to_name */ 13675 13676 Newx(vms_spec, VMS_MAXRSS + 1, char); 13677 13678 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode); 13679 if (sts == 0) { 13680 13681 13682 /* Now need to trim the version off */ 13683 sts = vms_split_path 13684 (vms_spec, 13685 &v_spec, 13686 &v_len, 13687 &r_spec, 13688 &r_len, 13689 &d_spec, 13690 &d_len, 13691 &n_spec, 13692 &n_len, 13693 &e_spec, 13694 &e_len, 13695 &vs_spec, 13696 &vs_len); 13697 13698 13699 if (sts == 0) { 13700 int haslower = 0; 13701 const char *cp; 13702 13703 /* Trim off the version */ 13704 int file_len = v_len + r_len + d_len + n_len + e_len; 13705 vms_spec[file_len] = 0; 13706 13707 /* Trim off the .DIR if this is a directory */ 13708 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) { 13709 if (S_ISDIR(my_mode)) { 13710 e_len = 0; 13711 e_spec[0] = 0; 13712 } 13713 } 13714 13715 /* Drop NULL extensions on UNIX file specification */ 13716 if ((e_len == 1) && decc_readdir_dropdotnotype) { 13717 e_len = 0; 13718 e_spec[0] = '\0'; 13719 } 13720 13721 /* The result is expected to be in UNIX format */ 13722 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl); 13723 13724 /* Downcase if input had any lower case letters and 13725 * case preservation is not in effect. 13726 */ 13727 if (!decc_efs_case_preserve) { 13728 for (cp = filespec; *cp; cp++) 13729 if (islower(*cp)) { haslower = 1; break; } 13730 13731 if (haslower) __mystrtolower(rslt); 13732 } 13733 } 13734 } else { 13735 13736 /* Now for some hacks to deal with backwards and forward */ 13737 /* compatibility */ 13738 if (!decc_efs_charset) { 13739 13740 /* 1. ODS-2 mode wants to do a syntax only translation */ 13741 rslt = int_rmsexpand(filespec, outbuf, 13742 NULL, 0, NULL, utf8_fl); 13743 13744 } else { 13745 if (decc_filename_unix_report) { 13746 char * dir_name; 13747 char * vms_dir_name; 13748 char * file_name; 13749 13750 /* 2. ODS-5 / UNIX report mode should return a failure */ 13751 /* if the parent directory also does not exist */ 13752 /* Otherwise, get the real path for the parent */ 13753 /* and add the child to it. */ 13754 13755 /* basename / dirname only available for VMS 7.0+ */ 13756 /* So we may need to implement them as common routines */ 13757 13758 Newx(dir_name, VMS_MAXRSS + 1, char); 13759 Newx(vms_dir_name, VMS_MAXRSS + 1, char); 13760 dir_name[0] = '\0'; 13761 file_name = NULL; 13762 13763 /* First try a VMS parse */ 13764 sts = vms_split_path 13765 (filespec, 13766 &v_spec, 13767 &v_len, 13768 &r_spec, 13769 &r_len, 13770 &d_spec, 13771 &d_len, 13772 &n_spec, 13773 &n_len, 13774 &e_spec, 13775 &e_len, 13776 &vs_spec, 13777 &vs_len); 13778 13779 if (sts == 0) { 13780 /* This is VMS */ 13781 13782 int dir_len = v_len + r_len + d_len + n_len; 13783 if (dir_len > 0) { 13784 memcpy(dir_name, filespec, dir_len); 13785 dir_name[dir_len] = '\0'; 13786 file_name = (char *)&filespec[dir_len + 1]; 13787 } 13788 } else { 13789 /* This must be UNIX */ 13790 char * tchar; 13791 13792 tchar = strrchr(filespec, '/'); 13793 13794 if (tchar != NULL) { 13795 int dir_len = tchar - filespec; 13796 memcpy(dir_name, filespec, dir_len); 13797 dir_name[dir_len] = '\0'; 13798 file_name = (char *) &filespec[dir_len + 1]; 13799 } 13800 } 13801 13802 /* Dir name is defaulted */ 13803 if (dir_name[0] == 0) { 13804 dir_name[0] = '.'; 13805 dir_name[1] = '\0'; 13806 } 13807 13808 /* Need realpath for the directory */ 13809 sts = vms_fid_to_name(vms_dir_name, 13810 VMS_MAXRSS + 1, 13811 dir_name, 0, NULL); 13812 13813 if (sts == 0) { 13814 /* Now need to pathify it. */ 13815 char *tdir = int_pathify_dirspec(vms_dir_name, 13816 outbuf); 13817 13818 /* And now add the original filespec to it */ 13819 if (file_name != NULL) { 13820 my_strlcat(outbuf, file_name, VMS_MAXRSS); 13821 } 13822 return outbuf; 13823 } 13824 Safefree(vms_dir_name); 13825 Safefree(dir_name); 13826 } 13827 } 13828 } 13829 Safefree(vms_spec); 13830 } 13831 return rslt; 13832 } 13833 13834 static char * 13835 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf, 13836 int *utf8_fl) 13837 { 13838 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13839 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13840 13841 /* Fall back to fid_to_name */ 13842 13843 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL); 13844 if (sts != 0) { 13845 return NULL; 13846 } 13847 else { 13848 13849 13850 /* Now need to trim the version off */ 13851 sts = vms_split_path 13852 (outbuf, 13853 &v_spec, 13854 &v_len, 13855 &r_spec, 13856 &r_len, 13857 &d_spec, 13858 &d_len, 13859 &n_spec, 13860 &n_len, 13861 &e_spec, 13862 &e_len, 13863 &vs_spec, 13864 &vs_len); 13865 13866 13867 if (sts == 0) { 13868 int haslower = 0; 13869 const char *cp; 13870 13871 /* Trim off the version */ 13872 int file_len = v_len + r_len + d_len + n_len + e_len; 13873 outbuf[file_len] = 0; 13874 13875 /* Downcase if input had any lower case letters and 13876 * case preservation is not in effect. 13877 */ 13878 if (!decc_efs_case_preserve) { 13879 for (cp = filespec; *cp; cp++) 13880 if (islower(*cp)) { haslower = 1; break; } 13881 13882 if (haslower) __mystrtolower(outbuf); 13883 } 13884 } 13885 } 13886 return outbuf; 13887 } 13888 13889 13890 /*}}}*/ 13891 /* External entry points */ 13892 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13893 { return do_vms_realpath(filespec, outbuf, utf8_fl); } 13894 13895 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13896 { return do_vms_realname(filespec, outbuf, utf8_fl); } 13897 13898 /* case_tolerant */ 13899 13900 /*{{{int do_vms_case_tolerant(void)*/ 13901 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is 13902 * controlled by a process setting. 13903 */ 13904 int do_vms_case_tolerant(void) 13905 { 13906 return vms_process_case_tolerant; 13907 } 13908 /*}}}*/ 13909 /* External entry points */ 13910 #if __CRTL_VER >= 70301000 && !defined(__VAX) 13911 int Perl_vms_case_tolerant(void) 13912 { return do_vms_case_tolerant(); } 13913 #else 13914 int Perl_vms_case_tolerant(void) 13915 { return vms_process_case_tolerant; } 13916 #endif 13917 13918 13919 /* Start of DECC RTL Feature handling */ 13920 13921 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13922 13923 static int 13924 set_feature_default(const char *name, int value) 13925 { 13926 int status; 13927 int index; 13928 char val_str[10]; 13929 13930 /* If the feature has been explicitly disabled in the environment, 13931 * then don't enable it here. 13932 */ 13933 if (value > 0) { 13934 status = simple_trnlnm(name, val_str, sizeof(val_str)); 13935 if (status) { 13936 val_str[0] = _toupper(val_str[0]); 13937 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F') 13938 return 0; 13939 } 13940 } 13941 13942 index = decc$feature_get_index(name); 13943 13944 status = decc$feature_set_value(index, 1, value); 13945 if (index == -1 || (status == -1)) { 13946 return -1; 13947 } 13948 13949 status = decc$feature_get_value(index, 1); 13950 if (status != value) { 13951 return -1; 13952 } 13953 13954 /* Various things may check for an environment setting 13955 * rather than the feature directly, so set that too. 13956 */ 13957 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE"); 13958 13959 return 0; 13960 } 13961 #endif 13962 13963 13964 /* C RTL Feature settings */ 13965 13966 #if defined(__DECC) || defined(__DECCXX) 13967 13968 #ifdef __cplusplus 13969 extern "C" { 13970 #endif 13971 13972 extern void 13973 vmsperl_set_features(void) 13974 { 13975 int status; 13976 int s; 13977 char val_str[10]; 13978 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX) 13979 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM; 13980 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE; 13981 unsigned long case_perm; 13982 unsigned long case_image; 13983 #endif 13984 13985 /* Allow an exception to bring Perl into the VMS debugger */ 13986 vms_debug_on_exception = 0; 13987 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str)); 13988 if (status) { 13989 val_str[0] = _toupper(val_str[0]); 13990 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13991 vms_debug_on_exception = 1; 13992 else 13993 vms_debug_on_exception = 0; 13994 } 13995 13996 /* Debug unix/vms file translation routines */ 13997 vms_debug_fileify = 0; 13998 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str)); 13999 if (status) { 14000 val_str[0] = _toupper(val_str[0]); 14001 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14002 vms_debug_fileify = 1; 14003 else 14004 vms_debug_fileify = 0; 14005 } 14006 14007 14008 /* Historically PERL has been doing vmsify / stat differently than */ 14009 /* the CRTL. In particular, under some conditions the CRTL will */ 14010 /* remove some illegal characters like spaces from filenames */ 14011 /* resulting in some differences. The stat()/lstat() wrapper has */ 14012 /* been reporting such file names as invalid and fails to stat them */ 14013 /* fixing this bug so that stat()/lstat() accept these like the */ 14014 /* CRTL does will result in several tests failing. */ 14015 /* This should really be fixed, but for now, set up a feature to */ 14016 /* enable it so that the impact can be studied. */ 14017 vms_bug_stat_filename = 0; 14018 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str)); 14019 if (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_bug_stat_filename = 1; 14023 else 14024 vms_bug_stat_filename = 0; 14025 } 14026 14027 14028 /* Create VTF-7 filenames from Unicode instead of UTF-8 */ 14029 vms_vtf7_filenames = 0; 14030 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str)); 14031 if (status) { 14032 val_str[0] = _toupper(val_str[0]); 14033 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14034 vms_vtf7_filenames = 1; 14035 else 14036 vms_vtf7_filenames = 0; 14037 } 14038 14039 /* unlink all versions on unlink() or rename() */ 14040 vms_unlink_all_versions = 0; 14041 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str)); 14042 if (status) { 14043 val_str[0] = _toupper(val_str[0]); 14044 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14045 vms_unlink_all_versions = 1; 14046 else 14047 vms_unlink_all_versions = 0; 14048 } 14049 14050 #if __CRTL_VER >= 70300000 && !defined(__VAX) 14051 /* Detect running under GNV Bash or other UNIX like shell */ 14052 gnv_unix_shell = 0; 14053 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str)); 14054 if (status) { 14055 gnv_unix_shell = 1; 14056 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1); 14057 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1); 14058 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1); 14059 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0); 14060 vms_unlink_all_versions = 1; 14061 vms_posix_exit = 1; 14062 } 14063 /* Some reasonable defaults that are not CRTL defaults */ 14064 set_feature_default("DECC$EFS_CASE_PRESERVE", 1); 14065 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */ 14066 set_feature_default("DECC$EFS_CHARSET", 1); 14067 #endif 14068 14069 /* hacks to see if known bugs are still present for testing */ 14070 14071 /* PCP mode requires creating /dev/null special device file */ 14072 decc_bug_devnull = 0; 14073 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str)); 14074 if (status) { 14075 val_str[0] = _toupper(val_str[0]); 14076 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14077 decc_bug_devnull = 1; 14078 else 14079 decc_bug_devnull = 0; 14080 } 14081 14082 #if __CRTL_VER >= 70300000 && !defined(__VAX) 14083 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION"); 14084 if (s >= 0) { 14085 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1); 14086 if (decc_disable_to_vms_logname_translation < 0) 14087 decc_disable_to_vms_logname_translation = 0; 14088 } 14089 14090 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE"); 14091 if (s >= 0) { 14092 decc_efs_case_preserve = decc$feature_get_value(s, 1); 14093 if (decc_efs_case_preserve < 0) 14094 decc_efs_case_preserve = 0; 14095 } 14096 14097 s = decc$feature_get_index("DECC$EFS_CHARSET"); 14098 decc_efs_charset_index = s; 14099 if (s >= 0) { 14100 decc_efs_charset = decc$feature_get_value(s, 1); 14101 if (decc_efs_charset < 0) 14102 decc_efs_charset = 0; 14103 } 14104 14105 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT"); 14106 if (s >= 0) { 14107 decc_filename_unix_report = decc$feature_get_value(s, 1); 14108 if (decc_filename_unix_report > 0) { 14109 decc_filename_unix_report = 1; 14110 vms_posix_exit = 1; 14111 } 14112 else 14113 decc_filename_unix_report = 0; 14114 } 14115 14116 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY"); 14117 if (s >= 0) { 14118 decc_filename_unix_only = decc$feature_get_value(s, 1); 14119 if (decc_filename_unix_only > 0) { 14120 decc_filename_unix_only = 1; 14121 } 14122 else { 14123 decc_filename_unix_only = 0; 14124 } 14125 } 14126 14127 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION"); 14128 if (s >= 0) { 14129 decc_filename_unix_no_version = decc$feature_get_value(s, 1); 14130 if (decc_filename_unix_no_version < 0) 14131 decc_filename_unix_no_version = 0; 14132 } 14133 14134 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE"); 14135 if (s >= 0) { 14136 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1); 14137 if (decc_readdir_dropdotnotype < 0) 14138 decc_readdir_dropdotnotype = 0; 14139 } 14140 14141 #if __CRTL_VER >= 80200000 14142 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES"); 14143 if (s >= 0) { 14144 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1); 14145 if (decc_posix_compliant_pathnames < 0) 14146 decc_posix_compliant_pathnames = 0; 14147 if (decc_posix_compliant_pathnames > 4) 14148 decc_posix_compliant_pathnames = 0; 14149 } 14150 14151 #endif 14152 #else 14153 status = simple_trnlnm 14154 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str)); 14155 if (status) { 14156 val_str[0] = _toupper(val_str[0]); 14157 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14158 decc_disable_to_vms_logname_translation = 1; 14159 } 14160 } 14161 14162 #ifndef __VAX 14163 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str)); 14164 if (status) { 14165 val_str[0] = _toupper(val_str[0]); 14166 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14167 decc_efs_case_preserve = 1; 14168 } 14169 } 14170 #endif 14171 14172 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str)); 14173 if (status) { 14174 val_str[0] = _toupper(val_str[0]); 14175 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14176 decc_filename_unix_report = 1; 14177 } 14178 } 14179 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str)); 14180 if (status) { 14181 val_str[0] = _toupper(val_str[0]); 14182 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14183 decc_filename_unix_only = 1; 14184 decc_filename_unix_report = 1; 14185 } 14186 } 14187 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str)); 14188 if (status) { 14189 val_str[0] = _toupper(val_str[0]); 14190 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14191 decc_filename_unix_no_version = 1; 14192 } 14193 } 14194 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str)); 14195 if (status) { 14196 val_str[0] = _toupper(val_str[0]); 14197 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 14198 decc_readdir_dropdotnotype = 1; 14199 } 14200 } 14201 #endif 14202 14203 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX) 14204 14205 /* Report true case tolerance */ 14206 /*----------------------------*/ 14207 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0); 14208 if (!$VMS_STATUS_SUCCESS(status)) 14209 case_perm = PPROP$K_CASE_BLIND; 14210 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0); 14211 if (!$VMS_STATUS_SUCCESS(status)) 14212 case_image = PPROP$K_CASE_BLIND; 14213 if ((case_perm == PPROP$K_CASE_SENSITIVE) || 14214 (case_image == PPROP$K_CASE_SENSITIVE)) 14215 vms_process_case_tolerant = 0; 14216 14217 #endif 14218 14219 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */ 14220 /* for strict backward compatibility */ 14221 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str)); 14222 if (status) { 14223 val_str[0] = _toupper(val_str[0]); 14224 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 14225 vms_posix_exit = 1; 14226 else 14227 vms_posix_exit = 0; 14228 } 14229 } 14230 14231 /* Use 32-bit pointers because that's what the image activator 14232 * assumes for the LIB$INITIALZE psect. 14233 */ 14234 #if __INITIAL_POINTER_SIZE 14235 #pragma pointer_size save 14236 #pragma pointer_size 32 14237 #endif 14238 14239 /* Create a reference to the LIB$INITIALIZE function. */ 14240 extern void LIB$INITIALIZE(void); 14241 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE; 14242 14243 /* Create an array of pointers to the init functions in the special 14244 * LIB$INITIALIZE section. In our case, the array only has one entry. 14245 */ 14246 #pragma extern_model save 14247 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long 14248 extern void (* const vmsperl_unused_global_2[])() = 14249 { 14250 vmsperl_set_features, 14251 }; 14252 #pragma extern_model restore 14253 14254 #if __INITIAL_POINTER_SIZE 14255 #pragma pointer_size restore 14256 #endif 14257 14258 #ifdef __cplusplus 14259 } 14260 #endif 14261 14262 #endif /* defined(__DECC) || defined(__DECCXX) */ 14263 /* End of vms.c */ 14264