1 /* vms.c 2 * 3 * VMS-specific routines for perl5 4 * 5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others. 7 * 8 * You may distribute under the terms of either the GNU General Public 9 * License or the Artistic License, as specified in the README file. 10 * 11 * Please see Changes*.* or the Perl Repository Browser for revision history. 12 */ 13 14 /* 15 * Yet small as was their hunted band 16 * still fell and fearless was each hand, 17 * and strong deeds they wrought yet oft, 18 * and loved the woods, whose ways more soft 19 * them seemed than thralls of that black throne 20 * to live and languish in halls of stone. 21 * 22 * The Lay of Leithian, 135-40 23 */ 24 25 #include <acedef.h> 26 #include <acldef.h> 27 #include <armdef.h> 28 #include <atrdef.h> 29 #include <chpdef.h> 30 #include <clidef.h> 31 #include <climsgdef.h> 32 #include <dcdef.h> 33 #include <descrip.h> 34 #include <devdef.h> 35 #include <dvidef.h> 36 #include <fibdef.h> 37 #include <float.h> 38 #include <fscndef.h> 39 #include <iodef.h> 40 #include <jpidef.h> 41 #include <kgbdef.h> 42 #include <libclidef.h> 43 #include <libdef.h> 44 #include <lib$routines.h> 45 #include <lnmdef.h> 46 #include <msgdef.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 <rmsdef.h> 64 #include <smgdef.h> 65 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */ 66 #include <efndef.h> 67 #define NO_EFN EFN$C_ENF 68 #else 69 #define NO_EFN 0; 70 #endif 71 72 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000 73 int decc$feature_get_index(const char *name); 74 char* decc$feature_get_name(int index); 75 int decc$feature_get_value(int index, int mode); 76 int decc$feature_set_value(int index, int mode, int value); 77 #else 78 #include <unixlib.h> 79 #endif 80 81 #pragma member_alignment save 82 #pragma nomember_alignment longword 83 struct item_list_3 { 84 unsigned short len; 85 unsigned short code; 86 void * bufadr; 87 unsigned short * retadr; 88 }; 89 #pragma member_alignment restore 90 91 /* More specific prototype than in starlet_c.h makes programming errors 92 more visible. 93 */ 94 #ifdef sys$getdviw 95 #undef sys$getdviw 96 int sys$getdviw 97 (unsigned long efn, 98 unsigned short chan, 99 const struct dsc$descriptor_s * devnam, 100 const struct item_list_3 * itmlst, 101 void * iosb, 102 void * (astadr)(unsigned long), 103 void * astprm, 104 void * nullarg); 105 #endif 106 107 #ifdef sys$get_security 108 #undef sys$get_security 109 int sys$get_security 110 (const struct dsc$descriptor_s * clsnam, 111 const struct dsc$descriptor_s * objnam, 112 const unsigned int *objhan, 113 unsigned int flags, 114 const struct item_list_3 * itmlst, 115 unsigned int * contxt, 116 const unsigned int * acmode); 117 #endif 118 119 #ifdef sys$set_security 120 #undef sys$set_security 121 int sys$set_security 122 (const struct dsc$descriptor_s * clsnam, 123 const struct dsc$descriptor_s * objnam, 124 const unsigned int *objhan, 125 unsigned int flags, 126 const struct item_list_3 * itmlst, 127 unsigned int * contxt, 128 const unsigned int * acmode); 129 #endif 130 131 #ifdef lib$find_image_symbol 132 #undef lib$find_image_symbol 133 int lib$find_image_symbol 134 (const struct dsc$descriptor_s * imgname, 135 const struct dsc$descriptor_s * symname, 136 void * symval, 137 const struct dsc$descriptor_s * defspec, 138 unsigned long flag); 139 #endif 140 141 #ifdef lib$rename_file 142 #undef lib$rename_file 143 int lib$rename_file 144 (const struct dsc$descriptor_s * old_file_dsc, 145 const struct dsc$descriptor_s * new_file_dsc, 146 const struct dsc$descriptor_s * default_file_dsc, 147 const struct dsc$descriptor_s * related_file_dsc, 148 const unsigned long * flags, 149 void * (success)(const struct dsc$descriptor_s * old_dsc, 150 const struct dsc$descriptor_s * new_dsc, 151 const void *), 152 void * (error)(const struct dsc$descriptor_s * old_dsc, 153 const struct dsc$descriptor_s * new_dsc, 154 const int * rms_sts, 155 const int * rms_stv, 156 const int * error_src, 157 const void * usr_arg), 158 int (confirm)(const struct dsc$descriptor_s * old_dsc, 159 const struct dsc$descriptor_s * new_dsc, 160 const void * old_fab, 161 const void * usr_arg), 162 void * user_arg, 163 struct dsc$descriptor_s * old_result_name_dsc, 164 struct dsc$descriptor_s * new_result_name_dsc, 165 unsigned long * file_scan_context); 166 #endif 167 168 #if __CRTL_VER >= 70300000 && !defined(__VAX) 169 170 static int set_feature_default(const char *name, int value) 171 { 172 int status; 173 int index; 174 175 index = decc$feature_get_index(name); 176 177 status = decc$feature_set_value(index, 1, value); 178 if (index == -1 || (status == -1)) { 179 return -1; 180 } 181 182 status = decc$feature_get_value(index, 1); 183 if (status != value) { 184 return -1; 185 } 186 187 return 0; 188 } 189 #endif 190 191 /* Older versions of ssdef.h don't have these */ 192 #ifndef SS$_INVFILFOROP 193 # define SS$_INVFILFOROP 3930 194 #endif 195 #ifndef SS$_NOSUCHOBJECT 196 # define SS$_NOSUCHOBJECT 2696 197 #endif 198 199 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */ 200 #define PERLIO_NOT_STDIO 0 201 202 /* Don't replace system definitions of vfork, getenv, lstat, and stat, 203 * code below needs to get to the underlying CRTL routines. */ 204 #define DONT_MASK_RTL_CALLS 205 #include "EXTERN.h" 206 #include "perl.h" 207 #include "XSUB.h" 208 /* Anticipating future expansion in lexical warnings . . . */ 209 #ifndef WARN_INTERNAL 210 # define WARN_INTERNAL WARN_MISC 211 #endif 212 213 #ifdef VMS_LONGNAME_SUPPORT 214 #include <libfildef.h> 215 #endif 216 217 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000 218 # define RTL_USES_UTC 1 219 #endif 220 221 /* Routine to create a decterm for use with the Perl debugger */ 222 /* No headers, this information was found in the Programming Concepts Manual */ 223 224 static int (*decw_term_port) 225 (const struct dsc$descriptor_s * display, 226 const struct dsc$descriptor_s * setup_file, 227 const struct dsc$descriptor_s * customization, 228 struct dsc$descriptor_s * result_device_name, 229 unsigned short * result_device_name_length, 230 void * controller, 231 void * char_buffer, 232 void * char_change_buffer) = 0; 233 234 /* gcc's header files don't #define direct access macros 235 * corresponding to VAXC's variant structs */ 236 #ifdef __GNUC__ 237 # define uic$v_format uic$r_uic_form.uic$v_format 238 # define uic$v_group uic$r_uic_form.uic$v_group 239 # define uic$v_member uic$r_uic_form.uic$v_member 240 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass 241 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv 242 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall 243 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv 244 #endif 245 246 #if defined(NEED_AN_H_ERRNO) 247 dEXT int h_errno; 248 #endif 249 250 #ifdef __DECC 251 #pragma message disable pragma 252 #pragma member_alignment save 253 #pragma nomember_alignment longword 254 #pragma message save 255 #pragma message disable misalgndmem 256 #endif 257 struct itmlst_3 { 258 unsigned short int buflen; 259 unsigned short int itmcode; 260 void *bufadr; 261 unsigned short int *retlen; 262 }; 263 264 struct filescan_itmlst_2 { 265 unsigned short length; 266 unsigned short itmcode; 267 char * component; 268 }; 269 270 struct vs_str_st { 271 unsigned short length; 272 char str[65536]; 273 }; 274 275 #ifdef __DECC 276 #pragma message restore 277 #pragma member_alignment restore 278 #endif 279 280 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d) 281 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d) 282 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d) 283 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d) 284 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g) 285 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c) 286 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c) 287 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d) 288 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d) 289 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a) 290 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d) 291 #define getredirection(a,b) mp_getredirection(aTHX_ a,b) 292 293 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *); 294 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *); 295 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 296 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *); 297 298 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */ 299 #define PERL_LNM_MAX_ALLOWED_INDEX 127 300 301 /* OpenVMS User's Guide says at least 9 iterative translations will be performed, 302 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for 303 * the Perl facility. 304 */ 305 #define PERL_LNM_MAX_ITER 10 306 307 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */ 308 #if __CRTL_VER >= 70302000 && !defined(__VAX) 309 #define MAX_DCL_SYMBOL (8192) 310 #define MAX_DCL_LINE_LENGTH (4096 - 4) 311 #else 312 #define MAX_DCL_SYMBOL (1024) 313 #define MAX_DCL_LINE_LENGTH (1024 - 4) 314 #endif 315 316 static char *__mystrtolower(char *str) 317 { 318 if (str) for (; *str; ++str) *str= tolower(*str); 319 return str; 320 } 321 322 static struct dsc$descriptor_s fildevdsc = 323 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" }; 324 static struct dsc$descriptor_s crtlenvdsc = 325 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" }; 326 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL }; 327 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL }; 328 static struct dsc$descriptor_s **env_tables = defenv; 329 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */ 330 331 /* True if we shouldn't treat barewords as logicals during directory */ 332 /* munching */ 333 static int no_translate_barewords; 334 335 #ifndef RTL_USES_UTC 336 static int tz_updated = 1; 337 #endif 338 339 /* DECC Features that may need to affect how Perl interprets 340 * displays filename information 341 */ 342 static int decc_disable_to_vms_logname_translation = 1; 343 static int decc_disable_posix_root = 1; 344 int decc_efs_case_preserve = 0; 345 static int decc_efs_charset = 0; 346 static int decc_filename_unix_no_version = 0; 347 static int decc_filename_unix_only = 0; 348 int decc_filename_unix_report = 0; 349 int decc_posix_compliant_pathnames = 0; 350 int decc_readdir_dropdotnotype = 0; 351 static int vms_process_case_tolerant = 1; 352 int vms_vtf7_filenames = 0; 353 int gnv_unix_shell = 0; 354 static int vms_unlink_all_versions = 0; 355 356 /* bug workarounds if needed */ 357 int decc_bug_readdir_efs1 = 0; 358 int decc_bug_devnull = 1; 359 int decc_bug_fgetname = 0; 360 int decc_dir_barename = 0; 361 362 static int vms_debug_on_exception = 0; 363 364 /* Is this a UNIX file specification? 365 * No longer a simple check with EFS file specs 366 * For now, not a full check, but need to 367 * handle POSIX ^UP^ specifications 368 * Fixing to handle ^/ cases would require 369 * changes to many other conversion routines. 370 */ 371 372 static int is_unix_filespec(const char *path) 373 { 374 int ret_val; 375 const char * pch1; 376 377 ret_val = 0; 378 if (strncmp(path,"\"^UP^",5) != 0) { 379 pch1 = strchr(path, '/'); 380 if (pch1 != NULL) 381 ret_val = 1; 382 else { 383 384 /* If the user wants UNIX files, "." needs to be treated as in UNIX */ 385 if (decc_filename_unix_report || decc_filename_unix_only) { 386 if (strcmp(path,".") == 0) 387 ret_val = 1; 388 } 389 } 390 } 391 return ret_val; 392 } 393 394 /* This routine converts a UCS-2 character to be VTF-7 encoded. 395 */ 396 397 static void ucs2_to_vtf7 398 (char *outspec, 399 unsigned long ucs2_char, 400 int * output_cnt) 401 { 402 unsigned char * ucs_ptr; 403 int hex; 404 405 ucs_ptr = (unsigned char *)&ucs2_char; 406 407 outspec[0] = '^'; 408 outspec[1] = 'U'; 409 hex = (ucs_ptr[1] >> 4) & 0xf; 410 if (hex < 0xA) 411 outspec[2] = hex + '0'; 412 else 413 outspec[2] = (hex - 9) + 'A'; 414 hex = ucs_ptr[1] & 0xF; 415 if (hex < 0xA) 416 outspec[3] = hex + '0'; 417 else { 418 outspec[3] = (hex - 9) + 'A'; 419 } 420 hex = (ucs_ptr[0] >> 4) & 0xf; 421 if (hex < 0xA) 422 outspec[4] = hex + '0'; 423 else 424 outspec[4] = (hex - 9) + 'A'; 425 hex = ucs_ptr[1] & 0xF; 426 if (hex < 0xA) 427 outspec[5] = hex + '0'; 428 else { 429 outspec[5] = (hex - 9) + 'A'; 430 } 431 *output_cnt = 6; 432 } 433 434 435 /* This handles the conversion of a UNIX extended character set to a ^ 436 * escaped VMS character. 437 * in a UNIX file specification. 438 * 439 * The output count variable contains the number of characters added 440 * to the output string. 441 * 442 * The return value is the number of characters read from the input string 443 */ 444 static int copy_expand_unix_filename_escape 445 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl) 446 { 447 int count; 448 int scnt; 449 int utf8_flag; 450 451 utf8_flag = 0; 452 if (utf8_fl) 453 utf8_flag = *utf8_fl; 454 455 count = 0; 456 *output_cnt = 0; 457 if (*inspec >= 0x80) { 458 if (utf8_fl && vms_vtf7_filenames) { 459 unsigned long ucs_char; 460 461 ucs_char = 0; 462 463 if ((*inspec & 0xE0) == 0xC0) { 464 /* 2 byte Unicode */ 465 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f); 466 if (ucs_char >= 0x80) { 467 ucs2_to_vtf7(outspec, ucs_char, output_cnt); 468 return 2; 469 } 470 } else if ((*inspec & 0xF0) == 0xE0) { 471 /* 3 byte Unicode */ 472 ucs_char = ((inspec[0] & 0xF) << 12) + 473 ((inspec[1] & 0x3f) << 6) + 474 (inspec[2] & 0x3f); 475 if (ucs_char >= 0x800) { 476 ucs2_to_vtf7(outspec, ucs_char, output_cnt); 477 return 3; 478 } 479 480 #if 0 /* I do not see longer sequences supported by OpenVMS */ 481 /* Maybe some one can fix this later */ 482 } else if ((*inspec & 0xF8) == 0xF0) { 483 /* 4 byte Unicode */ 484 /* UCS-4 to UCS-2 */ 485 } else if ((*inspec & 0xFC) == 0xF8) { 486 /* 5 byte Unicode */ 487 /* UCS-4 to UCS-2 */ 488 } else if ((*inspec & 0xFE) == 0xFC) { 489 /* 6 byte Unicode */ 490 /* UCS-4 to UCS-2 */ 491 #endif 492 } 493 } 494 495 /* High bit set, but not a Unicode character! */ 496 497 /* Non printing DECMCS or ISO Latin-1 character? */ 498 if (*inspec <= 0x9F) { 499 int hex; 500 outspec[0] = '^'; 501 outspec++; 502 hex = (*inspec >> 4) & 0xF; 503 if (hex < 0xA) 504 outspec[1] = hex + '0'; 505 else { 506 outspec[1] = (hex - 9) + 'A'; 507 } 508 hex = *inspec & 0xF; 509 if (hex < 0xA) 510 outspec[2] = hex + '0'; 511 else { 512 outspec[2] = (hex - 9) + 'A'; 513 } 514 *output_cnt = 3; 515 return 1; 516 } else if (*inspec == 0xA0) { 517 outspec[0] = '^'; 518 outspec[1] = 'A'; 519 outspec[2] = '0'; 520 *output_cnt = 3; 521 return 1; 522 } else if (*inspec == 0xFF) { 523 outspec[0] = '^'; 524 outspec[1] = 'F'; 525 outspec[2] = 'F'; 526 *output_cnt = 3; 527 return 1; 528 } 529 *outspec = *inspec; 530 *output_cnt = 1; 531 return 1; 532 } 533 534 /* Is this a macro that needs to be passed through? 535 * Macros start with $( and an alpha character, followed 536 * by a string of alpha numeric characters ending with a ) 537 * If this does not match, then encode it as ODS-5. 538 */ 539 if ((inspec[0] == '$') && (inspec[1] == '(')) { 540 int tcnt; 541 542 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) { 543 tcnt = 3; 544 outspec[0] = inspec[0]; 545 outspec[1] = inspec[1]; 546 outspec[2] = inspec[2]; 547 548 while(isalnum(inspec[tcnt]) || 549 (inspec[2] == '.') || (inspec[2] == '_')) { 550 outspec[tcnt] = inspec[tcnt]; 551 tcnt++; 552 } 553 if (inspec[tcnt] == ')') { 554 outspec[tcnt] = inspec[tcnt]; 555 tcnt++; 556 *output_cnt = tcnt; 557 return tcnt; 558 } 559 } 560 } 561 562 switch (*inspec) { 563 case 0x7f: 564 outspec[0] = '^'; 565 outspec[1] = '7'; 566 outspec[2] = 'F'; 567 *output_cnt = 3; 568 return 1; 569 break; 570 case '?': 571 if (decc_efs_charset == 0) 572 outspec[0] = '%'; 573 else 574 outspec[0] = '?'; 575 *output_cnt = 1; 576 return 1; 577 break; 578 case '.': 579 case '~': 580 case '!': 581 case '#': 582 case '&': 583 case '\'': 584 case '`': 585 case '(': 586 case ')': 587 case '+': 588 case '@': 589 case '{': 590 case '}': 591 case ',': 592 case ';': 593 case '[': 594 case ']': 595 case '%': 596 case '^': 597 /* Don't escape again if following character is 598 * already something we escape. 599 */ 600 if (strchr(".~!#&\'`()+@{},;[]%^=_", *(inspec+1))) { 601 *outspec = *inspec; 602 *output_cnt = 1; 603 return 1; 604 break; 605 } 606 /* But otherwise fall through and escape it. */ 607 case '=': 608 /* Assume that this is to be escaped */ 609 outspec[0] = '^'; 610 outspec[1] = *inspec; 611 *output_cnt = 2; 612 return 1; 613 break; 614 case ' ': /* space */ 615 /* Assume that this is to be escaped */ 616 outspec[0] = '^'; 617 outspec[1] = '_'; 618 *output_cnt = 2; 619 return 1; 620 break; 621 default: 622 *outspec = *inspec; 623 *output_cnt = 1; 624 return 1; 625 break; 626 } 627 } 628 629 630 /* This handles the expansion of a '^' prefix to the proper character 631 * in a UNIX file specification. 632 * 633 * The output count variable contains the number of characters added 634 * to the output string. 635 * 636 * The return value is the number of characters read from the input 637 * string 638 */ 639 static int copy_expand_vms_filename_escape 640 (char *outspec, const char *inspec, int *output_cnt) 641 { 642 int count; 643 int scnt; 644 645 count = 0; 646 *output_cnt = 0; 647 if (*inspec == '^') { 648 inspec++; 649 switch (*inspec) { 650 /* Spaces and non-trailing dots should just be passed through, 651 * but eat the escape character. 652 */ 653 case '.': 654 *outspec = *inspec; 655 count += 2; 656 (*output_cnt)++; 657 break; 658 case '_': /* space */ 659 *outspec = ' '; 660 count += 2; 661 (*output_cnt)++; 662 break; 663 case '^': 664 /* Hmm. Better leave the escape escaped. */ 665 outspec[0] = '^'; 666 outspec[1] = '^'; 667 count += 2; 668 (*output_cnt) += 2; 669 break; 670 case 'U': /* Unicode - FIX-ME this is wrong. */ 671 inspec++; 672 count++; 673 scnt = strspn(inspec, "0123456789ABCDEFabcdef"); 674 if (scnt == 4) { 675 unsigned int c1, c2; 676 scnt = sscanf(inspec, "%2x%2x", &c1, &c2); 677 outspec[0] == c1 & 0xff; 678 outspec[1] == c2 & 0xff; 679 if (scnt > 1) { 680 (*output_cnt) += 2; 681 count += 4; 682 } 683 } 684 else { 685 /* Error - do best we can to continue */ 686 *outspec = 'U'; 687 outspec++; 688 (*output_cnt++); 689 *outspec = *inspec; 690 count++; 691 (*output_cnt++); 692 } 693 break; 694 default: 695 scnt = strspn(inspec, "0123456789ABCDEFabcdef"); 696 if (scnt == 2) { 697 /* Hex encoded */ 698 unsigned int c1; 699 scnt = sscanf(inspec, "%2x", &c1); 700 outspec[0] = c1 & 0xff; 701 if (scnt > 0) { 702 (*output_cnt++); 703 count += 2; 704 } 705 } 706 else { 707 *outspec = *inspec; 708 count++; 709 (*output_cnt++); 710 } 711 } 712 } 713 else { 714 *outspec = *inspec; 715 count++; 716 (*output_cnt)++; 717 } 718 return count; 719 } 720 721 #ifdef sys$filescan 722 #undef sys$filescan 723 int sys$filescan 724 (const struct dsc$descriptor_s * srcstr, 725 struct filescan_itmlst_2 * valuelist, 726 unsigned long * fldflags, 727 struct dsc$descriptor_s *auxout, 728 unsigned short * retlen); 729 #endif 730 731 /* vms_split_path - Verify that the input file specification is a 732 * VMS format file specification, and provide pointers to the components of 733 * it. With EFS format filenames, this is virtually the only way to 734 * parse a VMS path specification into components. 735 * 736 * If the sum of the components do not add up to the length of the 737 * string, then the passed file specification is probably a UNIX style 738 * path. 739 */ 740 static int vms_split_path 741 (const char * path, 742 char * * volume, 743 int * vol_len, 744 char * * root, 745 int * root_len, 746 char * * dir, 747 int * dir_len, 748 char * * name, 749 int * name_len, 750 char * * ext, 751 int * ext_len, 752 char * * version, 753 int * ver_len) 754 { 755 struct dsc$descriptor path_desc; 756 int status; 757 unsigned long flags; 758 int ret_stat; 759 struct filescan_itmlst_2 item_list[9]; 760 const int filespec = 0; 761 const int nodespec = 1; 762 const int devspec = 2; 763 const int rootspec = 3; 764 const int dirspec = 4; 765 const int namespec = 5; 766 const int typespec = 6; 767 const int verspec = 7; 768 769 /* Assume the worst for an easy exit */ 770 ret_stat = -1; 771 *volume = NULL; 772 *vol_len = 0; 773 *root = NULL; 774 *root_len = 0; 775 *dir = NULL; 776 *dir_len; 777 *name = NULL; 778 *name_len = 0; 779 *ext = NULL; 780 *ext_len = 0; 781 *version = NULL; 782 *ver_len = 0; 783 784 path_desc.dsc$a_pointer = (char *)path; /* cast ok */ 785 path_desc.dsc$w_length = strlen(path); 786 path_desc.dsc$b_dtype = DSC$K_DTYPE_T; 787 path_desc.dsc$b_class = DSC$K_CLASS_S; 788 789 /* Get the total length, if it is shorter than the string passed 790 * then this was probably not a VMS formatted file specification 791 */ 792 item_list[filespec].itmcode = FSCN$_FILESPEC; 793 item_list[filespec].length = 0; 794 item_list[filespec].component = NULL; 795 796 /* If the node is present, then it gets considered as part of the 797 * volume name to hopefully make things simple. 798 */ 799 item_list[nodespec].itmcode = FSCN$_NODE; 800 item_list[nodespec].length = 0; 801 item_list[nodespec].component = NULL; 802 803 item_list[devspec].itmcode = FSCN$_DEVICE; 804 item_list[devspec].length = 0; 805 item_list[devspec].component = NULL; 806 807 /* root is a special case, adding it to either the directory or 808 * the device components will probalby complicate things for the 809 * callers of this routine, so leave it separate. 810 */ 811 item_list[rootspec].itmcode = FSCN$_ROOT; 812 item_list[rootspec].length = 0; 813 item_list[rootspec].component = NULL; 814 815 item_list[dirspec].itmcode = FSCN$_DIRECTORY; 816 item_list[dirspec].length = 0; 817 item_list[dirspec].component = NULL; 818 819 item_list[namespec].itmcode = FSCN$_NAME; 820 item_list[namespec].length = 0; 821 item_list[namespec].component = NULL; 822 823 item_list[typespec].itmcode = FSCN$_TYPE; 824 item_list[typespec].length = 0; 825 item_list[typespec].component = NULL; 826 827 item_list[verspec].itmcode = FSCN$_VERSION; 828 item_list[verspec].length = 0; 829 item_list[verspec].component = NULL; 830 831 item_list[8].itmcode = 0; 832 item_list[8].length = 0; 833 item_list[8].component = NULL; 834 835 status = sys$filescan 836 ((const struct dsc$descriptor_s *)&path_desc, item_list, 837 &flags, NULL, NULL); 838 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */ 839 840 /* If we parsed it successfully these two lengths should be the same */ 841 if (path_desc.dsc$w_length != item_list[filespec].length) 842 return ret_stat; 843 844 /* If we got here, then it is a VMS file specification */ 845 ret_stat = 0; 846 847 /* set the volume name */ 848 if (item_list[nodespec].length > 0) { 849 *volume = item_list[nodespec].component; 850 *vol_len = item_list[nodespec].length + item_list[devspec].length; 851 } 852 else { 853 *volume = item_list[devspec].component; 854 *vol_len = item_list[devspec].length; 855 } 856 857 *root = item_list[rootspec].component; 858 *root_len = item_list[rootspec].length; 859 860 *dir = item_list[dirspec].component; 861 *dir_len = item_list[dirspec].length; 862 863 /* Now fun with versions and EFS file specifications 864 * The parser can not tell the difference when a "." is a version 865 * delimiter or a part of the file specification. 866 */ 867 if ((decc_efs_charset) && 868 (item_list[verspec].length > 0) && 869 (item_list[verspec].component[0] == '.')) { 870 *name = item_list[namespec].component; 871 *name_len = item_list[namespec].length + item_list[typespec].length; 872 *ext = item_list[verspec].component; 873 *ext_len = item_list[verspec].length; 874 *version = NULL; 875 *ver_len = 0; 876 } 877 else { 878 *name = item_list[namespec].component; 879 *name_len = item_list[namespec].length; 880 *ext = item_list[typespec].component; 881 *ext_len = item_list[typespec].length; 882 *version = item_list[verspec].component; 883 *ver_len = item_list[verspec].length; 884 } 885 return ret_stat; 886 } 887 888 889 /* my_maxidx 890 * Routine to retrieve the maximum equivalence index for an input 891 * logical name. Some calls to this routine have no knowledge if 892 * the variable is a logical or not. So on error we return a max 893 * index of zero. 894 */ 895 /*{{{int my_maxidx(const char *lnm) */ 896 static int 897 my_maxidx(const char *lnm) 898 { 899 int status; 900 int midx; 901 int attr = LNM$M_CASE_BLIND; 902 struct dsc$descriptor lnmdsc; 903 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0}, 904 {0, 0, 0, 0}}; 905 906 lnmdsc.dsc$w_length = strlen(lnm); 907 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T; 908 lnmdsc.dsc$b_class = DSC$K_CLASS_S; 909 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */ 910 911 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst); 912 if ((status & 1) == 0) 913 midx = 0; 914 915 return (midx); 916 } 917 /*}}}*/ 918 919 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */ 920 int 921 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, 922 struct dsc$descriptor_s **tabvec, unsigned long int flags) 923 { 924 const char *cp1; 925 char uplnm[LNM$C_NAMLENGTH+1], *cp2; 926 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure; 927 unsigned long int retsts, attr = LNM$M_CASE_BLIND; 928 int midx; 929 unsigned char acmode; 930 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 931 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 932 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0}, 933 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen}, 934 {0, 0, 0, 0}}; 935 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 936 #if defined(PERL_IMPLICIT_CONTEXT) 937 pTHX = NULL; 938 if (PL_curinterp) { 939 aTHX = PERL_GET_INTERP; 940 } else { 941 aTHX = NULL; 942 } 943 #endif 944 945 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) { 946 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0; 947 } 948 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 949 *cp2 = _toupper(*cp1); 950 if (cp1 - lnm > LNM$C_NAMLENGTH) { 951 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 952 return 0; 953 } 954 } 955 lnmdsc.dsc$w_length = cp1 - lnm; 956 lnmdsc.dsc$a_pointer = uplnm; 957 uplnm[lnmdsc.dsc$w_length] = '\0'; 958 secure = flags & PERL__TRNENV_SECURE; 959 acmode = secure ? PSL$C_EXEC : PSL$C_USER; 960 if (!tabvec || !*tabvec) tabvec = env_tables; 961 962 for (curtab = 0; tabvec[curtab]; curtab++) { 963 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) { 964 if (!ivenv && !secure) { 965 char *eq, *end; 966 int i; 967 if (!environ) { 968 ivenv = 1; 969 Perl_warn(aTHX_ "Can't read CRTL environ\n"); 970 continue; 971 } 972 retsts = SS$_NOLOGNAM; 973 for (i = 0; environ[i]; i++) { 974 if ((eq = strchr(environ[i],'=')) && 975 lnmdsc.dsc$w_length == (eq - environ[i]) && 976 !strncmp(environ[i],uplnm,eq - environ[i])) { 977 eq++; 978 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen]; 979 if (!eqvlen) continue; 980 retsts = SS$_NORMAL; 981 break; 982 } 983 } 984 if (retsts != SS$_NOLOGNAM) break; 985 } 986 } 987 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 988 !str$case_blind_compare(&tmpdsc,&clisym)) { 989 if (!ivsym && !secure) { 990 unsigned short int deflen = LNM$C_NAMLENGTH; 991 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 992 /* dynamic dsc to accomodate possible long value */ 993 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc)); 994 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0); 995 if (retsts & 1) { 996 if (eqvlen > MAX_DCL_SYMBOL) { 997 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU); 998 eqvlen = MAX_DCL_SYMBOL; 999 /* Special hack--we might be called before the interpreter's */ 1000 /* fully initialized, in which case either thr or PL_curcop */ 1001 /* might be bogus. We have to check, since ckWARN needs them */ 1002 /* both to be valid if running threaded */ 1003 if (ckWARN(WARN_MISC)) { 1004 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm); 1005 } 1006 } 1007 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen); 1008 } 1009 _ckvmssts(lib$sfree1_dd(&eqvdsc)); 1010 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 1011 if (retsts == LIB$_NOSUCHSYM) continue; 1012 break; 1013 } 1014 } 1015 else if (!ivlnm) { 1016 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) { 1017 midx = my_maxidx(lnm); 1018 for (idx = 0, cp2 = eqv; idx <= midx; idx++) { 1019 lnmlst[1].bufadr = cp2; 1020 eqvlen = 0; 1021 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 1022 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; } 1023 if (retsts == SS$_NOLOGNAM) break; 1024 /* PPFs have a prefix */ 1025 if ( 1026 #if INTSIZE == 4 1027 *((int *)uplnm) == *((int *)"SYS$") && 1028 #endif 1029 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 && 1030 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) || 1031 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) || 1032 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) || 1033 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) { 1034 memmove(eqv,eqv+4,eqvlen-4); 1035 eqvlen -= 4; 1036 } 1037 cp2 += eqvlen; 1038 *cp2 = '\0'; 1039 } 1040 if ((retsts == SS$_IVLOGNAM) || 1041 (retsts == SS$_NOLOGNAM)) { continue; } 1042 } 1043 else { 1044 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst); 1045 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1046 if (retsts == SS$_NOLOGNAM) continue; 1047 eqv[eqvlen] = '\0'; 1048 } 1049 eqvlen = strlen(eqv); 1050 break; 1051 } 1052 } 1053 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; } 1054 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM || 1055 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB || 1056 retsts == SS$_NOLOGNAM) { 1057 set_errno(EINVAL); set_vaxc_errno(retsts); 1058 } 1059 else _ckvmssts(retsts); 1060 return 0; 1061 } /* end of vmstrnenv */ 1062 /*}}}*/ 1063 1064 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/ 1065 /* Define as a function so we can access statics. */ 1066 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx) 1067 { 1068 return vmstrnenv(lnm,eqv,idx,fildev, 1069 #ifdef SECURE_INTERNAL_GETENV 1070 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0 1071 #else 1072 0 1073 #endif 1074 ); 1075 } 1076 /*}}}*/ 1077 1078 /* my_getenv 1079 * Note: Uses Perl temp to store result so char * can be returned to 1080 * caller; this pointer will be invalidated at next Perl statement 1081 * transition. 1082 * We define this as a function rather than a macro in terms of my_getenv_len() 1083 * so that it'll work when PL_curinterp is undefined (and we therefore can't 1084 * allocate SVs). 1085 */ 1086 /*{{{ char *my_getenv(const char *lnm, bool sys)*/ 1087 char * 1088 Perl_my_getenv(pTHX_ const char *lnm, bool sys) 1089 { 1090 const char *cp1; 1091 static char *__my_getenv_eqv = NULL; 1092 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv; 1093 unsigned long int idx = 0; 1094 int trnsuccess, success, secure, saverr, savvmserr; 1095 int midx, flags; 1096 SV *tmpsv; 1097 1098 midx = my_maxidx(lnm) + 1; 1099 1100 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1101 /* Set up a temporary buffer for the return value; Perl will 1102 * clean it up at the next statement transition */ 1103 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1104 if (!tmpsv) return NULL; 1105 eqv = SvPVX(tmpsv); 1106 } 1107 else { 1108 /* Assume no interpreter ==> single thread */ 1109 if (__my_getenv_eqv != NULL) { 1110 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1111 } 1112 else { 1113 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char); 1114 } 1115 eqv = __my_getenv_eqv; 1116 } 1117 1118 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1119 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) { 1120 int len; 1121 getcwd(eqv,LNM$C_NAMLENGTH); 1122 1123 len = strlen(eqv); 1124 1125 /* Get rid of "000000/ in rooted filespecs */ 1126 if (len > 7) { 1127 char * zeros; 1128 zeros = strstr(eqv, "/000000/"); 1129 if (zeros != NULL) { 1130 int mlen; 1131 mlen = len - (zeros - eqv) - 7; 1132 memmove(zeros, &zeros[7], mlen); 1133 len = len - 7; 1134 eqv[len] = '\0'; 1135 } 1136 } 1137 return eqv; 1138 } 1139 else { 1140 /* Impose security constraints only if tainting */ 1141 if (sys) { 1142 /* Impose security constraints only if tainting */ 1143 secure = PL_curinterp ? PL_tainting : will_taint; 1144 saverr = errno; savvmserr = vaxc$errno; 1145 } 1146 else { 1147 secure = 0; 1148 } 1149 1150 flags = 1151 #ifdef SECURE_INTERNAL_GETENV 1152 secure ? PERL__TRNENV_SECURE : 0 1153 #else 1154 0 1155 #endif 1156 ; 1157 1158 /* For the getenv interface we combine all the equivalence names 1159 * of a search list logical into one value to acquire a maximum 1160 * value length of 255*128 (assuming %ENV is using logicals). 1161 */ 1162 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1163 1164 /* If the name contains a semicolon-delimited index, parse it 1165 * off and make sure we only retrieve the equivalence name for 1166 * that index. */ 1167 if ((cp2 = strchr(lnm,';')) != NULL) { 1168 strcpy(uplnm,lnm); 1169 uplnm[cp2-lnm] = '\0'; 1170 idx = strtoul(cp2+1,NULL,0); 1171 lnm = uplnm; 1172 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1173 } 1174 1175 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags); 1176 1177 /* Discard NOLOGNAM on internal calls since we're often looking 1178 * for an optional name, and this "error" often shows up as the 1179 * (bogus) exit status for a die() call later on. */ 1180 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr); 1181 return success ? eqv : Nullch; 1182 } 1183 1184 } /* end of my_getenv() */ 1185 /*}}}*/ 1186 1187 1188 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/ 1189 char * 1190 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys) 1191 { 1192 const char *cp1; 1193 char *buf, *cp2; 1194 unsigned long idx = 0; 1195 int midx, flags; 1196 static char *__my_getenv_len_eqv = NULL; 1197 int secure, saverr, savvmserr; 1198 SV *tmpsv; 1199 1200 midx = my_maxidx(lnm) + 1; 1201 1202 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */ 1203 /* Set up a temporary buffer for the return value; Perl will 1204 * clean it up at the next statement transition */ 1205 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1)); 1206 if (!tmpsv) return NULL; 1207 buf = SvPVX(tmpsv); 1208 } 1209 else { 1210 /* Assume no interpreter ==> single thread */ 1211 if (__my_getenv_len_eqv != NULL) { 1212 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1213 } 1214 else { 1215 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char); 1216 } 1217 buf = __my_getenv_len_eqv; 1218 } 1219 1220 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1); 1221 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) { 1222 char * zeros; 1223 1224 getcwd(buf,LNM$C_NAMLENGTH); 1225 *len = strlen(buf); 1226 1227 /* Get rid of "000000/ in rooted filespecs */ 1228 if (*len > 7) { 1229 zeros = strstr(buf, "/000000/"); 1230 if (zeros != NULL) { 1231 int mlen; 1232 mlen = *len - (zeros - buf) - 7; 1233 memmove(zeros, &zeros[7], mlen); 1234 *len = *len - 7; 1235 buf[*len] = '\0'; 1236 } 1237 } 1238 return buf; 1239 } 1240 else { 1241 if (sys) { 1242 /* Impose security constraints only if tainting */ 1243 secure = PL_curinterp ? PL_tainting : will_taint; 1244 saverr = errno; savvmserr = vaxc$errno; 1245 } 1246 else { 1247 secure = 0; 1248 } 1249 1250 flags = 1251 #ifdef SECURE_INTERNAL_GETENV 1252 secure ? PERL__TRNENV_SECURE : 0 1253 #else 1254 0 1255 #endif 1256 ; 1257 1258 flags |= PERL__TRNENV_JOIN_SEARCHLIST; 1259 1260 if ((cp2 = strchr(lnm,';')) != NULL) { 1261 strcpy(buf,lnm); 1262 buf[cp2-lnm] = '\0'; 1263 idx = strtoul(cp2+1,NULL,0); 1264 lnm = buf; 1265 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST; 1266 } 1267 1268 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags); 1269 1270 /* Get rid of "000000/ in rooted filespecs */ 1271 if (*len > 7) { 1272 char * zeros; 1273 zeros = strstr(buf, "/000000/"); 1274 if (zeros != NULL) { 1275 int mlen; 1276 mlen = *len - (zeros - buf) - 7; 1277 memmove(zeros, &zeros[7], mlen); 1278 *len = *len - 7; 1279 buf[*len] = '\0'; 1280 } 1281 } 1282 1283 /* Discard NOLOGNAM on internal calls since we're often looking 1284 * for an optional name, and this "error" often shows up as the 1285 * (bogus) exit status for a die() call later on. */ 1286 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr); 1287 return *len ? buf : Nullch; 1288 } 1289 1290 } /* end of my_getenv_len() */ 1291 /*}}}*/ 1292 1293 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *); 1294 1295 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); } 1296 1297 /*{{{ void prime_env_iter() */ 1298 void 1299 prime_env_iter(void) 1300 /* Fill the %ENV associative array with all logical names we can 1301 * find, in preparation for iterating over it. 1302 */ 1303 { 1304 static int primed = 0; 1305 HV *seenhv = NULL, *envhv; 1306 SV *sv = NULL; 1307 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch; 1308 unsigned short int chan; 1309 #ifndef CLI$M_TRUSTED 1310 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */ 1311 #endif 1312 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED; 1313 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0; 1314 long int i; 1315 bool have_sym = FALSE, have_lnm = FALSE; 1316 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1317 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:"); 1318 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES"); 1319 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1320 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 1321 #if defined(PERL_IMPLICIT_CONTEXT) 1322 pTHX; 1323 #endif 1324 #if defined(USE_ITHREADS) 1325 static perl_mutex primenv_mutex; 1326 MUTEX_INIT(&primenv_mutex); 1327 #endif 1328 1329 #if defined(PERL_IMPLICIT_CONTEXT) 1330 /* We jump through these hoops because we can be called at */ 1331 /* platform-specific initialization time, which is before anything is */ 1332 /* set up--we can't even do a plain dTHX since that relies on the */ 1333 /* interpreter structure to be initialized */ 1334 if (PL_curinterp) { 1335 aTHX = PERL_GET_INTERP; 1336 } else { 1337 aTHX = NULL; 1338 } 1339 #endif 1340 1341 if (primed || !PL_envgv) return; 1342 MUTEX_LOCK(&primenv_mutex); 1343 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; } 1344 envhv = GvHVn(PL_envgv); 1345 /* Perform a dummy fetch as an lval to insure that the hash table is 1346 * set up. Otherwise, the hv_store() will turn into a nullop. */ 1347 (void) hv_fetch(envhv,"DEFAULT",7,TRUE); 1348 1349 for (i = 0; env_tables[i]; i++) { 1350 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1351 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1; 1352 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1; 1353 } 1354 if (have_sym || have_lnm) { 1355 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM; 1356 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0)); 1357 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0)); 1358 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length)); 1359 } 1360 1361 for (i--; i >= 0; i--) { 1362 if (!str$case_blind_compare(env_tables[i],&crtlenv)) { 1363 char *start; 1364 int j; 1365 for (j = 0; environ[j]; j++) { 1366 if (!(start = strchr(environ[j],'='))) { 1367 if (ckWARN(WARN_INTERNAL)) 1368 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]); 1369 } 1370 else { 1371 start++; 1372 sv = newSVpv(start,0); 1373 SvTAINTED_on(sv); 1374 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0); 1375 } 1376 } 1377 continue; 1378 } 1379 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) && 1380 !str$case_blind_compare(&tmpdsc,&clisym)) { 1381 strcpy(cmd,"Show Symbol/Global *"); 1382 cmddsc.dsc$w_length = 20; 1383 if (env_tables[i]->dsc$w_length == 12 && 1384 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) && 1385 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *"); 1386 flags = defflags | CLI$M_NOLOGNAM; 1387 } 1388 else { 1389 strcpy(cmd,"Show Logical *"); 1390 if (str$case_blind_compare(env_tables[i],&fildevdsc)) { 1391 strcat(cmd," /Table="); 1392 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length); 1393 cmddsc.dsc$w_length = strlen(cmd); 1394 } 1395 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */ 1396 flags = defflags | CLI$M_NOCLISYM; 1397 } 1398 1399 /* Create a new subprocess to execute each command, to exclude the 1400 * remote possibility that someone could subvert a mbx or file used 1401 * to write multiple commands to a single subprocess. 1402 */ 1403 do { 1404 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs, 1405 0,&riseandshine,0,0,&clidsc,&clitabdsc); 1406 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */ 1407 defflags &= ~CLI$M_TRUSTED; 1408 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED)); 1409 _ckvmssts(retsts); 1410 if (!buf) Newx(buf,mbxbufsiz + 1,char); 1411 if (seenhv) SvREFCNT_dec(seenhv); 1412 seenhv = newHV(); 1413 while (1) { 1414 char *cp1, *cp2, *key; 1415 unsigned long int sts, iosb[2], retlen, keylen; 1416 register U32 hash; 1417 1418 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0); 1419 if (sts & 1) sts = iosb[0] & 0xffff; 1420 if (sts == SS$_ENDOFFILE) { 1421 int wakect = 0; 1422 while (substs == 0) { sys$hiber(); wakect++;} 1423 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */ 1424 _ckvmssts(substs); 1425 break; 1426 } 1427 _ckvmssts(sts); 1428 retlen = iosb[0] >> 16; 1429 if (!retlen) continue; /* blank line */ 1430 buf[retlen] = '\0'; 1431 if (iosb[1] != subpid) { 1432 if (iosb[1]) { 1433 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf); 1434 } 1435 continue; 1436 } 1437 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL)) 1438 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf); 1439 1440 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ; 1441 if (*cp1 == '(' || /* Logical name table name */ 1442 *cp1 == '=' /* Next eqv of searchlist */) continue; 1443 if (*cp1 == '"') cp1++; 1444 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ; 1445 key = cp1; keylen = cp2 - cp1; 1446 if (keylen && hv_exists(seenhv,key,keylen)) continue; 1447 while (*cp2 && *cp2 != '=') cp2++; 1448 while (*cp2 && *cp2 == '=') cp2++; 1449 while (*cp2 && *cp2 == ' ') cp2++; 1450 if (*cp2 == '"') { /* String translation; may embed "" */ 1451 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ; 1452 cp2++; cp1--; /* Skip "" surrounding translation */ 1453 } 1454 else { /* Numeric translation */ 1455 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ; 1456 cp1--; /* stop on last non-space char */ 1457 } 1458 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) { 1459 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf); 1460 continue; 1461 } 1462 PERL_HASH(hash,key,keylen); 1463 1464 if (cp1 == cp2 && *cp2 == '.') { 1465 /* A single dot usually means an unprintable character, such as a null 1466 * to indicate a zero-length value. Get the actual value to make sure. 1467 */ 1468 char lnm[LNM$C_NAMLENGTH+1]; 1469 char eqv[MAX_DCL_SYMBOL+1]; 1470 int trnlen; 1471 strncpy(lnm, key, keylen); 1472 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0); 1473 sv = newSVpvn(eqv, strlen(eqv)); 1474 } 1475 else { 1476 sv = newSVpvn(cp2,cp1 - cp2 + 1); 1477 } 1478 1479 SvTAINTED_on(sv); 1480 hv_store(envhv,key,keylen,sv,hash); 1481 hv_store(seenhv,key,keylen,&PL_sv_yes,hash); 1482 } 1483 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */ 1484 /* get the PPFs for this process, not the subprocess */ 1485 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL}; 1486 char eqv[LNM$C_NAMLENGTH+1]; 1487 int trnlen, i; 1488 for (i = 0; ppfs[i]; i++) { 1489 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0); 1490 sv = newSVpv(eqv,trnlen); 1491 SvTAINTED_on(sv); 1492 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0); 1493 } 1494 } 1495 } 1496 primed = 1; 1497 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan)); 1498 if (buf) Safefree(buf); 1499 if (seenhv) SvREFCNT_dec(seenhv); 1500 MUTEX_UNLOCK(&primenv_mutex); 1501 return; 1502 1503 } /* end of prime_env_iter */ 1504 /*}}}*/ 1505 1506 1507 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/ 1508 /* Define or delete an element in the same "environment" as 1509 * vmstrnenv(). If an element is to be deleted, it's removed from 1510 * the first place it's found. If it's to be set, it's set in the 1511 * place designated by the first element of the table vector. 1512 * Like setenv() returns 0 for success, non-zero on error. 1513 */ 1514 int 1515 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec) 1516 { 1517 const char *cp1; 1518 char uplnm[LNM$C_NAMLENGTH], *cp2, *c; 1519 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0; 1520 int nseg = 0, j; 1521 unsigned long int retsts, usermode = PSL$C_USER; 1522 struct itmlst_3 *ile, *ilist; 1523 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm}, 1524 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}, 1525 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; 1526 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM"); 1527 $DESCRIPTOR(local,"_LOCAL"); 1528 1529 if (!lnm) { 1530 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1531 return SS$_IVLOGNAM; 1532 } 1533 1534 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) { 1535 *cp2 = _toupper(*cp1); 1536 if (cp1 - lnm > LNM$C_NAMLENGTH) { 1537 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM); 1538 return SS$_IVLOGNAM; 1539 } 1540 } 1541 lnmdsc.dsc$w_length = cp1 - lnm; 1542 if (!tabvec || !*tabvec) tabvec = env_tables; 1543 1544 if (!eqv) { /* we're deleting n element */ 1545 for (curtab = 0; tabvec[curtab]; curtab++) { 1546 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) { 1547 int i; 1548 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */ 1549 if ((cp1 = strchr(environ[i],'=')) && 1550 lnmdsc.dsc$w_length == (cp1 - environ[i]) && 1551 !strncmp(environ[i],lnm,cp1 - environ[i])) { 1552 #ifdef HAS_SETENV 1553 return setenv(lnm,"",1) ? vaxc$errno : 0; 1554 } 1555 } 1556 ivenv = 1; retsts = SS$_NOLOGNAM; 1557 #else 1558 if (ckWARN(WARN_INTERNAL)) 1559 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm); 1560 ivenv = 1; retsts = SS$_NOSUCHPGM; 1561 break; 1562 } 1563 } 1564 #endif 1565 } 1566 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) && 1567 !str$case_blind_compare(&tmpdsc,&clisym)) { 1568 unsigned int symtype; 1569 if (tabvec[curtab]->dsc$w_length == 12 && 1570 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) && 1571 !str$case_blind_compare(&tmpdsc,&local)) 1572 symtype = LIB$K_CLI_LOCAL_SYM; 1573 else symtype = LIB$K_CLI_GLOBAL_SYM; 1574 retsts = lib$delete_symbol(&lnmdsc,&symtype); 1575 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; } 1576 if (retsts == LIB$_NOSUCHSYM) continue; 1577 break; 1578 } 1579 else if (!ivlnm) { 1580 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */ 1581 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; } 1582 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1583 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */ 1584 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break; 1585 } 1586 } 1587 } 1588 else { /* we're defining a value */ 1589 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) { 1590 #ifdef HAS_SETENV 1591 return setenv(lnm,eqv,1) ? vaxc$errno : 0; 1592 #else 1593 if (ckWARN(WARN_INTERNAL)) 1594 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv); 1595 retsts = SS$_NOSUCHPGM; 1596 #endif 1597 } 1598 else { 1599 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */ 1600 eqvdsc.dsc$w_length = strlen(eqv); 1601 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) && 1602 !str$case_blind_compare(&tmpdsc,&clisym)) { 1603 unsigned int symtype; 1604 if (tabvec[0]->dsc$w_length == 12 && 1605 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) && 1606 !str$case_blind_compare(&tmpdsc,&local)) 1607 symtype = LIB$K_CLI_LOCAL_SYM; 1608 else symtype = LIB$K_CLI_GLOBAL_SYM; 1609 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype); 1610 } 1611 else { 1612 if (!*eqv) eqvdsc.dsc$w_length = 1; 1613 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) { 1614 1615 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH; 1616 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) { 1617 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes", 1618 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1)); 1619 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1); 1620 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1; 1621 } 1622 1623 Newx(ilist,nseg+1,struct itmlst_3); 1624 ile = ilist; 1625 if (!ile) { 1626 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM); 1627 return SS$_INSFMEM; 1628 } 1629 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1))); 1630 1631 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) { 1632 ile->itmcode = LNM$_STRING; 1633 ile->bufadr = c; 1634 if ((j+1) == nseg) { 1635 ile->buflen = strlen(c); 1636 /* in case we are truncating one that's too long */ 1637 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH; 1638 } 1639 else { 1640 ile->buflen = LNM$C_NAMLENGTH; 1641 } 1642 } 1643 1644 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist); 1645 Safefree (ilist); 1646 } 1647 else { 1648 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0); 1649 } 1650 } 1651 } 1652 } 1653 if (!(retsts & 1)) { 1654 switch (retsts) { 1655 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM: 1656 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB: 1657 set_errno(EVMSERR); break; 1658 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 1659 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM: 1660 set_errno(EINVAL); break; 1661 case SS$_NOPRIV: 1662 set_errno(EACCES); break; 1663 default: 1664 _ckvmssts(retsts); 1665 set_errno(EVMSERR); 1666 } 1667 set_vaxc_errno(retsts); 1668 return (int) retsts || 44; /* retsts should never be 0, but just in case */ 1669 } 1670 else { 1671 /* We reset error values on success because Perl does an hv_fetch() 1672 * before each hv_store(), and if the thing we're setting didn't 1673 * previously exist, we've got a leftover error message. (Of course, 1674 * this fails in the face of 1675 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo'; 1676 * in that the error reported in $! isn't spurious, 1677 * but it's right more often than not.) 1678 */ 1679 set_errno(0); set_vaxc_errno(retsts); 1680 return 0; 1681 } 1682 1683 } /* end of vmssetenv() */ 1684 /*}}}*/ 1685 1686 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/ 1687 /* This has to be a function since there's a prototype for it in proto.h */ 1688 void 1689 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv) 1690 { 1691 if (lnm && *lnm) { 1692 int len = strlen(lnm); 1693 if (len == 7) { 1694 char uplnm[8]; 1695 int i; 1696 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]); 1697 if (!strcmp(uplnm,"DEFAULT")) { 1698 if (eqv && *eqv) my_chdir(eqv); 1699 return; 1700 } 1701 } 1702 #ifndef RTL_USES_UTC 1703 if (len == 6 || len == 2) { 1704 char uplnm[7]; 1705 int i; 1706 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]); 1707 uplnm[len] = '\0'; 1708 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1; 1709 if (!strcmp(uplnm,"TZ")) tz_updated = 1; 1710 } 1711 #endif 1712 } 1713 (void) vmssetenv(lnm,eqv,NULL); 1714 } 1715 /*}}}*/ 1716 1717 /*{{{static void vmssetuserlnm(char *name, char *eqv); */ 1718 /* vmssetuserlnm 1719 * sets a user-mode logical in the process logical name table 1720 * used for redirection of sys$error 1721 */ 1722 void 1723 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv) 1724 { 1725 $DESCRIPTOR(d_tab, "LNM$PROCESS"); 1726 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; 1727 unsigned long int iss, attr = LNM$M_CONFINE; 1728 unsigned char acmode = PSL$C_USER; 1729 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0}, 1730 {0, 0, 0, 0}}; 1731 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */ 1732 d_name.dsc$w_length = strlen(name); 1733 1734 lnmlst[0].buflen = strlen(eqv); 1735 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */ 1736 1737 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst); 1738 if (!(iss&1)) lib$signal(iss); 1739 } 1740 /*}}}*/ 1741 1742 1743 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/ 1744 /* my_crypt - VMS password hashing 1745 * my_crypt() provides an interface compatible with the Unix crypt() 1746 * C library function, and uses sys$hash_password() to perform VMS 1747 * password hashing. The quadword hashed password value is returned 1748 * as a NUL-terminated 8 character string. my_crypt() does not change 1749 * the case of its string arguments; in order to match the behavior 1750 * of LOGINOUT et al., alphabetic characters in both arguments must 1751 * be upcased by the caller. 1752 * 1753 * - fix me to call ACM services when available 1754 */ 1755 char * 1756 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname) 1757 { 1758 # ifndef UAI$C_PREFERRED_ALGORITHM 1759 # define UAI$C_PREFERRED_ALGORITHM 127 1760 # endif 1761 unsigned char alg = UAI$C_PREFERRED_ALGORITHM; 1762 unsigned short int salt = 0; 1763 unsigned long int sts; 1764 struct const_dsc { 1765 unsigned short int dsc$w_length; 1766 unsigned char dsc$b_type; 1767 unsigned char dsc$b_class; 1768 const char * dsc$a_pointer; 1769 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}, 1770 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1771 struct itmlst_3 uailst[3] = { 1772 { sizeof alg, UAI$_ENCRYPT, &alg, 0}, 1773 { sizeof salt, UAI$_SALT, &salt, 0}, 1774 { 0, 0, NULL, NULL}}; 1775 static char hash[9]; 1776 1777 usrdsc.dsc$w_length = strlen(usrname); 1778 usrdsc.dsc$a_pointer = usrname; 1779 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) { 1780 switch (sts) { 1781 case SS$_NOGRPPRV: case SS$_NOSYSPRV: 1782 set_errno(EACCES); 1783 break; 1784 case RMS$_RNF: 1785 set_errno(ESRCH); /* There isn't a Unix no-such-user error */ 1786 break; 1787 default: 1788 set_errno(EVMSERR); 1789 } 1790 set_vaxc_errno(sts); 1791 if (sts != RMS$_RNF) return NULL; 1792 } 1793 1794 txtdsc.dsc$w_length = strlen(textpasswd); 1795 txtdsc.dsc$a_pointer = textpasswd; 1796 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) { 1797 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL; 1798 } 1799 1800 return (char *) hash; 1801 1802 } /* end of my_crypt() */ 1803 /*}}}*/ 1804 1805 1806 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *); 1807 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *); 1808 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *); 1809 1810 /* fixup barenames that are directories for internal use. 1811 * There have been problems with the consistent handling of UNIX 1812 * style directory names when routines are presented with a name that 1813 * has no directory delimitors at all. So this routine will eventually 1814 * fix the issue. 1815 */ 1816 static char * fixup_bare_dirnames(const char * name) 1817 { 1818 if (decc_disable_to_vms_logname_translation) { 1819 /* fix me */ 1820 } 1821 return NULL; 1822 } 1823 1824 /* 8.3, remove() is now broken on symbolic links */ 1825 static int rms_erase(const char * vmsname); 1826 1827 1828 /* mp_do_kill_file 1829 * A little hack to get around a bug in some implemenation of remove() 1830 * that do not know how to delete a directory 1831 * 1832 * Delete any file to which user has control access, regardless of whether 1833 * delete access is explicitly allowed. 1834 * Limitations: User must have write access to parent directory. 1835 * Does not block signals or ASTs; if interrupted in midstream 1836 * may leave file with an altered ACL. 1837 * HANDLE WITH CARE! 1838 */ 1839 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/ 1840 static int 1841 mp_do_kill_file(pTHX_ const char *name, int dirflag) 1842 { 1843 char *vmsname; 1844 char *rslt; 1845 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; 1846 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1; 1847 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 1848 struct myacedef { 1849 unsigned char myace$b_length; 1850 unsigned char myace$b_type; 1851 unsigned short int myace$w_flags; 1852 unsigned long int myace$l_access; 1853 unsigned long int myace$l_ident; 1854 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 1855 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0}, 1856 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 1857 struct itmlst_3 1858 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0}, 1859 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}}, 1860 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}}, 1861 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}}, 1862 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}}, 1863 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}}; 1864 1865 /* Expand the input spec using RMS, since the CRTL remove() and 1866 * system services won't do this by themselves, so we may miss 1867 * a file "hiding" behind a logical name or search list. */ 1868 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1); 1869 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM); 1870 1871 rslt = do_rmsexpand(name, 1872 vmsname, 1873 0, 1874 NULL, 1875 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK, 1876 NULL, 1877 NULL); 1878 if (rslt == NULL) { 1879 PerlMem_free(vmsname); 1880 return -1; 1881 } 1882 1883 /* Erase the file */ 1884 rmsts = rms_erase(vmsname); 1885 1886 /* Did it succeed */ 1887 if ($VMS_STATUS_SUCCESS(rmsts)) { 1888 PerlMem_free(vmsname); 1889 return 0; 1890 } 1891 1892 /* If not, can changing protections help? */ 1893 if (rmsts != RMS$_PRV) { 1894 set_vaxc_errno(rmsts); 1895 PerlMem_free(vmsname); 1896 return -1; 1897 } 1898 1899 /* No, so we get our own UIC to use as a rights identifier, 1900 * and the insert an ACE at the head of the ACL which allows us 1901 * to delete the file. 1902 */ 1903 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 1904 fildsc.dsc$w_length = strlen(vmsname); 1905 fildsc.dsc$a_pointer = vmsname; 1906 cxt = 0; 1907 newace.myace$l_ident = oldace.myace$l_ident; 1908 rmsts = -1; 1909 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) { 1910 switch (aclsts) { 1911 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT: 1912 set_errno(ENOENT); break; 1913 case RMS$_DIR: 1914 set_errno(ENOTDIR); break; 1915 case RMS$_DEV: 1916 set_errno(ENODEV); break; 1917 case RMS$_SYN: case SS$_INVFILFOROP: 1918 set_errno(EINVAL); break; 1919 case RMS$_PRV: 1920 set_errno(EACCES); break; 1921 default: 1922 _ckvmssts(aclsts); 1923 } 1924 set_vaxc_errno(aclsts); 1925 PerlMem_free(vmsname); 1926 return -1; 1927 } 1928 /* Grab any existing ACEs with this identifier in case we fail */ 1929 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt); 1930 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY 1931 || fndsts == SS$_NOMOREACE ) { 1932 /* Add the new ACE . . . */ 1933 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1)) 1934 goto yourroom; 1935 1936 rmsts = rms_erase(vmsname); 1937 if ($VMS_STATUS_SUCCESS(rmsts)) { 1938 rmsts = 0; 1939 } 1940 else { 1941 rmsts = -1; 1942 /* We blew it - dir with files in it, no write priv for 1943 * parent directory, etc. Put things back the way they were. */ 1944 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1)) 1945 goto yourroom; 1946 if (fndsts & 1) { 1947 addlst[0].bufadr = &oldace; 1948 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1)) 1949 goto yourroom; 1950 } 1951 } 1952 } 1953 1954 yourroom: 1955 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0); 1956 /* We just deleted it, so of course it's not there. Some versions of 1957 * VMS seem to return success on the unlock operation anyhow (after all 1958 * the unlock is successful), but others don't. 1959 */ 1960 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL; 1961 if (aclsts & 1) aclsts = fndsts; 1962 if (!(aclsts & 1)) { 1963 set_errno(EVMSERR); 1964 set_vaxc_errno(aclsts); 1965 } 1966 1967 PerlMem_free(vmsname); 1968 return rmsts; 1969 1970 } /* end of kill_file() */ 1971 /*}}}*/ 1972 1973 1974 /*{{{int do_rmdir(char *name)*/ 1975 int 1976 Perl_do_rmdir(pTHX_ const char *name) 1977 { 1978 char * dirfile; 1979 int retval; 1980 Stat_t st; 1981 1982 dirfile = PerlMem_malloc(VMS_MAXRSS + 1); 1983 if (dirfile == NULL) 1984 _ckvmssts(SS$_INSFMEM); 1985 1986 /* Force to a directory specification */ 1987 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) { 1988 PerlMem_free(dirfile); 1989 return -1; 1990 } 1991 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) { 1992 errno = ENOTDIR; 1993 retval = -1; 1994 } 1995 else 1996 retval = mp_do_kill_file(aTHX_ dirfile, 1); 1997 1998 PerlMem_free(dirfile); 1999 return retval; 2000 2001 } /* end of do_rmdir */ 2002 /*}}}*/ 2003 2004 /* kill_file 2005 * Delete any file to which user has control access, regardless of whether 2006 * delete access is explicitly allowed. 2007 * Limitations: User must have write access to parent directory. 2008 * Does not block signals or ASTs; if interrupted in midstream 2009 * may leave file with an altered ACL. 2010 * HANDLE WITH CARE! 2011 */ 2012 /*{{{int kill_file(char *name)*/ 2013 int 2014 Perl_kill_file(pTHX_ const char *name) 2015 { 2016 char rspec[NAM$C_MAXRSS+1]; 2017 char *tspec; 2018 Stat_t st; 2019 int rmsts; 2020 2021 /* Remove() is allowed to delete directories, according to the X/Open 2022 * specifications. 2023 * This may need special handling to work with the ACL hacks. 2024 */ 2025 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) { 2026 rmsts = Perl_do_rmdir(aTHX_ name); 2027 return rmsts; 2028 } 2029 2030 rmsts = mp_do_kill_file(aTHX_ name, 0); 2031 2032 return rmsts; 2033 2034 } /* end of kill_file() */ 2035 /*}}}*/ 2036 2037 2038 /*{{{int my_mkdir(char *,Mode_t)*/ 2039 int 2040 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode) 2041 { 2042 STRLEN dirlen = strlen(dir); 2043 2044 /* zero length string sometimes gives ACCVIO */ 2045 if (dirlen == 0) return -1; 2046 2047 /* CRTL mkdir() doesn't tolerate trailing /, since that implies 2048 * null file name/type. However, it's commonplace under Unix, 2049 * so we'll allow it for a gain in portability. 2050 */ 2051 if (dir[dirlen-1] == '/') { 2052 char *newdir = savepvn(dir,dirlen-1); 2053 int ret = mkdir(newdir,mode); 2054 Safefree(newdir); 2055 return ret; 2056 } 2057 else return mkdir(dir,mode); 2058 } /* end of my_mkdir */ 2059 /*}}}*/ 2060 2061 /*{{{int my_chdir(char *)*/ 2062 int 2063 Perl_my_chdir(pTHX_ const char *dir) 2064 { 2065 STRLEN dirlen = strlen(dir); 2066 2067 /* zero length string sometimes gives ACCVIO */ 2068 if (dirlen == 0) return -1; 2069 const char *dir1; 2070 2071 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces. 2072 * This does not work if DECC$EFS_CHARSET is active. Hack it here 2073 * so that existing scripts do not need to be changed. 2074 */ 2075 dir1 = dir; 2076 while ((dirlen > 0) && (*dir1 == ' ')) { 2077 dir1++; 2078 dirlen--; 2079 } 2080 2081 /* some versions of CRTL chdir() doesn't tolerate trailing /, since 2082 * that implies 2083 * null file name/type. However, it's commonplace under Unix, 2084 * so we'll allow it for a gain in portability. 2085 * 2086 * - Preview- '/' will be valid soon on VMS 2087 */ 2088 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) { 2089 char *newdir = savepvn(dir1,dirlen-1); 2090 int ret = chdir(newdir); 2091 Safefree(newdir); 2092 return ret; 2093 } 2094 else return chdir(dir1); 2095 } /* end of my_chdir */ 2096 /*}}}*/ 2097 2098 2099 /*{{{int my_chmod(char *, mode_t)*/ 2100 int 2101 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode) 2102 { 2103 STRLEN speclen = strlen(file_spec); 2104 2105 /* zero length string sometimes gives ACCVIO */ 2106 if (speclen == 0) return -1; 2107 2108 /* some versions of CRTL chmod() doesn't tolerate trailing /, since 2109 * that implies null file name/type. However, it's commonplace under Unix, 2110 * so we'll allow it for a gain in portability. 2111 * 2112 * Tests are showing that chmod() on VMS 8.3 is only accepting directories 2113 * in VMS file.dir notation. 2114 */ 2115 if ((speclen > 1) && (file_spec[speclen-1] == '/')) { 2116 char *vms_src, *vms_dir, *rslt; 2117 int ret = -1; 2118 errno = EIO; 2119 2120 /* First convert this to a VMS format specification */ 2121 vms_src = PerlMem_malloc(VMS_MAXRSS); 2122 if (vms_src == NULL) 2123 _ckvmssts(SS$_INSFMEM); 2124 2125 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL); 2126 if (rslt == NULL) { 2127 /* If we fail, then not a file specification */ 2128 PerlMem_free(vms_src); 2129 errno = EIO; 2130 return -1; 2131 } 2132 2133 /* Now make it a directory spec so chmod is happy */ 2134 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1); 2135 if (vms_dir == NULL) 2136 _ckvmssts(SS$_INSFMEM); 2137 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL); 2138 PerlMem_free(vms_src); 2139 2140 /* Now do it */ 2141 if (rslt != NULL) { 2142 ret = chmod(vms_dir, mode); 2143 } else { 2144 errno = EIO; 2145 } 2146 PerlMem_free(vms_dir); 2147 return ret; 2148 } 2149 else return chmod(file_spec, mode); 2150 } /* end of my_chmod */ 2151 /*}}}*/ 2152 2153 2154 /*{{{FILE *my_tmpfile()*/ 2155 FILE * 2156 my_tmpfile(void) 2157 { 2158 FILE *fp; 2159 char *cp; 2160 2161 if ((fp = tmpfile())) return fp; 2162 2163 cp = PerlMem_malloc(L_tmpnam+24); 2164 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM); 2165 2166 if (decc_filename_unix_only == 0) 2167 strcpy(cp,"Sys$Scratch:"); 2168 else 2169 strcpy(cp,"/tmp/"); 2170 tmpnam(cp+strlen(cp)); 2171 strcat(cp,".Perltmp"); 2172 fp = fopen(cp,"w+","fop=dlt"); 2173 PerlMem_free(cp); 2174 return fp; 2175 } 2176 /*}}}*/ 2177 2178 2179 #ifndef HOMEGROWN_POSIX_SIGNALS 2180 /* 2181 * The C RTL's sigaction fails to check for invalid signal numbers so we 2182 * help it out a bit. The docs are correct, but the actual routine doesn't 2183 * do what the docs say it will. 2184 */ 2185 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/ 2186 int 2187 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act, 2188 struct sigaction* oact) 2189 { 2190 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) { 2191 SETERRNO(EINVAL, SS$_INVARG); 2192 return -1; 2193 } 2194 return sigaction(sig, act, oact); 2195 } 2196 /*}}}*/ 2197 #endif 2198 2199 #ifdef KILL_BY_SIGPRC 2200 #include <errnodef.h> 2201 2202 /* We implement our own kill() using the undocumented system service 2203 sys$sigprc for one of two reasons: 2204 2205 1.) If the kill() in an older CRTL uses sys$forcex, causing the 2206 target process to do a sys$exit, which usually can't be handled 2207 gracefully...certainly not by Perl and the %SIG{} mechanism. 2208 2209 2.) If the kill() in the CRTL can't be called from a signal 2210 handler without disappearing into the ether, i.e., the signal 2211 it purportedly sends is never trapped. Still true as of VMS 7.3. 2212 2213 sys$sigprc has the same parameters as sys$forcex, but throws an exception 2214 in the target process rather than calling sys$exit. 2215 2216 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg 2217 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't 2218 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc 2219 with condition codes C$_SIG0+nsig*8, catching the exception on the 2220 target process and resignaling with appropriate arguments. 2221 2222 But we don't have that VMS 7.0+ exception handler, so if you 2223 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well. 2224 2225 Also note that SIGTERM is listed in the docs as being "unimplemented", 2226 yet always seems to be signaled with a VMS condition code of 4 (and 2227 correctly handled for that code). So we hardwire it in. 2228 2229 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal 2230 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather 2231 than signalling with an unrecognized (and unhandled by CRTL) code. 2232 */ 2233 2234 #define _MY_SIG_MAX 28 2235 2236 static unsigned int 2237 Perl_sig_to_vmscondition_int(int sig) 2238 { 2239 static unsigned int sig_code[_MY_SIG_MAX+1] = 2240 { 2241 0, /* 0 ZERO */ 2242 SS$_HANGUP, /* 1 SIGHUP */ 2243 SS$_CONTROLC, /* 2 SIGINT */ 2244 SS$_CONTROLY, /* 3 SIGQUIT */ 2245 SS$_RADRMOD, /* 4 SIGILL */ 2246 SS$_BREAK, /* 5 SIGTRAP */ 2247 SS$_OPCCUS, /* 6 SIGABRT */ 2248 SS$_COMPAT, /* 7 SIGEMT */ 2249 #ifdef __VAX 2250 SS$_FLTOVF, /* 8 SIGFPE VAX */ 2251 #else 2252 SS$_HPARITH, /* 8 SIGFPE AXP */ 2253 #endif 2254 SS$_ABORT, /* 9 SIGKILL */ 2255 SS$_ACCVIO, /* 10 SIGBUS */ 2256 SS$_ACCVIO, /* 11 SIGSEGV */ 2257 SS$_BADPARAM, /* 12 SIGSYS */ 2258 SS$_NOMBX, /* 13 SIGPIPE */ 2259 SS$_ASTFLT, /* 14 SIGALRM */ 2260 4, /* 15 SIGTERM */ 2261 0, /* 16 SIGUSR1 */ 2262 0, /* 17 SIGUSR2 */ 2263 0, /* 18 */ 2264 0, /* 19 */ 2265 0, /* 20 SIGCHLD */ 2266 0, /* 21 SIGCONT */ 2267 0, /* 22 SIGSTOP */ 2268 0, /* 23 SIGTSTP */ 2269 0, /* 24 SIGTTIN */ 2270 0, /* 25 SIGTTOU */ 2271 0, /* 26 */ 2272 0, /* 27 */ 2273 0 /* 28 SIGWINCH */ 2274 }; 2275 2276 #if __VMS_VER >= 60200000 2277 static int initted = 0; 2278 if (!initted) { 2279 initted = 1; 2280 sig_code[16] = C$_SIGUSR1; 2281 sig_code[17] = C$_SIGUSR2; 2282 #if __CRTL_VER >= 70000000 2283 sig_code[20] = C$_SIGCHLD; 2284 #endif 2285 #if __CRTL_VER >= 70300000 2286 sig_code[28] = C$_SIGWINCH; 2287 #endif 2288 } 2289 #endif 2290 2291 if (sig < _SIG_MIN) return 0; 2292 if (sig > _MY_SIG_MAX) return 0; 2293 return sig_code[sig]; 2294 } 2295 2296 unsigned int 2297 Perl_sig_to_vmscondition(int sig) 2298 { 2299 #ifdef SS$_DEBUG 2300 if (vms_debug_on_exception != 0) 2301 lib$signal(SS$_DEBUG); 2302 #endif 2303 return Perl_sig_to_vmscondition_int(sig); 2304 } 2305 2306 2307 int 2308 Perl_my_kill(int pid, int sig) 2309 { 2310 dTHX; 2311 int iss; 2312 unsigned int code; 2313 int sys$sigprc(unsigned int *pidadr, 2314 struct dsc$descriptor_s *prcname, 2315 unsigned int code); 2316 2317 /* sig 0 means validate the PID */ 2318 /*------------------------------*/ 2319 if (sig == 0) { 2320 const unsigned long int jpicode = JPI$_PID; 2321 pid_t ret_pid; 2322 int status; 2323 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL); 2324 if ($VMS_STATUS_SUCCESS(status)) 2325 return 0; 2326 switch (status) { 2327 case SS$_NOSUCHNODE: 2328 case SS$_UNREACHABLE: 2329 case SS$_NONEXPR: 2330 errno = ESRCH; 2331 break; 2332 case SS$_NOPRIV: 2333 errno = EPERM; 2334 break; 2335 default: 2336 errno = EVMSERR; 2337 } 2338 vaxc$errno=status; 2339 return -1; 2340 } 2341 2342 code = Perl_sig_to_vmscondition_int(sig); 2343 2344 if (!code) { 2345 SETERRNO(EINVAL, SS$_BADPARAM); 2346 return -1; 2347 } 2348 2349 /* Fixme: Per official UNIX specification: If pid = 0, or negative then 2350 * signals are to be sent to multiple processes. 2351 * pid = 0 - all processes in group except ones that the system exempts 2352 * pid = -1 - all processes except ones that the system exempts 2353 * pid = -n - all processes in group (abs(n)) except ... 2354 * For now, just report as not supported. 2355 */ 2356 2357 if (pid <= 0) { 2358 SETERRNO(ENOTSUP, SS$_UNSUPPORTED); 2359 return -1; 2360 } 2361 2362 iss = sys$sigprc((unsigned int *)&pid,0,code); 2363 if (iss&1) return 0; 2364 2365 switch (iss) { 2366 case SS$_NOPRIV: 2367 set_errno(EPERM); break; 2368 case SS$_NONEXPR: 2369 case SS$_NOSUCHNODE: 2370 case SS$_UNREACHABLE: 2371 set_errno(ESRCH); break; 2372 case SS$_INSFMEM: 2373 set_errno(ENOMEM); break; 2374 default: 2375 _ckvmssts(iss); 2376 set_errno(EVMSERR); 2377 } 2378 set_vaxc_errno(iss); 2379 2380 return -1; 2381 } 2382 #endif 2383 2384 /* Routine to convert a VMS status code to a UNIX status code. 2385 ** More tricky than it appears because of conflicting conventions with 2386 ** existing code. 2387 ** 2388 ** VMS status codes are a bit mask, with the least significant bit set for 2389 ** success. 2390 ** 2391 ** Special UNIX status of EVMSERR indicates that no translation is currently 2392 ** available, and programs should check the VMS status code. 2393 ** 2394 ** Programs compiled with _POSIX_EXIT have a special encoding that requires 2395 ** decoding. 2396 */ 2397 2398 #ifndef C_FACILITY_NO 2399 #define C_FACILITY_NO 0x350000 2400 #endif 2401 #ifndef DCL_IVVERB 2402 #define DCL_IVVERB 0x38090 2403 #endif 2404 2405 int Perl_vms_status_to_unix(int vms_status, int child_flag) 2406 { 2407 int facility; 2408 int fac_sp; 2409 int msg_no; 2410 int msg_status; 2411 int unix_status; 2412 2413 /* Assume the best or the worst */ 2414 if (vms_status & STS$M_SUCCESS) 2415 unix_status = 0; 2416 else 2417 unix_status = EVMSERR; 2418 2419 msg_status = vms_status & ~STS$M_CONTROL; 2420 2421 facility = vms_status & STS$M_FAC_NO; 2422 fac_sp = vms_status & STS$M_FAC_SP; 2423 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY); 2424 2425 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) { 2426 switch(msg_no) { 2427 case SS$_NORMAL: 2428 unix_status = 0; 2429 break; 2430 case SS$_ACCVIO: 2431 unix_status = EFAULT; 2432 break; 2433 case SS$_DEVOFFLINE: 2434 unix_status = EBUSY; 2435 break; 2436 case SS$_CLEARED: 2437 unix_status = ENOTCONN; 2438 break; 2439 case SS$_IVCHAN: 2440 case SS$_IVLOGNAM: 2441 case SS$_BADPARAM: 2442 case SS$_IVLOGTAB: 2443 case SS$_NOLOGNAM: 2444 case SS$_NOLOGTAB: 2445 case SS$_INVFILFOROP: 2446 case SS$_INVARG: 2447 case SS$_NOSUCHID: 2448 case SS$_IVIDENT: 2449 unix_status = EINVAL; 2450 break; 2451 case SS$_UNSUPPORTED: 2452 unix_status = ENOTSUP; 2453 break; 2454 case SS$_FILACCERR: 2455 case SS$_NOGRPPRV: 2456 case SS$_NOSYSPRV: 2457 unix_status = EACCES; 2458 break; 2459 case SS$_DEVICEFULL: 2460 unix_status = ENOSPC; 2461 break; 2462 case SS$_NOSUCHDEV: 2463 unix_status = ENODEV; 2464 break; 2465 case SS$_NOSUCHFILE: 2466 case SS$_NOSUCHOBJECT: 2467 unix_status = ENOENT; 2468 break; 2469 case SS$_ABORT: /* Fatal case */ 2470 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */ 2471 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */ 2472 unix_status = EINTR; 2473 break; 2474 case SS$_BUFFEROVF: 2475 unix_status = E2BIG; 2476 break; 2477 case SS$_INSFMEM: 2478 unix_status = ENOMEM; 2479 break; 2480 case SS$_NOPRIV: 2481 unix_status = EPERM; 2482 break; 2483 case SS$_NOSUCHNODE: 2484 case SS$_UNREACHABLE: 2485 unix_status = ESRCH; 2486 break; 2487 case SS$_NONEXPR: 2488 unix_status = ECHILD; 2489 break; 2490 default: 2491 if ((facility == 0) && (msg_no < 8)) { 2492 /* These are not real VMS status codes so assume that they are 2493 ** already UNIX status codes 2494 */ 2495 unix_status = msg_no; 2496 break; 2497 } 2498 } 2499 } 2500 else { 2501 /* Translate a POSIX exit code to a UNIX exit code */ 2502 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) { 2503 unix_status = (msg_no & 0x07F8) >> 3; 2504 } 2505 else { 2506 2507 /* Documented traditional behavior for handling VMS child exits */ 2508 /*--------------------------------------------------------------*/ 2509 if (child_flag != 0) { 2510 2511 /* Success / Informational return 0 */ 2512 /*----------------------------------*/ 2513 if (msg_no & STS$K_SUCCESS) 2514 return 0; 2515 2516 /* Warning returns 1 */ 2517 /*-------------------*/ 2518 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0) 2519 return 1; 2520 2521 /* Everything else pass through the severity bits */ 2522 /*------------------------------------------------*/ 2523 return (msg_no & STS$M_SEVERITY); 2524 } 2525 2526 /* Normal VMS status to ERRNO mapping attempt */ 2527 /*--------------------------------------------*/ 2528 switch(msg_status) { 2529 /* case RMS$_EOF: */ /* End of File */ 2530 case RMS$_FNF: /* File Not Found */ 2531 case RMS$_DNF: /* Dir Not Found */ 2532 unix_status = ENOENT; 2533 break; 2534 case RMS$_RNF: /* Record Not Found */ 2535 unix_status = ESRCH; 2536 break; 2537 case RMS$_DIR: 2538 unix_status = ENOTDIR; 2539 break; 2540 case RMS$_DEV: 2541 unix_status = ENODEV; 2542 break; 2543 case RMS$_IFI: 2544 case RMS$_FAC: 2545 case RMS$_ISI: 2546 unix_status = EBADF; 2547 break; 2548 case RMS$_FEX: 2549 unix_status = EEXIST; 2550 break; 2551 case RMS$_SYN: 2552 case RMS$_FNM: 2553 case LIB$_INVSTRDES: 2554 case LIB$_INVARG: 2555 case LIB$_NOSUCHSYM: 2556 case LIB$_INVSYMNAM: 2557 case DCL_IVVERB: 2558 unix_status = EINVAL; 2559 break; 2560 case CLI$_BUFOVF: 2561 case RMS$_RTB: 2562 case CLI$_TKNOVF: 2563 case CLI$_RSLOVF: 2564 unix_status = E2BIG; 2565 break; 2566 case RMS$_PRV: /* No privilege */ 2567 case RMS$_ACC: /* ACP file access failed */ 2568 case RMS$_WLK: /* Device write locked */ 2569 unix_status = EACCES; 2570 break; 2571 /* case RMS$_NMF: */ /* No more files */ 2572 } 2573 } 2574 } 2575 2576 return unix_status; 2577 } 2578 2579 /* Try to guess at what VMS error status should go with a UNIX errno 2580 * value. This is hard to do as there could be many possible VMS 2581 * error statuses that caused the errno value to be set. 2582 */ 2583 2584 int Perl_unix_status_to_vms(int unix_status) 2585 { 2586 int test_unix_status; 2587 2588 /* Trivial cases first */ 2589 /*---------------------*/ 2590 if (unix_status == EVMSERR) 2591 return vaxc$errno; 2592 2593 /* Is vaxc$errno sane? */ 2594 /*---------------------*/ 2595 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0); 2596 if (test_unix_status == unix_status) 2597 return vaxc$errno; 2598 2599 /* If way out of range, must be VMS code already */ 2600 /*-----------------------------------------------*/ 2601 if (unix_status > EVMSERR) 2602 return unix_status; 2603 2604 /* If out of range, punt */ 2605 /*-----------------------*/ 2606 if (unix_status > __ERRNO_MAX) 2607 return SS$_ABORT; 2608 2609 2610 /* Ok, now we have to do it the hard way. */ 2611 /*----------------------------------------*/ 2612 switch(unix_status) { 2613 case 0: return SS$_NORMAL; 2614 case EPERM: return SS$_NOPRIV; 2615 case ENOENT: return SS$_NOSUCHOBJECT; 2616 case ESRCH: return SS$_UNREACHABLE; 2617 case EINTR: return SS$_ABORT; 2618 /* case EIO: */ 2619 /* case ENXIO: */ 2620 case E2BIG: return SS$_BUFFEROVF; 2621 /* case ENOEXEC */ 2622 case EBADF: return RMS$_IFI; 2623 case ECHILD: return SS$_NONEXPR; 2624 /* case EAGAIN */ 2625 case ENOMEM: return SS$_INSFMEM; 2626 case EACCES: return SS$_FILACCERR; 2627 case EFAULT: return SS$_ACCVIO; 2628 /* case ENOTBLK */ 2629 case EBUSY: return SS$_DEVOFFLINE; 2630 case EEXIST: return RMS$_FEX; 2631 /* case EXDEV */ 2632 case ENODEV: return SS$_NOSUCHDEV; 2633 case ENOTDIR: return RMS$_DIR; 2634 /* case EISDIR */ 2635 case EINVAL: return SS$_INVARG; 2636 /* case ENFILE */ 2637 /* case EMFILE */ 2638 /* case ENOTTY */ 2639 /* case ETXTBSY */ 2640 /* case EFBIG */ 2641 case ENOSPC: return SS$_DEVICEFULL; 2642 case ESPIPE: return LIB$_INVARG; 2643 /* case EROFS: */ 2644 /* case EMLINK: */ 2645 /* case EPIPE: */ 2646 /* case EDOM */ 2647 case ERANGE: return LIB$_INVARG; 2648 /* case EWOULDBLOCK */ 2649 /* case EINPROGRESS */ 2650 /* case EALREADY */ 2651 /* case ENOTSOCK */ 2652 /* case EDESTADDRREQ */ 2653 /* case EMSGSIZE */ 2654 /* case EPROTOTYPE */ 2655 /* case ENOPROTOOPT */ 2656 /* case EPROTONOSUPPORT */ 2657 /* case ESOCKTNOSUPPORT */ 2658 /* case EOPNOTSUPP */ 2659 /* case EPFNOSUPPORT */ 2660 /* case EAFNOSUPPORT */ 2661 /* case EADDRINUSE */ 2662 /* case EADDRNOTAVAIL */ 2663 /* case ENETDOWN */ 2664 /* case ENETUNREACH */ 2665 /* case ENETRESET */ 2666 /* case ECONNABORTED */ 2667 /* case ECONNRESET */ 2668 /* case ENOBUFS */ 2669 /* case EISCONN */ 2670 case ENOTCONN: return SS$_CLEARED; 2671 /* case ESHUTDOWN */ 2672 /* case ETOOMANYREFS */ 2673 /* case ETIMEDOUT */ 2674 /* case ECONNREFUSED */ 2675 /* case ELOOP */ 2676 /* case ENAMETOOLONG */ 2677 /* case EHOSTDOWN */ 2678 /* case EHOSTUNREACH */ 2679 /* case ENOTEMPTY */ 2680 /* case EPROCLIM */ 2681 /* case EUSERS */ 2682 /* case EDQUOT */ 2683 /* case ENOMSG */ 2684 /* case EIDRM */ 2685 /* case EALIGN */ 2686 /* case ESTALE */ 2687 /* case EREMOTE */ 2688 /* case ENOLCK */ 2689 /* case ENOSYS */ 2690 /* case EFTYPE */ 2691 /* case ECANCELED */ 2692 /* case EFAIL */ 2693 /* case EINPROG */ 2694 case ENOTSUP: 2695 return SS$_UNSUPPORTED; 2696 /* case EDEADLK */ 2697 /* case ENWAIT */ 2698 /* case EILSEQ */ 2699 /* case EBADCAT */ 2700 /* case EBADMSG */ 2701 /* case EABANDONED */ 2702 default: 2703 return SS$_ABORT; /* punt */ 2704 } 2705 2706 return SS$_ABORT; /* Should not get here */ 2707 } 2708 2709 2710 /* default piping mailbox size */ 2711 #define PERL_BUFSIZ 512 2712 2713 2714 static void 2715 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc) 2716 { 2717 unsigned long int mbxbufsiz; 2718 static unsigned long int syssize = 0; 2719 unsigned long int dviitm = DVI$_DEVNAM; 2720 char csize[LNM$C_NAMLENGTH+1]; 2721 int sts; 2722 2723 if (!syssize) { 2724 unsigned long syiitm = SYI$_MAXBUF; 2725 /* 2726 * Get the SYSGEN parameter MAXBUF 2727 * 2728 * If the logical 'PERL_MBX_SIZE' is defined 2729 * use the value of the logical instead of PERL_BUFSIZ, but 2730 * keep the size between 128 and MAXBUF. 2731 * 2732 */ 2733 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0)); 2734 } 2735 2736 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) { 2737 mbxbufsiz = atoi(csize); 2738 } else { 2739 mbxbufsiz = PERL_BUFSIZ; 2740 } 2741 if (mbxbufsiz < 128) mbxbufsiz = 128; 2742 if (mbxbufsiz > syssize) mbxbufsiz = syssize; 2743 2744 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0)); 2745 2746 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length)); 2747 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0'; 2748 2749 } /* end of create_mbx() */ 2750 2751 2752 /*{{{ my_popen and my_pclose*/ 2753 2754 typedef struct _iosb IOSB; 2755 typedef struct _iosb* pIOSB; 2756 typedef struct _pipe Pipe; 2757 typedef struct _pipe* pPipe; 2758 typedef struct pipe_details Info; 2759 typedef struct pipe_details* pInfo; 2760 typedef struct _srqp RQE; 2761 typedef struct _srqp* pRQE; 2762 typedef struct _tochildbuf CBuf; 2763 typedef struct _tochildbuf* pCBuf; 2764 2765 struct _iosb { 2766 unsigned short status; 2767 unsigned short count; 2768 unsigned long dvispec; 2769 }; 2770 2771 #pragma member_alignment save 2772 #pragma nomember_alignment quadword 2773 struct _srqp { /* VMS self-relative queue entry */ 2774 unsigned long qptr[2]; 2775 }; 2776 #pragma member_alignment restore 2777 static RQE RQE_ZERO = {0,0}; 2778 2779 struct _tochildbuf { 2780 RQE q; 2781 int eof; 2782 unsigned short size; 2783 char *buf; 2784 }; 2785 2786 struct _pipe { 2787 RQE free; 2788 RQE wait; 2789 int fd_out; 2790 unsigned short chan_in; 2791 unsigned short chan_out; 2792 char *buf; 2793 unsigned int bufsize; 2794 IOSB iosb; 2795 IOSB iosb2; 2796 int *pipe_done; 2797 int retry; 2798 int type; 2799 int shut_on_empty; 2800 int need_wake; 2801 pPipe *home; 2802 pInfo info; 2803 pCBuf curr; 2804 pCBuf curr2; 2805 #if defined(PERL_IMPLICIT_CONTEXT) 2806 void *thx; /* Either a thread or an interpreter */ 2807 /* pointer, depending on how we're built */ 2808 #endif 2809 }; 2810 2811 2812 struct pipe_details 2813 { 2814 pInfo next; 2815 PerlIO *fp; /* file pointer to pipe mailbox */ 2816 int useFILE; /* using stdio, not perlio */ 2817 int pid; /* PID of subprocess */ 2818 int mode; /* == 'r' if pipe open for reading */ 2819 int done; /* subprocess has completed */ 2820 int waiting; /* waiting for completion/closure */ 2821 int closing; /* my_pclose is closing this pipe */ 2822 unsigned long completion; /* termination status of subprocess */ 2823 pPipe in; /* pipe in to sub */ 2824 pPipe out; /* pipe out of sub */ 2825 pPipe err; /* pipe of sub's sys$error */ 2826 int in_done; /* true when in pipe finished */ 2827 int out_done; 2828 int err_done; 2829 unsigned short xchan; /* channel to debug xterm */ 2830 unsigned short xchan_valid; /* channel is assigned */ 2831 }; 2832 2833 struct exit_control_block 2834 { 2835 struct exit_control_block *flink; 2836 unsigned long int (*exit_routine)(); 2837 unsigned long int arg_count; 2838 unsigned long int *status_address; 2839 unsigned long int exit_status; 2840 }; 2841 2842 typedef struct _closed_pipes Xpipe; 2843 typedef struct _closed_pipes* pXpipe; 2844 2845 struct _closed_pipes { 2846 int pid; /* PID of subprocess */ 2847 unsigned long completion; /* termination status of subprocess */ 2848 }; 2849 #define NKEEPCLOSED 50 2850 static Xpipe closed_list[NKEEPCLOSED]; 2851 static int closed_index = 0; 2852 static int closed_num = 0; 2853 2854 #define RETRY_DELAY "0 ::0.20" 2855 #define MAX_RETRY 50 2856 2857 static int pipe_ef = 0; /* first call to safe_popen inits these*/ 2858 static unsigned long mypid; 2859 static unsigned long delaytime[2]; 2860 2861 static pInfo open_pipes = NULL; 2862 static $DESCRIPTOR(nl_desc, "NL:"); 2863 2864 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */ 2865 2866 2867 2868 static unsigned long int 2869 pipe_exit_routine(pTHX) 2870 { 2871 pInfo info; 2872 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT; 2873 int sts, did_stuff, need_eof, j; 2874 2875 /* 2876 * Flush any pending i/o, but since we are in process run-down, be 2877 * careful about referencing PerlIO structures that may already have 2878 * been deallocated. We may not even have an interpreter anymore. 2879 */ 2880 info = open_pipes; 2881 while (info) { 2882 if (info->fp) { 2883 if (!info->useFILE 2884 #if defined(USE_ITHREADS) 2885 && my_perl 2886 #endif 2887 && PL_perlio_fd_refcnt) 2888 PerlIO_flush(info->fp); 2889 else 2890 fflush((FILE *)info->fp); 2891 } 2892 info = info->next; 2893 } 2894 2895 /* 2896 next we try sending an EOF...ignore if doesn't work, make sure we 2897 don't hang 2898 */ 2899 did_stuff = 0; 2900 info = open_pipes; 2901 2902 while (info) { 2903 int need_eof; 2904 _ckvmssts_noperl(sys$setast(0)); 2905 if (info->in && !info->in->shut_on_empty) { 2906 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 2907 0, 0, 0, 0, 0, 0)); 2908 info->waiting = 1; 2909 did_stuff = 1; 2910 } 2911 _ckvmssts_noperl(sys$setast(1)); 2912 info = info->next; 2913 } 2914 2915 /* wait for EOF to have effect, up to ~ 30 sec [default] */ 2916 2917 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 2918 int nwait = 0; 2919 2920 info = open_pipes; 2921 while (info) { 2922 _ckvmssts_noperl(sys$setast(0)); 2923 if (info->waiting && info->done) 2924 info->waiting = 0; 2925 nwait += info->waiting; 2926 _ckvmssts_noperl(sys$setast(1)); 2927 info = info->next; 2928 } 2929 if (!nwait) break; 2930 sleep(1); 2931 } 2932 2933 did_stuff = 0; 2934 info = open_pipes; 2935 while (info) { 2936 _ckvmssts_noperl(sys$setast(0)); 2937 if (!info->done) { /* Tap them gently on the shoulder . . .*/ 2938 sts = sys$forcex(&info->pid,0,&abort); 2939 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 2940 did_stuff = 1; 2941 } 2942 _ckvmssts_noperl(sys$setast(1)); 2943 info = info->next; 2944 } 2945 2946 /* again, wait for effect */ 2947 2948 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) { 2949 int nwait = 0; 2950 2951 info = open_pipes; 2952 while (info) { 2953 _ckvmssts_noperl(sys$setast(0)); 2954 if (info->waiting && info->done) 2955 info->waiting = 0; 2956 nwait += info->waiting; 2957 _ckvmssts_noperl(sys$setast(1)); 2958 info = info->next; 2959 } 2960 if (!nwait) break; 2961 sleep(1); 2962 } 2963 2964 info = open_pipes; 2965 while (info) { 2966 _ckvmssts_noperl(sys$setast(0)); 2967 if (!info->done) { /* We tried to be nice . . . */ 2968 sts = sys$delprc(&info->pid,0); 2969 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 2970 info->done = 1; /* sys$delprc is as done as we're going to get. */ 2971 } 2972 _ckvmssts_noperl(sys$setast(1)); 2973 info = info->next; 2974 } 2975 2976 while(open_pipes) { 2977 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno; 2978 else if (!(sts & 1)) retsts = sts; 2979 } 2980 return retsts; 2981 } 2982 2983 static struct exit_control_block pipe_exitblock = 2984 {(struct exit_control_block *) 0, 2985 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0}; 2986 2987 static void pipe_mbxtofd_ast(pPipe p); 2988 static void pipe_tochild1_ast(pPipe p); 2989 static void pipe_tochild2_ast(pPipe p); 2990 2991 static void 2992 popen_completion_ast(pInfo info) 2993 { 2994 pInfo i = open_pipes; 2995 int iss; 2996 int sts; 2997 pXpipe x; 2998 2999 info->completion &= 0x0FFFFFFF; /* strip off "control" field */ 3000 closed_list[closed_index].pid = info->pid; 3001 closed_list[closed_index].completion = info->completion; 3002 closed_index++; 3003 if (closed_index == NKEEPCLOSED) 3004 closed_index = 0; 3005 closed_num++; 3006 3007 while (i) { 3008 if (i == info) break; 3009 i = i->next; 3010 } 3011 if (!i) return; /* unlinked, probably freed too */ 3012 3013 info->done = TRUE; 3014 3015 /* 3016 Writing to subprocess ... 3017 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe 3018 3019 chan_out may be waiting for "done" flag, or hung waiting 3020 for i/o completion to child...cancel the i/o. This will 3021 put it into "snarf mode" (done but no EOF yet) that discards 3022 input. 3023 3024 Output from subprocess (stdout, stderr) needs to be flushed and 3025 shut down. We try sending an EOF, but if the mbx is full the pipe 3026 routine should still catch the "shut_on_empty" flag, telling it to 3027 use immediate-style reads so that "mbx empty" -> EOF. 3028 3029 3030 */ 3031 if (info->in && !info->in_done) { /* only for mode=w */ 3032 if (info->in->shut_on_empty && info->in->need_wake) { 3033 info->in->need_wake = FALSE; 3034 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0)); 3035 } else { 3036 _ckvmssts_noperl(sys$cancel(info->in->chan_out)); 3037 } 3038 } 3039 3040 if (info->out && !info->out_done) { /* were we also piping output? */ 3041 info->out->shut_on_empty = TRUE; 3042 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3043 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3044 _ckvmssts_noperl(iss); 3045 } 3046 3047 if (info->err && !info->err_done) { /* we were piping stderr */ 3048 info->err->shut_on_empty = TRUE; 3049 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0); 3050 if (iss == SS$_MBFULL) iss = SS$_NORMAL; 3051 _ckvmssts_noperl(iss); 3052 } 3053 _ckvmssts_noperl(sys$setef(pipe_ef)); 3054 3055 } 3056 3057 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd); 3058 static void vms_execfree(struct dsc$descriptor_s *vmscmd); 3059 3060 /* 3061 we actually differ from vmstrnenv since we use this to 3062 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really* 3063 are pointing to the same thing 3064 */ 3065 3066 static unsigned short 3067 popen_translate(pTHX_ char *logical, char *result) 3068 { 3069 int iss; 3070 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE"); 3071 $DESCRIPTOR(d_log,""); 3072 struct _il3 { 3073 unsigned short length; 3074 unsigned short code; 3075 char * buffer_addr; 3076 unsigned short *retlenaddr; 3077 } itmlst[2]; 3078 unsigned short l, ifi; 3079 3080 d_log.dsc$a_pointer = logical; 3081 d_log.dsc$w_length = strlen(logical); 3082 3083 itmlst[0].code = LNM$_STRING; 3084 itmlst[0].length = 255; 3085 itmlst[0].buffer_addr = result; 3086 itmlst[0].retlenaddr = &l; 3087 3088 itmlst[1].code = 0; 3089 itmlst[1].length = 0; 3090 itmlst[1].buffer_addr = 0; 3091 itmlst[1].retlenaddr = 0; 3092 3093 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst); 3094 if (iss == SS$_NOLOGNAM) { 3095 iss = SS$_NORMAL; 3096 l = 0; 3097 } 3098 if (!(iss&1)) lib$signal(iss); 3099 result[l] = '\0'; 3100 /* 3101 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI) 3102 strip it off and return the ifi, if any 3103 */ 3104 ifi = 0; 3105 if (result[0] == 0x1b && result[1] == 0x00) { 3106 memmove(&ifi,result+2,2); 3107 strcpy(result,result+4); 3108 } 3109 return ifi; /* this is the RMS internal file id */ 3110 } 3111 3112 static void pipe_infromchild_ast(pPipe p); 3113 3114 /* 3115 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate 3116 inside an AST routine without worrying about reentrancy and which Perl 3117 memory allocator is being used. 3118 3119 We read data and queue up the buffers, then spit them out one at a 3120 time to the output mailbox when the output mailbox is ready for one. 3121 3122 */ 3123 #define INITIAL_TOCHILDQUEUE 2 3124 3125 static pPipe 3126 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx) 3127 { 3128 pPipe p; 3129 pCBuf b; 3130 char mbx1[64], mbx2[64]; 3131 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3132 DSC$K_CLASS_S, mbx1}, 3133 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3134 DSC$K_CLASS_S, mbx2}; 3135 unsigned int dviitm = DVI$_DEVBUFSIZ; 3136 int j, n; 3137 3138 n = sizeof(Pipe); 3139 _ckvmssts(lib$get_vm(&n, &p)); 3140 3141 create_mbx(aTHX_ &p->chan_in , &d_mbx1); 3142 create_mbx(aTHX_ &p->chan_out, &d_mbx2); 3143 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3144 3145 p->buf = 0; 3146 p->shut_on_empty = FALSE; 3147 p->need_wake = FALSE; 3148 p->type = 0; 3149 p->retry = 0; 3150 p->iosb.status = SS$_NORMAL; 3151 p->iosb2.status = SS$_NORMAL; 3152 p->free = RQE_ZERO; 3153 p->wait = RQE_ZERO; 3154 p->curr = 0; 3155 p->curr2 = 0; 3156 p->info = 0; 3157 #ifdef PERL_IMPLICIT_CONTEXT 3158 p->thx = aTHX; 3159 #endif 3160 3161 n = sizeof(CBuf) + p->bufsize; 3162 3163 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) { 3164 _ckvmssts(lib$get_vm(&n, &b)); 3165 b->buf = (char *) b + sizeof(CBuf); 3166 _ckvmssts(lib$insqhi(b, &p->free)); 3167 } 3168 3169 pipe_tochild2_ast(p); 3170 pipe_tochild1_ast(p); 3171 strcpy(wmbx, mbx1); 3172 strcpy(rmbx, mbx2); 3173 return p; 3174 } 3175 3176 /* reads the MBX Perl is writing, and queues */ 3177 3178 static void 3179 pipe_tochild1_ast(pPipe p) 3180 { 3181 pCBuf b = p->curr; 3182 int iss = p->iosb.status; 3183 int eof = (iss == SS$_ENDOFFILE); 3184 int sts; 3185 #ifdef PERL_IMPLICIT_CONTEXT 3186 pTHX = p->thx; 3187 #endif 3188 3189 if (p->retry) { 3190 if (eof) { 3191 p->shut_on_empty = TRUE; 3192 b->eof = TRUE; 3193 _ckvmssts(sys$dassgn(p->chan_in)); 3194 } else { 3195 _ckvmssts(iss); 3196 } 3197 3198 b->eof = eof; 3199 b->size = p->iosb.count; 3200 _ckvmssts(sts = lib$insqhi(b, &p->wait)); 3201 if (p->need_wake) { 3202 p->need_wake = FALSE; 3203 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0)); 3204 } 3205 } else { 3206 p->retry = 1; /* initial call */ 3207 } 3208 3209 if (eof) { /* flush the free queue, return when done */ 3210 int n = sizeof(CBuf) + p->bufsize; 3211 while (1) { 3212 iss = lib$remqti(&p->free, &b); 3213 if (iss == LIB$_QUEWASEMP) return; 3214 _ckvmssts(iss); 3215 _ckvmssts(lib$free_vm(&n, &b)); 3216 } 3217 } 3218 3219 iss = lib$remqti(&p->free, &b); 3220 if (iss == LIB$_QUEWASEMP) { 3221 int n = sizeof(CBuf) + p->bufsize; 3222 _ckvmssts(lib$get_vm(&n, &b)); 3223 b->buf = (char *) b + sizeof(CBuf); 3224 } else { 3225 _ckvmssts(iss); 3226 } 3227 3228 p->curr = b; 3229 iss = sys$qio(0,p->chan_in, 3230 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0), 3231 &p->iosb, 3232 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0); 3233 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL; 3234 _ckvmssts(iss); 3235 } 3236 3237 3238 /* writes queued buffers to output, waits for each to complete before 3239 doing the next */ 3240 3241 static void 3242 pipe_tochild2_ast(pPipe p) 3243 { 3244 pCBuf b = p->curr2; 3245 int iss = p->iosb2.status; 3246 int n = sizeof(CBuf) + p->bufsize; 3247 int done = (p->info && p->info->done) || 3248 iss == SS$_CANCEL || iss == SS$_ABORT; 3249 #if defined(PERL_IMPLICIT_CONTEXT) 3250 pTHX = p->thx; 3251 #endif 3252 3253 do { 3254 if (p->type) { /* type=1 has old buffer, dispose */ 3255 if (p->shut_on_empty) { 3256 _ckvmssts(lib$free_vm(&n, &b)); 3257 } else { 3258 _ckvmssts(lib$insqhi(b, &p->free)); 3259 } 3260 p->type = 0; 3261 } 3262 3263 iss = lib$remqti(&p->wait, &b); 3264 if (iss == LIB$_QUEWASEMP) { 3265 if (p->shut_on_empty) { 3266 if (done) { 3267 _ckvmssts(sys$dassgn(p->chan_out)); 3268 *p->pipe_done = TRUE; 3269 _ckvmssts(sys$setef(pipe_ef)); 3270 } else { 3271 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, 3272 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3273 } 3274 return; 3275 } 3276 p->need_wake = TRUE; 3277 return; 3278 } 3279 _ckvmssts(iss); 3280 p->type = 1; 3281 } while (done); 3282 3283 3284 p->curr2 = b; 3285 if (b->eof) { 3286 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, 3287 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0)); 3288 } else { 3289 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK, 3290 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0)); 3291 } 3292 3293 return; 3294 3295 } 3296 3297 3298 static pPipe 3299 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx) 3300 { 3301 pPipe p; 3302 char mbx1[64], mbx2[64]; 3303 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3304 DSC$K_CLASS_S, mbx1}, 3305 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T, 3306 DSC$K_CLASS_S, mbx2}; 3307 unsigned int dviitm = DVI$_DEVBUFSIZ; 3308 3309 int n = sizeof(Pipe); 3310 _ckvmssts(lib$get_vm(&n, &p)); 3311 create_mbx(aTHX_ &p->chan_in , &d_mbx1); 3312 create_mbx(aTHX_ &p->chan_out, &d_mbx2); 3313 3314 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3315 n = p->bufsize * sizeof(char); 3316 _ckvmssts(lib$get_vm(&n, &p->buf)); 3317 p->shut_on_empty = FALSE; 3318 p->info = 0; 3319 p->type = 0; 3320 p->iosb.status = SS$_NORMAL; 3321 #if defined(PERL_IMPLICIT_CONTEXT) 3322 p->thx = aTHX; 3323 #endif 3324 pipe_infromchild_ast(p); 3325 3326 strcpy(wmbx, mbx1); 3327 strcpy(rmbx, mbx2); 3328 return p; 3329 } 3330 3331 static void 3332 pipe_infromchild_ast(pPipe p) 3333 { 3334 int iss = p->iosb.status; 3335 int eof = (iss == SS$_ENDOFFILE); 3336 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0)); 3337 int kideof = (eof && (p->iosb.dvispec == p->info->pid)); 3338 #if defined(PERL_IMPLICIT_CONTEXT) 3339 pTHX = p->thx; 3340 #endif 3341 3342 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */ 3343 _ckvmssts(sys$dassgn(p->chan_out)); 3344 p->chan_out = 0; 3345 } 3346 3347 /* read completed: 3348 input shutdown if EOF from self (done or shut_on_empty) 3349 output shutdown if closing flag set (my_pclose) 3350 send data/eof from child or eof from self 3351 otherwise, re-read (snarf of data from child) 3352 */ 3353 3354 if (p->type == 1) { 3355 p->type = 0; 3356 if (myeof && p->chan_in) { /* input shutdown */ 3357 _ckvmssts(sys$dassgn(p->chan_in)); 3358 p->chan_in = 0; 3359 } 3360 3361 if (p->chan_out) { 3362 if (myeof || kideof) { /* pass EOF to parent */ 3363 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb, 3364 pipe_infromchild_ast, p, 3365 0, 0, 0, 0, 0, 0)); 3366 return; 3367 } else if (eof) { /* eat EOF --- fall through to read*/ 3368 3369 } else { /* transmit data */ 3370 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb, 3371 pipe_infromchild_ast,p, 3372 p->buf, p->iosb.count, 0, 0, 0, 0)); 3373 return; 3374 } 3375 } 3376 } 3377 3378 /* everything shut? flag as done */ 3379 3380 if (!p->chan_in && !p->chan_out) { 3381 *p->pipe_done = TRUE; 3382 _ckvmssts(sys$setef(pipe_ef)); 3383 return; 3384 } 3385 3386 /* write completed (or read, if snarfing from child) 3387 if still have input active, 3388 queue read...immediate mode if shut_on_empty so we get EOF if empty 3389 otherwise, 3390 check if Perl reading, generate EOFs as needed 3391 */ 3392 3393 if (p->type == 0) { 3394 p->type = 1; 3395 if (p->chan_in) { 3396 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb, 3397 pipe_infromchild_ast,p, 3398 p->buf, p->bufsize, 0, 0, 0, 0); 3399 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL; 3400 _ckvmssts(iss); 3401 } else { /* send EOFs for extra reads */ 3402 p->iosb.status = SS$_ENDOFFILE; 3403 p->iosb.dvispec = 0; 3404 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN, 3405 0, 0, 0, 3406 pipe_infromchild_ast, p, 0, 0, 0, 0)); 3407 } 3408 } 3409 } 3410 3411 static pPipe 3412 pipe_mbxtofd_setup(pTHX_ int fd, char *out) 3413 { 3414 pPipe p; 3415 char mbx[64]; 3416 unsigned long dviitm = DVI$_DEVBUFSIZ; 3417 struct stat s; 3418 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T, 3419 DSC$K_CLASS_S, mbx}; 3420 int n = sizeof(Pipe); 3421 3422 /* things like terminals and mbx's don't need this filter */ 3423 if (fd && fstat(fd,&s) == 0) { 3424 unsigned long dviitm = DVI$_DEVCHAR, devchar; 3425 char device[65]; 3426 unsigned short dev_len; 3427 struct dsc$descriptor_s d_dev; 3428 char * cptr; 3429 struct item_list_3 items[3]; 3430 int status; 3431 unsigned short dvi_iosb[4]; 3432 3433 cptr = getname(fd, out, 1); 3434 if (cptr == NULL) _ckvmssts(SS$_NOSUCHDEV); 3435 d_dev.dsc$a_pointer = out; 3436 d_dev.dsc$w_length = strlen(out); 3437 d_dev.dsc$b_dtype = DSC$K_DTYPE_T; 3438 d_dev.dsc$b_class = DSC$K_CLASS_S; 3439 3440 items[0].len = 4; 3441 items[0].code = DVI$_DEVCHAR; 3442 items[0].bufadr = &devchar; 3443 items[0].retadr = NULL; 3444 items[1].len = 64; 3445 items[1].code = DVI$_FULLDEVNAM; 3446 items[1].bufadr = device; 3447 items[1].retadr = &dev_len; 3448 items[2].len = 0; 3449 items[2].code = 0; 3450 3451 status = sys$getdviw 3452 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL); 3453 _ckvmssts(status); 3454 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) { 3455 device[dev_len] = 0; 3456 3457 if (!(devchar & DEV$M_DIR)) { 3458 strcpy(out, device); 3459 return 0; 3460 } 3461 } 3462 } 3463 3464 _ckvmssts(lib$get_vm(&n, &p)); 3465 p->fd_out = dup(fd); 3466 create_mbx(aTHX_ &p->chan_in, &d_mbx); 3467 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize)); 3468 n = (p->bufsize+1) * sizeof(char); 3469 _ckvmssts(lib$get_vm(&n, &p->buf)); 3470 p->shut_on_empty = FALSE; 3471 p->retry = 0; 3472 p->info = 0; 3473 strcpy(out, mbx); 3474 3475 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb, 3476 pipe_mbxtofd_ast, p, 3477 p->buf, p->bufsize, 0, 0, 0, 0)); 3478 3479 return p; 3480 } 3481 3482 static void 3483 pipe_mbxtofd_ast(pPipe p) 3484 { 3485 int iss = p->iosb.status; 3486 int done = p->info->done; 3487 int iss2; 3488 int eof = (iss == SS$_ENDOFFILE); 3489 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0)); 3490 int err = !(iss&1) && !eof; 3491 #if defined(PERL_IMPLICIT_CONTEXT) 3492 pTHX = p->thx; 3493 #endif 3494 3495 if (done && myeof) { /* end piping */ 3496 close(p->fd_out); 3497 sys$dassgn(p->chan_in); 3498 *p->pipe_done = TRUE; 3499 _ckvmssts(sys$setef(pipe_ef)); 3500 return; 3501 } 3502 3503 if (!err && !eof) { /* good data to send to file */ 3504 p->buf[p->iosb.count] = '\n'; 3505 iss2 = write(p->fd_out, p->buf, p->iosb.count+1); 3506 if (iss2 < 0) { 3507 p->retry++; 3508 if (p->retry < MAX_RETRY) { 3509 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p)); 3510 return; 3511 } 3512 } 3513 p->retry = 0; 3514 } else if (err) { 3515 _ckvmssts(iss); 3516 } 3517 3518 3519 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb, 3520 pipe_mbxtofd_ast, p, 3521 p->buf, p->bufsize, 0, 0, 0, 0); 3522 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL; 3523 _ckvmssts(iss); 3524 } 3525 3526 3527 typedef struct _pipeloc PLOC; 3528 typedef struct _pipeloc* pPLOC; 3529 3530 struct _pipeloc { 3531 pPLOC next; 3532 char dir[NAM$C_MAXRSS+1]; 3533 }; 3534 static pPLOC head_PLOC = 0; 3535 3536 void 3537 free_pipelocs(pTHX_ void *head) 3538 { 3539 pPLOC p, pnext; 3540 pPLOC *pHead = (pPLOC *)head; 3541 3542 p = *pHead; 3543 while (p) { 3544 pnext = p->next; 3545 PerlMem_free(p); 3546 p = pnext; 3547 } 3548 *pHead = 0; 3549 } 3550 3551 static void 3552 store_pipelocs(pTHX) 3553 { 3554 int i; 3555 pPLOC p; 3556 AV *av = 0; 3557 SV *dirsv; 3558 GV *gv; 3559 char *dir, *x; 3560 char *unixdir; 3561 char temp[NAM$C_MAXRSS+1]; 3562 STRLEN n_a; 3563 3564 if (head_PLOC) 3565 free_pipelocs(aTHX_ &head_PLOC); 3566 3567 /* the . directory from @INC comes last */ 3568 3569 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3570 if (p == NULL) _ckvmssts(SS$_INSFMEM); 3571 p->next = head_PLOC; 3572 head_PLOC = p; 3573 strcpy(p->dir,"./"); 3574 3575 /* get the directory from $^X */ 3576 3577 unixdir = PerlMem_malloc(VMS_MAXRSS); 3578 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM); 3579 3580 #ifdef PERL_IMPLICIT_CONTEXT 3581 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3582 #else 3583 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */ 3584 #endif 3585 strcpy(temp, PL_origargv[0]); 3586 x = strrchr(temp,']'); 3587 if (x == NULL) { 3588 x = strrchr(temp,'>'); 3589 if (x == NULL) { 3590 /* It could be a UNIX path */ 3591 x = strrchr(temp,'/'); 3592 } 3593 } 3594 if (x) 3595 x[1] = '\0'; 3596 else { 3597 /* Got a bare name, so use default directory */ 3598 temp[0] = '.'; 3599 temp[1] = '\0'; 3600 } 3601 3602 if ((tounixpath_utf8(temp, unixdir, NULL)) != Nullch) { 3603 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3604 if (p == NULL) _ckvmssts(SS$_INSFMEM); 3605 p->next = head_PLOC; 3606 head_PLOC = p; 3607 strncpy(p->dir,unixdir,sizeof(p->dir)-1); 3608 p->dir[NAM$C_MAXRSS] = '\0'; 3609 } 3610 } 3611 3612 /* reverse order of @INC entries, skip "." since entered above */ 3613 3614 #ifdef PERL_IMPLICIT_CONTEXT 3615 if (aTHX) 3616 #endif 3617 if (PL_incgv) av = GvAVn(PL_incgv); 3618 3619 for (i = 0; av && i <= AvFILL(av); i++) { 3620 dirsv = *av_fetch(av,i,TRUE); 3621 3622 if (SvROK(dirsv)) continue; 3623 dir = SvPVx(dirsv,n_a); 3624 if (strcmp(dir,".") == 0) continue; 3625 if ((tounixpath_utf8(dir, unixdir, NULL)) == Nullch) 3626 continue; 3627 3628 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3629 p->next = head_PLOC; 3630 head_PLOC = p; 3631 strncpy(p->dir,unixdir,sizeof(p->dir)-1); 3632 p->dir[NAM$C_MAXRSS] = '\0'; 3633 } 3634 3635 /* most likely spot (ARCHLIB) put first in the list */ 3636 3637 #ifdef ARCHLIB_EXP 3638 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != Nullch) { 3639 p = (pPLOC) PerlMem_malloc(sizeof(PLOC)); 3640 if (p == NULL) _ckvmssts(SS$_INSFMEM); 3641 p->next = head_PLOC; 3642 head_PLOC = p; 3643 strncpy(p->dir,unixdir,sizeof(p->dir)-1); 3644 p->dir[NAM$C_MAXRSS] = '\0'; 3645 } 3646 #endif 3647 PerlMem_free(unixdir); 3648 } 3649 3650 static I32 3651 Perl_cando_by_name_int 3652 (pTHX_ I32 bit, bool effective, const char *fname, int opts); 3653 #if !defined(PERL_IMPLICIT_CONTEXT) 3654 #define cando_by_name_int Perl_cando_by_name_int 3655 #else 3656 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d) 3657 #endif 3658 3659 static char * 3660 find_vmspipe(pTHX) 3661 { 3662 static int vmspipe_file_status = 0; 3663 static char vmspipe_file[NAM$C_MAXRSS+1]; 3664 3665 /* already found? Check and use ... need read+execute permission */ 3666 3667 if (vmspipe_file_status == 1) { 3668 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3669 && cando_by_name_int 3670 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3671 return vmspipe_file; 3672 } 3673 vmspipe_file_status = 0; 3674 } 3675 3676 /* scan through stored @INC, $^X */ 3677 3678 if (vmspipe_file_status == 0) { 3679 char file[NAM$C_MAXRSS+1]; 3680 pPLOC p = head_PLOC; 3681 3682 while (p) { 3683 char * exp_res; 3684 int dirlen; 3685 strcpy(file, p->dir); 3686 dirlen = strlen(file); 3687 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen); 3688 file[NAM$C_MAXRSS] = '\0'; 3689 p = p->next; 3690 3691 exp_res = do_rmsexpand 3692 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL); 3693 if (!exp_res) continue; 3694 3695 if (cando_by_name_int 3696 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN) 3697 && cando_by_name_int 3698 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) { 3699 vmspipe_file_status = 1; 3700 return vmspipe_file; 3701 } 3702 } 3703 vmspipe_file_status = -1; /* failed, use tempfiles */ 3704 } 3705 3706 return 0; 3707 } 3708 3709 static FILE * 3710 vmspipe_tempfile(pTHX) 3711 { 3712 char file[NAM$C_MAXRSS+1]; 3713 FILE *fp; 3714 static int index = 0; 3715 Stat_t s0, s1; 3716 int cmp_result; 3717 3718 /* create a tempfile */ 3719 3720 /* we can't go from W, shr=get to R, shr=get without 3721 an intermediate vulnerable state, so don't bother trying... 3722 3723 and lib$spawn doesn't shr=put, so have to close the write 3724 3725 So... match up the creation date/time and the FID to 3726 make sure we're dealing with the same file 3727 3728 */ 3729 3730 index++; 3731 if (!decc_filename_unix_only) { 3732 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index); 3733 fp = fopen(file,"w"); 3734 if (!fp) { 3735 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index); 3736 fp = fopen(file,"w"); 3737 if (!fp) { 3738 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index); 3739 fp = fopen(file,"w"); 3740 } 3741 } 3742 } 3743 else { 3744 sprintf(file,"/tmp/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,"./perlpipe_%08.8x_%d.com",mypid,index); 3751 fp = fopen(file,"w"); 3752 } 3753 } 3754 } 3755 if (!fp) return 0; /* we're hosed */ 3756 3757 fprintf(fp,"$! 'f$verify(0)'\n"); 3758 fprintf(fp,"$! --- protect against nonstandard definitions ---\n"); 3759 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n"); 3760 fprintf(fp,"$ perl_define = \"define/nolog\"\n"); 3761 fprintf(fp,"$ perl_on = \"set noon\"\n"); 3762 fprintf(fp,"$ perl_exit = \"exit\"\n"); 3763 fprintf(fp,"$ perl_del = \"delete\"\n"); 3764 fprintf(fp,"$ pif = \"if\"\n"); 3765 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n"); 3766 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n"); 3767 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n"); 3768 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n"); 3769 fprintf(fp,"$! --- build command line to get max possible length\n"); 3770 fprintf(fp,"$c=perl_popen_cmd0\n"); 3771 fprintf(fp,"$c=c+perl_popen_cmd1\n"); 3772 fprintf(fp,"$c=c+perl_popen_cmd2\n"); 3773 fprintf(fp,"$x=perl_popen_cmd3\n"); 3774 fprintf(fp,"$c=c+x\n"); 3775 fprintf(fp,"$ perl_on\n"); 3776 fprintf(fp,"$ 'c'\n"); 3777 fprintf(fp,"$ perl_status = $STATUS\n"); 3778 fprintf(fp,"$ perl_del 'perl_cfile'\n"); 3779 fprintf(fp,"$ perl_exit 'perl_status'\n"); 3780 fsync(fileno(fp)); 3781 3782 fgetname(fp, file, 1); 3783 fstat(fileno(fp), (struct stat *)&s0); 3784 fclose(fp); 3785 3786 if (decc_filename_unix_only) 3787 do_tounixspec(file, file, 0, NULL); 3788 fp = fopen(file,"r","shr=get"); 3789 if (!fp) return 0; 3790 fstat(fileno(fp), (struct stat *)&s1); 3791 3792 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino); 3793 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) { 3794 fclose(fp); 3795 return 0; 3796 } 3797 3798 return fp; 3799 } 3800 3801 3802 static int vms_is_syscommand_xterm(void) 3803 { 3804 const static struct dsc$descriptor_s syscommand_dsc = 3805 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" }; 3806 3807 const static struct dsc$descriptor_s decwdisplay_dsc = 3808 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" }; 3809 3810 struct item_list_3 items[2]; 3811 unsigned short dvi_iosb[4]; 3812 unsigned long devchar; 3813 unsigned long devclass; 3814 int status; 3815 3816 /* Very simple check to guess if sys$command is a decterm? */ 3817 /* First see if the DECW$DISPLAY: device exists */ 3818 items[0].len = 4; 3819 items[0].code = DVI$_DEVCHAR; 3820 items[0].bufadr = &devchar; 3821 items[0].retadr = NULL; 3822 items[1].len = 0; 3823 items[1].code = 0; 3824 3825 status = sys$getdviw 3826 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL); 3827 3828 if ($VMS_STATUS_SUCCESS(status)) { 3829 status = dvi_iosb[0]; 3830 } 3831 3832 if (!$VMS_STATUS_SUCCESS(status)) { 3833 SETERRNO(EVMSERR, status); 3834 return -1; 3835 } 3836 3837 /* If it does, then for now assume that we are on a workstation */ 3838 /* Now verify that SYS$COMMAND is a terminal */ 3839 /* for creating the debugger DECTerm */ 3840 3841 items[0].len = 4; 3842 items[0].code = DVI$_DEVCLASS; 3843 items[0].bufadr = &devclass; 3844 items[0].retadr = NULL; 3845 items[1].len = 0; 3846 items[1].code = 0; 3847 3848 status = sys$getdviw 3849 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL); 3850 3851 if ($VMS_STATUS_SUCCESS(status)) { 3852 status = dvi_iosb[0]; 3853 } 3854 3855 if (!$VMS_STATUS_SUCCESS(status)) { 3856 SETERRNO(EVMSERR, status); 3857 return -1; 3858 } 3859 else { 3860 if (devclass == DC$_TERM) { 3861 return 0; 3862 } 3863 } 3864 return -1; 3865 } 3866 3867 /* If we are on a DECTerm, we can pretend to fork xterms when requested */ 3868 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode) 3869 { 3870 int status; 3871 int ret_stat; 3872 char * ret_char; 3873 char device_name[65]; 3874 unsigned short device_name_len; 3875 struct dsc$descriptor_s customization_dsc; 3876 struct dsc$descriptor_s device_name_dsc; 3877 const char * cptr; 3878 char * tptr; 3879 char customization[200]; 3880 char title[40]; 3881 pInfo info = NULL; 3882 char mbx1[64]; 3883 unsigned short p_chan; 3884 int n; 3885 unsigned short iosb[4]; 3886 struct item_list_3 items[2]; 3887 const char * cust_str = 3888 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n"; 3889 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T, 3890 DSC$K_CLASS_S, mbx1}; 3891 3892 /* LIB$FIND_IMAGE_SIGNAL needs a handler */ 3893 /*---------------------------------------*/ 3894 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret); 3895 3896 3897 /* Make sure that this is from the Perl debugger */ 3898 ret_char = strstr(cmd," xterm "); 3899 if (ret_char == NULL) 3900 return NULL; 3901 cptr = ret_char + 7; 3902 ret_char = strstr(cmd,"tty"); 3903 if (ret_char == NULL) 3904 return NULL; 3905 ret_char = strstr(cmd,"sleep"); 3906 if (ret_char == NULL) 3907 return NULL; 3908 3909 if (decw_term_port == 0) { 3910 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12"); 3911 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR"); 3912 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT"); 3913 3914 status = lib$find_image_symbol 3915 (&filename1_dsc, 3916 &decw_term_port_dsc, 3917 (void *)&decw_term_port, 3918 NULL, 3919 0); 3920 3921 /* Try again with the other image name */ 3922 if (!$VMS_STATUS_SUCCESS(status)) { 3923 3924 status = lib$find_image_symbol 3925 (&filename2_dsc, 3926 &decw_term_port_dsc, 3927 (void *)&decw_term_port, 3928 NULL, 3929 0); 3930 3931 } 3932 3933 } 3934 3935 3936 /* No decw$term_port, give it up */ 3937 if (!$VMS_STATUS_SUCCESS(status)) 3938 return NULL; 3939 3940 /* Are we on a workstation? */ 3941 /* to do: capture the rows / columns and pass their properties */ 3942 ret_stat = vms_is_syscommand_xterm(); 3943 if (ret_stat < 0) 3944 return NULL; 3945 3946 /* Make the title: */ 3947 ret_char = strstr(cptr,"-title"); 3948 if (ret_char != NULL) { 3949 while ((*cptr != 0) && (*cptr != '\"')) { 3950 cptr++; 3951 } 3952 if (*cptr == '\"') 3953 cptr++; 3954 n = 0; 3955 while ((*cptr != 0) && (*cptr != '\"')) { 3956 title[n] = *cptr; 3957 n++; 3958 if (n == 39) { 3959 title[39] == 0; 3960 break; 3961 } 3962 cptr++; 3963 } 3964 title[n] = 0; 3965 } 3966 else { 3967 /* Default title */ 3968 strcpy(title,"Perl Debug DECTerm"); 3969 } 3970 sprintf(customization, cust_str, title); 3971 3972 customization_dsc.dsc$a_pointer = customization; 3973 customization_dsc.dsc$w_length = strlen(customization); 3974 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 3975 customization_dsc.dsc$b_class = DSC$K_CLASS_S; 3976 3977 device_name_dsc.dsc$a_pointer = device_name; 3978 device_name_dsc.dsc$w_length = sizeof device_name -1; 3979 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 3980 device_name_dsc.dsc$b_class = DSC$K_CLASS_S; 3981 3982 device_name_len = 0; 3983 3984 /* Try to create the window */ 3985 status = (*decw_term_port) 3986 (NULL, 3987 NULL, 3988 &customization_dsc, 3989 &device_name_dsc, 3990 &device_name_len, 3991 NULL, 3992 NULL, 3993 NULL); 3994 if (!$VMS_STATUS_SUCCESS(status)) { 3995 SETERRNO(EVMSERR, status); 3996 return NULL; 3997 } 3998 3999 device_name[device_name_len] = '\0'; 4000 4001 /* Need to set this up to look like a pipe for cleanup */ 4002 n = sizeof(Info); 4003 status = lib$get_vm(&n, &info); 4004 if (!$VMS_STATUS_SUCCESS(status)) { 4005 SETERRNO(ENOMEM, status); 4006 return NULL; 4007 } 4008 4009 info->mode = *mode; 4010 info->done = FALSE; 4011 info->completion = 0; 4012 info->closing = FALSE; 4013 info->in = 0; 4014 info->out = 0; 4015 info->err = 0; 4016 info->fp = Nullfp; 4017 info->useFILE = 0; 4018 info->waiting = 0; 4019 info->in_done = TRUE; 4020 info->out_done = TRUE; 4021 info->err_done = TRUE; 4022 4023 /* Assign a channel on this so that it will persist, and not login */ 4024 /* We stash this channel in the info structure for reference. */ 4025 /* The created xterm self destructs when the last channel is removed */ 4026 /* and it appears that perl5db.pl (perl debugger) does this routinely */ 4027 /* So leave this assigned. */ 4028 device_name_dsc.dsc$w_length = device_name_len; 4029 status = sys$assign(&device_name_dsc,&info->xchan,0,0); 4030 if (!$VMS_STATUS_SUCCESS(status)) { 4031 SETERRNO(EVMSERR, status); 4032 return NULL; 4033 } 4034 info->xchan_valid = 1; 4035 4036 /* Now create a mailbox to be read by the application */ 4037 4038 create_mbx(aTHX_ &p_chan, &d_mbx1); 4039 4040 /* write the name of the created terminal to the mailbox */ 4041 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW, 4042 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0); 4043 4044 if (!$VMS_STATUS_SUCCESS(status)) { 4045 SETERRNO(EVMSERR, status); 4046 return NULL; 4047 } 4048 4049 info->fp = PerlIO_open(mbx1, mode); 4050 4051 /* Done with this channel */ 4052 sys$dassgn(p_chan); 4053 4054 /* If any errors, then clean up */ 4055 if (!info->fp) { 4056 n = sizeof(Info); 4057 _ckvmssts(lib$free_vm(&n, &info)); 4058 return NULL; 4059 } 4060 4061 /* All done */ 4062 return info->fp; 4063 } 4064 4065 static PerlIO * 4066 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts) 4067 { 4068 static int handler_set_up = FALSE; 4069 unsigned long int sts, flags = CLI$M_NOWAIT; 4070 /* The use of a GLOBAL table (as was done previously) rendered 4071 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL 4072 * environment. Hence we've switched to LOCAL symbol table. 4073 */ 4074 unsigned int table = LIB$K_CLI_LOCAL_SYM; 4075 int j, wait = 0, n; 4076 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe; 4077 char *in, *out, *err, mbx[512]; 4078 FILE *tpipe = 0; 4079 char tfilebuf[NAM$C_MAXRSS+1]; 4080 pInfo info = NULL; 4081 char cmd_sym_name[20]; 4082 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T, 4083 DSC$K_CLASS_S, symbol}; 4084 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T, 4085 DSC$K_CLASS_S, 0}; 4086 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T, 4087 DSC$K_CLASS_S, cmd_sym_name}; 4088 struct dsc$descriptor_s *vmscmd; 4089 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN"); 4090 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT"); 4091 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR"); 4092 4093 /* Check here for Xterm create request. This means looking for 4094 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it 4095 * is possible to create an xterm. 4096 */ 4097 if (*in_mode == 'r') { 4098 PerlIO * xterm_fd; 4099 4100 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode); 4101 if (xterm_fd != Nullfp) 4102 return xterm_fd; 4103 } 4104 4105 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */ 4106 4107 /* once-per-program initialization... 4108 note that the SETAST calls and the dual test of pipe_ef 4109 makes sure that only the FIRST thread through here does 4110 the initialization...all other threads wait until it's 4111 done. 4112 4113 Yeah, uglier than a pthread call, it's got all the stuff inline 4114 rather than in a separate routine. 4115 */ 4116 4117 if (!pipe_ef) { 4118 _ckvmssts(sys$setast(0)); 4119 if (!pipe_ef) { 4120 unsigned long int pidcode = JPI$_PID; 4121 $DESCRIPTOR(d_delay, RETRY_DELAY); 4122 _ckvmssts(lib$get_ef(&pipe_ef)); 4123 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4124 _ckvmssts(sys$bintim(&d_delay, delaytime)); 4125 } 4126 if (!handler_set_up) { 4127 _ckvmssts(sys$dclexh(&pipe_exitblock)); 4128 handler_set_up = TRUE; 4129 } 4130 _ckvmssts(sys$setast(1)); 4131 } 4132 4133 /* see if we can find a VMSPIPE.COM */ 4134 4135 tfilebuf[0] = '@'; 4136 vmspipe = find_vmspipe(aTHX); 4137 if (vmspipe) { 4138 strcpy(tfilebuf+1,vmspipe); 4139 } else { /* uh, oh...we're in tempfile hell */ 4140 tpipe = vmspipe_tempfile(aTHX); 4141 if (!tpipe) { /* a fish popular in Boston */ 4142 if (ckWARN(WARN_PIPE)) { 4143 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping"); 4144 } 4145 return Nullfp; 4146 } 4147 fgetname(tpipe,tfilebuf+1,1); 4148 } 4149 vmspipedsc.dsc$a_pointer = tfilebuf; 4150 vmspipedsc.dsc$w_length = strlen(tfilebuf); 4151 4152 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd); 4153 if (!(sts & 1)) { 4154 switch (sts) { 4155 case RMS$_FNF: case RMS$_DNF: 4156 set_errno(ENOENT); break; 4157 case RMS$_DIR: 4158 set_errno(ENOTDIR); break; 4159 case RMS$_DEV: 4160 set_errno(ENODEV); break; 4161 case RMS$_PRV: 4162 set_errno(EACCES); break; 4163 case RMS$_SYN: 4164 set_errno(EINVAL); break; 4165 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 4166 set_errno(E2BIG); break; 4167 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 4168 _ckvmssts(sts); /* fall through */ 4169 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 4170 set_errno(EVMSERR); 4171 } 4172 set_vaxc_errno(sts); 4173 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) { 4174 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno)); 4175 } 4176 *psts = sts; 4177 return Nullfp; 4178 } 4179 n = sizeof(Info); 4180 _ckvmssts(lib$get_vm(&n, &info)); 4181 4182 strcpy(mode,in_mode); 4183 info->mode = *mode; 4184 info->done = FALSE; 4185 info->completion = 0; 4186 info->closing = FALSE; 4187 info->in = 0; 4188 info->out = 0; 4189 info->err = 0; 4190 info->fp = Nullfp; 4191 info->useFILE = 0; 4192 info->waiting = 0; 4193 info->in_done = TRUE; 4194 info->out_done = TRUE; 4195 info->err_done = TRUE; 4196 info->xchan = 0; 4197 info->xchan_valid = 0; 4198 4199 in = PerlMem_malloc(VMS_MAXRSS); 4200 if (in == NULL) _ckvmssts(SS$_INSFMEM); 4201 out = PerlMem_malloc(VMS_MAXRSS); 4202 if (out == NULL) _ckvmssts(SS$_INSFMEM); 4203 err = PerlMem_malloc(VMS_MAXRSS); 4204 if (err == NULL) _ckvmssts(SS$_INSFMEM); 4205 4206 in[0] = out[0] = err[0] = '\0'; 4207 4208 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */ 4209 info->useFILE = 1; 4210 strcpy(p,p+1); 4211 } 4212 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */ 4213 wait = 1; 4214 strcpy(p,p+1); 4215 } 4216 4217 if (*mode == 'r') { /* piping from subroutine */ 4218 4219 info->out = pipe_infromchild_setup(aTHX_ mbx,out); 4220 if (info->out) { 4221 info->out->pipe_done = &info->out_done; 4222 info->out_done = FALSE; 4223 info->out->info = info; 4224 } 4225 if (!info->useFILE) { 4226 info->fp = PerlIO_open(mbx, mode); 4227 } else { 4228 info->fp = (PerlIO *) freopen(mbx, mode, stdin); 4229 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx); 4230 } 4231 4232 if (!info->fp && info->out) { 4233 sys$cancel(info->out->chan_out); 4234 4235 while (!info->out_done) { 4236 int done; 4237 _ckvmssts(sys$setast(0)); 4238 done = info->out_done; 4239 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4240 _ckvmssts(sys$setast(1)); 4241 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4242 } 4243 4244 if (info->out->buf) { 4245 n = info->out->bufsize * sizeof(char); 4246 _ckvmssts(lib$free_vm(&n, &info->out->buf)); 4247 } 4248 n = sizeof(Pipe); 4249 _ckvmssts(lib$free_vm(&n, &info->out)); 4250 n = sizeof(Info); 4251 _ckvmssts(lib$free_vm(&n, &info)); 4252 *psts = RMS$_FNF; 4253 return Nullfp; 4254 } 4255 4256 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4257 if (info->err) { 4258 info->err->pipe_done = &info->err_done; 4259 info->err_done = FALSE; 4260 info->err->info = info; 4261 } 4262 4263 } else if (*mode == 'w') { /* piping to subroutine */ 4264 4265 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4266 if (info->out) { 4267 info->out->pipe_done = &info->out_done; 4268 info->out_done = FALSE; 4269 info->out->info = info; 4270 } 4271 4272 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4273 if (info->err) { 4274 info->err->pipe_done = &info->err_done; 4275 info->err_done = FALSE; 4276 info->err->info = info; 4277 } 4278 4279 info->in = pipe_tochild_setup(aTHX_ in,mbx); 4280 if (!info->useFILE) { 4281 info->fp = PerlIO_open(mbx, mode); 4282 } else { 4283 info->fp = (PerlIO *) freopen(mbx, mode, stdout); 4284 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx); 4285 } 4286 4287 if (info->in) { 4288 info->in->pipe_done = &info->in_done; 4289 info->in_done = FALSE; 4290 info->in->info = info; 4291 } 4292 4293 /* error cleanup */ 4294 if (!info->fp && info->in) { 4295 info->done = TRUE; 4296 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0, 4297 0, 0, 0, 0, 0, 0, 0, 0)); 4298 4299 while (!info->in_done) { 4300 int done; 4301 _ckvmssts(sys$setast(0)); 4302 done = info->in_done; 4303 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4304 _ckvmssts(sys$setast(1)); 4305 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4306 } 4307 4308 if (info->in->buf) { 4309 n = info->in->bufsize * sizeof(char); 4310 _ckvmssts(lib$free_vm(&n, &info->in->buf)); 4311 } 4312 n = sizeof(Pipe); 4313 _ckvmssts(lib$free_vm(&n, &info->in)); 4314 n = sizeof(Info); 4315 _ckvmssts(lib$free_vm(&n, &info)); 4316 *psts = RMS$_FNF; 4317 return Nullfp; 4318 } 4319 4320 4321 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */ 4322 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out); 4323 if (info->out) { 4324 info->out->pipe_done = &info->out_done; 4325 info->out_done = FALSE; 4326 info->out->info = info; 4327 } 4328 4329 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err); 4330 if (info->err) { 4331 info->err->pipe_done = &info->err_done; 4332 info->err_done = FALSE; 4333 info->err->info = info; 4334 } 4335 } 4336 4337 symbol[MAX_DCL_SYMBOL] = '\0'; 4338 4339 strncpy(symbol, in, MAX_DCL_SYMBOL); 4340 d_symbol.dsc$w_length = strlen(symbol); 4341 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table)); 4342 4343 strncpy(symbol, err, MAX_DCL_SYMBOL); 4344 d_symbol.dsc$w_length = strlen(symbol); 4345 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table)); 4346 4347 strncpy(symbol, out, MAX_DCL_SYMBOL); 4348 d_symbol.dsc$w_length = strlen(symbol); 4349 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table)); 4350 4351 /* Done with the names for the pipes */ 4352 PerlMem_free(err); 4353 PerlMem_free(out); 4354 PerlMem_free(in); 4355 4356 p = vmscmd->dsc$a_pointer; 4357 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */ 4358 if (*p == '$') p++; /* remove leading $ */ 4359 while (*p == ' ' || *p == '\t') p++; 4360 4361 for (j = 0; j < 4; j++) { 4362 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4363 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4364 4365 strncpy(symbol, p, MAX_DCL_SYMBOL); 4366 d_symbol.dsc$w_length = strlen(symbol); 4367 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table)); 4368 4369 if (strlen(p) > MAX_DCL_SYMBOL) { 4370 p += MAX_DCL_SYMBOL; 4371 } else { 4372 p += strlen(p); 4373 } 4374 } 4375 _ckvmssts(sys$setast(0)); 4376 info->next=open_pipes; /* prepend to list */ 4377 open_pipes=info; 4378 _ckvmssts(sys$setast(1)); 4379 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT 4380 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still 4381 * have SYS$COMMAND if we need it. 4382 */ 4383 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags, 4384 0, &info->pid, &info->completion, 4385 0, popen_completion_ast,info,0,0,0)); 4386 4387 /* if we were using a tempfile, close it now */ 4388 4389 if (tpipe) fclose(tpipe); 4390 4391 /* once the subprocess is spawned, it has copied the symbols and 4392 we can get rid of ours */ 4393 4394 for (j = 0; j < 4; j++) { 4395 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j); 4396 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name); 4397 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table)); 4398 } 4399 _ckvmssts(lib$delete_symbol(&d_sym_in, &table)); 4400 _ckvmssts(lib$delete_symbol(&d_sym_err, &table)); 4401 _ckvmssts(lib$delete_symbol(&d_sym_out, &table)); 4402 vms_execfree(vmscmd); 4403 4404 #ifdef PERL_IMPLICIT_CONTEXT 4405 if (aTHX) 4406 #endif 4407 PL_forkprocess = info->pid; 4408 4409 if (wait) { 4410 int done = 0; 4411 while (!done) { 4412 _ckvmssts(sys$setast(0)); 4413 done = info->done; 4414 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4415 _ckvmssts(sys$setast(1)); 4416 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4417 } 4418 *psts = info->completion; 4419 /* Caller thinks it is open and tries to close it. */ 4420 /* This causes some problems, as it changes the error status */ 4421 /* my_pclose(info->fp); */ 4422 } else { 4423 *psts = info->pid; 4424 } 4425 return info->fp; 4426 } /* end of safe_popen */ 4427 4428 4429 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/ 4430 PerlIO * 4431 Perl_my_popen(pTHX_ const char *cmd, const char *mode) 4432 { 4433 int sts; 4434 TAINT_ENV(); 4435 TAINT_PROPER("popen"); 4436 PERL_FLUSHALL_FOR_CHILD; 4437 return safe_popen(aTHX_ cmd,mode,&sts); 4438 } 4439 4440 /*}}}*/ 4441 4442 /*{{{ I32 my_pclose(PerlIO *fp)*/ 4443 I32 Perl_my_pclose(pTHX_ PerlIO *fp) 4444 { 4445 pInfo info, last = NULL; 4446 unsigned long int retsts; 4447 int done, iss, n; 4448 int status; 4449 4450 for (info = open_pipes; info != NULL; last = info, info = info->next) 4451 if (info->fp == fp) break; 4452 4453 if (info == NULL) { /* no such pipe open */ 4454 set_errno(ECHILD); /* quoth POSIX */ 4455 set_vaxc_errno(SS$_NONEXPR); 4456 return -1; 4457 } 4458 4459 /* If we were writing to a subprocess, insure that someone reading from 4460 * the mailbox gets an EOF. It looks like a simple fclose() doesn't 4461 * produce an EOF record in the mailbox. 4462 * 4463 * well, at least sometimes it *does*, so we have to watch out for 4464 * the first EOF closing the pipe (and DASSGN'ing the channel)... 4465 */ 4466 if (info->fp) { 4467 if (!info->useFILE 4468 #if defined(USE_ITHREADS) 4469 && my_perl 4470 #endif 4471 && PL_perlio_fd_refcnt) 4472 PerlIO_flush(info->fp); 4473 else 4474 fflush((FILE *)info->fp); 4475 } 4476 4477 _ckvmssts(sys$setast(0)); 4478 info->closing = TRUE; 4479 done = info->done && info->in_done && info->out_done && info->err_done; 4480 /* hanging on write to Perl's input? cancel it */ 4481 if (info->mode == 'r' && info->out && !info->out_done) { 4482 if (info->out->chan_out) { 4483 _ckvmssts(sys$cancel(info->out->chan_out)); 4484 if (!info->out->chan_in) { /* EOF generation, need AST */ 4485 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0)); 4486 } 4487 } 4488 } 4489 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */ 4490 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0, 4491 0, 0, 0, 0, 0, 0)); 4492 _ckvmssts(sys$setast(1)); 4493 if (info->fp) { 4494 if (!info->useFILE 4495 #if defined(USE_ITHREADS) 4496 && my_perl 4497 #endif 4498 && PL_perlio_fd_refcnt) 4499 PerlIO_close(info->fp); 4500 else 4501 fclose((FILE *)info->fp); 4502 } 4503 /* 4504 we have to wait until subprocess completes, but ALSO wait until all 4505 the i/o completes...otherwise we'll be freeing the "info" structure 4506 that the i/o ASTs could still be using... 4507 */ 4508 4509 while (!done) { 4510 _ckvmssts(sys$setast(0)); 4511 done = info->done && info->in_done && info->out_done && info->err_done; 4512 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4513 _ckvmssts(sys$setast(1)); 4514 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4515 } 4516 retsts = info->completion; 4517 4518 /* remove from list of open pipes */ 4519 _ckvmssts(sys$setast(0)); 4520 if (last) last->next = info->next; 4521 else open_pipes = info->next; 4522 _ckvmssts(sys$setast(1)); 4523 4524 /* free buffers and structures */ 4525 4526 if (info->in) { 4527 if (info->in->buf) { 4528 n = info->in->bufsize * sizeof(char); 4529 _ckvmssts(lib$free_vm(&n, &info->in->buf)); 4530 } 4531 n = sizeof(Pipe); 4532 _ckvmssts(lib$free_vm(&n, &info->in)); 4533 } 4534 if (info->out) { 4535 if (info->out->buf) { 4536 n = info->out->bufsize * sizeof(char); 4537 _ckvmssts(lib$free_vm(&n, &info->out->buf)); 4538 } 4539 n = sizeof(Pipe); 4540 _ckvmssts(lib$free_vm(&n, &info->out)); 4541 } 4542 if (info->err) { 4543 if (info->err->buf) { 4544 n = info->err->bufsize * sizeof(char); 4545 _ckvmssts(lib$free_vm(&n, &info->err->buf)); 4546 } 4547 n = sizeof(Pipe); 4548 _ckvmssts(lib$free_vm(&n, &info->err)); 4549 } 4550 n = sizeof(Info); 4551 _ckvmssts(lib$free_vm(&n, &info)); 4552 4553 return retsts; 4554 4555 } /* end of my_pclose() */ 4556 4557 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000 4558 /* Roll our own prototype because we want this regardless of whether 4559 * _VMS_WAIT is defined. 4560 */ 4561 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options ); 4562 #endif 4563 /* sort-of waitpid; special handling of pipe clean-up for subprocesses 4564 created with popen(); otherwise partially emulate waitpid() unless 4565 we have a suitable one from the CRTL that came with VMS 7.2 and later. 4566 Also check processes not considered by the CRTL waitpid(). 4567 */ 4568 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/ 4569 Pid_t 4570 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags) 4571 { 4572 pInfo info; 4573 int done; 4574 int sts; 4575 int j; 4576 4577 if (statusp) *statusp = 0; 4578 4579 for (info = open_pipes; info != NULL; info = info->next) 4580 if (info->pid == pid) break; 4581 4582 if (info != NULL) { /* we know about this child */ 4583 while (!info->done) { 4584 _ckvmssts(sys$setast(0)); 4585 done = info->done; 4586 if (!done) _ckvmssts(sys$clref(pipe_ef)); 4587 _ckvmssts(sys$setast(1)); 4588 if (!done) _ckvmssts(sys$waitfr(pipe_ef)); 4589 } 4590 4591 if (statusp) *statusp = info->completion; 4592 return pid; 4593 } 4594 4595 /* child that already terminated? */ 4596 4597 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) { 4598 if (closed_list[j].pid == pid) { 4599 if (statusp) *statusp = closed_list[j].completion; 4600 return pid; 4601 } 4602 } 4603 4604 /* fall through if this child is not one of our own pipe children */ 4605 4606 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000 4607 4608 /* waitpid() became available in the CRTL as of VMS 7.0, but only 4609 * in 7.2 did we get a version that fills in the VMS completion 4610 * status as Perl has always tried to do. 4611 */ 4612 4613 sts = __vms_waitpid( pid, statusp, flags ); 4614 4615 if ( sts == 0 || !(sts == -1 && errno == ECHILD) ) 4616 return sts; 4617 4618 /* If the real waitpid tells us the child does not exist, we 4619 * fall through here to implement waiting for a child that 4620 * was created by some means other than exec() (say, spawned 4621 * from DCL) or to wait for a process that is not a subprocess 4622 * of the current process. 4623 */ 4624 4625 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */ 4626 4627 { 4628 $DESCRIPTOR(intdsc,"0 00:00:01"); 4629 unsigned long int ownercode = JPI$_OWNER, ownerpid; 4630 unsigned long int pidcode = JPI$_PID, mypid; 4631 unsigned long int interval[2]; 4632 unsigned int jpi_iosb[2]; 4633 struct itmlst_3 jpilist[2] = { 4634 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0}, 4635 { 0, 0, 0, 0} 4636 }; 4637 4638 if (pid <= 0) { 4639 /* Sorry folks, we don't presently implement rooting around for 4640 the first child we can find, and we definitely don't want to 4641 pass a pid of -1 to $getjpi, where it is a wildcard operation. 4642 */ 4643 set_errno(ENOTSUP); 4644 return -1; 4645 } 4646 4647 /* Get the owner of the child so I can warn if it's not mine. If the 4648 * process doesn't exist or I don't have the privs to look at it, 4649 * I can go home early. 4650 */ 4651 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL); 4652 if (sts & 1) sts = jpi_iosb[0]; 4653 if (!(sts & 1)) { 4654 switch (sts) { 4655 case SS$_NONEXPR: 4656 set_errno(ECHILD); 4657 break; 4658 case SS$_NOPRIV: 4659 set_errno(EACCES); 4660 break; 4661 default: 4662 _ckvmssts(sts); 4663 } 4664 set_vaxc_errno(sts); 4665 return -1; 4666 } 4667 4668 if (ckWARN(WARN_EXEC)) { 4669 /* remind folks they are asking for non-standard waitpid behavior */ 4670 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0)); 4671 if (ownerpid != mypid) 4672 Perl_warner(aTHX_ packWARN(WARN_EXEC), 4673 "waitpid: process %x is not a child of process %x", 4674 pid,mypid); 4675 } 4676 4677 /* simply check on it once a second until it's not there anymore. */ 4678 4679 _ckvmssts(sys$bintim(&intdsc,interval)); 4680 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) { 4681 _ckvmssts(sys$schdwk(0,0,interval,0)); 4682 _ckvmssts(sys$hiber()); 4683 } 4684 if (sts == SS$_NONEXPR) sts = SS$_NORMAL; 4685 4686 _ckvmssts(sts); 4687 return pid; 4688 } 4689 } /* end of waitpid() */ 4690 /*}}}*/ 4691 /*}}}*/ 4692 /*}}}*/ 4693 4694 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */ 4695 char * 4696 my_gconvert(double val, int ndig, int trail, char *buf) 4697 { 4698 static char __gcvtbuf[DBL_DIG+1]; 4699 char *loc; 4700 4701 loc = buf ? buf : __gcvtbuf; 4702 4703 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */ 4704 if (val < 1) { 4705 sprintf(loc,"%.*g",ndig,val); 4706 return loc; 4707 } 4708 #endif 4709 4710 if (val) { 4711 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG; 4712 return gcvt(val,ndig,loc); 4713 } 4714 else { 4715 loc[0] = '0'; loc[1] = '\0'; 4716 return loc; 4717 } 4718 4719 } 4720 /*}}}*/ 4721 4722 #if defined(__VAX) || !defined(NAML$C_MAXRSS) 4723 static int rms_free_search_context(struct FAB * fab) 4724 { 4725 struct NAM * nam; 4726 4727 nam = fab->fab$l_nam; 4728 nam->nam$b_nop |= NAM$M_SYNCHK; 4729 nam->nam$l_rlf = NULL; 4730 fab->fab$b_dns = 0; 4731 return sys$parse(fab, NULL, NULL); 4732 } 4733 4734 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam 4735 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0; 4736 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt) 4737 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt) 4738 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt)) 4739 #define rms_nam_esll(nam) nam.nam$b_esl 4740 #define rms_nam_esl(nam) nam.nam$b_esl 4741 #define rms_nam_name(nam) nam.nam$l_name 4742 #define rms_nam_namel(nam) nam.nam$l_name 4743 #define rms_nam_type(nam) nam.nam$l_type 4744 #define rms_nam_typel(nam) nam.nam$l_type 4745 #define rms_nam_ver(nam) nam.nam$l_ver 4746 #define rms_nam_verl(nam) nam.nam$l_ver 4747 #define rms_nam_rsll(nam) nam.nam$b_rsl 4748 #define rms_nam_rsl(nam) nam.nam$b_rsl 4749 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam 4750 #define rms_set_fna(fab, nam, name, size) \ 4751 { fab.fab$b_fns = size; fab.fab$l_fna = name; } 4752 #define rms_get_fna(fab, nam) fab.fab$l_fna 4753 #define rms_set_dna(fab, nam, name, size) \ 4754 { fab.fab$b_dns = size; fab.fab$l_dna = name; } 4755 #define rms_nam_dns(fab, nam) fab.fab$b_dns 4756 #define rms_set_esa(nam, name, size) \ 4757 { nam.nam$b_ess = size; nam.nam$l_esa = name; } 4758 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4759 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;} 4760 #define rms_set_rsa(nam, name, size) \ 4761 { nam.nam$l_rsa = name; nam.nam$b_rss = size; } 4762 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4763 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; } 4764 #define rms_nam_name_type_l_size(nam) \ 4765 (nam.nam$b_name + nam.nam$b_type) 4766 #else 4767 static int rms_free_search_context(struct FAB * fab) 4768 { 4769 struct NAML * nam; 4770 4771 nam = fab->fab$l_naml; 4772 nam->naml$b_nop |= NAM$M_SYNCHK; 4773 nam->naml$l_rlf = NULL; 4774 nam->naml$l_long_defname_size = 0; 4775 4776 fab->fab$b_dns = 0; 4777 return sys$parse(fab, NULL, NULL); 4778 } 4779 4780 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml 4781 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0; 4782 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt) 4783 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt) 4784 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt)) 4785 #define rms_nam_esll(nam) nam.naml$l_long_expand_size 4786 #define rms_nam_esl(nam) nam.naml$b_esl 4787 #define rms_nam_name(nam) nam.naml$l_name 4788 #define rms_nam_namel(nam) nam.naml$l_long_name 4789 #define rms_nam_type(nam) nam.naml$l_type 4790 #define rms_nam_typel(nam) nam.naml$l_long_type 4791 #define rms_nam_ver(nam) nam.naml$l_ver 4792 #define rms_nam_verl(nam) nam.naml$l_long_ver 4793 #define rms_nam_rsll(nam) nam.naml$l_long_result_size 4794 #define rms_nam_rsl(nam) nam.naml$b_rsl 4795 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam 4796 #define rms_set_fna(fab, nam, name, size) \ 4797 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \ 4798 nam.naml$l_long_filename_size = size; \ 4799 nam.naml$l_long_filename = name;} 4800 #define rms_get_fna(fab, nam) nam.naml$l_long_filename 4801 #define rms_set_dna(fab, nam, name, size) \ 4802 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \ 4803 nam.naml$l_long_defname_size = size; \ 4804 nam.naml$l_long_defname = name; } 4805 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size 4806 #define rms_set_esa(nam, name, size) \ 4807 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \ 4808 nam.naml$l_long_expand_alloc = size; \ 4809 nam.naml$l_long_expand = name; } 4810 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \ 4811 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \ 4812 nam.naml$l_long_expand = l_name; \ 4813 nam.naml$l_long_expand_alloc = l_size; } 4814 #define rms_set_rsa(nam, name, size) \ 4815 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \ 4816 nam.naml$l_long_result = name; \ 4817 nam.naml$l_long_result_alloc = size; } 4818 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \ 4819 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \ 4820 nam.naml$l_long_result = l_name; \ 4821 nam.naml$l_long_result_alloc = l_size; } 4822 #define rms_nam_name_type_l_size(nam) \ 4823 (nam.naml$l_long_name_size + nam.naml$l_long_type_size) 4824 #endif 4825 4826 4827 /* rms_erase 4828 * The CRTL for 8.3 and later can create symbolic links in any mode, 4829 * however in 8.3 the unlink/remove/delete routines will only properly handle 4830 * them if one of the PCP modes is active. 4831 */ 4832 static int rms_erase(const char * vmsname) 4833 { 4834 int status; 4835 struct FAB myfab = cc$rms_fab; 4836 rms_setup_nam(mynam); 4837 4838 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */ 4839 rms_bind_fab_nam(myfab, mynam); 4840 4841 /* Are we removing all versions? */ 4842 if (vms_unlink_all_versions == 1) { 4843 const char * defspec = ";*"; 4844 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */ 4845 } 4846 4847 #ifdef NAML$M_OPEN_SPECIAL 4848 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 4849 #endif 4850 4851 status = sys$erase(&myfab, 0, 0); 4852 4853 return status; 4854 } 4855 4856 4857 static int 4858 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc, 4859 const struct dsc$descriptor_s * vms_dst_dsc, 4860 unsigned long flags) 4861 { 4862 /* VMS and UNIX handle file permissions differently and the 4863 * the same ACL trick may be needed for renaming files, 4864 * especially if they are directories. 4865 */ 4866 4867 /* todo: get kill_file and rename to share common code */ 4868 /* I can not find online documentation for $change_acl 4869 * it appears to be replaced by $set_security some time ago */ 4870 4871 const unsigned int access_mode = 0; 4872 $DESCRIPTOR(obj_file_dsc,"FILE"); 4873 char *vmsname; 4874 char *rslt; 4875 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE; 4876 int aclsts, fndsts, rnsts = -1; 4877 unsigned int ctx = 0; 4878 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 4879 struct dsc$descriptor_s * clean_dsc; 4880 4881 struct myacedef { 4882 unsigned char myace$b_length; 4883 unsigned char myace$b_type; 4884 unsigned short int myace$w_flags; 4885 unsigned long int myace$l_access; 4886 unsigned long int myace$l_ident; 4887 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 4888 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 4889 0}, 4890 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0}; 4891 4892 struct item_list_3 4893 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0}, 4894 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0}, 4895 {0,0,0,0}}, 4896 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}}, 4897 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0}, 4898 {0,0,0,0}}; 4899 4900 4901 /* Expand the input spec using RMS, since we do not want to put 4902 * ACLs on the target of a symbolic link */ 4903 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1); 4904 if (vmsname == NULL) 4905 return SS$_INSFMEM; 4906 4907 rslt = do_rmsexpand(vms_src_dsc->dsc$a_pointer, 4908 vmsname, 4909 0, 4910 NULL, 4911 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK, 4912 NULL, 4913 NULL); 4914 if (rslt == NULL) { 4915 PerlMem_free(vmsname); 4916 return SS$_INSFMEM; 4917 } 4918 4919 /* So we get our own UIC to use as a rights identifier, 4920 * and the insert an ACE at the head of the ACL which allows us 4921 * to delete the file. 4922 */ 4923 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0)); 4924 4925 fildsc.dsc$w_length = strlen(vmsname); 4926 fildsc.dsc$a_pointer = vmsname; 4927 ctx = 0; 4928 newace.myace$l_ident = oldace.myace$l_ident; 4929 rnsts = SS$_ABORT; 4930 4931 /* Grab any existing ACEs with this identifier in case we fail */ 4932 clean_dsc = &fildsc; 4933 aclsts = fndsts = sys$get_security(&obj_file_dsc, 4934 &fildsc, 4935 NULL, 4936 OSS$M_WLOCK, 4937 findlst, 4938 &ctx, 4939 &access_mode); 4940 4941 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) { 4942 /* Add the new ACE . . . */ 4943 4944 /* if the sys$get_security succeeded, then ctx is valid, and the 4945 * object/file descriptors will be ignored. But otherwise they 4946 * are needed 4947 */ 4948 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL, 4949 OSS$M_RELCTX, addlst, &ctx, &access_mode); 4950 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 4951 set_errno(EVMSERR); 4952 set_vaxc_errno(aclsts); 4953 PerlMem_free(vmsname); 4954 return aclsts; 4955 } 4956 4957 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc, 4958 NULL, NULL, 4959 &flags, 4960 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 4961 4962 if ($VMS_STATUS_SUCCESS(rnsts)) { 4963 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc; 4964 } 4965 4966 /* Put things back the way they were. */ 4967 ctx = 0; 4968 aclsts = sys$get_security(&obj_file_dsc, 4969 clean_dsc, 4970 NULL, 4971 OSS$M_WLOCK, 4972 findlst, 4973 &ctx, 4974 &access_mode); 4975 4976 if ($VMS_STATUS_SUCCESS(aclsts)) { 4977 int sec_flags; 4978 4979 sec_flags = 0; 4980 if (!$VMS_STATUS_SUCCESS(fndsts)) 4981 sec_flags = OSS$M_RELCTX; 4982 4983 /* Get rid of the new ACE */ 4984 aclsts = sys$set_security(NULL, NULL, NULL, 4985 sec_flags, dellst, &ctx, &access_mode); 4986 4987 /* If there was an old ACE, put it back */ 4988 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) { 4989 addlst[0].bufadr = &oldace; 4990 aclsts = sys$set_security(NULL, NULL, NULL, 4991 OSS$M_RELCTX, addlst, &ctx, &access_mode); 4992 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) { 4993 set_errno(EVMSERR); 4994 set_vaxc_errno(aclsts); 4995 rnsts = aclsts; 4996 } 4997 } else { 4998 int aclsts2; 4999 5000 /* Try to clear the lock on the ACL list */ 5001 aclsts2 = sys$set_security(NULL, NULL, NULL, 5002 OSS$M_RELCTX, NULL, &ctx, &access_mode); 5003 5004 /* Rename errors are most important */ 5005 if (!$VMS_STATUS_SUCCESS(rnsts)) 5006 aclsts = rnsts; 5007 set_errno(EVMSERR); 5008 set_vaxc_errno(aclsts); 5009 rnsts = aclsts; 5010 } 5011 } 5012 else { 5013 if (aclsts != SS$_ACLEMPTY) 5014 rnsts = aclsts; 5015 } 5016 } 5017 else 5018 rnsts = fndsts; 5019 5020 PerlMem_free(vmsname); 5021 return rnsts; 5022 } 5023 5024 5025 /*{{{int rename(const char *, const char * */ 5026 /* Not exactly what X/Open says to do, but doing it absolutely right 5027 * and efficiently would require a lot more work. This should be close 5028 * enough to pass all but the most strict X/Open compliance test. 5029 */ 5030 int 5031 Perl_rename(pTHX_ const char *src, const char * dst) 5032 { 5033 int retval; 5034 int pre_delete = 0; 5035 int src_sts; 5036 int dst_sts; 5037 Stat_t src_st; 5038 Stat_t dst_st; 5039 5040 /* Validate the source file */ 5041 src_sts = flex_lstat(src, &src_st); 5042 if (src_sts != 0) { 5043 5044 /* No source file or other problem */ 5045 return src_sts; 5046 } 5047 5048 dst_sts = flex_lstat(dst, &dst_st); 5049 if (dst_sts == 0) { 5050 5051 if (dst_st.st_dev != src_st.st_dev) { 5052 /* Must be on the same device */ 5053 errno = EXDEV; 5054 return -1; 5055 } 5056 5057 /* VMS_INO_T_COMPARE is true if the inodes are different 5058 * to match the output of memcmp 5059 */ 5060 5061 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) { 5062 /* That was easy, the files are the same! */ 5063 return 0; 5064 } 5065 5066 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) { 5067 /* If source is a directory, so must be dest */ 5068 errno = EISDIR; 5069 return -1; 5070 } 5071 5072 } 5073 5074 5075 if ((dst_sts == 0) && 5076 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) { 5077 5078 /* We have issues here if vms_unlink_all_versions is set 5079 * If the destination exists, and is not a directory, then 5080 * we must delete in advance. 5081 * 5082 * If the src is a directory, then we must always pre-delete 5083 * the destination. 5084 * 5085 * If we successfully delete the dst in advance, and the rename fails 5086 * X/Open requires that errno be EIO. 5087 * 5088 */ 5089 5090 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) { 5091 int d_sts; 5092 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode)); 5093 if (d_sts != 0) 5094 return d_sts; 5095 5096 /* We killed the destination, so only errno now is EIO */ 5097 pre_delete = 1; 5098 } 5099 } 5100 5101 /* Originally the idea was to call the CRTL rename() and only 5102 * try the lib$rename_file if it failed. 5103 * It turns out that there are too many variants in what the 5104 * the CRTL rename might do, so only use lib$rename_file 5105 */ 5106 retval = -1; 5107 5108 { 5109 /* Is the source and dest both in VMS format */ 5110 /* if the source is a directory, then need to fileify */ 5111 /* and dest must be a directory or non-existant. */ 5112 5113 char * vms_src; 5114 char * vms_dst; 5115 int sts; 5116 char * ret_str; 5117 unsigned long flags; 5118 struct dsc$descriptor_s old_file_dsc; 5119 struct dsc$descriptor_s new_file_dsc; 5120 5121 /* We need to modify the src and dst depending 5122 * on if one or more of them are directories. 5123 */ 5124 5125 vms_src = PerlMem_malloc(VMS_MAXRSS); 5126 if (vms_src == NULL) 5127 _ckvmssts(SS$_INSFMEM); 5128 5129 /* Source is always a VMS format file */ 5130 ret_str = do_tovmsspec(src, vms_src, 0, NULL); 5131 if (ret_str == NULL) { 5132 PerlMem_free(vms_src); 5133 errno = EIO; 5134 return -1; 5135 } 5136 5137 vms_dst = PerlMem_malloc(VMS_MAXRSS); 5138 if (vms_dst == NULL) 5139 _ckvmssts(SS$_INSFMEM); 5140 5141 if (S_ISDIR(src_st.st_mode)) { 5142 char * ret_str; 5143 char * vms_dir_file; 5144 5145 vms_dir_file = PerlMem_malloc(VMS_MAXRSS); 5146 if (vms_dir_file == NULL) 5147 _ckvmssts(SS$_INSFMEM); 5148 5149 /* The source must be a file specification */ 5150 ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL); 5151 if (ret_str == NULL) { 5152 PerlMem_free(vms_src); 5153 PerlMem_free(vms_dst); 5154 PerlMem_free(vms_dir_file); 5155 errno = EIO; 5156 return -1; 5157 } 5158 PerlMem_free(vms_src); 5159 vms_src = vms_dir_file; 5160 5161 /* If the dest is a directory, we must remove it 5162 if (dst_sts == 0) { 5163 int d_sts; 5164 d_sts = mp_do_kill_file(aTHX_ dst, 1); 5165 if (d_sts != 0) { 5166 PerlMem_free(vms_src); 5167 PerlMem_free(vms_dst); 5168 errno = EIO; 5169 return sts; 5170 } 5171 5172 pre_delete = 1; 5173 } 5174 5175 /* The dest must be a VMS file specification */ 5176 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL); 5177 if (ret_str == NULL) { 5178 PerlMem_free(vms_src); 5179 PerlMem_free(vms_dst); 5180 errno = EIO; 5181 return -1; 5182 } 5183 5184 /* The source must be a file specification */ 5185 vms_dir_file = PerlMem_malloc(VMS_MAXRSS); 5186 if (vms_dir_file == NULL) 5187 _ckvmssts(SS$_INSFMEM); 5188 5189 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL); 5190 if (ret_str == NULL) { 5191 PerlMem_free(vms_src); 5192 PerlMem_free(vms_dst); 5193 PerlMem_free(vms_dir_file); 5194 errno = EIO; 5195 return -1; 5196 } 5197 PerlMem_free(vms_dst); 5198 vms_dst = vms_dir_file; 5199 5200 } else { 5201 /* File to file or file to new dir */ 5202 5203 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) { 5204 /* VMS pathify a dir target */ 5205 ret_str = do_tovmspath(dst, vms_dst, 0, NULL); 5206 if (ret_str == NULL) { 5207 PerlMem_free(vms_src); 5208 PerlMem_free(vms_dst); 5209 errno = EIO; 5210 return -1; 5211 } 5212 } else { 5213 5214 /* fileify a target VMS file specification */ 5215 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL); 5216 if (ret_str == NULL) { 5217 PerlMem_free(vms_src); 5218 PerlMem_free(vms_dst); 5219 errno = EIO; 5220 return -1; 5221 } 5222 } 5223 } 5224 5225 old_file_dsc.dsc$a_pointer = vms_src; 5226 old_file_dsc.dsc$w_length = strlen(vms_src); 5227 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5228 old_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5229 5230 new_file_dsc.dsc$a_pointer = vms_dst; 5231 new_file_dsc.dsc$w_length = strlen(vms_dst); 5232 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 5233 new_file_dsc.dsc$b_class = DSC$K_CLASS_S; 5234 5235 flags = 0; 5236 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5237 flags |= 2; /* LIB$M_FIL_LONG_NAMES */ 5238 #endif 5239 5240 sts = lib$rename_file(&old_file_dsc, 5241 &new_file_dsc, 5242 NULL, NULL, 5243 &flags, 5244 NULL, NULL, NULL, NULL, NULL, NULL, NULL); 5245 if (!$VMS_STATUS_SUCCESS(sts)) { 5246 5247 /* We could have failed because VMS style permissions do not 5248 * permit renames that UNIX will allow. Just like the hack 5249 * in for kill_file. 5250 */ 5251 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags); 5252 } 5253 5254 PerlMem_free(vms_src); 5255 PerlMem_free(vms_dst); 5256 if (!$VMS_STATUS_SUCCESS(sts)) { 5257 errno = EIO; 5258 return -1; 5259 } 5260 retval = 0; 5261 } 5262 5263 if (vms_unlink_all_versions) { 5264 /* Now get rid of any previous versions of the source file that 5265 * might still exist 5266 */ 5267 int save_errno; 5268 save_errno = errno; 5269 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode)); 5270 errno = save_errno; 5271 } 5272 5273 /* We deleted the destination, so must force the error to be EIO */ 5274 if ((retval != 0) && (pre_delete != 0)) 5275 errno = EIO; 5276 5277 return retval; 5278 } 5279 /*}}}*/ 5280 5281 5282 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/ 5283 /* Shortcut for common case of simple calls to $PARSE and $SEARCH 5284 * to expand file specification. Allows for a single default file 5285 * specification and a simple mask of options. If outbuf is non-NULL, 5286 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which 5287 * the resultant file specification is placed. If outbuf is NULL, the 5288 * resultant file specification is placed into a static buffer. 5289 * The third argument, if non-NULL, is taken to be a default file 5290 * specification string. The fourth argument is unused at present. 5291 * rmesexpand() returns the address of the resultant string if 5292 * successful, and NULL on error. 5293 * 5294 * New functionality for previously unused opts value: 5295 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format. 5296 * PERL_RMSEXPAND_M_LONG - Want output in long formst 5297 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify 5298 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target 5299 */ 5300 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *); 5301 5302 static char * 5303 mp_do_rmsexpand 5304 (pTHX_ const char *filespec, 5305 char *outbuf, 5306 int ts, 5307 const char *defspec, 5308 unsigned opts, 5309 int * fs_utf8, 5310 int * dfs_utf8) 5311 { 5312 static char __rmsexpand_retbuf[VMS_MAXRSS]; 5313 char * vmsfspec, *tmpfspec; 5314 char * esa, *cp, *out = NULL; 5315 char * tbuf; 5316 char * esal = NULL; 5317 char * outbufl; 5318 struct FAB myfab = cc$rms_fab; 5319 rms_setup_nam(mynam); 5320 STRLEN speclen; 5321 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0; 5322 int sts; 5323 5324 /* temp hack until UTF8 is actually implemented */ 5325 if (fs_utf8 != NULL) 5326 *fs_utf8 = 0; 5327 5328 if (!filespec || !*filespec) { 5329 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL); 5330 return NULL; 5331 } 5332 if (!outbuf) { 5333 if (ts) out = Newx(outbuf,VMS_MAXRSS,char); 5334 else outbuf = __rmsexpand_retbuf; 5335 } 5336 5337 vmsfspec = NULL; 5338 tmpfspec = NULL; 5339 outbufl = NULL; 5340 5341 isunix = 0; 5342 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) { 5343 isunix = is_unix_filespec(filespec); 5344 if (isunix) { 5345 vmsfspec = PerlMem_malloc(VMS_MAXRSS); 5346 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM); 5347 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) { 5348 PerlMem_free(vmsfspec); 5349 if (out) 5350 Safefree(out); 5351 return NULL; 5352 } 5353 filespec = vmsfspec; 5354 5355 /* Unless we are forcing to VMS format, a UNIX input means 5356 * UNIX output, and that requires long names to be used 5357 */ 5358 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5359 if ((opts & PERL_RMSEXPAND_M_VMS) == 0) 5360 opts |= PERL_RMSEXPAND_M_LONG; 5361 else 5362 #endif 5363 isunix = 0; 5364 } 5365 } 5366 5367 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */ 5368 rms_bind_fab_nam(myfab, mynam); 5369 5370 if (defspec && *defspec) { 5371 int t_isunix; 5372 t_isunix = is_unix_filespec(defspec); 5373 if (t_isunix) { 5374 tmpfspec = PerlMem_malloc(VMS_MAXRSS); 5375 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM); 5376 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) { 5377 PerlMem_free(tmpfspec); 5378 if (vmsfspec != NULL) 5379 PerlMem_free(vmsfspec); 5380 if (out) 5381 Safefree(out); 5382 return NULL; 5383 } 5384 defspec = tmpfspec; 5385 } 5386 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */ 5387 } 5388 5389 esa = PerlMem_malloc(NAM$C_MAXRSS + 1); 5390 if (esa == NULL) _ckvmssts(SS$_INSFMEM); 5391 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5392 esal = PerlMem_malloc(VMS_MAXRSS); 5393 if (esal == NULL) _ckvmssts(SS$_INSFMEM); 5394 #endif 5395 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1); 5396 5397 /* If a NAML block is used RMS always writes to the long and short 5398 * addresses unless you suppress the short name. 5399 */ 5400 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5401 outbufl = PerlMem_malloc(VMS_MAXRSS); 5402 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM); 5403 #endif 5404 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1)); 5405 5406 #ifdef NAM$M_NO_SHORT_UPCASE 5407 if (decc_efs_case_preserve) 5408 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE); 5409 #endif 5410 5411 /* We may not want to follow symbolic links */ 5412 #ifdef NAML$M_OPEN_SPECIAL 5413 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5414 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5415 #endif 5416 5417 /* First attempt to parse as an existing file */ 5418 retsts = sys$parse(&myfab,0,0); 5419 if (!(retsts & STS$K_SUCCESS)) { 5420 5421 /* Could not find the file, try as syntax only if error is not fatal */ 5422 rms_set_nam_nop(mynam, NAM$M_SYNCHK); 5423 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) { 5424 retsts = sys$parse(&myfab,0,0); 5425 if (retsts & STS$K_SUCCESS) goto expanded; 5426 } 5427 5428 /* Still could not parse the file specification */ 5429 /*----------------------------------------------*/ 5430 sts = rms_free_search_context(&myfab); /* Free search context */ 5431 if (out) Safefree(out); 5432 if (tmpfspec != NULL) 5433 PerlMem_free(tmpfspec); 5434 if (vmsfspec != NULL) 5435 PerlMem_free(vmsfspec); 5436 if (outbufl != NULL) 5437 PerlMem_free(outbufl); 5438 PerlMem_free(esa); 5439 if (esal != NULL) 5440 PerlMem_free(esal); 5441 set_vaxc_errno(retsts); 5442 if (retsts == RMS$_PRV) set_errno(EACCES); 5443 else if (retsts == RMS$_DEV) set_errno(ENODEV); 5444 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 5445 else set_errno(EVMSERR); 5446 return NULL; 5447 } 5448 retsts = sys$search(&myfab,0,0); 5449 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) { 5450 sts = rms_free_search_context(&myfab); /* Free search context */ 5451 if (out) Safefree(out); 5452 if (tmpfspec != NULL) 5453 PerlMem_free(tmpfspec); 5454 if (vmsfspec != NULL) 5455 PerlMem_free(vmsfspec); 5456 if (outbufl != NULL) 5457 PerlMem_free(outbufl); 5458 PerlMem_free(esa); 5459 if (esal != NULL) 5460 PerlMem_free(esal); 5461 set_vaxc_errno(retsts); 5462 if (retsts == RMS$_PRV) set_errno(EACCES); 5463 else set_errno(EVMSERR); 5464 return NULL; 5465 } 5466 5467 /* If the input filespec contained any lowercase characters, 5468 * downcase the result for compatibility with Unix-minded code. */ 5469 expanded: 5470 if (!decc_efs_case_preserve) { 5471 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++) 5472 if (islower(*tbuf)) { haslower = 1; break; } 5473 } 5474 5475 /* Is a long or a short name expected */ 5476 /*------------------------------------*/ 5477 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5478 if (rms_nam_rsll(mynam)) { 5479 tbuf = outbufl; 5480 speclen = rms_nam_rsll(mynam); 5481 } 5482 else { 5483 tbuf = esal; /* Not esa */ 5484 speclen = rms_nam_esll(mynam); 5485 } 5486 } 5487 else { 5488 if (rms_nam_rsl(mynam)) { 5489 tbuf = outbuf; 5490 speclen = rms_nam_rsl(mynam); 5491 } 5492 else { 5493 tbuf = esa; /* Not esal */ 5494 speclen = rms_nam_esl(mynam); 5495 } 5496 } 5497 tbuf[speclen] = '\0'; 5498 5499 /* Trim off null fields added by $PARSE 5500 * If type > 1 char, must have been specified in original or default spec 5501 * (not true for version; $SEARCH may have added version of existing file). 5502 */ 5503 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER); 5504 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5505 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5506 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1); 5507 } 5508 else { 5509 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) && 5510 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1); 5511 } 5512 if (trimver || trimtype) { 5513 if (defspec && *defspec) { 5514 char *defesal = NULL; 5515 char *defesa = NULL; 5516 defesa = PerlMem_malloc(VMS_MAXRSS + 1); 5517 if (defesa != NULL) { 5518 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5519 defesal = PerlMem_malloc(VMS_MAXRSS + 1); 5520 if (defesal == NULL) _ckvmssts(SS$_INSFMEM); 5521 #endif 5522 struct FAB deffab = cc$rms_fab; 5523 rms_setup_nam(defnam); 5524 5525 rms_bind_fab_nam(deffab, defnam); 5526 5527 /* Cast ok */ 5528 rms_set_fna 5529 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam)); 5530 5531 /* RMS needs the esa/esal as a work area if wildcards are involved */ 5532 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1); 5533 5534 rms_clear_nam_nop(defnam); 5535 rms_set_nam_nop(defnam, NAM$M_SYNCHK); 5536 #ifdef NAM$M_NO_SHORT_UPCASE 5537 if (decc_efs_case_preserve) 5538 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE); 5539 #endif 5540 #ifdef NAML$M_OPEN_SPECIAL 5541 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0) 5542 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL); 5543 #endif 5544 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) { 5545 if (trimver) { 5546 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER); 5547 } 5548 if (trimtype) { 5549 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE); 5550 } 5551 } 5552 if (defesal != NULL) 5553 PerlMem_free(defesal); 5554 PerlMem_free(defesa); 5555 } 5556 } 5557 if (trimver) { 5558 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5559 if (*(rms_nam_verl(mynam)) != '\"') 5560 speclen = rms_nam_verl(mynam) - tbuf; 5561 } 5562 else { 5563 if (*(rms_nam_ver(mynam)) != '\"') 5564 speclen = rms_nam_ver(mynam) - tbuf; 5565 } 5566 } 5567 if (trimtype) { 5568 /* If we didn't already trim version, copy down */ 5569 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5570 if (speclen > rms_nam_verl(mynam) - tbuf) 5571 memmove 5572 (rms_nam_typel(mynam), 5573 rms_nam_verl(mynam), 5574 speclen - (rms_nam_verl(mynam) - tbuf)); 5575 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam); 5576 } 5577 else { 5578 if (speclen > rms_nam_ver(mynam) - tbuf) 5579 memmove 5580 (rms_nam_type(mynam), 5581 rms_nam_ver(mynam), 5582 speclen - (rms_nam_ver(mynam) - tbuf)); 5583 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam); 5584 } 5585 } 5586 } 5587 5588 /* Done with these copies of the input files */ 5589 /*-------------------------------------------*/ 5590 if (vmsfspec != NULL) 5591 PerlMem_free(vmsfspec); 5592 if (tmpfspec != NULL) 5593 PerlMem_free(tmpfspec); 5594 5595 /* If we just had a directory spec on input, $PARSE "helpfully" 5596 * adds an empty name and type for us */ 5597 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5598 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5599 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) && 5600 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 && 5601 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5602 speclen = rms_nam_namel(mynam) - tbuf; 5603 } 5604 else 5605 #endif 5606 { 5607 if (rms_nam_name(mynam) == rms_nam_type(mynam) && 5608 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 && 5609 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME))) 5610 speclen = rms_nam_name(mynam) - tbuf; 5611 } 5612 5613 /* Posix format specifications must have matching quotes */ 5614 if (speclen < (VMS_MAXRSS - 1)) { 5615 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) { 5616 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) { 5617 tbuf[speclen] = '\"'; 5618 speclen++; 5619 } 5620 } 5621 } 5622 tbuf[speclen] = '\0'; 5623 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf); 5624 5625 /* Have we been working with an expanded, but not resultant, spec? */ 5626 /* Also, convert back to Unix syntax if necessary. */ 5627 { 5628 int rsl; 5629 5630 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5631 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) { 5632 rsl = rms_nam_rsll(mynam); 5633 } else 5634 #endif 5635 { 5636 rsl = rms_nam_rsl(mynam); 5637 } 5638 if (!rsl) { 5639 if (isunix) { 5640 if (do_tounixspec(tbuf, outbuf ,0 , fs_utf8) == NULL) { 5641 if (out) Safefree(out); 5642 if (esal != NULL) 5643 PerlMem_free(esal); 5644 PerlMem_free(esa); 5645 if (outbufl != NULL) 5646 PerlMem_free(outbufl); 5647 return NULL; 5648 } 5649 } 5650 else strcpy(outbuf, tbuf); 5651 } 5652 else if (isunix) { 5653 tmpfspec = PerlMem_malloc(VMS_MAXRSS); 5654 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM); 5655 if (do_tounixspec(tbuf,tmpfspec,0,fs_utf8) == NULL) { 5656 if (out) Safefree(out); 5657 PerlMem_free(esa); 5658 if (esal != NULL) 5659 PerlMem_free(esal); 5660 PerlMem_free(tmpfspec); 5661 if (outbufl != NULL) 5662 PerlMem_free(outbufl); 5663 return NULL; 5664 } 5665 strcpy(outbuf,tmpfspec); 5666 PerlMem_free(tmpfspec); 5667 } 5668 } 5669 rms_set_rsal(mynam, NULL, 0, NULL, 0); 5670 sts = rms_free_search_context(&myfab); /* Free search context */ 5671 PerlMem_free(esa); 5672 if (esal != NULL) 5673 PerlMem_free(esal); 5674 if (outbufl != NULL) 5675 PerlMem_free(outbufl); 5676 return outbuf; 5677 } 5678 /*}}}*/ 5679 /* External entry points */ 5680 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5681 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); } 5682 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt) 5683 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); } 5684 char *Perl_rmsexpand_utf8 5685 (pTHX_ const char *spec, char *buf, const char *def, 5686 unsigned opt, int * fs_utf8, int * dfs_utf8) 5687 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); } 5688 char *Perl_rmsexpand_utf8_ts 5689 (pTHX_ const char *spec, char *buf, const char *def, 5690 unsigned opt, int * fs_utf8, int * dfs_utf8) 5691 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); } 5692 5693 5694 /* 5695 ** The following routines are provided to make life easier when 5696 ** converting among VMS-style and Unix-style directory specifications. 5697 ** All will take input specifications in either VMS or Unix syntax. On 5698 ** failure, all return NULL. If successful, the routines listed below 5699 ** return a pointer to a buffer containing the appropriately 5700 ** reformatted spec (and, therefore, subsequent calls to that routine 5701 ** will clobber the result), while the routines of the same names with 5702 ** a _ts suffix appended will return a pointer to a mallocd string 5703 ** containing the appropriately reformatted spec. 5704 ** In all cases, only explicit syntax is altered; no check is made that 5705 ** the resulting string is valid or that the directory in question 5706 ** actually exists. 5707 ** 5708 ** fileify_dirspec() - convert a directory spec into the name of the 5709 ** directory file (i.e. what you can stat() to see if it's a dir). 5710 ** The style (VMS or Unix) of the result is the same as the style 5711 ** of the parameter passed in. 5712 ** pathify_dirspec() - convert a directory spec into a path (i.e. 5713 ** what you prepend to a filename to indicate what directory it's in). 5714 ** The style (VMS or Unix) of the result is the same as the style 5715 ** of the parameter passed in. 5716 ** tounixpath() - convert a directory spec into a Unix-style path. 5717 ** tovmspath() - convert a directory spec into a VMS-style path. 5718 ** tounixspec() - convert any file spec into a Unix-style file spec. 5719 ** tovmsspec() - convert any file spec into a VMS-style spec. 5720 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec. 5721 ** 5722 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu> 5723 ** Permission is given to distribute this code as part of the Perl 5724 ** standard distribution under the terms of the GNU General Public 5725 ** License or the Perl Artistic License. Copies of each may be 5726 ** found in the Perl standard distribution. 5727 */ 5728 5729 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/ 5730 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl) 5731 { 5732 static char __fileify_retbuf[VMS_MAXRSS]; 5733 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0; 5734 char *retspec, *cp1, *cp2, *lastdir; 5735 char *trndir, *vmsdir; 5736 unsigned short int trnlnm_iter_count; 5737 int sts; 5738 if (utf8_fl != NULL) 5739 *utf8_fl = 0; 5740 5741 if (!dir || !*dir) { 5742 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL; 5743 } 5744 dirlen = strlen(dir); 5745 while (dirlen && dir[dirlen-1] == '/') --dirlen; 5746 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */ 5747 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) { 5748 dir = "/sys$disk"; 5749 dirlen = 9; 5750 } 5751 else 5752 dirlen = 1; 5753 } 5754 if (dirlen > (VMS_MAXRSS - 1)) { 5755 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); 5756 return NULL; 5757 } 5758 trndir = PerlMem_malloc(VMS_MAXRSS + 1); 5759 if (trndir == NULL) _ckvmssts(SS$_INSFMEM); 5760 if (!strpbrk(dir+1,"/]>:") && 5761 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) { 5762 strcpy(trndir,*dir == '/' ? dir + 1: dir); 5763 trnlnm_iter_count = 0; 5764 while (!strpbrk(trndir,"/]>:") && my_trnlnm(trndir,trndir,0)) { 5765 trnlnm_iter_count++; 5766 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 5767 } 5768 dirlen = strlen(trndir); 5769 } 5770 else { 5771 strncpy(trndir,dir,dirlen); 5772 trndir[dirlen] = '\0'; 5773 } 5774 5775 /* At this point we are done with *dir and use *trndir which is a 5776 * copy that can be modified. *dir must not be modified. 5777 */ 5778 5779 /* If we were handed a rooted logical name or spec, treat it like a 5780 * simple directory, so that 5781 * $ Define myroot dev:[dir.] 5782 * ... do_fileify_dirspec("myroot",buf,1) ... 5783 * does something useful. 5784 */ 5785 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) { 5786 trndir[--dirlen] = '\0'; 5787 trndir[dirlen-1] = ']'; 5788 } 5789 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) { 5790 trndir[--dirlen] = '\0'; 5791 trndir[dirlen-1] = '>'; 5792 } 5793 5794 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) { 5795 /* If we've got an explicit filename, we can just shuffle the string. */ 5796 if (*(cp1+1)) hasfilename = 1; 5797 /* Similarly, we can just back up a level if we've got multiple levels 5798 of explicit directories in a VMS spec which ends with directories. */ 5799 else { 5800 for (cp2 = cp1; cp2 > trndir; cp2--) { 5801 if (*cp2 == '.') { 5802 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) { 5803 /* fix-me, can not scan EFS file specs backward like this */ 5804 *cp2 = *cp1; *cp1 = '\0'; 5805 hasfilename = 1; 5806 break; 5807 } 5808 } 5809 if (*cp2 == '[' || *cp2 == '<') break; 5810 } 5811 } 5812 } 5813 5814 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1); 5815 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM); 5816 cp1 = strpbrk(trndir,"]:>"); 5817 if (hasfilename || !cp1) { /* Unix-style path or filename */ 5818 if (trndir[0] == '.') { 5819 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) { 5820 PerlMem_free(trndir); 5821 PerlMem_free(vmsdir); 5822 return do_fileify_dirspec("[]",buf,ts,NULL); 5823 } 5824 else if (trndir[1] == '.' && 5825 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) { 5826 PerlMem_free(trndir); 5827 PerlMem_free(vmsdir); 5828 return do_fileify_dirspec("[-]",buf,ts,NULL); 5829 } 5830 } 5831 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */ 5832 dirlen -= 1; /* to last element */ 5833 lastdir = strrchr(trndir,'/'); 5834 } 5835 else if ((cp1 = strstr(trndir,"/.")) != NULL) { 5836 /* If we have "/." or "/..", VMSify it and let the VMS code 5837 * below expand it, rather than repeating the code to handle 5838 * relative components of a filespec here */ 5839 do { 5840 if (*(cp1+2) == '.') cp1++; 5841 if (*(cp1+2) == '/' || *(cp1+2) == '\0') { 5842 char * ret_chr; 5843 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) { 5844 PerlMem_free(trndir); 5845 PerlMem_free(vmsdir); 5846 return NULL; 5847 } 5848 if (strchr(vmsdir,'/') != NULL) { 5849 /* If do_tovmsspec() returned it, it must have VMS syntax 5850 * delimiters in it, so it's a mixed VMS/Unix spec. We take 5851 * the time to check this here only so we avoid a recursion 5852 * loop; otherwise, gigo. 5853 */ 5854 PerlMem_free(trndir); 5855 PerlMem_free(vmsdir); 5856 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); 5857 return NULL; 5858 } 5859 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) { 5860 PerlMem_free(trndir); 5861 PerlMem_free(vmsdir); 5862 return NULL; 5863 } 5864 ret_chr = do_tounixspec(trndir,buf,ts,NULL); 5865 PerlMem_free(trndir); 5866 PerlMem_free(vmsdir); 5867 return ret_chr; 5868 } 5869 cp1++; 5870 } while ((cp1 = strstr(cp1,"/.")) != NULL); 5871 lastdir = strrchr(trndir,'/'); 5872 } 5873 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) { 5874 char * ret_chr; 5875 /* Ditto for specs that end in an MFD -- let the VMS code 5876 * figure out whether it's a real device or a rooted logical. */ 5877 5878 /* This should not happen any more. Allowing the fake /000000 5879 * in a UNIX pathname causes all sorts of problems when trying 5880 * to run in UNIX emulation. So the VMS to UNIX conversions 5881 * now remove the fake /000000 directories. 5882 */ 5883 5884 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0'; 5885 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) { 5886 PerlMem_free(trndir); 5887 PerlMem_free(vmsdir); 5888 return NULL; 5889 } 5890 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) { 5891 PerlMem_free(trndir); 5892 PerlMem_free(vmsdir); 5893 return NULL; 5894 } 5895 ret_chr = do_tounixspec(trndir,buf,ts,NULL); 5896 PerlMem_free(trndir); 5897 PerlMem_free(vmsdir); 5898 return ret_chr; 5899 } 5900 else { 5901 5902 if ( !(lastdir = cp1 = strrchr(trndir,'/')) && 5903 !(lastdir = cp1 = strrchr(trndir,']')) && 5904 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir; 5905 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */ 5906 int ver; char *cp3; 5907 5908 /* For EFS or ODS-5 look for the last dot */ 5909 if (decc_efs_charset) { 5910 cp2 = strrchr(cp1,'.'); 5911 } 5912 if (vms_process_case_tolerant) { 5913 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */ 5914 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */ 5915 !*(cp2+3) || toupper(*(cp2+3)) != 'R' || 5916 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') || 5917 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 && 5918 (ver || *cp3)))))) { 5919 PerlMem_free(trndir); 5920 PerlMem_free(vmsdir); 5921 set_errno(ENOTDIR); 5922 set_vaxc_errno(RMS$_DIR); 5923 return NULL; 5924 } 5925 } 5926 else { 5927 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */ 5928 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */ 5929 !*(cp2+3) || *(cp2+3) != 'R' || 5930 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') || 5931 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 && 5932 (ver || *cp3)))))) { 5933 PerlMem_free(trndir); 5934 PerlMem_free(vmsdir); 5935 set_errno(ENOTDIR); 5936 set_vaxc_errno(RMS$_DIR); 5937 return NULL; 5938 } 5939 } 5940 dirlen = cp2 - trndir; 5941 } 5942 } 5943 5944 retlen = dirlen + 6; 5945 if (buf) retspec = buf; 5946 else if (ts) Newx(retspec,retlen+1,char); 5947 else retspec = __fileify_retbuf; 5948 memcpy(retspec,trndir,dirlen); 5949 retspec[dirlen] = '\0'; 5950 5951 /* We've picked up everything up to the directory file name. 5952 Now just add the type and version, and we're set. */ 5953 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) 5954 strcat(retspec,".dir;1"); 5955 else 5956 strcat(retspec,".DIR;1"); 5957 PerlMem_free(trndir); 5958 PerlMem_free(vmsdir); 5959 return retspec; 5960 } 5961 else { /* VMS-style directory spec */ 5962 5963 char *esa, *esal, term, *cp; 5964 char *my_esa; 5965 int my_esa_len; 5966 unsigned long int sts, cmplen, haslower = 0; 5967 unsigned int nam_fnb; 5968 char * nam_type; 5969 struct FAB dirfab = cc$rms_fab; 5970 rms_setup_nam(savnam); 5971 rms_setup_nam(dirnam); 5972 5973 esa = PerlMem_malloc(NAM$C_MAXRSS + 1); 5974 if (esa == NULL) _ckvmssts(SS$_INSFMEM); 5975 esal = NULL; 5976 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 5977 esal = PerlMem_malloc(VMS_MAXRSS); 5978 if (esal == NULL) _ckvmssts(SS$_INSFMEM); 5979 #endif 5980 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir)); 5981 rms_bind_fab_nam(dirfab, dirnam); 5982 rms_set_dna(dirfab, dirnam, ".DIR;1", 6); 5983 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 5984 #ifdef NAM$M_NO_SHORT_UPCASE 5985 if (decc_efs_case_preserve) 5986 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 5987 #endif 5988 5989 for (cp = trndir; *cp; cp++) 5990 if (islower(*cp)) { haslower = 1; break; } 5991 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) { 5992 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) { 5993 rms_set_nam_nop(dirnam, NAM$M_SYNCHK); 5994 sts = sys$parse(&dirfab) & STS$K_SUCCESS; 5995 } 5996 if (!sts) { 5997 PerlMem_free(esa); 5998 if (esal != NULL) 5999 PerlMem_free(esal); 6000 PerlMem_free(trndir); 6001 PerlMem_free(vmsdir); 6002 set_errno(EVMSERR); 6003 set_vaxc_errno(dirfab.fab$l_sts); 6004 return NULL; 6005 } 6006 } 6007 else { 6008 savnam = dirnam; 6009 /* Does the file really exist? */ 6010 if (sys$search(&dirfab)& STS$K_SUCCESS) { 6011 /* Yes; fake the fnb bits so we'll check type below */ 6012 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER)); 6013 } 6014 else { /* No; just work with potential name */ 6015 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam; 6016 else { 6017 int fab_sts; 6018 fab_sts = dirfab.fab$l_sts; 6019 sts = rms_free_search_context(&dirfab); 6020 PerlMem_free(esa); 6021 if (esal != NULL) 6022 PerlMem_free(esal); 6023 PerlMem_free(trndir); 6024 PerlMem_free(vmsdir); 6025 set_errno(EVMSERR); set_vaxc_errno(fab_sts); 6026 return NULL; 6027 } 6028 } 6029 } 6030 6031 /* Make sure we are using the right buffer */ 6032 if (esal != NULL) { 6033 my_esa = esal; 6034 my_esa_len = rms_nam_esll(dirnam); 6035 } else { 6036 my_esa = esa; 6037 my_esa_len = rms_nam_esl(dirnam); 6038 } 6039 my_esa[my_esa_len] = '\0'; 6040 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) { 6041 cp1 = strchr(my_esa,']'); 6042 if (!cp1) cp1 = strchr(my_esa,'>'); 6043 if (cp1) { /* Should always be true */ 6044 my_esa_len -= cp1 - my_esa - 1; 6045 memmove(my_esa, cp1 + 1, my_esa_len); 6046 } 6047 } 6048 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */ 6049 /* Yep; check version while we're at it, if it's there. */ 6050 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4; 6051 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) { 6052 /* Something other than .DIR[;1]. Bzzt. */ 6053 sts = rms_free_search_context(&dirfab); 6054 PerlMem_free(esa); 6055 if (esal != NULL) 6056 PerlMem_free(esal); 6057 PerlMem_free(trndir); 6058 PerlMem_free(vmsdir); 6059 set_errno(ENOTDIR); 6060 set_vaxc_errno(RMS$_DIR); 6061 return NULL; 6062 } 6063 } 6064 6065 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) { 6066 /* They provided at least the name; we added the type, if necessary, */ 6067 if (buf) retspec = buf; /* in sys$parse() */ 6068 else if (ts) Newx(retspec, my_esa_len + 1, char); 6069 else retspec = __fileify_retbuf; 6070 strcpy(retspec,my_esa); 6071 sts = rms_free_search_context(&dirfab); 6072 PerlMem_free(trndir); 6073 PerlMem_free(esa); 6074 if (esal != NULL) 6075 PerlMem_free(esal); 6076 PerlMem_free(vmsdir); 6077 return retspec; 6078 } 6079 if ((cp1 = strstr(esa,".][000000]")) != NULL) { 6080 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2; 6081 *cp1 = '\0'; 6082 my_esa_len -= 9; 6083 } 6084 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>'); 6085 if (cp1 == NULL) { /* should never happen */ 6086 sts = rms_free_search_context(&dirfab); 6087 PerlMem_free(trndir); 6088 PerlMem_free(esa); 6089 if (esal != NULL) 6090 PerlMem_free(esal); 6091 PerlMem_free(vmsdir); 6092 return NULL; 6093 } 6094 term = *cp1; 6095 *cp1 = '\0'; 6096 retlen = strlen(my_esa); 6097 cp1 = strrchr(my_esa,'.'); 6098 /* ODS-5 directory specifications can have extra "." in them. */ 6099 /* Fix-me, can not scan EFS file specifications backwards */ 6100 while (cp1 != NULL) { 6101 if ((cp1-1 == my_esa) || (*(cp1-1) != '^')) 6102 break; 6103 else { 6104 cp1--; 6105 while ((cp1 > my_esa) && (*cp1 != '.')) 6106 cp1--; 6107 } 6108 if (cp1 == my_esa) 6109 cp1 = NULL; 6110 } 6111 6112 if ((cp1) != NULL) { 6113 /* There's more than one directory in the path. Just roll back. */ 6114 *cp1 = term; 6115 if (buf) retspec = buf; 6116 else if (ts) Newx(retspec,retlen+7,char); 6117 else retspec = __fileify_retbuf; 6118 strcpy(retspec,my_esa); 6119 } 6120 else { 6121 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) { 6122 /* Go back and expand rooted logical name */ 6123 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL); 6124 #ifdef NAM$M_NO_SHORT_UPCASE 6125 if (decc_efs_case_preserve) 6126 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6127 #endif 6128 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) { 6129 sts = rms_free_search_context(&dirfab); 6130 PerlMem_free(esa); 6131 if (esal != NULL) 6132 PerlMem_free(esal); 6133 PerlMem_free(trndir); 6134 PerlMem_free(vmsdir); 6135 set_errno(EVMSERR); 6136 set_vaxc_errno(dirfab.fab$l_sts); 6137 return NULL; 6138 } 6139 6140 /* This changes the length of the string of course */ 6141 if (esal != NULL) { 6142 my_esa_len = rms_nam_esll(dirnam); 6143 } else { 6144 my_esa_len = rms_nam_esl(dirnam); 6145 } 6146 6147 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */ 6148 if (buf) retspec = buf; 6149 else if (ts) Newx(retspec,retlen+16,char); 6150 else retspec = __fileify_retbuf; 6151 cp1 = strstr(my_esa,"]["); 6152 if (!cp1) cp1 = strstr(my_esa,"]<"); 6153 dirlen = cp1 - my_esa; 6154 memcpy(retspec,my_esa,dirlen); 6155 if (!strncmp(cp1+2,"000000]",7)) { 6156 retspec[dirlen-1] = '\0'; 6157 /* fix-me Not full ODS-5, just extra dots in directories for now */ 6158 cp1 = retspec + dirlen - 1; 6159 while (cp1 > retspec) 6160 { 6161 if (*cp1 == '[') 6162 break; 6163 if (*cp1 == '.') { 6164 if (*(cp1-1) != '^') 6165 break; 6166 } 6167 cp1--; 6168 } 6169 if (*cp1 == '.') *cp1 = ']'; 6170 else { 6171 memmove(cp1+8,cp1+1,retspec+dirlen-cp1); 6172 memmove(cp1+1,"000000]",7); 6173 } 6174 } 6175 else { 6176 memmove(retspec+dirlen,cp1+2,retlen-dirlen); 6177 retspec[retlen] = '\0'; 6178 /* Convert last '.' to ']' */ 6179 cp1 = retspec+retlen-1; 6180 while (*cp != '[') { 6181 cp1--; 6182 if (*cp1 == '.') { 6183 /* Do not trip on extra dots in ODS-5 directories */ 6184 if ((cp1 == retspec) || (*(cp1-1) != '^')) 6185 break; 6186 } 6187 } 6188 if (*cp1 == '.') *cp1 = ']'; 6189 else { 6190 memmove(cp1+8,cp1+1,retspec+dirlen-cp1); 6191 memmove(cp1+1,"000000]",7); 6192 } 6193 } 6194 } 6195 else { /* This is a top-level dir. Add the MFD to the path. */ 6196 if (buf) retspec = buf; 6197 else if (ts) Newx(retspec,retlen+16,char); 6198 else retspec = __fileify_retbuf; 6199 cp1 = my_esa; 6200 cp2 = retspec; 6201 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++); 6202 strcpy(cp2,":[000000]"); 6203 cp1 += 2; 6204 strcpy(cp2+9,cp1); 6205 } 6206 } 6207 sts = rms_free_search_context(&dirfab); 6208 /* We've set up the string up through the filename. Add the 6209 type and version, and we're done. */ 6210 strcat(retspec,".DIR;1"); 6211 6212 /* $PARSE may have upcased filespec, so convert output to lower 6213 * case if input contained any lowercase characters. */ 6214 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec); 6215 PerlMem_free(trndir); 6216 PerlMem_free(esa); 6217 if (esal != NULL) 6218 PerlMem_free(esal); 6219 PerlMem_free(vmsdir); 6220 return retspec; 6221 } 6222 } /* end of do_fileify_dirspec() */ 6223 /*}}}*/ 6224 /* External entry points */ 6225 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf) 6226 { return do_fileify_dirspec(dir,buf,0,NULL); } 6227 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf) 6228 { return do_fileify_dirspec(dir,buf,1,NULL); } 6229 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl) 6230 { return do_fileify_dirspec(dir,buf,0,utf8_fl); } 6231 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl) 6232 { return do_fileify_dirspec(dir,buf,1,utf8_fl); } 6233 6234 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/ 6235 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl) 6236 { 6237 static char __pathify_retbuf[VMS_MAXRSS]; 6238 unsigned long int retlen; 6239 char *retpath, *cp1, *cp2, *trndir; 6240 unsigned short int trnlnm_iter_count; 6241 STRLEN trnlen; 6242 int sts; 6243 if (utf8_fl != NULL) 6244 *utf8_fl = 0; 6245 6246 if (!dir || !*dir) { 6247 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL; 6248 } 6249 6250 trndir = PerlMem_malloc(VMS_MAXRSS); 6251 if (trndir == NULL) _ckvmssts(SS$_INSFMEM); 6252 if (*dir) strcpy(trndir,dir); 6253 else getcwd(trndir,VMS_MAXRSS - 1); 6254 6255 trnlnm_iter_count = 0; 6256 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords 6257 && my_trnlnm(trndir,trndir,0)) { 6258 trnlnm_iter_count++; 6259 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 6260 trnlen = strlen(trndir); 6261 6262 /* Trap simple rooted lnms, and return lnm:[000000] */ 6263 if (!strcmp(trndir+trnlen-2,".]")) { 6264 if (buf) retpath = buf; 6265 else if (ts) Newx(retpath,strlen(dir)+10,char); 6266 else retpath = __pathify_retbuf; 6267 strcpy(retpath,dir); 6268 strcat(retpath,":[000000]"); 6269 PerlMem_free(trndir); 6270 return retpath; 6271 } 6272 } 6273 6274 /* At this point we do not work with *dir, but the copy in 6275 * *trndir that is modifiable. 6276 */ 6277 6278 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */ 6279 if (*trndir == '.' && (*(trndir+1) == '\0' || 6280 (*(trndir+1) == '.' && *(trndir+2) == '\0'))) 6281 retlen = 2 + (*(trndir+1) != '\0'); 6282 else { 6283 if ( !(cp1 = strrchr(trndir,'/')) && 6284 !(cp1 = strrchr(trndir,']')) && 6285 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir; 6286 if ((cp2 = strchr(cp1,'.')) != NULL && 6287 (*(cp2-1) != '/' || /* Trailing '.', '..', */ 6288 !(*(cp2+1) == '\0' || /* or '...' are dirs. */ 6289 (*(cp2+1) == '.' && *(cp2+2) == '\0') || 6290 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) { 6291 int ver; char *cp3; 6292 6293 /* For EFS or ODS-5 look for the last dot */ 6294 if (decc_efs_charset) { 6295 cp2 = strrchr(cp1,'.'); 6296 } 6297 if (vms_process_case_tolerant) { 6298 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */ 6299 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */ 6300 !*(cp2+3) || toupper(*(cp2+3)) != 'R' || 6301 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') || 6302 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 && 6303 (ver || *cp3)))))) { 6304 PerlMem_free(trndir); 6305 set_errno(ENOTDIR); 6306 set_vaxc_errno(RMS$_DIR); 6307 return NULL; 6308 } 6309 } 6310 else { 6311 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */ 6312 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */ 6313 !*(cp2+3) || *(cp2+3) != 'R' || 6314 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') || 6315 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 && 6316 (ver || *cp3)))))) { 6317 PerlMem_free(trndir); 6318 set_errno(ENOTDIR); 6319 set_vaxc_errno(RMS$_DIR); 6320 return NULL; 6321 } 6322 } 6323 retlen = cp2 - trndir + 1; 6324 } 6325 else { /* No file type present. Treat the filename as a directory. */ 6326 retlen = strlen(trndir) + 1; 6327 } 6328 } 6329 if (buf) retpath = buf; 6330 else if (ts) Newx(retpath,retlen+1,char); 6331 else retpath = __pathify_retbuf; 6332 strncpy(retpath, trndir, retlen-1); 6333 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */ 6334 retpath[retlen-1] = '/'; /* with '/', add it. */ 6335 retpath[retlen] = '\0'; 6336 } 6337 else retpath[retlen-1] = '\0'; 6338 } 6339 else { /* VMS-style directory spec */ 6340 char *esa, *esal, *cp; 6341 char *my_esa; 6342 int my_esa_len; 6343 unsigned long int sts, cmplen, haslower; 6344 struct FAB dirfab = cc$rms_fab; 6345 int dirlen; 6346 rms_setup_nam(savnam); 6347 rms_setup_nam(dirnam); 6348 6349 /* If we've got an explicit filename, we can just shuffle the string. */ 6350 if ( ( (cp1 = strrchr(trndir,']')) != NULL || 6351 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) { 6352 if ((cp2 = strchr(cp1,'.')) != NULL) { 6353 int ver; char *cp3; 6354 if (vms_process_case_tolerant) { 6355 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */ 6356 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */ 6357 !*(cp2+3) || toupper(*(cp2+3)) != 'R' || 6358 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') || 6359 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 && 6360 (ver || *cp3)))))) { 6361 PerlMem_free(trndir); 6362 set_errno(ENOTDIR); 6363 set_vaxc_errno(RMS$_DIR); 6364 return NULL; 6365 } 6366 } 6367 else { 6368 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */ 6369 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */ 6370 !*(cp2+3) || *(cp2+3) != 'R' || 6371 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') || 6372 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 && 6373 (ver || *cp3)))))) { 6374 PerlMem_free(trndir); 6375 set_errno(ENOTDIR); 6376 set_vaxc_errno(RMS$_DIR); 6377 return NULL; 6378 } 6379 } 6380 } 6381 else { /* No file type, so just draw name into directory part */ 6382 for (cp2 = cp1; *cp2; cp2++) ; 6383 } 6384 *cp2 = *cp1; 6385 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */ 6386 *cp1 = '.'; 6387 /* We've now got a VMS 'path'; fall through */ 6388 } 6389 6390 dirlen = strlen(trndir); 6391 if (trndir[dirlen-1] == ']' || 6392 trndir[dirlen-1] == '>' || 6393 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */ 6394 if (buf) retpath = buf; 6395 else if (ts) Newx(retpath,strlen(trndir)+1,char); 6396 else retpath = __pathify_retbuf; 6397 strcpy(retpath,trndir); 6398 PerlMem_free(trndir); 6399 return retpath; 6400 } 6401 rms_set_fna(dirfab, dirnam, trndir, dirlen); 6402 esa = PerlMem_malloc(VMS_MAXRSS); 6403 if (esa == NULL) _ckvmssts(SS$_INSFMEM); 6404 esal = NULL; 6405 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 6406 esal = PerlMem_malloc(VMS_MAXRSS); 6407 if (esal == NULL) _ckvmssts(SS$_INSFMEM); 6408 #endif 6409 rms_set_dna(dirfab, dirnam, ".DIR;1", 6); 6410 rms_bind_fab_nam(dirfab, dirnam); 6411 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1); 6412 #ifdef NAM$M_NO_SHORT_UPCASE 6413 if (decc_efs_case_preserve) 6414 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE); 6415 #endif 6416 6417 for (cp = trndir; *cp; cp++) 6418 if (islower(*cp)) { haslower = 1; break; } 6419 6420 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) { 6421 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) { 6422 rms_set_nam_nop(dirnam, NAM$M_SYNCHK); 6423 sts = sys$parse(&dirfab) & STS$K_SUCCESS; 6424 } 6425 if (!sts) { 6426 PerlMem_free(trndir); 6427 PerlMem_free(esa); 6428 if (esal != NULL) 6429 PerlMem_free(esal); 6430 set_errno(EVMSERR); 6431 set_vaxc_errno(dirfab.fab$l_sts); 6432 return NULL; 6433 } 6434 } 6435 else { 6436 savnam = dirnam; 6437 /* Does the file really exist? */ 6438 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) { 6439 if (dirfab.fab$l_sts != RMS$_FNF) { 6440 int sts1; 6441 sts1 = rms_free_search_context(&dirfab); 6442 PerlMem_free(trndir); 6443 PerlMem_free(esa); 6444 if (esal != NULL) 6445 PerlMem_free(esal); 6446 set_errno(EVMSERR); 6447 set_vaxc_errno(dirfab.fab$l_sts); 6448 return NULL; 6449 } 6450 dirnam = savnam; /* No; just work with potential name */ 6451 } 6452 } 6453 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */ 6454 /* Yep; check version while we're at it, if it's there. */ 6455 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4; 6456 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) { 6457 int sts2; 6458 /* Something other than .DIR[;1]. Bzzt. */ 6459 sts2 = rms_free_search_context(&dirfab); 6460 PerlMem_free(trndir); 6461 PerlMem_free(esa); 6462 if (esal != NULL) 6463 PerlMem_free(esal); 6464 set_errno(ENOTDIR); 6465 set_vaxc_errno(RMS$_DIR); 6466 return NULL; 6467 } 6468 } 6469 /* Make sure we are using the right buffer */ 6470 if (esal != NULL) { 6471 /* We only need one, clean up the other */ 6472 my_esa = esal; 6473 my_esa_len = rms_nam_esll(dirnam); 6474 } else { 6475 my_esa = esa; 6476 my_esa_len = rms_nam_esl(dirnam); 6477 } 6478 6479 /* Null terminate the buffer */ 6480 my_esa[my_esa_len] = '\0'; 6481 6482 /* OK, the type was fine. Now pull any file name into the 6483 directory path. */ 6484 if ((cp1 = strrchr(my_esa,']'))) *(rms_nam_typel(dirnam)) = ']'; 6485 else { 6486 cp1 = strrchr(my_esa,'>'); 6487 *(rms_nam_typel(dirnam)) = '>'; 6488 } 6489 *cp1 = '.'; 6490 *(rms_nam_typel(dirnam) + 1) = '\0'; 6491 retlen = (rms_nam_typel(dirnam)) - my_esa + 2; 6492 if (buf) retpath = buf; 6493 else if (ts) Newx(retpath,retlen,char); 6494 else retpath = __pathify_retbuf; 6495 strcpy(retpath,my_esa); 6496 PerlMem_free(esa); 6497 if (esal != NULL) 6498 PerlMem_free(esal); 6499 sts = rms_free_search_context(&dirfab); 6500 /* $PARSE may have upcased filespec, so convert output to lower 6501 * case if input contained any lowercase characters. */ 6502 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath); 6503 } 6504 6505 PerlMem_free(trndir); 6506 return retpath; 6507 } /* end of do_pathify_dirspec() */ 6508 /*}}}*/ 6509 /* External entry points */ 6510 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf) 6511 { return do_pathify_dirspec(dir,buf,0,NULL); } 6512 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf) 6513 { return do_pathify_dirspec(dir,buf,1,NULL); } 6514 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl) 6515 { return do_pathify_dirspec(dir,buf,0,utf8_fl); } 6516 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl) 6517 { return do_pathify_dirspec(dir,buf,1,utf8_fl); } 6518 6519 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/ 6520 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl) 6521 { 6522 static char __tounixspec_retbuf[VMS_MAXRSS]; 6523 char *dirend, *rslt, *cp1, *cp3, *tmp; 6524 const char *cp2; 6525 int devlen, dirlen, retlen = VMS_MAXRSS; 6526 int expand = 1; /* guarantee room for leading and trailing slashes */ 6527 unsigned short int trnlnm_iter_count; 6528 int cmp_rslt; 6529 if (utf8_fl != NULL) 6530 *utf8_fl = 0; 6531 6532 if (spec == NULL) return NULL; 6533 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL; 6534 if (buf) rslt = buf; 6535 else if (ts) { 6536 Newx(rslt, VMS_MAXRSS, char); 6537 } 6538 else rslt = __tounixspec_retbuf; 6539 6540 /* New VMS specific format needs translation 6541 * glob passes filenames with trailing '\n' and expects this preserved. 6542 */ 6543 if (decc_posix_compliant_pathnames) { 6544 if (strncmp(spec, "\"^UP^", 5) == 0) { 6545 char * uspec; 6546 char *tunix; 6547 int tunix_len; 6548 int nl_flag; 6549 6550 tunix = PerlMem_malloc(VMS_MAXRSS); 6551 if (tunix == NULL) _ckvmssts(SS$_INSFMEM); 6552 strcpy(tunix, spec); 6553 tunix_len = strlen(tunix); 6554 nl_flag = 0; 6555 if (tunix[tunix_len - 1] == '\n') { 6556 tunix[tunix_len - 1] = '\"'; 6557 tunix[tunix_len] = '\0'; 6558 tunix_len--; 6559 nl_flag = 1; 6560 } 6561 uspec = decc$translate_vms(tunix); 6562 PerlMem_free(tunix); 6563 if ((int)uspec > 0) { 6564 strcpy(rslt,uspec); 6565 if (nl_flag) { 6566 strcat(rslt,"\n"); 6567 } 6568 else { 6569 /* If we can not translate it, makemaker wants as-is */ 6570 strcpy(rslt, spec); 6571 } 6572 return rslt; 6573 } 6574 } 6575 } 6576 6577 cmp_rslt = 0; /* Presume VMS */ 6578 cp1 = strchr(spec, '/'); 6579 if (cp1 == NULL) 6580 cmp_rslt = 0; 6581 6582 /* Look for EFS ^/ */ 6583 if (decc_efs_charset) { 6584 while (cp1 != NULL) { 6585 cp2 = cp1 - 1; 6586 if (*cp2 != '^') { 6587 /* Found illegal VMS, assume UNIX */ 6588 cmp_rslt = 1; 6589 break; 6590 } 6591 cp1++; 6592 cp1 = strchr(cp1, '/'); 6593 } 6594 } 6595 6596 /* Look for "." and ".." */ 6597 if (decc_filename_unix_report) { 6598 if (spec[0] == '.') { 6599 if ((spec[1] == '\0') || (spec[1] == '\n')) { 6600 cmp_rslt = 1; 6601 } 6602 else { 6603 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) { 6604 cmp_rslt = 1; 6605 } 6606 } 6607 } 6608 } 6609 /* This is already UNIX or at least nothing VMS understands */ 6610 if (cmp_rslt) { 6611 strcpy(rslt,spec); 6612 return rslt; 6613 } 6614 6615 cp1 = rslt; 6616 cp2 = spec; 6617 dirend = strrchr(spec,']'); 6618 if (dirend == NULL) dirend = strrchr(spec,'>'); 6619 if (dirend == NULL) dirend = strchr(spec,':'); 6620 if (dirend == NULL) { 6621 strcpy(rslt,spec); 6622 return rslt; 6623 } 6624 6625 /* Special case 1 - sys$posix_root = / */ 6626 #if __CRTL_VER >= 70000000 6627 if (!decc_disable_posix_root) { 6628 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) { 6629 *cp1 = '/'; 6630 cp1++; 6631 cp2 = cp2 + 15; 6632 } 6633 } 6634 #endif 6635 6636 /* Special case 2 - Convert NLA0: to /dev/null */ 6637 #if __CRTL_VER < 70000000 6638 cmp_rslt = strncmp(spec,"NLA0:", 5); 6639 if (cmp_rslt != 0) 6640 cmp_rslt = strncmp(spec,"nla0:", 5); 6641 #else 6642 cmp_rslt = strncasecmp(spec,"NLA0:", 5); 6643 #endif 6644 if (cmp_rslt == 0) { 6645 strcpy(rslt, "/dev/null"); 6646 cp1 = cp1 + 9; 6647 cp2 = cp2 + 5; 6648 if (spec[6] != '\0') { 6649 cp1[9] == '/'; 6650 cp1++; 6651 cp2++; 6652 } 6653 } 6654 6655 /* Also handle special case "SYS$SCRATCH:" */ 6656 #if __CRTL_VER < 70000000 6657 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12); 6658 if (cmp_rslt != 0) 6659 cmp_rslt = strncmp(spec,"sys$scratch:", 12); 6660 #else 6661 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12); 6662 #endif 6663 tmp = PerlMem_malloc(VMS_MAXRSS); 6664 if (tmp == NULL) _ckvmssts(SS$_INSFMEM); 6665 if (cmp_rslt == 0) { 6666 int islnm; 6667 6668 islnm = my_trnlnm(tmp, "TMP", 0); 6669 if (!islnm) { 6670 strcpy(rslt, "/tmp"); 6671 cp1 = cp1 + 4; 6672 cp2 = cp2 + 12; 6673 if (spec[12] != '\0') { 6674 cp1[4] == '/'; 6675 cp1++; 6676 cp2++; 6677 } 6678 } 6679 } 6680 6681 if (*cp2 != '[' && *cp2 != '<') { 6682 *(cp1++) = '/'; 6683 } 6684 else { /* the VMS spec begins with directories */ 6685 cp2++; 6686 if (*cp2 == ']' || *cp2 == '>') { 6687 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0'; 6688 PerlMem_free(tmp); 6689 return rslt; 6690 } 6691 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */ 6692 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) { 6693 if (ts) Safefree(rslt); 6694 PerlMem_free(tmp); 6695 return NULL; 6696 } 6697 trnlnm_iter_count = 0; 6698 do { 6699 cp3 = tmp; 6700 while (*cp3 != ':' && *cp3) cp3++; 6701 *(cp3++) = '\0'; 6702 if (strchr(cp3,']') != NULL) break; 6703 trnlnm_iter_count++; 6704 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break; 6705 } while (vmstrnenv(tmp,tmp,0,fildev,0)); 6706 if (ts && !buf && 6707 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) { 6708 retlen = devlen + dirlen; 6709 Renew(rslt,retlen+1+2*expand,char); 6710 cp1 = rslt; 6711 } 6712 cp3 = tmp; 6713 *(cp1++) = '/'; 6714 while (*cp3) { 6715 *(cp1++) = *(cp3++); 6716 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) { 6717 PerlMem_free(tmp); 6718 return NULL; /* No room */ 6719 } 6720 } 6721 *(cp1++) = '/'; 6722 } 6723 if ((*cp2 == '^')) { 6724 /* EFS file escape, pass the next character as is */ 6725 /* Fix me: HEX encoding for Unicode not implemented */ 6726 cp2++; 6727 } 6728 else if ( *cp2 == '.') { 6729 if (*(cp2+1) == '.' && *(cp2+2) == '.') { 6730 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 6731 cp2 += 3; 6732 } 6733 else cp2++; 6734 } 6735 } 6736 PerlMem_free(tmp); 6737 for (; cp2 <= dirend; cp2++) { 6738 if ((*cp2 == '^')) { 6739 /* EFS file escape, pass the next character as is */ 6740 /* Fix me: HEX encoding for Unicode not implemented */ 6741 *(cp1++) = *(++cp2); 6742 /* An escaped dot stays as is -- don't convert to slash */ 6743 if (*cp2 == '.') cp2++; 6744 } 6745 if (*cp2 == ':') { 6746 *(cp1++) = '/'; 6747 if (*(cp2+1) == '[') cp2++; 6748 } 6749 else if (*cp2 == ']' || *cp2 == '>') { 6750 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */ 6751 } 6752 else if ((*cp2 == '.') && (*cp2-1 != '^')) { 6753 *(cp1++) = '/'; 6754 if (*(cp2+1) == ']' || *(cp2+1) == '>') { 6755 while (*(cp2+1) == ']' || *(cp2+1) == '>' || 6756 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++; 6757 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' || 6758 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7; 6759 } 6760 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') { 6761 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/'; 6762 cp2 += 2; 6763 } 6764 } 6765 else if (*cp2 == '-') { 6766 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') { 6767 while (*cp2 == '-') { 6768 cp2++; 6769 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/'; 6770 } 6771 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */ 6772 if (ts) Safefree(rslt); /* filespecs like */ 6773 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */ 6774 return NULL; 6775 } 6776 } 6777 else *(cp1++) = *cp2; 6778 } 6779 else *(cp1++) = *cp2; 6780 } 6781 while (*cp2) { 6782 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */ 6783 *(cp1++) = *(cp2++); 6784 } 6785 *cp1 = '\0'; 6786 6787 /* This still leaves /000000/ when working with a 6788 * VMS device root or concealed root. 6789 */ 6790 { 6791 int ulen; 6792 char * zeros; 6793 6794 ulen = strlen(rslt); 6795 6796 /* Get rid of "000000/ in rooted filespecs */ 6797 if (ulen > 7) { 6798 zeros = strstr(rslt, "/000000/"); 6799 if (zeros != NULL) { 6800 int mlen; 6801 mlen = ulen - (zeros - rslt) - 7; 6802 memmove(zeros, &zeros[7], mlen); 6803 ulen = ulen - 7; 6804 rslt[ulen] = '\0'; 6805 } 6806 } 6807 } 6808 6809 return rslt; 6810 6811 } /* end of do_tounixspec() */ 6812 /*}}}*/ 6813 /* External entry points */ 6814 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) 6815 { return do_tounixspec(spec,buf,0, NULL); } 6816 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) 6817 { return do_tounixspec(spec,buf,1, NULL); } 6818 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl) 6819 { return do_tounixspec(spec,buf,0, utf8_fl); } 6820 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl) 6821 { return do_tounixspec(spec,buf,1, utf8_fl); } 6822 6823 #if __CRTL_VER >= 70200000 && !defined(__VAX) 6824 6825 /* 6826 This procedure is used to identify if a path is based in either 6827 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and 6828 it returns the OpenVMS format directory for it. 6829 6830 It is expecting specifications of only '/' or '/xxxx/' 6831 6832 If a posix root does not exist, or 'xxxx' is not a directory 6833 in the posix root, it returns a failure. 6834 6835 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7. 6836 6837 It is used only internally by posix_to_vmsspec_hardway(). 6838 */ 6839 6840 static int posix_root_to_vms 6841 (char *vmspath, int vmspath_len, 6842 const char *unixpath, 6843 const int * utf8_fl) 6844 { 6845 int sts; 6846 struct FAB myfab = cc$rms_fab; 6847 rms_setup_nam(mynam); 6848 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 6849 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 6850 char * esa, * esal, * rsa, * rsal; 6851 char *vms_delim; 6852 int dir_flag; 6853 int unixlen; 6854 6855 dir_flag = 0; 6856 vmspath[0] = '\0'; 6857 unixlen = strlen(unixpath); 6858 if (unixlen == 0) { 6859 return RMS$_FNF; 6860 } 6861 6862 #if __CRTL_VER >= 80200000 6863 /* If not a posix spec already, convert it */ 6864 if (decc_posix_compliant_pathnames) { 6865 if (strncmp(unixpath,"\"^UP^",5) != 0) { 6866 sprintf(vmspath,"\"^UP^%s\"",unixpath); 6867 } 6868 else { 6869 /* This is already a VMS specification, no conversion */ 6870 unixlen--; 6871 strncpy(vmspath,unixpath, vmspath_len); 6872 } 6873 } 6874 else 6875 #endif 6876 { 6877 int path_len; 6878 int i,j; 6879 6880 /* Check to see if this is under the POSIX root */ 6881 if (decc_disable_posix_root) { 6882 return RMS$_FNF; 6883 } 6884 6885 /* Skip leading / */ 6886 if (unixpath[0] == '/') { 6887 unixpath++; 6888 unixlen--; 6889 } 6890 6891 6892 strcpy(vmspath,"SYS$POSIX_ROOT:"); 6893 6894 /* If this is only the / , or blank, then... */ 6895 if (unixpath[0] == '\0') { 6896 /* by definition, this is the answer */ 6897 return SS$_NORMAL; 6898 } 6899 6900 /* Need to look up a directory */ 6901 vmspath[15] = '['; 6902 vmspath[16] = '\0'; 6903 6904 /* Copy and add '^' escape characters as needed */ 6905 j = 16; 6906 i = 0; 6907 while (unixpath[i] != 0) { 6908 int k; 6909 6910 j += copy_expand_unix_filename_escape 6911 (&vmspath[j], &unixpath[i], &k, utf8_fl); 6912 i += k; 6913 } 6914 6915 path_len = strlen(vmspath); 6916 if (vmspath[path_len - 1] == '/') 6917 path_len--; 6918 vmspath[path_len] = ']'; 6919 path_len++; 6920 vmspath[path_len] = '\0'; 6921 6922 } 6923 vmspath[vmspath_len] = 0; 6924 if (unixpath[unixlen - 1] == '/') 6925 dir_flag = 1; 6926 esal = PerlMem_malloc(VMS_MAXRSS); 6927 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6928 esa = PerlMem_malloc(NAM$C_MAXRSS + 1); 6929 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6930 rsal = PerlMem_malloc(VMS_MAXRSS); 6931 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6932 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1); 6933 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 6934 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */ 6935 rms_bind_fab_nam(myfab, mynam); 6936 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1); 6937 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1); 6938 if (decc_efs_case_preserve) 6939 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE; 6940 #ifdef NAML$M_OPEN_SPECIAL 6941 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL; 6942 #endif 6943 6944 /* Set up the remaining naml fields */ 6945 sts = sys$parse(&myfab); 6946 6947 /* It failed! Try again as a UNIX filespec */ 6948 if (!(sts & 1)) { 6949 PerlMem_free(esal); 6950 PerlMem_free(esa); 6951 PerlMem_free(rsal); 6952 PerlMem_free(rsa); 6953 return sts; 6954 } 6955 6956 /* get the Device ID and the FID */ 6957 sts = sys$search(&myfab); 6958 6959 /* These are no longer needed */ 6960 PerlMem_free(esa); 6961 PerlMem_free(rsal); 6962 PerlMem_free(rsa); 6963 6964 /* on any failure, returned the POSIX ^UP^ filespec */ 6965 if (!(sts & 1)) { 6966 PerlMem_free(esal); 6967 return sts; 6968 } 6969 specdsc.dsc$a_pointer = vmspath; 6970 specdsc.dsc$w_length = vmspath_len; 6971 6972 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1]; 6973 dvidsc.dsc$w_length = mynam.naml$t_dvi[0]; 6974 sts = lib$fid_to_name 6975 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length); 6976 6977 /* on any failure, returned the POSIX ^UP^ filespec */ 6978 if (!(sts & 1)) { 6979 /* This can happen if user does not have permission to read directories */ 6980 if (strncmp(unixpath,"\"^UP^",5) != 0) 6981 sprintf(vmspath,"\"^UP^%s\"",unixpath); 6982 else 6983 strcpy(vmspath, unixpath); 6984 } 6985 else { 6986 vmspath[specdsc.dsc$w_length] = 0; 6987 6988 /* Are we expecting a directory? */ 6989 if (dir_flag != 0) { 6990 int i; 6991 char *eptr; 6992 6993 eptr = NULL; 6994 6995 i = specdsc.dsc$w_length - 1; 6996 while (i > 0) { 6997 int zercnt; 6998 zercnt = 0; 6999 /* Version must be '1' */ 7000 if (vmspath[i--] != '1') 7001 break; 7002 /* Version delimiter is one of ".;" */ 7003 if ((vmspath[i] != '.') && (vmspath[i] != ';')) 7004 break; 7005 i--; 7006 if (vmspath[i--] != 'R') 7007 break; 7008 if (vmspath[i--] != 'I') 7009 break; 7010 if (vmspath[i--] != 'D') 7011 break; 7012 if (vmspath[i--] != '.') 7013 break; 7014 eptr = &vmspath[i+1]; 7015 while (i > 0) { 7016 if ((vmspath[i] == ']') || (vmspath[i] == '>')) { 7017 if (vmspath[i-1] != '^') { 7018 if (zercnt != 6) { 7019 *eptr = vmspath[i]; 7020 eptr[1] = '\0'; 7021 vmspath[i] = '.'; 7022 break; 7023 } 7024 else { 7025 /* Get rid of 6 imaginary zero directory filename */ 7026 vmspath[i+1] = '\0'; 7027 } 7028 } 7029 } 7030 if (vmspath[i] == '0') 7031 zercnt++; 7032 else 7033 zercnt = 10; 7034 i--; 7035 } 7036 break; 7037 } 7038 } 7039 } 7040 PerlMem_free(esal); 7041 return sts; 7042 } 7043 7044 /* /dev/mumble needs to be handled special. 7045 /dev/null becomes NLA0:, And there is the potential for other stuff 7046 like /dev/tty which may need to be mapped to something. 7047 */ 7048 7049 static int 7050 slash_dev_special_to_vms 7051 (const char * unixptr, 7052 char * vmspath, 7053 int vmspath_len) 7054 { 7055 char * nextslash; 7056 int len; 7057 int cmp; 7058 int islnm; 7059 7060 unixptr += 4; 7061 nextslash = strchr(unixptr, '/'); 7062 len = strlen(unixptr); 7063 if (nextslash != NULL) 7064 len = nextslash - unixptr; 7065 cmp = strncmp("null", unixptr, 5); 7066 if (cmp == 0) { 7067 if (vmspath_len >= 6) { 7068 strcpy(vmspath, "_NLA0:"); 7069 return SS$_NORMAL; 7070 } 7071 } 7072 } 7073 7074 7075 /* The built in routines do not understand perl's special needs, so 7076 doing a manual conversion from UNIX to VMS 7077 7078 If the utf8_fl is not null and points to a non-zero value, then 7079 treat 8 bit characters as UTF-8. 7080 7081 The sequence starting with '$(' and ending with ')' will be passed 7082 through with out interpretation instead of being escaped. 7083 7084 */ 7085 static int posix_to_vmsspec_hardway 7086 (char *vmspath, int vmspath_len, 7087 const char *unixpath, 7088 int dir_flag, 7089 int * utf8_fl) { 7090 7091 char *esa; 7092 const char *unixptr; 7093 const char *unixend; 7094 char *vmsptr; 7095 const char *lastslash; 7096 const char *lastdot; 7097 int unixlen; 7098 int vmslen; 7099 int dir_start; 7100 int dir_dot; 7101 int quoted; 7102 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 7103 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 7104 7105 if (utf8_fl != NULL) 7106 *utf8_fl = 0; 7107 7108 unixptr = unixpath; 7109 dir_dot = 0; 7110 7111 /* Ignore leading "/" characters */ 7112 while((unixptr[0] == '/') && (unixptr[1] == '/')) { 7113 unixptr++; 7114 } 7115 unixlen = strlen(unixptr); 7116 7117 /* Do nothing with blank paths */ 7118 if (unixlen == 0) { 7119 vmspath[0] = '\0'; 7120 return SS$_NORMAL; 7121 } 7122 7123 quoted = 0; 7124 /* This could have a "^UP^ on the front */ 7125 if (strncmp(unixptr,"\"^UP^",5) == 0) { 7126 quoted = 1; 7127 unixptr+= 5; 7128 unixlen-= 5; 7129 } 7130 7131 lastslash = strrchr(unixptr,'/'); 7132 lastdot = strrchr(unixptr,'.'); 7133 unixend = strrchr(unixptr,'\"'); 7134 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) { 7135 unixend = unixptr + unixlen; 7136 } 7137 7138 /* last dot is last dot or past end of string */ 7139 if (lastdot == NULL) 7140 lastdot = unixptr + unixlen; 7141 7142 /* if no directories, set last slash to beginning of string */ 7143 if (lastslash == NULL) { 7144 lastslash = unixptr; 7145 } 7146 else { 7147 /* Watch out for trailing "." after last slash, still a directory */ 7148 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) { 7149 lastslash = unixptr + unixlen; 7150 } 7151 7152 /* Watch out for traiing ".." after last slash, still a directory */ 7153 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) { 7154 lastslash = unixptr + unixlen; 7155 } 7156 7157 /* dots in directories are aways escaped */ 7158 if (lastdot < lastslash) 7159 lastdot = unixptr + unixlen; 7160 } 7161 7162 /* if (unixptr < lastslash) then we are in a directory */ 7163 7164 dir_start = 0; 7165 7166 vmsptr = vmspath; 7167 vmslen = 0; 7168 7169 /* Start with the UNIX path */ 7170 if (*unixptr != '/') { 7171 /* relative paths */ 7172 7173 /* If allowing logical names on relative pathnames, then handle here */ 7174 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation && 7175 !decc_posix_compliant_pathnames) { 7176 char * nextslash; 7177 int seg_len; 7178 char * trn; 7179 int islnm; 7180 7181 /* Find the next slash */ 7182 nextslash = strchr(unixptr,'/'); 7183 7184 esa = PerlMem_malloc(vmspath_len); 7185 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7186 7187 trn = PerlMem_malloc(VMS_MAXRSS); 7188 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7189 7190 if (nextslash != NULL) { 7191 7192 seg_len = nextslash - unixptr; 7193 strncpy(esa, unixptr, seg_len); 7194 esa[seg_len] = 0; 7195 } 7196 else { 7197 strcpy(esa, unixptr); 7198 seg_len = strlen(unixptr); 7199 } 7200 /* trnlnm(section) */ 7201 islnm = vmstrnenv(esa, trn, 0, fildev, 0); 7202 7203 if (islnm) { 7204 /* Now fix up the directory */ 7205 7206 /* Split up the path to find the components */ 7207 sts = vms_split_path 7208 (trn, 7209 &v_spec, 7210 &v_len, 7211 &r_spec, 7212 &r_len, 7213 &d_spec, 7214 &d_len, 7215 &n_spec, 7216 &n_len, 7217 &e_spec, 7218 &e_len, 7219 &vs_spec, 7220 &vs_len); 7221 7222 while (sts == 0) { 7223 char * strt; 7224 int cmp; 7225 7226 /* A logical name must be a directory or the full 7227 specification. It is only a full specification if 7228 it is the only component */ 7229 if ((unixptr[seg_len] == '\0') || 7230 (unixptr[seg_len+1] == '\0')) { 7231 7232 /* Is a directory being required? */ 7233 if (((n_len + e_len) != 0) && (dir_flag !=0)) { 7234 /* Not a logical name */ 7235 break; 7236 } 7237 7238 7239 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) { 7240 /* This must be a directory */ 7241 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) { 7242 strcpy(vmsptr, esa); 7243 vmslen=strlen(vmsptr); 7244 vmsptr[vmslen] = ':'; 7245 vmslen++; 7246 vmsptr[vmslen] = '\0'; 7247 return SS$_NORMAL; 7248 } 7249 } 7250 7251 } 7252 7253 7254 /* must be dev/directory - ignore version */ 7255 if ((n_len + e_len) != 0) 7256 break; 7257 7258 /* transfer the volume */ 7259 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) { 7260 strncpy(vmsptr, v_spec, v_len); 7261 vmsptr += v_len; 7262 vmsptr[0] = '\0'; 7263 vmslen += v_len; 7264 } 7265 7266 /* unroot the rooted directory */ 7267 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) { 7268 r_spec[0] = '['; 7269 r_spec[r_len - 1] = ']'; 7270 7271 /* This should not be there, but nothing is perfect */ 7272 if (r_len > 9) { 7273 cmp = strcmp(&r_spec[1], "000000."); 7274 if (cmp == 0) { 7275 r_spec += 7; 7276 r_spec[7] = '['; 7277 r_len -= 7; 7278 if (r_len == 2) 7279 r_len = 0; 7280 } 7281 } 7282 if (r_len > 0) { 7283 strncpy(vmsptr, r_spec, r_len); 7284 vmsptr += r_len; 7285 vmslen += r_len; 7286 vmsptr[0] = '\0'; 7287 } 7288 } 7289 /* Bring over the directory. */ 7290 if ((d_len > 0) && 7291 ((d_len + vmslen) < vmspath_len)) { 7292 d_spec[0] = '['; 7293 d_spec[d_len - 1] = ']'; 7294 if (d_len > 9) { 7295 cmp = strcmp(&d_spec[1], "000000."); 7296 if (cmp == 0) { 7297 d_spec += 7; 7298 d_spec[7] = '['; 7299 d_len -= 7; 7300 if (d_len == 2) 7301 d_len = 0; 7302 } 7303 } 7304 7305 if (r_len > 0) { 7306 /* Remove the redundant root */ 7307 if (r_len > 0) { 7308 /* remove the ][ */ 7309 vmsptr--; 7310 vmslen--; 7311 d_spec++; 7312 d_len--; 7313 } 7314 strncpy(vmsptr, d_spec, d_len); 7315 vmsptr += d_len; 7316 vmslen += d_len; 7317 vmsptr[0] = '\0'; 7318 } 7319 } 7320 break; 7321 } 7322 } 7323 7324 PerlMem_free(esa); 7325 PerlMem_free(trn); 7326 } 7327 7328 if (lastslash > unixptr) { 7329 int dotdir_seen; 7330 7331 /* skip leading ./ */ 7332 dotdir_seen = 0; 7333 while ((unixptr[0] == '.') && (unixptr[1] == '/')) { 7334 dotdir_seen = 1; 7335 unixptr++; 7336 unixptr++; 7337 } 7338 7339 /* Are we still in a directory? */ 7340 if (unixptr <= lastslash) { 7341 *vmsptr++ = '['; 7342 vmslen = 1; 7343 dir_start = 1; 7344 7345 /* if not backing up, then it is relative forward. */ 7346 if (!((*unixptr == '.') && (unixptr[1] == '.') && 7347 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) { 7348 *vmsptr++ = '.'; 7349 vmslen++; 7350 dir_dot = 1; 7351 } 7352 } 7353 else { 7354 if (dotdir_seen) { 7355 /* Perl wants an empty directory here to tell the difference 7356 * between a DCL commmand and a filename 7357 */ 7358 *vmsptr++ = '['; 7359 *vmsptr++ = ']'; 7360 vmslen = 2; 7361 } 7362 } 7363 } 7364 else { 7365 /* Handle two special files . and .. */ 7366 if (unixptr[0] == '.') { 7367 if (&unixptr[1] == unixend) { 7368 *vmsptr++ = '['; 7369 *vmsptr++ = ']'; 7370 vmslen += 2; 7371 *vmsptr++ = '\0'; 7372 return SS$_NORMAL; 7373 } 7374 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) { 7375 *vmsptr++ = '['; 7376 *vmsptr++ = '-'; 7377 *vmsptr++ = ']'; 7378 vmslen += 3; 7379 *vmsptr++ = '\0'; 7380 return SS$_NORMAL; 7381 } 7382 } 7383 } 7384 } 7385 else { /* Absolute PATH handling */ 7386 int sts; 7387 char * nextslash; 7388 int seg_len; 7389 /* Need to find out where root is */ 7390 7391 /* In theory, this procedure should never get an absolute POSIX pathname 7392 * that can not be found on the POSIX root. 7393 * In practice, that can not be relied on, and things will show up 7394 * here that are a VMS device name or concealed logical name instead. 7395 * So to make things work, this procedure must be tolerant. 7396 */ 7397 esa = PerlMem_malloc(vmspath_len); 7398 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM); 7399 7400 sts = SS$_NORMAL; 7401 nextslash = strchr(&unixptr[1],'/'); 7402 seg_len = 0; 7403 if (nextslash != NULL) { 7404 int cmp; 7405 seg_len = nextslash - &unixptr[1]; 7406 strncpy(vmspath, unixptr, seg_len + 1); 7407 vmspath[seg_len+1] = 0; 7408 cmp = 1; 7409 if (seg_len == 3) { 7410 cmp = strncmp(vmspath, "dev", 4); 7411 if (cmp == 0) { 7412 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len); 7413 if (sts = SS$_NORMAL) 7414 return SS$_NORMAL; 7415 } 7416 } 7417 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl); 7418 } 7419 7420 if ($VMS_STATUS_SUCCESS(sts)) { 7421 /* This is verified to be a real path */ 7422 7423 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL); 7424 if ($VMS_STATUS_SUCCESS(sts)) { 7425 strcpy(vmspath, esa); 7426 vmslen = strlen(vmspath); 7427 vmsptr = vmspath + vmslen; 7428 unixptr++; 7429 if (unixptr < lastslash) { 7430 char * rptr; 7431 vmsptr--; 7432 *vmsptr++ = '.'; 7433 dir_start = 1; 7434 dir_dot = 1; 7435 if (vmslen > 7) { 7436 int cmp; 7437 rptr = vmsptr - 7; 7438 cmp = strcmp(rptr,"000000."); 7439 if (cmp == 0) { 7440 vmslen -= 7; 7441 vmsptr -= 7; 7442 vmsptr[1] = '\0'; 7443 } /* removing 6 zeros */ 7444 } /* vmslen < 7, no 6 zeros possible */ 7445 } /* Not in a directory */ 7446 } /* Posix root found */ 7447 else { 7448 /* No posix root, fall back to default directory */ 7449 strcpy(vmspath, "SYS$DISK:["); 7450 vmsptr = &vmspath[10]; 7451 vmslen = 10; 7452 if (unixptr > lastslash) { 7453 *vmsptr = ']'; 7454 vmsptr++; 7455 vmslen++; 7456 } 7457 else { 7458 dir_start = 1; 7459 } 7460 } 7461 } /* end of verified real path handling */ 7462 else { 7463 int add_6zero; 7464 int islnm; 7465 7466 /* Ok, we have a device or a concealed root that is not in POSIX 7467 * or we have garbage. Make the best of it. 7468 */ 7469 7470 /* Posix to VMS destroyed this, so copy it again */ 7471 strncpy(vmspath, &unixptr[1], seg_len); 7472 vmspath[seg_len] = 0; 7473 vmslen = seg_len; 7474 vmsptr = &vmsptr[vmslen]; 7475 islnm = 0; 7476 7477 /* Now do we need to add the fake 6 zero directory to it? */ 7478 add_6zero = 1; 7479 if ((*lastslash == '/') && (nextslash < lastslash)) { 7480 /* No there is another directory */ 7481 add_6zero = 0; 7482 } 7483 else { 7484 int trnend; 7485 int cmp; 7486 7487 /* now we have foo:bar or foo:[000000]bar to decide from */ 7488 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0); 7489 7490 if (!islnm && !decc_posix_compliant_pathnames) { 7491 7492 cmp = strncmp("bin", vmspath, 4); 7493 if (cmp == 0) { 7494 /* bin => SYS$SYSTEM: */ 7495 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0); 7496 } 7497 else { 7498 /* tmp => SYS$SCRATCH: */ 7499 cmp = strncmp("tmp", vmspath, 4); 7500 if (cmp == 0) { 7501 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0); 7502 } 7503 } 7504 } 7505 7506 trnend = islnm ? islnm - 1 : 0; 7507 7508 /* if this was a logical name, ']' or '>' must be present */ 7509 /* if not a logical name, then assume a device and hope. */ 7510 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0; 7511 7512 /* if log name and trailing '.' then rooted - treat as device */ 7513 add_6zero = islnm ? (esa[trnend-1] == '.') : 0; 7514 7515 /* Fix me, if not a logical name, a device lookup should be 7516 * done to see if the device is file structured. If the device 7517 * is not file structured, the 6 zeros should not be put on. 7518 * 7519 * As it is, perl is occasionally looking for dev:[000000]tty. 7520 * which looks a little strange. 7521 * 7522 * Not that easy to detect as "/dev" may be file structured with 7523 * special device files. 7524 */ 7525 7526 if ((add_6zero == 0) && (*nextslash == '/') && 7527 (&nextslash[1] == unixend)) { 7528 /* No real directory present */ 7529 add_6zero = 1; 7530 } 7531 } 7532 7533 /* Put the device delimiter on */ 7534 *vmsptr++ = ':'; 7535 vmslen++; 7536 unixptr = nextslash; 7537 unixptr++; 7538 7539 /* Start directory if needed */ 7540 if (!islnm || add_6zero) { 7541 *vmsptr++ = '['; 7542 vmslen++; 7543 dir_start = 1; 7544 } 7545 7546 /* add fake 000000] if needed */ 7547 if (add_6zero) { 7548 *vmsptr++ = '0'; 7549 *vmsptr++ = '0'; 7550 *vmsptr++ = '0'; 7551 *vmsptr++ = '0'; 7552 *vmsptr++ = '0'; 7553 *vmsptr++ = '0'; 7554 *vmsptr++ = ']'; 7555 vmslen += 7; 7556 dir_start = 0; 7557 } 7558 7559 } /* non-POSIX translation */ 7560 PerlMem_free(esa); 7561 } /* End of relative/absolute path handling */ 7562 7563 while ((unixptr <= unixend) && (vmslen < vmspath_len)){ 7564 int dash_flag; 7565 int in_cnt; 7566 int out_cnt; 7567 7568 dash_flag = 0; 7569 7570 if (dir_start != 0) { 7571 7572 /* First characters in a directory are handled special */ 7573 while ((*unixptr == '/') || 7574 ((*unixptr == '.') && 7575 ((unixptr[1]=='.') || (unixptr[1]=='/') || 7576 (&unixptr[1]==unixend)))) { 7577 int loop_flag; 7578 7579 loop_flag = 0; 7580 7581 /* Skip redundant / in specification */ 7582 while ((*unixptr == '/') && (dir_start != 0)) { 7583 loop_flag = 1; 7584 unixptr++; 7585 if (unixptr == lastslash) 7586 break; 7587 } 7588 if (unixptr == lastslash) 7589 break; 7590 7591 /* Skip redundant ./ characters */ 7592 while ((*unixptr == '.') && 7593 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) { 7594 loop_flag = 1; 7595 unixptr++; 7596 if (unixptr == lastslash) 7597 break; 7598 if (*unixptr == '/') 7599 unixptr++; 7600 } 7601 if (unixptr == lastslash) 7602 break; 7603 7604 /* Skip redundant ../ characters */ 7605 while ((*unixptr == '.') && (unixptr[1] == '.') && 7606 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) { 7607 /* Set the backing up flag */ 7608 loop_flag = 1; 7609 dir_dot = 0; 7610 dash_flag = 1; 7611 *vmsptr++ = '-'; 7612 vmslen++; 7613 unixptr++; /* first . */ 7614 unixptr++; /* second . */ 7615 if (unixptr == lastslash) 7616 break; 7617 if (*unixptr == '/') /* The slash */ 7618 unixptr++; 7619 } 7620 if (unixptr == lastslash) 7621 break; 7622 7623 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 7624 /* Not needed when VMS is pretending to be UNIX. */ 7625 7626 /* Is this loop stuck because of too many dots? */ 7627 if (loop_flag == 0) { 7628 /* Exit the loop and pass the rest through */ 7629 break; 7630 } 7631 } 7632 7633 /* Are we done with directories yet? */ 7634 if (unixptr >= lastslash) { 7635 7636 /* Watch out for trailing dots */ 7637 if (dir_dot != 0) { 7638 vmslen --; 7639 vmsptr--; 7640 } 7641 *vmsptr++ = ']'; 7642 vmslen++; 7643 dash_flag = 0; 7644 dir_start = 0; 7645 if (*unixptr == '/') 7646 unixptr++; 7647 } 7648 else { 7649 /* Have we stopped backing up? */ 7650 if (dash_flag) { 7651 *vmsptr++ = '.'; 7652 vmslen++; 7653 dash_flag = 0; 7654 /* dir_start continues to be = 1 */ 7655 } 7656 if (*unixptr == '-') { 7657 *vmsptr++ = '^'; 7658 *vmsptr++ = *unixptr++; 7659 vmslen += 2; 7660 dir_start = 0; 7661 7662 /* Now are we done with directories yet? */ 7663 if (unixptr >= lastslash) { 7664 7665 /* Watch out for trailing dots */ 7666 if (dir_dot != 0) { 7667 vmslen --; 7668 vmsptr--; 7669 } 7670 7671 *vmsptr++ = ']'; 7672 vmslen++; 7673 dash_flag = 0; 7674 dir_start = 0; 7675 } 7676 } 7677 } 7678 } 7679 7680 /* All done? */ 7681 if (unixptr >= unixend) 7682 break; 7683 7684 /* Normal characters - More EFS work probably needed */ 7685 dir_start = 0; 7686 dir_dot = 0; 7687 7688 switch(*unixptr) { 7689 case '/': 7690 /* remove multiple / */ 7691 while (unixptr[1] == '/') { 7692 unixptr++; 7693 } 7694 if (unixptr == lastslash) { 7695 /* Watch out for trailing dots */ 7696 if (dir_dot != 0) { 7697 vmslen --; 7698 vmsptr--; 7699 } 7700 *vmsptr++ = ']'; 7701 } 7702 else { 7703 dir_start = 1; 7704 *vmsptr++ = '.'; 7705 dir_dot = 1; 7706 7707 /* To do: Perl expects /.../ to be translated to [...] on VMS */ 7708 /* Not needed when VMS is pretending to be UNIX. */ 7709 7710 } 7711 dash_flag = 0; 7712 if (unixptr != unixend) 7713 unixptr++; 7714 vmslen++; 7715 break; 7716 case '.': 7717 if ((unixptr < lastdot) || (unixptr < lastslash) || 7718 (&unixptr[1] == unixend)) { 7719 *vmsptr++ = '^'; 7720 *vmsptr++ = '.'; 7721 vmslen += 2; 7722 unixptr++; 7723 7724 /* trailing dot ==> '^..' on VMS */ 7725 if (unixptr == unixend) { 7726 *vmsptr++ = '.'; 7727 vmslen++; 7728 unixptr++; 7729 } 7730 break; 7731 } 7732 7733 *vmsptr++ = *unixptr++; 7734 vmslen ++; 7735 break; 7736 case '"': 7737 if (quoted && (&unixptr[1] == unixend)) { 7738 unixptr++; 7739 break; 7740 } 7741 in_cnt = copy_expand_unix_filename_escape 7742 (vmsptr, unixptr, &out_cnt, utf8_fl); 7743 vmsptr += out_cnt; 7744 unixptr += in_cnt; 7745 break; 7746 case '~': 7747 case ';': 7748 case '\\': 7749 case '?': 7750 case ' ': 7751 default: 7752 in_cnt = copy_expand_unix_filename_escape 7753 (vmsptr, unixptr, &out_cnt, utf8_fl); 7754 vmsptr += out_cnt; 7755 unixptr += in_cnt; 7756 break; 7757 } 7758 } 7759 7760 /* Make sure directory is closed */ 7761 if (unixptr == lastslash) { 7762 char *vmsptr2; 7763 vmsptr2 = vmsptr - 1; 7764 7765 if (*vmsptr2 != ']') { 7766 *vmsptr2--; 7767 7768 /* directories do not end in a dot bracket */ 7769 if (*vmsptr2 == '.') { 7770 vmsptr2--; 7771 7772 /* ^. is allowed */ 7773 if (*vmsptr2 != '^') { 7774 vmsptr--; /* back up over the dot */ 7775 } 7776 } 7777 *vmsptr++ = ']'; 7778 } 7779 } 7780 else { 7781 char *vmsptr2; 7782 /* Add a trailing dot if a file with no extension */ 7783 vmsptr2 = vmsptr - 1; 7784 if ((vmslen > 1) && 7785 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') && 7786 (*vmsptr2 != ')') && (*lastdot != '.')) { 7787 *vmsptr++ = '.'; 7788 vmslen++; 7789 } 7790 } 7791 7792 *vmsptr = '\0'; 7793 return SS$_NORMAL; 7794 } 7795 #endif 7796 7797 /* Eventual routine to convert a UTF-8 specification to VTF-7. */ 7798 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl) 7799 { 7800 char * result; 7801 int utf8_flag; 7802 7803 /* If a UTF8 flag is being passed, honor it */ 7804 utf8_flag = 0; 7805 if (utf8_fl != NULL) { 7806 utf8_flag = *utf8_fl; 7807 *utf8_fl = 0; 7808 } 7809 7810 if (utf8_flag) { 7811 /* If there is a possibility of UTF8, then if any UTF8 characters 7812 are present, then they must be converted to VTF-7 7813 */ 7814 result = strcpy(rslt, path); /* FIX-ME */ 7815 } 7816 else 7817 result = strcpy(rslt, path); 7818 7819 return result; 7820 } 7821 7822 7823 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/ 7824 static char *mp_do_tovmsspec 7825 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) { 7826 static char __tovmsspec_retbuf[VMS_MAXRSS]; 7827 char *rslt, *dirend; 7828 char *lastdot; 7829 char *vms_delim; 7830 register char *cp1; 7831 const char *cp2; 7832 unsigned long int infront = 0, hasdir = 1; 7833 int rslt_len; 7834 int no_type_seen; 7835 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 7836 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 7837 7838 if (path == NULL) return NULL; 7839 rslt_len = VMS_MAXRSS-1; 7840 if (buf) rslt = buf; 7841 else if (ts) Newx(rslt, VMS_MAXRSS, char); 7842 else rslt = __tovmsspec_retbuf; 7843 7844 /* '.' and '..' are "[]" and "[-]" for a quick check */ 7845 if (path[0] == '.') { 7846 if (path[1] == '\0') { 7847 strcpy(rslt,"[]"); 7848 if (utf8_flag != NULL) 7849 *utf8_flag = 0; 7850 return rslt; 7851 } 7852 else { 7853 if (path[1] == '.' && path[2] == '\0') { 7854 strcpy(rslt,"[-]"); 7855 if (utf8_flag != NULL) 7856 *utf8_flag = 0; 7857 return rslt; 7858 } 7859 } 7860 } 7861 7862 /* Posix specifications are now a native VMS format */ 7863 /*--------------------------------------------------*/ 7864 #if __CRTL_VER >= 80200000 && !defined(__VAX) 7865 if (decc_posix_compliant_pathnames) { 7866 if (strncmp(path,"\"^UP^",5) == 0) { 7867 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag); 7868 return rslt; 7869 } 7870 } 7871 #endif 7872 7873 /* This is really the only way to see if this is already in VMS format */ 7874 sts = vms_split_path 7875 (path, 7876 &v_spec, 7877 &v_len, 7878 &r_spec, 7879 &r_len, 7880 &d_spec, 7881 &d_len, 7882 &n_spec, 7883 &n_len, 7884 &e_spec, 7885 &e_len, 7886 &vs_spec, 7887 &vs_len); 7888 if (sts == 0) { 7889 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath() 7890 replacement, because the above parse just took care of most of 7891 what is needed to do vmspath when the specification is already 7892 in VMS format. 7893 7894 And if it is not already, it is easier to do the conversion as 7895 part of this routine than to call this routine and then work on 7896 the result. 7897 */ 7898 7899 /* If VMS punctuation was found, it is already VMS format */ 7900 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) { 7901 if (utf8_flag != NULL) 7902 *utf8_flag = 0; 7903 strcpy(rslt, path); 7904 return rslt; 7905 } 7906 /* Now, what to do with trailing "." cases where there is no 7907 extension? If this is a UNIX specification, and EFS characters 7908 are enabled, then the trailing "." should be converted to a "^.". 7909 But if this was already a VMS specification, then it should be 7910 left alone. 7911 7912 So in the case of ambiguity, leave the specification alone. 7913 */ 7914 7915 7916 /* If there is a possibility of UTF8, then if any UTF8 characters 7917 are present, then they must be converted to VTF-7 7918 */ 7919 if (utf8_flag != NULL) 7920 *utf8_flag = 0; 7921 strcpy(rslt, path); 7922 return rslt; 7923 } 7924 7925 dirend = strrchr(path,'/'); 7926 7927 if (dirend == NULL) { 7928 /* If we get here with no UNIX directory delimiters, then this is 7929 not a complete file specification, either garbage a UNIX glob 7930 specification that can not be converted to a VMS wildcard, or 7931 it a UNIX shell macro. MakeMaker wants these passed through AS-IS, 7932 so apparently other programs expect this also. 7933 7934 utf8 flag setting needs to be preserved. 7935 */ 7936 strcpy(rslt, path); 7937 return rslt; 7938 } 7939 7940 /* If POSIX mode active, handle the conversion */ 7941 #if __CRTL_VER >= 80200000 && !defined(__VAX) 7942 if (decc_efs_charset) { 7943 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag); 7944 return rslt; 7945 } 7946 #endif 7947 7948 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */ 7949 if (!*(dirend+2)) dirend +=2; 7950 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3; 7951 if (decc_efs_charset == 0) { 7952 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4; 7953 } 7954 } 7955 7956 cp1 = rslt; 7957 cp2 = path; 7958 lastdot = strrchr(cp2,'.'); 7959 if (*cp2 == '/') { 7960 char *trndev; 7961 int islnm, rooted; 7962 STRLEN trnend; 7963 7964 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 7965 if (!*(cp2+1)) { 7966 if (decc_disable_posix_root) { 7967 strcpy(rslt,"sys$disk:[000000]"); 7968 } 7969 else { 7970 strcpy(rslt,"sys$posix_root:[000000]"); 7971 } 7972 if (utf8_flag != NULL) 7973 *utf8_flag = 0; 7974 return rslt; 7975 } 7976 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2; 7977 *cp1 = '\0'; 7978 trndev = PerlMem_malloc(VMS_MAXRSS); 7979 if (trndev == NULL) _ckvmssts(SS$_INSFMEM); 7980 islnm = my_trnlnm(rslt,trndev,0); 7981 7982 /* DECC special handling */ 7983 if (!islnm) { 7984 if (strcmp(rslt,"bin") == 0) { 7985 strcpy(rslt,"sys$system"); 7986 cp1 = rslt + 10; 7987 *cp1 = 0; 7988 islnm = my_trnlnm(rslt,trndev,0); 7989 } 7990 else if (strcmp(rslt,"tmp") == 0) { 7991 strcpy(rslt,"sys$scratch"); 7992 cp1 = rslt + 11; 7993 *cp1 = 0; 7994 islnm = my_trnlnm(rslt,trndev,0); 7995 } 7996 else if (!decc_disable_posix_root) { 7997 strcpy(rslt, "sys$posix_root"); 7998 cp1 = rslt + 13; 7999 *cp1 = 0; 8000 cp2 = path; 8001 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */ 8002 islnm = my_trnlnm(rslt,trndev,0); 8003 } 8004 else if (strcmp(rslt,"dev") == 0) { 8005 if (strncmp(cp2,"/null", 5) == 0) { 8006 if ((cp2[5] == 0) || (cp2[5] == '/')) { 8007 strcpy(rslt,"NLA0"); 8008 cp1 = rslt + 4; 8009 *cp1 = 0; 8010 cp2 = cp2 + 5; 8011 islnm = my_trnlnm(rslt,trndev,0); 8012 } 8013 } 8014 } 8015 } 8016 8017 trnend = islnm ? strlen(trndev) - 1 : 0; 8018 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0; 8019 rooted = islnm ? (trndev[trnend-1] == '.') : 0; 8020 /* If the first element of the path is a logical name, determine 8021 * whether it has to be translated so we can add more directories. */ 8022 if (!islnm || rooted) { 8023 *(cp1++) = ':'; 8024 *(cp1++) = '['; 8025 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0'; 8026 else cp2++; 8027 } 8028 else { 8029 if (cp2 != dirend) { 8030 strcpy(rslt,trndev); 8031 cp1 = rslt + trnend; 8032 if (*cp2 != 0) { 8033 *(cp1++) = '.'; 8034 cp2++; 8035 } 8036 } 8037 else { 8038 if (decc_disable_posix_root) { 8039 *(cp1++) = ':'; 8040 hasdir = 0; 8041 } 8042 } 8043 } 8044 PerlMem_free(trndev); 8045 } 8046 else { 8047 *(cp1++) = '['; 8048 if (*cp2 == '.') { 8049 if (*(cp2+1) == '/' || *(cp2+1) == '\0') { 8050 cp2 += 2; /* skip over "./" - it's redundant */ 8051 *(cp1++) = '.'; /* but it does indicate a relative dirspec */ 8052 } 8053 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8054 *(cp1++) = '-'; /* "../" --> "-" */ 8055 cp2 += 3; 8056 } 8057 else if (*(cp2+1) == '.' && *(cp2+2) == '.' && 8058 (*(cp2+3) == '/' || *(cp2+3) == '\0')) { 8059 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8060 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */ 8061 cp2 += 4; 8062 } 8063 else if ((cp2 != lastdot) || (lastdot < dirend)) { 8064 /* Escape the extra dots in EFS file specifications */ 8065 *(cp1++) = '^'; 8066 } 8067 if (cp2 > dirend) cp2 = dirend; 8068 } 8069 else *(cp1++) = '.'; 8070 } 8071 for (; cp2 < dirend; cp2++) { 8072 if (*cp2 == '/') { 8073 if (*(cp2-1) == '/') continue; 8074 if (*(cp1-1) != '.') *(cp1++) = '.'; 8075 infront = 0; 8076 } 8077 else if (!infront && *cp2 == '.') { 8078 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; } 8079 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */ 8080 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) { 8081 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */ 8082 else if (*(cp1-2) == '[') *(cp1-1) = '-'; 8083 else { /* back up over previous directory name */ 8084 cp1--; 8085 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--; 8086 if (*(cp1-1) == '[') { 8087 memcpy(cp1,"000000.",7); 8088 cp1 += 7; 8089 } 8090 } 8091 cp2 += 2; 8092 if (cp2 == dirend) break; 8093 } 8094 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' && 8095 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) { 8096 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */ 8097 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */ 8098 if (!*(cp2+3)) { 8099 *(cp1++) = '.'; /* Simulate trailing '/' */ 8100 cp2 += 2; /* for loop will incr this to == dirend */ 8101 } 8102 else cp2 += 3; /* Trailing '/' was there, so skip it, too */ 8103 } 8104 else { 8105 if (decc_efs_charset == 0) 8106 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */ 8107 else { 8108 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */ 8109 *(cp1++) = '.'; 8110 } 8111 } 8112 } 8113 else { 8114 if (!infront && *(cp1-1) == '-') *(cp1++) = '.'; 8115 if (*cp2 == '.') { 8116 if (decc_efs_charset == 0) 8117 *(cp1++) = '_'; 8118 else { 8119 *(cp1++) = '^'; 8120 *(cp1++) = '.'; 8121 } 8122 } 8123 else *(cp1++) = *cp2; 8124 infront = 1; 8125 } 8126 } 8127 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */ 8128 if (hasdir) *(cp1++) = ']'; 8129 if (*cp2) cp2++; /* check in case we ended with trailing '..' */ 8130 /* fixme for ODS5 */ 8131 no_type_seen = 0; 8132 if (cp2 > lastdot) 8133 no_type_seen = 1; 8134 while (*cp2) { 8135 switch(*cp2) { 8136 case '?': 8137 if (decc_efs_charset == 0) 8138 *(cp1++) = '%'; 8139 else 8140 *(cp1++) = '?'; 8141 cp2++; 8142 case ' ': 8143 *(cp1)++ = '^'; 8144 *(cp1)++ = '_'; 8145 cp2++; 8146 break; 8147 case '.': 8148 if (((cp2 < lastdot) || (cp2[1] == '\0')) && 8149 decc_readdir_dropdotnotype) { 8150 *(cp1)++ = '^'; 8151 *(cp1)++ = '.'; 8152 cp2++; 8153 8154 /* trailing dot ==> '^..' on VMS */ 8155 if (*cp2 == '\0') { 8156 *(cp1++) = '.'; 8157 no_type_seen = 0; 8158 } 8159 } 8160 else { 8161 *(cp1++) = *(cp2++); 8162 no_type_seen = 0; 8163 } 8164 break; 8165 case '$': 8166 /* This could be a macro to be passed through */ 8167 *(cp1++) = *(cp2++); 8168 if (*cp2 == '(') { 8169 const char * save_cp2; 8170 char * save_cp1; 8171 int is_macro; 8172 8173 /* paranoid check */ 8174 save_cp2 = cp2; 8175 save_cp1 = cp1; 8176 is_macro = 0; 8177 8178 /* Test through */ 8179 *(cp1++) = *(cp2++); 8180 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8181 *(cp1++) = *(cp2++); 8182 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) { 8183 *(cp1++) = *(cp2++); 8184 } 8185 if (*cp2 == ')') { 8186 *(cp1++) = *(cp2++); 8187 is_macro = 1; 8188 } 8189 } 8190 if (is_macro == 0) { 8191 /* Not really a macro - never mind */ 8192 cp2 = save_cp2; 8193 cp1 = save_cp1; 8194 } 8195 } 8196 break; 8197 case '\"': 8198 case '~': 8199 case '`': 8200 case '!': 8201 case '#': 8202 case '%': 8203 case '^': 8204 /* Don't escape again if following character is 8205 * already something we escape. 8206 */ 8207 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) { 8208 *(cp1++) = *(cp2++); 8209 break; 8210 } 8211 /* But otherwise fall through and escape it. */ 8212 case '&': 8213 case '(': 8214 case ')': 8215 case '=': 8216 case '+': 8217 case '\'': 8218 case '@': 8219 case '[': 8220 case ']': 8221 case '{': 8222 case '}': 8223 case ':': 8224 case '\\': 8225 case '|': 8226 case '<': 8227 case '>': 8228 *(cp1++) = '^'; 8229 *(cp1++) = *(cp2++); 8230 break; 8231 case ';': 8232 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs 8233 * which is wrong. UNIX notation should be ".dir." unless 8234 * the DECC$FILENAME_UNIX_NO_VERSION is enabled. 8235 * changing this behavior could break more things at this time. 8236 * efs character set effectively does not allow "." to be a version 8237 * delimiter as a further complication about changing this. 8238 */ 8239 if (decc_filename_unix_report != 0) { 8240 *(cp1++) = '^'; 8241 } 8242 *(cp1++) = *(cp2++); 8243 break; 8244 default: 8245 *(cp1++) = *(cp2++); 8246 } 8247 } 8248 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) { 8249 char *lcp1; 8250 lcp1 = cp1; 8251 lcp1--; 8252 /* Fix me for "^]", but that requires making sure that you do 8253 * not back up past the start of the filename 8254 */ 8255 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%')) 8256 *cp1++ = '.'; 8257 } 8258 *cp1 = '\0'; 8259 8260 if (utf8_flag != NULL) 8261 *utf8_flag = 0; 8262 return rslt; 8263 8264 } /* end of do_tovmsspec() */ 8265 /*}}}*/ 8266 /* External entry points */ 8267 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) 8268 { return do_tovmsspec(path,buf,0,NULL); } 8269 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) 8270 { return do_tovmsspec(path,buf,1,NULL); } 8271 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8272 { return do_tovmsspec(path,buf,0,utf8_fl); } 8273 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8274 { return do_tovmsspec(path,buf,1,utf8_fl); } 8275 8276 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/ 8277 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) { 8278 static char __tovmspath_retbuf[VMS_MAXRSS]; 8279 int vmslen; 8280 char *pathified, *vmsified, *cp; 8281 8282 if (path == NULL) return NULL; 8283 pathified = PerlMem_malloc(VMS_MAXRSS); 8284 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8285 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) { 8286 PerlMem_free(pathified); 8287 return NULL; 8288 } 8289 8290 vmsified = NULL; 8291 if (buf == NULL) 8292 Newx(vmsified, VMS_MAXRSS, char); 8293 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) { 8294 PerlMem_free(pathified); 8295 if (vmsified) Safefree(vmsified); 8296 return NULL; 8297 } 8298 PerlMem_free(pathified); 8299 if (buf) { 8300 return buf; 8301 } 8302 else if (ts) { 8303 vmslen = strlen(vmsified); 8304 Newx(cp,vmslen+1,char); 8305 memcpy(cp,vmsified,vmslen); 8306 cp[vmslen] = '\0'; 8307 Safefree(vmsified); 8308 return cp; 8309 } 8310 else { 8311 strcpy(__tovmspath_retbuf,vmsified); 8312 Safefree(vmsified); 8313 return __tovmspath_retbuf; 8314 } 8315 8316 } /* end of do_tovmspath() */ 8317 /*}}}*/ 8318 /* External entry points */ 8319 char *Perl_tovmspath(pTHX_ const char *path, char *buf) 8320 { return do_tovmspath(path,buf,0, NULL); } 8321 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) 8322 { return do_tovmspath(path,buf,1, NULL); } 8323 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl) 8324 { return do_tovmspath(path,buf,0,utf8_fl); } 8325 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl) 8326 { return do_tovmspath(path,buf,1,utf8_fl); } 8327 8328 8329 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/ 8330 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) { 8331 static char __tounixpath_retbuf[VMS_MAXRSS]; 8332 int unixlen; 8333 char *pathified, *unixified, *cp; 8334 8335 if (path == NULL) return NULL; 8336 pathified = PerlMem_malloc(VMS_MAXRSS); 8337 if (pathified == NULL) _ckvmssts(SS$_INSFMEM); 8338 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) { 8339 PerlMem_free(pathified); 8340 return NULL; 8341 } 8342 8343 unixified = NULL; 8344 if (buf == NULL) { 8345 Newx(unixified, VMS_MAXRSS, char); 8346 } 8347 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) { 8348 PerlMem_free(pathified); 8349 if (unixified) Safefree(unixified); 8350 return NULL; 8351 } 8352 PerlMem_free(pathified); 8353 if (buf) { 8354 return buf; 8355 } 8356 else if (ts) { 8357 unixlen = strlen(unixified); 8358 Newx(cp,unixlen+1,char); 8359 memcpy(cp,unixified,unixlen); 8360 cp[unixlen] = '\0'; 8361 Safefree(unixified); 8362 return cp; 8363 } 8364 else { 8365 strcpy(__tounixpath_retbuf,unixified); 8366 Safefree(unixified); 8367 return __tounixpath_retbuf; 8368 } 8369 8370 } /* end of do_tounixpath() */ 8371 /*}}}*/ 8372 /* External entry points */ 8373 char *Perl_tounixpath(pTHX_ const char *path, char *buf) 8374 { return do_tounixpath(path,buf,0,NULL); } 8375 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) 8376 { return do_tounixpath(path,buf,1,NULL); } 8377 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl) 8378 { return do_tounixpath(path,buf,0,utf8_fl); } 8379 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl) 8380 { return do_tounixpath(path,buf,1,utf8_fl); } 8381 8382 /* 8383 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com) 8384 * 8385 ***************************************************************************** 8386 * * 8387 * Copyright (C) 1989-1994, 2007 by * 8388 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 * 8389 * * 8390 * Permission is hereby granted for the reproduction of this software * 8391 * on condition that this copyright notice is included in source * 8392 * distributions of the software. The code may be modified and * 8393 * distributed under the same terms as Perl itself. * 8394 * * 8395 * 27-Aug-1994 Modified for inclusion in perl5 * 8396 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) * 8397 ***************************************************************************** 8398 */ 8399 8400 /* 8401 * getredirection() is intended to aid in porting C programs 8402 * to VMS (Vax-11 C). The native VMS environment does not support 8403 * '>' and '<' I/O redirection, or command line wild card expansion, 8404 * or a command line pipe mechanism using the '|' AND background 8405 * command execution '&'. All of these capabilities are provided to any 8406 * C program which calls this procedure as the first thing in the 8407 * main program. 8408 * The piping mechanism will probably work with almost any 'filter' type 8409 * of program. With suitable modification, it may useful for other 8410 * portability problems as well. 8411 * 8412 * Author: Mark Pizzolato (mark AT infocomm DOT com) 8413 */ 8414 struct list_item 8415 { 8416 struct list_item *next; 8417 char *value; 8418 }; 8419 8420 static void add_item(struct list_item **head, 8421 struct list_item **tail, 8422 char *value, 8423 int *count); 8424 8425 static void mp_expand_wild_cards(pTHX_ char *item, 8426 struct list_item **head, 8427 struct list_item **tail, 8428 int *count); 8429 8430 static int background_process(pTHX_ int argc, char **argv); 8431 8432 static void pipe_and_fork(pTHX_ char **cmargv); 8433 8434 /*{{{ void getredirection(int *ac, char ***av)*/ 8435 static void 8436 mp_getredirection(pTHX_ int *ac, char ***av) 8437 /* 8438 * Process vms redirection arg's. Exit if any error is seen. 8439 * If getredirection() processes an argument, it is erased 8440 * from the vector. getredirection() returns a new argc and argv value. 8441 * In the event that a background command is requested (by a trailing "&"), 8442 * this routine creates a background subprocess, and simply exits the program. 8443 * 8444 * Warning: do not try to simplify the code for vms. The code 8445 * presupposes that getredirection() is called before any data is 8446 * read from stdin or written to stdout. 8447 * 8448 * Normal usage is as follows: 8449 * 8450 * main(argc, argv) 8451 * int argc; 8452 * char *argv[]; 8453 * { 8454 * getredirection(&argc, &argv); 8455 * } 8456 */ 8457 { 8458 int argc = *ac; /* Argument Count */ 8459 char **argv = *av; /* Argument Vector */ 8460 char *ap; /* Argument pointer */ 8461 int j; /* argv[] index */ 8462 int item_count = 0; /* Count of Items in List */ 8463 struct list_item *list_head = 0; /* First Item in List */ 8464 struct list_item *list_tail; /* Last Item in List */ 8465 char *in = NULL; /* Input File Name */ 8466 char *out = NULL; /* Output File Name */ 8467 char *outmode = "w"; /* Mode to Open Output File */ 8468 char *err = NULL; /* Error File Name */ 8469 char *errmode = "w"; /* Mode to Open Error File */ 8470 int cmargc = 0; /* Piped Command Arg Count */ 8471 char **cmargv = NULL;/* Piped Command Arg Vector */ 8472 8473 /* 8474 * First handle the case where the last thing on the line ends with 8475 * a '&'. This indicates the desire for the command to be run in a 8476 * subprocess, so we satisfy that desire. 8477 */ 8478 ap = argv[argc-1]; 8479 if (0 == strcmp("&", ap)) 8480 exit(background_process(aTHX_ --argc, argv)); 8481 if (*ap && '&' == ap[strlen(ap)-1]) 8482 { 8483 ap[strlen(ap)-1] = '\0'; 8484 exit(background_process(aTHX_ argc, argv)); 8485 } 8486 /* 8487 * Now we handle the general redirection cases that involve '>', '>>', 8488 * '<', and pipes '|'. 8489 */ 8490 for (j = 0; j < argc; ++j) 8491 { 8492 if (0 == strcmp("<", argv[j])) 8493 { 8494 if (j+1 >= argc) 8495 { 8496 fprintf(stderr,"No input file after < on command line"); 8497 exit(LIB$_WRONUMARG); 8498 } 8499 in = argv[++j]; 8500 continue; 8501 } 8502 if ('<' == *(ap = argv[j])) 8503 { 8504 in = 1 + ap; 8505 continue; 8506 } 8507 if (0 == strcmp(">", ap)) 8508 { 8509 if (j+1 >= argc) 8510 { 8511 fprintf(stderr,"No output file after > on command line"); 8512 exit(LIB$_WRONUMARG); 8513 } 8514 out = argv[++j]; 8515 continue; 8516 } 8517 if ('>' == *ap) 8518 { 8519 if ('>' == ap[1]) 8520 { 8521 outmode = "a"; 8522 if ('\0' == ap[2]) 8523 out = argv[++j]; 8524 else 8525 out = 2 + ap; 8526 } 8527 else 8528 out = 1 + ap; 8529 if (j >= argc) 8530 { 8531 fprintf(stderr,"No output file after > or >> on command line"); 8532 exit(LIB$_WRONUMARG); 8533 } 8534 continue; 8535 } 8536 if (('2' == *ap) && ('>' == ap[1])) 8537 { 8538 if ('>' == ap[2]) 8539 { 8540 errmode = "a"; 8541 if ('\0' == ap[3]) 8542 err = argv[++j]; 8543 else 8544 err = 3 + ap; 8545 } 8546 else 8547 if ('\0' == ap[2]) 8548 err = argv[++j]; 8549 else 8550 err = 2 + ap; 8551 if (j >= argc) 8552 { 8553 fprintf(stderr,"No output file after 2> or 2>> on command line"); 8554 exit(LIB$_WRONUMARG); 8555 } 8556 continue; 8557 } 8558 if (0 == strcmp("|", argv[j])) 8559 { 8560 if (j+1 >= argc) 8561 { 8562 fprintf(stderr,"No command into which to pipe on command line"); 8563 exit(LIB$_WRONUMARG); 8564 } 8565 cmargc = argc-(j+1); 8566 cmargv = &argv[j+1]; 8567 argc = j; 8568 continue; 8569 } 8570 if ('|' == *(ap = argv[j])) 8571 { 8572 ++argv[j]; 8573 cmargc = argc-j; 8574 cmargv = &argv[j]; 8575 argc = j; 8576 continue; 8577 } 8578 expand_wild_cards(ap, &list_head, &list_tail, &item_count); 8579 } 8580 /* 8581 * Allocate and fill in the new argument vector, Some Unix's terminate 8582 * the list with an extra null pointer. 8583 */ 8584 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *)); 8585 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8586 *av = argv; 8587 for (j = 0; j < item_count; ++j, list_head = list_head->next) 8588 argv[j] = list_head->value; 8589 *ac = item_count; 8590 if (cmargv != NULL) 8591 { 8592 if (out != NULL) 8593 { 8594 fprintf(stderr,"'|' and '>' may not both be specified on command line"); 8595 exit(LIB$_INVARGORD); 8596 } 8597 pipe_and_fork(aTHX_ cmargv); 8598 } 8599 8600 /* Check for input from a pipe (mailbox) */ 8601 8602 if (in == NULL && 1 == isapipe(0)) 8603 { 8604 char mbxname[L_tmpnam]; 8605 long int bufsize; 8606 long int dvi_item = DVI$_DEVBUFSIZ; 8607 $DESCRIPTOR(mbxnam, ""); 8608 $DESCRIPTOR(mbxdevnam, ""); 8609 8610 /* Input from a pipe, reopen it in binary mode to disable */ 8611 /* carriage control processing. */ 8612 8613 fgetname(stdin, mbxname); 8614 mbxnam.dsc$a_pointer = mbxname; 8615 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer); 8616 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0); 8617 mbxdevnam.dsc$a_pointer = mbxname; 8618 mbxdevnam.dsc$w_length = sizeof(mbxname); 8619 dvi_item = DVI$_DEVNAM; 8620 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length); 8621 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0'; 8622 set_errno(0); 8623 set_vaxc_errno(1); 8624 freopen(mbxname, "rb", stdin); 8625 if (errno != 0) 8626 { 8627 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname); 8628 exit(vaxc$errno); 8629 } 8630 } 8631 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2"))) 8632 { 8633 fprintf(stderr,"Can't open input file %s as stdin",in); 8634 exit(vaxc$errno); 8635 } 8636 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2"))) 8637 { 8638 fprintf(stderr,"Can't open output file %s as stdout",out); 8639 exit(vaxc$errno); 8640 } 8641 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out); 8642 8643 if (err != NULL) { 8644 if (strcmp(err,"&1") == 0) { 8645 dup2(fileno(stdout), fileno(stderr)); 8646 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT"); 8647 } else { 8648 FILE *tmperr; 8649 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2"))) 8650 { 8651 fprintf(stderr,"Can't open error file %s as stderr",err); 8652 exit(vaxc$errno); 8653 } 8654 fclose(tmperr); 8655 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2")) 8656 { 8657 exit(vaxc$errno); 8658 } 8659 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err); 8660 } 8661 } 8662 #ifdef ARGPROC_DEBUG 8663 PerlIO_printf(Perl_debug_log, "Arglist:\n"); 8664 for (j = 0; j < *ac; ++j) 8665 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]); 8666 #endif 8667 /* Clear errors we may have hit expanding wildcards, so they don't 8668 show up in Perl's $! later */ 8669 set_errno(0); set_vaxc_errno(1); 8670 } /* end of getredirection() */ 8671 /*}}}*/ 8672 8673 static void add_item(struct list_item **head, 8674 struct list_item **tail, 8675 char *value, 8676 int *count) 8677 { 8678 if (*head == 0) 8679 { 8680 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 8681 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8682 *tail = *head; 8683 } 8684 else { 8685 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item)); 8686 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8687 *tail = (*tail)->next; 8688 } 8689 (*tail)->value = value; 8690 ++(*count); 8691 } 8692 8693 static void mp_expand_wild_cards(pTHX_ char *item, 8694 struct list_item **head, 8695 struct list_item **tail, 8696 int *count) 8697 { 8698 int expcount = 0; 8699 unsigned long int context = 0; 8700 int isunix = 0; 8701 int item_len = 0; 8702 char *had_version; 8703 char *had_device; 8704 int had_directory; 8705 char *devdir,*cp; 8706 char *vmsspec; 8707 $DESCRIPTOR(filespec, ""); 8708 $DESCRIPTOR(defaultspec, "SYS$DISK:[]"); 8709 $DESCRIPTOR(resultspec, ""); 8710 unsigned long int lff_flags = 0; 8711 int sts; 8712 int rms_sts; 8713 8714 #ifdef VMS_LONGNAME_SUPPORT 8715 lff_flags = LIB$M_FIL_LONG_NAMES; 8716 #endif 8717 8718 for (cp = item; *cp; cp++) { 8719 if (*cp == '*' || *cp == '%' || isspace(*cp)) break; 8720 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break; 8721 } 8722 if (!*cp || isspace(*cp)) 8723 { 8724 add_item(head, tail, item, count); 8725 return; 8726 } 8727 else 8728 { 8729 /* "double quoted" wild card expressions pass as is */ 8730 /* From DCL that means using e.g.: */ 8731 /* perl program """perl.*""" */ 8732 item_len = strlen(item); 8733 if ( '"' == *item && '"' == item[item_len-1] ) 8734 { 8735 item++; 8736 item[item_len-2] = '\0'; 8737 add_item(head, tail, item, count); 8738 return; 8739 } 8740 } 8741 resultspec.dsc$b_dtype = DSC$K_DTYPE_T; 8742 resultspec.dsc$b_class = DSC$K_CLASS_D; 8743 resultspec.dsc$a_pointer = NULL; 8744 vmsspec = PerlMem_malloc(VMS_MAXRSS); 8745 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8746 if ((isunix = (int) strchr(item,'/')) != (int) NULL) 8747 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL); 8748 if (!isunix || !filespec.dsc$a_pointer) 8749 filespec.dsc$a_pointer = item; 8750 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer); 8751 /* 8752 * Only return version specs, if the caller specified a version 8753 */ 8754 had_version = strchr(item, ';'); 8755 /* 8756 * Only return device and directory specs, if the caller specifed either. 8757 */ 8758 had_device = strchr(item, ':'); 8759 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<')); 8760 8761 while ($VMS_STATUS_SUCCESS(sts = lib$find_file 8762 (&filespec, &resultspec, &context, 8763 &defaultspec, 0, &rms_sts, &lff_flags))) 8764 { 8765 char *string; 8766 char *c; 8767 8768 string = PerlMem_malloc(resultspec.dsc$w_length+1); 8769 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM); 8770 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length); 8771 string[resultspec.dsc$w_length] = '\0'; 8772 if (NULL == had_version) 8773 *(strrchr(string, ';')) = '\0'; 8774 if ((!had_directory) && (had_device == NULL)) 8775 { 8776 if (NULL == (devdir = strrchr(string, ']'))) 8777 devdir = strrchr(string, '>'); 8778 strcpy(string, devdir + 1); 8779 } 8780 /* 8781 * Be consistent with what the C RTL has already done to the rest of 8782 * the argv items and lowercase all of these names. 8783 */ 8784 if (!decc_efs_case_preserve) { 8785 for (c = string; *c; ++c) 8786 if (isupper(*c)) 8787 *c = tolower(*c); 8788 } 8789 if (isunix) trim_unixpath(string,item,1); 8790 add_item(head, tail, string, count); 8791 ++expcount; 8792 } 8793 PerlMem_free(vmsspec); 8794 if (sts != RMS$_NMF) 8795 { 8796 set_vaxc_errno(sts); 8797 switch (sts) 8798 { 8799 case RMS$_FNF: case RMS$_DNF: 8800 set_errno(ENOENT); break; 8801 case RMS$_DIR: 8802 set_errno(ENOTDIR); break; 8803 case RMS$_DEV: 8804 set_errno(ENODEV); break; 8805 case RMS$_FNM: case RMS$_SYN: 8806 set_errno(EINVAL); break; 8807 case RMS$_PRV: 8808 set_errno(EACCES); break; 8809 default: 8810 _ckvmssts_noperl(sts); 8811 } 8812 } 8813 if (expcount == 0) 8814 add_item(head, tail, item, count); 8815 _ckvmssts_noperl(lib$sfree1_dd(&resultspec)); 8816 _ckvmssts_noperl(lib$find_file_end(&context)); 8817 } 8818 8819 static int child_st[2];/* Event Flag set when child process completes */ 8820 8821 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */ 8822 8823 static unsigned long int exit_handler(int *status) 8824 { 8825 short iosb[4]; 8826 8827 if (0 == child_st[0]) 8828 { 8829 #ifdef ARGPROC_DEBUG 8830 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n"); 8831 #endif 8832 fflush(stdout); /* Have to flush pipe for binary data to */ 8833 /* terminate properly -- <tp@mccall.com> */ 8834 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0); 8835 sys$dassgn(child_chan); 8836 fclose(stdout); 8837 sys$synch(0, child_st); 8838 } 8839 return(1); 8840 } 8841 8842 static void sig_child(int chan) 8843 { 8844 #ifdef ARGPROC_DEBUG 8845 PerlIO_printf(Perl_debug_log, "Child Completion AST\n"); 8846 #endif 8847 if (child_st[0] == 0) 8848 child_st[0] = 1; 8849 } 8850 8851 static struct exit_control_block exit_block = 8852 { 8853 0, 8854 exit_handler, 8855 1, 8856 &exit_block.exit_status, 8857 0 8858 }; 8859 8860 static void 8861 pipe_and_fork(pTHX_ char **cmargv) 8862 { 8863 PerlIO *fp; 8864 struct dsc$descriptor_s *vmscmd; 8865 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q; 8866 int sts, j, l, ismcr, quote, tquote = 0; 8867 8868 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd); 8869 vms_execfree(vmscmd); 8870 8871 j = l = 0; 8872 p = subcmd; 8873 q = cmargv[0]; 8874 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C' 8875 && toupper(*(q+2)) == 'R' && !*(q+3); 8876 8877 while (q && l < MAX_DCL_LINE_LENGTH) { 8878 if (!*q) { 8879 if (j > 0 && quote) { 8880 *p++ = '"'; 8881 l++; 8882 } 8883 q = cmargv[++j]; 8884 if (q) { 8885 if (ismcr && j > 1) quote = 1; 8886 tquote = (strchr(q,' ')) != NULL || *q == '\0'; 8887 *p++ = ' '; 8888 l++; 8889 if (quote || tquote) { 8890 *p++ = '"'; 8891 l++; 8892 } 8893 } 8894 } else { 8895 if ((quote||tquote) && *q == '"') { 8896 *p++ = '"'; 8897 l++; 8898 } 8899 *p++ = *q++; 8900 l++; 8901 } 8902 } 8903 *p = '\0'; 8904 8905 fp = safe_popen(aTHX_ subcmd,"wbF",&sts); 8906 if (fp == Nullfp) { 8907 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts); 8908 } 8909 } 8910 8911 static int background_process(pTHX_ int argc, char **argv) 8912 { 8913 char command[MAX_DCL_SYMBOL + 1] = "$"; 8914 $DESCRIPTOR(value, ""); 8915 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND"); 8916 static $DESCRIPTOR(null, "NLA0:"); 8917 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID"); 8918 char pidstring[80]; 8919 $DESCRIPTOR(pidstr, ""); 8920 int pid; 8921 unsigned long int flags = 17, one = 1, retsts; 8922 int len; 8923 8924 strcat(command, argv[0]); 8925 len = strlen(command); 8926 while (--argc && (len < MAX_DCL_SYMBOL)) 8927 { 8928 strcat(command, " \""); 8929 strcat(command, *(++argv)); 8930 strcat(command, "\""); 8931 len = strlen(command); 8932 } 8933 value.dsc$a_pointer = command; 8934 value.dsc$w_length = strlen(value.dsc$a_pointer); 8935 _ckvmssts_noperl(lib$set_symbol(&cmd, &value)); 8936 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid); 8937 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */ 8938 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid)); 8939 } 8940 else { 8941 _ckvmssts_noperl(retsts); 8942 } 8943 #ifdef ARGPROC_DEBUG 8944 PerlIO_printf(Perl_debug_log, "%s\n", command); 8945 #endif 8946 sprintf(pidstring, "%08X", pid); 8947 PerlIO_printf(Perl_debug_log, "%s\n", pidstring); 8948 pidstr.dsc$a_pointer = pidstring; 8949 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer); 8950 lib$set_symbol(&pidsymbol, &pidstr); 8951 return(SS$_NORMAL); 8952 } 8953 /*}}}*/ 8954 /***** End of code taken from Mark Pizzolato's argproc.c package *****/ 8955 8956 8957 /* OS-specific initialization at image activation (not thread startup) */ 8958 /* Older VAXC header files lack these constants */ 8959 #ifndef JPI$_RIGHTS_SIZE 8960 # define JPI$_RIGHTS_SIZE 817 8961 #endif 8962 #ifndef KGB$M_SUBSYSTEM 8963 # define KGB$M_SUBSYSTEM 0x8 8964 #endif 8965 8966 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */ 8967 8968 /*{{{void vms_image_init(int *, char ***)*/ 8969 void 8970 vms_image_init(int *argcp, char ***argvp) 8971 { 8972 char eqv[LNM$C_NAMLENGTH+1] = ""; 8973 unsigned int len, tabct = 8, tabidx = 0; 8974 unsigned long int *mask, iosb[2], i, rlst[128], rsz; 8975 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)]; 8976 unsigned short int dummy, rlen; 8977 struct dsc$descriptor_s **tabvec; 8978 #if defined(PERL_IMPLICIT_CONTEXT) 8979 pTHX = NULL; 8980 #endif 8981 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy}, 8982 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen}, 8983 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy}, 8984 { 0, 0, 0, 0} }; 8985 8986 #ifdef KILL_BY_SIGPRC 8987 Perl_csighandler_init(); 8988 #endif 8989 8990 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL)); 8991 _ckvmssts_noperl(iosb[0]); 8992 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) { 8993 if (iprv[i]) { /* Running image installed with privs? */ 8994 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */ 8995 will_taint = TRUE; 8996 break; 8997 } 8998 } 8999 /* Rights identifiers might trigger tainting as well. */ 9000 if (!will_taint && (rlen || rsz)) { 9001 while (rlen < rsz) { 9002 /* We didn't get all the identifiers on the first pass. Allocate a 9003 * buffer much larger than $GETJPI wants (rsz is size in bytes that 9004 * were needed to hold all identifiers at time of last call; we'll 9005 * allocate that many unsigned long ints), and go back and get 'em. 9006 * If it gave us less than it wanted to despite ample buffer space, 9007 * something's broken. Is your system missing a system identifier? 9008 */ 9009 if (rsz <= jpilist[1].buflen) { 9010 /* Perl_croak accvios when used this early in startup. */ 9011 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s", 9012 rsz, (unsigned long) jpilist[1].buflen, 9013 "Check your rights database for corruption.\n"); 9014 exit(SS$_ABORT); 9015 } 9016 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr); 9017 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int)); 9018 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9019 jpilist[1].buflen = rsz * sizeof(unsigned long int); 9020 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL)); 9021 _ckvmssts_noperl(iosb[0]); 9022 } 9023 mask = jpilist[1].bufadr; 9024 /* Check attribute flags for each identifier (2nd longword); protected 9025 * subsystem identifiers trigger tainting. 9026 */ 9027 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) { 9028 if (mask[i] & KGB$M_SUBSYSTEM) { 9029 will_taint = TRUE; 9030 break; 9031 } 9032 } 9033 if (mask != rlst) PerlMem_free(mask); 9034 } 9035 9036 /* When Perl is in decc_filename_unix_report mode and is run from a concealed 9037 * logical, some versions of the CRTL will add a phanthom /000000/ 9038 * directory. This needs to be removed. 9039 */ 9040 if (decc_filename_unix_report) { 9041 char * zeros; 9042 int ulen; 9043 ulen = strlen(argvp[0][0]); 9044 if (ulen > 7) { 9045 zeros = strstr(argvp[0][0], "/000000/"); 9046 if (zeros != NULL) { 9047 int mlen; 9048 mlen = ulen - (zeros - argvp[0][0]) - 7; 9049 memmove(zeros, &zeros[7], mlen); 9050 ulen = ulen - 7; 9051 argvp[0][0][ulen] = '\0'; 9052 } 9053 } 9054 /* It also may have a trailing dot that needs to be removed otherwise 9055 * it will be converted to VMS mode incorrectly. 9056 */ 9057 ulen--; 9058 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype)) 9059 argvp[0][0][ulen] = '\0'; 9060 } 9061 9062 /* We need to use this hack to tell Perl it should run with tainting, 9063 * since its tainting flag may be part of the PL_curinterp struct, which 9064 * hasn't been allocated when vms_image_init() is called. 9065 */ 9066 if (will_taint) { 9067 char **newargv, **oldargv; 9068 oldargv = *argvp; 9069 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *)); 9070 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9071 newargv[0] = oldargv[0]; 9072 newargv[1] = PerlMem_malloc(3 * sizeof(char)); 9073 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9074 strcpy(newargv[1], "-T"); 9075 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **); 9076 (*argcp)++; 9077 newargv[*argcp] = NULL; 9078 /* We orphan the old argv, since we don't know where it's come from, 9079 * so we don't know how to free it. 9080 */ 9081 *argvp = newargv; 9082 } 9083 else { /* Did user explicitly request tainting? */ 9084 int i; 9085 char *cp, **av = *argvp; 9086 for (i = 1; i < *argcp; i++) { 9087 if (*av[i] != '-') break; 9088 for (cp = av[i]+1; *cp; cp++) { 9089 if (*cp == 'T') { will_taint = 1; break; } 9090 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' || 9091 strchr("DFIiMmx",*cp)) break; 9092 } 9093 if (will_taint) break; 9094 } 9095 } 9096 9097 for (tabidx = 0; 9098 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx); 9099 tabidx++) { 9100 if (!tabidx) { 9101 tabvec = (struct dsc$descriptor_s **) 9102 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *)); 9103 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9104 } 9105 else if (tabidx >= tabct) { 9106 tabct += 8; 9107 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *)); 9108 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9109 } 9110 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 9111 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM); 9112 tabvec[tabidx]->dsc$w_length = 0; 9113 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T; 9114 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D; 9115 tabvec[tabidx]->dsc$a_pointer = NULL; 9116 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx])); 9117 } 9118 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; } 9119 9120 getredirection(argcp,argvp); 9121 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) ) 9122 { 9123 # include <reentrancy.h> 9124 decc$set_reentrancy(C$C_MULTITHREAD); 9125 } 9126 #endif 9127 return; 9128 } 9129 /*}}}*/ 9130 9131 9132 /* trim_unixpath() 9133 * Trim Unix-style prefix off filespec, so it looks like what a shell 9134 * glob expansion would return (i.e. from specified prefix on, not 9135 * full path). Note that returned filespec is Unix-style, regardless 9136 * of whether input filespec was VMS-style or Unix-style. 9137 * 9138 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to 9139 * determine prefix (both may be in VMS or Unix syntax). opts is a bit 9140 * vector of options; at present, only bit 0 is used, and if set tells 9141 * trim unixpath to try the current default directory as a prefix when 9142 * presented with a possibly ambiguous ... wildcard. 9143 * 9144 * Returns !=0 on success, with trimmed filespec replacing contents of 9145 * fspec, and 0 on failure, with contents of fpsec unchanged. 9146 */ 9147 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/ 9148 int 9149 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts) 9150 { 9151 char *unixified, *unixwild, 9152 *template, *base, *end, *cp1, *cp2; 9153 register int tmplen, reslen = 0, dirs = 0; 9154 9155 unixwild = PerlMem_malloc(VMS_MAXRSS); 9156 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM); 9157 if (!wildspec || !fspec) return 0; 9158 template = unixwild; 9159 if (strpbrk(wildspec,"]>:") != NULL) { 9160 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) { 9161 PerlMem_free(unixwild); 9162 return 0; 9163 } 9164 } 9165 else { 9166 strncpy(unixwild, wildspec, VMS_MAXRSS-1); 9167 unixwild[VMS_MAXRSS-1] = 0; 9168 } 9169 unixified = PerlMem_malloc(VMS_MAXRSS); 9170 if (unixified == NULL) _ckvmssts(SS$_INSFMEM); 9171 if (strpbrk(fspec,"]>:") != NULL) { 9172 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) { 9173 PerlMem_free(unixwild); 9174 PerlMem_free(unixified); 9175 return 0; 9176 } 9177 else base = unixified; 9178 /* reslen != 0 ==> we had to unixify resultant filespec, so we must 9179 * check to see that final result fits into (isn't longer than) fspec */ 9180 reslen = strlen(fspec); 9181 } 9182 else base = fspec; 9183 9184 /* No prefix or absolute path on wildcard, so nothing to remove */ 9185 if (!*template || *template == '/') { 9186 PerlMem_free(unixwild); 9187 if (base == fspec) { 9188 PerlMem_free(unixified); 9189 return 1; 9190 } 9191 tmplen = strlen(unixified); 9192 if (tmplen > reslen) { 9193 PerlMem_free(unixified); 9194 return 0; /* not enough space */ 9195 } 9196 /* Copy unixified resultant, including trailing NUL */ 9197 memmove(fspec,unixified,tmplen+1); 9198 PerlMem_free(unixified); 9199 return 1; 9200 } 9201 9202 for (end = base; *end; end++) ; /* Find end of resultant filespec */ 9203 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */ 9204 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++; 9205 for (cp1 = end ;cp1 >= base; cp1--) 9206 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */ 9207 { cp1++; break; } 9208 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1); 9209 PerlMem_free(unixified); 9210 PerlMem_free(unixwild); 9211 return 1; 9212 } 9213 else { 9214 char *tpl, *lcres; 9215 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1; 9216 int ells = 1, totells, segdirs, match; 9217 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL}, 9218 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 9219 9220 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;} 9221 totells = ells; 9222 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++; 9223 tpl = PerlMem_malloc(VMS_MAXRSS); 9224 if (tpl == NULL) _ckvmssts(SS$_INSFMEM); 9225 if (ellipsis == template && opts & 1) { 9226 /* Template begins with an ellipsis. Since we can't tell how many 9227 * directory names at the front of the resultant to keep for an 9228 * arbitrary starting point, we arbitrarily choose the current 9229 * default directory as a starting point. If it's there as a prefix, 9230 * clip it off. If not, fall through and act as if the leading 9231 * ellipsis weren't there (i.e. return shortest possible path that 9232 * could match template). 9233 */ 9234 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) { 9235 PerlMem_free(tpl); 9236 PerlMem_free(unixified); 9237 PerlMem_free(unixwild); 9238 return 0; 9239 } 9240 if (!decc_efs_case_preserve) { 9241 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9242 if (_tolower(*cp1) != _tolower(*cp2)) break; 9243 } 9244 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9245 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--; 9246 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) { 9247 memmove(fspec,cp2+1,end - cp2); 9248 PerlMem_free(tpl); 9249 PerlMem_free(unixified); 9250 PerlMem_free(unixwild); 9251 return 1; 9252 } 9253 } 9254 /* First off, back up over constant elements at end of path */ 9255 if (dirs) { 9256 for (front = end ; front >= base; front--) 9257 if (*front == '/' && !dirs--) { front++; break; } 9258 } 9259 lcres = PerlMem_malloc(VMS_MAXRSS); 9260 if (lcres == NULL) _ckvmssts(SS$_INSFMEM); 9261 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1); 9262 cp1++,cp2++) { 9263 if (!decc_efs_case_preserve) { 9264 *cp2 = _tolower(*cp1); /* Make lc copy for match */ 9265 } 9266 else { 9267 *cp2 = *cp1; 9268 } 9269 } 9270 if (cp1 != '\0') { 9271 PerlMem_free(tpl); 9272 PerlMem_free(unixified); 9273 PerlMem_free(unixwild); 9274 PerlMem_free(lcres); 9275 return 0; /* Path too long. */ 9276 } 9277 lcend = cp2; 9278 *cp2 = '\0'; /* Pick up with memcpy later */ 9279 lcfront = lcres + (front - base); 9280 /* Now skip over each ellipsis and try to match the path in front of it. */ 9281 while (ells--) { 9282 for (cp1 = ellipsis - 2; cp1 >= template; cp1--) 9283 if (*(cp1) == '.' && *(cp1+1) == '.' && 9284 *(cp1+2) == '.' && *(cp1+3) == '/' ) break; 9285 if (cp1 < template) break; /* template started with an ellipsis */ 9286 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */ 9287 ellipsis = cp1; continue; 9288 } 9289 wilddsc.dsc$a_pointer = tpl; 9290 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1; 9291 nextell = cp1; 9292 for (segdirs = 0, cp2 = tpl; 9293 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1); 9294 cp1++, cp2++) { 9295 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */ 9296 else { 9297 if (!decc_efs_case_preserve) { 9298 *cp2 = _tolower(*cp1); /* else lowercase for match */ 9299 } 9300 else { 9301 *cp2 = *cp1; /* else preserve case for match */ 9302 } 9303 } 9304 if (*cp2 == '/') segdirs++; 9305 } 9306 if (cp1 != ellipsis - 1) { 9307 PerlMem_free(tpl); 9308 PerlMem_free(unixified); 9309 PerlMem_free(unixwild); 9310 PerlMem_free(lcres); 9311 return 0; /* Path too long */ 9312 } 9313 /* Back up at least as many dirs as in template before matching */ 9314 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--) 9315 if (*cp1 == '/' && !segdirs--) { cp1++; break; } 9316 for (match = 0; cp1 > lcres;) { 9317 resdsc.dsc$a_pointer = cp1; 9318 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) { 9319 match++; 9320 if (match == 1) lcfront = cp1; 9321 } 9322 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; } 9323 } 9324 if (!match) { 9325 PerlMem_free(tpl); 9326 PerlMem_free(unixified); 9327 PerlMem_free(unixwild); 9328 PerlMem_free(lcres); 9329 return 0; /* Can't find prefix ??? */ 9330 } 9331 if (match > 1 && opts & 1) { 9332 /* This ... wildcard could cover more than one set of dirs (i.e. 9333 * a set of similar dir names is repeated). If the template 9334 * contains more than 1 ..., upstream elements could resolve the 9335 * ambiguity, but it's not worth a full backtracking setup here. 9336 * As a quick heuristic, clip off the current default directory 9337 * if it's present to find the trimmed spec, else use the 9338 * shortest string that this ... could cover. 9339 */ 9340 char def[NAM$C_MAXRSS+1], *st; 9341 9342 if (getcwd(def, sizeof def,0) == NULL) { 9343 Safefree(unixified); 9344 Safefree(unixwild); 9345 Safefree(lcres); 9346 Safefree(tpl); 9347 return 0; 9348 } 9349 if (!decc_efs_case_preserve) { 9350 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++) 9351 if (_tolower(*cp1) != _tolower(*cp2)) break; 9352 } 9353 segdirs = dirs - totells; /* Min # of dirs we must have left */ 9354 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--; 9355 if (*cp1 == '\0' && *cp2 == '/') { 9356 memmove(fspec,cp2+1,end - cp2); 9357 PerlMem_free(tpl); 9358 PerlMem_free(unixified); 9359 PerlMem_free(unixwild); 9360 PerlMem_free(lcres); 9361 return 1; 9362 } 9363 /* Nope -- stick with lcfront from above and keep going. */ 9364 } 9365 } 9366 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1); 9367 PerlMem_free(tpl); 9368 PerlMem_free(unixified); 9369 PerlMem_free(unixwild); 9370 PerlMem_free(lcres); 9371 return 1; 9372 ellipsis = nextell; 9373 } 9374 9375 } /* end of trim_unixpath() */ 9376 /*}}}*/ 9377 9378 9379 /* 9380 * VMS readdir() routines. 9381 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990. 9382 * 9383 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu 9384 * Minor modifications to original routines. 9385 */ 9386 9387 /* readdir may have been redefined by reentr.h, so make sure we get 9388 * the local version for what we do here. 9389 */ 9390 #ifdef readdir 9391 # undef readdir 9392 #endif 9393 #if !defined(PERL_IMPLICIT_CONTEXT) 9394 # define readdir Perl_readdir 9395 #else 9396 # define readdir(a) Perl_readdir(aTHX_ a) 9397 #endif 9398 9399 /* Number of elements in vms_versions array */ 9400 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0]) 9401 9402 /* 9403 * Open a directory, return a handle for later use. 9404 */ 9405 /*{{{ DIR *opendir(char*name) */ 9406 DIR * 9407 Perl_opendir(pTHX_ const char *name) 9408 { 9409 DIR *dd; 9410 char *dir; 9411 Stat_t sb; 9412 9413 Newx(dir, VMS_MAXRSS, char); 9414 if (do_tovmspath(name,dir,0,NULL) == NULL) { 9415 Safefree(dir); 9416 return NULL; 9417 } 9418 /* Check access before stat; otherwise stat does not 9419 * accurately report whether it's a directory. 9420 */ 9421 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) { 9422 /* cando_by_name has already set errno */ 9423 Safefree(dir); 9424 return NULL; 9425 } 9426 if (flex_stat(dir,&sb) == -1) return NULL; 9427 if (!S_ISDIR(sb.st_mode)) { 9428 Safefree(dir); 9429 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR); 9430 return NULL; 9431 } 9432 /* Get memory for the handle, and the pattern. */ 9433 Newx(dd,1,DIR); 9434 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char); 9435 9436 /* Fill in the fields; mainly playing with the descriptor. */ 9437 sprintf(dd->pattern, "%s*.*",dir); 9438 Safefree(dir); 9439 dd->context = 0; 9440 dd->count = 0; 9441 dd->flags = 0; 9442 /* By saying we always want the result of readdir() in unix format, we 9443 * are really saying we want all the escapes removed. Otherwise the caller, 9444 * having no way to know whether it's already in VMS format, might send it 9445 * through tovmsspec again, thus double escaping. 9446 */ 9447 dd->flags = PERL_VMSDIR_M_UNIXSPECS; 9448 dd->pat.dsc$a_pointer = dd->pattern; 9449 dd->pat.dsc$w_length = strlen(dd->pattern); 9450 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T; 9451 dd->pat.dsc$b_class = DSC$K_CLASS_S; 9452 #if defined(USE_ITHREADS) 9453 Newx(dd->mutex,1,perl_mutex); 9454 MUTEX_INIT( (perl_mutex *) dd->mutex ); 9455 #else 9456 dd->mutex = NULL; 9457 #endif 9458 9459 return dd; 9460 } /* end of opendir() */ 9461 /*}}}*/ 9462 9463 /* 9464 * Set the flag to indicate we want versions or not. 9465 */ 9466 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/ 9467 void 9468 vmsreaddirversions(DIR *dd, int flag) 9469 { 9470 if (flag) 9471 dd->flags |= PERL_VMSDIR_M_VERSIONS; 9472 else 9473 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 9474 } 9475 /*}}}*/ 9476 9477 /* 9478 * Free up an opened directory. 9479 */ 9480 /*{{{ void closedir(DIR *dd)*/ 9481 void 9482 Perl_closedir(DIR *dd) 9483 { 9484 int sts; 9485 9486 sts = lib$find_file_end(&dd->context); 9487 Safefree(dd->pattern); 9488 #if defined(USE_ITHREADS) 9489 MUTEX_DESTROY( (perl_mutex *) dd->mutex ); 9490 Safefree(dd->mutex); 9491 #endif 9492 Safefree(dd); 9493 } 9494 /*}}}*/ 9495 9496 /* 9497 * Collect all the version numbers for the current file. 9498 */ 9499 static void 9500 collectversions(pTHX_ DIR *dd) 9501 { 9502 struct dsc$descriptor_s pat; 9503 struct dsc$descriptor_s res; 9504 struct dirent *e; 9505 char *p, *text, *buff; 9506 int i; 9507 unsigned long context, tmpsts; 9508 9509 /* Convenient shorthand. */ 9510 e = &dd->entry; 9511 9512 /* Add the version wildcard, ignoring the "*.*" put on before */ 9513 i = strlen(dd->pattern); 9514 Newx(text,i + e->d_namlen + 3,char); 9515 strcpy(text, dd->pattern); 9516 sprintf(&text[i - 3], "%s;*", e->d_name); 9517 9518 /* Set up the pattern descriptor. */ 9519 pat.dsc$a_pointer = text; 9520 pat.dsc$w_length = i + e->d_namlen - 1; 9521 pat.dsc$b_dtype = DSC$K_DTYPE_T; 9522 pat.dsc$b_class = DSC$K_CLASS_S; 9523 9524 /* Set up result descriptor. */ 9525 Newx(buff, VMS_MAXRSS, char); 9526 res.dsc$a_pointer = buff; 9527 res.dsc$w_length = VMS_MAXRSS - 1; 9528 res.dsc$b_dtype = DSC$K_DTYPE_T; 9529 res.dsc$b_class = DSC$K_CLASS_S; 9530 9531 /* Read files, collecting versions. */ 9532 for (context = 0, e->vms_verscount = 0; 9533 e->vms_verscount < VERSIZE(e); 9534 e->vms_verscount++) { 9535 unsigned long rsts; 9536 unsigned long flags = 0; 9537 9538 #ifdef VMS_LONGNAME_SUPPORT 9539 flags = LIB$M_FIL_LONG_NAMES; 9540 #endif 9541 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags); 9542 if (tmpsts == RMS$_NMF || context == 0) break; 9543 _ckvmssts(tmpsts); 9544 buff[VMS_MAXRSS - 1] = '\0'; 9545 if ((p = strchr(buff, ';'))) 9546 e->vms_versions[e->vms_verscount] = atoi(p + 1); 9547 else 9548 e->vms_versions[e->vms_verscount] = -1; 9549 } 9550 9551 _ckvmssts(lib$find_file_end(&context)); 9552 Safefree(text); 9553 Safefree(buff); 9554 9555 } /* end of collectversions() */ 9556 9557 /* 9558 * Read the next entry from the directory. 9559 */ 9560 /*{{{ struct dirent *readdir(DIR *dd)*/ 9561 struct dirent * 9562 Perl_readdir(pTHX_ DIR *dd) 9563 { 9564 struct dsc$descriptor_s res; 9565 char *p, *buff; 9566 unsigned long int tmpsts; 9567 unsigned long rsts; 9568 unsigned long flags = 0; 9569 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 9570 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 9571 9572 /* Set up result descriptor, and get next file. */ 9573 Newx(buff, VMS_MAXRSS, char); 9574 res.dsc$a_pointer = buff; 9575 res.dsc$w_length = VMS_MAXRSS - 1; 9576 res.dsc$b_dtype = DSC$K_DTYPE_T; 9577 res.dsc$b_class = DSC$K_CLASS_S; 9578 9579 #ifdef VMS_LONGNAME_SUPPORT 9580 flags = LIB$M_FIL_LONG_NAMES; 9581 #endif 9582 9583 tmpsts = lib$find_file 9584 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags); 9585 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */ 9586 if (!(tmpsts & 1)) { 9587 set_vaxc_errno(tmpsts); 9588 switch (tmpsts) { 9589 case RMS$_PRV: 9590 set_errno(EACCES); break; 9591 case RMS$_DEV: 9592 set_errno(ENODEV); break; 9593 case RMS$_DIR: 9594 set_errno(ENOTDIR); break; 9595 case RMS$_FNF: case RMS$_DNF: 9596 set_errno(ENOENT); break; 9597 default: 9598 set_errno(EVMSERR); 9599 } 9600 Safefree(buff); 9601 return NULL; 9602 } 9603 dd->count++; 9604 /* Force the buffer to end with a NUL, and downcase name to match C convention. */ 9605 buff[res.dsc$w_length] = '\0'; 9606 p = buff + res.dsc$w_length; 9607 while (--p >= buff) if (!isspace(*p)) break; 9608 *p = '\0'; 9609 if (!decc_efs_case_preserve) { 9610 for (p = buff; *p; p++) *p = _tolower(*p); 9611 } 9612 9613 /* Skip any directory component and just copy the name. */ 9614 sts = vms_split_path 9615 (buff, 9616 &v_spec, 9617 &v_len, 9618 &r_spec, 9619 &r_len, 9620 &d_spec, 9621 &d_len, 9622 &n_spec, 9623 &n_len, 9624 &e_spec, 9625 &e_len, 9626 &vs_spec, 9627 &vs_len); 9628 9629 /* Drop NULL extensions on UNIX file specification */ 9630 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS && 9631 (e_len == 1) && decc_readdir_dropdotnotype)) { 9632 e_len = 0; 9633 e_spec[0] = '\0'; 9634 } 9635 9636 strncpy(dd->entry.d_name, n_spec, n_len + e_len); 9637 dd->entry.d_name[n_len + e_len] = '\0'; 9638 dd->entry.d_namlen = strlen(dd->entry.d_name); 9639 9640 /* Convert the filename to UNIX format if needed */ 9641 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) { 9642 9643 /* Translate the encoded characters. */ 9644 /* Fixme: Unicode handling could result in embedded 0 characters */ 9645 if (strchr(dd->entry.d_name, '^') != NULL) { 9646 char new_name[256]; 9647 char * q; 9648 p = dd->entry.d_name; 9649 q = new_name; 9650 while (*p != 0) { 9651 int inchars_read, outchars_added; 9652 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added); 9653 p += inchars_read; 9654 q += outchars_added; 9655 /* fix-me */ 9656 /* if outchars_added > 1, then this is a wide file specification */ 9657 /* Wide file specifications need to be passed in Perl */ 9658 /* counted strings apparently with a Unicode flag */ 9659 } 9660 *q = 0; 9661 strcpy(dd->entry.d_name, new_name); 9662 dd->entry.d_namlen = strlen(dd->entry.d_name); 9663 } 9664 } 9665 9666 dd->entry.vms_verscount = 0; 9667 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd); 9668 Safefree(buff); 9669 return &dd->entry; 9670 9671 } /* end of readdir() */ 9672 /*}}}*/ 9673 9674 /* 9675 * Read the next entry from the directory -- thread-safe version. 9676 */ 9677 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/ 9678 int 9679 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result) 9680 { 9681 int retval; 9682 9683 MUTEX_LOCK( (perl_mutex *) dd->mutex ); 9684 9685 entry = readdir(dd); 9686 *result = entry; 9687 retval = ( *result == NULL ? errno : 0 ); 9688 9689 MUTEX_UNLOCK( (perl_mutex *) dd->mutex ); 9690 9691 return retval; 9692 9693 } /* end of readdir_r() */ 9694 /*}}}*/ 9695 9696 /* 9697 * Return something that can be used in a seekdir later. 9698 */ 9699 /*{{{ long telldir(DIR *dd)*/ 9700 long 9701 Perl_telldir(DIR *dd) 9702 { 9703 return dd->count; 9704 } 9705 /*}}}*/ 9706 9707 /* 9708 * Return to a spot where we used to be. Brute force. 9709 */ 9710 /*{{{ void seekdir(DIR *dd,long count)*/ 9711 void 9712 Perl_seekdir(pTHX_ DIR *dd, long count) 9713 { 9714 int old_flags; 9715 9716 /* If we haven't done anything yet... */ 9717 if (dd->count == 0) 9718 return; 9719 9720 /* Remember some state, and clear it. */ 9721 old_flags = dd->flags; 9722 dd->flags &= ~PERL_VMSDIR_M_VERSIONS; 9723 _ckvmssts(lib$find_file_end(&dd->context)); 9724 dd->context = 0; 9725 9726 /* The increment is in readdir(). */ 9727 for (dd->count = 0; dd->count < count; ) 9728 readdir(dd); 9729 9730 dd->flags = old_flags; 9731 9732 } /* end of seekdir() */ 9733 /*}}}*/ 9734 9735 /* VMS subprocess management 9736 * 9737 * my_vfork() - just a vfork(), after setting a flag to record that 9738 * the current script is trying a Unix-style fork/exec. 9739 * 9740 * vms_do_aexec() and vms_do_exec() are called in response to the 9741 * perl 'exec' function. If this follows a vfork call, then they 9742 * call out the regular perl routines in doio.c which do an 9743 * execvp (for those who really want to try this under VMS). 9744 * Otherwise, they do exactly what the perl docs say exec should 9745 * do - terminate the current script and invoke a new command 9746 * (See below for notes on command syntax.) 9747 * 9748 * do_aspawn() and do_spawn() implement the VMS side of the perl 9749 * 'system' function. 9750 * 9751 * Note on command arguments to perl 'exec' and 'system': When handled 9752 * in 'VMSish fashion' (i.e. not after a call to vfork) The args 9753 * are concatenated to form a DCL command string. If the first non-numeric 9754 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such), 9755 * the command string is handed off to DCL directly. Otherwise, 9756 * the first token of the command is taken as the filespec of an image 9757 * to run. The filespec is expanded using a default type of '.EXE' and 9758 * the process defaults for device, directory, etc., and if found, the resultant 9759 * filespec is invoked using the DCL verb 'MCR', and passed the rest of 9760 * the command string as parameters. This is perhaps a bit complicated, 9761 * but I hope it will form a happy medium between what VMS folks expect 9762 * from lib$spawn and what Unix folks expect from exec. 9763 */ 9764 9765 static int vfork_called; 9766 9767 /*{{{int my_vfork()*/ 9768 int 9769 my_vfork() 9770 { 9771 vfork_called++; 9772 return vfork(); 9773 } 9774 /*}}}*/ 9775 9776 9777 static void 9778 vms_execfree(struct dsc$descriptor_s *vmscmd) 9779 { 9780 if (vmscmd) { 9781 if (vmscmd->dsc$a_pointer) { 9782 PerlMem_free(vmscmd->dsc$a_pointer); 9783 } 9784 PerlMem_free(vmscmd); 9785 } 9786 } 9787 9788 static char * 9789 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp) 9790 { 9791 char *junk, *tmps = Nullch; 9792 register size_t cmdlen = 0; 9793 size_t rlen; 9794 register SV **idx; 9795 STRLEN n_a; 9796 9797 idx = mark; 9798 if (really) { 9799 tmps = SvPV(really,rlen); 9800 if (*tmps) { 9801 cmdlen += rlen + 1; 9802 idx++; 9803 } 9804 } 9805 9806 for (idx++; idx <= sp; idx++) { 9807 if (*idx) { 9808 junk = SvPVx(*idx,rlen); 9809 cmdlen += rlen ? rlen + 1 : 0; 9810 } 9811 } 9812 Newx(PL_Cmd, cmdlen+1, char); 9813 9814 if (tmps && *tmps) { 9815 strcpy(PL_Cmd,tmps); 9816 mark++; 9817 } 9818 else *PL_Cmd = '\0'; 9819 while (++mark <= sp) { 9820 if (*mark) { 9821 char *s = SvPVx(*mark,n_a); 9822 if (!*s) continue; 9823 if (*PL_Cmd) strcat(PL_Cmd," "); 9824 strcat(PL_Cmd,s); 9825 } 9826 } 9827 return PL_Cmd; 9828 9829 } /* end of setup_argstr() */ 9830 9831 9832 static unsigned long int 9833 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote, 9834 struct dsc$descriptor_s **pvmscmd) 9835 { 9836 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1]; 9837 char image_name[NAM$C_MAXRSS+1]; 9838 char image_argv[NAM$C_MAXRSS+1]; 9839 $DESCRIPTOR(defdsc,".EXE"); 9840 $DESCRIPTOR(defdsc2,"."); 9841 $DESCRIPTOR(resdsc,resspec); 9842 struct dsc$descriptor_s *vmscmd; 9843 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 9844 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL; 9845 register char *s, *rest, *cp, *wordbreak; 9846 char * cmd; 9847 int cmdlen; 9848 register int isdcl; 9849 9850 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s)); 9851 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM); 9852 9853 /* Make a copy for modification */ 9854 cmdlen = strlen(incmd); 9855 cmd = PerlMem_malloc(cmdlen+1); 9856 if (cmd == NULL) _ckvmssts(SS$_INSFMEM); 9857 strncpy(cmd, incmd, cmdlen); 9858 cmd[cmdlen] = 0; 9859 image_name[0] = 0; 9860 image_argv[0] = 0; 9861 9862 vmscmd->dsc$a_pointer = NULL; 9863 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T; 9864 vmscmd->dsc$b_class = DSC$K_CLASS_S; 9865 vmscmd->dsc$w_length = 0; 9866 if (pvmscmd) *pvmscmd = vmscmd; 9867 9868 if (suggest_quote) *suggest_quote = 0; 9869 9870 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) { 9871 PerlMem_free(cmd); 9872 return CLI$_BUFOVF; /* continuation lines currently unsupported */ 9873 } 9874 9875 s = cmd; 9876 9877 while (*s && isspace(*s)) s++; 9878 9879 if (*s == '@' || *s == '$') { 9880 vmsspec[0] = *s; rest = s + 1; 9881 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest; 9882 } 9883 else { cp = vmsspec; rest = s; } 9884 if (*rest == '.' || *rest == '/') { 9885 char *cp2; 9886 for (cp2 = resspec; 9887 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec; 9888 rest++, cp2++) *cp2 = *rest; 9889 *cp2 = '\0'; 9890 if (do_tovmsspec(resspec,cp,0,NULL)) { 9891 s = vmsspec; 9892 if (*rest) { 9893 for (cp2 = vmsspec + strlen(vmsspec); 9894 *rest && cp2 - vmsspec < sizeof vmsspec; 9895 rest++, cp2++) *cp2 = *rest; 9896 *cp2 = '\0'; 9897 } 9898 } 9899 } 9900 /* Intuit whether verb (first word of cmd) is a DCL command: 9901 * - if first nonspace char is '@', it's a DCL indirection 9902 * otherwise 9903 * - if verb contains a filespec separator, it's not a DCL command 9904 * - if it doesn't, caller tells us whether to default to a DCL 9905 * command, or to a local image unless told it's DCL (by leading '$') 9906 */ 9907 if (*s == '@') { 9908 isdcl = 1; 9909 if (suggest_quote) *suggest_quote = 1; 9910 } else { 9911 register char *filespec = strpbrk(s,":<[.;"); 9912 rest = wordbreak = strpbrk(s," \"\t/"); 9913 if (!wordbreak) wordbreak = s + strlen(s); 9914 if (*s == '$') check_img = 0; 9915 if (filespec && (filespec < wordbreak)) isdcl = 0; 9916 else isdcl = !check_img; 9917 } 9918 9919 if (!isdcl) { 9920 int rsts; 9921 imgdsc.dsc$a_pointer = s; 9922 imgdsc.dsc$w_length = wordbreak - s; 9923 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 9924 if (!(retsts&1)) { 9925 _ckvmssts(lib$find_file_end(&cxt)); 9926 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 9927 if (!(retsts & 1) && *s == '$') { 9928 _ckvmssts(lib$find_file_end(&cxt)); 9929 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--; 9930 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags); 9931 if (!(retsts&1)) { 9932 _ckvmssts(lib$find_file_end(&cxt)); 9933 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags); 9934 } 9935 } 9936 } 9937 _ckvmssts(lib$find_file_end(&cxt)); 9938 9939 if (retsts & 1) { 9940 FILE *fp; 9941 s = resspec; 9942 while (*s && !isspace(*s)) s++; 9943 *s = '\0'; 9944 9945 /* check that it's really not DCL with no file extension */ 9946 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get"); 9947 if (fp) { 9948 char b[256] = {0,0,0,0}; 9949 read(fileno(fp), b, 256); 9950 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]); 9951 if (isdcl) { 9952 int shebang_len; 9953 9954 /* Check for script */ 9955 shebang_len = 0; 9956 if ((b[0] == '#') && (b[1] == '!')) 9957 shebang_len = 2; 9958 #ifdef ALTERNATE_SHEBANG 9959 else { 9960 shebang_len = strlen(ALTERNATE_SHEBANG); 9961 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) { 9962 char * perlstr; 9963 perlstr = strstr("perl",b); 9964 if (perlstr == NULL) 9965 shebang_len = 0; 9966 } 9967 else 9968 shebang_len = 0; 9969 } 9970 #endif 9971 9972 if (shebang_len > 0) { 9973 int i; 9974 int j; 9975 char tmpspec[NAM$C_MAXRSS + 1]; 9976 9977 i = shebang_len; 9978 /* Image is following after white space */ 9979 /*--------------------------------------*/ 9980 while (isprint(b[i]) && isspace(b[i])) 9981 i++; 9982 9983 j = 0; 9984 while (isprint(b[i]) && !isspace(b[i])) { 9985 tmpspec[j++] = b[i++]; 9986 if (j >= NAM$C_MAXRSS) 9987 break; 9988 } 9989 tmpspec[j] = '\0'; 9990 9991 /* There may be some default parameters to the image */ 9992 /*---------------------------------------------------*/ 9993 j = 0; 9994 while (isprint(b[i])) { 9995 image_argv[j++] = b[i++]; 9996 if (j >= NAM$C_MAXRSS) 9997 break; 9998 } 9999 while ((j > 0) && !isprint(image_argv[j-1])) 10000 j--; 10001 image_argv[j] = 0; 10002 10003 /* It will need to be converted to VMS format and validated */ 10004 if (tmpspec[0] != '\0') { 10005 char * iname; 10006 10007 /* Try to find the exact program requested to be run */ 10008 /*---------------------------------------------------*/ 10009 iname = do_rmsexpand 10010 (tmpspec, image_name, 0, ".exe", 10011 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10012 if (iname != NULL) { 10013 if (cando_by_name_int 10014 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) { 10015 /* MCR prefix needed */ 10016 isdcl = 0; 10017 } 10018 else { 10019 /* Try again with a null type */ 10020 /*----------------------------*/ 10021 iname = do_rmsexpand 10022 (tmpspec, image_name, 0, ".", 10023 PERL_RMSEXPAND_M_VMS, NULL, NULL); 10024 if (iname != NULL) { 10025 if (cando_by_name_int 10026 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) { 10027 /* MCR prefix needed */ 10028 isdcl = 0; 10029 } 10030 } 10031 } 10032 10033 /* Did we find the image to run the script? */ 10034 /*------------------------------------------*/ 10035 if (isdcl) { 10036 char *tchr; 10037 10038 /* Assume DCL or foreign command exists */ 10039 /*--------------------------------------*/ 10040 tchr = strrchr(tmpspec, '/'); 10041 if (tchr != NULL) { 10042 tchr++; 10043 } 10044 else { 10045 tchr = tmpspec; 10046 } 10047 strcpy(image_name, tchr); 10048 } 10049 } 10050 } 10051 } 10052 } 10053 fclose(fp); 10054 } 10055 if (check_img && isdcl) return RMS$_FNF; 10056 10057 if (cando_by_name(S_IXUSR,0,resspec)) { 10058 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH); 10059 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM); 10060 if (!isdcl) { 10061 strcpy(vmscmd->dsc$a_pointer,"$ MCR "); 10062 if (image_name[0] != 0) { 10063 strcat(vmscmd->dsc$a_pointer, image_name); 10064 strcat(vmscmd->dsc$a_pointer, " "); 10065 } 10066 } else if (image_name[0] != 0) { 10067 strcpy(vmscmd->dsc$a_pointer, image_name); 10068 strcat(vmscmd->dsc$a_pointer, " "); 10069 } else { 10070 strcpy(vmscmd->dsc$a_pointer,"@"); 10071 } 10072 if (suggest_quote) *suggest_quote = 1; 10073 10074 /* If there is an image name, use original command */ 10075 if (image_name[0] == 0) 10076 strcat(vmscmd->dsc$a_pointer,resspec); 10077 else { 10078 rest = cmd; 10079 while (*rest && isspace(*rest)) rest++; 10080 } 10081 10082 if (image_argv[0] != 0) { 10083 strcat(vmscmd->dsc$a_pointer,image_argv); 10084 strcat(vmscmd->dsc$a_pointer, " "); 10085 } 10086 if (rest) { 10087 int rest_len; 10088 int vmscmd_len; 10089 10090 rest_len = strlen(rest); 10091 vmscmd_len = strlen(vmscmd->dsc$a_pointer); 10092 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH) 10093 strcat(vmscmd->dsc$a_pointer,rest); 10094 else 10095 retsts = CLI$_BUFOVF; 10096 } 10097 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer); 10098 PerlMem_free(cmd); 10099 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10100 } 10101 else 10102 retsts = RMS$_PRV; 10103 } 10104 } 10105 /* It's either a DCL command or we couldn't find a suitable image */ 10106 vmscmd->dsc$w_length = strlen(cmd); 10107 10108 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1); 10109 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length); 10110 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0; 10111 10112 PerlMem_free(cmd); 10113 10114 /* check if it's a symbol (for quoting purposes) */ 10115 if (suggest_quote && !*suggest_quote) { 10116 int iss; 10117 char equiv[LNM$C_NAMLENGTH]; 10118 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 10119 eqvdsc.dsc$a_pointer = equiv; 10120 10121 iss = lib$get_symbol(vmscmd,&eqvdsc); 10122 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1; 10123 } 10124 if (!(retsts & 1)) { 10125 /* just hand off status values likely to be due to user error */ 10126 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV || 10127 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN || 10128 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts; 10129 else { _ckvmssts(retsts); } 10130 } 10131 10132 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts); 10133 10134 } /* end of setup_cmddsc() */ 10135 10136 10137 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */ 10138 bool 10139 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp) 10140 { 10141 bool exec_sts; 10142 char * cmd; 10143 10144 if (sp > mark) { 10145 if (vfork_called) { /* this follows a vfork - act Unixish */ 10146 vfork_called--; 10147 if (vfork_called < 0) { 10148 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10149 vfork_called = 0; 10150 } 10151 else return do_aexec(really,mark,sp); 10152 } 10153 /* no vfork - act VMSish */ 10154 cmd = setup_argstr(aTHX_ really,mark,sp); 10155 exec_sts = vms_do_exec(cmd); 10156 Safefree(cmd); /* Clean up from setup_argstr() */ 10157 return exec_sts; 10158 } 10159 10160 return FALSE; 10161 } /* end of vms_do_aexec() */ 10162 /*}}}*/ 10163 10164 /* {{{bool vms_do_exec(char *cmd) */ 10165 bool 10166 Perl_vms_do_exec(pTHX_ const char *cmd) 10167 { 10168 struct dsc$descriptor_s *vmscmd; 10169 10170 if (vfork_called) { /* this follows a vfork - act Unixish */ 10171 vfork_called--; 10172 if (vfork_called < 0) { 10173 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks"); 10174 vfork_called = 0; 10175 } 10176 else return do_exec(cmd); 10177 } 10178 10179 { /* no vfork - act VMSish */ 10180 unsigned long int retsts; 10181 10182 TAINT_ENV(); 10183 TAINT_PROPER("exec"); 10184 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1) 10185 retsts = lib$do_command(vmscmd); 10186 10187 switch (retsts) { 10188 case RMS$_FNF: case RMS$_DNF: 10189 set_errno(ENOENT); break; 10190 case RMS$_DIR: 10191 set_errno(ENOTDIR); break; 10192 case RMS$_DEV: 10193 set_errno(ENODEV); break; 10194 case RMS$_PRV: 10195 set_errno(EACCES); break; 10196 case RMS$_SYN: 10197 set_errno(EINVAL); break; 10198 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 10199 set_errno(E2BIG); break; 10200 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 10201 _ckvmssts(retsts); /* fall through */ 10202 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 10203 set_errno(EVMSERR); 10204 } 10205 set_vaxc_errno(retsts); 10206 if (ckWARN(WARN_EXEC)) { 10207 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s", 10208 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno)); 10209 } 10210 vms_execfree(vmscmd); 10211 } 10212 10213 return FALSE; 10214 10215 } /* end of vms_do_exec() */ 10216 /*}}}*/ 10217 10218 unsigned long int Perl_do_spawn(pTHX_ const char *); 10219 unsigned long int do_spawn2(pTHX_ const char *, int); 10220 10221 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */ 10222 unsigned long int 10223 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp) 10224 { 10225 unsigned long int sts; 10226 char * cmd; 10227 int flags = 0; 10228 10229 if (sp > mark) { 10230 10231 /* We'll copy the (undocumented?) Win32 behavior and allow a 10232 * numeric first argument. But the only value we'll support 10233 * through do_aspawn is a value of 1, which means spawn without 10234 * waiting for completion -- other values are ignored. 10235 */ 10236 if (SvNIOKp(*((SV**)mark+1)) && !SvPOKp(*((SV**)mark+1))) { 10237 ++mark; 10238 flags = SvIVx(*(SV**)mark); 10239 } 10240 10241 if (flags && flags == 1) /* the Win32 P_NOWAIT value */ 10242 flags = CLI$M_NOWAIT; 10243 else 10244 flags = 0; 10245 10246 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp); 10247 sts = do_spawn2(aTHX_ cmd, flags); 10248 /* pp_sys will clean up cmd */ 10249 return sts; 10250 } 10251 return SS$_ABORT; 10252 } /* end of do_aspawn() */ 10253 /*}}}*/ 10254 10255 10256 /* {{{unsigned long int do_spawn(char *cmd) */ 10257 unsigned long int 10258 Perl_do_spawn(pTHX_ const char *cmd) 10259 { 10260 return do_spawn2(aTHX_ cmd, 0); 10261 } 10262 /*}}}*/ 10263 10264 /* {{{unsigned long int do_spawn2(char *cmd) */ 10265 unsigned long int 10266 do_spawn2(pTHX_ const char *cmd, int flags) 10267 { 10268 unsigned long int sts, substs; 10269 10270 /* The caller of this routine expects to Safefree(PL_Cmd) */ 10271 Newx(PL_Cmd,10,char); 10272 10273 TAINT_ENV(); 10274 TAINT_PROPER("spawn"); 10275 if (!cmd || !*cmd) { 10276 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0); 10277 if (!(sts & 1)) { 10278 switch (sts) { 10279 case RMS$_FNF: case RMS$_DNF: 10280 set_errno(ENOENT); break; 10281 case RMS$_DIR: 10282 set_errno(ENOTDIR); break; 10283 case RMS$_DEV: 10284 set_errno(ENODEV); break; 10285 case RMS$_PRV: 10286 set_errno(EACCES); break; 10287 case RMS$_SYN: 10288 set_errno(EINVAL); break; 10289 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF: 10290 set_errno(E2BIG); break; 10291 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */ 10292 _ckvmssts(sts); /* fall through */ 10293 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */ 10294 set_errno(EVMSERR); 10295 } 10296 set_vaxc_errno(sts); 10297 if (ckWARN(WARN_EXEC)) { 10298 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s", 10299 Strerror(errno)); 10300 } 10301 } 10302 sts = substs; 10303 } 10304 else { 10305 char mode[3]; 10306 PerlIO * fp; 10307 if (flags & CLI$M_NOWAIT) 10308 strcpy(mode, "n"); 10309 else 10310 strcpy(mode, "nW"); 10311 10312 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts); 10313 if (fp != NULL) 10314 my_pclose(fp); 10315 /* sts will be the pid in the nowait case */ 10316 } 10317 return sts; 10318 } /* end of do_spawn2() */ 10319 /*}}}*/ 10320 10321 10322 static unsigned int *sockflags, sockflagsize; 10323 10324 /* 10325 * Shim fdopen to identify sockets for my_fwrite later, since the stdio 10326 * routines found in some versions of the CRTL can't deal with sockets. 10327 * We don't shim the other file open routines since a socket isn't 10328 * likely to be opened by a name. 10329 */ 10330 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/ 10331 FILE *my_fdopen(int fd, const char *mode) 10332 { 10333 FILE *fp = fdopen(fd, mode); 10334 10335 if (fp) { 10336 unsigned int fdoff = fd / sizeof(unsigned int); 10337 Stat_t sbuf; /* native stat; we don't need flex_stat */ 10338 if (!sockflagsize || fdoff > sockflagsize) { 10339 if (sockflags) Renew( sockflags,fdoff+2,unsigned int); 10340 else Newx (sockflags,fdoff+2,unsigned int); 10341 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize); 10342 sockflagsize = fdoff + 2; 10343 } 10344 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode)) 10345 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int)); 10346 } 10347 return fp; 10348 10349 } 10350 /*}}}*/ 10351 10352 10353 /* 10354 * Clear the corresponding bit when the (possibly) socket stream is closed. 10355 * There still a small hole: we miss an implicit close which might occur 10356 * via freopen(). >> Todo 10357 */ 10358 /*{{{ int my_fclose(FILE *fp)*/ 10359 int my_fclose(FILE *fp) { 10360 if (fp) { 10361 unsigned int fd = fileno(fp); 10362 unsigned int fdoff = fd / sizeof(unsigned int); 10363 10364 if (sockflagsize && fdoff <= sockflagsize) 10365 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int)); 10366 } 10367 return fclose(fp); 10368 } 10369 /*}}}*/ 10370 10371 10372 /* 10373 * A simple fwrite replacement which outputs itmsz*nitm chars without 10374 * introducing record boundaries every itmsz chars. 10375 * We are using fputs, which depends on a terminating null. We may 10376 * well be writing binary data, so we need to accommodate not only 10377 * data with nulls sprinkled in the middle but also data with no null 10378 * byte at the end. 10379 */ 10380 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/ 10381 int 10382 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest) 10383 { 10384 register char *cp, *end, *cpd, *data; 10385 register unsigned int fd = fileno(dest); 10386 register unsigned int fdoff = fd / sizeof(unsigned int); 10387 int retval; 10388 int bufsize = itmsz * nitm + 1; 10389 10390 if (fdoff < sockflagsize && 10391 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) { 10392 if (write(fd, src, itmsz * nitm) == EOF) return EOF; 10393 return nitm; 10394 } 10395 10396 _ckvmssts_noperl(lib$get_vm(&bufsize, &data)); 10397 memcpy( data, src, itmsz*nitm ); 10398 data[itmsz*nitm] = '\0'; 10399 10400 end = data + itmsz * nitm; 10401 retval = (int) nitm; /* on success return # items written */ 10402 10403 cpd = data; 10404 while (cpd <= end) { 10405 for (cp = cpd; cp <= end; cp++) if (!*cp) break; 10406 if (fputs(cpd,dest) == EOF) { retval = EOF; break; } 10407 if (cp < end) 10408 if (fputc('\0',dest) == EOF) { retval = EOF; break; } 10409 cpd = cp + 1; 10410 } 10411 10412 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data)); 10413 return retval; 10414 10415 } /* end of my_fwrite() */ 10416 /*}}}*/ 10417 10418 /*{{{ int my_flush(FILE *fp)*/ 10419 int 10420 Perl_my_flush(pTHX_ FILE *fp) 10421 { 10422 int res; 10423 if ((res = fflush(fp)) == 0 && fp) { 10424 #ifdef VMS_DO_SOCKETS 10425 Stat_t s; 10426 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode)) 10427 #endif 10428 res = fsync(fileno(fp)); 10429 } 10430 /* 10431 * If the flush succeeded but set end-of-file, we need to clear 10432 * the error because our caller may check ferror(). BTW, this 10433 * probably means we just flushed an empty file. 10434 */ 10435 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp); 10436 10437 return res; 10438 } 10439 /*}}}*/ 10440 10441 /* 10442 * Here are replacements for the following Unix routines in the VMS environment: 10443 * getpwuid Get information for a particular UIC or UID 10444 * getpwnam Get information for a named user 10445 * getpwent Get information for each user in the rights database 10446 * setpwent Reset search to the start of the rights database 10447 * endpwent Finish searching for users in the rights database 10448 * 10449 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure 10450 * (defined in pwd.h), which contains the following fields:- 10451 * struct passwd { 10452 * char *pw_name; Username (in lower case) 10453 * char *pw_passwd; Hashed password 10454 * unsigned int pw_uid; UIC 10455 * unsigned int pw_gid; UIC group number 10456 * char *pw_unixdir; Default device/directory (VMS-style) 10457 * char *pw_gecos; Owner name 10458 * char *pw_dir; Default device/directory (Unix-style) 10459 * char *pw_shell; Default CLI name (eg. DCL) 10460 * }; 10461 * If the specified user does not exist, getpwuid and getpwnam return NULL. 10462 * 10463 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid). 10464 * not the UIC member number (eg. what's returned by getuid()), 10465 * getpwuid() can accept either as input (if uid is specified, the caller's 10466 * UIC group is used), though it won't recognise gid=0. 10467 * 10468 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return 10469 * information about other users in your group or in other groups, respectively. 10470 * If the required privilege is not available, then these routines fill only 10471 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty 10472 * string). 10473 * 10474 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995. 10475 */ 10476 10477 /* sizes of various UAF record fields */ 10478 #define UAI$S_USERNAME 12 10479 #define UAI$S_IDENT 31 10480 #define UAI$S_OWNER 31 10481 #define UAI$S_DEFDEV 31 10482 #define UAI$S_DEFDIR 63 10483 #define UAI$S_DEFCLI 31 10484 #define UAI$S_PWD 8 10485 10486 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \ 10487 (uic).uic$v_member != UIC$K_WILD_MEMBER && \ 10488 (uic).uic$v_group != UIC$K_WILD_GROUP) 10489 10490 static char __empty[]= ""; 10491 static struct passwd __passwd_empty= 10492 {(char *) __empty, (char *) __empty, 0, 0, 10493 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty}; 10494 static int contxt= 0; 10495 static struct passwd __pwdcache; 10496 static char __pw_namecache[UAI$S_IDENT+1]; 10497 10498 /* 10499 * This routine does most of the work extracting the user information. 10500 */ 10501 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd) 10502 { 10503 static struct { 10504 unsigned char length; 10505 char pw_gecos[UAI$S_OWNER+1]; 10506 } owner; 10507 static union uicdef uic; 10508 static struct { 10509 unsigned char length; 10510 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1]; 10511 } defdev; 10512 static struct { 10513 unsigned char length; 10514 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1]; 10515 } defdir; 10516 static struct { 10517 unsigned char length; 10518 char pw_shell[UAI$S_DEFCLI+1]; 10519 } defcli; 10520 static char pw_passwd[UAI$S_PWD+1]; 10521 10522 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd; 10523 struct dsc$descriptor_s name_desc; 10524 unsigned long int sts; 10525 10526 static struct itmlst_3 itmlst[]= { 10527 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner}, 10528 {sizeof(uic), UAI$_UIC, &uic, &luic}, 10529 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev}, 10530 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir}, 10531 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli}, 10532 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd}, 10533 {0, 0, NULL, NULL}}; 10534 10535 name_desc.dsc$w_length= strlen(name); 10536 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 10537 name_desc.dsc$b_class= DSC$K_CLASS_S; 10538 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */ 10539 10540 /* Note that sys$getuai returns many fields as counted strings. */ 10541 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0); 10542 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) { 10543 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES); 10544 } 10545 else { _ckvmssts(sts); } 10546 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */ 10547 10548 if ((int) owner.length < lowner) lowner= (int) owner.length; 10549 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length; 10550 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length; 10551 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length; 10552 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir); 10553 owner.pw_gecos[lowner]= '\0'; 10554 defdev.pw_dir[ldefdev+ldefdir]= '\0'; 10555 defcli.pw_shell[ldefcli]= '\0'; 10556 if (valid_uic(uic)) { 10557 pwd->pw_uid= uic.uic$l_uic; 10558 pwd->pw_gid= uic.uic$v_group; 10559 } 10560 else 10561 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\""); 10562 pwd->pw_passwd= pw_passwd; 10563 pwd->pw_gecos= owner.pw_gecos; 10564 pwd->pw_dir= defdev.pw_dir; 10565 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL); 10566 pwd->pw_shell= defcli.pw_shell; 10567 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) { 10568 int ldir; 10569 ldir= strlen(pwd->pw_unixdir) - 1; 10570 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0'; 10571 } 10572 else 10573 strcpy(pwd->pw_unixdir, pwd->pw_dir); 10574 if (!decc_efs_case_preserve) 10575 __mystrtolower(pwd->pw_unixdir); 10576 return 1; 10577 } 10578 10579 /* 10580 * Get information for a named user. 10581 */ 10582 /*{{{struct passwd *getpwnam(char *name)*/ 10583 struct passwd *Perl_my_getpwnam(pTHX_ const char *name) 10584 { 10585 struct dsc$descriptor_s name_desc; 10586 union uicdef uic; 10587 unsigned long int status, sts; 10588 10589 __pwdcache = __passwd_empty; 10590 if (!fillpasswd(aTHX_ name, &__pwdcache)) { 10591 /* We still may be able to determine pw_uid and pw_gid */ 10592 name_desc.dsc$w_length= strlen(name); 10593 name_desc.dsc$b_dtype= DSC$K_DTYPE_T; 10594 name_desc.dsc$b_class= DSC$K_CLASS_S; 10595 name_desc.dsc$a_pointer= (char *) name; 10596 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) { 10597 __pwdcache.pw_uid= uic.uic$l_uic; 10598 __pwdcache.pw_gid= uic.uic$v_group; 10599 } 10600 else { 10601 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) { 10602 set_vaxc_errno(sts); 10603 set_errno(sts == RMS$_PRV ? EACCES : EINVAL); 10604 return NULL; 10605 } 10606 else { _ckvmssts(sts); } 10607 } 10608 } 10609 strncpy(__pw_namecache, name, sizeof(__pw_namecache)); 10610 __pw_namecache[sizeof __pw_namecache - 1] = '\0'; 10611 __pwdcache.pw_name= __pw_namecache; 10612 return &__pwdcache; 10613 } /* end of my_getpwnam() */ 10614 /*}}}*/ 10615 10616 /* 10617 * Get information for a particular UIC or UID. 10618 * Called by my_getpwent with uid=-1 to list all users. 10619 */ 10620 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/ 10621 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid) 10622 { 10623 const $DESCRIPTOR(name_desc,__pw_namecache); 10624 unsigned short lname; 10625 union uicdef uic; 10626 unsigned long int status; 10627 10628 if (uid == (unsigned int) -1) { 10629 do { 10630 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt); 10631 if (status == SS$_NOSUCHID || status == RMS$_PRV) { 10632 set_vaxc_errno(status); 10633 set_errno(status == RMS$_PRV ? EACCES : EINVAL); 10634 my_endpwent(); 10635 return NULL; 10636 } 10637 else { _ckvmssts(status); } 10638 } while (!valid_uic (uic)); 10639 } 10640 else { 10641 uic.uic$l_uic= uid; 10642 if (!uic.uic$v_group) 10643 uic.uic$v_group= PerlProc_getgid(); 10644 if (valid_uic(uic)) 10645 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0); 10646 else status = SS$_IVIDENT; 10647 if (status == SS$_IVIDENT || status == SS$_NOSUCHID || 10648 status == RMS$_PRV) { 10649 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL); 10650 return NULL; 10651 } 10652 else { _ckvmssts(status); } 10653 } 10654 __pw_namecache[lname]= '\0'; 10655 __mystrtolower(__pw_namecache); 10656 10657 __pwdcache = __passwd_empty; 10658 __pwdcache.pw_name = __pw_namecache; 10659 10660 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege). 10661 The identifier's value is usually the UIC, but it doesn't have to be, 10662 so if we can, we let fillpasswd update this. */ 10663 __pwdcache.pw_uid = uic.uic$l_uic; 10664 __pwdcache.pw_gid = uic.uic$v_group; 10665 10666 fillpasswd(aTHX_ __pw_namecache, &__pwdcache); 10667 return &__pwdcache; 10668 10669 } /* end of my_getpwuid() */ 10670 /*}}}*/ 10671 10672 /* 10673 * Get information for next user. 10674 */ 10675 /*{{{struct passwd *my_getpwent()*/ 10676 struct passwd *Perl_my_getpwent(pTHX) 10677 { 10678 return (my_getpwuid((unsigned int) -1)); 10679 } 10680 /*}}}*/ 10681 10682 /* 10683 * Finish searching rights database for users. 10684 */ 10685 /*{{{void my_endpwent()*/ 10686 void Perl_my_endpwent(pTHX) 10687 { 10688 if (contxt) { 10689 _ckvmssts(sys$finish_rdb(&contxt)); 10690 contxt= 0; 10691 } 10692 } 10693 /*}}}*/ 10694 10695 #ifdef HOMEGROWN_POSIX_SIGNALS 10696 /* Signal handling routines, pulled into the core from POSIX.xs. 10697 * 10698 * We need these for threads, so they've been rolled into the core, 10699 * rather than left in POSIX.xs. 10700 * 10701 * (DRS, Oct 23, 1997) 10702 */ 10703 10704 /* sigset_t is atomic under VMS, so these routines are easy */ 10705 /*{{{int my_sigemptyset(sigset_t *) */ 10706 int my_sigemptyset(sigset_t *set) { 10707 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; } 10708 *set = 0; return 0; 10709 } 10710 /*}}}*/ 10711 10712 10713 /*{{{int my_sigfillset(sigset_t *)*/ 10714 int my_sigfillset(sigset_t *set) { 10715 int i; 10716 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; } 10717 for (i = 0; i < NSIG; i++) *set |= (1 << i); 10718 return 0; 10719 } 10720 /*}}}*/ 10721 10722 10723 /*{{{int my_sigaddset(sigset_t *set, int sig)*/ 10724 int my_sigaddset(sigset_t *set, int sig) { 10725 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; } 10726 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; } 10727 *set |= (1 << (sig - 1)); 10728 return 0; 10729 } 10730 /*}}}*/ 10731 10732 10733 /*{{{int my_sigdelset(sigset_t *set, int sig)*/ 10734 int my_sigdelset(sigset_t *set, int sig) { 10735 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; } 10736 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; } 10737 *set &= ~(1 << (sig - 1)); 10738 return 0; 10739 } 10740 /*}}}*/ 10741 10742 10743 /*{{{int my_sigismember(sigset_t *set, int sig)*/ 10744 int my_sigismember(sigset_t *set, int sig) { 10745 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; } 10746 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; } 10747 return *set & (1 << (sig - 1)); 10748 } 10749 /*}}}*/ 10750 10751 10752 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/ 10753 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) { 10754 sigset_t tempmask; 10755 10756 /* If set and oset are both null, then things are badly wrong. Bail out. */ 10757 if ((oset == NULL) && (set == NULL)) { 10758 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO); 10759 return -1; 10760 } 10761 10762 /* If set's null, then we're just handling a fetch. */ 10763 if (set == NULL) { 10764 tempmask = sigblock(0); 10765 } 10766 else { 10767 switch (how) { 10768 case SIG_SETMASK: 10769 tempmask = sigsetmask(*set); 10770 break; 10771 case SIG_BLOCK: 10772 tempmask = sigblock(*set); 10773 break; 10774 case SIG_UNBLOCK: 10775 tempmask = sigblock(0); 10776 sigsetmask(*oset & ~tempmask); 10777 break; 10778 default: 10779 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 10780 return -1; 10781 } 10782 } 10783 10784 /* Did they pass us an oset? If so, stick our holding mask into it */ 10785 if (oset) 10786 *oset = tempmask; 10787 10788 return 0; 10789 } 10790 /*}}}*/ 10791 #endif /* HOMEGROWN_POSIX_SIGNALS */ 10792 10793 10794 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(), 10795 * my_utime(), and flex_stat(), all of which operate on UTC unless 10796 * VMSISH_TIMES is true. 10797 */ 10798 /* method used to handle UTC conversions: 10799 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction 10800 */ 10801 static int gmtime_emulation_type; 10802 /* number of secs to add to UTC POSIX-style time to get local time */ 10803 static long int utc_offset_secs; 10804 10805 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc. 10806 * in vmsish.h. #undef them here so we can call the CRTL routines 10807 * directly. 10808 */ 10809 #undef gmtime 10810 #undef localtime 10811 #undef time 10812 10813 10814 /* 10815 * DEC C previous to 6.0 corrupts the behavior of the /prefix 10816 * qualifier with the extern prefix pragma. This provisional 10817 * hack circumvents this prefix pragma problem in previous 10818 * precompilers. 10819 */ 10820 #if defined(__VMS_VER) && __VMS_VER >= 70000000 10821 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000) 10822 # pragma __extern_prefix save 10823 # pragma __extern_prefix "" /* set to empty to prevent prefixing */ 10824 # define gmtime decc$__utctz_gmtime 10825 # define localtime decc$__utctz_localtime 10826 # define time decc$__utc_time 10827 # pragma __extern_prefix restore 10828 10829 struct tm *gmtime(), *localtime(); 10830 10831 # endif 10832 #endif 10833 10834 10835 static time_t toutc_dst(time_t loc) { 10836 struct tm *rsltmp; 10837 10838 if ((rsltmp = localtime(&loc)) == NULL) return -1; 10839 loc -= utc_offset_secs; 10840 if (rsltmp->tm_isdst) loc -= 3600; 10841 return loc; 10842 } 10843 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 10844 ((gmtime_emulation_type || my_time(NULL)), \ 10845 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \ 10846 ((secs) - utc_offset_secs)))) 10847 10848 static time_t toloc_dst(time_t utc) { 10849 struct tm *rsltmp; 10850 10851 utc += utc_offset_secs; 10852 if ((rsltmp = localtime(&utc)) == NULL) return -1; 10853 if (rsltmp->tm_isdst) utc += 3600; 10854 return utc; 10855 } 10856 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \ 10857 ((gmtime_emulation_type || my_time(NULL)), \ 10858 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \ 10859 ((secs) + utc_offset_secs)))) 10860 10861 #ifndef RTL_USES_UTC 10862 /* 10863 10864 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical 10865 DST starts on 1st sun of april at 02:00 std time 10866 ends on last sun of october at 02:00 dst time 10867 see the UCX management command reference, SET CONFIG TIMEZONE 10868 for formatting info. 10869 10870 No, it's not as general as it should be, but then again, NOTHING 10871 will handle UK times in a sensible way. 10872 */ 10873 10874 10875 /* 10876 parse the DST start/end info: 10877 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss] 10878 */ 10879 10880 static char * 10881 tz_parse_startend(char *s, struct tm *w, int *past) 10882 { 10883 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 10884 int ly, dozjd, d, m, n, hour, min, sec, j, k; 10885 time_t g; 10886 10887 if (!s) return 0; 10888 if (!w) return 0; 10889 if (!past) return 0; 10890 10891 ly = 0; 10892 if (w->tm_year % 4 == 0) ly = 1; 10893 if (w->tm_year % 100 == 0) ly = 0; 10894 if (w->tm_year+1900 % 400 == 0) ly = 1; 10895 if (ly) dinm[1]++; 10896 10897 dozjd = isdigit(*s); 10898 if (*s == 'J' || *s == 'j' || dozjd) { 10899 if (!dozjd && !isdigit(*++s)) return 0; 10900 d = *s++ - '0'; 10901 if (isdigit(*s)) { 10902 d = d*10 + *s++ - '0'; 10903 if (isdigit(*s)) { 10904 d = d*10 + *s++ - '0'; 10905 } 10906 } 10907 if (d == 0) return 0; 10908 if (d > 366) return 0; 10909 d--; 10910 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */ 10911 g = d * 86400; 10912 dozjd = 1; 10913 } else if (*s == 'M' || *s == 'm') { 10914 if (!isdigit(*++s)) return 0; 10915 m = *s++ - '0'; 10916 if (isdigit(*s)) m = 10*m + *s++ - '0'; 10917 if (*s != '.') return 0; 10918 if (!isdigit(*++s)) return 0; 10919 n = *s++ - '0'; 10920 if (n < 1 || n > 5) return 0; 10921 if (*s != '.') return 0; 10922 if (!isdigit(*++s)) return 0; 10923 d = *s++ - '0'; 10924 if (d > 6) return 0; 10925 } 10926 10927 if (*s == '/') { 10928 if (!isdigit(*++s)) return 0; 10929 hour = *s++ - '0'; 10930 if (isdigit(*s)) hour = 10*hour + *s++ - '0'; 10931 if (*s == ':') { 10932 if (!isdigit(*++s)) return 0; 10933 min = *s++ - '0'; 10934 if (isdigit(*s)) min = 10*min + *s++ - '0'; 10935 if (*s == ':') { 10936 if (!isdigit(*++s)) return 0; 10937 sec = *s++ - '0'; 10938 if (isdigit(*s)) sec = 10*sec + *s++ - '0'; 10939 } 10940 } 10941 } else { 10942 hour = 2; 10943 min = 0; 10944 sec = 0; 10945 } 10946 10947 if (dozjd) { 10948 if (w->tm_yday < d) goto before; 10949 if (w->tm_yday > d) goto after; 10950 } else { 10951 if (w->tm_mon+1 < m) goto before; 10952 if (w->tm_mon+1 > m) goto after; 10953 10954 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */ 10955 k = d - j; /* mday of first d */ 10956 if (k <= 0) k += 7; 10957 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */ 10958 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7; 10959 if (w->tm_mday < k) goto before; 10960 if (w->tm_mday > k) goto after; 10961 } 10962 10963 if (w->tm_hour < hour) goto before; 10964 if (w->tm_hour > hour) goto after; 10965 if (w->tm_min < min) goto before; 10966 if (w->tm_min > min) goto after; 10967 if (w->tm_sec < sec) goto before; 10968 goto after; 10969 10970 before: 10971 *past = 0; 10972 return s; 10973 after: 10974 *past = 1; 10975 return s; 10976 } 10977 10978 10979 10980 10981 /* parse the offset: (+|-)hh[:mm[:ss]] */ 10982 10983 static char * 10984 tz_parse_offset(char *s, int *offset) 10985 { 10986 int hour = 0, min = 0, sec = 0; 10987 int neg = 0; 10988 if (!s) return 0; 10989 if (!offset) return 0; 10990 10991 if (*s == '-') {neg++; s++;} 10992 if (*s == '+') s++; 10993 if (!isdigit(*s)) return 0; 10994 hour = *s++ - '0'; 10995 if (isdigit(*s)) hour = hour*10+(*s++ - '0'); 10996 if (hour > 24) return 0; 10997 if (*s == ':') { 10998 if (!isdigit(*++s)) return 0; 10999 min = *s++ - '0'; 11000 if (isdigit(*s)) min = min*10 + (*s++ - '0'); 11001 if (min > 59) return 0; 11002 if (*s == ':') { 11003 if (!isdigit(*++s)) return 0; 11004 sec = *s++ - '0'; 11005 if (isdigit(*s)) sec = sec*10 + (*s++ - '0'); 11006 if (sec > 59) return 0; 11007 } 11008 } 11009 11010 *offset = (hour*60+min)*60 + sec; 11011 if (neg) *offset = -*offset; 11012 return s; 11013 } 11014 11015 /* 11016 input time is w, whatever type of time the CRTL localtime() uses. 11017 sets dst, the zone, and the gmtoff (seconds) 11018 11019 caches the value of TZ and UCX$TZ env variables; note that 11020 my_setenv looks for these and sets a flag if they're changed 11021 for efficiency. 11022 11023 We have to watch out for the "australian" case (dst starts in 11024 october, ends in april)...flagged by "reverse" and checked by 11025 scanning through the months of the previous year. 11026 11027 */ 11028 11029 static int 11030 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff) 11031 { 11032 time_t when; 11033 struct tm *w2; 11034 char *s,*s2; 11035 char *dstzone, *tz, *s_start, *s_end; 11036 int std_off, dst_off, isdst; 11037 int y, dststart, dstend; 11038 static char envtz[1025]; /* longer than any logical, symbol, ... */ 11039 static char ucxtz[1025]; 11040 static char reversed = 0; 11041 11042 if (!w) return 0; 11043 11044 if (tz_updated) { 11045 tz_updated = 0; 11046 reversed = -1; /* flag need to check */ 11047 envtz[0] = ucxtz[0] = '\0'; 11048 tz = my_getenv("TZ",0); 11049 if (tz) strcpy(envtz, tz); 11050 tz = my_getenv("UCX$TZ",0); 11051 if (tz) strcpy(ucxtz, tz); 11052 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */ 11053 } 11054 tz = envtz; 11055 if (!*tz) tz = ucxtz; 11056 11057 s = tz; 11058 while (isalpha(*s)) s++; 11059 s = tz_parse_offset(s, &std_off); 11060 if (!s) return 0; 11061 if (!*s) { /* no DST, hurray we're done! */ 11062 isdst = 0; 11063 goto done; 11064 } 11065 11066 dstzone = s; 11067 while (isalpha(*s)) s++; 11068 s2 = tz_parse_offset(s, &dst_off); 11069 if (s2) { 11070 s = s2; 11071 } else { 11072 dst_off = std_off - 3600; 11073 } 11074 11075 if (!*s) { /* default dst start/end?? */ 11076 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */ 11077 s = strchr(ucxtz,','); 11078 } 11079 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */ 11080 } 11081 if (*s != ',') return 0; 11082 11083 when = *w; 11084 when = _toutc(when); /* convert to utc */ 11085 when = when - std_off; /* convert to pseudolocal time*/ 11086 11087 w2 = localtime(&when); 11088 y = w2->tm_year; 11089 s_start = s+1; 11090 s = tz_parse_startend(s_start,w2,&dststart); 11091 if (!s) return 0; 11092 if (*s != ',') return 0; 11093 11094 when = *w; 11095 when = _toutc(when); /* convert to utc */ 11096 when = when - dst_off; /* convert to pseudolocal time*/ 11097 w2 = localtime(&when); 11098 if (w2->tm_year != y) { /* spans a year, just check one time */ 11099 when += dst_off - std_off; 11100 w2 = localtime(&when); 11101 } 11102 s_end = s+1; 11103 s = tz_parse_startend(s_end,w2,&dstend); 11104 if (!s) return 0; 11105 11106 if (reversed == -1) { /* need to check if start later than end */ 11107 int j, ds, de; 11108 11109 when = *w; 11110 if (when < 2*365*86400) { 11111 when += 2*365*86400; 11112 } else { 11113 when -= 365*86400; 11114 } 11115 w2 =localtime(&when); 11116 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */ 11117 11118 for (j = 0; j < 12; j++) { 11119 w2 =localtime(&when); 11120 tz_parse_startend(s_start,w2,&ds); 11121 tz_parse_startend(s_end,w2,&de); 11122 if (ds != de) break; 11123 when += 30*86400; 11124 } 11125 reversed = 0; 11126 if (de && !ds) reversed = 1; 11127 } 11128 11129 isdst = dststart && !dstend; 11130 if (reversed) isdst = dststart || !dstend; 11131 11132 done: 11133 if (dst) *dst = isdst; 11134 if (gmtoff) *gmtoff = isdst ? dst_off : std_off; 11135 if (isdst) tz = dstzone; 11136 if (zone) { 11137 while(isalpha(*tz)) *zone++ = *tz++; 11138 *zone = '\0'; 11139 } 11140 return 1; 11141 } 11142 11143 #endif /* !RTL_USES_UTC */ 11144 11145 /* my_time(), my_localtime(), my_gmtime() 11146 * By default traffic in UTC time values, using CRTL gmtime() or 11147 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone. 11148 * Note: We need to use these functions even when the CRTL has working 11149 * UTC support, since they also handle C<use vmsish qw(times);> 11150 * 11151 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu> 11152 * Modified by Charles Bailey <bailey@newman.upenn.edu> 11153 */ 11154 11155 /*{{{time_t my_time(time_t *timep)*/ 11156 time_t Perl_my_time(pTHX_ time_t *timep) 11157 { 11158 time_t when; 11159 struct tm *tm_p; 11160 11161 if (gmtime_emulation_type == 0) { 11162 int dstnow; 11163 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */ 11164 /* results of calls to gmtime() and localtime() */ 11165 /* for same &base */ 11166 11167 gmtime_emulation_type++; 11168 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */ 11169 char off[LNM$C_NAMLENGTH+1];; 11170 11171 gmtime_emulation_type++; 11172 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) { 11173 gmtime_emulation_type++; 11174 utc_offset_secs = 0; 11175 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC"); 11176 } 11177 else { utc_offset_secs = atol(off); } 11178 } 11179 else { /* We've got a working gmtime() */ 11180 struct tm gmt, local; 11181 11182 gmt = *tm_p; 11183 tm_p = localtime(&base); 11184 local = *tm_p; 11185 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400; 11186 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600; 11187 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60; 11188 utc_offset_secs += (local.tm_sec - gmt.tm_sec); 11189 } 11190 } 11191 11192 when = time(NULL); 11193 # ifdef VMSISH_TIME 11194 # ifdef RTL_USES_UTC 11195 if (VMSISH_TIME) when = _toloc(when); 11196 # else 11197 if (!VMSISH_TIME) when = _toutc(when); 11198 # endif 11199 # endif 11200 if (timep != NULL) *timep = when; 11201 return when; 11202 11203 } /* end of my_time() */ 11204 /*}}}*/ 11205 11206 11207 /*{{{struct tm *my_gmtime(const time_t *timep)*/ 11208 struct tm * 11209 Perl_my_gmtime(pTHX_ const time_t *timep) 11210 { 11211 char *p; 11212 time_t when; 11213 struct tm *rsltmp; 11214 11215 if (timep == NULL) { 11216 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11217 return NULL; 11218 } 11219 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11220 11221 when = *timep; 11222 # ifdef VMSISH_TIME 11223 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */ 11224 # endif 11225 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */ 11226 return gmtime(&when); 11227 # else 11228 /* CRTL localtime() wants local time as input, so does no tz correction */ 11229 rsltmp = localtime(&when); 11230 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */ 11231 return rsltmp; 11232 #endif 11233 } /* end of my_gmtime() */ 11234 /*}}}*/ 11235 11236 11237 /*{{{struct tm *my_localtime(const time_t *timep)*/ 11238 struct tm * 11239 Perl_my_localtime(pTHX_ const time_t *timep) 11240 { 11241 time_t when, whenutc; 11242 struct tm *rsltmp; 11243 int dst, offset; 11244 11245 if (timep == NULL) { 11246 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 11247 return NULL; 11248 } 11249 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */ 11250 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */ 11251 11252 when = *timep; 11253 # ifdef RTL_USES_UTC 11254 # ifdef VMSISH_TIME 11255 if (VMSISH_TIME) when = _toutc(when); 11256 # endif 11257 /* CRTL localtime() wants UTC as input, does tz correction itself */ 11258 return localtime(&when); 11259 11260 # else /* !RTL_USES_UTC */ 11261 whenutc = when; 11262 # ifdef VMSISH_TIME 11263 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */ 11264 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */ 11265 # endif 11266 dst = -1; 11267 #ifndef RTL_USES_UTC 11268 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/ 11269 when = whenutc - offset; /* pseudolocal time*/ 11270 } 11271 # endif 11272 /* CRTL localtime() wants local time as input, so does no tz correction */ 11273 rsltmp = localtime(&when); 11274 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst; 11275 return rsltmp; 11276 # endif 11277 11278 } /* end of my_localtime() */ 11279 /*}}}*/ 11280 11281 /* Reset definitions for later calls */ 11282 #define gmtime(t) my_gmtime(t) 11283 #define localtime(t) my_localtime(t) 11284 #define time(t) my_time(t) 11285 11286 11287 /* my_utime - update modification/access time of a file 11288 * 11289 * VMS 7.3 and later implementation 11290 * Only the UTC translation is home-grown. The rest is handled by the 11291 * CRTL utime(), which will take into account the relevant feature 11292 * logicals and ODS-5 volume characteristics for true access times. 11293 * 11294 * pre VMS 7.3 implementation: 11295 * The calling sequence is identical to POSIX utime(), but under 11296 * VMS with ODS-2, only the modification time is changed; ODS-2 does 11297 * not maintain access times. Restrictions differ from the POSIX 11298 * definition in that the time can be changed as long as the 11299 * caller has permission to execute the necessary IO$_MODIFY $QIO; 11300 * no separate checks are made to insure that the caller is the 11301 * owner of the file or has special privs enabled. 11302 * Code here is based on Joe Meadows' FILE utility. 11303 * 11304 */ 11305 11306 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00) 11307 * to VMS epoch (01-JAN-1858 00:00:00.00) 11308 * in 100 ns intervals. 11309 */ 11310 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 }; 11311 11312 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/ 11313 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes) 11314 { 11315 #if __CRTL_VER >= 70300000 11316 struct utimbuf utc_utimes, *utc_utimesp; 11317 11318 if (utimes != NULL) { 11319 utc_utimes.actime = utimes->actime; 11320 utc_utimes.modtime = utimes->modtime; 11321 # ifdef VMSISH_TIME 11322 /* If input was local; convert to UTC for sys svc */ 11323 if (VMSISH_TIME) { 11324 utc_utimes.actime = _toutc(utimes->actime); 11325 utc_utimes.modtime = _toutc(utimes->modtime); 11326 } 11327 # endif 11328 utc_utimesp = &utc_utimes; 11329 } 11330 else { 11331 utc_utimesp = NULL; 11332 } 11333 11334 return utime(file, utc_utimesp); 11335 11336 #else /* __CRTL_VER < 70300000 */ 11337 11338 register int i; 11339 int sts; 11340 long int bintime[2], len = 2, lowbit, unixtime, 11341 secscale = 10000000; /* seconds --> 100 ns intervals */ 11342 unsigned long int chan, iosb[2], retsts; 11343 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS]; 11344 struct FAB myfab = cc$rms_fab; 11345 struct NAM mynam = cc$rms_nam; 11346 #if defined (__DECC) && defined (__VAX) 11347 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr, 11348 * at least through VMS V6.1, which causes a type-conversion warning. 11349 */ 11350 # pragma message save 11351 # pragma message disable cvtdiftypes 11352 #endif 11353 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}}; 11354 struct fibdef myfib; 11355 #if defined (__DECC) && defined (__VAX) 11356 /* This should be right after the declaration of myatr, but due 11357 * to a bug in VAX DEC C, this takes effect a statement early. 11358 */ 11359 # pragma message restore 11360 #endif 11361 /* cast ok for read only parameter */ 11362 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib}, 11363 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0}, 11364 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0}; 11365 11366 if (file == NULL || *file == '\0') { 11367 SETERRNO(ENOENT, LIB$_INVARG); 11368 return -1; 11369 } 11370 11371 /* Convert to VMS format ensuring that it will fit in 255 characters */ 11372 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) { 11373 SETERRNO(ENOENT, LIB$_INVARG); 11374 return -1; 11375 } 11376 if (utimes != NULL) { 11377 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00) 11378 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00). 11379 * Since time_t is unsigned long int, and lib$emul takes a signed long int 11380 * as input, we force the sign bit to be clear by shifting unixtime right 11381 * one bit, then multiplying by an extra factor of 2 in lib$emul(). 11382 */ 11383 lowbit = (utimes->modtime & 1) ? secscale : 0; 11384 unixtime = (long int) utimes->modtime; 11385 # ifdef VMSISH_TIME 11386 /* If input was UTC; convert to local for sys svc */ 11387 if (!VMSISH_TIME) unixtime = _toloc(unixtime); 11388 # endif 11389 unixtime >>= 1; secscale <<= 1; 11390 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime); 11391 if (!(retsts & 1)) { 11392 SETERRNO(EVMSERR, retsts); 11393 return -1; 11394 } 11395 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len); 11396 if (!(retsts & 1)) { 11397 SETERRNO(EVMSERR, retsts); 11398 return -1; 11399 } 11400 } 11401 else { 11402 /* Just get the current time in VMS format directly */ 11403 retsts = sys$gettim(bintime); 11404 if (!(retsts & 1)) { 11405 SETERRNO(EVMSERR, retsts); 11406 return -1; 11407 } 11408 } 11409 11410 myfab.fab$l_fna = vmsspec; 11411 myfab.fab$b_fns = (unsigned char) strlen(vmsspec); 11412 myfab.fab$l_nam = &mynam; 11413 mynam.nam$l_esa = esa; 11414 mynam.nam$b_ess = (unsigned char) sizeof esa; 11415 mynam.nam$l_rsa = rsa; 11416 mynam.nam$b_rss = (unsigned char) sizeof rsa; 11417 if (decc_efs_case_preserve) 11418 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE; 11419 11420 /* Look for the file to be affected, letting RMS parse the file 11421 * specification for us as well. I have set errno using only 11422 * values documented in the utime() man page for VMS POSIX. 11423 */ 11424 retsts = sys$parse(&myfab,0,0); 11425 if (!(retsts & 1)) { 11426 set_vaxc_errno(retsts); 11427 if (retsts == RMS$_PRV) set_errno(EACCES); 11428 else if (retsts == RMS$_DIR) set_errno(ENOTDIR); 11429 else set_errno(EVMSERR); 11430 return -1; 11431 } 11432 retsts = sys$search(&myfab,0,0); 11433 if (!(retsts & 1)) { 11434 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11435 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11436 set_vaxc_errno(retsts); 11437 if (retsts == RMS$_PRV) set_errno(EACCES); 11438 else if (retsts == RMS$_FNF) set_errno(ENOENT); 11439 else set_errno(EVMSERR); 11440 return -1; 11441 } 11442 11443 devdsc.dsc$w_length = mynam.nam$b_dev; 11444 /* cast ok for read only parameter */ 11445 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev; 11446 11447 retsts = sys$assign(&devdsc,&chan,0,0); 11448 if (!(retsts & 1)) { 11449 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11450 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11451 set_vaxc_errno(retsts); 11452 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR); 11453 else if (retsts == SS$_NOPRIV) set_errno(EACCES); 11454 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR); 11455 else set_errno(EVMSERR); 11456 return -1; 11457 } 11458 11459 fnmdsc.dsc$a_pointer = mynam.nam$l_name; 11460 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver; 11461 11462 memset((void *) &myfib, 0, sizeof myfib); 11463 #if defined(__DECC) || defined(__DECCXX) 11464 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i]; 11465 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i]; 11466 /* This prevents the revision time of the file being reset to the current 11467 * time as a result of our IO$_MODIFY $QIO. */ 11468 myfib.fib$l_acctl = FIB$M_NORECORD; 11469 #else 11470 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i]; 11471 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i]; 11472 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD; 11473 #endif 11474 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0); 11475 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL; 11476 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); 11477 _ckvmssts(sys$dassgn(chan)); 11478 if (retsts & 1) retsts = iosb[0]; 11479 if (!(retsts & 1)) { 11480 set_vaxc_errno(retsts); 11481 if (retsts == SS$_NOPRIV) set_errno(EACCES); 11482 else set_errno(EVMSERR); 11483 return -1; 11484 } 11485 11486 return 0; 11487 11488 #endif /* #if __CRTL_VER >= 70300000 */ 11489 11490 } /* end of my_utime() */ 11491 /*}}}*/ 11492 11493 /* 11494 * flex_stat, flex_lstat, flex_fstat 11495 * basic stat, but gets it right when asked to stat 11496 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3) 11497 */ 11498 11499 #ifndef _USE_STD_STAT 11500 /* encode_dev packs a VMS device name string into an integer to allow 11501 * simple comparisons. This can be used, for example, to check whether two 11502 * files are located on the same device, by comparing their encoded device 11503 * names. Even a string comparison would not do, because stat() reuses the 11504 * device name buffer for each call; so without encode_dev, it would be 11505 * necessary to save the buffer and use strcmp (this would mean a number of 11506 * changes to the standard Perl code, to say nothing of what a Perl script 11507 * would have to do. 11508 * 11509 * The device lock id, if it exists, should be unique (unless perhaps compared 11510 * with lock ids transferred from other nodes). We have a lock id if the disk is 11511 * mounted cluster-wide, which is when we tend to get long (host-qualified) 11512 * device names. Thus we use the lock id in preference, and only if that isn't 11513 * available, do we try to pack the device name into an integer (flagged by 11514 * the sign bit (LOCKID_MASK) being set). 11515 * 11516 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device 11517 * name and its encoded form, but it seems very unlikely that we will find 11518 * two files on different disks that share the same encoded device names, 11519 * and even more remote that they will share the same file id (if the test 11520 * is to check for the same file). 11521 * 11522 * A better method might be to use sys$device_scan on the first call, and to 11523 * search for the device, returning an index into the cached array. 11524 * The number returned would be more intelligible. 11525 * This is probably not worth it, and anyway would take quite a bit longer 11526 * on the first call. 11527 */ 11528 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */ 11529 static mydev_t encode_dev (pTHX_ const char *dev) 11530 { 11531 int i; 11532 unsigned long int f; 11533 mydev_t enc; 11534 char c; 11535 const char *q; 11536 11537 if (!dev || !dev[0]) return 0; 11538 11539 #if LOCKID_MASK 11540 { 11541 struct dsc$descriptor_s dev_desc; 11542 unsigned long int status, lockid = 0, item = DVI$_LOCKID; 11543 11544 /* For cluster-mounted disks, the disk lock identifier is unique, so we 11545 can try that first. */ 11546 dev_desc.dsc$w_length = strlen (dev); 11547 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T; 11548 dev_desc.dsc$b_class = DSC$K_CLASS_S; 11549 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */ 11550 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0); 11551 if (!$VMS_STATUS_SUCCESS(status)) { 11552 switch (status) { 11553 case SS$_NOSUCHDEV: 11554 SETERRNO(ENODEV, status); 11555 return 0; 11556 default: 11557 _ckvmssts(status); 11558 } 11559 } 11560 if (lockid) return (lockid & ~LOCKID_MASK); 11561 } 11562 #endif 11563 11564 /* Otherwise we try to encode the device name */ 11565 enc = 0; 11566 f = 1; 11567 i = 0; 11568 for (q = dev + strlen(dev); q--; q >= dev) { 11569 if (*q == ':') 11570 break; 11571 if (isdigit (*q)) 11572 c= (*q) - '0'; 11573 else if (isalpha (toupper (*q))) 11574 c= toupper (*q) - 'A' + (char)10; 11575 else 11576 continue; /* Skip '$'s */ 11577 i++; 11578 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */ 11579 if (i>1) f *= 36; 11580 enc += f * (unsigned long int) c; 11581 } 11582 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */ 11583 11584 } /* end of encode_dev() */ 11585 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11586 device_no = encode_dev(aTHX_ devname) 11587 #else 11588 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \ 11589 device_no = new_dev_no 11590 #endif 11591 11592 static int 11593 is_null_device(name) 11594 const char *name; 11595 { 11596 if (decc_bug_devnull != 0) { 11597 if (strncmp("/dev/null", name, 9) == 0) 11598 return 1; 11599 } 11600 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:". 11601 The underscore prefix, controller letter, and unit number are 11602 independently optional; for our purposes, the colon punctuation 11603 is not. The colon can be trailed by optional directory and/or 11604 filename, but two consecutive colons indicates a nodename rather 11605 than a device. [pr] */ 11606 if (*name == '_') ++name; 11607 if (tolower(*name++) != 'n') return 0; 11608 if (tolower(*name++) != 'l') return 0; 11609 if (tolower(*name) == 'a') ++name; 11610 if (*name == '0') ++name; 11611 return (*name++ == ':') && (*name != ':'); 11612 } 11613 11614 11615 static I32 11616 Perl_cando_by_name_int 11617 (pTHX_ I32 bit, bool effective, const char *fname, int opts) 11618 { 11619 char usrname[L_cuserid]; 11620 struct dsc$descriptor_s usrdsc = 11621 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname}; 11622 char *vmsname = NULL, *fileified = NULL; 11623 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags; 11624 unsigned short int retlen, trnlnm_iter_count; 11625 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11626 union prvdef curprv; 11627 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen}, 11628 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen}, 11629 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}}; 11630 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen}, 11631 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length}, 11632 {0,0,0,0}}; 11633 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen}, 11634 {0,0,0,0}}; 11635 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 11636 Stat_t st; 11637 static int profile_context = -1; 11638 11639 if (!fname || !*fname) return FALSE; 11640 11641 /* Make sure we expand logical names, since sys$check_access doesn't */ 11642 fileified = PerlMem_malloc(VMS_MAXRSS); 11643 if (fileified == NULL) _ckvmssts(SS$_INSFMEM); 11644 if (!strpbrk(fname,"/]>:")) { 11645 strcpy(fileified,fname); 11646 trnlnm_iter_count = 0; 11647 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) { 11648 trnlnm_iter_count++; 11649 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break; 11650 } 11651 fname = fileified; 11652 } 11653 11654 vmsname = PerlMem_malloc(VMS_MAXRSS); 11655 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM); 11656 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) { 11657 /* Don't know if already in VMS format, so make sure */ 11658 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) { 11659 PerlMem_free(fileified); 11660 PerlMem_free(vmsname); 11661 return FALSE; 11662 } 11663 } 11664 else { 11665 strcpy(vmsname,fname); 11666 } 11667 11668 /* sys$check_access needs a file spec, not a directory spec. 11669 * Don't use flex_stat here, as that depends on thread context 11670 * having been initialized, and we may get here during startup. 11671 */ 11672 11673 retlen = namdsc.dsc$w_length = strlen(vmsname); 11674 if (vmsname[retlen-1] == ']' 11675 || vmsname[retlen-1] == '>' 11676 || vmsname[retlen-1] == ':' 11677 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) { 11678 11679 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) { 11680 PerlMem_free(fileified); 11681 PerlMem_free(vmsname); 11682 return FALSE; 11683 } 11684 fname = fileified; 11685 } 11686 else { 11687 fname = vmsname; 11688 } 11689 11690 retlen = namdsc.dsc$w_length = strlen(fname); 11691 namdsc.dsc$a_pointer = (char *)fname; 11692 11693 switch (bit) { 11694 case S_IXUSR: case S_IXGRP: case S_IXOTH: 11695 access = ARM$M_EXECUTE; 11696 flags = CHP$M_READ; 11697 break; 11698 case S_IRUSR: case S_IRGRP: case S_IROTH: 11699 access = ARM$M_READ; 11700 flags = CHP$M_READ | CHP$M_USEREADALL; 11701 break; 11702 case S_IWUSR: case S_IWGRP: case S_IWOTH: 11703 access = ARM$M_WRITE; 11704 flags = CHP$M_READ | CHP$M_WRITE; 11705 break; 11706 case S_IDUSR: case S_IDGRP: case S_IDOTH: 11707 access = ARM$M_DELETE; 11708 flags = CHP$M_READ | CHP$M_WRITE; 11709 break; 11710 default: 11711 if (fileified != NULL) 11712 PerlMem_free(fileified); 11713 if (vmsname != NULL) 11714 PerlMem_free(vmsname); 11715 return FALSE; 11716 } 11717 11718 /* Before we call $check_access, create a user profile with the current 11719 * process privs since otherwise it just uses the default privs from the 11720 * UAF and might give false positives or negatives. This only works on 11721 * VMS versions v6.0 and later since that's when sys$create_user_profile 11722 * became available. 11723 */ 11724 11725 /* get current process privs and username */ 11726 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0)); 11727 _ckvmssts(iosb[0]); 11728 11729 #if defined(__VMS_VER) && __VMS_VER >= 60000000 11730 11731 /* find out the space required for the profile */ 11732 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0, 11733 &usrprodsc.dsc$w_length,&profile_context)); 11734 11735 /* allocate space for the profile and get it filled in */ 11736 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length); 11737 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM); 11738 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer, 11739 &usrprodsc.dsc$w_length,&profile_context)); 11740 11741 /* use the profile to check access to the file; free profile & analyze results */ 11742 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc); 11743 PerlMem_free(usrprodsc.dsc$a_pointer); 11744 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */ 11745 11746 #else 11747 11748 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst); 11749 11750 #endif 11751 11752 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT || 11753 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN || 11754 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) { 11755 set_vaxc_errno(retsts); 11756 if (retsts == SS$_NOPRIV) set_errno(EACCES); 11757 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL); 11758 else set_errno(ENOENT); 11759 if (fileified != NULL) 11760 PerlMem_free(fileified); 11761 if (vmsname != NULL) 11762 PerlMem_free(vmsname); 11763 return FALSE; 11764 } 11765 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) { 11766 if (fileified != NULL) 11767 PerlMem_free(fileified); 11768 if (vmsname != NULL) 11769 PerlMem_free(vmsname); 11770 return TRUE; 11771 } 11772 _ckvmssts(retsts); 11773 11774 if (fileified != NULL) 11775 PerlMem_free(fileified); 11776 if (vmsname != NULL) 11777 PerlMem_free(vmsname); 11778 return FALSE; /* Should never get here */ 11779 11780 } 11781 11782 /* Do the permissions allow some operation? Assumes PL_statcache already set. */ 11783 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a 11784 * subset of the applicable information. 11785 */ 11786 bool 11787 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp) 11788 { 11789 return cando_by_name_int 11790 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN); 11791 } /* end of cando() */ 11792 /*}}}*/ 11793 11794 11795 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/ 11796 I32 11797 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname) 11798 { 11799 return cando_by_name_int(bit, effective, fname, 0); 11800 11801 } /* end of cando_by_name() */ 11802 /*}}}*/ 11803 11804 11805 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/ 11806 int 11807 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp) 11808 { 11809 if (!fstat(fd,(stat_t *) statbufp)) { 11810 char *cptr; 11811 char *vms_filename; 11812 vms_filename = PerlMem_malloc(VMS_MAXRSS); 11813 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM); 11814 11815 /* Save name for cando by name in VMS format */ 11816 cptr = getname(fd, vms_filename, 1); 11817 11818 /* This should not happen, but just in case */ 11819 if (cptr == NULL) { 11820 statbufp->st_devnam[0] = 0; 11821 } 11822 else { 11823 /* Make sure that the saved name fits in 255 characters */ 11824 cptr = do_rmsexpand 11825 (vms_filename, 11826 statbufp->st_devnam, 11827 0, 11828 NULL, 11829 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN, 11830 NULL, 11831 NULL); 11832 if (cptr == NULL) 11833 statbufp->st_devnam[0] = 0; 11834 } 11835 PerlMem_free(vms_filename); 11836 11837 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 11838 VMS_DEVICE_ENCODE 11839 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 11840 11841 # ifdef RTL_USES_UTC 11842 # ifdef VMSISH_TIME 11843 if (VMSISH_TIME) { 11844 statbufp->st_mtime = _toloc(statbufp->st_mtime); 11845 statbufp->st_atime = _toloc(statbufp->st_atime); 11846 statbufp->st_ctime = _toloc(statbufp->st_ctime); 11847 } 11848 # endif 11849 # else 11850 # ifdef VMSISH_TIME 11851 if (!VMSISH_TIME) { /* Return UTC instead of local time */ 11852 # else 11853 if (1) { 11854 # endif 11855 statbufp->st_mtime = _toutc(statbufp->st_mtime); 11856 statbufp->st_atime = _toutc(statbufp->st_atime); 11857 statbufp->st_ctime = _toutc(statbufp->st_ctime); 11858 } 11859 #endif 11860 return 0; 11861 } 11862 return -1; 11863 11864 } /* end of flex_fstat() */ 11865 /*}}}*/ 11866 11867 #if !defined(__VAX) && __CRTL_VER >= 80200000 11868 #ifdef lstat 11869 #undef lstat 11870 #endif 11871 #else 11872 #ifdef lstat 11873 #undef lstat 11874 #endif 11875 #define lstat(_x, _y) stat(_x, _y) 11876 #endif 11877 11878 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c) 11879 11880 static int 11881 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag) 11882 { 11883 char fileified[VMS_MAXRSS]; 11884 char temp_fspec[VMS_MAXRSS]; 11885 char *save_spec; 11886 int retval = -1; 11887 int saved_errno, saved_vaxc_errno; 11888 11889 if (!fspec) return retval; 11890 saved_errno = errno; saved_vaxc_errno = vaxc$errno; 11891 strcpy(temp_fspec, fspec); 11892 11893 if (decc_bug_devnull != 0) { 11894 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */ 11895 memset(statbufp,0,sizeof *statbufp); 11896 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0); 11897 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC; 11898 statbufp->st_uid = 0x00010001; 11899 statbufp->st_gid = 0x0001; 11900 time((time_t *)&statbufp->st_mtime); 11901 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime; 11902 return 0; 11903 } 11904 } 11905 11906 /* Try for a directory name first. If fspec contains a filename without 11907 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir 11908 * and sea:[wine.dark]water. exist, we prefer the directory here. 11909 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir, 11910 * not sea:[wine.dark]., if the latter exists. If the intended target is 11911 * the file with null type, specify this by calling flex_stat() with 11912 * a '.' at the end of fspec. 11913 * 11914 * If we are in Posix filespec mode, accept the filename as is. 11915 */ 11916 11917 11918 #if __CRTL_VER >= 70300000 && !defined(__VAX) 11919 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless 11920 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already. 11921 */ 11922 if (!decc_efs_charset) 11923 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1); 11924 #endif 11925 11926 #if __CRTL_VER >= 80200000 && !defined(__VAX) 11927 if (decc_posix_compliant_pathnames == 0) { 11928 #endif 11929 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) { 11930 if (lstat_flag == 0) 11931 retval = stat(fileified,(stat_t *) statbufp); 11932 else 11933 retval = lstat(fileified,(stat_t *) statbufp); 11934 save_spec = fileified; 11935 } 11936 if (retval) { 11937 if (lstat_flag == 0) 11938 retval = stat(temp_fspec,(stat_t *) statbufp); 11939 else 11940 retval = lstat(temp_fspec,(stat_t *) statbufp); 11941 save_spec = temp_fspec; 11942 } 11943 /* 11944 * In debugging, on 8.3 Alpha, I found a case where stat was returning a 11945 * file not found error for a directory named foo:[bar.t] or /foo/bar/t 11946 * and lstat was working correctly for the same file. 11947 * The only syntax that was working for stat was "foo:[bar]t.dir". 11948 * 11949 * Other directories with the same syntax worked fine. 11950 * So work around the problem when it shows up here. 11951 */ 11952 if (retval) { 11953 int save_errno = errno; 11954 if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) { 11955 if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) { 11956 retval = stat(fileified, (stat_t *) statbufp); 11957 save_spec = fileified; 11958 } 11959 } 11960 /* Restore the errno value if third stat does not succeed */ 11961 if (retval != 0) 11962 errno = save_errno; 11963 } 11964 #if __CRTL_VER >= 80200000 && !defined(__VAX) 11965 } else { 11966 if (lstat_flag == 0) 11967 retval = stat(temp_fspec,(stat_t *) statbufp); 11968 else 11969 retval = lstat(temp_fspec,(stat_t *) statbufp); 11970 save_spec = temp_fspec; 11971 } 11972 #endif 11973 11974 #if __CRTL_VER >= 70300000 && !defined(__VAX) 11975 /* As you were... */ 11976 if (!decc_efs_charset) 11977 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0); 11978 #endif 11979 11980 if (!retval) { 11981 char * cptr; 11982 int rmsex_flags = PERL_RMSEXPAND_M_VMS; 11983 11984 /* If this is an lstat, do not follow the link */ 11985 if (lstat_flag) 11986 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK; 11987 11988 cptr = do_rmsexpand 11989 (save_spec, statbufp->st_devnam, 0, NULL, rmsex_flags, NULL, NULL); 11990 if (cptr == NULL) 11991 statbufp->st_devnam[0] = 0; 11992 11993 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino); 11994 VMS_DEVICE_ENCODE 11995 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev); 11996 # ifdef RTL_USES_UTC 11997 # ifdef VMSISH_TIME 11998 if (VMSISH_TIME) { 11999 statbufp->st_mtime = _toloc(statbufp->st_mtime); 12000 statbufp->st_atime = _toloc(statbufp->st_atime); 12001 statbufp->st_ctime = _toloc(statbufp->st_ctime); 12002 } 12003 # endif 12004 # else 12005 # ifdef VMSISH_TIME 12006 if (!VMSISH_TIME) { /* Return UTC instead of local time */ 12007 # else 12008 if (1) { 12009 # endif 12010 statbufp->st_mtime = _toutc(statbufp->st_mtime); 12011 statbufp->st_atime = _toutc(statbufp->st_atime); 12012 statbufp->st_ctime = _toutc(statbufp->st_ctime); 12013 } 12014 # endif 12015 } 12016 /* If we were successful, leave errno where we found it */ 12017 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; } 12018 return retval; 12019 12020 } /* end of flex_stat_int() */ 12021 12022 12023 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/ 12024 int 12025 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp) 12026 { 12027 return flex_stat_int(fspec, statbufp, 0); 12028 } 12029 /*}}}*/ 12030 12031 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/ 12032 int 12033 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp) 12034 { 12035 return flex_stat_int(fspec, statbufp, 1); 12036 } 12037 /*}}}*/ 12038 12039 12040 /*{{{char *my_getlogin()*/ 12041 /* VMS cuserid == Unix getlogin, except calling sequence */ 12042 char * 12043 my_getlogin(void) 12044 { 12045 static char user[L_cuserid]; 12046 return cuserid(user); 12047 } 12048 /*}}}*/ 12049 12050 12051 /* rmscopy - copy a file using VMS RMS routines 12052 * 12053 * Copies contents and attributes of spec_in to spec_out, except owner 12054 * and protection information. Name and type of spec_in are used as 12055 * defaults for spec_out. The third parameter specifies whether rmscopy() 12056 * should try to propagate timestamps from the input file to the output file. 12057 * If it is less than 0, no timestamps are preserved. If it is 0, then 12058 * rmscopy() will behave similarly to the DCL COPY command: timestamps are 12059 * propagated to the output file at creation iff the output file specification 12060 * did not contain an explicit name or type, and the revision date is always 12061 * updated at the end of the copy operation. If it is greater than 0, then 12062 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps 12063 * other than the revision date should be propagated, and bit 1 indicates 12064 * that the revision date should be propagated. 12065 * 12066 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure. 12067 * 12068 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>. 12069 * Incorporates, with permission, some code from EZCOPY by Tim Adye 12070 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code 12071 * as part of the Perl standard distribution under the terms of the 12072 * GNU General Public License or the Perl Artistic License. Copies 12073 * of each may be found in the Perl standard distribution. 12074 */ /* FIXME */ 12075 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/ 12076 int 12077 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates) 12078 { 12079 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out, 12080 *rsa, *rsal, *rsa_out, *rsal_out, *ubf; 12081 unsigned long int i, sts, sts2; 12082 int dna_len; 12083 struct FAB fab_in, fab_out; 12084 struct RAB rab_in, rab_out; 12085 rms_setup_nam(nam); 12086 rms_setup_nam(nam_out); 12087 struct XABDAT xabdat; 12088 struct XABFHC xabfhc; 12089 struct XABRDT xabrdt; 12090 struct XABSUM xabsum; 12091 12092 vmsin = PerlMem_malloc(VMS_MAXRSS); 12093 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM); 12094 vmsout = PerlMem_malloc(VMS_MAXRSS); 12095 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM); 12096 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) || 12097 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) { 12098 PerlMem_free(vmsin); 12099 PerlMem_free(vmsout); 12100 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12101 return 0; 12102 } 12103 12104 esa = PerlMem_malloc(VMS_MAXRSS); 12105 if (esa == NULL) _ckvmssts(SS$_INSFMEM); 12106 esal = NULL; 12107 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12108 esal = PerlMem_malloc(VMS_MAXRSS); 12109 if (esal == NULL) _ckvmssts(SS$_INSFMEM); 12110 #endif 12111 fab_in = cc$rms_fab; 12112 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin)); 12113 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI; 12114 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET; 12115 fab_in.fab$l_fop = FAB$M_SQO; 12116 rms_bind_fab_nam(fab_in, nam); 12117 fab_in.fab$l_xab = (void *) &xabdat; 12118 12119 rsa = PerlMem_malloc(VMS_MAXRSS); 12120 if (rsa == NULL) _ckvmssts(SS$_INSFMEM); 12121 rsal = NULL; 12122 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12123 rsal = PerlMem_malloc(VMS_MAXRSS); 12124 if (rsal == NULL) _ckvmssts(SS$_INSFMEM); 12125 #endif 12126 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1)); 12127 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1)); 12128 rms_nam_esl(nam) = 0; 12129 rms_nam_rsl(nam) = 0; 12130 rms_nam_esll(nam) = 0; 12131 rms_nam_rsll(nam) = 0; 12132 #ifdef NAM$M_NO_SHORT_UPCASE 12133 if (decc_efs_case_preserve) 12134 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE); 12135 #endif 12136 12137 xabdat = cc$rms_xabdat; /* To get creation date */ 12138 xabdat.xab$l_nxt = (void *) &xabfhc; 12139 12140 xabfhc = cc$rms_xabfhc; /* To get record length */ 12141 xabfhc.xab$l_nxt = (void *) &xabsum; 12142 12143 xabsum = cc$rms_xabsum; /* To get key and area information */ 12144 12145 if (!((sts = sys$open(&fab_in)) & 1)) { 12146 PerlMem_free(vmsin); 12147 PerlMem_free(vmsout); 12148 PerlMem_free(esa); 12149 if (esal != NULL) 12150 PerlMem_free(esal); 12151 PerlMem_free(rsa); 12152 if (rsal != NULL) 12153 PerlMem_free(rsal); 12154 set_vaxc_errno(sts); 12155 switch (sts) { 12156 case RMS$_FNF: case RMS$_DNF: 12157 set_errno(ENOENT); break; 12158 case RMS$_DIR: 12159 set_errno(ENOTDIR); break; 12160 case RMS$_DEV: 12161 set_errno(ENODEV); break; 12162 case RMS$_SYN: 12163 set_errno(EINVAL); break; 12164 case RMS$_PRV: 12165 set_errno(EACCES); break; 12166 default: 12167 set_errno(EVMSERR); 12168 } 12169 return 0; 12170 } 12171 12172 nam_out = nam; 12173 fab_out = fab_in; 12174 fab_out.fab$w_ifi = 0; 12175 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT; 12176 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI; 12177 fab_out.fab$l_fop = FAB$M_SQO; 12178 rms_bind_fab_nam(fab_out, nam_out); 12179 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout)); 12180 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0; 12181 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len); 12182 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1); 12183 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM); 12184 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1); 12185 if (rsa_out == NULL) _ckvmssts(SS$_INSFMEM); 12186 esal_out = NULL; 12187 rsal_out = NULL; 12188 #if !defined(__VAX) && defined(NAML$C_MAXRSS) 12189 esal_out = PerlMem_malloc(VMS_MAXRSS); 12190 if (esal_out == NULL) _ckvmssts(SS$_INSFMEM); 12191 rsal_out = PerlMem_malloc(VMS_MAXRSS); 12192 if (rsal_out == NULL) _ckvmssts(SS$_INSFMEM); 12193 #endif 12194 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1)); 12195 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1)); 12196 12197 if (preserve_dates == 0) { /* Act like DCL COPY */ 12198 rms_set_nam_nop(nam_out, NAM$M_SYNCHK); 12199 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */ 12200 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) { 12201 PerlMem_free(vmsin); 12202 PerlMem_free(vmsout); 12203 PerlMem_free(esa); 12204 if (esal != NULL) 12205 PerlMem_free(esal); 12206 PerlMem_free(rsa); 12207 if (rsal != NULL) 12208 PerlMem_free(rsal); 12209 PerlMem_free(esa_out); 12210 if (esal_out != NULL) 12211 PerlMem_free(esal_out); 12212 PerlMem_free(rsa_out); 12213 if (rsal_out != NULL) 12214 PerlMem_free(rsal_out); 12215 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR); 12216 set_vaxc_errno(sts); 12217 return 0; 12218 } 12219 fab_out.fab$l_xab = (void *) &xabdat; 12220 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) 12221 preserve_dates = 1; 12222 } 12223 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */ 12224 preserve_dates =0; /* bitmask from this point forward */ 12225 12226 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc; 12227 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) { 12228 PerlMem_free(vmsin); 12229 PerlMem_free(vmsout); 12230 PerlMem_free(esa); 12231 if (esal != NULL) 12232 PerlMem_free(esal); 12233 PerlMem_free(rsa); 12234 if (rsal != NULL) 12235 PerlMem_free(rsal); 12236 PerlMem_free(esa_out); 12237 if (esal_out != NULL) 12238 PerlMem_free(esal_out); 12239 PerlMem_free(rsa_out); 12240 if (rsal_out != NULL) 12241 PerlMem_free(rsal_out); 12242 set_vaxc_errno(sts); 12243 switch (sts) { 12244 case RMS$_DNF: 12245 set_errno(ENOENT); break; 12246 case RMS$_DIR: 12247 set_errno(ENOTDIR); break; 12248 case RMS$_DEV: 12249 set_errno(ENODEV); break; 12250 case RMS$_SYN: 12251 set_errno(EINVAL); break; 12252 case RMS$_PRV: 12253 set_errno(EACCES); break; 12254 default: 12255 set_errno(EVMSERR); 12256 } 12257 return 0; 12258 } 12259 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */ 12260 if (preserve_dates & 2) { 12261 /* sys$close() will process xabrdt, not xabdat */ 12262 xabrdt = cc$rms_xabrdt; 12263 #ifndef __GNUC__ 12264 xabrdt.xab$q_rdt = xabdat.xab$q_rdt; 12265 #else 12266 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt 12267 * is unsigned long[2], while DECC & VAXC use a struct */ 12268 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt); 12269 #endif 12270 fab_out.fab$l_xab = (void *) &xabrdt; 12271 } 12272 12273 ubf = PerlMem_malloc(32256); 12274 if (ubf == NULL) _ckvmssts(SS$_INSFMEM); 12275 rab_in = cc$rms_rab; 12276 rab_in.rab$l_fab = &fab_in; 12277 rab_in.rab$l_rop = RAB$M_BIO; 12278 rab_in.rab$l_ubf = ubf; 12279 rab_in.rab$w_usz = 32256; 12280 if (!((sts = sys$connect(&rab_in)) & 1)) { 12281 sys$close(&fab_in); sys$close(&fab_out); 12282 PerlMem_free(vmsin); 12283 PerlMem_free(vmsout); 12284 PerlMem_free(ubf); 12285 PerlMem_free(esa); 12286 if (esal != NULL) 12287 PerlMem_free(esal); 12288 PerlMem_free(rsa); 12289 if (rsal != NULL) 12290 PerlMem_free(rsal); 12291 PerlMem_free(esa_out); 12292 if (esal_out != NULL) 12293 PerlMem_free(esal_out); 12294 PerlMem_free(rsa_out); 12295 if (rsal_out != NULL) 12296 PerlMem_free(rsal_out); 12297 set_errno(EVMSERR); set_vaxc_errno(sts); 12298 return 0; 12299 } 12300 12301 rab_out = cc$rms_rab; 12302 rab_out.rab$l_fab = &fab_out; 12303 rab_out.rab$l_rbf = ubf; 12304 if (!((sts = sys$connect(&rab_out)) & 1)) { 12305 sys$close(&fab_in); sys$close(&fab_out); 12306 PerlMem_free(vmsin); 12307 PerlMem_free(vmsout); 12308 PerlMem_free(ubf); 12309 PerlMem_free(esa); 12310 if (esal != NULL) 12311 PerlMem_free(esal); 12312 PerlMem_free(rsa); 12313 if (rsal != NULL) 12314 PerlMem_free(rsal); 12315 PerlMem_free(esa_out); 12316 if (esal_out != NULL) 12317 PerlMem_free(esal_out); 12318 PerlMem_free(rsa_out); 12319 if (rsal_out != NULL) 12320 PerlMem_free(rsal_out); 12321 set_errno(EVMSERR); set_vaxc_errno(sts); 12322 return 0; 12323 } 12324 12325 while ((sts = sys$read(&rab_in))) { /* always true */ 12326 if (sts == RMS$_EOF) break; 12327 rab_out.rab$w_rsz = rab_in.rab$w_rsz; 12328 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) { 12329 sys$close(&fab_in); sys$close(&fab_out); 12330 PerlMem_free(vmsin); 12331 PerlMem_free(vmsout); 12332 PerlMem_free(ubf); 12333 PerlMem_free(esa); 12334 if (esal != NULL) 12335 PerlMem_free(esal); 12336 PerlMem_free(rsa); 12337 if (rsal != NULL) 12338 PerlMem_free(rsal); 12339 PerlMem_free(esa_out); 12340 if (esal_out != NULL) 12341 PerlMem_free(esal_out); 12342 PerlMem_free(rsa_out); 12343 if (rsal_out != NULL) 12344 PerlMem_free(rsal_out); 12345 set_errno(EVMSERR); set_vaxc_errno(sts); 12346 return 0; 12347 } 12348 } 12349 12350 12351 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */ 12352 sys$close(&fab_in); sys$close(&fab_out); 12353 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts; 12354 12355 PerlMem_free(vmsin); 12356 PerlMem_free(vmsout); 12357 PerlMem_free(ubf); 12358 PerlMem_free(esa); 12359 if (esal != NULL) 12360 PerlMem_free(esal); 12361 PerlMem_free(rsa); 12362 if (rsal != NULL) 12363 PerlMem_free(rsal); 12364 PerlMem_free(esa_out); 12365 if (esal_out != NULL) 12366 PerlMem_free(esal_out); 12367 PerlMem_free(rsa_out); 12368 if (rsal_out != NULL) 12369 PerlMem_free(rsal_out); 12370 12371 if (!(sts & 1)) { 12372 set_errno(EVMSERR); set_vaxc_errno(sts); 12373 return 0; 12374 } 12375 12376 return 1; 12377 12378 } /* end of rmscopy() */ 12379 /*}}}*/ 12380 12381 12382 /*** The following glue provides 'hooks' to make some of the routines 12383 * from this file available from Perl. These routines are sufficiently 12384 * basic, and are required sufficiently early in the build process, 12385 * that's it's nice to have them available to miniperl as well as the 12386 * full Perl, so they're set up here instead of in an extension. The 12387 * Perl code which handles importation of these names into a given 12388 * package lives in [.VMS]Filespec.pm in @INC. 12389 */ 12390 12391 void 12392 rmsexpand_fromperl(pTHX_ CV *cv) 12393 { 12394 dXSARGS; 12395 char *fspec, *defspec = NULL, *rslt; 12396 STRLEN n_a; 12397 int fs_utf8, dfs_utf8; 12398 12399 fs_utf8 = 0; 12400 dfs_utf8 = 0; 12401 if (!items || items > 2) 12402 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])"); 12403 fspec = SvPV(ST(0),n_a); 12404 fs_utf8 = SvUTF8(ST(0)); 12405 if (!fspec || !*fspec) XSRETURN_UNDEF; 12406 if (items == 2) { 12407 defspec = SvPV(ST(1),n_a); 12408 dfs_utf8 = SvUTF8(ST(1)); 12409 } 12410 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8); 12411 ST(0) = sv_newmortal(); 12412 if (rslt != NULL) { 12413 sv_usepvn(ST(0),rslt,strlen(rslt)); 12414 if (fs_utf8) { 12415 SvUTF8_on(ST(0)); 12416 } 12417 } 12418 XSRETURN(1); 12419 } 12420 12421 void 12422 vmsify_fromperl(pTHX_ CV *cv) 12423 { 12424 dXSARGS; 12425 char *vmsified; 12426 STRLEN n_a; 12427 int utf8_fl; 12428 12429 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)"); 12430 utf8_fl = SvUTF8(ST(0)); 12431 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12432 ST(0) = sv_newmortal(); 12433 if (vmsified != NULL) { 12434 sv_usepvn(ST(0),vmsified,strlen(vmsified)); 12435 if (utf8_fl) { 12436 SvUTF8_on(ST(0)); 12437 } 12438 } 12439 XSRETURN(1); 12440 } 12441 12442 void 12443 unixify_fromperl(pTHX_ CV *cv) 12444 { 12445 dXSARGS; 12446 char *unixified; 12447 STRLEN n_a; 12448 int utf8_fl; 12449 12450 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)"); 12451 utf8_fl = SvUTF8(ST(0)); 12452 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12453 ST(0) = sv_newmortal(); 12454 if (unixified != NULL) { 12455 sv_usepvn(ST(0),unixified,strlen(unixified)); 12456 if (utf8_fl) { 12457 SvUTF8_on(ST(0)); 12458 } 12459 } 12460 XSRETURN(1); 12461 } 12462 12463 void 12464 fileify_fromperl(pTHX_ CV *cv) 12465 { 12466 dXSARGS; 12467 char *fileified; 12468 STRLEN n_a; 12469 int utf8_fl; 12470 12471 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)"); 12472 utf8_fl = SvUTF8(ST(0)); 12473 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12474 ST(0) = sv_newmortal(); 12475 if (fileified != NULL) { 12476 sv_usepvn(ST(0),fileified,strlen(fileified)); 12477 if (utf8_fl) { 12478 SvUTF8_on(ST(0)); 12479 } 12480 } 12481 XSRETURN(1); 12482 } 12483 12484 void 12485 pathify_fromperl(pTHX_ CV *cv) 12486 { 12487 dXSARGS; 12488 char *pathified; 12489 STRLEN n_a; 12490 int utf8_fl; 12491 12492 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)"); 12493 utf8_fl = SvUTF8(ST(0)); 12494 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12495 ST(0) = sv_newmortal(); 12496 if (pathified != NULL) { 12497 sv_usepvn(ST(0),pathified,strlen(pathified)); 12498 if (utf8_fl) { 12499 SvUTF8_on(ST(0)); 12500 } 12501 } 12502 XSRETURN(1); 12503 } 12504 12505 void 12506 vmspath_fromperl(pTHX_ CV *cv) 12507 { 12508 dXSARGS; 12509 char *vmspath; 12510 STRLEN n_a; 12511 int utf8_fl; 12512 12513 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)"); 12514 utf8_fl = SvUTF8(ST(0)); 12515 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12516 ST(0) = sv_newmortal(); 12517 if (vmspath != NULL) { 12518 sv_usepvn(ST(0),vmspath,strlen(vmspath)); 12519 if (utf8_fl) { 12520 SvUTF8_on(ST(0)); 12521 } 12522 } 12523 XSRETURN(1); 12524 } 12525 12526 void 12527 unixpath_fromperl(pTHX_ CV *cv) 12528 { 12529 dXSARGS; 12530 char *unixpath; 12531 STRLEN n_a; 12532 int utf8_fl; 12533 12534 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)"); 12535 utf8_fl = SvUTF8(ST(0)); 12536 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl); 12537 ST(0) = sv_newmortal(); 12538 if (unixpath != NULL) { 12539 sv_usepvn(ST(0),unixpath,strlen(unixpath)); 12540 if (utf8_fl) { 12541 SvUTF8_on(ST(0)); 12542 } 12543 } 12544 XSRETURN(1); 12545 } 12546 12547 void 12548 candelete_fromperl(pTHX_ CV *cv) 12549 { 12550 dXSARGS; 12551 char *fspec, *fsp; 12552 SV *mysv; 12553 IO *io; 12554 STRLEN n_a; 12555 12556 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)"); 12557 12558 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12559 Newx(fspec, VMS_MAXRSS, char); 12560 if (fspec == NULL) _ckvmssts(SS$_INSFMEM); 12561 if (SvTYPE(mysv) == SVt_PVGV) { 12562 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) { 12563 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12564 ST(0) = &PL_sv_no; 12565 Safefree(fspec); 12566 XSRETURN(1); 12567 } 12568 fsp = fspec; 12569 } 12570 else { 12571 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) { 12572 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12573 ST(0) = &PL_sv_no; 12574 Safefree(fspec); 12575 XSRETURN(1); 12576 } 12577 } 12578 12579 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp)); 12580 Safefree(fspec); 12581 XSRETURN(1); 12582 } 12583 12584 void 12585 rmscopy_fromperl(pTHX_ CV *cv) 12586 { 12587 dXSARGS; 12588 char *inspec, *outspec, *inp, *outp; 12589 int date_flag; 12590 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}, 12591 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 12592 unsigned long int sts; 12593 SV *mysv; 12594 IO *io; 12595 STRLEN n_a; 12596 12597 if (items < 2 || items > 3) 12598 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])"); 12599 12600 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0); 12601 Newx(inspec, VMS_MAXRSS, char); 12602 if (SvTYPE(mysv) == SVt_PVGV) { 12603 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) { 12604 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12605 ST(0) = &PL_sv_no; 12606 Safefree(inspec); 12607 XSRETURN(1); 12608 } 12609 inp = inspec; 12610 } 12611 else { 12612 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) { 12613 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12614 ST(0) = &PL_sv_no; 12615 Safefree(inspec); 12616 XSRETURN(1); 12617 } 12618 } 12619 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1); 12620 Newx(outspec, VMS_MAXRSS, char); 12621 if (SvTYPE(mysv) == SVt_PVGV) { 12622 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) { 12623 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12624 ST(0) = &PL_sv_no; 12625 Safefree(inspec); 12626 Safefree(outspec); 12627 XSRETURN(1); 12628 } 12629 outp = outspec; 12630 } 12631 else { 12632 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) { 12633 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG); 12634 ST(0) = &PL_sv_no; 12635 Safefree(inspec); 12636 Safefree(outspec); 12637 XSRETURN(1); 12638 } 12639 } 12640 date_flag = (items == 3) ? SvIV(ST(2)) : 0; 12641 12642 ST(0) = boolSV(rmscopy(inp,outp,date_flag)); 12643 Safefree(inspec); 12644 Safefree(outspec); 12645 XSRETURN(1); 12646 } 12647 12648 /* The mod2fname is limited to shorter filenames by design, so it should 12649 * not be modified to support longer EFS pathnames 12650 */ 12651 void 12652 mod2fname(pTHX_ CV *cv) 12653 { 12654 dXSARGS; 12655 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1], 12656 workbuff[NAM$C_MAXRSS*1 + 1]; 12657 int total_namelen = 3, counter, num_entries; 12658 /* ODS-5 ups this, but we want to be consistent, so... */ 12659 int max_name_len = 39; 12660 AV *in_array = (AV *)SvRV(ST(0)); 12661 12662 num_entries = av_len(in_array); 12663 12664 /* All the names start with PL_. */ 12665 strcpy(ultimate_name, "PL_"); 12666 12667 /* Clean up our working buffer */ 12668 Zero(work_name, sizeof(work_name), char); 12669 12670 /* Run through the entries and build up a working name */ 12671 for(counter = 0; counter <= num_entries; counter++) { 12672 /* If it's not the first name then tack on a __ */ 12673 if (counter) { 12674 strcat(work_name, "__"); 12675 } 12676 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE), 12677 PL_na)); 12678 } 12679 12680 /* Check to see if we actually have to bother...*/ 12681 if (strlen(work_name) + 3 <= max_name_len) { 12682 strcat(ultimate_name, work_name); 12683 } else { 12684 /* It's too darned big, so we need to go strip. We use the same */ 12685 /* algorithm as xsubpp does. First, strip out doubled __ */ 12686 char *source, *dest, last; 12687 dest = workbuff; 12688 last = 0; 12689 for (source = work_name; *source; source++) { 12690 if (last == *source && last == '_') { 12691 continue; 12692 } 12693 *dest++ = *source; 12694 last = *source; 12695 } 12696 /* Go put it back */ 12697 strcpy(work_name, workbuff); 12698 /* Is it still too big? */ 12699 if (strlen(work_name) + 3 > max_name_len) { 12700 /* Strip duplicate letters */ 12701 last = 0; 12702 dest = workbuff; 12703 for (source = work_name; *source; source++) { 12704 if (last == toupper(*source)) { 12705 continue; 12706 } 12707 *dest++ = *source; 12708 last = toupper(*source); 12709 } 12710 strcpy(work_name, workbuff); 12711 } 12712 12713 /* Is it *still* too big? */ 12714 if (strlen(work_name) + 3 > max_name_len) { 12715 /* Too bad, we truncate */ 12716 work_name[max_name_len - 2] = 0; 12717 } 12718 strcat(ultimate_name, work_name); 12719 } 12720 12721 /* Okay, return it */ 12722 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0)); 12723 XSRETURN(1); 12724 } 12725 12726 void 12727 hushexit_fromperl(pTHX_ CV *cv) 12728 { 12729 dXSARGS; 12730 12731 if (items > 0) { 12732 VMSISH_HUSHED = SvTRUE(ST(0)); 12733 } 12734 ST(0) = boolSV(VMSISH_HUSHED); 12735 XSRETURN(1); 12736 } 12737 12738 12739 PerlIO * 12740 Perl_vms_start_glob 12741 (pTHX_ SV *tmpglob, 12742 IO *io) 12743 { 12744 PerlIO *fp; 12745 struct vs_str_st *rslt; 12746 char *vmsspec; 12747 char *rstr; 12748 char *begin, *cp; 12749 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;"); 12750 PerlIO *tmpfp; 12751 STRLEN i; 12752 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 12753 struct dsc$descriptor_vs rsdsc; 12754 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0; 12755 unsigned long hasver = 0, isunix = 0; 12756 unsigned long int lff_flags = 0; 12757 int rms_sts; 12758 12759 #ifdef VMS_LONGNAME_SUPPORT 12760 lff_flags = LIB$M_FIL_LONG_NAMES; 12761 #endif 12762 /* The Newx macro will not allow me to assign a smaller array 12763 * to the rslt pointer, so we will assign it to the begin char pointer 12764 * and then copy the value into the rslt pointer. 12765 */ 12766 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char); 12767 rslt = (struct vs_str_st *)begin; 12768 rslt->length = 0; 12769 rstr = &rslt->str[0]; 12770 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */ 12771 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int); 12772 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT; 12773 rsdsc.dsc$b_class = DSC$K_CLASS_VS; 12774 12775 Newx(vmsspec, VMS_MAXRSS, char); 12776 12777 /* We could find out if there's an explicit dev/dir or version 12778 by peeking into lib$find_file's internal context at 12779 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb 12780 but that's unsupported, so I don't want to do it now and 12781 have it bite someone in the future. */ 12782 /* Fix-me: vms_split_path() is the only way to do this, the 12783 existing method will fail with many legal EFS or UNIX specifications 12784 */ 12785 12786 cp = SvPV(tmpglob,i); 12787 12788 for (; i; i--) { 12789 if (cp[i] == ';') hasver = 1; 12790 if (cp[i] == '.') { 12791 if (sts) hasver = 1; 12792 else sts = 1; 12793 } 12794 if (cp[i] == '/') { 12795 hasdir = isunix = 1; 12796 break; 12797 } 12798 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') { 12799 hasdir = 1; 12800 break; 12801 } 12802 } 12803 if ((tmpfp = PerlIO_tmpfile()) != NULL) { 12804 int found = 0; 12805 Stat_t st; 12806 int stat_sts; 12807 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st); 12808 if (!stat_sts && S_ISDIR(st.st_mode)) { 12809 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL); 12810 ok = (wilddsc.dsc$a_pointer != NULL); 12811 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */ 12812 hasdir = 1; 12813 } 12814 else { 12815 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL); 12816 ok = (wilddsc.dsc$a_pointer != NULL); 12817 } 12818 if (ok) 12819 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer); 12820 12821 /* If not extended character set, replace ? with % */ 12822 /* With extended character set, ? is a wildcard single character */ 12823 if (!decc_efs_case_preserve) { 12824 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) 12825 if (*cp == '?') *cp = '%'; 12826 } 12827 sts = SS$_NORMAL; 12828 while (ok && $VMS_STATUS_SUCCESS(sts)) { 12829 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 12830 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len; 12831 12832 sts = lib$find_file(&wilddsc,&rsdsc,&cxt, 12833 &dfltdsc,NULL,&rms_sts,&lff_flags); 12834 if (!$VMS_STATUS_SUCCESS(sts)) 12835 break; 12836 12837 found++; 12838 12839 /* with varying string, 1st word of buffer contains result length */ 12840 rstr[rslt->length] = '\0'; 12841 12842 /* Find where all the components are */ 12843 v_sts = vms_split_path 12844 (rstr, 12845 &v_spec, 12846 &v_len, 12847 &r_spec, 12848 &r_len, 12849 &d_spec, 12850 &d_len, 12851 &n_spec, 12852 &n_len, 12853 &e_spec, 12854 &e_len, 12855 &vs_spec, 12856 &vs_len); 12857 12858 /* If no version on input, truncate the version on output */ 12859 if (!hasver && (vs_len > 0)) { 12860 *vs_spec = '\0'; 12861 vs_len = 0; 12862 12863 /* No version & a null extension on UNIX handling */ 12864 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) { 12865 e_len = 0; 12866 *e_spec = '\0'; 12867 } 12868 } 12869 12870 if (!decc_efs_case_preserve) { 12871 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp); 12872 } 12873 12874 if (hasdir) { 12875 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1); 12876 begin = rstr; 12877 } 12878 else { 12879 /* Start with the name */ 12880 begin = n_spec; 12881 } 12882 strcat(begin,"\n"); 12883 ok = (PerlIO_puts(tmpfp,begin) != EOF); 12884 } 12885 if (cxt) (void)lib$find_file_end(&cxt); 12886 12887 if (!found) { 12888 /* Be POSIXish: return the input pattern when no matches */ 12889 strcpy(rstr,SvPVX(tmpglob)); 12890 strcat(rstr,"\n"); 12891 ok = (PerlIO_puts(tmpfp,rstr) != EOF); 12892 } 12893 12894 if (ok && sts != RMS$_NMF && 12895 sts != RMS$_DNF && sts != RMS_FNF) ok = 0; 12896 if (!ok) { 12897 if (!(sts & 1)) { 12898 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts); 12899 } 12900 PerlIO_close(tmpfp); 12901 fp = NULL; 12902 } 12903 else { 12904 PerlIO_rewind(tmpfp); 12905 IoTYPE(io) = IoTYPE_RDONLY; 12906 IoIFP(io) = fp = tmpfp; 12907 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */ 12908 } 12909 } 12910 Safefree(vmsspec); 12911 Safefree(rslt); 12912 return fp; 12913 } 12914 12915 12916 static char * 12917 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec, 12918 int *utf8_fl); 12919 12920 void 12921 unixrealpath_fromperl(pTHX_ CV *cv) 12922 { 12923 dXSARGS; 12924 char *fspec, *rslt_spec, *rslt; 12925 STRLEN n_a; 12926 12927 if (!items || items != 1) 12928 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)"); 12929 12930 fspec = SvPV(ST(0),n_a); 12931 if (!fspec || !*fspec) XSRETURN_UNDEF; 12932 12933 Newx(rslt_spec, VMS_MAXRSS + 1, char); 12934 rslt = do_vms_realpath(fspec, rslt_spec, NULL); 12935 12936 ST(0) = sv_newmortal(); 12937 if (rslt != NULL) 12938 sv_usepvn(ST(0),rslt,strlen(rslt)); 12939 else 12940 Safefree(rslt_spec); 12941 XSRETURN(1); 12942 } 12943 12944 static char * 12945 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec, 12946 int *utf8_fl); 12947 12948 void 12949 vmsrealpath_fromperl(pTHX_ CV *cv) 12950 { 12951 dXSARGS; 12952 char *fspec, *rslt_spec, *rslt; 12953 STRLEN n_a; 12954 12955 if (!items || items != 1) 12956 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)"); 12957 12958 fspec = SvPV(ST(0),n_a); 12959 if (!fspec || !*fspec) XSRETURN_UNDEF; 12960 12961 Newx(rslt_spec, VMS_MAXRSS + 1, char); 12962 rslt = do_vms_realname(fspec, rslt_spec, NULL); 12963 12964 ST(0) = sv_newmortal(); 12965 if (rslt != NULL) 12966 sv_usepvn(ST(0),rslt,strlen(rslt)); 12967 else 12968 Safefree(rslt_spec); 12969 XSRETURN(1); 12970 } 12971 12972 #ifdef HAS_SYMLINK 12973 /* 12974 * A thin wrapper around decc$symlink to make sure we follow the 12975 * standard and do not create a symlink with a zero-length name. 12976 */ 12977 /*{{{ int my_symlink(const char *path1, const char *path2)*/ 12978 int my_symlink(const char *path1, const char *path2) { 12979 if (!path2 || !*path2) { 12980 SETERRNO(ENOENT, SS$_NOSUCHFILE); 12981 return -1; 12982 } 12983 return symlink(path1, path2); 12984 } 12985 /*}}}*/ 12986 12987 #endif /* HAS_SYMLINK */ 12988 12989 int do_vms_case_tolerant(void); 12990 12991 void 12992 case_tolerant_process_fromperl(pTHX_ CV *cv) 12993 { 12994 dXSARGS; 12995 ST(0) = boolSV(do_vms_case_tolerant()); 12996 XSRETURN(1); 12997 } 12998 12999 void 13000 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, 13001 struct interp_intern *dst) 13002 { 13003 memcpy(dst,src,sizeof(struct interp_intern)); 13004 } 13005 13006 void 13007 Perl_sys_intern_clear(pTHX) 13008 { 13009 } 13010 13011 void 13012 Perl_sys_intern_init(pTHX) 13013 { 13014 unsigned int ix = RAND_MAX; 13015 double x; 13016 13017 VMSISH_HUSHED = 0; 13018 13019 /* fix me later to track running under GNV */ 13020 /* this allows some limited testing */ 13021 MY_POSIX_EXIT = decc_filename_unix_report; 13022 13023 x = (float)ix; 13024 MY_INV_RAND_MAX = 1./x; 13025 } 13026 13027 void 13028 init_os_extras(void) 13029 { 13030 dTHX; 13031 char* file = __FILE__; 13032 if (decc_disable_to_vms_logname_translation) { 13033 no_translate_barewords = TRUE; 13034 } else { 13035 no_translate_barewords = FALSE; 13036 } 13037 13038 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$"); 13039 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$"); 13040 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$"); 13041 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$"); 13042 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$"); 13043 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$"); 13044 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$"); 13045 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$"); 13046 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$"); 13047 newXS("File::Copy::rmscopy",rmscopy_fromperl,file); 13048 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$"); 13049 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$"); 13050 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$"); 13051 newXSproto("VMS::Filespec::case_tolerant_process", 13052 case_tolerant_process_fromperl,file,""); 13053 13054 store_pipelocs(aTHX); /* will redo any earlier attempts */ 13055 13056 return; 13057 } 13058 13059 #if __CRTL_VER == 80200000 13060 /* This missed getting in to the DECC SDK for 8.2 */ 13061 char *realpath(const char *file_name, char * resolved_name, ...); 13062 #endif 13063 13064 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/ 13065 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK. 13066 * The perl fallback routine to provide realpath() is not as efficient 13067 * on OpenVMS. 13068 */ 13069 13070 /* Hack, use old stat() as fastest way of getting ino_t and device */ 13071 int decc$stat(const char *name, void * statbuf); 13072 13073 13074 /* Realpath is fragile. In 8.3 it does not work if the feature 13075 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic 13076 * links are implemented in RMS, not the CRTL. It also can fail if the 13077 * user does not have read/execute access to some of the directories. 13078 * So in order for Do What I Mean mode to work, if realpath() fails, 13079 * fall back to looking up the filename by the device name and FID. 13080 */ 13081 13082 int vms_fid_to_name(char * outname, int outlen, const char * name) 13083 { 13084 struct statbuf_t { 13085 char * st_dev; 13086 unsigned short st_ino[3]; 13087 unsigned short padw; 13088 unsigned long padl[30]; /* plenty of room */ 13089 } statbuf; 13090 int sts; 13091 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13092 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0}; 13093 13094 sts = decc$stat(name, &statbuf); 13095 if (sts == 0) { 13096 13097 dvidsc.dsc$a_pointer=statbuf.st_dev; 13098 dvidsc.dsc$w_length=strlen(statbuf.st_dev); 13099 13100 specdsc.dsc$a_pointer = outname; 13101 specdsc.dsc$w_length = outlen-1; 13102 13103 sts = lib$fid_to_name 13104 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length); 13105 if ($VMS_STATUS_SUCCESS(sts)) { 13106 outname[specdsc.dsc$w_length] = 0; 13107 return 0; 13108 } 13109 } 13110 return sts; 13111 } 13112 13113 13114 13115 static char * 13116 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf, 13117 int *utf8_fl) 13118 { 13119 char * rslt = NULL; 13120 13121 #ifdef HAS_SYMLINK 13122 if (decc_posix_compliant_pathnames > 0 ) { 13123 /* realpath currently only works if posix compliant pathnames are 13124 * enabled. It may start working when they are not, but in that 13125 * case we still want the fallback behavior for backwards compatibility 13126 */ 13127 rslt = realpath(filespec, outbuf); 13128 } 13129 #endif 13130 13131 if (rslt == NULL) { 13132 char * vms_spec; 13133 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13134 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13135 int file_len; 13136 13137 /* Fall back to fid_to_name */ 13138 13139 Newx(vms_spec, VMS_MAXRSS + 1, char); 13140 13141 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec); 13142 if (sts == 0) { 13143 13144 13145 /* Now need to trim the version off */ 13146 sts = vms_split_path 13147 (vms_spec, 13148 &v_spec, 13149 &v_len, 13150 &r_spec, 13151 &r_len, 13152 &d_spec, 13153 &d_len, 13154 &n_spec, 13155 &n_len, 13156 &e_spec, 13157 &e_len, 13158 &vs_spec, 13159 &vs_len); 13160 13161 13162 if (sts == 0) { 13163 int haslower = 0; 13164 const char *cp; 13165 13166 /* Trim off the version */ 13167 int file_len = v_len + r_len + d_len + n_len + e_len; 13168 vms_spec[file_len] = 0; 13169 13170 /* The result is expected to be in UNIX format */ 13171 rslt = do_tounixspec(vms_spec, outbuf, 0, utf8_fl); 13172 13173 /* Downcase if input had any lower case letters and 13174 * case preservation is not in effect. 13175 */ 13176 if (!decc_efs_case_preserve) { 13177 for (cp = filespec; *cp; cp++) 13178 if (islower(*cp)) { haslower = 1; break; } 13179 13180 if (haslower) __mystrtolower(rslt); 13181 } 13182 } 13183 } 13184 13185 Safefree(vms_spec); 13186 } 13187 return rslt; 13188 } 13189 13190 static char * 13191 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf, 13192 int *utf8_fl) 13193 { 13194 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec; 13195 int sts, v_len, r_len, d_len, n_len, e_len, vs_len; 13196 int file_len; 13197 13198 /* Fall back to fid_to_name */ 13199 13200 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec); 13201 if (sts != 0) { 13202 return NULL; 13203 } 13204 else { 13205 13206 13207 /* Now need to trim the version off */ 13208 sts = vms_split_path 13209 (outbuf, 13210 &v_spec, 13211 &v_len, 13212 &r_spec, 13213 &r_len, 13214 &d_spec, 13215 &d_len, 13216 &n_spec, 13217 &n_len, 13218 &e_spec, 13219 &e_len, 13220 &vs_spec, 13221 &vs_len); 13222 13223 13224 if (sts == 0) { 13225 int haslower = 0; 13226 const char *cp; 13227 13228 /* Trim off the version */ 13229 int file_len = v_len + r_len + d_len + n_len + e_len; 13230 outbuf[file_len] = 0; 13231 13232 /* Downcase if input had any lower case letters and 13233 * case preservation is not in effect. 13234 */ 13235 if (!decc_efs_case_preserve) { 13236 for (cp = filespec; *cp; cp++) 13237 if (islower(*cp)) { haslower = 1; break; } 13238 13239 if (haslower) __mystrtolower(outbuf); 13240 } 13241 } 13242 } 13243 return outbuf; 13244 } 13245 13246 13247 /*}}}*/ 13248 /* External entry points */ 13249 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13250 { return do_vms_realpath(filespec, outbuf, utf8_fl); } 13251 13252 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl) 13253 { return do_vms_realname(filespec, outbuf, utf8_fl); } 13254 13255 /* case_tolerant */ 13256 13257 /*{{{int do_vms_case_tolerant(void)*/ 13258 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is 13259 * controlled by a process setting. 13260 */ 13261 int do_vms_case_tolerant(void) 13262 { 13263 return vms_process_case_tolerant; 13264 } 13265 /*}}}*/ 13266 /* External entry points */ 13267 #if __CRTL_VER >= 70301000 && !defined(__VAX) 13268 int Perl_vms_case_tolerant(void) 13269 { return do_vms_case_tolerant(); } 13270 #else 13271 int Perl_vms_case_tolerant(void) 13272 { return vms_process_case_tolerant; } 13273 #endif 13274 13275 13276 /* Start of DECC RTL Feature handling */ 13277 13278 static int sys_trnlnm 13279 (const char * logname, 13280 char * value, 13281 int value_len) 13282 { 13283 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV"); 13284 const unsigned long attr = LNM$M_CASE_BLIND; 13285 struct dsc$descriptor_s name_dsc; 13286 int status; 13287 unsigned short result; 13288 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result}, 13289 {0, 0, 0, 0}}; 13290 13291 name_dsc.dsc$w_length = strlen(logname); 13292 name_dsc.dsc$a_pointer = (char *)logname; 13293 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 13294 name_dsc.dsc$b_class = DSC$K_CLASS_S; 13295 13296 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst); 13297 13298 if ($VMS_STATUS_SUCCESS(status)) { 13299 13300 /* Null terminate and return the string */ 13301 /*--------------------------------------*/ 13302 value[result] = 0; 13303 } 13304 13305 return status; 13306 } 13307 13308 static int sys_crelnm 13309 (const char * logname, 13310 const char * value) 13311 { 13312 int ret_val; 13313 const char * proc_table = "LNM$PROCESS_TABLE"; 13314 struct dsc$descriptor_s proc_table_dsc; 13315 struct dsc$descriptor_s logname_dsc; 13316 struct itmlst_3 item_list[2]; 13317 13318 proc_table_dsc.dsc$a_pointer = (char *) proc_table; 13319 proc_table_dsc.dsc$w_length = strlen(proc_table); 13320 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 13321 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S; 13322 13323 logname_dsc.dsc$a_pointer = (char *) logname; 13324 logname_dsc.dsc$w_length = strlen(logname); 13325 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T; 13326 logname_dsc.dsc$b_class = DSC$K_CLASS_S; 13327 13328 item_list[0].buflen = strlen(value); 13329 item_list[0].itmcode = LNM$_STRING; 13330 item_list[0].bufadr = (char *)value; 13331 item_list[0].retlen = NULL; 13332 13333 item_list[1].buflen = 0; 13334 item_list[1].itmcode = 0; 13335 13336 ret_val = sys$crelnm 13337 (NULL, 13338 (const struct dsc$descriptor_s *)&proc_table_dsc, 13339 (const struct dsc$descriptor_s *)&logname_dsc, 13340 NULL, 13341 (const struct item_list_3 *) item_list); 13342 13343 return ret_val; 13344 } 13345 13346 /* C RTL Feature settings */ 13347 13348 static int set_features 13349 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */ 13350 int (* cli_routine)(void), /* Not documented */ 13351 void *image_info) /* Not documented */ 13352 { 13353 int status; 13354 int s; 13355 int dflt; 13356 char* str; 13357 char val_str[10]; 13358 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX) 13359 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM; 13360 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE; 13361 unsigned long case_perm; 13362 unsigned long case_image; 13363 #endif 13364 13365 /* Allow an exception to bring Perl into the VMS debugger */ 13366 vms_debug_on_exception = 0; 13367 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str)); 13368 if ($VMS_STATUS_SUCCESS(status)) { 13369 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13370 vms_debug_on_exception = 1; 13371 else 13372 vms_debug_on_exception = 0; 13373 } 13374 13375 /* Create VTF-7 filenames from Unicode instead of UTF-8 */ 13376 vms_vtf7_filenames = 0; 13377 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str)); 13378 if ($VMS_STATUS_SUCCESS(status)) { 13379 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13380 vms_vtf7_filenames = 1; 13381 else 13382 vms_vtf7_filenames = 0; 13383 } 13384 13385 13386 /* unlink all versions on unlink() or rename() */ 13387 vms_unlink_all_versions = 0; 13388 status = sys_trnlnm 13389 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str)); 13390 if ($VMS_STATUS_SUCCESS(status)) { 13391 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13392 vms_unlink_all_versions = 1; 13393 else 13394 vms_unlink_all_versions = 0; 13395 } 13396 13397 /* Dectect running under GNV Bash or other UNIX like shell */ 13398 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13399 gnv_unix_shell = 0; 13400 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str)); 13401 if ($VMS_STATUS_SUCCESS(status)) { 13402 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13403 gnv_unix_shell = 1; 13404 set_feature_default("DECC$EFS_CASE_PRESERVE", 1); 13405 set_feature_default("DECC$EFS_CHARSET", 1); 13406 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1); 13407 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1); 13408 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1); 13409 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0); 13410 vms_unlink_all_versions = 1; 13411 } 13412 else 13413 gnv_unix_shell = 0; 13414 } 13415 #endif 13416 13417 /* hacks to see if known bugs are still present for testing */ 13418 13419 /* Readdir is returning filenames in VMS syntax always */ 13420 decc_bug_readdir_efs1 = 1; 13421 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str)); 13422 if ($VMS_STATUS_SUCCESS(status)) { 13423 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13424 decc_bug_readdir_efs1 = 1; 13425 else 13426 decc_bug_readdir_efs1 = 0; 13427 } 13428 13429 /* PCP mode requires creating /dev/null special device file */ 13430 decc_bug_devnull = 0; 13431 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str)); 13432 if ($VMS_STATUS_SUCCESS(status)) { 13433 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13434 decc_bug_devnull = 1; 13435 else 13436 decc_bug_devnull = 0; 13437 } 13438 13439 /* fgetname returning a VMS name in UNIX mode */ 13440 decc_bug_fgetname = 1; 13441 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str)); 13442 if ($VMS_STATUS_SUCCESS(status)) { 13443 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13444 decc_bug_fgetname = 1; 13445 else 13446 decc_bug_fgetname = 0; 13447 } 13448 13449 /* UNIX directory names with no paths are broken in a lot of places */ 13450 decc_dir_barename = 1; 13451 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str)); 13452 if ($VMS_STATUS_SUCCESS(status)) { 13453 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) 13454 decc_dir_barename = 1; 13455 else 13456 decc_dir_barename = 0; 13457 } 13458 13459 #if __CRTL_VER >= 70300000 && !defined(__VAX) 13460 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION"); 13461 if (s >= 0) { 13462 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1); 13463 if (decc_disable_to_vms_logname_translation < 0) 13464 decc_disable_to_vms_logname_translation = 0; 13465 } 13466 13467 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE"); 13468 if (s >= 0) { 13469 decc_efs_case_preserve = decc$feature_get_value(s, 1); 13470 if (decc_efs_case_preserve < 0) 13471 decc_efs_case_preserve = 0; 13472 } 13473 13474 s = decc$feature_get_index("DECC$EFS_CHARSET"); 13475 if (s >= 0) { 13476 decc_efs_charset = decc$feature_get_value(s, 1); 13477 if (decc_efs_charset < 0) 13478 decc_efs_charset = 0; 13479 } 13480 13481 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT"); 13482 if (s >= 0) { 13483 decc_filename_unix_report = decc$feature_get_value(s, 1); 13484 if (decc_filename_unix_report > 0) 13485 decc_filename_unix_report = 1; 13486 else 13487 decc_filename_unix_report = 0; 13488 } 13489 13490 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY"); 13491 if (s >= 0) { 13492 decc_filename_unix_only = decc$feature_get_value(s, 1); 13493 if (decc_filename_unix_only > 0) { 13494 decc_filename_unix_only = 1; 13495 } 13496 else { 13497 decc_filename_unix_only = 0; 13498 } 13499 } 13500 13501 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION"); 13502 if (s >= 0) { 13503 decc_filename_unix_no_version = decc$feature_get_value(s, 1); 13504 if (decc_filename_unix_no_version < 0) 13505 decc_filename_unix_no_version = 0; 13506 } 13507 13508 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE"); 13509 if (s >= 0) { 13510 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1); 13511 if (decc_readdir_dropdotnotype < 0) 13512 decc_readdir_dropdotnotype = 0; 13513 } 13514 13515 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str)); 13516 if ($VMS_STATUS_SUCCESS(status)) { 13517 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT"); 13518 if (s >= 0) { 13519 dflt = decc$feature_get_value(s, 4); 13520 if (dflt > 0) { 13521 decc_disable_posix_root = decc$feature_get_value(s, 1); 13522 if (decc_disable_posix_root <= 0) { 13523 decc$feature_set_value(s, 1, 1); 13524 decc_disable_posix_root = 1; 13525 } 13526 } 13527 else { 13528 /* Traditionally Perl assumes this is off */ 13529 decc_disable_posix_root = 1; 13530 decc$feature_set_value(s, 1, 1); 13531 } 13532 } 13533 } 13534 13535 #if __CRTL_VER >= 80200000 13536 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES"); 13537 if (s >= 0) { 13538 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1); 13539 if (decc_posix_compliant_pathnames < 0) 13540 decc_posix_compliant_pathnames = 0; 13541 if (decc_posix_compliant_pathnames > 4) 13542 decc_posix_compliant_pathnames = 0; 13543 } 13544 13545 #endif 13546 #else 13547 status = sys_trnlnm 13548 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str)); 13549 if ($VMS_STATUS_SUCCESS(status)) { 13550 val_str[0] = _toupper(val_str[0]); 13551 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13552 decc_disable_to_vms_logname_translation = 1; 13553 } 13554 } 13555 13556 #ifndef __VAX 13557 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str)); 13558 if ($VMS_STATUS_SUCCESS(status)) { 13559 val_str[0] = _toupper(val_str[0]); 13560 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13561 decc_efs_case_preserve = 1; 13562 } 13563 } 13564 #endif 13565 13566 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str)); 13567 if ($VMS_STATUS_SUCCESS(status)) { 13568 val_str[0] = _toupper(val_str[0]); 13569 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13570 decc_filename_unix_report = 1; 13571 } 13572 } 13573 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str)); 13574 if ($VMS_STATUS_SUCCESS(status)) { 13575 val_str[0] = _toupper(val_str[0]); 13576 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13577 decc_filename_unix_only = 1; 13578 decc_filename_unix_report = 1; 13579 } 13580 } 13581 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str)); 13582 if ($VMS_STATUS_SUCCESS(status)) { 13583 val_str[0] = _toupper(val_str[0]); 13584 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13585 decc_filename_unix_no_version = 1; 13586 } 13587 } 13588 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str)); 13589 if ($VMS_STATUS_SUCCESS(status)) { 13590 val_str[0] = _toupper(val_str[0]); 13591 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) { 13592 decc_readdir_dropdotnotype = 1; 13593 } 13594 } 13595 #endif 13596 13597 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX) 13598 13599 /* Report true case tolerance */ 13600 /*----------------------------*/ 13601 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0); 13602 if (!$VMS_STATUS_SUCCESS(status)) 13603 case_perm = PPROP$K_CASE_BLIND; 13604 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0); 13605 if (!$VMS_STATUS_SUCCESS(status)) 13606 case_image = PPROP$K_CASE_BLIND; 13607 if ((case_perm == PPROP$K_CASE_SENSITIVE) || 13608 (case_image == PPROP$K_CASE_SENSITIVE)) 13609 vms_process_case_tolerant = 0; 13610 13611 #endif 13612 13613 13614 /* CRTL can be initialized past this point, but not before. */ 13615 /* DECC$CRTL_INIT(); */ 13616 13617 return SS$_NORMAL; 13618 } 13619 13620 #ifdef __DECC 13621 #pragma nostandard 13622 #pragma extern_model save 13623 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt 13624 const __align (LONGWORD) int spare[8] = {0}; 13625 13626 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */ 13627 #if __DECC_VER >= 60560002 13628 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long 13629 #else 13630 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long 13631 #endif 13632 #endif /* __DECC */ 13633 13634 const long vms_cc_features = (const long)set_features; 13635 13636 /* 13637 ** Force a reference to LIB$INITIALIZE to ensure it 13638 ** exists in the image. 13639 */ 13640 int lib$initialize(void); 13641 #ifdef __DECC 13642 #pragma extern_model strict_refdef 13643 #endif 13644 int lib_init_ref = (int) lib$initialize; 13645 13646 #ifdef __DECC 13647 #pragma extern_model restore 13648 #pragma standard 13649 #endif 13650 13651 /* End of vms.c */ 13652