3 * VMS-specific routines for perl5
6 * August 2005 Convert VMS status code to UNIX status codes
7 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
8 * and Perl_cando by Craig Berry
9 * 29-Aug-2000 Charles Lane's piping improvements rolled in
10 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
19 #include <climsgdef.h>
29 #include <libclidef.h>
31 #include <lib$routines.h>
34 #if __CRTL_VER >= 70301000 && !defined(__VAX)
44 #include <str$routines.h>
51 /* Set the maximum filespec size here as it is larger for EFS file
53 * Not fully implemented at this time because the larger size
54 * will likely impact the stack local storage requirements of
55 * threaded code, and probably cause hard to diagnose failures.
56 * To implement the larger sizes, all places where filename
57 * storage is put on the stack need to be changed to use
58 * New()/SafeFree() instead.
63 #define VMS_MAXRSS (NAML$C_MAXRSS+1)
64 #ifndef VMS_LONGNAME_SUPPORT
65 #define VMS_LONGNAME_SUPPORT 1
66 #endif /* VMS_LONGNAME_SUPPORT */
67 #endif /* NAML$C_MAXRSS */
68 #endif /* VMS_MAXRSS */
71 /* temporary hack until support is complete */
72 #ifdef VMS_LONGNAME_SUPPORT
73 #undef VMS_LONGNAME_SUPPORT
76 /* end of temporary hack until support is complete */
79 #define VMS_MAXRSS (NAM$C_MAXRSS + 1)
82 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
83 int decc$feature_get_index(const char *name);
84 char* decc$feature_get_name(int index);
85 int decc$feature_get_value(int index, int mode);
86 int decc$feature_set_value(int index, int mode, int value);
91 #if __CRTL_VER >= 70300000 && !defined(__VAX)
93 static int set_feature_default(const char *name, int value)
98 index = decc$feature_get_index(name);
100 status = decc$feature_set_value(index, 1, value);
101 if (index == -1 || (status == -1)) {
105 status = decc$feature_get_value(index, 1);
106 if (status != value) {
114 /* Older versions of ssdef.h don't have these */
115 #ifndef SS$_INVFILFOROP
116 # define SS$_INVFILFOROP 3930
118 #ifndef SS$_NOSUCHOBJECT
119 # define SS$_NOSUCHOBJECT 2696
122 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
123 #define PERLIO_NOT_STDIO 0
125 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
126 * code below needs to get to the underlying CRTL routines. */
127 #define DONT_MASK_RTL_CALLS
131 /* Anticipating future expansion in lexical warnings . . . */
132 #ifndef WARN_INTERNAL
133 # define WARN_INTERNAL WARN_MISC
136 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
137 # define RTL_USES_UTC 1
141 /* gcc's header files don't #define direct access macros
142 * corresponding to VAXC's variant structs */
144 # define uic$v_format uic$r_uic_form.uic$v_format
145 # define uic$v_group uic$r_uic_form.uic$v_group
146 # define uic$v_member uic$r_uic_form.uic$v_member
147 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
148 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
149 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
150 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
153 #if defined(NEED_AN_H_ERRNO)
158 #pragma message disable pragma
159 #pragma member_alignment save
160 #pragma nomember_alignment longword
162 #pragma message disable misalgndmem
165 unsigned short int buflen;
166 unsigned short int itmcode;
168 unsigned short int *retlen;
171 struct filescan_itmlst_2 {
172 unsigned short length;
173 unsigned short itmcode;
178 unsigned short length;
183 #pragma message restore
184 #pragma member_alignment restore
187 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
188 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
189 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
190 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
191 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
192 #define do_vms_realpath(a,b) mp_do_vms_realpath(aTHX_ a,b)
193 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
194 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
195 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
196 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
197 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
199 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts);
200 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts);
201 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
202 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts);
204 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
205 #define PERL_LNM_MAX_ALLOWED_INDEX 127
207 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
208 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
211 #define PERL_LNM_MAX_ITER 10
213 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
214 #if __CRTL_VER >= 70302000 && !defined(__VAX)
215 #define MAX_DCL_SYMBOL (8192)
216 #define MAX_DCL_LINE_LENGTH (4096 - 4)
218 #define MAX_DCL_SYMBOL (1024)
219 #define MAX_DCL_LINE_LENGTH (1024 - 4)
222 static char *__mystrtolower(char *str)
224 if (str) for (; *str; ++str) *str= tolower(*str);
228 static struct dsc$descriptor_s fildevdsc =
229 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
230 static struct dsc$descriptor_s crtlenvdsc =
231 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
232 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
233 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
234 static struct dsc$descriptor_s **env_tables = defenv;
235 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
237 /* True if we shouldn't treat barewords as logicals during directory */
239 static int no_translate_barewords;
242 static int tz_updated = 1;
245 /* DECC Features that may need to affect how Perl interprets
246 * displays filename information
248 static int decc_disable_to_vms_logname_translation = 1;
249 static int decc_disable_posix_root = 1;
250 int decc_efs_case_preserve = 0;
251 static int decc_efs_charset = 0;
252 static int decc_filename_unix_no_version = 0;
253 static int decc_filename_unix_only = 0;
254 int decc_filename_unix_report = 0;
255 int decc_posix_compliant_pathnames = 0;
256 int decc_readdir_dropdotnotype = 0;
257 static int vms_process_case_tolerant = 1;
259 /* bug workarounds if needed */
260 int decc_bug_readdir_efs1 = 0;
261 int decc_bug_devnull = 1;
262 int decc_bug_fgetname = 0;
263 int decc_dir_barename = 0;
265 static int vms_debug_on_exception = 0;
267 /* Is this a UNIX file specification?
268 * No longer a simple check with EFS file specs
269 * For now, not a full check, but need to
270 * handle POSIX ^UP^ specifications
271 * Fixing to handle ^/ cases would require
272 * changes to many other conversion routines.
275 static int is_unix_filespec(const char *path)
281 if (strncmp(path,"\"^UP^",5) != 0) {
282 pch1 = strchr(path, '/');
287 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
288 if (decc_filename_unix_report || decc_filename_unix_only) {
289 if (strcmp(path,".") == 0)
297 /* This handles the expansion of a '^' prefix to the proper character
298 * in a UNIX file specification.
300 * The output count variable contains the number of characters added
301 * to the output string.
303 * The return value is the number of characters read from the input
306 static int copy_expand_vms_filename_escape
307 (char *outspec, const char *inspec, int *output_cnt)
314 if (*inspec == '^') {
318 /* Non trailing dots should just be passed through */
323 case '_': /* space */
329 case 'U': /* Unicode */
332 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
335 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
336 outspec[0] == c1 & 0xff;
337 outspec[1] == c2 & 0xff;
344 /* Error - do best we can to continue */
354 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
358 scnt = sscanf(inspec, "%2x", &c1);
359 outspec[0] = c1 & 0xff;
382 (const struct dsc$descriptor_s * srcstr,
383 struct filescan_itmlst_2 * valuelist,
384 unsigned long * fldflags,
385 struct dsc$descriptor_s *auxout,
386 unsigned short * retlen);
388 /* vms_split_path - Verify that the input file specification is a
389 * VMS format file specification, and provide pointers to the components of
390 * it. With EFS format filenames, this is virtually the only way to
391 * parse a VMS path specification into components.
393 * If the sum of the components do not add up to the length of the
394 * string, then the passed file specification is probably a UNIX style
397 static int vms_split_path
398 (pTHX_ const char * path,
412 struct dsc$descriptor path_desc;
416 struct filescan_itmlst_2 item_list[9];
417 const int filespec = 0;
418 const int nodespec = 1;
419 const int devspec = 2;
420 const int rootspec = 3;
421 const int dirspec = 4;
422 const int namespec = 5;
423 const int typespec = 6;
424 const int verspec = 7;
426 /* Assume the worst for an easy exit */
441 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
442 path_desc.dsc$w_length = strlen(path);
443 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
444 path_desc.dsc$b_class = DSC$K_CLASS_S;
446 /* Get the total length, if it is shorter than the string passed
447 * then this was probably not a VMS formatted file specification
449 item_list[filespec].itmcode = FSCN$_FILESPEC;
450 item_list[filespec].length = 0;
451 item_list[filespec].component = NULL;
453 /* If the node is present, then it gets considered as part of the
454 * volume name to hopefully make things simple.
456 item_list[nodespec].itmcode = FSCN$_NODE;
457 item_list[nodespec].length = 0;
458 item_list[nodespec].component = NULL;
460 item_list[devspec].itmcode = FSCN$_DEVICE;
461 item_list[devspec].length = 0;
462 item_list[devspec].component = NULL;
464 /* root is a special case, adding it to either the directory or
465 * the device components will probalby complicate things for the
466 * callers of this routine, so leave it separate.
468 item_list[rootspec].itmcode = FSCN$_ROOT;
469 item_list[rootspec].length = 0;
470 item_list[rootspec].component = NULL;
472 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
473 item_list[dirspec].length = 0;
474 item_list[dirspec].component = NULL;
476 item_list[namespec].itmcode = FSCN$_NAME;
477 item_list[namespec].length = 0;
478 item_list[namespec].component = NULL;
480 item_list[typespec].itmcode = FSCN$_TYPE;
481 item_list[typespec].length = 0;
482 item_list[typespec].component = NULL;
484 item_list[verspec].itmcode = FSCN$_VERSION;
485 item_list[verspec].length = 0;
486 item_list[verspec].component = NULL;
488 item_list[8].itmcode = 0;
489 item_list[8].length = 0;
490 item_list[8].component = NULL;
492 status = SYS$FILESCAN
493 ((const struct dsc$descriptor_s *)&path_desc, item_list,
495 _ckvmssts(status); /* All failure status values indicate a coding error */
497 /* If we parsed it successfully these two lengths should be the same */
498 if (path_desc.dsc$w_length != item_list[filespec].length)
501 /* If we got here, then it is a VMS file specification */
504 /* set the volume name */
505 if (item_list[nodespec].length > 0) {
506 *volume = item_list[nodespec].component;
507 *vol_len = item_list[nodespec].length + item_list[devspec].length;
510 *volume = item_list[devspec].component;
511 *vol_len = item_list[devspec].length;
514 *root = item_list[rootspec].component;
515 *root_len = item_list[rootspec].length;
517 *dir = item_list[dirspec].component;
518 *dir_len = item_list[dirspec].length;
520 /* Now fun with versions and EFS file specifications
521 * The parser can not tell the difference when a "." is a version
522 * delimiter or a part of the file specification.
524 if ((decc_efs_charset) &&
525 (item_list[verspec].length > 0) &&
526 (item_list[verspec].component[0] == '.')) {
527 *name = item_list[namespec].component;
528 *name_len = item_list[namespec].length + item_list[typespec].length;
529 *ext = item_list[verspec].component;
530 *ext_len = item_list[verspec].length;
535 *name = item_list[namespec].component;
536 *name_len = item_list[namespec].length;
537 *ext = item_list[typespec].component;
538 *ext_len = item_list[typespec].length;
539 *version = item_list[verspec].component;
540 *ver_len = item_list[verspec].length;
547 * Routine to retrieve the maximum equivalence index for an input
548 * logical name. Some calls to this routine have no knowledge if
549 * the variable is a logical or not. So on error we return a max
552 /*{{{int my_maxidx(const char *lnm) */
554 my_maxidx(const char *lnm)
558 int attr = LNM$M_CASE_BLIND;
559 struct dsc$descriptor lnmdsc;
560 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
563 lnmdsc.dsc$w_length = strlen(lnm);
564 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
565 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
566 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
568 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
569 if ((status & 1) == 0)
576 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
578 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
579 struct dsc$descriptor_s **tabvec, unsigned long int flags)
582 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
583 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
584 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
586 unsigned char acmode;
587 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
588 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
589 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
590 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
592 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
593 #if defined(PERL_IMPLICIT_CONTEXT)
596 aTHX = PERL_GET_INTERP;
602 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
603 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
605 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
606 *cp2 = _toupper(*cp1);
607 if (cp1 - lnm > LNM$C_NAMLENGTH) {
608 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
612 lnmdsc.dsc$w_length = cp1 - lnm;
613 lnmdsc.dsc$a_pointer = uplnm;
614 uplnm[lnmdsc.dsc$w_length] = '\0';
615 secure = flags & PERL__TRNENV_SECURE;
616 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
617 if (!tabvec || !*tabvec) tabvec = env_tables;
619 for (curtab = 0; tabvec[curtab]; curtab++) {
620 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
621 if (!ivenv && !secure) {
626 Perl_warn(aTHX_ "Can't read CRTL environ\n");
629 retsts = SS$_NOLOGNAM;
630 for (i = 0; environ[i]; i++) {
631 if ((eq = strchr(environ[i],'=')) &&
632 lnmdsc.dsc$w_length == (eq - environ[i]) &&
633 !strncmp(environ[i],uplnm,eq - environ[i])) {
635 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
636 if (!eqvlen) continue;
641 if (retsts != SS$_NOLOGNAM) break;
644 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
645 !str$case_blind_compare(&tmpdsc,&clisym)) {
646 if (!ivsym && !secure) {
647 unsigned short int deflen = LNM$C_NAMLENGTH;
648 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
649 /* dynamic dsc to accomodate possible long value */
650 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
651 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
653 if (eqvlen > MAX_DCL_SYMBOL) {
654 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
655 eqvlen = MAX_DCL_SYMBOL;
656 /* Special hack--we might be called before the interpreter's */
657 /* fully initialized, in which case either thr or PL_curcop */
658 /* might be bogus. We have to check, since ckWARN needs them */
659 /* both to be valid if running threaded */
660 if (ckWARN(WARN_MISC)) {
661 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
664 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
666 _ckvmssts(lib$sfree1_dd(&eqvdsc));
667 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
668 if (retsts == LIB$_NOSUCHSYM) continue;
673 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
674 midx = my_maxidx(lnm);
675 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
676 lnmlst[1].bufadr = cp2;
678 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
679 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
680 if (retsts == SS$_NOLOGNAM) break;
681 /* PPFs have a prefix */
684 *((int *)uplnm) == *((int *)"SYS$") &&
686 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
687 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
688 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
689 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
690 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
691 memmove(eqv,eqv+4,eqvlen-4);
697 if ((retsts == SS$_IVLOGNAM) ||
698 (retsts == SS$_NOLOGNAM)) { continue; }
701 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
702 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
703 if (retsts == SS$_NOLOGNAM) continue;
706 eqvlen = strlen(eqv);
710 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
711 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
712 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
713 retsts == SS$_NOLOGNAM) {
714 set_errno(EINVAL); set_vaxc_errno(retsts);
716 else _ckvmssts(retsts);
718 } /* end of vmstrnenv */
721 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
722 /* Define as a function so we can access statics. */
723 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
725 return vmstrnenv(lnm,eqv,idx,fildev,
726 #ifdef SECURE_INTERNAL_GETENV
727 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
736 * Note: Uses Perl temp to store result so char * can be returned to
737 * caller; this pointer will be invalidated at next Perl statement
739 * We define this as a function rather than a macro in terms of my_getenv_len()
740 * so that it'll work when PL_curinterp is undefined (and we therefore can't
743 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
745 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
748 static char *__my_getenv_eqv = NULL;
749 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
750 unsigned long int idx = 0;
751 int trnsuccess, success, secure, saverr, savvmserr;
755 midx = my_maxidx(lnm) + 1;
757 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
758 /* Set up a temporary buffer for the return value; Perl will
759 * clean it up at the next statement transition */
760 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
761 if (!tmpsv) return NULL;
765 /* Assume no interpreter ==> single thread */
766 if (__my_getenv_eqv != NULL) {
767 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
770 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
772 eqv = __my_getenv_eqv;
775 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
776 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
778 getcwd(eqv,LNM$C_NAMLENGTH);
782 /* Get rid of "000000/ in rooted filespecs */
785 zeros = strstr(eqv, "/000000/");
788 mlen = len - (zeros - eqv) - 7;
789 memmove(zeros, &zeros[7], mlen);
797 /* Impose security constraints only if tainting */
799 /* Impose security constraints only if tainting */
800 secure = PL_curinterp ? PL_tainting : will_taint;
801 saverr = errno; savvmserr = vaxc$errno;
808 #ifdef SECURE_INTERNAL_GETENV
809 secure ? PERL__TRNENV_SECURE : 0
815 /* For the getenv interface we combine all the equivalence names
816 * of a search list logical into one value to acquire a maximum
817 * value length of 255*128 (assuming %ENV is using logicals).
819 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
821 /* If the name contains a semicolon-delimited index, parse it
822 * off and make sure we only retrieve the equivalence name for
824 if ((cp2 = strchr(lnm,';')) != NULL) {
826 uplnm[cp2-lnm] = '\0';
827 idx = strtoul(cp2+1,NULL,0);
829 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
832 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
834 /* Discard NOLOGNAM on internal calls since we're often looking
835 * for an optional name, and this "error" often shows up as the
836 * (bogus) exit status for a die() call later on. */
837 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
838 return success ? eqv : Nullch;
841 } /* end of my_getenv() */
845 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
847 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
851 unsigned long idx = 0;
853 static char *__my_getenv_len_eqv = NULL;
854 int secure, saverr, savvmserr;
857 midx = my_maxidx(lnm) + 1;
859 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
860 /* Set up a temporary buffer for the return value; Perl will
861 * clean it up at the next statement transition */
862 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
863 if (!tmpsv) return NULL;
867 /* Assume no interpreter ==> single thread */
868 if (__my_getenv_len_eqv != NULL) {
869 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
872 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
874 buf = __my_getenv_len_eqv;
877 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
878 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
881 getcwd(buf,LNM$C_NAMLENGTH);
884 /* Get rid of "000000/ in rooted filespecs */
886 zeros = strstr(buf, "/000000/");
889 mlen = *len - (zeros - buf) - 7;
890 memmove(zeros, &zeros[7], mlen);
899 /* Impose security constraints only if tainting */
900 secure = PL_curinterp ? PL_tainting : will_taint;
901 saverr = errno; savvmserr = vaxc$errno;
908 #ifdef SECURE_INTERNAL_GETENV
909 secure ? PERL__TRNENV_SECURE : 0
915 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
917 if ((cp2 = strchr(lnm,';')) != NULL) {
920 idx = strtoul(cp2+1,NULL,0);
922 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
925 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
927 /* Get rid of "000000/ in rooted filespecs */
930 zeros = strstr(buf, "/000000/");
933 mlen = *len - (zeros - buf) - 7;
934 memmove(zeros, &zeros[7], mlen);
940 /* Discard NOLOGNAM on internal calls since we're often looking
941 * for an optional name, and this "error" often shows up as the
942 * (bogus) exit status for a die() call later on. */
943 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
944 return *len ? buf : Nullch;
947 } /* end of my_getenv_len() */
950 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
952 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
954 /*{{{ void prime_env_iter() */
957 /* Fill the %ENV associative array with all logical names we can
958 * find, in preparation for iterating over it.
961 static int primed = 0;
962 HV *seenhv = NULL, *envhv;
964 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
965 unsigned short int chan;
966 #ifndef CLI$M_TRUSTED
967 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
969 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
970 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
972 bool have_sym = FALSE, have_lnm = FALSE;
973 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
974 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
975 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
976 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
977 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
978 #if defined(PERL_IMPLICIT_CONTEXT)
981 #if defined(USE_ITHREADS)
982 static perl_mutex primenv_mutex;
983 MUTEX_INIT(&primenv_mutex);
986 #if defined(PERL_IMPLICIT_CONTEXT)
987 /* We jump through these hoops because we can be called at */
988 /* platform-specific initialization time, which is before anything is */
989 /* set up--we can't even do a plain dTHX since that relies on the */
990 /* interpreter structure to be initialized */
992 aTHX = PERL_GET_INTERP;
998 if (primed || !PL_envgv) return;
999 MUTEX_LOCK(&primenv_mutex);
1000 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1001 envhv = GvHVn(PL_envgv);
1002 /* Perform a dummy fetch as an lval to insure that the hash table is
1003 * set up. Otherwise, the hv_store() will turn into a nullop. */
1004 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1006 for (i = 0; env_tables[i]; i++) {
1007 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1008 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1009 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1011 if (have_sym || have_lnm) {
1012 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1013 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1014 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1015 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1018 for (i--; i >= 0; i--) {
1019 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1022 for (j = 0; environ[j]; j++) {
1023 if (!(start = strchr(environ[j],'='))) {
1024 if (ckWARN(WARN_INTERNAL))
1025 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1029 sv = newSVpv(start,0);
1031 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1036 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1037 !str$case_blind_compare(&tmpdsc,&clisym)) {
1038 strcpy(cmd,"Show Symbol/Global *");
1039 cmddsc.dsc$w_length = 20;
1040 if (env_tables[i]->dsc$w_length == 12 &&
1041 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1042 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1043 flags = defflags | CLI$M_NOLOGNAM;
1046 strcpy(cmd,"Show Logical *");
1047 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1048 strcat(cmd," /Table=");
1049 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1050 cmddsc.dsc$w_length = strlen(cmd);
1052 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1053 flags = defflags | CLI$M_NOCLISYM;
1056 /* Create a new subprocess to execute each command, to exclude the
1057 * remote possibility that someone could subvert a mbx or file used
1058 * to write multiple commands to a single subprocess.
1061 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1062 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1063 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1064 defflags &= ~CLI$M_TRUSTED;
1065 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1067 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1068 if (seenhv) SvREFCNT_dec(seenhv);
1071 char *cp1, *cp2, *key;
1072 unsigned long int sts, iosb[2], retlen, keylen;
1075 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1076 if (sts & 1) sts = iosb[0] & 0xffff;
1077 if (sts == SS$_ENDOFFILE) {
1079 while (substs == 0) { sys$hiber(); wakect++;}
1080 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1085 retlen = iosb[0] >> 16;
1086 if (!retlen) continue; /* blank line */
1088 if (iosb[1] != subpid) {
1090 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1094 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1095 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1097 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1098 if (*cp1 == '(' || /* Logical name table name */
1099 *cp1 == '=' /* Next eqv of searchlist */) continue;
1100 if (*cp1 == '"') cp1++;
1101 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1102 key = cp1; keylen = cp2 - cp1;
1103 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1104 while (*cp2 && *cp2 != '=') cp2++;
1105 while (*cp2 && *cp2 == '=') cp2++;
1106 while (*cp2 && *cp2 == ' ') cp2++;
1107 if (*cp2 == '"') { /* String translation; may embed "" */
1108 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1109 cp2++; cp1--; /* Skip "" surrounding translation */
1111 else { /* Numeric translation */
1112 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1113 cp1--; /* stop on last non-space char */
1115 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1116 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1119 PERL_HASH(hash,key,keylen);
1121 if (cp1 == cp2 && *cp2 == '.') {
1122 /* A single dot usually means an unprintable character, such as a null
1123 * to indicate a zero-length value. Get the actual value to make sure.
1125 char lnm[LNM$C_NAMLENGTH+1];
1126 char eqv[MAX_DCL_SYMBOL+1];
1127 strncpy(lnm, key, keylen);
1128 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1129 sv = newSVpvn(eqv, strlen(eqv));
1132 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1136 hv_store(envhv,key,keylen,sv,hash);
1137 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1139 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1140 /* get the PPFs for this process, not the subprocess */
1141 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1142 char eqv[LNM$C_NAMLENGTH+1];
1144 for (i = 0; ppfs[i]; i++) {
1145 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1146 sv = newSVpv(eqv,trnlen);
1148 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1153 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1154 if (buf) Safefree(buf);
1155 if (seenhv) SvREFCNT_dec(seenhv);
1156 MUTEX_UNLOCK(&primenv_mutex);
1159 } /* end of prime_env_iter */
1163 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1164 /* Define or delete an element in the same "environment" as
1165 * vmstrnenv(). If an element is to be deleted, it's removed from
1166 * the first place it's found. If it's to be set, it's set in the
1167 * place designated by the first element of the table vector.
1168 * Like setenv() returns 0 for success, non-zero on error.
1171 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1174 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1175 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1177 unsigned long int retsts, usermode = PSL$C_USER;
1178 struct itmlst_3 *ile, *ilist;
1179 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1180 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1181 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1182 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1183 $DESCRIPTOR(local,"_LOCAL");
1186 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1187 return SS$_IVLOGNAM;
1190 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1191 *cp2 = _toupper(*cp1);
1192 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1193 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1194 return SS$_IVLOGNAM;
1197 lnmdsc.dsc$w_length = cp1 - lnm;
1198 if (!tabvec || !*tabvec) tabvec = env_tables;
1200 if (!eqv) { /* we're deleting n element */
1201 for (curtab = 0; tabvec[curtab]; curtab++) {
1202 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1204 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1205 if ((cp1 = strchr(environ[i],'=')) &&
1206 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1207 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1209 return setenv(lnm,"",1) ? vaxc$errno : 0;
1212 ivenv = 1; retsts = SS$_NOLOGNAM;
1214 if (ckWARN(WARN_INTERNAL))
1215 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1216 ivenv = 1; retsts = SS$_NOSUCHPGM;
1222 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1223 !str$case_blind_compare(&tmpdsc,&clisym)) {
1224 unsigned int symtype;
1225 if (tabvec[curtab]->dsc$w_length == 12 &&
1226 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1227 !str$case_blind_compare(&tmpdsc,&local))
1228 symtype = LIB$K_CLI_LOCAL_SYM;
1229 else symtype = LIB$K_CLI_GLOBAL_SYM;
1230 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1231 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1232 if (retsts == LIB$_NOSUCHSYM) continue;
1236 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1237 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1238 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1239 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1240 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1244 else { /* we're defining a value */
1245 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1247 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1249 if (ckWARN(WARN_INTERNAL))
1250 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1251 retsts = SS$_NOSUCHPGM;
1255 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1256 eqvdsc.dsc$w_length = strlen(eqv);
1257 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1258 !str$case_blind_compare(&tmpdsc,&clisym)) {
1259 unsigned int symtype;
1260 if (tabvec[0]->dsc$w_length == 12 &&
1261 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1262 !str$case_blind_compare(&tmpdsc,&local))
1263 symtype = LIB$K_CLI_LOCAL_SYM;
1264 else symtype = LIB$K_CLI_GLOBAL_SYM;
1265 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1268 if (!*eqv) eqvdsc.dsc$w_length = 1;
1269 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1271 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1272 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1273 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1274 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1275 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1276 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1279 Newx(ilist,nseg+1,struct itmlst_3);
1282 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1285 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1287 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1288 ile->itmcode = LNM$_STRING;
1290 if ((j+1) == nseg) {
1291 ile->buflen = strlen(c);
1292 /* in case we are truncating one that's too long */
1293 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1296 ile->buflen = LNM$C_NAMLENGTH;
1300 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1304 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1309 if (!(retsts & 1)) {
1311 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1312 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1313 set_errno(EVMSERR); break;
1314 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1315 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1316 set_errno(EINVAL); break;
1323 set_vaxc_errno(retsts);
1324 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1327 /* We reset error values on success because Perl does an hv_fetch()
1328 * before each hv_store(), and if the thing we're setting didn't
1329 * previously exist, we've got a leftover error message. (Of course,
1330 * this fails in the face of
1331 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1332 * in that the error reported in $! isn't spurious,
1333 * but it's right more often than not.)
1335 set_errno(0); set_vaxc_errno(retsts);
1339 } /* end of vmssetenv() */
1342 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1343 /* This has to be a function since there's a prototype for it in proto.h */
1345 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1348 int len = strlen(lnm);
1352 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1353 if (!strcmp(uplnm,"DEFAULT")) {
1354 if (eqv && *eqv) my_chdir(eqv);
1358 #ifndef RTL_USES_UTC
1359 if (len == 6 || len == 2) {
1362 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1364 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1365 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1369 (void) vmssetenv(lnm,eqv,NULL);
1373 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1375 * sets a user-mode logical in the process logical name table
1376 * used for redirection of sys$error
1379 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1381 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1382 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1383 unsigned long int iss, attr = LNM$M_CONFINE;
1384 unsigned char acmode = PSL$C_USER;
1385 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1387 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1388 d_name.dsc$w_length = strlen(name);
1390 lnmlst[0].buflen = strlen(eqv);
1391 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1393 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1394 if (!(iss&1)) lib$signal(iss);
1399 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1400 /* my_crypt - VMS password hashing
1401 * my_crypt() provides an interface compatible with the Unix crypt()
1402 * C library function, and uses sys$hash_password() to perform VMS
1403 * password hashing. The quadword hashed password value is returned
1404 * as a NUL-terminated 8 character string. my_crypt() does not change
1405 * the case of its string arguments; in order to match the behavior
1406 * of LOGINOUT et al., alphabetic characters in both arguments must
1407 * be upcased by the caller.
1409 * - fix me to call ACM services when available
1412 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1414 # ifndef UAI$C_PREFERRED_ALGORITHM
1415 # define UAI$C_PREFERRED_ALGORITHM 127
1417 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1418 unsigned short int salt = 0;
1419 unsigned long int sts;
1421 unsigned short int dsc$w_length;
1422 unsigned char dsc$b_type;
1423 unsigned char dsc$b_class;
1424 const char * dsc$a_pointer;
1425 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1426 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1427 struct itmlst_3 uailst[3] = {
1428 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1429 { sizeof salt, UAI$_SALT, &salt, 0},
1430 { 0, 0, NULL, NULL}};
1431 static char hash[9];
1433 usrdsc.dsc$w_length = strlen(usrname);
1434 usrdsc.dsc$a_pointer = usrname;
1435 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1437 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1441 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1446 set_vaxc_errno(sts);
1447 if (sts != RMS$_RNF) return NULL;
1450 txtdsc.dsc$w_length = strlen(textpasswd);
1451 txtdsc.dsc$a_pointer = textpasswd;
1452 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1453 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1456 return (char *) hash;
1458 } /* end of my_crypt() */
1462 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1463 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1464 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1466 /* fixup barenames that are directories for internal use.
1467 * There have been problems with the consistent handling of UNIX
1468 * style directory names when routines are presented with a name that
1469 * has no directory delimitors at all. So this routine will eventually
1472 static char * fixup_bare_dirnames(const char * name)
1474 if (decc_disable_to_vms_logname_translation) {
1481 * A little hack to get around a bug in some implemenation of remove()
1482 * that do not know how to delete a directory
1484 * Delete any file to which user has control access, regardless of whether
1485 * delete access is explicitly allowed.
1486 * Limitations: User must have write access to parent directory.
1487 * Does not block signals or ASTs; if interrupted in midstream
1488 * may leave file with an altered ACL.
1491 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1493 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1495 char *vmsname, *rspec;
1497 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1498 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1499 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1501 unsigned char myace$b_length;
1502 unsigned char myace$b_type;
1503 unsigned short int myace$w_flags;
1504 unsigned long int myace$l_access;
1505 unsigned long int myace$l_ident;
1506 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1507 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1508 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1510 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1511 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1512 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1513 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1514 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1515 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1517 /* Expand the input spec using RMS, since the CRTL remove() and
1518 * system services won't do this by themselves, so we may miss
1519 * a file "hiding" behind a logical name or search list. */
1520 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1521 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
1523 if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1524 PerlMem_free(vmsname);
1528 if (decc_posix_compliant_pathnames) {
1529 /* In POSIX mode, we prefer to remove the UNIX name */
1531 remove_name = (char *)name;
1534 rspec = PerlMem_malloc(NAM$C_MAXRSS+1);
1535 if (rspec == NULL) _ckvmssts(SS$_INSFMEM);
1536 if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1537 PerlMem_free(rspec);
1538 PerlMem_free(vmsname);
1541 PerlMem_free(vmsname);
1542 remove_name = rspec;
1545 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1547 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1548 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1549 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1551 do_pathify_dirspec(name, remove_name, 0);
1552 if (!rmdir(remove_name)) {
1554 PerlMem_free(remove_name);
1555 PerlMem_free(rspec);
1556 return 0; /* Can we just get rid of it? */
1560 if (!rmdir(remove_name)) {
1561 PerlMem_free(rspec);
1562 return 0; /* Can we just get rid of it? */
1568 if (!remove(remove_name)) {
1569 PerlMem_free(rspec);
1570 return 0; /* Can we just get rid of it? */
1573 /* If not, can changing protections help? */
1574 if (vaxc$errno != RMS$_PRV) {
1575 PerlMem_free(rspec);
1579 /* No, so we get our own UIC to use as a rights identifier,
1580 * and the insert an ACE at the head of the ACL which allows us
1581 * to delete the file.
1583 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1584 fildsc.dsc$w_length = strlen(rspec);
1585 fildsc.dsc$a_pointer = rspec;
1587 newace.myace$l_ident = oldace.myace$l_ident;
1588 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1590 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1591 set_errno(ENOENT); break;
1593 set_errno(ENOTDIR); break;
1595 set_errno(ENODEV); break;
1596 case RMS$_SYN: case SS$_INVFILFOROP:
1597 set_errno(EINVAL); break;
1599 set_errno(EACCES); break;
1603 set_vaxc_errno(aclsts);
1604 PerlMem_free(rspec);
1607 /* Grab any existing ACEs with this identifier in case we fail */
1608 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1609 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1610 || fndsts == SS$_NOMOREACE ) {
1611 /* Add the new ACE . . . */
1612 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1615 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1617 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1618 remove_name = PerlMem_malloc(NAM$C_MAXRSS+1);
1619 if (remove_name == NULL) _ckvmssts(SS$_INSFMEM);
1621 do_pathify_dirspec(name, remove_name, 0);
1622 rmsts = rmdir(remove_name);
1623 PerlMem_free(remove_name);
1626 rmsts = rmdir(remove_name);
1630 rmsts = remove(remove_name);
1632 /* We blew it - dir with files in it, no write priv for
1633 * parent directory, etc. Put things back the way they were. */
1634 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1637 addlst[0].bufadr = &oldace;
1638 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1645 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1646 /* We just deleted it, so of course it's not there. Some versions of
1647 * VMS seem to return success on the unlock operation anyhow (after all
1648 * the unlock is successful), but others don't.
1650 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1651 if (aclsts & 1) aclsts = fndsts;
1652 if (!(aclsts & 1)) {
1654 set_vaxc_errno(aclsts);
1655 PerlMem_free(rspec);
1659 PerlMem_free(rspec);
1662 } /* end of kill_file() */
1666 /*{{{int do_rmdir(char *name)*/
1668 Perl_do_rmdir(pTHX_ const char *name)
1670 char dirfile[NAM$C_MAXRSS+1];
1674 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1675 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1676 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1679 } /* end of do_rmdir */
1683 * Delete any file to which user has control access, regardless of whether
1684 * delete access is explicitly allowed.
1685 * Limitations: User must have write access to parent directory.
1686 * Does not block signals or ASTs; if interrupted in midstream
1687 * may leave file with an altered ACL.
1690 /*{{{int kill_file(char *name)*/
1692 Perl_kill_file(pTHX_ const char *name)
1694 char rspec[NAM$C_MAXRSS+1];
1696 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1697 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1698 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1700 unsigned char myace$b_length;
1701 unsigned char myace$b_type;
1702 unsigned short int myace$w_flags;
1703 unsigned long int myace$l_access;
1704 unsigned long int myace$l_ident;
1705 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1706 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1707 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1709 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1710 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1711 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1712 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1713 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1714 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1716 /* Expand the input spec using RMS, since the CRTL remove() and
1717 * system services won't do this by themselves, so we may miss
1718 * a file "hiding" behind a logical name or search list. */
1719 tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS);
1720 if (tspec == NULL) return -1;
1721 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1722 /* If not, can changing protections help? */
1723 if (vaxc$errno != RMS$_PRV) return -1;
1725 /* No, so we get our own UIC to use as a rights identifier,
1726 * and the insert an ACE at the head of the ACL which allows us
1727 * to delete the file.
1729 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1730 fildsc.dsc$w_length = strlen(rspec);
1731 fildsc.dsc$a_pointer = rspec;
1733 newace.myace$l_ident = oldace.myace$l_ident;
1734 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1736 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1737 set_errno(ENOENT); break;
1739 set_errno(ENOTDIR); break;
1741 set_errno(ENODEV); break;
1742 case RMS$_SYN: case SS$_INVFILFOROP:
1743 set_errno(EINVAL); break;
1745 set_errno(EACCES); break;
1749 set_vaxc_errno(aclsts);
1752 /* Grab any existing ACEs with this identifier in case we fail */
1753 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1754 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1755 || fndsts == SS$_NOMOREACE ) {
1756 /* Add the new ACE . . . */
1757 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1759 if ((rmsts = remove(name))) {
1760 /* We blew it - dir with files in it, no write priv for
1761 * parent directory, etc. Put things back the way they were. */
1762 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1765 addlst[0].bufadr = &oldace;
1766 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1773 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1774 /* We just deleted it, so of course it's not there. Some versions of
1775 * VMS seem to return success on the unlock operation anyhow (after all
1776 * the unlock is successful), but others don't.
1778 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1779 if (aclsts & 1) aclsts = fndsts;
1780 if (!(aclsts & 1)) {
1782 set_vaxc_errno(aclsts);
1788 } /* end of kill_file() */
1792 /*{{{int my_mkdir(char *,Mode_t)*/
1794 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1796 STRLEN dirlen = strlen(dir);
1798 /* zero length string sometimes gives ACCVIO */
1799 if (dirlen == 0) return -1;
1801 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1802 * null file name/type. However, it's commonplace under Unix,
1803 * so we'll allow it for a gain in portability.
1805 if (dir[dirlen-1] == '/') {
1806 char *newdir = savepvn(dir,dirlen-1);
1807 int ret = mkdir(newdir,mode);
1811 else return mkdir(dir,mode);
1812 } /* end of my_mkdir */
1815 /*{{{int my_chdir(char *)*/
1817 Perl_my_chdir(pTHX_ const char *dir)
1819 STRLEN dirlen = strlen(dir);
1821 /* zero length string sometimes gives ACCVIO */
1822 if (dirlen == 0) return -1;
1825 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
1826 * This does not work if DECC$EFS_CHARSET is active. Hack it here
1827 * so that existing scripts do not need to be changed.
1830 while ((dirlen > 0) && (*dir1 == ' ')) {
1835 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1837 * null file name/type. However, it's commonplace under Unix,
1838 * so we'll allow it for a gain in portability.
1840 * - Preview- '/' will be valid soon on VMS
1842 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
1843 char *newdir = savepvn(dir1,dirlen-1);
1844 int ret = chdir(newdir);
1848 else return chdir(dir1);
1849 } /* end of my_chdir */
1853 /*{{{FILE *my_tmpfile()*/
1860 if ((fp = tmpfile())) return fp;
1862 cp = PerlMem_malloc(L_tmpnam+24);
1863 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1865 if (decc_filename_unix_only == 0)
1866 strcpy(cp,"Sys$Scratch:");
1869 tmpnam(cp+strlen(cp));
1870 strcat(cp,".Perltmp");
1871 fp = fopen(cp,"w+","fop=dlt");
1878 #ifndef HOMEGROWN_POSIX_SIGNALS
1880 * The C RTL's sigaction fails to check for invalid signal numbers so we
1881 * help it out a bit. The docs are correct, but the actual routine doesn't
1882 * do what the docs say it will.
1884 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1886 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1887 struct sigaction* oact)
1889 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1890 SETERRNO(EINVAL, SS$_INVARG);
1893 return sigaction(sig, act, oact);
1898 #ifdef KILL_BY_SIGPRC
1899 #include <errnodef.h>
1901 /* We implement our own kill() using the undocumented system service
1902 sys$sigprc for one of two reasons:
1904 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1905 target process to do a sys$exit, which usually can't be handled
1906 gracefully...certainly not by Perl and the %SIG{} mechanism.
1908 2.) If the kill() in the CRTL can't be called from a signal
1909 handler without disappearing into the ether, i.e., the signal
1910 it purportedly sends is never trapped. Still true as of VMS 7.3.
1912 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1913 in the target process rather than calling sys$exit.
1915 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1916 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1917 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1918 with condition codes C$_SIG0+nsig*8, catching the exception on the
1919 target process and resignaling with appropriate arguments.
1921 But we don't have that VMS 7.0+ exception handler, so if you
1922 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1924 Also note that SIGTERM is listed in the docs as being "unimplemented",
1925 yet always seems to be signaled with a VMS condition code of 4 (and
1926 correctly handled for that code). So we hardwire it in.
1928 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1929 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1930 than signalling with an unrecognized (and unhandled by CRTL) code.
1933 #define _MY_SIG_MAX 17
1936 Perl_sig_to_vmscondition_int(int sig)
1938 static unsigned int sig_code[_MY_SIG_MAX+1] =
1941 SS$_HANGUP, /* 1 SIGHUP */
1942 SS$_CONTROLC, /* 2 SIGINT */
1943 SS$_CONTROLY, /* 3 SIGQUIT */
1944 SS$_RADRMOD, /* 4 SIGILL */
1945 SS$_BREAK, /* 5 SIGTRAP */
1946 SS$_OPCCUS, /* 6 SIGABRT */
1947 SS$_COMPAT, /* 7 SIGEMT */
1949 SS$_FLTOVF, /* 8 SIGFPE VAX */
1951 SS$_HPARITH, /* 8 SIGFPE AXP */
1953 SS$_ABORT, /* 9 SIGKILL */
1954 SS$_ACCVIO, /* 10 SIGBUS */
1955 SS$_ACCVIO, /* 11 SIGSEGV */
1956 SS$_BADPARAM, /* 12 SIGSYS */
1957 SS$_NOMBX, /* 13 SIGPIPE */
1958 SS$_ASTFLT, /* 14 SIGALRM */
1964 #if __VMS_VER >= 60200000
1965 static int initted = 0;
1968 sig_code[16] = C$_SIGUSR1;
1969 sig_code[17] = C$_SIGUSR2;
1973 if (sig < _SIG_MIN) return 0;
1974 if (sig > _MY_SIG_MAX) return 0;
1975 return sig_code[sig];
1979 Perl_sig_to_vmscondition(int sig)
1982 if (vms_debug_on_exception != 0)
1983 lib$signal(SS$_DEBUG);
1985 return Perl_sig_to_vmscondition_int(sig);
1990 Perl_my_kill(int pid, int sig)
1995 int sys$sigprc(unsigned int *pidadr,
1996 struct dsc$descriptor_s *prcname,
1999 /* sig 0 means validate the PID */
2000 /*------------------------------*/
2002 const unsigned long int jpicode = JPI$_PID;
2005 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2006 if ($VMS_STATUS_SUCCESS(status))
2009 case SS$_NOSUCHNODE:
2010 case SS$_UNREACHABLE:
2024 code = Perl_sig_to_vmscondition_int(sig);
2027 SETERRNO(EINVAL, SS$_BADPARAM);
2031 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2032 * signals are to be sent to multiple processes.
2033 * pid = 0 - all processes in group except ones that the system exempts
2034 * pid = -1 - all processes except ones that the system exempts
2035 * pid = -n - all processes in group (abs(n)) except ...
2036 * For now, just report as not supported.
2040 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2044 iss = sys$sigprc((unsigned int *)&pid,0,code);
2045 if (iss&1) return 0;
2049 set_errno(EPERM); break;
2051 case SS$_NOSUCHNODE:
2052 case SS$_UNREACHABLE:
2053 set_errno(ESRCH); break;
2055 set_errno(ENOMEM); break;
2060 set_vaxc_errno(iss);
2066 /* Routine to convert a VMS status code to a UNIX status code.
2067 ** More tricky than it appears because of conflicting conventions with
2070 ** VMS status codes are a bit mask, with the least significant bit set for
2073 ** Special UNIX status of EVMSERR indicates that no translation is currently
2074 ** available, and programs should check the VMS status code.
2076 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2080 #ifndef C_FACILITY_NO
2081 #define C_FACILITY_NO 0x350000
2084 #define DCL_IVVERB 0x38090
2087 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2095 /* Assume the best or the worst */
2096 if (vms_status & STS$M_SUCCESS)
2099 unix_status = EVMSERR;
2101 msg_status = vms_status & ~STS$M_CONTROL;
2103 facility = vms_status & STS$M_FAC_NO;
2104 fac_sp = vms_status & STS$M_FAC_SP;
2105 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2107 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2113 unix_status = EFAULT;
2115 case SS$_DEVOFFLINE:
2116 unix_status = EBUSY;
2119 unix_status = ENOTCONN;
2127 case SS$_INVFILFOROP:
2131 unix_status = EINVAL;
2133 case SS$_UNSUPPORTED:
2134 unix_status = ENOTSUP;
2139 unix_status = EACCES;
2141 case SS$_DEVICEFULL:
2142 unix_status = ENOSPC;
2145 unix_status = ENODEV;
2147 case SS$_NOSUCHFILE:
2148 case SS$_NOSUCHOBJECT:
2149 unix_status = ENOENT;
2151 case SS$_ABORT: /* Fatal case */
2152 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2153 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2154 unix_status = EINTR;
2157 unix_status = E2BIG;
2160 unix_status = ENOMEM;
2163 unix_status = EPERM;
2165 case SS$_NOSUCHNODE:
2166 case SS$_UNREACHABLE:
2167 unix_status = ESRCH;
2170 unix_status = ECHILD;
2173 if ((facility == 0) && (msg_no < 8)) {
2174 /* These are not real VMS status codes so assume that they are
2175 ** already UNIX status codes
2177 unix_status = msg_no;
2183 /* Translate a POSIX exit code to a UNIX exit code */
2184 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2185 unix_status = (msg_no & 0x07F8) >> 3;
2189 /* Documented traditional behavior for handling VMS child exits */
2190 /*--------------------------------------------------------------*/
2191 if (child_flag != 0) {
2193 /* Success / Informational return 0 */
2194 /*----------------------------------*/
2195 if (msg_no & STS$K_SUCCESS)
2198 /* Warning returns 1 */
2199 /*-------------------*/
2200 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2203 /* Everything else pass through the severity bits */
2204 /*------------------------------------------------*/
2205 return (msg_no & STS$M_SEVERITY);
2208 /* Normal VMS status to ERRNO mapping attempt */
2209 /*--------------------------------------------*/
2210 switch(msg_status) {
2211 /* case RMS$_EOF: */ /* End of File */
2212 case RMS$_FNF: /* File Not Found */
2213 case RMS$_DNF: /* Dir Not Found */
2214 unix_status = ENOENT;
2216 case RMS$_RNF: /* Record Not Found */
2217 unix_status = ESRCH;
2220 unix_status = ENOTDIR;
2223 unix_status = ENODEV;
2228 unix_status = EBADF;
2231 unix_status = EEXIST;
2235 case LIB$_INVSTRDES:
2237 case LIB$_NOSUCHSYM:
2238 case LIB$_INVSYMNAM:
2240 unix_status = EINVAL;
2246 unix_status = E2BIG;
2248 case RMS$_PRV: /* No privilege */
2249 case RMS$_ACC: /* ACP file access failed */
2250 case RMS$_WLK: /* Device write locked */
2251 unix_status = EACCES;
2253 /* case RMS$_NMF: */ /* No more files */
2261 /* Try to guess at what VMS error status should go with a UNIX errno
2262 * value. This is hard to do as there could be many possible VMS
2263 * error statuses that caused the errno value to be set.
2266 int Perl_unix_status_to_vms(int unix_status)
2268 int test_unix_status;
2270 /* Trivial cases first */
2271 /*---------------------*/
2272 if (unix_status == EVMSERR)
2275 /* Is vaxc$errno sane? */
2276 /*---------------------*/
2277 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2278 if (test_unix_status == unix_status)
2281 /* If way out of range, must be VMS code already */
2282 /*-----------------------------------------------*/
2283 if (unix_status > EVMSERR)
2286 /* If out of range, punt */
2287 /*-----------------------*/
2288 if (unix_status > __ERRNO_MAX)
2292 /* Ok, now we have to do it the hard way. */
2293 /*----------------------------------------*/
2294 switch(unix_status) {
2295 case 0: return SS$_NORMAL;
2296 case EPERM: return SS$_NOPRIV;
2297 case ENOENT: return SS$_NOSUCHOBJECT;
2298 case ESRCH: return SS$_UNREACHABLE;
2299 case EINTR: return SS$_ABORT;
2302 case E2BIG: return SS$_BUFFEROVF;
2304 case EBADF: return RMS$_IFI;
2305 case ECHILD: return SS$_NONEXPR;
2307 case ENOMEM: return SS$_INSFMEM;
2308 case EACCES: return SS$_FILACCERR;
2309 case EFAULT: return SS$_ACCVIO;
2311 case EBUSY: return SS$_DEVOFFLINE;
2312 case EEXIST: return RMS$_FEX;
2314 case ENODEV: return SS$_NOSUCHDEV;
2315 case ENOTDIR: return RMS$_DIR;
2317 case EINVAL: return SS$_INVARG;
2323 case ENOSPC: return SS$_DEVICEFULL;
2324 case ESPIPE: return LIB$_INVARG;
2329 case ERANGE: return LIB$_INVARG;
2330 /* case EWOULDBLOCK */
2331 /* case EINPROGRESS */
2334 /* case EDESTADDRREQ */
2336 /* case EPROTOTYPE */
2337 /* case ENOPROTOOPT */
2338 /* case EPROTONOSUPPORT */
2339 /* case ESOCKTNOSUPPORT */
2340 /* case EOPNOTSUPP */
2341 /* case EPFNOSUPPORT */
2342 /* case EAFNOSUPPORT */
2343 /* case EADDRINUSE */
2344 /* case EADDRNOTAVAIL */
2346 /* case ENETUNREACH */
2347 /* case ENETRESET */
2348 /* case ECONNABORTED */
2349 /* case ECONNRESET */
2352 case ENOTCONN: return SS$_CLEARED;
2353 /* case ESHUTDOWN */
2354 /* case ETOOMANYREFS */
2355 /* case ETIMEDOUT */
2356 /* case ECONNREFUSED */
2358 /* case ENAMETOOLONG */
2359 /* case EHOSTDOWN */
2360 /* case EHOSTUNREACH */
2361 /* case ENOTEMPTY */
2373 /* case ECANCELED */
2377 return SS$_UNSUPPORTED;
2383 /* case EABANDONED */
2385 return SS$_ABORT; /* punt */
2388 return SS$_ABORT; /* Should not get here */
2392 /* default piping mailbox size */
2393 #define PERL_BUFSIZ 512
2397 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2399 unsigned long int mbxbufsiz;
2400 static unsigned long int syssize = 0;
2401 unsigned long int dviitm = DVI$_DEVNAM;
2402 char csize[LNM$C_NAMLENGTH+1];
2406 unsigned long syiitm = SYI$_MAXBUF;
2408 * Get the SYSGEN parameter MAXBUF
2410 * If the logical 'PERL_MBX_SIZE' is defined
2411 * use the value of the logical instead of PERL_BUFSIZ, but
2412 * keep the size between 128 and MAXBUF.
2415 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2418 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2419 mbxbufsiz = atoi(csize);
2421 mbxbufsiz = PERL_BUFSIZ;
2423 if (mbxbufsiz < 128) mbxbufsiz = 128;
2424 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2426 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2428 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2429 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2431 } /* end of create_mbx() */
2434 /*{{{ my_popen and my_pclose*/
2436 typedef struct _iosb IOSB;
2437 typedef struct _iosb* pIOSB;
2438 typedef struct _pipe Pipe;
2439 typedef struct _pipe* pPipe;
2440 typedef struct pipe_details Info;
2441 typedef struct pipe_details* pInfo;
2442 typedef struct _srqp RQE;
2443 typedef struct _srqp* pRQE;
2444 typedef struct _tochildbuf CBuf;
2445 typedef struct _tochildbuf* pCBuf;
2448 unsigned short status;
2449 unsigned short count;
2450 unsigned long dvispec;
2453 #pragma member_alignment save
2454 #pragma nomember_alignment quadword
2455 struct _srqp { /* VMS self-relative queue entry */
2456 unsigned long qptr[2];
2458 #pragma member_alignment restore
2459 static RQE RQE_ZERO = {0,0};
2461 struct _tochildbuf {
2464 unsigned short size;
2472 unsigned short chan_in;
2473 unsigned short chan_out;
2475 unsigned int bufsize;
2487 #if defined(PERL_IMPLICIT_CONTEXT)
2488 void *thx; /* Either a thread or an interpreter */
2489 /* pointer, depending on how we're built */
2497 PerlIO *fp; /* file pointer to pipe mailbox */
2498 int useFILE; /* using stdio, not perlio */
2499 int pid; /* PID of subprocess */
2500 int mode; /* == 'r' if pipe open for reading */
2501 int done; /* subprocess has completed */
2502 int waiting; /* waiting for completion/closure */
2503 int closing; /* my_pclose is closing this pipe */
2504 unsigned long completion; /* termination status of subprocess */
2505 pPipe in; /* pipe in to sub */
2506 pPipe out; /* pipe out of sub */
2507 pPipe err; /* pipe of sub's sys$error */
2508 int in_done; /* true when in pipe finished */
2513 struct exit_control_block
2515 struct exit_control_block *flink;
2516 unsigned long int (*exit_routine)();
2517 unsigned long int arg_count;
2518 unsigned long int *status_address;
2519 unsigned long int exit_status;
2522 typedef struct _closed_pipes Xpipe;
2523 typedef struct _closed_pipes* pXpipe;
2525 struct _closed_pipes {
2526 int pid; /* PID of subprocess */
2527 unsigned long completion; /* termination status of subprocess */
2529 #define NKEEPCLOSED 50
2530 static Xpipe closed_list[NKEEPCLOSED];
2531 static int closed_index = 0;
2532 static int closed_num = 0;
2534 #define RETRY_DELAY "0 ::0.20"
2535 #define MAX_RETRY 50
2537 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2538 static unsigned long mypid;
2539 static unsigned long delaytime[2];
2541 static pInfo open_pipes = NULL;
2542 static $DESCRIPTOR(nl_desc, "NL:");
2544 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2548 static unsigned long int
2549 pipe_exit_routine(pTHX)
2552 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2553 int sts, did_stuff, need_eof, j;
2556 flush any pending i/o
2562 PerlIO_flush(info->fp); /* first, flush data */
2564 fflush((FILE *)info->fp);
2570 next we try sending an EOF...ignore if doesn't work, make sure we
2578 _ckvmssts_noperl(sys$setast(0));
2579 if (info->in && !info->in->shut_on_empty) {
2580 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2585 _ckvmssts_noperl(sys$setast(1));
2589 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2591 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2596 _ckvmssts_noperl(sys$setast(0));
2597 if (info->waiting && info->done)
2599 nwait += info->waiting;
2600 _ckvmssts_noperl(sys$setast(1));
2610 _ckvmssts_noperl(sys$setast(0));
2611 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2612 sts = sys$forcex(&info->pid,0,&abort);
2613 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2616 _ckvmssts_noperl(sys$setast(1));
2620 /* again, wait for effect */
2622 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2627 _ckvmssts_noperl(sys$setast(0));
2628 if (info->waiting && info->done)
2630 nwait += info->waiting;
2631 _ckvmssts_noperl(sys$setast(1));
2640 _ckvmssts_noperl(sys$setast(0));
2641 if (!info->done) { /* We tried to be nice . . . */
2642 sts = sys$delprc(&info->pid,0);
2643 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2645 _ckvmssts_noperl(sys$setast(1));
2650 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2651 else if (!(sts & 1)) retsts = sts;
2656 static struct exit_control_block pipe_exitblock =
2657 {(struct exit_control_block *) 0,
2658 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2660 static void pipe_mbxtofd_ast(pPipe p);
2661 static void pipe_tochild1_ast(pPipe p);
2662 static void pipe_tochild2_ast(pPipe p);
2665 popen_completion_ast(pInfo info)
2667 pInfo i = open_pipes;
2672 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2673 closed_list[closed_index].pid = info->pid;
2674 closed_list[closed_index].completion = info->completion;
2676 if (closed_index == NKEEPCLOSED)
2681 if (i == info) break;
2684 if (!i) return; /* unlinked, probably freed too */
2689 Writing to subprocess ...
2690 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2692 chan_out may be waiting for "done" flag, or hung waiting
2693 for i/o completion to child...cancel the i/o. This will
2694 put it into "snarf mode" (done but no EOF yet) that discards
2697 Output from subprocess (stdout, stderr) needs to be flushed and
2698 shut down. We try sending an EOF, but if the mbx is full the pipe
2699 routine should still catch the "shut_on_empty" flag, telling it to
2700 use immediate-style reads so that "mbx empty" -> EOF.
2704 if (info->in && !info->in_done) { /* only for mode=w */
2705 if (info->in->shut_on_empty && info->in->need_wake) {
2706 info->in->need_wake = FALSE;
2707 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2709 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2713 if (info->out && !info->out_done) { /* were we also piping output? */
2714 info->out->shut_on_empty = TRUE;
2715 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2716 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2717 _ckvmssts_noperl(iss);
2720 if (info->err && !info->err_done) { /* we were piping stderr */
2721 info->err->shut_on_empty = TRUE;
2722 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2723 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2724 _ckvmssts_noperl(iss);
2726 _ckvmssts_noperl(sys$setef(pipe_ef));
2730 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2731 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2734 we actually differ from vmstrnenv since we use this to
2735 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2736 are pointing to the same thing
2739 static unsigned short
2740 popen_translate(pTHX_ char *logical, char *result)
2743 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2744 $DESCRIPTOR(d_log,"");
2746 unsigned short length;
2747 unsigned short code;
2749 unsigned short *retlenaddr;
2751 unsigned short l, ifi;
2753 d_log.dsc$a_pointer = logical;
2754 d_log.dsc$w_length = strlen(logical);
2756 itmlst[0].code = LNM$_STRING;
2757 itmlst[0].length = 255;
2758 itmlst[0].buffer_addr = result;
2759 itmlst[0].retlenaddr = &l;
2762 itmlst[1].length = 0;
2763 itmlst[1].buffer_addr = 0;
2764 itmlst[1].retlenaddr = 0;
2766 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
2767 if (iss == SS$_NOLOGNAM) {
2771 if (!(iss&1)) lib$signal(iss);
2774 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
2775 strip it off and return the ifi, if any
2778 if (result[0] == 0x1b && result[1] == 0x00) {
2779 memmove(&ifi,result+2,2);
2780 strcpy(result,result+4);
2782 return ifi; /* this is the RMS internal file id */
2785 static void pipe_infromchild_ast(pPipe p);
2788 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
2789 inside an AST routine without worrying about reentrancy and which Perl
2790 memory allocator is being used.
2792 We read data and queue up the buffers, then spit them out one at a
2793 time to the output mailbox when the output mailbox is ready for one.
2796 #define INITIAL_TOCHILDQUEUE 2
2799 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
2803 char mbx1[64], mbx2[64];
2804 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2805 DSC$K_CLASS_S, mbx1},
2806 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2807 DSC$K_CLASS_S, mbx2};
2808 unsigned int dviitm = DVI$_DEVBUFSIZ;
2812 _ckvmssts(lib$get_vm(&n, &p));
2814 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2815 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2816 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2819 p->shut_on_empty = FALSE;
2820 p->need_wake = FALSE;
2823 p->iosb.status = SS$_NORMAL;
2824 p->iosb2.status = SS$_NORMAL;
2830 #ifdef PERL_IMPLICIT_CONTEXT
2834 n = sizeof(CBuf) + p->bufsize;
2836 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
2837 _ckvmssts(lib$get_vm(&n, &b));
2838 b->buf = (char *) b + sizeof(CBuf);
2839 _ckvmssts(lib$insqhi(b, &p->free));
2842 pipe_tochild2_ast(p);
2843 pipe_tochild1_ast(p);
2849 /* reads the MBX Perl is writing, and queues */
2852 pipe_tochild1_ast(pPipe p)
2855 int iss = p->iosb.status;
2856 int eof = (iss == SS$_ENDOFFILE);
2858 #ifdef PERL_IMPLICIT_CONTEXT
2864 p->shut_on_empty = TRUE;
2866 _ckvmssts(sys$dassgn(p->chan_in));
2872 b->size = p->iosb.count;
2873 _ckvmssts(sts = lib$insqhi(b, &p->wait));
2875 p->need_wake = FALSE;
2876 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
2879 p->retry = 1; /* initial call */
2882 if (eof) { /* flush the free queue, return when done */
2883 int n = sizeof(CBuf) + p->bufsize;
2885 iss = lib$remqti(&p->free, &b);
2886 if (iss == LIB$_QUEWASEMP) return;
2888 _ckvmssts(lib$free_vm(&n, &b));
2892 iss = lib$remqti(&p->free, &b);
2893 if (iss == LIB$_QUEWASEMP) {
2894 int n = sizeof(CBuf) + p->bufsize;
2895 _ckvmssts(lib$get_vm(&n, &b));
2896 b->buf = (char *) b + sizeof(CBuf);
2902 iss = sys$qio(0,p->chan_in,
2903 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2905 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2906 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2911 /* writes queued buffers to output, waits for each to complete before
2915 pipe_tochild2_ast(pPipe p)
2918 int iss = p->iosb2.status;
2919 int n = sizeof(CBuf) + p->bufsize;
2920 int done = (p->info && p->info->done) ||
2921 iss == SS$_CANCEL || iss == SS$_ABORT;
2922 #if defined(PERL_IMPLICIT_CONTEXT)
2927 if (p->type) { /* type=1 has old buffer, dispose */
2928 if (p->shut_on_empty) {
2929 _ckvmssts(lib$free_vm(&n, &b));
2931 _ckvmssts(lib$insqhi(b, &p->free));
2936 iss = lib$remqti(&p->wait, &b);
2937 if (iss == LIB$_QUEWASEMP) {
2938 if (p->shut_on_empty) {
2940 _ckvmssts(sys$dassgn(p->chan_out));
2941 *p->pipe_done = TRUE;
2942 _ckvmssts(sys$setef(pipe_ef));
2944 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2945 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2949 p->need_wake = TRUE;
2959 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2960 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2962 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2963 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2972 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2975 char mbx1[64], mbx2[64];
2976 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2977 DSC$K_CLASS_S, mbx1},
2978 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2979 DSC$K_CLASS_S, mbx2};
2980 unsigned int dviitm = DVI$_DEVBUFSIZ;
2982 int n = sizeof(Pipe);
2983 _ckvmssts(lib$get_vm(&n, &p));
2984 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2985 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2987 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2988 n = p->bufsize * sizeof(char);
2989 _ckvmssts(lib$get_vm(&n, &p->buf));
2990 p->shut_on_empty = FALSE;
2993 p->iosb.status = SS$_NORMAL;
2994 #if defined(PERL_IMPLICIT_CONTEXT)
2997 pipe_infromchild_ast(p);
3005 pipe_infromchild_ast(pPipe p)
3007 int iss = p->iosb.status;
3008 int eof = (iss == SS$_ENDOFFILE);
3009 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3010 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3011 #if defined(PERL_IMPLICIT_CONTEXT)
3015 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3016 _ckvmssts(sys$dassgn(p->chan_out));
3021 input shutdown if EOF from self (done or shut_on_empty)
3022 output shutdown if closing flag set (my_pclose)
3023 send data/eof from child or eof from self
3024 otherwise, re-read (snarf of data from child)
3029 if (myeof && p->chan_in) { /* input shutdown */
3030 _ckvmssts(sys$dassgn(p->chan_in));
3035 if (myeof || kideof) { /* pass EOF to parent */
3036 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3037 pipe_infromchild_ast, p,
3040 } else if (eof) { /* eat EOF --- fall through to read*/
3042 } else { /* transmit data */
3043 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3044 pipe_infromchild_ast,p,
3045 p->buf, p->iosb.count, 0, 0, 0, 0));
3051 /* everything shut? flag as done */
3053 if (!p->chan_in && !p->chan_out) {
3054 *p->pipe_done = TRUE;
3055 _ckvmssts(sys$setef(pipe_ef));
3059 /* write completed (or read, if snarfing from child)
3060 if still have input active,
3061 queue read...immediate mode if shut_on_empty so we get EOF if empty
3063 check if Perl reading, generate EOFs as needed
3069 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3070 pipe_infromchild_ast,p,
3071 p->buf, p->bufsize, 0, 0, 0, 0);
3072 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3074 } else { /* send EOFs for extra reads */
3075 p->iosb.status = SS$_ENDOFFILE;
3076 p->iosb.dvispec = 0;
3077 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3079 pipe_infromchild_ast, p, 0, 0, 0, 0));
3085 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3089 unsigned long dviitm = DVI$_DEVBUFSIZ;
3091 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3092 DSC$K_CLASS_S, mbx};
3093 int n = sizeof(Pipe);
3095 /* things like terminals and mbx's don't need this filter */
3096 if (fd && fstat(fd,&s) == 0) {
3097 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3098 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
3099 DSC$K_CLASS_S, s.st_dev};
3101 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
3102 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
3103 strcpy(out, s.st_dev);
3108 _ckvmssts(lib$get_vm(&n, &p));
3109 p->fd_out = dup(fd);
3110 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3111 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3112 n = (p->bufsize+1) * sizeof(char);
3113 _ckvmssts(lib$get_vm(&n, &p->buf));
3114 p->shut_on_empty = FALSE;
3119 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3120 pipe_mbxtofd_ast, p,
3121 p->buf, p->bufsize, 0, 0, 0, 0));
3127 pipe_mbxtofd_ast(pPipe p)
3129 int iss = p->iosb.status;
3130 int done = p->info->done;
3132 int eof = (iss == SS$_ENDOFFILE);
3133 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3134 int err = !(iss&1) && !eof;
3135 #if defined(PERL_IMPLICIT_CONTEXT)
3139 if (done && myeof) { /* end piping */
3141 sys$dassgn(p->chan_in);
3142 *p->pipe_done = TRUE;
3143 _ckvmssts(sys$setef(pipe_ef));
3147 if (!err && !eof) { /* good data to send to file */
3148 p->buf[p->iosb.count] = '\n';
3149 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3152 if (p->retry < MAX_RETRY) {
3153 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3163 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3164 pipe_mbxtofd_ast, p,
3165 p->buf, p->bufsize, 0, 0, 0, 0);
3166 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3171 typedef struct _pipeloc PLOC;
3172 typedef struct _pipeloc* pPLOC;
3176 char dir[NAM$C_MAXRSS+1];
3178 static pPLOC head_PLOC = 0;
3181 free_pipelocs(pTHX_ void *head)
3184 pPLOC *pHead = (pPLOC *)head;
3196 store_pipelocs(pTHX)
3205 char temp[NAM$C_MAXRSS+1];
3209 free_pipelocs(aTHX_ &head_PLOC);
3211 /* the . directory from @INC comes last */
3213 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3214 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3215 p->next = head_PLOC;
3217 strcpy(p->dir,"./");
3219 /* get the directory from $^X */
3221 unixdir = PerlMem_malloc(VMS_MAXRSS);
3222 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM);
3224 #ifdef PERL_IMPLICIT_CONTEXT
3225 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3227 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3229 strcpy(temp, PL_origargv[0]);
3230 x = strrchr(temp,']');
3232 x = strrchr(temp,'>');
3234 /* It could be a UNIX path */
3235 x = strrchr(temp,'/');
3241 /* Got a bare name, so use default directory */
3246 if ((tounixpath(temp, unixdir)) != Nullch) {
3247 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3248 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3249 p->next = head_PLOC;
3251 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3252 p->dir[NAM$C_MAXRSS] = '\0';
3256 /* reverse order of @INC entries, skip "." since entered above */
3258 #ifdef PERL_IMPLICIT_CONTEXT
3261 if (PL_incgv) av = GvAVn(PL_incgv);
3263 for (i = 0; av && i <= AvFILL(av); i++) {
3264 dirsv = *av_fetch(av,i,TRUE);
3266 if (SvROK(dirsv)) continue;
3267 dir = SvPVx(dirsv,n_a);
3268 if (strcmp(dir,".") == 0) continue;
3269 if ((tounixpath(dir, unixdir)) == Nullch)
3272 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3273 p->next = head_PLOC;
3275 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3276 p->dir[NAM$C_MAXRSS] = '\0';
3279 /* most likely spot (ARCHLIB) put first in the list */
3282 if ((tounixpath(ARCHLIB_EXP, unixdir)) != Nullch) {
3283 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3284 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3285 p->next = head_PLOC;
3287 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3288 p->dir[NAM$C_MAXRSS] = '\0';
3291 PerlMem_free(unixdir);
3298 static int vmspipe_file_status = 0;
3299 static char vmspipe_file[NAM$C_MAXRSS+1];
3301 /* already found? Check and use ... need read+execute permission */
3303 if (vmspipe_file_status == 1) {
3304 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3305 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3306 return vmspipe_file;
3308 vmspipe_file_status = 0;
3311 /* scan through stored @INC, $^X */
3313 if (vmspipe_file_status == 0) {
3314 char file[NAM$C_MAXRSS+1];
3315 pPLOC p = head_PLOC;
3320 strcpy(file, p->dir);
3321 dirlen = strlen(file);
3322 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3323 file[NAM$C_MAXRSS] = '\0';
3326 exp_res = do_rmsexpand
3327 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS);
3328 if (!exp_res) continue;
3330 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3331 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3332 vmspipe_file_status = 1;
3333 return vmspipe_file;
3336 vmspipe_file_status = -1; /* failed, use tempfiles */
3343 vmspipe_tempfile(pTHX)
3345 char file[NAM$C_MAXRSS+1];
3347 static int index = 0;
3351 /* create a tempfile */
3353 /* we can't go from W, shr=get to R, shr=get without
3354 an intermediate vulnerable state, so don't bother trying...
3356 and lib$spawn doesn't shr=put, so have to close the write
3358 So... match up the creation date/time and the FID to
3359 make sure we're dealing with the same file
3364 if (!decc_filename_unix_only) {
3365 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3366 fp = fopen(file,"w");
3368 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3369 fp = fopen(file,"w");
3371 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3372 fp = fopen(file,"w");
3377 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3378 fp = fopen(file,"w");
3380 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3381 fp = fopen(file,"w");
3383 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3384 fp = fopen(file,"w");
3388 if (!fp) return 0; /* we're hosed */
3390 fprintf(fp,"$! 'f$verify(0)'\n");
3391 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3392 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3393 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3394 fprintf(fp,"$ perl_on = \"set noon\"\n");
3395 fprintf(fp,"$ perl_exit = \"exit\"\n");
3396 fprintf(fp,"$ perl_del = \"delete\"\n");
3397 fprintf(fp,"$ pif = \"if\"\n");
3398 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3399 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3400 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3401 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3402 fprintf(fp,"$! --- build command line to get max possible length\n");
3403 fprintf(fp,"$c=perl_popen_cmd0\n");
3404 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3405 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3406 fprintf(fp,"$x=perl_popen_cmd3\n");
3407 fprintf(fp,"$c=c+x\n");
3408 fprintf(fp,"$ perl_on\n");
3409 fprintf(fp,"$ 'c'\n");
3410 fprintf(fp,"$ perl_status = $STATUS\n");
3411 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3412 fprintf(fp,"$ perl_exit 'perl_status'\n");
3415 fgetname(fp, file, 1);
3416 fstat(fileno(fp), (struct stat *)&s0);
3419 if (decc_filename_unix_only)
3420 do_tounixspec(file, file, 0);
3421 fp = fopen(file,"r","shr=get");
3423 fstat(fileno(fp), (struct stat *)&s1);
3425 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3426 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3437 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3439 static int handler_set_up = FALSE;
3440 unsigned long int sts, flags = CLI$M_NOWAIT;
3441 /* The use of a GLOBAL table (as was done previously) rendered
3442 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3443 * environment. Hence we've switched to LOCAL symbol table.
3445 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3447 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3448 char in[512], out[512], err[512], mbx[512];
3450 char tfilebuf[NAM$C_MAXRSS+1];
3452 char cmd_sym_name[20];
3453 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3454 DSC$K_CLASS_S, symbol};
3455 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3457 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3458 DSC$K_CLASS_S, cmd_sym_name};
3459 struct dsc$descriptor_s *vmscmd;
3460 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3461 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3462 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3464 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3466 /* once-per-program initialization...
3467 note that the SETAST calls and the dual test of pipe_ef
3468 makes sure that only the FIRST thread through here does
3469 the initialization...all other threads wait until it's
3472 Yeah, uglier than a pthread call, it's got all the stuff inline
3473 rather than in a separate routine.
3477 _ckvmssts(sys$setast(0));
3479 unsigned long int pidcode = JPI$_PID;
3480 $DESCRIPTOR(d_delay, RETRY_DELAY);
3481 _ckvmssts(lib$get_ef(&pipe_ef));
3482 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3483 _ckvmssts(sys$bintim(&d_delay, delaytime));
3485 if (!handler_set_up) {
3486 _ckvmssts(sys$dclexh(&pipe_exitblock));
3487 handler_set_up = TRUE;
3489 _ckvmssts(sys$setast(1));
3492 /* see if we can find a VMSPIPE.COM */
3495 vmspipe = find_vmspipe(aTHX);
3497 strcpy(tfilebuf+1,vmspipe);
3498 } else { /* uh, oh...we're in tempfile hell */
3499 tpipe = vmspipe_tempfile(aTHX);
3500 if (!tpipe) { /* a fish popular in Boston */
3501 if (ckWARN(WARN_PIPE)) {
3502 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3506 fgetname(tpipe,tfilebuf+1,1);
3508 vmspipedsc.dsc$a_pointer = tfilebuf;
3509 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3511 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3514 case RMS$_FNF: case RMS$_DNF:
3515 set_errno(ENOENT); break;
3517 set_errno(ENOTDIR); break;
3519 set_errno(ENODEV); break;
3521 set_errno(EACCES); break;
3523 set_errno(EINVAL); break;
3524 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3525 set_errno(E2BIG); break;
3526 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3527 _ckvmssts(sts); /* fall through */
3528 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3531 set_vaxc_errno(sts);
3532 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3533 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3539 _ckvmssts(lib$get_vm(&n, &info));
3541 strcpy(mode,in_mode);
3544 info->completion = 0;
3545 info->closing = FALSE;
3552 info->in_done = TRUE;
3553 info->out_done = TRUE;
3554 info->err_done = TRUE;
3555 in[0] = out[0] = err[0] = '\0';
3557 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3561 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3566 if (*mode == 'r') { /* piping from subroutine */
3568 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3570 info->out->pipe_done = &info->out_done;
3571 info->out_done = FALSE;
3572 info->out->info = info;
3574 if (!info->useFILE) {
3575 info->fp = PerlIO_open(mbx, mode);
3577 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3578 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3581 if (!info->fp && info->out) {
3582 sys$cancel(info->out->chan_out);
3584 while (!info->out_done) {
3586 _ckvmssts(sys$setast(0));
3587 done = info->out_done;
3588 if (!done) _ckvmssts(sys$clref(pipe_ef));
3589 _ckvmssts(sys$setast(1));
3590 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3593 if (info->out->buf) {
3594 n = info->out->bufsize * sizeof(char);
3595 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3598 _ckvmssts(lib$free_vm(&n, &info->out));
3600 _ckvmssts(lib$free_vm(&n, &info));
3605 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3607 info->err->pipe_done = &info->err_done;
3608 info->err_done = FALSE;
3609 info->err->info = info;
3612 } else if (*mode == 'w') { /* piping to subroutine */
3614 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3616 info->out->pipe_done = &info->out_done;
3617 info->out_done = FALSE;
3618 info->out->info = info;
3621 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3623 info->err->pipe_done = &info->err_done;
3624 info->err_done = FALSE;
3625 info->err->info = info;
3628 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3629 if (!info->useFILE) {
3630 info->fp = PerlIO_open(mbx, mode);
3632 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3633 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3637 info->in->pipe_done = &info->in_done;
3638 info->in_done = FALSE;
3639 info->in->info = info;
3643 if (!info->fp && info->in) {
3645 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3646 0, 0, 0, 0, 0, 0, 0, 0));
3648 while (!info->in_done) {
3650 _ckvmssts(sys$setast(0));
3651 done = info->in_done;
3652 if (!done) _ckvmssts(sys$clref(pipe_ef));
3653 _ckvmssts(sys$setast(1));
3654 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3657 if (info->in->buf) {
3658 n = info->in->bufsize * sizeof(char);
3659 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3662 _ckvmssts(lib$free_vm(&n, &info->in));
3664 _ckvmssts(lib$free_vm(&n, &info));
3670 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3671 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3673 info->out->pipe_done = &info->out_done;
3674 info->out_done = FALSE;
3675 info->out->info = info;
3678 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3680 info->err->pipe_done = &info->err_done;
3681 info->err_done = FALSE;
3682 info->err->info = info;
3686 symbol[MAX_DCL_SYMBOL] = '\0';
3688 strncpy(symbol, in, MAX_DCL_SYMBOL);
3689 d_symbol.dsc$w_length = strlen(symbol);
3690 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3692 strncpy(symbol, err, MAX_DCL_SYMBOL);
3693 d_symbol.dsc$w_length = strlen(symbol);
3694 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3696 strncpy(symbol, out, MAX_DCL_SYMBOL);
3697 d_symbol.dsc$w_length = strlen(symbol);
3698 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3700 p = vmscmd->dsc$a_pointer;
3701 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3702 if (*p == '$') p++; /* remove leading $ */
3703 while (*p == ' ' || *p == '\t') p++;
3705 for (j = 0; j < 4; j++) {
3706 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3707 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3709 strncpy(symbol, p, MAX_DCL_SYMBOL);
3710 d_symbol.dsc$w_length = strlen(symbol);
3711 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3713 if (strlen(p) > MAX_DCL_SYMBOL) {
3714 p += MAX_DCL_SYMBOL;
3719 _ckvmssts(sys$setast(0));
3720 info->next=open_pipes; /* prepend to list */
3722 _ckvmssts(sys$setast(1));
3723 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
3724 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
3725 * have SYS$COMMAND if we need it.
3727 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
3728 0, &info->pid, &info->completion,
3729 0, popen_completion_ast,info,0,0,0));
3731 /* if we were using a tempfile, close it now */
3733 if (tpipe) fclose(tpipe);
3735 /* once the subprocess is spawned, it has copied the symbols and
3736 we can get rid of ours */
3738 for (j = 0; j < 4; j++) {
3739 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3740 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3741 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
3743 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
3744 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
3745 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
3746 vms_execfree(vmscmd);
3748 #ifdef PERL_IMPLICIT_CONTEXT
3751 PL_forkprocess = info->pid;
3756 _ckvmssts(sys$setast(0));
3758 if (!done) _ckvmssts(sys$clref(pipe_ef));
3759 _ckvmssts(sys$setast(1));
3760 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3762 *psts = info->completion;
3763 /* Caller thinks it is open and tries to close it. */
3764 /* This causes some problems, as it changes the error status */
3765 /* my_pclose(info->fp); */
3770 } /* end of safe_popen */
3773 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
3775 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
3779 TAINT_PROPER("popen");
3780 PERL_FLUSHALL_FOR_CHILD;
3781 return safe_popen(aTHX_ cmd,mode,&sts);
3786 /*{{{ I32 my_pclose(PerlIO *fp)*/
3787 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
3789 pInfo info, last = NULL;
3790 unsigned long int retsts;
3793 for (info = open_pipes; info != NULL; last = info, info = info->next)
3794 if (info->fp == fp) break;
3796 if (info == NULL) { /* no such pipe open */
3797 set_errno(ECHILD); /* quoth POSIX */
3798 set_vaxc_errno(SS$_NONEXPR);
3802 /* If we were writing to a subprocess, insure that someone reading from
3803 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
3804 * produce an EOF record in the mailbox.
3806 * well, at least sometimes it *does*, so we have to watch out for
3807 * the first EOF closing the pipe (and DASSGN'ing the channel)...
3811 PerlIO_flush(info->fp); /* first, flush data */
3813 fflush((FILE *)info->fp);
3816 _ckvmssts(sys$setast(0));
3817 info->closing = TRUE;
3818 done = info->done && info->in_done && info->out_done && info->err_done;
3819 /* hanging on write to Perl's input? cancel it */
3820 if (info->mode == 'r' && info->out && !info->out_done) {
3821 if (info->out->chan_out) {
3822 _ckvmssts(sys$cancel(info->out->chan_out));
3823 if (!info->out->chan_in) { /* EOF generation, need AST */
3824 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
3828 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
3829 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3831 _ckvmssts(sys$setast(1));
3834 PerlIO_close(info->fp);
3836 fclose((FILE *)info->fp);
3839 we have to wait until subprocess completes, but ALSO wait until all
3840 the i/o completes...otherwise we'll be freeing the "info" structure
3841 that the i/o ASTs could still be using...
3845 _ckvmssts(sys$setast(0));
3846 done = info->done && info->in_done && info->out_done && info->err_done;
3847 if (!done) _ckvmssts(sys$clref(pipe_ef));
3848 _ckvmssts(sys$setast(1));
3849 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3851 retsts = info->completion;
3853 /* remove from list of open pipes */
3854 _ckvmssts(sys$setast(0));
3855 if (last) last->next = info->next;
3856 else open_pipes = info->next;
3857 _ckvmssts(sys$setast(1));
3859 /* free buffers and structures */
3862 if (info->in->buf) {
3863 n = info->in->bufsize * sizeof(char);
3864 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3867 _ckvmssts(lib$free_vm(&n, &info->in));
3870 if (info->out->buf) {
3871 n = info->out->bufsize * sizeof(char);
3872 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3875 _ckvmssts(lib$free_vm(&n, &info->out));
3878 if (info->err->buf) {
3879 n = info->err->bufsize * sizeof(char);
3880 _ckvmssts(lib$free_vm(&n, &info->err->buf));
3883 _ckvmssts(lib$free_vm(&n, &info->err));
3886 _ckvmssts(lib$free_vm(&n, &info));
3890 } /* end of my_pclose() */
3892 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3893 /* Roll our own prototype because we want this regardless of whether
3894 * _VMS_WAIT is defined.
3896 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
3898 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
3899 created with popen(); otherwise partially emulate waitpid() unless
3900 we have a suitable one from the CRTL that came with VMS 7.2 and later.
3901 Also check processes not considered by the CRTL waitpid().
3903 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
3905 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
3912 if (statusp) *statusp = 0;
3914 for (info = open_pipes; info != NULL; info = info->next)
3915 if (info->pid == pid) break;
3917 if (info != NULL) { /* we know about this child */
3918 while (!info->done) {
3919 _ckvmssts(sys$setast(0));
3921 if (!done) _ckvmssts(sys$clref(pipe_ef));
3922 _ckvmssts(sys$setast(1));
3923 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3926 if (statusp) *statusp = info->completion;
3930 /* child that already terminated? */
3932 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
3933 if (closed_list[j].pid == pid) {
3934 if (statusp) *statusp = closed_list[j].completion;
3939 /* fall through if this child is not one of our own pipe children */
3941 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3943 /* waitpid() became available in the CRTL as of VMS 7.0, but only
3944 * in 7.2 did we get a version that fills in the VMS completion
3945 * status as Perl has always tried to do.
3948 sts = __vms_waitpid( pid, statusp, flags );
3950 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
3953 /* If the real waitpid tells us the child does not exist, we
3954 * fall through here to implement waiting for a child that
3955 * was created by some means other than exec() (say, spawned
3956 * from DCL) or to wait for a process that is not a subprocess
3957 * of the current process.
3960 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3963 $DESCRIPTOR(intdsc,"0 00:00:01");
3964 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3965 unsigned long int pidcode = JPI$_PID, mypid;
3966 unsigned long int interval[2];
3967 unsigned int jpi_iosb[2];
3968 struct itmlst_3 jpilist[2] = {
3969 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3974 /* Sorry folks, we don't presently implement rooting around for
3975 the first child we can find, and we definitely don't want to
3976 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3982 /* Get the owner of the child so I can warn if it's not mine. If the
3983 * process doesn't exist or I don't have the privs to look at it,
3984 * I can go home early.
3986 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3987 if (sts & 1) sts = jpi_iosb[0];
3999 set_vaxc_errno(sts);
4003 if (ckWARN(WARN_EXEC)) {
4004 /* remind folks they are asking for non-standard waitpid behavior */
4005 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4006 if (ownerpid != mypid)
4007 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4008 "waitpid: process %x is not a child of process %x",
4012 /* simply check on it once a second until it's not there anymore. */
4014 _ckvmssts(sys$bintim(&intdsc,interval));
4015 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4016 _ckvmssts(sys$schdwk(0,0,interval,0));
4017 _ckvmssts(sys$hiber());
4019 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4024 } /* end of waitpid() */
4029 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4031 my_gconvert(double val, int ndig, int trail, char *buf)
4033 static char __gcvtbuf[DBL_DIG+1];
4036 loc = buf ? buf : __gcvtbuf;
4038 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4040 sprintf(loc,"%.*g",ndig,val);
4046 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4047 return gcvt(val,ndig,loc);
4050 loc[0] = '0'; loc[1] = '\0';
4057 #if 1 /* defined(__VAX) || !defined(NAML$C_MAXRSS) */
4058 static int rms_free_search_context(struct FAB * fab)
4062 nam = fab->fab$l_nam;
4063 nam->nam$b_nop |= NAM$M_SYNCHK;
4064 nam->nam$l_rlf = NULL;
4066 return sys$parse(fab, NULL, NULL);
4069 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4070 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4071 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4072 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4073 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4074 #define rms_nam_esll(nam) nam.nam$b_esl
4075 #define rms_nam_esl(nam) nam.nam$b_esl
4076 #define rms_nam_name(nam) nam.nam$l_name
4077 #define rms_nam_namel(nam) nam.nam$l_name
4078 #define rms_nam_type(nam) nam.nam$l_type
4079 #define rms_nam_typel(nam) nam.nam$l_type
4080 #define rms_nam_ver(nam) nam.nam$l_ver
4081 #define rms_nam_verl(nam) nam.nam$l_ver
4082 #define rms_nam_rsll(nam) nam.nam$b_rsl
4083 #define rms_nam_rsl(nam) nam.nam$b_rsl
4084 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4085 #define rms_set_fna(fab, nam, name, size) \
4086 fab.fab$b_fns = size; fab.fab$l_fna = name;
4087 #define rms_get_fna(fab, nam) fab.fab$l_fna
4088 #define rms_set_dna(fab, nam, name, size) \
4089 fab.fab$b_dns = size; fab.fab$l_dna = name;
4090 #define rms_nam_dns(fab, nam) fab.fab$b_dns;
4091 #define rms_set_esa(fab, nam, name, size) \
4092 nam.nam$b_ess = size; nam.nam$l_esa = name;
4093 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4094 nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;
4095 #define rms_set_rsa(nam, name, size) \
4096 nam.nam$l_rsa = name; nam.nam$b_rss = size;
4097 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4098 nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size;
4101 static int rms_free_search_context(struct FAB * fab)
4105 nam = fab->fab$l_naml;
4106 nam->naml$b_nop |= NAM$M_SYNCHK;
4107 nam->naml$l_rlf = NULL;
4108 nam->naml$l_long_defname_size = 0;
4110 return sys$parse(fab, NULL, NULL);
4113 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4114 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4115 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4116 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4117 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4118 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4119 #define rms_nam_esl(nam) nam.naml$b_esl
4120 #define rms_nam_name(nam) nam.naml$l_name
4121 #define rms_nam_namel(nam) nam.naml$l_long_name
4122 #define rms_nam_type(nam) nam.naml$l_type
4123 #define rms_nam_typel(nam) nam.naml$l_long_type
4124 #define rms_nam_ver(nam) nam.naml$l_ver
4125 #define rms_nam_verl(nam) nam.naml$l_long_ver
4126 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4127 #define rms_nam_rsl(nam) nam.naml$b_rsl
4128 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4129 #define rms_set_fna(fab, nam, name, size) \
4130 fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4131 nam.naml$l_long_filename_size = size; \
4132 nam.naml$l_long_filename = name
4133 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4134 #define rms_set_dna(fab, nam, name, size) \
4135 fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4136 nam.naml$l_long_defname_size = size; \
4137 nam.naml$l_long_defname = name
4138 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4139 #define rms_set_esa(fab, nam, name, size) \
4140 nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4141 nam.naml$l_long_expand_alloc = size; \
4142 nam.naml$l_long_expand = name
4143 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4144 nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4145 nam.naml$l_long_expand = l_name; \
4146 nam.naml$l_long_expand_alloc = l_size;
4147 #define rms_set_rsa(nam, name, size) \
4148 nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4149 nam.naml$l_long_result = name; \
4150 nam.naml$l_long_result_alloc = size;
4151 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4152 nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4153 nam.naml$l_long_result = l_name; \
4154 nam.naml$l_long_result_alloc = l_size;
4159 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
4160 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
4161 * to expand file specification. Allows for a single default file
4162 * specification and a simple mask of options. If outbuf is non-NULL,
4163 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
4164 * the resultant file specification is placed. If outbuf is NULL, the
4165 * resultant file specification is placed into a static buffer.
4166 * The third argument, if non-NULL, is taken to be a default file
4167 * specification string. The fourth argument is unused at present.
4168 * rmesexpand() returns the address of the resultant string if
4169 * successful, and NULL on error.
4171 * New functionality for previously unused opts value:
4172 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
4174 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
4176 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4177 /* ODS-2 only version */
4179 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
4181 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
4182 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
4183 char esa[NAM$C_MAXRSS+1], *cp, *out = NULL;
4184 struct FAB myfab = cc$rms_fab;
4185 struct NAM mynam = cc$rms_nam;
4187 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4190 if (!filespec || !*filespec) {
4191 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4195 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
4196 else outbuf = __rmsexpand_retbuf;
4198 isunix = is_unix_filespec(filespec);
4200 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
4205 filespec = vmsfspec;
4208 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
4209 myfab.fab$b_fns = strlen(filespec);
4210 myfab.fab$l_nam = &mynam;
4212 if (defspec && *defspec) {
4213 if (strchr(defspec,'/') != NULL) {
4214 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
4221 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
4222 myfab.fab$b_dns = strlen(defspec);
4225 mynam.nam$l_esa = esa;
4226 mynam.nam$b_ess = NAM$C_MAXRSS;
4227 mynam.nam$l_rsa = outbuf;
4228 mynam.nam$b_rss = NAM$C_MAXRSS;
4230 #ifdef NAM$M_NO_SHORT_UPCASE
4231 if (decc_efs_case_preserve)
4232 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4235 retsts = sys$parse(&myfab,0,0);
4236 if (!(retsts & 1)) {
4237 mynam.nam$b_nop |= NAM$M_SYNCHK;
4238 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4239 retsts = sys$parse(&myfab,0,0);
4240 if (retsts & 1) goto expanded;
4242 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
4243 sts = sys$parse(&myfab,0,0); /* Free search context */
4244 if (out) Safefree(out);
4245 set_vaxc_errno(retsts);
4246 if (retsts == RMS$_PRV) set_errno(EACCES);
4247 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4248 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4249 else set_errno(EVMSERR);
4252 retsts = sys$search(&myfab,0,0);
4253 if (!(retsts & 1) && retsts != RMS$_FNF) {
4254 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4255 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
4256 if (out) Safefree(out);
4257 set_vaxc_errno(retsts);
4258 if (retsts == RMS$_PRV) set_errno(EACCES);
4259 else set_errno(EVMSERR);
4263 /* If the input filespec contained any lowercase characters,
4264 * downcase the result for compatibility with Unix-minded code. */
4266 if (!decc_efs_case_preserve) {
4267 for (out = myfab.fab$l_fna; *out; out++)
4268 if (islower(*out)) { haslower = 1; break; }
4270 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
4271 else { out = esa; speclen = mynam.nam$b_esl; }
4273 /* Trim off null fields added by $PARSE
4274 * If type > 1 char, must have been specified in original or default spec
4275 * (not true for version; $SEARCH may have added version of existing file).
4277 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
4278 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
4279 (mynam.nam$l_ver - mynam.nam$l_type == 1);
4280 if (trimver || trimtype) {
4281 if (defspec && *defspec) {
4282 char defesa[NAM$C_MAXRSS];
4283 struct FAB deffab = cc$rms_fab;
4284 struct NAM defnam = cc$rms_nam;
4286 deffab.fab$l_nam = &defnam;
4287 /* cast below ok for read only pointer */
4288 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
4289 defnam.nam$l_esa = defesa; defnam.nam$b_ess = NAM$C_MAXRSS;
4290 defnam.nam$b_nop = NAM$M_SYNCHK;
4291 #ifdef NAM$M_NO_SHORT_UPCASE
4292 if (decc_efs_case_preserve)
4293 defnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4295 if (sys$parse(&deffab,0,0) & 1) {
4296 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
4297 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
4301 if (*mynam.nam$l_ver != '\"')
4302 speclen = mynam.nam$l_ver - out;
4305 /* If we didn't already trim version, copy down */
4306 if (speclen > mynam.nam$l_ver - out)
4307 memmove(mynam.nam$l_type, mynam.nam$l_ver,
4308 speclen - (mynam.nam$l_ver - out));
4309 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
4312 /* If we just had a directory spec on input, $PARSE "helpfully"
4313 * adds an empty name and type for us */
4314 if (mynam.nam$l_name == mynam.nam$l_type &&
4315 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
4316 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
4317 speclen = mynam.nam$l_name - out;
4319 /* Posix format specifications must have matching quotes */
4320 if (speclen < NAM$C_MAXRSS) {
4321 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4322 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4323 out[speclen] = '\"';
4329 out[speclen] = '\0';
4330 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4332 /* Have we been working with an expanded, but not resultant, spec? */
4333 /* Also, convert back to Unix syntax if necessary. */
4334 if ((opts & PERL_RMSEXPAND_M_VMS) != 0)
4337 if (!mynam.nam$b_rsl) {
4339 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
4341 else strcpy(outbuf,esa);
4344 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
4345 strcpy(outbuf,tmpfspec);
4347 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4348 mynam.nam$l_rsa = NULL;
4349 mynam.nam$b_rss = 0;
4350 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
4354 /* ODS-5 supporting routine */
4356 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
4358 static char __rmsexpand_retbuf[NAML$C_MAXRSS+1];
4359 char * vmsfspec, *tmpfspec;
4360 char * esa, *cp, *out = NULL;
4364 struct FAB myfab = cc$rms_fab;
4365 rms_setup_nam(mynam);
4367 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4370 if (!filespec || !*filespec) {
4371 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4375 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
4376 else outbuf = __rmsexpand_retbuf;
4382 isunix = is_unix_filespec(filespec);
4384 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
4385 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM);
4386 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
4387 PerlMem_free(vmsfspec);
4392 filespec = vmsfspec;
4394 /* Unless we are forcing to VMS format, a UNIX input means
4395 * UNIX output, and that requires long names to be used
4397 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4398 opts |= PERL_RMSEXPAND_M_LONG;
4404 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
4405 rms_bind_fab_nam(myfab, mynam);
4407 if (defspec && *defspec) {
4409 t_isunix = is_unix_filespec(defspec);
4411 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
4412 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
4413 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
4414 PerlMem_free(tmpfspec);
4415 if (vmsfspec != NULL)
4416 PerlMem_free(vmsfspec);
4423 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4426 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
4427 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
4428 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4429 esal = PerlMem_malloc(NAML$C_MAXRSS + 1);
4430 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
4432 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, NAML$C_MAXRSS);
4434 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4435 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
4438 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4439 outbufl = PerlMem_malloc(VMS_MAXRSS);
4440 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM);
4441 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
4443 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
4447 #ifdef NAM$M_NO_SHORT_UPCASE
4448 if (decc_efs_case_preserve)
4449 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
4452 /* First attempt to parse as an existing file */
4453 retsts = sys$parse(&myfab,0,0);
4454 if (!(retsts & STS$K_SUCCESS)) {
4456 /* Could not find the file, try as syntax only if error is not fatal */
4457 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
4458 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4459 retsts = sys$parse(&myfab,0,0);
4460 if (retsts & STS$K_SUCCESS) goto expanded;
4463 /* Still could not parse the file specification */
4464 /*----------------------------------------------*/
4465 sts = rms_free_search_context(&myfab); /* Free search context */
4466 if (out) Safefree(out);
4467 if (tmpfspec != NULL)
4468 PerlMem_free(tmpfspec);
4469 if (vmsfspec != NULL)
4470 PerlMem_free(vmsfspec);
4471 if (outbufl != NULL)
4472 PerlMem_free(outbufl);
4475 set_vaxc_errno(retsts);
4476 if (retsts == RMS$_PRV) set_errno(EACCES);
4477 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4478 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4479 else set_errno(EVMSERR);
4482 retsts = sys$search(&myfab,0,0);
4483 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4484 sts = rms_free_search_context(&myfab); /* Free search context */
4485 if (out) Safefree(out);
4486 if (tmpfspec != NULL)
4487 PerlMem_free(tmpfspec);
4488 if (vmsfspec != NULL)
4489 PerlMem_free(vmsfspec);
4490 if (outbufl != NULL)
4491 PerlMem_free(outbufl);
4494 set_vaxc_errno(retsts);
4495 if (retsts == RMS$_PRV) set_errno(EACCES);
4496 else set_errno(EVMSERR);
4500 /* If the input filespec contained any lowercase characters,
4501 * downcase the result for compatibility with Unix-minded code. */
4503 if (!decc_efs_case_preserve) {
4504 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
4505 if (islower(*tbuf)) { haslower = 1; break; }
4508 /* Is a long or a short name expected */
4509 /*------------------------------------*/
4510 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4511 if (rms_nam_rsll(mynam)) {
4513 speclen = rms_nam_rsll(mynam);
4516 tbuf = esal; /* Not esa */
4517 speclen = rms_nam_esll(mynam);
4521 if (rms_nam_rsl(mynam)) {
4523 speclen = rms_nam_rsl(mynam);
4526 tbuf = esa; /* Not esal */
4527 speclen = rms_nam_esl(mynam);
4530 tbuf[speclen] = '\0';
4532 /* Trim off null fields added by $PARSE
4533 * If type > 1 char, must have been specified in original or default spec
4534 * (not true for version; $SEARCH may have added version of existing file).
4536 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
4537 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4538 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4539 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
4542 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4543 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
4545 if (trimver || trimtype) {
4546 if (defspec && *defspec) {
4547 char *defesal = NULL;
4548 defesal = PerlMem_malloc(NAML$C_MAXRSS + 1);
4549 if (defesal != NULL) {
4550 struct FAB deffab = cc$rms_fab;
4551 rms_setup_nam(defnam);
4553 rms_bind_fab_nam(deffab, defnam);
4557 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
4559 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
4561 rms_clear_nam_nop(defnam);
4562 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
4563 #ifdef NAM$M_NO_SHORT_UPCASE
4564 if (decc_efs_case_preserve)
4565 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
4567 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4569 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
4572 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
4575 PerlMem_free(defesal);
4579 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4580 if (*(rms_nam_verl(mynam)) != '\"')
4581 speclen = rms_nam_verl(mynam) - tbuf;
4584 if (*(rms_nam_ver(mynam)) != '\"')
4585 speclen = rms_nam_ver(mynam) - tbuf;
4589 /* If we didn't already trim version, copy down */
4590 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4591 if (speclen > rms_nam_verl(mynam) - tbuf)
4593 (rms_nam_typel(mynam),
4594 rms_nam_verl(mynam),
4595 speclen - (rms_nam_verl(mynam) - tbuf));
4596 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
4599 if (speclen > rms_nam_ver(mynam) - tbuf)
4601 (rms_nam_type(mynam),
4603 speclen - (rms_nam_ver(mynam) - tbuf));
4604 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
4609 /* Done with these copies of the input files */
4610 /*-------------------------------------------*/
4611 if (vmsfspec != NULL)
4612 PerlMem_free(vmsfspec);
4613 if (tmpfspec != NULL)
4614 PerlMem_free(tmpfspec);
4616 /* If we just had a directory spec on input, $PARSE "helpfully"
4617 * adds an empty name and type for us */
4618 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4619 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
4620 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
4621 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4622 speclen = rms_nam_namel(mynam) - tbuf;
4625 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
4626 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
4627 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4628 speclen = rms_nam_name(mynam) - tbuf;
4631 /* Posix format specifications must have matching quotes */
4632 if (speclen < (VMS_MAXRSS - 1)) {
4633 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
4634 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
4635 tbuf[speclen] = '\"';
4640 tbuf[speclen] = '\0';
4641 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
4643 /* Have we been working with an expanded, but not resultant, spec? */
4644 /* Also, convert back to Unix syntax if necessary. */
4646 if (!rms_nam_rsll(mynam)) {
4648 if (do_tounixspec(esa,outbuf,0) == NULL) {
4649 if (out) Safefree(out);
4652 if (outbufl != NULL)
4653 PerlMem_free(outbufl);
4657 else strcpy(outbuf,esa);
4660 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
4661 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
4662 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) {
4663 if (out) Safefree(out);
4666 PerlMem_free(tmpfspec);
4667 if (outbufl != NULL)
4668 PerlMem_free(outbufl);
4671 strcpy(outbuf,tmpfspec);
4672 PerlMem_free(tmpfspec);
4675 rms_set_rsal(mynam, NULL, 0, NULL, 0);
4676 sts = rms_free_search_context(&myfab); /* Free search context */
4679 if (outbufl != NULL)
4680 PerlMem_free(outbufl);
4685 /* External entry points */
4686 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4687 { return do_rmsexpand(spec,buf,0,def,opt); }
4688 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4689 { return do_rmsexpand(spec,buf,1,def,opt); }
4693 ** The following routines are provided to make life easier when
4694 ** converting among VMS-style and Unix-style directory specifications.
4695 ** All will take input specifications in either VMS or Unix syntax. On
4696 ** failure, all return NULL. If successful, the routines listed below
4697 ** return a pointer to a buffer containing the appropriately
4698 ** reformatted spec (and, therefore, subsequent calls to that routine
4699 ** will clobber the result), while the routines of the same names with
4700 ** a _ts suffix appended will return a pointer to a mallocd string
4701 ** containing the appropriately reformatted spec.
4702 ** In all cases, only explicit syntax is altered; no check is made that
4703 ** the resulting string is valid or that the directory in question
4706 ** fileify_dirspec() - convert a directory spec into the name of the
4707 ** directory file (i.e. what you can stat() to see if it's a dir).
4708 ** The style (VMS or Unix) of the result is the same as the style
4709 ** of the parameter passed in.
4710 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4711 ** what you prepend to a filename to indicate what directory it's in).
4712 ** The style (VMS or Unix) of the result is the same as the style
4713 ** of the parameter passed in.
4714 ** tounixpath() - convert a directory spec into a Unix-style path.
4715 ** tovmspath() - convert a directory spec into a VMS-style path.
4716 ** tounixspec() - convert any file spec into a Unix-style file spec.
4717 ** tovmsspec() - convert any file spec into a VMS-style spec.
4719 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4720 ** Permission is given to distribute this code as part of the Perl
4721 ** standard distribution under the terms of the GNU General Public
4722 ** License or the Perl Artistic License. Copies of each may be
4723 ** found in the Perl standard distribution.
4726 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf)*/
4727 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
4729 static char __fileify_retbuf[VMS_MAXRSS];
4730 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4731 char *retspec, *cp1, *cp2, *lastdir;
4732 char *trndir, *vmsdir;
4733 unsigned short int trnlnm_iter_count;
4736 if (!dir || !*dir) {
4737 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4739 dirlen = strlen(dir);
4740 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4741 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4742 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4749 if (dirlen > (VMS_MAXRSS - 1)) {
4750 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
4753 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
4754 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
4755 if (!strpbrk(dir+1,"/]>:") &&
4756 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4757 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4758 trnlnm_iter_count = 0;
4759 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4760 trnlnm_iter_count++;
4761 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4763 dirlen = strlen(trndir);
4766 strncpy(trndir,dir,dirlen);
4767 trndir[dirlen] = '\0';
4770 /* At this point we are done with *dir and use *trndir which is a
4771 * copy that can be modified. *dir must not be modified.
4774 /* If we were handed a rooted logical name or spec, treat it like a
4775 * simple directory, so that
4776 * $ Define myroot dev:[dir.]
4777 * ... do_fileify_dirspec("myroot",buf,1) ...
4778 * does something useful.
4780 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4781 trndir[--dirlen] = '\0';
4782 trndir[dirlen-1] = ']';
4784 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4785 trndir[--dirlen] = '\0';
4786 trndir[dirlen-1] = '>';
4789 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4790 /* If we've got an explicit filename, we can just shuffle the string. */
4791 if (*(cp1+1)) hasfilename = 1;
4792 /* Similarly, we can just back up a level if we've got multiple levels
4793 of explicit directories in a VMS spec which ends with directories. */
4795 for (cp2 = cp1; cp2 > trndir; cp2--) {
4797 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4798 /* fix-me, can not scan EFS file specs backward like this */
4799 *cp2 = *cp1; *cp1 = '\0';
4804 if (*cp2 == '[' || *cp2 == '<') break;
4809 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
4810 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM);
4811 cp1 = strpbrk(trndir,"]:>");
4812 if (hasfilename || !cp1) { /* Unix-style path or filename */
4813 if (trndir[0] == '.') {
4814 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
4815 PerlMem_free(trndir);
4816 PerlMem_free(vmsdir);
4817 return do_fileify_dirspec("[]",buf,ts);
4819 else if (trndir[1] == '.' &&
4820 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
4821 PerlMem_free(trndir);
4822 PerlMem_free(vmsdir);
4823 return do_fileify_dirspec("[-]",buf,ts);
4826 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4827 dirlen -= 1; /* to last element */
4828 lastdir = strrchr(trndir,'/');
4830 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4831 /* If we have "/." or "/..", VMSify it and let the VMS code
4832 * below expand it, rather than repeating the code to handle
4833 * relative components of a filespec here */
4835 if (*(cp1+2) == '.') cp1++;
4836 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4838 if (do_tovmsspec(trndir,vmsdir,0) == NULL) {
4839 PerlMem_free(trndir);
4840 PerlMem_free(vmsdir);
4843 if (strchr(vmsdir,'/') != NULL) {
4844 /* If do_tovmsspec() returned it, it must have VMS syntax
4845 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4846 * the time to check this here only so we avoid a recursion
4847 * loop; otherwise, gigo.
4849 PerlMem_free(trndir);
4850 PerlMem_free(vmsdir);
4851 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
4854 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) {
4855 PerlMem_free(trndir);
4856 PerlMem_free(vmsdir);
4859 ret_chr = do_tounixspec(trndir,buf,ts);
4860 PerlMem_free(trndir);
4861 PerlMem_free(vmsdir);
4865 } while ((cp1 = strstr(cp1,"/.")) != NULL);
4866 lastdir = strrchr(trndir,'/');
4868 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
4870 /* Ditto for specs that end in an MFD -- let the VMS code
4871 * figure out whether it's a real device or a rooted logical. */
4873 /* This should not happen any more. Allowing the fake /000000
4874 * in a UNIX pathname causes all sorts of problems when trying
4875 * to run in UNIX emulation. So the VMS to UNIX conversions
4876 * now remove the fake /000000 directories.
4879 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
4880 if (do_tovmsspec(trndir,vmsdir,0) == NULL) {
4881 PerlMem_free(trndir);
4882 PerlMem_free(vmsdir);
4885 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) {
4886 PerlMem_free(trndir);
4887 PerlMem_free(vmsdir);
4890 ret_chr = do_tounixspec(trndir,buf,ts);
4891 PerlMem_free(trndir);
4892 PerlMem_free(vmsdir);
4897 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
4898 !(lastdir = cp1 = strrchr(trndir,']')) &&
4899 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
4900 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
4903 /* For EFS or ODS-5 look for the last dot */
4904 if (decc_efs_charset) {
4905 cp2 = strrchr(cp1,'.');
4907 if (vms_process_case_tolerant) {
4908 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4909 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4910 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4911 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4912 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4913 (ver || *cp3)))))) {
4914 PerlMem_free(trndir);
4915 PerlMem_free(vmsdir);
4917 set_vaxc_errno(RMS$_DIR);
4922 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4923 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4924 !*(cp2+3) || *(cp2+3) != 'R' ||
4925 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4926 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4927 (ver || *cp3)))))) {
4928 PerlMem_free(trndir);
4929 PerlMem_free(vmsdir);
4931 set_vaxc_errno(RMS$_DIR);
4935 dirlen = cp2 - trndir;
4939 retlen = dirlen + 6;
4940 if (buf) retspec = buf;
4941 else if (ts) Newx(retspec,retlen+1,char);
4942 else retspec = __fileify_retbuf;
4943 memcpy(retspec,trndir,dirlen);
4944 retspec[dirlen] = '\0';
4946 /* We've picked up everything up to the directory file name.
4947 Now just add the type and version, and we're set. */
4948 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
4949 strcat(retspec,".dir;1");
4951 strcat(retspec,".DIR;1");
4952 PerlMem_free(trndir);
4953 PerlMem_free(vmsdir);
4956 else { /* VMS-style directory spec */
4958 char *esa, term, *cp;
4959 unsigned long int sts, cmplen, haslower = 0;
4960 unsigned int nam_fnb;
4962 struct FAB dirfab = cc$rms_fab;
4963 rms_setup_nam(savnam);
4964 rms_setup_nam(dirnam);
4966 esa = PerlMem_malloc(VMS_MAXRSS + 1);
4967 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
4968 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
4969 rms_bind_fab_nam(dirfab, dirnam);
4970 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
4971 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
4972 #ifdef NAM$M_NO_SHORT_UPCASE
4973 if (decc_efs_case_preserve)
4974 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
4977 for (cp = trndir; *cp; cp++)
4978 if (islower(*cp)) { haslower = 1; break; }
4979 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
4980 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4981 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
4982 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
4986 PerlMem_free(trndir);
4987 PerlMem_free(vmsdir);
4989 set_vaxc_errno(dirfab.fab$l_sts);
4995 /* Does the file really exist? */
4996 if (sys$search(&dirfab)& STS$K_SUCCESS) {
4997 /* Yes; fake the fnb bits so we'll check type below */
4998 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
5000 else { /* No; just work with potential name */
5001 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
5004 fab_sts = dirfab.fab$l_sts;
5005 sts = rms_free_search_context(&dirfab);
5007 PerlMem_free(trndir);
5008 PerlMem_free(vmsdir);
5009 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
5014 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
5015 cp1 = strchr(esa,']');
5016 if (!cp1) cp1 = strchr(esa,'>');
5017 if (cp1) { /* Should always be true */
5018 rms_nam_esll(dirnam) -= cp1 - esa - 1;
5019 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
5022 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5023 /* Yep; check version while we're at it, if it's there. */
5024 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5025 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
5026 /* Something other than .DIR[;1]. Bzzt. */
5027 sts = rms_free_search_context(&dirfab);
5029 PerlMem_free(trndir);
5030 PerlMem_free(vmsdir);
5032 set_vaxc_errno(RMS$_DIR);
5036 esa[rms_nam_esll(dirnam)] = '\0';
5037 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
5038 /* They provided at least the name; we added the type, if necessary, */
5039 if (buf) retspec = buf; /* in sys$parse() */
5040 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
5041 else retspec = __fileify_retbuf;
5042 strcpy(retspec,esa);
5043 sts = rms_free_search_context(&dirfab);
5044 PerlMem_free(trndir);
5046 PerlMem_free(vmsdir);
5049 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
5050 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
5052 rms_nam_esll(dirnam) -= 9;
5054 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
5055 if (cp1 == NULL) { /* should never happen */
5056 sts = rms_free_search_context(&dirfab);
5057 PerlMem_free(trndir);
5059 PerlMem_free(vmsdir);
5064 retlen = strlen(esa);
5065 cp1 = strrchr(esa,'.');
5066 /* ODS-5 directory specifications can have extra "." in them. */
5067 /* Fix-me, can not scan EFS file specifications backwards */
5068 while (cp1 != NULL) {
5069 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
5073 while ((cp1 > esa) && (*cp1 != '.'))
5080 if ((cp1) != NULL) {
5081 /* There's more than one directory in the path. Just roll back. */
5083 if (buf) retspec = buf;
5084 else if (ts) Newx(retspec,retlen+7,char);
5085 else retspec = __fileify_retbuf;
5086 strcpy(retspec,esa);
5089 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
5090 /* Go back and expand rooted logical name */
5091 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
5092 #ifdef NAM$M_NO_SHORT_UPCASE
5093 if (decc_efs_case_preserve)
5094 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5096 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
5097 sts = rms_free_search_context(&dirfab);
5099 PerlMem_free(trndir);
5100 PerlMem_free(vmsdir);
5102 set_vaxc_errno(dirfab.fab$l_sts);
5105 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
5106 if (buf) retspec = buf;
5107 else if (ts) Newx(retspec,retlen+16,char);
5108 else retspec = __fileify_retbuf;
5109 cp1 = strstr(esa,"][");
5110 if (!cp1) cp1 = strstr(esa,"]<");
5112 memcpy(retspec,esa,dirlen);
5113 if (!strncmp(cp1+2,"000000]",7)) {
5114 retspec[dirlen-1] = '\0';
5115 /* fix-me Not full ODS-5, just extra dots in directories for now */
5116 cp1 = retspec + dirlen - 1;
5117 while (cp1 > retspec)
5122 if (*(cp1-1) != '^')
5127 if (*cp1 == '.') *cp1 = ']';
5129 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5130 memmove(cp1+1,"000000]",7);
5134 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
5135 retspec[retlen] = '\0';
5136 /* Convert last '.' to ']' */
5137 cp1 = retspec+retlen-1;
5138 while (*cp != '[') {
5141 /* Do not trip on extra dots in ODS-5 directories */
5142 if ((cp1 == retspec) || (*(cp1-1) != '^'))
5146 if (*cp1 == '.') *cp1 = ']';
5148 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5149 memmove(cp1+1,"000000]",7);
5153 else { /* This is a top-level dir. Add the MFD to the path. */
5154 if (buf) retspec = buf;
5155 else if (ts) Newx(retspec,retlen+16,char);
5156 else retspec = __fileify_retbuf;
5159 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
5160 strcpy(cp2,":[000000]");
5165 sts = rms_free_search_context(&dirfab);
5166 /* We've set up the string up through the filename. Add the
5167 type and version, and we're done. */
5168 strcat(retspec,".DIR;1");
5170 /* $PARSE may have upcased filespec, so convert output to lower
5171 * case if input contained any lowercase characters. */
5172 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
5173 PerlMem_free(trndir);
5175 PerlMem_free(vmsdir);
5178 } /* end of do_fileify_dirspec() */
5180 /* External entry points */
5181 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
5182 { return do_fileify_dirspec(dir,buf,0); }
5183 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
5184 { return do_fileify_dirspec(dir,buf,1); }
5186 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
5187 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
5189 static char __pathify_retbuf[VMS_MAXRSS];
5190 unsigned long int retlen;
5191 char *retpath, *cp1, *cp2, *trndir;
5192 unsigned short int trnlnm_iter_count;
5196 if (!dir || !*dir) {
5197 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5200 trndir = PerlMem_malloc(VMS_MAXRSS);
5201 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5202 if (*dir) strcpy(trndir,dir);
5203 else getcwd(trndir,VMS_MAXRSS - 1);
5205 trnlnm_iter_count = 0;
5206 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
5207 && my_trnlnm(trndir,trndir,0)) {
5208 trnlnm_iter_count++;
5209 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5210 trnlen = strlen(trndir);
5212 /* Trap simple rooted lnms, and return lnm:[000000] */
5213 if (!strcmp(trndir+trnlen-2,".]")) {
5214 if (buf) retpath = buf;
5215 else if (ts) Newx(retpath,strlen(dir)+10,char);
5216 else retpath = __pathify_retbuf;
5217 strcpy(retpath,dir);
5218 strcat(retpath,":[000000]");
5219 PerlMem_free(trndir);
5224 /* At this point we do not work with *dir, but the copy in
5225 * *trndir that is modifiable.
5228 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
5229 if (*trndir == '.' && (*(trndir+1) == '\0' ||
5230 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
5231 retlen = 2 + (*(trndir+1) != '\0');
5233 if ( !(cp1 = strrchr(trndir,'/')) &&
5234 !(cp1 = strrchr(trndir,']')) &&
5235 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
5236 if ((cp2 = strchr(cp1,'.')) != NULL &&
5237 (*(cp2-1) != '/' || /* Trailing '.', '..', */
5238 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
5239 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
5240 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
5243 /* For EFS or ODS-5 look for the last dot */
5244 if (decc_efs_charset) {
5245 cp2 = strrchr(cp1,'.');
5247 if (vms_process_case_tolerant) {
5248 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5249 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5250 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5251 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5252 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5253 (ver || *cp3)))))) {
5254 PerlMem_free(trndir);
5256 set_vaxc_errno(RMS$_DIR);
5261 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5262 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5263 !*(cp2+3) || *(cp2+3) != 'R' ||
5264 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5265 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5266 (ver || *cp3)))))) {
5267 PerlMem_free(trndir);
5269 set_vaxc_errno(RMS$_DIR);
5273 retlen = cp2 - trndir + 1;
5275 else { /* No file type present. Treat the filename as a directory. */
5276 retlen = strlen(trndir) + 1;
5279 if (buf) retpath = buf;
5280 else if (ts) Newx(retpath,retlen+1,char);
5281 else retpath = __pathify_retbuf;
5282 strncpy(retpath, trndir, retlen-1);
5283 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
5284 retpath[retlen-1] = '/'; /* with '/', add it. */
5285 retpath[retlen] = '\0';
5287 else retpath[retlen-1] = '\0';
5289 else { /* VMS-style directory spec */
5291 unsigned long int sts, cmplen, haslower;
5292 struct FAB dirfab = cc$rms_fab;
5294 rms_setup_nam(savnam);
5295 rms_setup_nam(dirnam);
5297 /* If we've got an explicit filename, we can just shuffle the string. */
5298 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
5299 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
5300 if ((cp2 = strchr(cp1,'.')) != NULL) {
5302 if (vms_process_case_tolerant) {
5303 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5304 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5305 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5306 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5307 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5308 (ver || *cp3)))))) {
5309 PerlMem_free(trndir);
5311 set_vaxc_errno(RMS$_DIR);
5316 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5317 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5318 !*(cp2+3) || *(cp2+3) != 'R' ||
5319 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5320 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5321 (ver || *cp3)))))) {
5322 PerlMem_free(trndir);
5324 set_vaxc_errno(RMS$_DIR);
5329 else { /* No file type, so just draw name into directory part */
5330 for (cp2 = cp1; *cp2; cp2++) ;
5333 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5335 /* We've now got a VMS 'path'; fall through */
5338 dirlen = strlen(trndir);
5339 if (trndir[dirlen-1] == ']' ||
5340 trndir[dirlen-1] == '>' ||
5341 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5342 if (buf) retpath = buf;
5343 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5344 else retpath = __pathify_retbuf;
5345 strcpy(retpath,trndir);
5346 PerlMem_free(trndir);
5349 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5350 esa = PerlMem_malloc(VMS_MAXRSS);
5351 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5352 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5353 rms_bind_fab_nam(dirfab, dirnam);
5354 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5355 #ifdef NAM$M_NO_SHORT_UPCASE
5356 if (decc_efs_case_preserve)
5357 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5360 for (cp = trndir; *cp; cp++)
5361 if (islower(*cp)) { haslower = 1; break; }
5363 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5364 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5365 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5366 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5369 PerlMem_free(trndir);
5372 set_vaxc_errno(dirfab.fab$l_sts);
5378 /* Does the file really exist? */
5379 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5380 if (dirfab.fab$l_sts != RMS$_FNF) {
5382 sts1 = rms_free_search_context(&dirfab);
5383 PerlMem_free(trndir);
5386 set_vaxc_errno(dirfab.fab$l_sts);
5389 dirnam = savnam; /* No; just work with potential name */
5392 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5393 /* Yep; check version while we're at it, if it's there. */
5394 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5395 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5397 /* Something other than .DIR[;1]. Bzzt. */
5398 sts2 = rms_free_search_context(&dirfab);
5399 PerlMem_free(trndir);
5402 set_vaxc_errno(RMS$_DIR);
5406 /* OK, the type was fine. Now pull any file name into the
5408 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5410 cp1 = strrchr(esa,'>');
5411 *(rms_nam_typel(dirnam)) = '>';
5414 *(rms_nam_typel(dirnam) + 1) = '\0';
5415 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5416 if (buf) retpath = buf;
5417 else if (ts) Newx(retpath,retlen,char);
5418 else retpath = __pathify_retbuf;
5419 strcpy(retpath,esa);
5421 sts = rms_free_search_context(&dirfab);
5422 /* $PARSE may have upcased filespec, so convert output to lower
5423 * case if input contained any lowercase characters. */
5424 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5427 PerlMem_free(trndir);
5429 } /* end of do_pathify_dirspec() */
5431 /* External entry points */
5432 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5433 { return do_pathify_dirspec(dir,buf,0); }
5434 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5435 { return do_pathify_dirspec(dir,buf,1); }
5437 /*{{{ char *tounixspec[_ts](char *spec, char *buf)*/
5438 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
5440 static char __tounixspec_retbuf[VMS_MAXRSS];
5441 char *dirend, *rslt, *cp1, *cp3, *tmp;
5443 int devlen, dirlen, retlen = VMS_MAXRSS;
5444 int expand = 1; /* guarantee room for leading and trailing slashes */
5445 unsigned short int trnlnm_iter_count;
5448 if (spec == NULL) return NULL;
5449 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
5450 if (buf) rslt = buf;
5452 Newx(rslt, VMS_MAXRSS, char);
5454 else rslt = __tounixspec_retbuf;
5456 /* New VMS specific format needs translation
5457 * glob passes filenames with trailing '\n' and expects this preserved.
5459 if (decc_posix_compliant_pathnames) {
5460 if (strncmp(spec, "\"^UP^", 5) == 0) {
5466 tunix = PerlMem_malloc(VMS_MAXRSS);
5467 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
5468 strcpy(tunix, spec);
5469 tunix_len = strlen(tunix);
5471 if (tunix[tunix_len - 1] == '\n') {
5472 tunix[tunix_len - 1] = '\"';
5473 tunix[tunix_len] = '\0';
5477 uspec = decc$translate_vms(tunix);
5478 PerlMem_free(tunix);
5479 if ((int)uspec > 0) {
5485 /* If we can not translate it, makemaker wants as-is */
5493 cmp_rslt = 0; /* Presume VMS */
5494 cp1 = strchr(spec, '/');
5498 /* Look for EFS ^/ */
5499 if (decc_efs_charset) {
5500 while (cp1 != NULL) {
5503 /* Found illegal VMS, assume UNIX */
5508 cp1 = strchr(cp1, '/');
5512 /* Look for "." and ".." */
5513 if (decc_filename_unix_report) {
5514 if (spec[0] == '.') {
5515 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5519 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5525 /* This is already UNIX or at least nothing VMS understands */
5533 dirend = strrchr(spec,']');
5534 if (dirend == NULL) dirend = strrchr(spec,'>');
5535 if (dirend == NULL) dirend = strchr(spec,':');
5536 if (dirend == NULL) {
5541 /* Special case 1 - sys$posix_root = / */
5542 #if __CRTL_VER >= 70000000
5543 if (!decc_disable_posix_root) {
5544 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5552 /* Special case 2 - Convert NLA0: to /dev/null */
5553 #if __CRTL_VER < 70000000
5554 cmp_rslt = strncmp(spec,"NLA0:", 5);
5556 cmp_rslt = strncmp(spec,"nla0:", 5);
5558 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5560 if (cmp_rslt == 0) {
5561 strcpy(rslt, "/dev/null");
5564 if (spec[6] != '\0') {
5571 /* Also handle special case "SYS$SCRATCH:" */
5572 #if __CRTL_VER < 70000000
5573 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5575 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5577 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5579 tmp = PerlMem_malloc(VMS_MAXRSS);
5580 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
5581 if (cmp_rslt == 0) {
5584 islnm = my_trnlnm(tmp, "TMP", 0);
5586 strcpy(rslt, "/tmp");
5589 if (spec[12] != '\0') {
5597 if (*cp2 != '[' && *cp2 != '<') {
5600 else { /* the VMS spec begins with directories */
5602 if (*cp2 == ']' || *cp2 == '>') {
5603 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5607 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5608 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
5609 if (ts) Safefree(rslt);
5613 trnlnm_iter_count = 0;
5616 while (*cp3 != ':' && *cp3) cp3++;
5618 if (strchr(cp3,']') != NULL) break;
5619 trnlnm_iter_count++;
5620 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5621 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5623 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5624 retlen = devlen + dirlen;
5625 Renew(rslt,retlen+1+2*expand,char);
5631 *(cp1++) = *(cp3++);
5632 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
5634 return NULL; /* No room */
5639 if ((*cp2 == '^')) {
5640 /* EFS file escape, pass the next character as is */
5641 /* Fix me: HEX encoding for UNICODE not implemented */
5644 else if ( *cp2 == '.') {
5645 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5646 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5653 for (; cp2 <= dirend; cp2++) {
5654 if ((*cp2 == '^')) {
5655 /* EFS file escape, pass the next character as is */
5656 /* Fix me: HEX encoding for UNICODE not implemented */
5662 if (*(cp2+1) == '[') cp2++;
5664 else if (*cp2 == ']' || *cp2 == '>') {
5665 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5667 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5669 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5670 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5671 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5672 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5673 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5675 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5676 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5680 else if (*cp2 == '-') {
5681 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5682 while (*cp2 == '-') {
5684 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5686 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5687 if (ts) Safefree(rslt); /* filespecs like */
5688 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5692 else *(cp1++) = *cp2;
5694 else *(cp1++) = *cp2;
5696 while (*cp2) *(cp1++) = *(cp2++);
5699 /* This still leaves /000000/ when working with a
5700 * VMS device root or concealed root.
5706 ulen = strlen(rslt);
5708 /* Get rid of "000000/ in rooted filespecs */
5710 zeros = strstr(rslt, "/000000/");
5711 if (zeros != NULL) {
5713 mlen = ulen - (zeros - rslt) - 7;
5714 memmove(zeros, &zeros[7], mlen);
5723 } /* end of do_tounixspec() */
5725 /* External entry points */
5726 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
5727 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
5729 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5731 static int posix_to_vmsspec
5732 (char *vmspath, int vmspath_len, const char *unixpath) {
5734 struct FAB myfab = cc$rms_fab;
5735 struct NAML mynam = cc$rms_naml;
5736 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5737 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5743 /* If not a posix spec already, convert it */
5745 unixlen = strlen(unixpath);
5750 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5751 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5754 /* This is already a VMS specification, no conversion */
5756 strncpy(vmspath,unixpath, vmspath_len);
5758 vmspath[vmspath_len] = 0;
5759 if (unixpath[unixlen - 1] == '/')
5761 esa = PerlMem_malloc(VMS_MAXRSS);
5762 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5763 myfab.fab$l_fna = vmspath;
5764 myfab.fab$b_fns = strlen(vmspath);
5765 myfab.fab$l_naml = &mynam;
5766 mynam.naml$l_esa = NULL;
5767 mynam.naml$b_ess = 0;
5768 mynam.naml$l_long_expand = esa;
5769 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
5770 mynam.naml$l_rsa = NULL;
5771 mynam.naml$b_rss = 0;
5772 if (decc_efs_case_preserve)
5773 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
5774 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
5776 /* Set up the remaining naml fields */
5777 sts = sys$parse(&myfab);
5779 /* It failed! Try again as a UNIX filespec */
5785 /* get the Device ID and the FID */
5786 sts = sys$search(&myfab);
5787 /* on any failure, returned the POSIX ^UP^ filespec */
5792 specdsc.dsc$a_pointer = vmspath;
5793 specdsc.dsc$w_length = vmspath_len;
5795 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
5796 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
5797 sts = lib$fid_to_name
5798 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
5800 /* on any failure, returned the POSIX ^UP^ filespec */
5802 /* This can happen if user does not have permission to read directories */
5803 if (strncmp(unixpath,"\"^UP^",5) != 0)
5804 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5806 strcpy(vmspath, unixpath);
5809 vmspath[specdsc.dsc$w_length] = 0;
5811 /* Are we expecting a directory? */
5812 if (dir_flag != 0) {
5818 i = specdsc.dsc$w_length - 1;
5822 /* Version must be '1' */
5823 if (vmspath[i--] != '1')
5825 /* Version delimiter is one of ".;" */
5826 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
5829 if (vmspath[i--] != 'R')
5831 if (vmspath[i--] != 'I')
5833 if (vmspath[i--] != 'D')
5835 if (vmspath[i--] != '.')
5837 eptr = &vmspath[i+1];
5839 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
5840 if (vmspath[i-1] != '^') {
5848 /* Get rid of 6 imaginary zero directory filename */
5849 vmspath[i+1] = '\0';
5853 if (vmspath[i] == '0')
5867 /* Can not use LIB$FID_TO_NAME, so doing a manual conversion */
5868 static int posix_to_vmsspec_hardway
5869 (char *vmspath, int vmspath_len, const char *unixpath) {
5872 const char *unixptr;
5874 const char *lastslash;
5875 const char *lastdot;
5886 /* Ignore leading "/" characters */
5887 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
5890 unixlen = strlen(unixptr);
5892 /* Do nothing with blank paths */
5898 lastslash = strrchr(unixptr,'/');
5899 lastdot = strrchr(unixptr,'.');
5902 /* last dot is last dot or past end of string */
5903 if (lastdot == NULL)
5904 lastdot = unixptr + unixlen;
5906 /* if no directories, set last slash to beginning of string */
5907 if (lastslash == NULL) {
5908 lastslash = unixptr;
5911 /* Watch out for trailing "." after last slash, still a directory */
5912 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
5913 lastslash = unixptr + unixlen;
5916 /* Watch out for traiing ".." after last slash, still a directory */
5917 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
5918 lastslash = unixptr + unixlen;
5921 /* dots in directories are aways escaped */
5922 if (lastdot < lastslash)
5923 lastdot = unixptr + unixlen;
5926 /* if (unixptr < lastslash) then we are in a directory */
5934 /* This could have a "^UP^ on the front */
5935 if (strncmp(unixptr,"\"^UP^",5) == 0) {
5940 /* Start with the UNIX path */
5941 if (*unixptr != '/') {
5942 /* relative paths */
5943 if (lastslash > unixptr) {
5946 /* skip leading ./ */
5948 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
5954 /* Are we still in a directory? */
5955 if (unixptr <= lastslash) {
5960 /* if not backing up, then it is relative forward. */
5961 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
5962 ((unixptr[2] == '/') || (unixptr[2] == '\0')))) {
5970 /* Perl wants an empty directory here to tell the difference
5971 * between a DCL commmand and a filename
5980 /* Handle two special files . and .. */
5981 if (unixptr[0] == '.') {
5982 if (unixptr[1] == '\0') {
5989 if ((unixptr[1] == '.') && (unixptr[2] == '\0')) {
6000 else { /* Absolute PATH handling */
6004 /* Need to find out where root is */
6006 /* In theory, this procedure should never get an absolute POSIX pathname
6007 * that can not be found on the POSIX root.
6008 * In practice, that can not be relied on, and things will show up
6009 * here that are a VMS device name or concealed logical name instead.
6010 * So to make things work, this procedure must be tolerant.
6012 esa = PerlMem_malloc(vmspath_len);
6013 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6016 nextslash = strchr(&unixptr[1],'/');
6018 if (nextslash != NULL) {
6019 seg_len = nextslash - &unixptr[1];
6020 strncpy(vmspath, unixptr, seg_len + 1);
6021 vmspath[seg_len+1] = 0;
6022 sts = posix_to_vmsspec(esa, vmspath_len, vmspath);
6026 /* This is verified to be a real path */
6028 sts = posix_to_vmsspec(esa, vmspath_len, "/");
6029 strcpy(vmspath, esa);
6030 vmslen = strlen(vmspath);
6031 vmsptr = vmspath + vmslen;
6033 if (unixptr < lastslash) {
6042 cmp = strcmp(rptr,"000000.");
6047 } /* removing 6 zeros */
6048 } /* vmslen < 7, no 6 zeros possible */
6049 } /* Not in a directory */
6050 } /* end of verified real path handling */
6055 /* Ok, we have a device or a concealed root that is not in POSIX
6056 * or we have garbage. Make the best of it.
6059 /* Posix to VMS destroyed this, so copy it again */
6060 strncpy(vmspath, &unixptr[1], seg_len);
6061 vmspath[seg_len] = 0;
6063 vmsptr = &vmsptr[vmslen];
6066 /* Now do we need to add the fake 6 zero directory to it? */
6068 if ((*lastslash == '/') && (nextslash < lastslash)) {
6069 /* No there is another directory */
6075 /* now we have foo:bar or foo:[000000]bar to decide from */
6076 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
6077 trnend = islnm ? islnm - 1 : 0;
6079 /* if this was a logical name, ']' or '>' must be present */
6080 /* if not a logical name, then assume a device and hope. */
6081 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
6083 /* if log name and trailing '.' then rooted - treat as device */
6084 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
6086 /* Fix me, if not a logical name, a device lookup should be
6087 * done to see if the device is file structured. If the device
6088 * is not file structured, the 6 zeros should not be put on.
6090 * As it is, perl is occasionally looking for dev:[000000]tty.
6091 * which looks a little strange.
6094 if ((add_6zero == 0) && (*nextslash == '/') && (nextslash[1] == '\0')) {
6095 /* No real directory present */
6100 /* Put the device delimiter on */
6103 unixptr = nextslash;
6106 /* Start directory if needed */
6107 if (!islnm || add_6zero) {
6113 /* add fake 000000] if needed */
6126 } /* non-POSIX translation */
6128 } /* End of relative/absolute path handling */
6130 while ((*unixptr) && (vmslen < vmspath_len)){
6135 if (dir_start != 0) {
6137 /* First characters in a directory are handled special */
6138 while ((*unixptr == '/') ||
6139 ((*unixptr == '.') &&
6140 ((unixptr[1]=='.') || (unixptr[1]=='/') || (unixptr[1]=='\0')))) {
6145 /* Skip redundant / in specification */
6146 while ((*unixptr == '/') && (dir_start != 0)) {
6149 if (unixptr == lastslash)
6152 if (unixptr == lastslash)
6155 /* Skip redundant ./ characters */
6156 while ((*unixptr == '.') &&
6157 ((unixptr[1] == '/')||(unixptr[1] == '\0'))) {
6160 if (unixptr == lastslash)
6162 if (*unixptr == '/')
6165 if (unixptr == lastslash)
6168 /* Skip redundant ../ characters */
6169 while ((*unixptr == '.') && (unixptr[1] == '.') &&
6170 ((unixptr[2] == '/') || (unixptr[2] == '\0'))) {
6171 /* Set the backing up flag */
6177 unixptr++; /* first . */
6178 unixptr++; /* second . */
6179 if (unixptr == lastslash)
6181 if (*unixptr == '/') /* The slash */
6184 if (unixptr == lastslash)
6187 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6188 /* Not needed when VMS is pretending to be UNIX. */
6190 /* Is this loop stuck because of too many dots? */
6191 if (loop_flag == 0) {
6192 /* Exit the loop and pass the rest through */
6197 /* Are we done with directories yet? */
6198 if (unixptr >= lastslash) {
6200 /* Watch out for trailing dots */
6209 if (*unixptr == '/')
6213 /* Have we stopped backing up? */
6218 /* dir_start continues to be = 1 */
6220 if (*unixptr == '-') {
6222 *vmsptr++ = *unixptr++;
6226 /* Now are we done with directories yet? */
6227 if (unixptr >= lastslash) {
6229 /* Watch out for trailing dots */
6245 if (*unixptr == '\0')
6248 /* Normal characters - More EFS work probably needed */
6254 /* remove multiple / */
6255 while (unixptr[1] == '/') {
6258 if (unixptr == lastslash) {
6259 /* Watch out for trailing dots */
6271 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6272 /* Not needed when VMS is pretending to be UNIX. */
6276 if (*unixptr != '\0')
6292 if ((unixptr < lastdot) || (unixptr[1] == '\0')) {
6298 /* trailing dot ==> '^..' on VMS */
6299 if (*unixptr == '\0') {
6303 *vmsptr++ = *unixptr++;
6306 if (quoted && (unixptr[1] == '\0')) {
6311 *vmsptr++ = *unixptr++;
6318 *vmsptr++ = *unixptr++;
6322 if (*unixptr != '\0') {
6323 *vmsptr++ = *unixptr++;
6330 /* Make sure directory is closed */
6331 if (unixptr == lastslash) {
6333 vmsptr2 = vmsptr - 1;
6335 if (*vmsptr2 != ']') {
6338 /* directories do not end in a dot bracket */
6339 if (*vmsptr2 == '.') {
6343 if (*vmsptr2 != '^') {
6344 vmsptr--; /* back up over the dot */
6352 /* Add a trailing dot if a file with no extension */
6353 vmsptr2 = vmsptr - 1;
6354 if ((*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
6355 (*lastdot != '.')) {
6366 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
6367 static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
6368 static char __tovmsspec_retbuf[VMS_MAXRSS];
6369 char *rslt, *dirend;
6374 unsigned long int infront = 0, hasdir = 1;
6378 if (path == NULL) return NULL;
6379 rslt_len = VMS_MAXRSS-1;
6380 if (buf) rslt = buf;
6381 else if (ts) Newx(rslt, VMS_MAXRSS, char);
6382 else rslt = __tovmsspec_retbuf;
6383 if (strpbrk(path,"]:>") ||
6384 (dirend = strrchr(path,'/')) == NULL) {
6385 if (path[0] == '.') {
6386 if (path[1] == '\0') strcpy(rslt,"[]");
6387 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
6388 else strcpy(rslt,path); /* probably garbage */
6390 else strcpy(rslt,path);
6394 /* Posix specifications are now a native VMS format */
6395 /*--------------------------------------------------*/
6396 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6397 if (decc_posix_compliant_pathnames) {
6398 if (strncmp(path,"\"^UP^",5) == 0) {
6399 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6405 vms_delim = strpbrk(path,"]:>");
6407 if ((vms_delim != NULL) ||
6408 ((dirend = strrchr(path,'/')) == NULL)) {
6410 /* VMS special characters found! */
6412 if (path[0] == '.') {
6413 if (path[1] == '\0') strcpy(rslt,"[]");
6414 else if (path[1] == '.' && path[2] == '\0')
6417 /* Dot preceeding a device or directory ? */
6419 /* If not in POSIX mode, pass it through and hope it works */
6420 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6421 if (!decc_posix_compliant_pathnames)
6422 strcpy(rslt,path); /* probably garbage */
6424 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6426 strcpy(rslt,path); /* probably garbage */
6432 /* If no VMS characters and in POSIX mode, convert it!
6433 * This is the easiest way to get directory specifications
6434 * handled correctly in POSIX mode
6436 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6437 if ((vms_delim == NULL) && decc_posix_compliant_pathnames)
6438 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6440 /* No unix path separators - presume VMS already */
6444 strcpy(rslt,path); /* probably garbage */
6450 /* If POSIX mode active, handle the conversion */
6451 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6452 if (decc_posix_compliant_pathnames) {
6453 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6458 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
6459 if (!*(dirend+2)) dirend +=2;
6460 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
6461 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
6466 lastdot = strrchr(cp2,'.');
6472 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6474 if (decc_disable_posix_root) {
6475 strcpy(rslt,"sys$disk:[000000]");
6478 strcpy(rslt,"sys$posix_root:[000000]");
6482 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
6484 trndev = PerlMem_malloc(VMS_MAXRSS);
6485 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
6486 islnm = my_trnlnm(rslt,trndev,0);
6488 /* DECC special handling */
6490 if (strcmp(rslt,"bin") == 0) {
6491 strcpy(rslt,"sys$system");
6494 islnm = my_trnlnm(rslt,trndev,0);
6496 else if (strcmp(rslt,"tmp") == 0) {
6497 strcpy(rslt,"sys$scratch");
6500 islnm = my_trnlnm(rslt,trndev,0);
6502 else if (!decc_disable_posix_root) {
6503 strcpy(rslt, "sys$posix_root");
6507 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6508 islnm = my_trnlnm(rslt,trndev,0);
6510 else if (strcmp(rslt,"dev") == 0) {
6511 if (strncmp(cp2,"/null", 5) == 0) {
6512 if ((cp2[5] == 0) || (cp2[5] == '/')) {
6513 strcpy(rslt,"NLA0");
6517 islnm = my_trnlnm(rslt,trndev,0);
6523 trnend = islnm ? strlen(trndev) - 1 : 0;
6524 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
6525 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
6526 /* If the first element of the path is a logical name, determine
6527 * whether it has to be translated so we can add more directories. */
6528 if (!islnm || rooted) {
6531 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
6535 if (cp2 != dirend) {
6536 strcpy(rslt,trndev);
6537 cp1 = rslt + trnend;
6544 if (decc_disable_posix_root) {
6550 PerlMem_free(trndev);
6555 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
6556 cp2 += 2; /* skip over "./" - it's redundant */
6557 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
6559 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6560 *(cp1++) = '-'; /* "../" --> "-" */
6563 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
6564 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
6565 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6566 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
6569 else if ((cp2 != lastdot) || (lastdot < dirend)) {
6570 /* Escape the extra dots in EFS file specifications */
6573 if (cp2 > dirend) cp2 = dirend;
6575 else *(cp1++) = '.';
6577 for (; cp2 < dirend; cp2++) {
6579 if (*(cp2-1) == '/') continue;
6580 if (*(cp1-1) != '.') *(cp1++) = '.';
6583 else if (!infront && *cp2 == '.') {
6584 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
6585 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
6586 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6587 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
6588 else if (*(cp1-2) == '[') *(cp1-1) = '-';
6589 else { /* back up over previous directory name */
6591 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
6592 if (*(cp1-1) == '[') {
6593 memcpy(cp1,"000000.",7);
6598 if (cp2 == dirend) break;
6600 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
6601 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
6602 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
6603 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6605 *(cp1++) = '.'; /* Simulate trailing '/' */
6606 cp2 += 2; /* for loop will incr this to == dirend */
6608 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
6611 if (decc_efs_charset == 0)
6612 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
6614 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
6620 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
6622 if (decc_efs_charset == 0)
6629 else *(cp1++) = *cp2;
6633 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
6634 if (hasdir) *(cp1++) = ']';
6635 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
6636 /* fixme for ODS5 */
6651 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
6652 decc_readdir_dropdotnotype) {
6657 /* trailing dot ==> '^..' on VMS */
6664 *(cp1++) = *(cp2++);
6692 *(cp1++) = *(cp2++);
6695 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
6696 * which is wrong. UNIX notation should be ".dir." unless
6697 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
6698 * changing this behavior could break more things at this time.
6699 * efs character set effectively does not allow "." to be a version
6700 * delimiter as a further complication about changing this.
6702 if (decc_filename_unix_report != 0) {
6705 *(cp1++) = *(cp2++);
6708 *(cp1++) = *(cp2++);
6711 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
6715 /* Fix me for "^]", but that requires making sure that you do
6716 * not back up past the start of the filename
6718 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
6725 } /* end of do_tovmsspec() */
6727 /* External entry points */
6728 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,0); }
6729 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,1); }
6731 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
6732 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts) {
6733 static char __tovmspath_retbuf[VMS_MAXRSS];
6735 char *pathified, *vmsified, *cp;
6737 if (path == NULL) return NULL;
6738 pathified = PerlMem_malloc(VMS_MAXRSS);
6739 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
6740 if (do_pathify_dirspec(path,pathified,0) == NULL) {
6741 PerlMem_free(pathified);
6747 Newx(vmsified, VMS_MAXRSS, char);
6748 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0) == NULL) {
6749 PerlMem_free(pathified);
6750 if (vmsified) Safefree(vmsified);
6753 PerlMem_free(pathified);
6758 vmslen = strlen(vmsified);
6759 Newx(cp,vmslen+1,char);
6760 memcpy(cp,vmsified,vmslen);
6766 strcpy(__tovmspath_retbuf,vmsified);
6768 return __tovmspath_retbuf;
6771 } /* end of do_tovmspath() */
6773 /* External entry points */
6774 char *Perl_tovmspath(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,0); }
6775 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,1); }
6778 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
6779 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts) {
6780 static char __tounixpath_retbuf[VMS_MAXRSS];
6782 char *pathified, *unixified, *cp;
6784 if (path == NULL) return NULL;
6785 pathified = PerlMem_malloc(VMS_MAXRSS);
6786 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
6787 if (do_pathify_dirspec(path,pathified,0) == NULL) {
6788 PerlMem_free(pathified);
6794 Newx(unixified, VMS_MAXRSS, char);
6796 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) {
6797 PerlMem_free(pathified);
6798 if (unixified) Safefree(unixified);
6801 PerlMem_free(pathified);
6806 unixlen = strlen(unixified);
6807 Newx(cp,unixlen+1,char);
6808 memcpy(cp,unixified,unixlen);
6810 Safefree(unixified);
6814 strcpy(__tounixpath_retbuf,unixified);
6815 Safefree(unixified);
6816 return __tounixpath_retbuf;
6819 } /* end of do_tounixpath() */
6821 /* External entry points */
6822 char *Perl_tounixpath(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,0); }
6823 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,1); }
6826 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
6828 *****************************************************************************
6830 * Copyright (C) 1989-1994 by *
6831 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
6833 * Permission is hereby granted for the reproduction of this software, *
6834 * on condition that this copyright notice is included in the reproduction, *
6835 * and that such reproduction is not for purposes of profit or material *
6838 * 27-Aug-1994 Modified for inclusion in perl5 *
6839 * by Charles Bailey bailey@newman.upenn.edu *
6840 *****************************************************************************
6844 * getredirection() is intended to aid in porting C programs
6845 * to VMS (Vax-11 C). The native VMS environment does not support
6846 * '>' and '<' I/O redirection, or command line wild card expansion,
6847 * or a command line pipe mechanism using the '|' AND background
6848 * command execution '&'. All of these capabilities are provided to any
6849 * C program which calls this procedure as the first thing in the
6851 * The piping mechanism will probably work with almost any 'filter' type
6852 * of program. With suitable modification, it may useful for other
6853 * portability problems as well.
6855 * Author: Mark Pizzolato mark@infocomm.com
6859 struct list_item *next;
6863 static void add_item(struct list_item **head,
6864 struct list_item **tail,
6868 static void mp_expand_wild_cards(pTHX_ char *item,
6869 struct list_item **head,
6870 struct list_item **tail,
6873 static int background_process(pTHX_ int argc, char **argv);
6875 static void pipe_and_fork(pTHX_ char **cmargv);
6877 /*{{{ void getredirection(int *ac, char ***av)*/
6879 mp_getredirection(pTHX_ int *ac, char ***av)
6881 * Process vms redirection arg's. Exit if any error is seen.
6882 * If getredirection() processes an argument, it is erased
6883 * from the vector. getredirection() returns a new argc and argv value.
6884 * In the event that a background command is requested (by a trailing "&"),
6885 * this routine creates a background subprocess, and simply exits the program.
6887 * Warning: do not try to simplify the code for vms. The code
6888 * presupposes that getredirection() is called before any data is
6889 * read from stdin or written to stdout.
6891 * Normal usage is as follows:
6897 * getredirection(&argc, &argv);
6901 int argc = *ac; /* Argument Count */
6902 char **argv = *av; /* Argument Vector */
6903 char *ap; /* Argument pointer */
6904 int j; /* argv[] index */
6905 int item_count = 0; /* Count of Items in List */
6906 struct list_item *list_head = 0; /* First Item in List */
6907 struct list_item *list_tail; /* Last Item in List */
6908 char *in = NULL; /* Input File Name */
6909 char *out = NULL; /* Output File Name */
6910 char *outmode = "w"; /* Mode to Open Output File */
6911 char *err = NULL; /* Error File Name */
6912 char *errmode = "w"; /* Mode to Open Error File */
6913 int cmargc = 0; /* Piped Command Arg Count */
6914 char **cmargv = NULL;/* Piped Command Arg Vector */
6917 * First handle the case where the last thing on the line ends with
6918 * a '&'. This indicates the desire for the command to be run in a
6919 * subprocess, so we satisfy that desire.
6922 if (0 == strcmp("&", ap))
6923 exit(background_process(aTHX_ --argc, argv));
6924 if (*ap && '&' == ap[strlen(ap)-1])
6926 ap[strlen(ap)-1] = '\0';
6927 exit(background_process(aTHX_ argc, argv));
6930 * Now we handle the general redirection cases that involve '>', '>>',
6931 * '<', and pipes '|'.
6933 for (j = 0; j < argc; ++j)
6935 if (0 == strcmp("<", argv[j]))
6939 fprintf(stderr,"No input file after < on command line");
6940 exit(LIB$_WRONUMARG);
6945 if ('<' == *(ap = argv[j]))
6950 if (0 == strcmp(">", ap))
6954 fprintf(stderr,"No output file after > on command line");
6955 exit(LIB$_WRONUMARG);
6974 fprintf(stderr,"No output file after > or >> on command line");
6975 exit(LIB$_WRONUMARG);
6979 if (('2' == *ap) && ('>' == ap[1]))
6996 fprintf(stderr,"No output file after 2> or 2>> on command line");
6997 exit(LIB$_WRONUMARG);
7001 if (0 == strcmp("|", argv[j]))
7005 fprintf(stderr,"No command into which to pipe on command line");
7006 exit(LIB$_WRONUMARG);
7008 cmargc = argc-(j+1);
7009 cmargv = &argv[j+1];
7013 if ('|' == *(ap = argv[j]))
7021 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
7024 * Allocate and fill in the new argument vector, Some Unix's terminate
7025 * the list with an extra null pointer.
7027 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
7028 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7030 for (j = 0; j < item_count; ++j, list_head = list_head->next)
7031 argv[j] = list_head->value;
7037 fprintf(stderr,"'|' and '>' may not both be specified on command line");
7038 exit(LIB$_INVARGORD);
7040 pipe_and_fork(aTHX_ cmargv);
7043 /* Check for input from a pipe (mailbox) */
7045 if (in == NULL && 1 == isapipe(0))
7047 char mbxname[L_tmpnam];
7049 long int dvi_item = DVI$_DEVBUFSIZ;
7050 $DESCRIPTOR(mbxnam, "");
7051 $DESCRIPTOR(mbxdevnam, "");
7053 /* Input from a pipe, reopen it in binary mode to disable */
7054 /* carriage control processing. */
7056 fgetname(stdin, mbxname);
7057 mbxnam.dsc$a_pointer = mbxname;
7058 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
7059 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
7060 mbxdevnam.dsc$a_pointer = mbxname;
7061 mbxdevnam.dsc$w_length = sizeof(mbxname);
7062 dvi_item = DVI$_DEVNAM;
7063 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
7064 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
7067 freopen(mbxname, "rb", stdin);
7070 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
7074 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
7076 fprintf(stderr,"Can't open input file %s as stdin",in);
7079 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
7081 fprintf(stderr,"Can't open output file %s as stdout",out);
7084 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
7087 if (strcmp(err,"&1") == 0) {
7088 dup2(fileno(stdout), fileno(stderr));
7089 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
7092 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
7094 fprintf(stderr,"Can't open error file %s as stderr",err);
7098 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
7102 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
7105 #ifdef ARGPROC_DEBUG
7106 PerlIO_printf(Perl_debug_log, "Arglist:\n");
7107 for (j = 0; j < *ac; ++j)
7108 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
7110 /* Clear errors we may have hit expanding wildcards, so they don't
7111 show up in Perl's $! later */
7112 set_errno(0); set_vaxc_errno(1);
7113 } /* end of getredirection() */
7116 static void add_item(struct list_item **head,
7117 struct list_item **tail,
7123 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7124 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7128 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7129 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7130 *tail = (*tail)->next;
7132 (*tail)->value = value;
7136 static void mp_expand_wild_cards(pTHX_ char *item,
7137 struct list_item **head,
7138 struct list_item **tail,
7142 unsigned long int context = 0;
7150 $DESCRIPTOR(filespec, "");
7151 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
7152 $DESCRIPTOR(resultspec, "");
7153 unsigned long int lff_flags = 0;
7157 #ifdef VMS_LONGNAME_SUPPORT
7158 lff_flags = LIB$M_FIL_LONG_NAMES;
7161 for (cp = item; *cp; cp++) {
7162 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
7163 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
7165 if (!*cp || isspace(*cp))
7167 add_item(head, tail, item, count);
7172 /* "double quoted" wild card expressions pass as is */
7173 /* From DCL that means using e.g.: */
7174 /* perl program """perl.*""" */
7175 item_len = strlen(item);
7176 if ( '"' == *item && '"' == item[item_len-1] )
7179 item[item_len-2] = '\0';
7180 add_item(head, tail, item, count);
7184 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
7185 resultspec.dsc$b_class = DSC$K_CLASS_D;
7186 resultspec.dsc$a_pointer = NULL;
7187 vmsspec = PerlMem_malloc(VMS_MAXRSS);
7188 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7189 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
7190 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
7191 if (!isunix || !filespec.dsc$a_pointer)
7192 filespec.dsc$a_pointer = item;
7193 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
7195 * Only return version specs, if the caller specified a version
7197 had_version = strchr(item, ';');
7199 * Only return device and directory specs, if the caller specifed either.
7201 had_device = strchr(item, ':');
7202 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
7204 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
7205 (&filespec, &resultspec, &context,
7206 &defaultspec, 0, &rms_sts, &lff_flags)))
7211 string = PerlMem_malloc(resultspec.dsc$w_length+1);
7212 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7213 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
7214 string[resultspec.dsc$w_length] = '\0';
7215 if (NULL == had_version)
7216 *(strrchr(string, ';')) = '\0';
7217 if ((!had_directory) && (had_device == NULL))
7219 if (NULL == (devdir = strrchr(string, ']')))
7220 devdir = strrchr(string, '>');
7221 strcpy(string, devdir + 1);
7224 * Be consistent with what the C RTL has already done to the rest of
7225 * the argv items and lowercase all of these names.
7227 if (!decc_efs_case_preserve) {
7228 for (c = string; *c; ++c)
7232 if (isunix) trim_unixpath(string,item,1);
7233 add_item(head, tail, string, count);
7236 PerlMem_free(vmsspec);
7237 if (sts != RMS$_NMF)
7239 set_vaxc_errno(sts);
7242 case RMS$_FNF: case RMS$_DNF:
7243 set_errno(ENOENT); break;
7245 set_errno(ENOTDIR); break;
7247 set_errno(ENODEV); break;
7248 case RMS$_FNM: case RMS$_SYN:
7249 set_errno(EINVAL); break;
7251 set_errno(EACCES); break;
7253 _ckvmssts_noperl(sts);
7257 add_item(head, tail, item, count);
7258 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
7259 _ckvmssts_noperl(lib$find_file_end(&context));
7262 static int child_st[2];/* Event Flag set when child process completes */
7264 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
7266 static unsigned long int exit_handler(int *status)
7270 if (0 == child_st[0])
7272 #ifdef ARGPROC_DEBUG
7273 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
7275 fflush(stdout); /* Have to flush pipe for binary data to */
7276 /* terminate properly -- <tp@mccall.com> */
7277 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
7278 sys$dassgn(child_chan);
7280 sys$synch(0, child_st);
7285 static void sig_child(int chan)
7287 #ifdef ARGPROC_DEBUG
7288 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
7290 if (child_st[0] == 0)
7294 static struct exit_control_block exit_block =
7299 &exit_block.exit_status,
7304 pipe_and_fork(pTHX_ char **cmargv)
7307 struct dsc$descriptor_s *vmscmd;
7308 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
7309 int sts, j, l, ismcr, quote, tquote = 0;
7311 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
7312 vms_execfree(vmscmd);
7317 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
7318 && toupper(*(q+2)) == 'R' && !*(q+3);
7320 while (q && l < MAX_DCL_LINE_LENGTH) {
7322 if (j > 0 && quote) {
7328 if (ismcr && j > 1) quote = 1;
7329 tquote = (strchr(q,' ')) != NULL || *q == '\0';
7332 if (quote || tquote) {
7338 if ((quote||tquote) && *q == '"') {
7348 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
7350 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
7354 static int background_process(pTHX_ int argc, char **argv)
7356 char command[MAX_DCL_SYMBOL + 1] = "$";
7357 $DESCRIPTOR(value, "");
7358 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
7359 static $DESCRIPTOR(null, "NLA0:");
7360 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
7362 $DESCRIPTOR(pidstr, "");
7364 unsigned long int flags = 17, one = 1, retsts;
7367 strcat(command, argv[0]);
7368 len = strlen(command);
7369 while (--argc && (len < MAX_DCL_SYMBOL))
7371 strcat(command, " \"");
7372 strcat(command, *(++argv));
7373 strcat(command, "\"");
7374 len = strlen(command);
7376 value.dsc$a_pointer = command;
7377 value.dsc$w_length = strlen(value.dsc$a_pointer);
7378 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
7379 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
7380 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
7381 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
7384 _ckvmssts_noperl(retsts);
7386 #ifdef ARGPROC_DEBUG
7387 PerlIO_printf(Perl_debug_log, "%s\n", command);
7389 sprintf(pidstring, "%08X", pid);
7390 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
7391 pidstr.dsc$a_pointer = pidstring;
7392 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
7393 lib$set_symbol(&pidsymbol, &pidstr);
7397 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
7400 /* OS-specific initialization at image activation (not thread startup) */
7401 /* Older VAXC header files lack these constants */
7402 #ifndef JPI$_RIGHTS_SIZE
7403 # define JPI$_RIGHTS_SIZE 817
7405 #ifndef KGB$M_SUBSYSTEM
7406 # define KGB$M_SUBSYSTEM 0x8
7409 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
7411 /*{{{void vms_image_init(int *, char ***)*/
7413 vms_image_init(int *argcp, char ***argvp)
7415 char eqv[LNM$C_NAMLENGTH+1] = "";
7416 unsigned int len, tabct = 8, tabidx = 0;
7417 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
7418 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
7419 unsigned short int dummy, rlen;
7420 struct dsc$descriptor_s **tabvec;
7421 #if defined(PERL_IMPLICIT_CONTEXT)
7424 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
7425 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
7426 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
7429 #ifdef KILL_BY_SIGPRC
7430 Perl_csighandler_init();
7433 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
7434 _ckvmssts_noperl(iosb[0]);
7435 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
7436 if (iprv[i]) { /* Running image installed with privs? */
7437 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
7442 /* Rights identifiers might trigger tainting as well. */
7443 if (!will_taint && (rlen || rsz)) {
7444 while (rlen < rsz) {
7445 /* We didn't get all the identifiers on the first pass. Allocate a
7446 * buffer much larger than $GETJPI wants (rsz is size in bytes that
7447 * were needed to hold all identifiers at time of last call; we'll
7448 * allocate that many unsigned long ints), and go back and get 'em.
7449 * If it gave us less than it wanted to despite ample buffer space,
7450 * something's broken. Is your system missing a system identifier?
7452 if (rsz <= jpilist[1].buflen) {
7453 /* Perl_croak accvios when used this early in startup. */
7454 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
7455 rsz, (unsigned long) jpilist[1].buflen,
7456 "Check your rights database for corruption.\n");
7459 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
7460 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
7461 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7462 jpilist[1].buflen = rsz * sizeof(unsigned long int);
7463 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
7464 _ckvmssts_noperl(iosb[0]);
7466 mask = jpilist[1].bufadr;
7467 /* Check attribute flags for each identifier (2nd longword); protected
7468 * subsystem identifiers trigger tainting.
7470 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
7471 if (mask[i] & KGB$M_SUBSYSTEM) {
7476 if (mask != rlst) PerlMem_free(mask);
7479 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
7480 * logical, some versions of the CRTL will add a phanthom /000000/
7481 * directory. This needs to be removed.
7483 if (decc_filename_unix_report) {
7486 ulen = strlen(argvp[0][0]);
7488 zeros = strstr(argvp[0][0], "/000000/");
7489 if (zeros != NULL) {
7491 mlen = ulen - (zeros - argvp[0][0]) - 7;
7492 memmove(zeros, &zeros[7], mlen);
7494 argvp[0][0][ulen] = '\0';
7497 /* It also may have a trailing dot that needs to be removed otherwise
7498 * it will be converted to VMS mode incorrectly.
7501 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
7502 argvp[0][0][ulen] = '\0';
7505 /* We need to use this hack to tell Perl it should run with tainting,
7506 * since its tainting flag may be part of the PL_curinterp struct, which
7507 * hasn't been allocated when vms_image_init() is called.
7510 char **newargv, **oldargv;
7512 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
7513 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7514 newargv[0] = oldargv[0];
7515 newargv[1] = PerlMem_malloc(3 * sizeof(char));
7516 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7517 strcpy(newargv[1], "-T");
7518 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
7520 newargv[*argcp] = NULL;
7521 /* We orphan the old argv, since we don't know where it's come from,
7522 * so we don't know how to free it.
7526 else { /* Did user explicitly request tainting? */
7528 char *cp, **av = *argvp;
7529 for (i = 1; i < *argcp; i++) {
7530 if (*av[i] != '-') break;
7531 for (cp = av[i]+1; *cp; cp++) {
7532 if (*cp == 'T') { will_taint = 1; break; }
7533 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
7534 strchr("DFIiMmx",*cp)) break;
7536 if (will_taint) break;
7541 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
7544 tabvec = (struct dsc$descriptor_s **)
7545 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
7546 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7548 else if (tabidx >= tabct) {
7550 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
7551 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7553 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
7554 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7555 tabvec[tabidx]->dsc$w_length = 0;
7556 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
7557 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
7558 tabvec[tabidx]->dsc$a_pointer = NULL;
7559 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
7561 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
7563 getredirection(argcp,argvp);
7564 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
7566 # include <reentrancy.h>
7567 decc$set_reentrancy(C$C_MULTITHREAD);
7576 * Trim Unix-style prefix off filespec, so it looks like what a shell
7577 * glob expansion would return (i.e. from specified prefix on, not
7578 * full path). Note that returned filespec is Unix-style, regardless
7579 * of whether input filespec was VMS-style or Unix-style.
7581 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
7582 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
7583 * vector of options; at present, only bit 0 is used, and if set tells
7584 * trim unixpath to try the current default directory as a prefix when
7585 * presented with a possibly ambiguous ... wildcard.
7587 * Returns !=0 on success, with trimmed filespec replacing contents of
7588 * fspec, and 0 on failure, with contents of fpsec unchanged.
7590 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
7592 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
7594 char *unixified, *unixwild,
7595 *template, *base, *end, *cp1, *cp2;
7596 register int tmplen, reslen = 0, dirs = 0;
7598 unixwild = PerlMem_malloc(VMS_MAXRSS);
7599 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
7600 if (!wildspec || !fspec) return 0;
7601 template = unixwild;
7602 if (strpbrk(wildspec,"]>:") != NULL) {
7603 if (do_tounixspec(wildspec,unixwild,0) == NULL) {
7604 PerlMem_free(unixwild);
7609 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
7610 unixwild[VMS_MAXRSS-1] = 0;
7612 unixified = PerlMem_malloc(VMS_MAXRSS);
7613 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
7614 if (strpbrk(fspec,"]>:") != NULL) {
7615 if (do_tounixspec(fspec,unixified,0) == NULL) {
7616 PerlMem_free(unixwild);
7617 PerlMem_free(unixified);
7620 else base = unixified;
7621 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
7622 * check to see that final result fits into (isn't longer than) fspec */
7623 reslen = strlen(fspec);
7627 /* No prefix or absolute path on wildcard, so nothing to remove */
7628 if (!*template || *template == '/') {
7629 PerlMem_free(unixwild);
7630 if (base == fspec) {
7631 PerlMem_free(unixified);
7634 tmplen = strlen(unixified);
7635 if (tmplen > reslen) {
7636 PerlMem_free(unixified);
7637 return 0; /* not enough space */
7639 /* Copy unixified resultant, including trailing NUL */
7640 memmove(fspec,unixified,tmplen+1);
7641 PerlMem_free(unixified);
7645 for (end = base; *end; end++) ; /* Find end of resultant filespec */
7646 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
7647 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
7648 for (cp1 = end ;cp1 >= base; cp1--)
7649 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
7651 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
7652 PerlMem_free(unixified);
7653 PerlMem_free(unixwild);
7658 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
7659 int ells = 1, totells, segdirs, match;
7660 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
7661 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7663 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
7665 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
7666 tpl = PerlMem_malloc(VMS_MAXRSS);
7667 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
7668 if (ellipsis == template && opts & 1) {
7669 /* Template begins with an ellipsis. Since we can't tell how many
7670 * directory names at the front of the resultant to keep for an
7671 * arbitrary starting point, we arbitrarily choose the current
7672 * default directory as a starting point. If it's there as a prefix,
7673 * clip it off. If not, fall through and act as if the leading
7674 * ellipsis weren't there (i.e. return shortest possible path that
7675 * could match template).
7677 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
7679 PerlMem_free(unixified);
7680 PerlMem_free(unixwild);
7683 if (!decc_efs_case_preserve) {
7684 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7685 if (_tolower(*cp1) != _tolower(*cp2)) break;
7687 segdirs = dirs - totells; /* Min # of dirs we must have left */
7688 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
7689 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
7690 memmove(fspec,cp2+1,end - cp2);
7692 PerlMem_free(unixified);
7693 PerlMem_free(unixwild);
7697 /* First off, back up over constant elements at end of path */
7699 for (front = end ; front >= base; front--)
7700 if (*front == '/' && !dirs--) { front++; break; }
7702 lcres = PerlMem_malloc(VMS_MAXRSS);
7703 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
7704 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
7706 if (!decc_efs_case_preserve) {
7707 *cp2 = _tolower(*cp1); /* Make lc copy for match */
7715 PerlMem_free(unixified);
7716 PerlMem_free(unixwild);
7717 PerlMem_free(lcres);
7718 return 0; /* Path too long. */
7721 *cp2 = '\0'; /* Pick up with memcpy later */
7722 lcfront = lcres + (front - base);
7723 /* Now skip over each ellipsis and try to match the path in front of it. */
7725 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
7726 if (*(cp1) == '.' && *(cp1+1) == '.' &&
7727 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
7728 if (cp1 < template) break; /* template started with an ellipsis */
7729 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
7730 ellipsis = cp1; continue;
7732 wilddsc.dsc$a_pointer = tpl;
7733 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
7735 for (segdirs = 0, cp2 = tpl;
7736 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
7738 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
7740 if (!decc_efs_case_preserve) {
7741 *cp2 = _tolower(*cp1); /* else lowercase for match */
7744 *cp2 = *cp1; /* else preserve case for match */
7747 if (*cp2 == '/') segdirs++;
7749 if (cp1 != ellipsis - 1) {
7751 PerlMem_free(unixified);
7752 PerlMem_free(unixwild);
7753 PerlMem_free(lcres);
7754 return 0; /* Path too long */
7756 /* Back up at least as many dirs as in template before matching */
7757 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
7758 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
7759 for (match = 0; cp1 > lcres;) {
7760 resdsc.dsc$a_pointer = cp1;
7761 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
7763 if (match == 1) lcfront = cp1;
7765 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
7769 PerlMem_free(unixified);
7770 PerlMem_free(unixwild);
7771 PerlMem_free(lcres);
7772 return 0; /* Can't find prefix ??? */
7774 if (match > 1 && opts & 1) {
7775 /* This ... wildcard could cover more than one set of dirs (i.e.
7776 * a set of similar dir names is repeated). If the template
7777 * contains more than 1 ..., upstream elements could resolve the
7778 * ambiguity, but it's not worth a full backtracking setup here.
7779 * As a quick heuristic, clip off the current default directory
7780 * if it's present to find the trimmed spec, else use the
7781 * shortest string that this ... could cover.
7783 char def[NAM$C_MAXRSS+1], *st;
7785 if (getcwd(def, sizeof def,0) == NULL) {
7786 Safefree(unixified);
7792 if (!decc_efs_case_preserve) {
7793 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7794 if (_tolower(*cp1) != _tolower(*cp2)) break;
7796 segdirs = dirs - totells; /* Min # of dirs we must have left */
7797 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
7798 if (*cp1 == '\0' && *cp2 == '/') {
7799 memmove(fspec,cp2+1,end - cp2);
7801 PerlMem_free(unixified);
7802 PerlMem_free(unixwild);
7803 PerlMem_free(lcres);
7806 /* Nope -- stick with lcfront from above and keep going. */
7809 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
7811 PerlMem_free(unixified);
7812 PerlMem_free(unixwild);
7813 PerlMem_free(lcres);
7818 } /* end of trim_unixpath() */
7823 * VMS readdir() routines.
7824 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
7826 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
7827 * Minor modifications to original routines.
7830 /* readdir may have been redefined by reentr.h, so make sure we get
7831 * the local version for what we do here.
7836 #if !defined(PERL_IMPLICIT_CONTEXT)
7837 # define readdir Perl_readdir
7839 # define readdir(a) Perl_readdir(aTHX_ a)
7842 /* Number of elements in vms_versions array */
7843 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
7846 * Open a directory, return a handle for later use.
7848 /*{{{ DIR *opendir(char*name) */
7850 Perl_opendir(pTHX_ const char *name)
7858 if (decc_efs_charset) {
7859 unix_flag = is_unix_filespec(name);
7862 Newx(dir, VMS_MAXRSS, char);
7863 if (do_tovmspath(name,dir,0) == NULL) {
7867 /* Check access before stat; otherwise stat does not
7868 * accurately report whether it's a directory.
7870 if (!cando_by_name(S_IRUSR,0,dir)) {
7871 /* cando_by_name has already set errno */
7875 if (flex_stat(dir,&sb) == -1) return NULL;
7876 if (!S_ISDIR(sb.st_mode)) {
7878 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
7881 /* Get memory for the handle, and the pattern. */
7883 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
7885 /* Fill in the fields; mainly playing with the descriptor. */
7886 sprintf(dd->pattern, "%s*.*",dir);
7892 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
7893 dd->pat.dsc$a_pointer = dd->pattern;
7894 dd->pat.dsc$w_length = strlen(dd->pattern);
7895 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
7896 dd->pat.dsc$b_class = DSC$K_CLASS_S;
7897 #if defined(USE_ITHREADS)
7898 Newx(dd->mutex,1,perl_mutex);
7899 MUTEX_INIT( (perl_mutex *) dd->mutex );
7905 } /* end of opendir() */
7909 * Set the flag to indicate we want versions or not.
7911 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
7913 vmsreaddirversions(DIR *dd, int flag)
7916 dd->flags |= PERL_VMSDIR_M_VERSIONS;
7918 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
7923 * Free up an opened directory.
7925 /*{{{ void closedir(DIR *dd)*/
7927 Perl_closedir(DIR *dd)
7931 sts = lib$find_file_end(&dd->context);
7932 Safefree(dd->pattern);
7933 #if defined(USE_ITHREADS)
7934 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
7935 Safefree(dd->mutex);
7942 * Collect all the version numbers for the current file.
7945 collectversions(pTHX_ DIR *dd)
7947 struct dsc$descriptor_s pat;
7948 struct dsc$descriptor_s res;
7950 char *p, *text, *buff;
7952 unsigned long context, tmpsts;
7954 /* Convenient shorthand. */
7957 /* Add the version wildcard, ignoring the "*.*" put on before */
7958 i = strlen(dd->pattern);
7959 Newx(text,i + e->d_namlen + 3,char);
7960 strcpy(text, dd->pattern);
7961 sprintf(&text[i - 3], "%s;*", e->d_name);
7963 /* Set up the pattern descriptor. */
7964 pat.dsc$a_pointer = text;
7965 pat.dsc$w_length = i + e->d_namlen - 1;
7966 pat.dsc$b_dtype = DSC$K_DTYPE_T;
7967 pat.dsc$b_class = DSC$K_CLASS_S;
7969 /* Set up result descriptor. */
7970 Newx(buff, VMS_MAXRSS, char);
7971 res.dsc$a_pointer = buff;
7972 res.dsc$w_length = VMS_MAXRSS - 1;
7973 res.dsc$b_dtype = DSC$K_DTYPE_T;
7974 res.dsc$b_class = DSC$K_CLASS_S;
7976 /* Read files, collecting versions. */
7977 for (context = 0, e->vms_verscount = 0;
7978 e->vms_verscount < VERSIZE(e);
7979 e->vms_verscount++) {
7981 unsigned long flags = 0;
7983 #ifdef VMS_LONGNAME_SUPPORT
7984 flags = LIB$M_FIL_LONG_NAMES
7986 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
7987 if (tmpsts == RMS$_NMF || context == 0) break;
7989 buff[VMS_MAXRSS - 1] = '\0';
7990 if ((p = strchr(buff, ';')))
7991 e->vms_versions[e->vms_verscount] = atoi(p + 1);
7993 e->vms_versions[e->vms_verscount] = -1;
7996 _ckvmssts(lib$find_file_end(&context));
8000 } /* end of collectversions() */
8003 * Read the next entry from the directory.
8005 /*{{{ struct dirent *readdir(DIR *dd)*/
8007 Perl_readdir(pTHX_ DIR *dd)
8009 struct dsc$descriptor_s res;
8011 unsigned long int tmpsts;
8013 unsigned long flags = 0;
8014 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8015 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8017 /* Set up result descriptor, and get next file. */
8018 Newx(buff, VMS_MAXRSS, char);
8019 res.dsc$a_pointer = buff;
8020 res.dsc$w_length = VMS_MAXRSS - 1;
8021 res.dsc$b_dtype = DSC$K_DTYPE_T;
8022 res.dsc$b_class = DSC$K_CLASS_S;
8024 #ifdef VMS_LONGNAME_SUPPORT
8025 flags = LIB$M_FIL_LONG_NAMES
8028 tmpsts = lib$find_file
8029 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
8030 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
8031 if (!(tmpsts & 1)) {
8032 set_vaxc_errno(tmpsts);
8035 set_errno(EACCES); break;
8037 set_errno(ENODEV); break;
8039 set_errno(ENOTDIR); break;
8040 case RMS$_FNF: case RMS$_DNF:
8041 set_errno(ENOENT); break;
8049 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
8050 if (!decc_efs_case_preserve) {
8051 buff[VMS_MAXRSS - 1] = '\0';
8052 for (p = buff; *p; p++) *p = _tolower(*p);
8055 /* we don't want to force to lowercase, just null terminate */
8056 buff[res.dsc$w_length] = '\0';
8058 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
8061 /* Skip any directory component and just copy the name. */
8062 sts = vms_split_path
8077 /* Drop NULL extensions on UNIX file specification */
8078 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
8079 (e_len == 1) && decc_readdir_dropdotnotype)) {
8084 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
8085 dd->entry.d_name[n_len + e_len] = '\0';
8086 dd->entry.d_namlen = strlen(dd->entry.d_name);
8088 /* Convert the filename to UNIX format if needed */
8089 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
8091 /* Translate the encoded characters. */
8092 /* Fixme: unicode handling could result in embedded 0 characters */
8093 if (strchr(dd->entry.d_name, '^') != NULL) {
8097 p = dd->entry.d_name;
8101 x = copy_expand_vms_filename_escape(q, p, &y);
8105 /* if y > 1, then this is a wide file specification */
8106 /* Wide file specifications need to be passed in Perl */
8107 /* counted strings apparently with a unicode flag */
8110 strcpy(dd->entry.d_name, new_name);
8114 dd->entry.vms_verscount = 0;
8115 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
8119 } /* end of readdir() */
8123 * Read the next entry from the directory -- thread-safe version.
8125 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
8127 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
8131 MUTEX_LOCK( (perl_mutex *) dd->mutex );
8133 entry = readdir(dd);
8135 retval = ( *result == NULL ? errno : 0 );
8137 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
8141 } /* end of readdir_r() */
8145 * Return something that can be used in a seekdir later.
8147 /*{{{ long telldir(DIR *dd)*/
8149 Perl_telldir(DIR *dd)
8156 * Return to a spot where we used to be. Brute force.
8158 /*{{{ void seekdir(DIR *dd,long count)*/
8160 Perl_seekdir(pTHX_ DIR *dd, long count)
8164 /* If we haven't done anything yet... */
8168 /* Remember some state, and clear it. */
8169 old_flags = dd->flags;
8170 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8171 _ckvmssts(lib$find_file_end(&dd->context));
8174 /* The increment is in readdir(). */
8175 for (dd->count = 0; dd->count < count; )
8178 dd->flags = old_flags;
8180 } /* end of seekdir() */
8183 /* VMS subprocess management
8185 * my_vfork() - just a vfork(), after setting a flag to record that
8186 * the current script is trying a Unix-style fork/exec.
8188 * vms_do_aexec() and vms_do_exec() are called in response to the
8189 * perl 'exec' function. If this follows a vfork call, then they
8190 * call out the regular perl routines in doio.c which do an
8191 * execvp (for those who really want to try this under VMS).
8192 * Otherwise, they do exactly what the perl docs say exec should
8193 * do - terminate the current script and invoke a new command
8194 * (See below for notes on command syntax.)
8196 * do_aspawn() and do_spawn() implement the VMS side of the perl
8197 * 'system' function.
8199 * Note on command arguments to perl 'exec' and 'system': When handled
8200 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
8201 * are concatenated to form a DCL command string. If the first arg
8202 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
8203 * the command string is handed off to DCL directly. Otherwise,
8204 * the first token of the command is taken as the filespec of an image
8205 * to run. The filespec is expanded using a default type of '.EXE' and
8206 * the process defaults for device, directory, etc., and if found, the resultant
8207 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
8208 * the command string as parameters. This is perhaps a bit complicated,
8209 * but I hope it will form a happy medium between what VMS folks expect
8210 * from lib$spawn and what Unix folks expect from exec.
8213 static int vfork_called;
8215 /*{{{int my_vfork()*/
8226 vms_execfree(struct dsc$descriptor_s *vmscmd)
8229 if (vmscmd->dsc$a_pointer) {
8230 PerlMem_free(vmscmd->dsc$a_pointer);
8232 PerlMem_free(vmscmd);
8237 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
8239 char *junk, *tmps = Nullch;
8240 register size_t cmdlen = 0;
8247 tmps = SvPV(really,rlen);
8254 for (idx++; idx <= sp; idx++) {
8256 junk = SvPVx(*idx,rlen);
8257 cmdlen += rlen ? rlen + 1 : 0;
8260 Newx(PL_Cmd, cmdlen+1, char);
8262 if (tmps && *tmps) {
8263 strcpy(PL_Cmd,tmps);
8266 else *PL_Cmd = '\0';
8267 while (++mark <= sp) {
8269 char *s = SvPVx(*mark,n_a);
8271 if (*PL_Cmd) strcat(PL_Cmd," ");
8277 } /* end of setup_argstr() */
8280 static unsigned long int
8281 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
8282 struct dsc$descriptor_s **pvmscmd)
8284 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
8285 char image_name[NAM$C_MAXRSS+1];
8286 char image_argv[NAM$C_MAXRSS+1];
8287 $DESCRIPTOR(defdsc,".EXE");
8288 $DESCRIPTOR(defdsc2,".");
8289 $DESCRIPTOR(resdsc,resspec);
8290 struct dsc$descriptor_s *vmscmd;
8291 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8292 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
8293 register char *s, *rest, *cp, *wordbreak;
8298 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
8299 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
8301 /* Make a copy for modification */
8302 cmdlen = strlen(incmd);
8303 cmd = PerlMem_malloc(cmdlen+1);
8304 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
8305 strncpy(cmd, incmd, cmdlen);
8310 vmscmd->dsc$a_pointer = NULL;
8311 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
8312 vmscmd->dsc$b_class = DSC$K_CLASS_S;
8313 vmscmd->dsc$w_length = 0;
8314 if (pvmscmd) *pvmscmd = vmscmd;
8316 if (suggest_quote) *suggest_quote = 0;
8318 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
8320 return CLI$_BUFOVF; /* continuation lines currently unsupported */
8325 while (*s && isspace(*s)) s++;
8327 if (*s == '@' || *s == '$') {
8328 vmsspec[0] = *s; rest = s + 1;
8329 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
8331 else { cp = vmsspec; rest = s; }
8332 if (*rest == '.' || *rest == '/') {
8335 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
8336 rest++, cp2++) *cp2 = *rest;
8338 if (do_tovmsspec(resspec,cp,0)) {
8341 for (cp2 = vmsspec + strlen(vmsspec);
8342 *rest && cp2 - vmsspec < sizeof vmsspec;
8343 rest++, cp2++) *cp2 = *rest;
8348 /* Intuit whether verb (first word of cmd) is a DCL command:
8349 * - if first nonspace char is '@', it's a DCL indirection
8351 * - if verb contains a filespec separator, it's not a DCL command
8352 * - if it doesn't, caller tells us whether to default to a DCL
8353 * command, or to a local image unless told it's DCL (by leading '$')
8357 if (suggest_quote) *suggest_quote = 1;
8359 register char *filespec = strpbrk(s,":<[.;");
8360 rest = wordbreak = strpbrk(s," \"\t/");
8361 if (!wordbreak) wordbreak = s + strlen(s);
8362 if (*s == '$') check_img = 0;
8363 if (filespec && (filespec < wordbreak)) isdcl = 0;
8364 else isdcl = !check_img;
8369 imgdsc.dsc$a_pointer = s;
8370 imgdsc.dsc$w_length = wordbreak - s;
8371 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8373 _ckvmssts(lib$find_file_end(&cxt));
8374 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8375 if (!(retsts & 1) && *s == '$') {
8376 _ckvmssts(lib$find_file_end(&cxt));
8377 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
8378 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8380 _ckvmssts(lib$find_file_end(&cxt));
8381 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8385 _ckvmssts(lib$find_file_end(&cxt));
8390 while (*s && !isspace(*s)) s++;
8393 /* check that it's really not DCL with no file extension */
8394 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8396 char b[256] = {0,0,0,0};
8397 read(fileno(fp), b, 256);
8398 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
8402 /* Check for script */
8404 if ((b[0] == '#') && (b[1] == '!'))
8406 #ifdef ALTERNATE_SHEBANG
8408 shebang_len = strlen(ALTERNATE_SHEBANG);
8409 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
8411 perlstr = strstr("perl",b);
8412 if (perlstr == NULL)
8420 if (shebang_len > 0) {
8423 char tmpspec[NAM$C_MAXRSS + 1];
8426 /* Image is following after white space */
8427 /*--------------------------------------*/
8428 while (isprint(b[i]) && isspace(b[i]))
8432 while (isprint(b[i]) && !isspace(b[i])) {
8433 tmpspec[j++] = b[i++];
8434 if (j >= NAM$C_MAXRSS)
8439 /* There may be some default parameters to the image */
8440 /*---------------------------------------------------*/
8442 while (isprint(b[i])) {
8443 image_argv[j++] = b[i++];
8444 if (j >= NAM$C_MAXRSS)
8447 while ((j > 0) && !isprint(image_argv[j-1]))
8451 /* It will need to be converted to VMS format and validated */
8452 if (tmpspec[0] != '\0') {
8455 /* Try to find the exact program requested to be run */
8456 /*---------------------------------------------------*/
8457 iname = do_rmsexpand
8458 (tmpspec, image_name, 0, ".exe", PERL_RMSEXPAND_M_VMS);
8459 if (iname != NULL) {
8460 if (cando_by_name(S_IXUSR,0,image_name)) {
8461 /* MCR prefix needed */
8465 /* Try again with a null type */
8466 /*----------------------------*/
8467 iname = do_rmsexpand
8468 (tmpspec, image_name, 0, ".", PERL_RMSEXPAND_M_VMS);
8469 if (iname != NULL) {
8470 if (cando_by_name(S_IXUSR,0,image_name)) {
8471 /* MCR prefix needed */
8477 /* Did we find the image to run the script? */
8478 /*------------------------------------------*/
8482 /* Assume DCL or foreign command exists */
8483 /*--------------------------------------*/
8484 tchr = strrchr(tmpspec, '/');
8491 strcpy(image_name, tchr);
8499 if (check_img && isdcl) return RMS$_FNF;
8501 if (cando_by_name(S_IXUSR,0,resspec)) {
8502 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
8503 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
8505 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
8506 if (image_name[0] != 0) {
8507 strcat(vmscmd->dsc$a_pointer, image_name);
8508 strcat(vmscmd->dsc$a_pointer, " ");
8510 } else if (image_name[0] != 0) {
8511 strcpy(vmscmd->dsc$a_pointer, image_name);
8512 strcat(vmscmd->dsc$a_pointer, " ");
8514 strcpy(vmscmd->dsc$a_pointer,"@");
8516 if (suggest_quote) *suggest_quote = 1;
8518 /* If there is an image name, use original command */
8519 if (image_name[0] == 0)
8520 strcat(vmscmd->dsc$a_pointer,resspec);
8523 while (*rest && isspace(*rest)) rest++;
8526 if (image_argv[0] != 0) {
8527 strcat(vmscmd->dsc$a_pointer,image_argv);
8528 strcat(vmscmd->dsc$a_pointer, " ");
8534 rest_len = strlen(rest);
8535 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
8536 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
8537 strcat(vmscmd->dsc$a_pointer,rest);
8539 retsts = CLI$_BUFOVF;
8541 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
8543 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
8549 /* It's either a DCL command or we couldn't find a suitable image */
8550 vmscmd->dsc$w_length = strlen(cmd);
8552 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
8553 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
8554 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
8558 /* check if it's a symbol (for quoting purposes) */
8559 if (suggest_quote && !*suggest_quote) {
8561 char equiv[LNM$C_NAMLENGTH];
8562 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8563 eqvdsc.dsc$a_pointer = equiv;
8565 iss = lib$get_symbol(vmscmd,&eqvdsc);
8566 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
8568 if (!(retsts & 1)) {
8569 /* just hand off status values likely to be due to user error */
8570 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
8571 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
8572 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
8573 else { _ckvmssts(retsts); }
8576 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
8578 } /* end of setup_cmddsc() */
8581 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
8583 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
8589 if (vfork_called) { /* this follows a vfork - act Unixish */
8591 if (vfork_called < 0) {
8592 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
8595 else return do_aexec(really,mark,sp);
8597 /* no vfork - act VMSish */
8598 cmd = setup_argstr(aTHX_ really,mark,sp);
8599 exec_sts = vms_do_exec(cmd);
8600 Safefree(cmd); /* Clean up from setup_argstr() */
8605 } /* end of vms_do_aexec() */
8608 /* {{{bool vms_do_exec(char *cmd) */
8610 Perl_vms_do_exec(pTHX_ const char *cmd)
8612 struct dsc$descriptor_s *vmscmd;
8614 if (vfork_called) { /* this follows a vfork - act Unixish */
8616 if (vfork_called < 0) {
8617 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
8620 else return do_exec(cmd);
8623 { /* no vfork - act VMSish */
8624 unsigned long int retsts;
8627 TAINT_PROPER("exec");
8628 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
8629 retsts = lib$do_command(vmscmd);
8632 case RMS$_FNF: case RMS$_DNF:
8633 set_errno(ENOENT); break;
8635 set_errno(ENOTDIR); break;
8637 set_errno(ENODEV); break;
8639 set_errno(EACCES); break;
8641 set_errno(EINVAL); break;
8642 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
8643 set_errno(E2BIG); break;
8644 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
8645 _ckvmssts(retsts); /* fall through */
8646 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8649 set_vaxc_errno(retsts);
8650 if (ckWARN(WARN_EXEC)) {
8651 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
8652 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
8654 vms_execfree(vmscmd);
8659 } /* end of vms_do_exec() */
8662 unsigned long int Perl_do_spawn(pTHX_ const char *);
8664 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
8666 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
8668 unsigned long int sts;
8672 cmd = setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp);
8673 sts = do_spawn(cmd);
8674 /* pp_sys will clean up cmd */
8678 } /* end of do_aspawn() */
8681 /* {{{unsigned long int do_spawn(char *cmd) */
8683 Perl_do_spawn(pTHX_ const char *cmd)
8685 unsigned long int sts, substs;
8687 /* The caller of this routine expects to Safefree(PL_Cmd) */
8688 Newx(PL_Cmd,10,char);
8691 TAINT_PROPER("spawn");
8692 if (!cmd || !*cmd) {
8693 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
8696 case RMS$_FNF: case RMS$_DNF:
8697 set_errno(ENOENT); break;
8699 set_errno(ENOTDIR); break;
8701 set_errno(ENODEV); break;
8703 set_errno(EACCES); break;
8705 set_errno(EINVAL); break;
8706 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
8707 set_errno(E2BIG); break;
8708 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
8709 _ckvmssts(sts); /* fall through */
8710 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8713 set_vaxc_errno(sts);
8714 if (ckWARN(WARN_EXEC)) {
8715 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
8723 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
8728 } /* end of do_spawn() */
8732 static unsigned int *sockflags, sockflagsize;
8735 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
8736 * routines found in some versions of the CRTL can't deal with sockets.
8737 * We don't shim the other file open routines since a socket isn't
8738 * likely to be opened by a name.
8740 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
8741 FILE *my_fdopen(int fd, const char *mode)
8743 FILE *fp = fdopen(fd, mode);
8746 unsigned int fdoff = fd / sizeof(unsigned int);
8747 Stat_t sbuf; /* native stat; we don't need flex_stat */
8748 if (!sockflagsize || fdoff > sockflagsize) {
8749 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
8750 else Newx (sockflags,fdoff+2,unsigned int);
8751 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
8752 sockflagsize = fdoff + 2;
8754 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
8755 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
8764 * Clear the corresponding bit when the (possibly) socket stream is closed.
8765 * There still a small hole: we miss an implicit close which might occur
8766 * via freopen(). >> Todo
8768 /*{{{ int my_fclose(FILE *fp)*/
8769 int my_fclose(FILE *fp) {
8771 unsigned int fd = fileno(fp);
8772 unsigned int fdoff = fd / sizeof(unsigned int);
8774 if (sockflagsize && fdoff <= sockflagsize)
8775 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
8783 * A simple fwrite replacement which outputs itmsz*nitm chars without
8784 * introducing record boundaries every itmsz chars.
8785 * We are using fputs, which depends on a terminating null. We may
8786 * well be writing binary data, so we need to accommodate not only
8787 * data with nulls sprinkled in the middle but also data with no null
8790 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
8792 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
8794 register char *cp, *end, *cpd, *data;
8795 register unsigned int fd = fileno(dest);
8796 register unsigned int fdoff = fd / sizeof(unsigned int);
8798 int bufsize = itmsz * nitm + 1;
8800 if (fdoff < sockflagsize &&
8801 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
8802 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
8806 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
8807 memcpy( data, src, itmsz*nitm );
8808 data[itmsz*nitm] = '\0';
8810 end = data + itmsz * nitm;
8811 retval = (int) nitm; /* on success return # items written */
8814 while (cpd <= end) {
8815 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
8816 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
8818 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
8822 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
8825 } /* end of my_fwrite() */
8828 /*{{{ int my_flush(FILE *fp)*/
8830 Perl_my_flush(pTHX_ FILE *fp)
8833 if ((res = fflush(fp)) == 0 && fp) {
8834 #ifdef VMS_DO_SOCKETS
8836 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
8838 res = fsync(fileno(fp));
8841 * If the flush succeeded but set end-of-file, we need to clear
8842 * the error because our caller may check ferror(). BTW, this
8843 * probably means we just flushed an empty file.
8845 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
8852 * Here are replacements for the following Unix routines in the VMS environment:
8853 * getpwuid Get information for a particular UIC or UID
8854 * getpwnam Get information for a named user
8855 * getpwent Get information for each user in the rights database
8856 * setpwent Reset search to the start of the rights database
8857 * endpwent Finish searching for users in the rights database
8859 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
8860 * (defined in pwd.h), which contains the following fields:-
8862 * char *pw_name; Username (in lower case)
8863 * char *pw_passwd; Hashed password
8864 * unsigned int pw_uid; UIC
8865 * unsigned int pw_gid; UIC group number
8866 * char *pw_unixdir; Default device/directory (VMS-style)
8867 * char *pw_gecos; Owner name
8868 * char *pw_dir; Default device/directory (Unix-style)
8869 * char *pw_shell; Default CLI name (eg. DCL)
8871 * If the specified user does not exist, getpwuid and getpwnam return NULL.
8873 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
8874 * not the UIC member number (eg. what's returned by getuid()),
8875 * getpwuid() can accept either as input (if uid is specified, the caller's
8876 * UIC group is used), though it won't recognise gid=0.
8878 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
8879 * information about other users in your group or in other groups, respectively.
8880 * If the required privilege is not available, then these routines fill only
8881 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
8884 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
8887 /* sizes of various UAF record fields */
8888 #define UAI$S_USERNAME 12
8889 #define UAI$S_IDENT 31
8890 #define UAI$S_OWNER 31
8891 #define UAI$S_DEFDEV 31
8892 #define UAI$S_DEFDIR 63
8893 #define UAI$S_DEFCLI 31
8896 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
8897 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
8898 (uic).uic$v_group != UIC$K_WILD_GROUP)
8900 static char __empty[]= "";
8901 static struct passwd __passwd_empty=
8902 {(char *) __empty, (char *) __empty, 0, 0,
8903 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
8904 static int contxt= 0;
8905 static struct passwd __pwdcache;
8906 static char __pw_namecache[UAI$S_IDENT+1];
8909 * This routine does most of the work extracting the user information.
8911 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
8914 unsigned char length;
8915 char pw_gecos[UAI$S_OWNER+1];
8917 static union uicdef uic;
8919 unsigned char length;
8920 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
8923 unsigned char length;
8924 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
8927 unsigned char length;
8928 char pw_shell[UAI$S_DEFCLI+1];
8930 static char pw_passwd[UAI$S_PWD+1];
8932 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
8933 struct dsc$descriptor_s name_desc;
8934 unsigned long int sts;
8936 static struct itmlst_3 itmlst[]= {
8937 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
8938 {sizeof(uic), UAI$_UIC, &uic, &luic},
8939 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
8940 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
8941 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
8942 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
8943 {0, 0, NULL, NULL}};
8945 name_desc.dsc$w_length= strlen(name);
8946 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8947 name_desc.dsc$b_class= DSC$K_CLASS_S;
8948 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
8950 /* Note that sys$getuai returns many fields as counted strings. */
8951 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
8952 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
8953 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
8955 else { _ckvmssts(sts); }
8956 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
8958 if ((int) owner.length < lowner) lowner= (int) owner.length;
8959 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
8960 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
8961 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
8962 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
8963 owner.pw_gecos[lowner]= '\0';
8964 defdev.pw_dir[ldefdev+ldefdir]= '\0';
8965 defcli.pw_shell[ldefcli]= '\0';
8966 if (valid_uic(uic)) {
8967 pwd->pw_uid= uic.uic$l_uic;
8968 pwd->pw_gid= uic.uic$v_group;
8971 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
8972 pwd->pw_passwd= pw_passwd;
8973 pwd->pw_gecos= owner.pw_gecos;
8974 pwd->pw_dir= defdev.pw_dir;
8975 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
8976 pwd->pw_shell= defcli.pw_shell;
8977 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
8979 ldir= strlen(pwd->pw_unixdir) - 1;
8980 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
8983 strcpy(pwd->pw_unixdir, pwd->pw_dir);
8984 if (!decc_efs_case_preserve)
8985 __mystrtolower(pwd->pw_unixdir);
8990 * Get information for a named user.
8992 /*{{{struct passwd *getpwnam(char *name)*/
8993 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
8995 struct dsc$descriptor_s name_desc;
8997 unsigned long int status, sts;
8999 __pwdcache = __passwd_empty;
9000 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
9001 /* We still may be able to determine pw_uid and pw_gid */
9002 name_desc.dsc$w_length= strlen(name);
9003 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
9004 name_desc.dsc$b_class= DSC$K_CLASS_S;
9005 name_desc.dsc$a_pointer= (char *) name;
9006 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
9007 __pwdcache.pw_uid= uic.uic$l_uic;
9008 __pwdcache.pw_gid= uic.uic$v_group;
9011 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
9012 set_vaxc_errno(sts);
9013 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
9016 else { _ckvmssts(sts); }
9019 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
9020 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
9021 __pwdcache.pw_name= __pw_namecache;
9023 } /* end of my_getpwnam() */
9027 * Get information for a particular UIC or UID.
9028 * Called by my_getpwent with uid=-1 to list all users.
9030 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
9031 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
9033 const $DESCRIPTOR(name_desc,__pw_namecache);
9034 unsigned short lname;
9036 unsigned long int status;
9038 if (uid == (unsigned int) -1) {
9040 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
9041 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
9042 set_vaxc_errno(status);
9043 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9047 else { _ckvmssts(status); }
9048 } while (!valid_uic (uic));
9052 if (!uic.uic$v_group)
9053 uic.uic$v_group= PerlProc_getgid();
9055 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
9056 else status = SS$_IVIDENT;
9057 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
9058 status == RMS$_PRV) {
9059 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
9062 else { _ckvmssts(status); }
9064 __pw_namecache[lname]= '\0';
9065 __mystrtolower(__pw_namecache);
9067 __pwdcache = __passwd_empty;
9068 __pwdcache.pw_name = __pw_namecache;
9070 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
9071 The identifier's value is usually the UIC, but it doesn't have to be,
9072 so if we can, we let fillpasswd update this. */
9073 __pwdcache.pw_uid = uic.uic$l_uic;
9074 __pwdcache.pw_gid = uic.uic$v_group;
9076 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
9079 } /* end of my_getpwuid() */
9083 * Get information for next user.
9085 /*{{{struct passwd *my_getpwent()*/
9086 struct passwd *Perl_my_getpwent(pTHX)
9088 return (my_getpwuid((unsigned int) -1));
9093 * Finish searching rights database for users.
9095 /*{{{void my_endpwent()*/
9096 void Perl_my_endpwent(pTHX)
9099 _ckvmssts(sys$finish_rdb(&contxt));
9105 #ifdef HOMEGROWN_POSIX_SIGNALS
9106 /* Signal handling routines, pulled into the core from POSIX.xs.
9108 * We need these for threads, so they've been rolled into the core,
9109 * rather than left in POSIX.xs.
9111 * (DRS, Oct 23, 1997)
9114 /* sigset_t is atomic under VMS, so these routines are easy */
9115 /*{{{int my_sigemptyset(sigset_t *) */
9116 int my_sigemptyset(sigset_t *set) {
9117 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9123 /*{{{int my_sigfillset(sigset_t *)*/
9124 int my_sigfillset(sigset_t *set) {
9126 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9127 for (i = 0; i < NSIG; i++) *set |= (1 << i);
9133 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
9134 int my_sigaddset(sigset_t *set, int sig) {
9135 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9136 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9137 *set |= (1 << (sig - 1));
9143 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
9144 int my_sigdelset(sigset_t *set, int sig) {
9145 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9146 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9147 *set &= ~(1 << (sig - 1));
9153 /*{{{int my_sigismember(sigset_t *set, int sig)*/
9154 int my_sigismember(sigset_t *set, int sig) {
9155 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9156 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9157 return *set & (1 << (sig - 1));
9162 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
9163 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
9166 /* If set and oset are both null, then things are badly wrong. Bail out. */
9167 if ((oset == NULL) && (set == NULL)) {
9168 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
9172 /* If set's null, then we're just handling a fetch. */
9174 tempmask = sigblock(0);
9179 tempmask = sigsetmask(*set);
9182 tempmask = sigblock(*set);
9185 tempmask = sigblock(0);
9186 sigsetmask(*oset & ~tempmask);
9189 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9194 /* Did they pass us an oset? If so, stick our holding mask into it */
9201 #endif /* HOMEGROWN_POSIX_SIGNALS */
9204 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
9205 * my_utime(), and flex_stat(), all of which operate on UTC unless
9206 * VMSISH_TIMES is true.
9208 /* method used to handle UTC conversions:
9209 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
9211 static int gmtime_emulation_type;
9212 /* number of secs to add to UTC POSIX-style time to get local time */
9213 static long int utc_offset_secs;
9215 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
9216 * in vmsish.h. #undef them here so we can call the CRTL routines
9225 * DEC C previous to 6.0 corrupts the behavior of the /prefix
9226 * qualifier with the extern prefix pragma. This provisional
9227 * hack circumvents this prefix pragma problem in previous
9230 #if defined(__VMS_VER) && __VMS_VER >= 70000000
9231 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
9232 # pragma __extern_prefix save
9233 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
9234 # define gmtime decc$__utctz_gmtime
9235 # define localtime decc$__utctz_localtime
9236 # define time decc$__utc_time
9237 # pragma __extern_prefix restore
9239 struct tm *gmtime(), *localtime();
9245 static time_t toutc_dst(time_t loc) {
9248 if ((rsltmp = localtime(&loc)) == NULL) return -1;
9249 loc -= utc_offset_secs;
9250 if (rsltmp->tm_isdst) loc -= 3600;
9253 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9254 ((gmtime_emulation_type || my_time(NULL)), \
9255 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
9256 ((secs) - utc_offset_secs))))
9258 static time_t toloc_dst(time_t utc) {
9261 utc += utc_offset_secs;
9262 if ((rsltmp = localtime(&utc)) == NULL) return -1;
9263 if (rsltmp->tm_isdst) utc += 3600;
9266 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9267 ((gmtime_emulation_type || my_time(NULL)), \
9268 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
9269 ((secs) + utc_offset_secs))))
9271 #ifndef RTL_USES_UTC
9274 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
9275 DST starts on 1st sun of april at 02:00 std time
9276 ends on last sun of october at 02:00 dst time
9277 see the UCX management command reference, SET CONFIG TIMEZONE
9278 for formatting info.
9280 No, it's not as general as it should be, but then again, NOTHING
9281 will handle UK times in a sensible way.
9286 parse the DST start/end info:
9287 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
9291 tz_parse_startend(char *s, struct tm *w, int *past)
9293 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
9294 int ly, dozjd, d, m, n, hour, min, sec, j, k;
9299 if (!past) return 0;
9302 if (w->tm_year % 4 == 0) ly = 1;
9303 if (w->tm_year % 100 == 0) ly = 0;
9304 if (w->tm_year+1900 % 400 == 0) ly = 1;
9307 dozjd = isdigit(*s);
9308 if (*s == 'J' || *s == 'j' || dozjd) {
9309 if (!dozjd && !isdigit(*++s)) return 0;
9312 d = d*10 + *s++ - '0';
9314 d = d*10 + *s++ - '0';
9317 if (d == 0) return 0;
9318 if (d > 366) return 0;
9320 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
9323 } else if (*s == 'M' || *s == 'm') {
9324 if (!isdigit(*++s)) return 0;
9326 if (isdigit(*s)) m = 10*m + *s++ - '0';
9327 if (*s != '.') return 0;
9328 if (!isdigit(*++s)) return 0;
9330 if (n < 1 || n > 5) return 0;
9331 if (*s != '.') return 0;
9332 if (!isdigit(*++s)) return 0;
9334 if (d > 6) return 0;
9338 if (!isdigit(*++s)) return 0;
9340 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
9342 if (!isdigit(*++s)) return 0;
9344 if (isdigit(*s)) min = 10*min + *s++ - '0';
9346 if (!isdigit(*++s)) return 0;
9348 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
9358 if (w->tm_yday < d) goto before;
9359 if (w->tm_yday > d) goto after;
9361 if (w->tm_mon+1 < m) goto before;
9362 if (w->tm_mon+1 > m) goto after;
9364 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
9365 k = d - j; /* mday of first d */
9367 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
9368 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
9369 if (w->tm_mday < k) goto before;
9370 if (w->tm_mday > k) goto after;
9373 if (w->tm_hour < hour) goto before;
9374 if (w->tm_hour > hour) goto after;
9375 if (w->tm_min < min) goto before;
9376 if (w->tm_min > min) goto after;
9377 if (w->tm_sec < sec) goto before;
9391 /* parse the offset: (+|-)hh[:mm[:ss]] */
9394 tz_parse_offset(char *s, int *offset)
9396 int hour = 0, min = 0, sec = 0;
9399 if (!offset) return 0;
9401 if (*s == '-') {neg++; s++;}
9403 if (!isdigit(*s)) return 0;
9405 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
9406 if (hour > 24) return 0;
9408 if (!isdigit(*++s)) return 0;
9410 if (isdigit(*s)) min = min*10 + (*s++ - '0');
9411 if (min > 59) return 0;
9413 if (!isdigit(*++s)) return 0;
9415 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
9416 if (sec > 59) return 0;
9420 *offset = (hour*60+min)*60 + sec;
9421 if (neg) *offset = -*offset;
9426 input time is w, whatever type of time the CRTL localtime() uses.
9427 sets dst, the zone, and the gmtoff (seconds)
9429 caches the value of TZ and UCX$TZ env variables; note that
9430 my_setenv looks for these and sets a flag if they're changed
9433 We have to watch out for the "australian" case (dst starts in
9434 october, ends in april)...flagged by "reverse" and checked by
9435 scanning through the months of the previous year.
9440 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
9445 char *dstzone, *tz, *s_start, *s_end;
9446 int std_off, dst_off, isdst;
9447 int y, dststart, dstend;
9448 static char envtz[1025]; /* longer than any logical, symbol, ... */
9449 static char ucxtz[1025];
9450 static char reversed = 0;
9456 reversed = -1; /* flag need to check */
9457 envtz[0] = ucxtz[0] = '\0';
9458 tz = my_getenv("TZ",0);
9459 if (tz) strcpy(envtz, tz);
9460 tz = my_getenv("UCX$TZ",0);
9461 if (tz) strcpy(ucxtz, tz);
9462 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
9465 if (!*tz) tz = ucxtz;
9468 while (isalpha(*s)) s++;
9469 s = tz_parse_offset(s, &std_off);
9471 if (!*s) { /* no DST, hurray we're done! */
9477 while (isalpha(*s)) s++;
9478 s2 = tz_parse_offset(s, &dst_off);
9482 dst_off = std_off - 3600;
9485 if (!*s) { /* default dst start/end?? */
9486 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
9487 s = strchr(ucxtz,',');
9489 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
9491 if (*s != ',') return 0;
9494 when = _toutc(when); /* convert to utc */
9495 when = when - std_off; /* convert to pseudolocal time*/
9497 w2 = localtime(&when);
9500 s = tz_parse_startend(s_start,w2,&dststart);
9502 if (*s != ',') return 0;
9505 when = _toutc(when); /* convert to utc */
9506 when = when - dst_off; /* convert to pseudolocal time*/
9507 w2 = localtime(&when);
9508 if (w2->tm_year != y) { /* spans a year, just check one time */
9509 when += dst_off - std_off;
9510 w2 = localtime(&when);
9513 s = tz_parse_startend(s_end,w2,&dstend);
9516 if (reversed == -1) { /* need to check if start later than end */
9520 if (when < 2*365*86400) {
9521 when += 2*365*86400;
9525 w2 =localtime(&when);
9526 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
9528 for (j = 0; j < 12; j++) {
9529 w2 =localtime(&when);
9530 tz_parse_startend(s_start,w2,&ds);
9531 tz_parse_startend(s_end,w2,&de);
9532 if (ds != de) break;
9536 if (de && !ds) reversed = 1;
9539 isdst = dststart && !dstend;
9540 if (reversed) isdst = dststart || !dstend;
9543 if (dst) *dst = isdst;
9544 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
9545 if (isdst) tz = dstzone;
9547 while(isalpha(*tz)) *zone++ = *tz++;
9553 #endif /* !RTL_USES_UTC */
9555 /* my_time(), my_localtime(), my_gmtime()
9556 * By default traffic in UTC time values, using CRTL gmtime() or
9557 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
9558 * Note: We need to use these functions even when the CRTL has working
9559 * UTC support, since they also handle C<use vmsish qw(times);>
9561 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
9562 * Modified by Charles Bailey <bailey@newman.upenn.edu>
9565 /*{{{time_t my_time(time_t *timep)*/
9566 time_t Perl_my_time(pTHX_ time_t *timep)
9571 if (gmtime_emulation_type == 0) {
9573 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
9574 /* results of calls to gmtime() and localtime() */
9575 /* for same &base */
9577 gmtime_emulation_type++;
9578 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
9579 char off[LNM$C_NAMLENGTH+1];;
9581 gmtime_emulation_type++;
9582 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
9583 gmtime_emulation_type++;
9584 utc_offset_secs = 0;
9585 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
9587 else { utc_offset_secs = atol(off); }
9589 else { /* We've got a working gmtime() */
9590 struct tm gmt, local;
9593 tm_p = localtime(&base);
9595 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
9596 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
9597 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
9598 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
9604 # ifdef RTL_USES_UTC
9605 if (VMSISH_TIME) when = _toloc(when);
9607 if (!VMSISH_TIME) when = _toutc(when);
9610 if (timep != NULL) *timep = when;
9613 } /* end of my_time() */
9617 /*{{{struct tm *my_gmtime(const time_t *timep)*/
9619 Perl_my_gmtime(pTHX_ const time_t *timep)
9625 if (timep == NULL) {
9626 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9629 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
9633 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
9635 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
9636 return gmtime(&when);
9638 /* CRTL localtime() wants local time as input, so does no tz correction */
9639 rsltmp = localtime(&when);
9640 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
9643 } /* end of my_gmtime() */
9647 /*{{{struct tm *my_localtime(const time_t *timep)*/
9649 Perl_my_localtime(pTHX_ const time_t *timep)
9651 time_t when, whenutc;
9655 if (timep == NULL) {
9656 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9659 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
9660 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
9663 # ifdef RTL_USES_UTC
9665 if (VMSISH_TIME) when = _toutc(when);
9667 /* CRTL localtime() wants UTC as input, does tz correction itself */
9668 return localtime(&when);
9670 # else /* !RTL_USES_UTC */
9673 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
9674 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
9677 #ifndef RTL_USES_UTC
9678 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
9679 when = whenutc - offset; /* pseudolocal time*/
9682 /* CRTL localtime() wants local time as input, so does no tz correction */
9683 rsltmp = localtime(&when);
9684 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
9688 } /* end of my_localtime() */
9691 /* Reset definitions for later calls */
9692 #define gmtime(t) my_gmtime(t)
9693 #define localtime(t) my_localtime(t)
9694 #define time(t) my_time(t)
9697 /* my_utime - update modification time of a file
9698 * calling sequence is identical to POSIX utime(), but under
9699 * VMS only the modification time is changed; ODS-2 does not
9700 * maintain access times. Restrictions differ from the POSIX
9701 * definition in that the time can be changed as long as the
9702 * caller has permission to execute the necessary IO$_MODIFY $QIO;
9703 * no separate checks are made to insure that the caller is the
9704 * owner of the file or has special privs enabled.
9705 * Code here is based on Joe Meadows' FILE utility.
9708 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
9709 * to VMS epoch (01-JAN-1858 00:00:00.00)
9710 * in 100 ns intervals.
9712 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
9714 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
9715 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
9719 long int bintime[2], len = 2, lowbit, unixtime,
9720 secscale = 10000000; /* seconds --> 100 ns intervals */
9721 unsigned long int chan, iosb[2], retsts;
9722 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
9723 struct FAB myfab = cc$rms_fab;
9724 struct NAM mynam = cc$rms_nam;
9725 #if defined (__DECC) && defined (__VAX)
9726 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
9727 * at least through VMS V6.1, which causes a type-conversion warning.
9729 # pragma message save
9730 # pragma message disable cvtdiftypes
9732 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
9733 struct fibdef myfib;
9734 #if defined (__DECC) && defined (__VAX)
9735 /* This should be right after the declaration of myatr, but due
9736 * to a bug in VAX DEC C, this takes effect a statement early.
9738 # pragma message restore
9740 /* cast ok for read only parameter */
9741 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
9742 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
9743 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
9745 if (decc_efs_charset != 0) {
9746 struct utimbuf utc_utimes;
9748 utc_utimes.actime = utimes->actime;
9749 utc_utimes.modtime = utimes->modtime;
9751 /* If input was local; convert to UTC for sys svc */
9753 utc_utimes.actime = _toutc(utimes->actime);
9754 utc_utimes.modtime = _toutc(utimes->modtime);
9757 sts = utime(file, &utc_utimes);
9761 if (file == NULL || *file == '\0') {
9763 set_vaxc_errno(LIB$_INVARG);
9767 /* Convert to VMS format ensuring that it will fit in 255 characters */
9768 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL)
9771 if (utimes != NULL) {
9772 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
9773 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
9774 * Since time_t is unsigned long int, and lib$emul takes a signed long int
9775 * as input, we force the sign bit to be clear by shifting unixtime right
9776 * one bit, then multiplying by an extra factor of 2 in lib$emul().
9778 lowbit = (utimes->modtime & 1) ? secscale : 0;
9779 unixtime = (long int) utimes->modtime;
9781 /* If input was UTC; convert to local for sys svc */
9782 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
9784 unixtime >>= 1; secscale <<= 1;
9785 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
9786 if (!(retsts & 1)) {
9788 set_vaxc_errno(retsts);
9791 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
9792 if (!(retsts & 1)) {
9794 set_vaxc_errno(retsts);
9799 /* Just get the current time in VMS format directly */
9800 retsts = sys$gettim(bintime);
9801 if (!(retsts & 1)) {
9803 set_vaxc_errno(retsts);
9808 myfab.fab$l_fna = vmsspec;
9809 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
9810 myfab.fab$l_nam = &mynam;
9811 mynam.nam$l_esa = esa;
9812 mynam.nam$b_ess = (unsigned char) sizeof esa;
9813 mynam.nam$l_rsa = rsa;
9814 mynam.nam$b_rss = (unsigned char) sizeof rsa;
9815 if (decc_efs_case_preserve)
9816 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9818 /* Look for the file to be affected, letting RMS parse the file
9819 * specification for us as well. I have set errno using only
9820 * values documented in the utime() man page for VMS POSIX.
9822 retsts = sys$parse(&myfab,0,0);
9823 if (!(retsts & 1)) {
9824 set_vaxc_errno(retsts);
9825 if (retsts == RMS$_PRV) set_errno(EACCES);
9826 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
9827 else set_errno(EVMSERR);
9830 retsts = sys$search(&myfab,0,0);
9831 if (!(retsts & 1)) {
9832 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9833 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9834 set_vaxc_errno(retsts);
9835 if (retsts == RMS$_PRV) set_errno(EACCES);
9836 else if (retsts == RMS$_FNF) set_errno(ENOENT);
9837 else set_errno(EVMSERR);
9841 devdsc.dsc$w_length = mynam.nam$b_dev;
9842 /* cast ok for read only parameter */
9843 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
9845 retsts = sys$assign(&devdsc,&chan,0,0);
9846 if (!(retsts & 1)) {
9847 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9848 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9849 set_vaxc_errno(retsts);
9850 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
9851 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
9852 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
9853 else set_errno(EVMSERR);
9857 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
9858 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
9860 memset((void *) &myfib, 0, sizeof myfib);
9861 #if defined(__DECC) || defined(__DECCXX)
9862 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
9863 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
9864 /* This prevents the revision time of the file being reset to the current
9865 * time as a result of our IO$_MODIFY $QIO. */
9866 myfib.fib$l_acctl = FIB$M_NORECORD;
9868 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
9869 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
9870 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
9872 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
9873 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9874 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9875 _ckvmssts(sys$dassgn(chan));
9876 if (retsts & 1) retsts = iosb[0];
9877 if (!(retsts & 1)) {
9878 set_vaxc_errno(retsts);
9879 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9880 else set_errno(EVMSERR);
9885 } /* end of my_utime() */
9889 * flex_stat, flex_lstat, flex_fstat
9890 * basic stat, but gets it right when asked to stat
9891 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
9894 #ifndef _USE_STD_STAT
9895 /* encode_dev packs a VMS device name string into an integer to allow
9896 * simple comparisons. This can be used, for example, to check whether two
9897 * files are located on the same device, by comparing their encoded device
9898 * names. Even a string comparison would not do, because stat() reuses the
9899 * device name buffer for each call; so without encode_dev, it would be
9900 * necessary to save the buffer and use strcmp (this would mean a number of
9901 * changes to the standard Perl code, to say nothing of what a Perl script
9904 * The device lock id, if it exists, should be unique (unless perhaps compared
9905 * with lock ids transferred from other nodes). We have a lock id if the disk is
9906 * mounted cluster-wide, which is when we tend to get long (host-qualified)
9907 * device names. Thus we use the lock id in preference, and only if that isn't
9908 * available, do we try to pack the device name into an integer (flagged by
9909 * the sign bit (LOCKID_MASK) being set).
9911 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
9912 * name and its encoded form, but it seems very unlikely that we will find
9913 * two files on different disks that share the same encoded device names,
9914 * and even more remote that they will share the same file id (if the test
9915 * is to check for the same file).
9917 * A better method might be to use sys$device_scan on the first call, and to
9918 * search for the device, returning an index into the cached array.
9919 * The number returned would be more intelligable.
9920 * This is probably not worth it, and anyway would take quite a bit longer
9921 * on the first call.
9923 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
9924 static mydev_t encode_dev (pTHX_ const char *dev)
9927 unsigned long int f;
9932 if (!dev || !dev[0]) return 0;
9936 struct dsc$descriptor_s dev_desc;
9937 unsigned long int status, lockid, item = DVI$_LOCKID;
9939 /* For cluster-mounted disks, the disk lock identifier is unique, so we
9940 can try that first. */
9941 dev_desc.dsc$w_length = strlen (dev);
9942 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
9943 dev_desc.dsc$b_class = DSC$K_CLASS_S;
9944 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
9945 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
9946 if (lockid) return (lockid & ~LOCKID_MASK);
9950 /* Otherwise we try to encode the device name */
9954 for (q = dev + strlen(dev); q--; q >= dev) {
9957 else if (isalpha (toupper (*q)))
9958 c= toupper (*q) - 'A' + (char)10;
9960 continue; /* Skip '$'s */
9962 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
9964 enc += f * (unsigned long int) c;
9966 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
9968 } /* end of encode_dev() */
9971 static char namecache[NAM$C_MAXRSS+1];
9974 is_null_device(name)
9977 if (decc_bug_devnull != 0) {
9978 if (strncmp("/dev/null", name, 9) == 0)
9981 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
9982 The underscore prefix, controller letter, and unit number are
9983 independently optional; for our purposes, the colon punctuation
9984 is not. The colon can be trailed by optional directory and/or
9985 filename, but two consecutive colons indicates a nodename rather
9986 than a device. [pr] */
9987 if (*name == '_') ++name;
9988 if (tolower(*name++) != 'n') return 0;
9989 if (tolower(*name++) != 'l') return 0;
9990 if (tolower(*name) == 'a') ++name;
9991 if (*name == '0') ++name;
9992 return (*name++ == ':') && (*name != ':');
9995 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
9996 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
9997 * subset of the applicable information.
10000 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
10002 char fname_phdev[NAM$C_MAXRSS+1];
10003 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10004 /* Namecache not workable with symbolic links, as symbolic links do
10005 * not have extensions and directories do in VMS mode. So in order
10006 * to test this, the did and ino_t must be used.
10008 * Fix-me - Hide the information in the new stat structure
10009 * Get rid of the namecache.
10011 if (decc_posix_compliant_pathnames == 0)
10013 if (statbufp == &PL_statcache)
10014 return cando_by_name(bit,effective,namecache);
10016 char fname[NAM$C_MAXRSS+1];
10017 unsigned long int retsts;
10018 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
10019 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10021 /* If the struct mystat is stale, we're OOL; stat() overwrites the
10022 device name on successive calls */
10023 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
10024 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
10025 namdsc.dsc$a_pointer = fname;
10026 namdsc.dsc$w_length = sizeof fname - 1;
10028 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
10029 &namdsc,&namdsc.dsc$w_length,0,0);
10031 fname[namdsc.dsc$w_length] = '\0';
10033 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
10034 * but if someone has redefined that logical, Perl gets very lost. Since
10035 * we have the physical device name from the stat buffer, just paste it on.
10037 strcpy( fname_phdev, statbufp->st_devnam );
10038 strcat( fname_phdev, strrchr(fname, ':') );
10040 return cando_by_name(bit,effective,fname_phdev);
10042 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
10043 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
10047 return FALSE; /* Should never get to here */
10049 } /* end of cando() */
10053 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
10055 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
10057 static char usrname[L_cuserid];
10058 static struct dsc$descriptor_s usrdsc =
10059 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
10060 char vmsname[NAM$C_MAXRSS+1];
10062 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
10063 unsigned short int retlen, trnlnm_iter_count;
10064 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10065 union prvdef curprv;
10066 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
10067 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
10068 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
10069 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
10071 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
10073 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10075 if (!fname || !*fname) return FALSE;
10076 /* Make sure we expand logical names, since sys$check_access doesn't */
10077 fileified = PerlMem_malloc(VMS_MAXRSS);
10078 if (!strpbrk(fname,"/]>:")) {
10079 strcpy(fileified,fname);
10080 trnlnm_iter_count = 0;
10081 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
10082 trnlnm_iter_count++;
10083 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
10087 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS)) {
10088 PerlMem_free(fileified);
10091 retlen = namdsc.dsc$w_length = strlen(vmsname);
10092 namdsc.dsc$a_pointer = vmsname;
10093 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
10094 vmsname[retlen-1] == ':') {
10095 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
10096 namdsc.dsc$w_length = strlen(fileified);
10097 namdsc.dsc$a_pointer = fileified;
10101 case S_IXUSR: case S_IXGRP: case S_IXOTH:
10102 access = ARM$M_EXECUTE; break;
10103 case S_IRUSR: case S_IRGRP: case S_IROTH:
10104 access = ARM$M_READ; break;
10105 case S_IWUSR: case S_IWGRP: case S_IWOTH:
10106 access = ARM$M_WRITE; break;
10107 case S_IDUSR: case S_IDGRP: case S_IDOTH:
10108 access = ARM$M_DELETE; break;
10110 PerlMem_free(fileified);
10114 /* Before we call $check_access, create a user profile with the current
10115 * process privs since otherwise it just uses the default privs from the
10116 * UAF and might give false positives or negatives. This only works on
10117 * VMS versions v6.0 and later since that's when sys$create_user_profile
10118 * became available.
10121 /* get current process privs and username */
10122 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
10123 _ckvmssts(iosb[0]);
10125 #if defined(__VMS_VER) && __VMS_VER >= 60000000
10127 /* find out the space required for the profile */
10128 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
10129 &usrprodsc.dsc$w_length,0));
10131 /* allocate space for the profile and get it filled in */
10132 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
10133 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
10134 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
10135 &usrprodsc.dsc$w_length,0));
10137 /* use the profile to check access to the file; free profile & analyze results */
10138 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
10139 PerlMem_free(usrprodsc.dsc$a_pointer);
10140 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
10144 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
10148 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
10149 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
10150 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
10151 set_vaxc_errno(retsts);
10152 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10153 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
10154 else set_errno(ENOENT);
10155 PerlMem_free(fileified);
10158 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
10159 PerlMem_free(fileified);
10164 PerlMem_free(fileified);
10165 return FALSE; /* Should never get here */
10167 } /* end of cando_by_name() */
10171 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
10173 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
10175 if (!fstat(fd,(stat_t *) statbufp)) {
10176 if (statbufp == (Stat_t *) &PL_statcache) {
10179 /* Save name for cando by name in VMS format */
10180 cptr = getname(fd, namecache, 1);
10182 /* This should not happen, but just in case */
10184 namecache[0] = '\0';
10187 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10188 #ifndef _USE_STD_STAT
10189 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
10190 statbufp->st_devnam[63] = 0;
10191 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
10194 * The device is only encoded so that Perl_cando can use it to
10195 * look up ACLS. So rmsexpand it to the 255 character version
10196 * and store it in ->st_devnam. rmsexpand needs to be fixed
10197 * for long filenames and symbolic links first. This also seems
10198 * to remove the need for a namecache that could be stale.
10202 # ifdef RTL_USES_UTC
10203 # ifdef VMSISH_TIME
10205 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10206 statbufp->st_atime = _toloc(statbufp->st_atime);
10207 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10211 # ifdef VMSISH_TIME
10212 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10216 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10217 statbufp->st_atime = _toutc(statbufp->st_atime);
10218 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10225 } /* end of flex_fstat() */
10228 #if !defined(__VAX) && __CRTL_VER >= 80200000
10236 #define lstat(_x, _y) stat(_x, _y)
10239 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
10242 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
10244 char fileified[NAM$C_MAXRSS+1];
10245 char temp_fspec[NAM$C_MAXRSS+300];
10247 int saved_errno, saved_vaxc_errno;
10249 if (!fspec) return retval;
10250 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
10251 strcpy(temp_fspec, fspec);
10252 if (statbufp == (Stat_t *) &PL_statcache)
10253 do_tovmsspec(temp_fspec,namecache,0);
10254 if (decc_bug_devnull != 0) {
10255 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
10256 memset(statbufp,0,sizeof *statbufp);
10257 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
10258 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
10259 statbufp->st_uid = 0x00010001;
10260 statbufp->st_gid = 0x0001;
10261 time((time_t *)&statbufp->st_mtime);
10262 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
10267 /* Try for a directory name first. If fspec contains a filename without
10268 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
10269 * and sea:[wine.dark]water. exist, we prefer the directory here.
10270 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
10271 * not sea:[wine.dark]., if the latter exists. If the intended target is
10272 * the file with null type, specify this by calling flex_stat() with
10273 * a '.' at the end of fspec.
10275 * If we are in Posix filespec mode, accept the filename as is.
10277 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10278 if (decc_posix_compliant_pathnames == 0) {
10280 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
10281 if (lstat_flag == 0)
10282 retval = stat(fileified,(stat_t *) statbufp);
10284 retval = lstat(fileified,(stat_t *) statbufp);
10285 if (!retval && statbufp == (Stat_t *) &PL_statcache)
10286 strcpy(namecache,fileified);
10289 if (lstat_flag == 0)
10290 retval = stat(temp_fspec,(stat_t *) statbufp);
10292 retval = lstat(temp_fspec,(stat_t *) statbufp);
10294 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10296 if (lstat_flag == 0)
10297 retval = stat(temp_fspec,(stat_t *) statbufp);
10299 retval = lstat(temp_fspec,(stat_t *) statbufp);
10303 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10304 #ifndef _USE_STD_STAT
10305 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
10306 statbufp->st_devnam[63] = 0;
10307 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
10310 * The device is only encoded so that Perl_cando can use it to
10311 * look up ACLS. So rmsexpand it to the 255 character version
10312 * and store it in ->st_devnam. rmsexpand needs to be fixed
10313 * for long filenames and symbolic links first. This also seems
10314 * to remove the need for a namecache that could be stale.
10317 # ifdef RTL_USES_UTC
10318 # ifdef VMSISH_TIME
10320 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10321 statbufp->st_atime = _toloc(statbufp->st_atime);
10322 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10326 # ifdef VMSISH_TIME
10327 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10331 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10332 statbufp->st_atime = _toutc(statbufp->st_atime);
10333 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10337 /* If we were successful, leave errno where we found it */
10338 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
10341 } /* end of flex_stat_int() */
10344 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
10346 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
10348 return flex_stat_int(fspec, statbufp, 0);
10352 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
10354 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
10356 return flex_stat_int(fspec, statbufp, 1);
10361 /*{{{char *my_getlogin()*/
10362 /* VMS cuserid == Unix getlogin, except calling sequence */
10366 static char user[L_cuserid];
10367 return cuserid(user);
10372 /* rmscopy - copy a file using VMS RMS routines
10374 * Copies contents and attributes of spec_in to spec_out, except owner
10375 * and protection information. Name and type of spec_in are used as
10376 * defaults for spec_out. The third parameter specifies whether rmscopy()
10377 * should try to propagate timestamps from the input file to the output file.
10378 * If it is less than 0, no timestamps are preserved. If it is 0, then
10379 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
10380 * propagated to the output file at creation iff the output file specification
10381 * did not contain an explicit name or type, and the revision date is always
10382 * updated at the end of the copy operation. If it is greater than 0, then
10383 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
10384 * other than the revision date should be propagated, and bit 1 indicates
10385 * that the revision date should be propagated.
10387 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
10389 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
10390 * Incorporates, with permission, some code from EZCOPY by Tim Adye
10391 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
10392 * as part of the Perl standard distribution under the terms of the
10393 * GNU General Public License or the Perl Artistic License. Copies
10394 * of each may be found in the Perl standard distribution.
10396 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
10397 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
10399 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
10401 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
10402 rsa[NAM$C_MAXRSS], ubf[32256];
10403 unsigned long int i, sts, sts2;
10404 struct FAB fab_in, fab_out;
10405 struct RAB rab_in, rab_out;
10407 struct XABDAT xabdat;
10408 struct XABFHC xabfhc;
10409 struct XABRDT xabrdt;
10410 struct XABSUM xabsum;
10412 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
10413 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
10414 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10418 fab_in = cc$rms_fab;
10419 fab_in.fab$l_fna = vmsin;
10420 fab_in.fab$b_fns = strlen(vmsin);
10421 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
10422 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
10423 fab_in.fab$l_fop = FAB$M_SQO;
10424 fab_in.fab$l_nam = &nam;
10425 fab_in.fab$l_xab = (void *) &xabdat;
10428 nam.nam$l_rsa = rsa;
10429 nam.nam$b_rss = sizeof(rsa);
10430 nam.nam$l_esa = esa;
10431 nam.nam$b_ess = sizeof (esa);
10432 nam.nam$b_esl = nam.nam$b_rsl = 0;
10433 #ifdef NAM$M_NO_SHORT_UPCASE
10434 if (decc_efs_case_preserve)
10435 nam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
10438 xabdat = cc$rms_xabdat; /* To get creation date */
10439 xabdat.xab$l_nxt = (void *) &xabfhc;
10441 xabfhc = cc$rms_xabfhc; /* To get record length */
10442 xabfhc.xab$l_nxt = (void *) &xabsum;
10444 xabsum = cc$rms_xabsum; /* To get key and area information */
10446 if (!((sts = sys$open(&fab_in)) & 1)) {
10447 set_vaxc_errno(sts);
10449 case RMS$_FNF: case RMS$_DNF:
10450 set_errno(ENOENT); break;
10452 set_errno(ENOTDIR); break;
10454 set_errno(ENODEV); break;
10456 set_errno(EINVAL); break;
10458 set_errno(EACCES); break;
10460 set_errno(EVMSERR);
10466 fab_out.fab$w_ifi = 0;
10467 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
10468 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
10469 fab_out.fab$l_fop = FAB$M_SQO;
10470 fab_out.fab$l_fna = vmsout;
10471 fab_out.fab$b_fns = strlen(vmsout);
10472 fab_out.fab$l_dna = nam.nam$l_name;
10473 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
10475 if (preserve_dates == 0) { /* Act like DCL COPY */
10476 nam.nam$b_nop |= NAM$M_SYNCHK;
10477 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
10478 if (!((sts = sys$parse(&fab_out)) & 1)) {
10479 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
10480 set_vaxc_errno(sts);
10483 fab_out.fab$l_xab = (void *) &xabdat;
10484 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
10486 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
10487 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
10488 preserve_dates =0; /* bitmask from this point forward */
10490 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
10491 if (!((sts = sys$create(&fab_out)) & 1)) {
10492 set_vaxc_errno(sts);
10495 set_errno(ENOENT); break;
10497 set_errno(ENOTDIR); break;
10499 set_errno(ENODEV); break;
10501 set_errno(EINVAL); break;
10503 set_errno(EACCES); break;
10505 set_errno(EVMSERR);
10509 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
10510 if (preserve_dates & 2) {
10511 /* sys$close() will process xabrdt, not xabdat */
10512 xabrdt = cc$rms_xabrdt;
10514 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
10516 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
10517 * is unsigned long[2], while DECC & VAXC use a struct */
10518 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
10520 fab_out.fab$l_xab = (void *) &xabrdt;
10523 rab_in = cc$rms_rab;
10524 rab_in.rab$l_fab = &fab_in;
10525 rab_in.rab$l_rop = RAB$M_BIO;
10526 rab_in.rab$l_ubf = ubf;
10527 rab_in.rab$w_usz = sizeof ubf;
10528 if (!((sts = sys$connect(&rab_in)) & 1)) {
10529 sys$close(&fab_in); sys$close(&fab_out);
10530 set_errno(EVMSERR); set_vaxc_errno(sts);
10534 rab_out = cc$rms_rab;
10535 rab_out.rab$l_fab = &fab_out;
10536 rab_out.rab$l_rbf = ubf;
10537 if (!((sts = sys$connect(&rab_out)) & 1)) {
10538 sys$close(&fab_in); sys$close(&fab_out);
10539 set_errno(EVMSERR); set_vaxc_errno(sts);
10543 while ((sts = sys$read(&rab_in))) { /* always true */
10544 if (sts == RMS$_EOF) break;
10545 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
10546 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
10547 sys$close(&fab_in); sys$close(&fab_out);
10548 set_errno(EVMSERR); set_vaxc_errno(sts);
10553 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
10554 sys$close(&fab_in); sys$close(&fab_out);
10555 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
10557 set_errno(EVMSERR); set_vaxc_errno(sts);
10563 } /* end of rmscopy() */
10565 /* ODS-5 support version */
10567 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
10569 char *vmsin, * vmsout, *esa, *esa_out,
10571 unsigned long int i, sts, sts2;
10572 struct FAB fab_in, fab_out;
10573 struct RAB rab_in, rab_out;
10575 struct NAML nam_out;
10576 struct XABDAT xabdat;
10577 struct XABFHC xabfhc;
10578 struct XABRDT xabrdt;
10579 struct XABSUM xabsum;
10581 vmsin = PerlMem_malloc(VMS_MAXRSS);
10582 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
10583 vmsout = PerlMem_malloc(VMS_MAXRSS);
10584 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
10585 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
10586 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
10587 PerlMem_free(vmsin);
10588 PerlMem_free(vmsout);
10589 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10593 esa = PerlMem_malloc(VMS_MAXRSS);
10594 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
10596 fab_in = cc$rms_fab;
10597 fab_in.fab$l_fna = (char *) -1;
10598 fab_in.fab$b_fns = 0;
10599 nam.naml$l_long_filename = vmsin;
10600 nam.naml$l_long_filename_size = strlen(vmsin);
10601 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
10602 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
10603 fab_in.fab$l_fop = FAB$M_SQO;
10604 fab_in.fab$l_naml = &nam;
10605 fab_in.fab$l_xab = (void *) &xabdat;
10607 rsa = PerlMem_malloc(VMS_MAXRSS);
10608 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
10609 nam.naml$l_rsa = NULL;
10610 nam.naml$b_rss = 0;
10611 nam.naml$l_long_result = rsa;
10612 nam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
10613 nam.naml$l_esa = NULL;
10614 nam.naml$b_ess = 0;
10615 nam.naml$l_long_expand = esa;
10616 nam.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
10617 nam.naml$b_esl = nam.naml$b_rsl = 0;
10618 nam.naml$l_long_expand_size = 0;
10619 nam.naml$l_long_result_size = 0;
10620 #ifdef NAM$M_NO_SHORT_UPCASE
10621 if (decc_efs_case_preserve)
10622 nam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
10625 xabdat = cc$rms_xabdat; /* To get creation date */
10626 xabdat.xab$l_nxt = (void *) &xabfhc;
10628 xabfhc = cc$rms_xabfhc; /* To get record length */
10629 xabfhc.xab$l_nxt = (void *) &xabsum;
10631 xabsum = cc$rms_xabsum; /* To get key and area information */
10633 if (!((sts = sys$open(&fab_in)) & 1)) {
10634 PerlMem_free(vmsin);
10635 PerlMem_free(vmsout);
10638 set_vaxc_errno(sts);
10640 case RMS$_FNF: case RMS$_DNF:
10641 set_errno(ENOENT); break;
10643 set_errno(ENOTDIR); break;
10645 set_errno(ENODEV); break;
10647 set_errno(EINVAL); break;
10649 set_errno(EACCES); break;
10651 set_errno(EVMSERR);
10658 fab_out.fab$w_ifi = 0;
10659 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
10660 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
10661 fab_out.fab$l_fop = FAB$M_SQO;
10662 fab_out.fab$l_naml = &nam_out;
10663 fab_out.fab$l_fna = (char *) -1;
10664 fab_out.fab$b_fns = 0;
10665 nam_out.naml$l_long_filename = vmsout;
10666 nam_out.naml$l_long_filename_size = strlen(vmsout);
10667 fab_out.fab$l_dna = (char *) -1;
10668 fab_out.fab$b_dns = 0;
10669 nam_out.naml$l_long_defname = nam.naml$l_long_name;
10670 nam_out.naml$l_long_defname_size =
10671 nam.naml$l_long_name ?
10672 nam.naml$l_long_name_size + nam.naml$l_long_type_size : 0;
10674 esa_out = PerlMem_malloc(VMS_MAXRSS);
10675 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
10676 nam_out.naml$l_rsa = NULL;
10677 nam_out.naml$b_rss = 0;
10678 nam_out.naml$l_long_result = NULL;
10679 nam_out.naml$l_long_result_alloc = 0;
10680 nam_out.naml$l_esa = NULL;
10681 nam_out.naml$b_ess = 0;
10682 nam_out.naml$l_long_expand = esa_out;
10683 nam_out.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
10685 if (preserve_dates == 0) { /* Act like DCL COPY */
10686 nam_out.naml$b_nop |= NAM$M_SYNCHK;
10687 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
10688 if (!((sts = sys$parse(&fab_out)) & 1)) {
10689 PerlMem_free(vmsin);
10690 PerlMem_free(vmsout);
10693 PerlMem_free(esa_out);
10694 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
10695 set_vaxc_errno(sts);
10698 fab_out.fab$l_xab = (void *) &xabdat;
10699 if (nam.naml$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
10701 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
10702 preserve_dates =0; /* bitmask from this point forward */
10704 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
10705 if (!((sts = sys$create(&fab_out)) & 1)) {
10706 PerlMem_free(vmsin);
10707 PerlMem_free(vmsout);
10710 PerlMem_free(esa_out);
10711 set_vaxc_errno(sts);
10714 set_errno(ENOENT); break;
10716 set_errno(ENOTDIR); break;
10718 set_errno(ENODEV); break;
10720 set_errno(EINVAL); break;
10722 set_errno(EACCES); break;
10724 set_errno(EVMSERR);
10728 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
10729 if (preserve_dates & 2) {
10730 /* sys$close() will process xabrdt, not xabdat */
10731 xabrdt = cc$rms_xabrdt;
10733 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
10735 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
10736 * is unsigned long[2], while DECC & VAXC use a struct */
10737 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
10739 fab_out.fab$l_xab = (void *) &xabrdt;
10742 ubf = PerlMem_malloc(32256);
10743 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
10744 rab_in = cc$rms_rab;
10745 rab_in.rab$l_fab = &fab_in;
10746 rab_in.rab$l_rop = RAB$M_BIO;
10747 rab_in.rab$l_ubf = ubf;
10748 rab_in.rab$w_usz = 32256;
10749 if (!((sts = sys$connect(&rab_in)) & 1)) {
10750 sys$close(&fab_in); sys$close(&fab_out);
10751 PerlMem_free(vmsin);
10752 PerlMem_free(vmsout);
10756 PerlMem_free(esa_out);
10757 set_errno(EVMSERR); set_vaxc_errno(sts);
10761 rab_out = cc$rms_rab;
10762 rab_out.rab$l_fab = &fab_out;
10763 rab_out.rab$l_rbf = ubf;
10764 if (!((sts = sys$connect(&rab_out)) & 1)) {
10765 sys$close(&fab_in); sys$close(&fab_out);
10766 PerlMem_free(vmsin);
10767 PerlMem_free(vmsout);
10771 PerlMem_free(esa_out);
10772 set_errno(EVMSERR); set_vaxc_errno(sts);
10776 while ((sts = sys$read(&rab_in))) { /* always true */
10777 if (sts == RMS$_EOF) break;
10778 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
10779 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
10780 sys$close(&fab_in); sys$close(&fab_out);
10781 PerlMem_free(vmsin);
10782 PerlMem_free(vmsout);
10786 PerlMem_free(esa_out);
10787 set_errno(EVMSERR); set_vaxc_errno(sts);
10793 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
10794 sys$close(&fab_in); sys$close(&fab_out);
10795 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
10797 PerlMem_free(vmsin);
10798 PerlMem_free(vmsout);
10802 PerlMem_free(esa_out);
10803 set_errno(EVMSERR); set_vaxc_errno(sts);
10807 PerlMem_free(vmsin);
10808 PerlMem_free(vmsout);
10812 PerlMem_free(esa_out);
10815 } /* end of rmscopy() */
10820 /*** The following glue provides 'hooks' to make some of the routines
10821 * from this file available from Perl. These routines are sufficiently
10822 * basic, and are required sufficiently early in the build process,
10823 * that's it's nice to have them available to miniperl as well as the
10824 * full Perl, so they're set up here instead of in an extension. The
10825 * Perl code which handles importation of these names into a given
10826 * package lives in [.VMS]Filespec.pm in @INC.
10830 rmsexpand_fromperl(pTHX_ CV *cv)
10833 char *fspec, *defspec = NULL, *rslt;
10836 if (!items || items > 2)
10837 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
10838 fspec = SvPV(ST(0),n_a);
10839 if (!fspec || !*fspec) XSRETURN_UNDEF;
10840 if (items == 2) defspec = SvPV(ST(1),n_a);
10842 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
10843 ST(0) = sv_newmortal();
10844 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
10849 vmsify_fromperl(pTHX_ CV *cv)
10855 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
10856 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
10857 ST(0) = sv_newmortal();
10858 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
10863 unixify_fromperl(pTHX_ CV *cv)
10869 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
10870 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
10871 ST(0) = sv_newmortal();
10872 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
10877 fileify_fromperl(pTHX_ CV *cv)
10883 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
10884 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
10885 ST(0) = sv_newmortal();
10886 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
10891 pathify_fromperl(pTHX_ CV *cv)
10897 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
10898 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
10899 ST(0) = sv_newmortal();
10900 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
10905 vmspath_fromperl(pTHX_ CV *cv)
10911 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
10912 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
10913 ST(0) = sv_newmortal();
10914 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
10919 unixpath_fromperl(pTHX_ CV *cv)
10925 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
10926 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
10927 ST(0) = sv_newmortal();
10928 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
10933 candelete_fromperl(pTHX_ CV *cv)
10936 char fspec[NAM$C_MAXRSS+1], *fsp;
10941 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
10943 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
10944 if (SvTYPE(mysv) == SVt_PVGV) {
10945 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
10946 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10953 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
10954 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10960 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
10965 rmscopy_fromperl(pTHX_ CV *cv)
10968 char *inspec, *outspec, *inp, *outp;
10970 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
10971 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10972 unsigned long int sts;
10977 if (items < 2 || items > 3)
10978 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
10980 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
10981 Newx(inspec, VMS_MAXRSS, char);
10982 if (SvTYPE(mysv) == SVt_PVGV) {
10983 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
10984 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10992 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
10993 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10999 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
11000 Newx(outspec, VMS_MAXRSS, char);
11001 if (SvTYPE(mysv) == SVt_PVGV) {
11002 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
11003 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11012 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
11013 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11020 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
11022 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
11028 /* The mod2fname is limited to shorter filenames by design, so it should
11029 * not be modified to support longer EFS pathnames
11032 mod2fname(pTHX_ CV *cv)
11035 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
11036 workbuff[NAM$C_MAXRSS*1 + 1];
11037 int total_namelen = 3, counter, num_entries;
11038 /* ODS-5 ups this, but we want to be consistent, so... */
11039 int max_name_len = 39;
11040 AV *in_array = (AV *)SvRV(ST(0));
11042 num_entries = av_len(in_array);
11044 /* All the names start with PL_. */
11045 strcpy(ultimate_name, "PL_");
11047 /* Clean up our working buffer */
11048 Zero(work_name, sizeof(work_name), char);
11050 /* Run through the entries and build up a working name */
11051 for(counter = 0; counter <= num_entries; counter++) {
11052 /* If it's not the first name then tack on a __ */
11054 strcat(work_name, "__");
11056 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
11060 /* Check to see if we actually have to bother...*/
11061 if (strlen(work_name) + 3 <= max_name_len) {
11062 strcat(ultimate_name, work_name);
11064 /* It's too darned big, so we need to go strip. We use the same */
11065 /* algorithm as xsubpp does. First, strip out doubled __ */
11066 char *source, *dest, last;
11069 for (source = work_name; *source; source++) {
11070 if (last == *source && last == '_') {
11076 /* Go put it back */
11077 strcpy(work_name, workbuff);
11078 /* Is it still too big? */
11079 if (strlen(work_name) + 3 > max_name_len) {
11080 /* Strip duplicate letters */
11083 for (source = work_name; *source; source++) {
11084 if (last == toupper(*source)) {
11088 last = toupper(*source);
11090 strcpy(work_name, workbuff);
11093 /* Is it *still* too big? */
11094 if (strlen(work_name) + 3 > max_name_len) {
11095 /* Too bad, we truncate */
11096 work_name[max_name_len - 2] = 0;
11098 strcat(ultimate_name, work_name);
11101 /* Okay, return it */
11102 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
11107 hushexit_fromperl(pTHX_ CV *cv)
11112 VMSISH_HUSHED = SvTRUE(ST(0));
11114 ST(0) = boolSV(VMSISH_HUSHED);
11120 Perl_vms_start_glob
11121 (pTHX_ SV *tmpglob,
11125 struct vs_str_st *rslt;
11129 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
11132 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11133 struct dsc$descriptor_vs rsdsc;
11134 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
11135 unsigned long hasver = 0, isunix = 0;
11136 unsigned long int lff_flags = 0;
11139 #ifdef VMS_LONGNAME_SUPPORT
11140 lff_flags = LIB$M_FIL_LONG_NAMES;
11142 /* The Newx macro will not allow me to assign a smaller array
11143 * to the rslt pointer, so we will assign it to the begin char pointer
11144 * and then copy the value into the rslt pointer.
11146 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
11147 rslt = (struct vs_str_st *)begin;
11149 rstr = &rslt->str[0];
11150 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
11151 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
11152 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
11153 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
11155 Newx(vmsspec, VMS_MAXRSS, char);
11157 /* We could find out if there's an explicit dev/dir or version
11158 by peeking into lib$find_file's internal context at
11159 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
11160 but that's unsupported, so I don't want to do it now and
11161 have it bite someone in the future. */
11162 /* Fix-me: vms_split_path() is the only way to do this, the
11163 existing method will fail with many legal EFS or UNIX specifications
11166 cp = SvPV(tmpglob,i);
11169 if (cp[i] == ';') hasver = 1;
11170 if (cp[i] == '.') {
11171 if (sts) hasver = 1;
11174 if (cp[i] == '/') {
11175 hasdir = isunix = 1;
11178 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
11183 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
11186 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
11187 if (!stat_sts && S_ISDIR(st.st_mode)) {
11188 wilddsc.dsc$a_pointer = tovmspath(SvPVX(tmpglob),vmsspec);
11189 ok = (wilddsc.dsc$a_pointer != NULL);
11192 wilddsc.dsc$a_pointer = tovmsspec(SvPVX(tmpglob),vmsspec);
11193 ok = (wilddsc.dsc$a_pointer != NULL);
11196 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
11198 /* If not extended character set, replace ? with % */
11199 /* With extended character set, ? is a wildcard single character */
11200 if (!decc_efs_case_preserve) {
11201 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
11202 if (*cp == '?') *cp = '%';
11205 while (ok && $VMS_STATUS_SUCCESS(sts)) {
11206 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
11207 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
11209 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
11210 &dfltdsc,NULL,&rms_sts,&lff_flags);
11211 if (!$VMS_STATUS_SUCCESS(sts))
11214 /* with varying string, 1st word of buffer contains result length */
11215 rstr[rslt->length] = '\0';
11217 /* Find where all the components are */
11218 v_sts = vms_split_path
11233 /* If no version on input, truncate the version on output */
11234 if (!hasver && (vs_len > 0)) {
11238 /* No version & a null extension on UNIX handling */
11239 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
11245 if (!decc_efs_case_preserve) {
11246 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
11250 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
11254 /* Start with the name */
11257 strcat(begin,"\n");
11258 ok = (PerlIO_puts(tmpfp,begin) != EOF);
11260 if (cxt) (void)lib$find_file_end(&cxt);
11261 if (ok && sts != RMS$_NMF &&
11262 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
11265 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
11267 PerlIO_close(tmpfp);
11271 PerlIO_rewind(tmpfp);
11272 IoTYPE(io) = IoTYPE_RDONLY;
11273 IoIFP(io) = fp = tmpfp;
11274 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
11284 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec);
11287 vms_realpath_fromperl(pTHX_ CV *cv)
11290 char *fspec, *rslt_spec, *rslt;
11293 if (!items || items != 1)
11294 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
11296 fspec = SvPV(ST(0),n_a);
11297 if (!fspec || !*fspec) XSRETURN_UNDEF;
11299 Newx(rslt_spec, VMS_MAXRSS + 1, char);
11300 rslt = do_vms_realpath(fspec, rslt_spec);
11301 ST(0) = sv_newmortal();
11303 sv_usepvn(ST(0),rslt,strlen(rslt));
11305 Safefree(rslt_spec);
11310 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11311 int do_vms_case_tolerant(void);
11314 vms_case_tolerant_fromperl(pTHX_ CV *cv)
11317 ST(0) = boolSV(do_vms_case_tolerant());
11323 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
11324 struct interp_intern *dst)
11326 memcpy(dst,src,sizeof(struct interp_intern));
11330 Perl_sys_intern_clear(pTHX)
11335 Perl_sys_intern_init(pTHX)
11337 unsigned int ix = RAND_MAX;
11342 /* fix me later to track running under GNV */
11343 /* this allows some limited testing */
11344 MY_POSIX_EXIT = decc_filename_unix_report;
11347 MY_INV_RAND_MAX = 1./x;
11351 init_os_extras(void)
11354 char* file = __FILE__;
11355 char temp_buff[512];
11356 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
11357 no_translate_barewords = TRUE;
11359 no_translate_barewords = FALSE;
11362 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
11363 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
11364 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
11365 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
11366 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
11367 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
11368 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
11369 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
11370 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
11371 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
11372 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
11374 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
11376 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11377 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
11380 store_pipelocs(aTHX); /* will redo any earlier attempts */
11387 #if __CRTL_VER == 80200000
11388 /* This missed getting in to the DECC SDK for 8.2 */
11389 char *realpath(const char *file_name, char * resolved_name, ...);
11392 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
11393 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
11394 * The perl fallback routine to provide realpath() is not as efficient
11398 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf)
11400 return realpath(filespec, outbuf);
11404 /* External entry points */
11405 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
11406 { return do_vms_realpath(filespec, outbuf); }
11408 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
11413 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11414 /* case_tolerant */
11416 /*{{{int do_vms_case_tolerant(void)*/
11417 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
11418 * controlled by a process setting.
11420 int do_vms_case_tolerant(void)
11422 return vms_process_case_tolerant;
11425 /* External entry points */
11426 int Perl_vms_case_tolerant(void)
11427 { return do_vms_case_tolerant(); }
11429 int Perl_vms_case_tolerant(void)
11430 { return vms_process_case_tolerant; }
11434 /* Start of DECC RTL Feature handling */
11436 static int sys_trnlnm
11437 (const char * logname,
11441 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
11442 const unsigned long attr = LNM$M_CASE_BLIND;
11443 struct dsc$descriptor_s name_dsc;
11445 unsigned short result;
11446 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
11449 name_dsc.dsc$w_length = strlen(logname);
11450 name_dsc.dsc$a_pointer = (char *)logname;
11451 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11452 name_dsc.dsc$b_class = DSC$K_CLASS_S;
11454 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
11456 if ($VMS_STATUS_SUCCESS(status)) {
11458 /* Null terminate and return the string */
11459 /*--------------------------------------*/
11466 static int sys_crelnm
11467 (const char * logname,
11468 const char * value)
11471 const char * proc_table = "LNM$PROCESS_TABLE";
11472 struct dsc$descriptor_s proc_table_dsc;
11473 struct dsc$descriptor_s logname_dsc;
11474 struct itmlst_3 item_list[2];
11476 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
11477 proc_table_dsc.dsc$w_length = strlen(proc_table);
11478 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11479 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
11481 logname_dsc.dsc$a_pointer = (char *) logname;
11482 logname_dsc.dsc$w_length = strlen(logname);
11483 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11484 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
11486 item_list[0].buflen = strlen(value);
11487 item_list[0].itmcode = LNM$_STRING;
11488 item_list[0].bufadr = (char *)value;
11489 item_list[0].retlen = NULL;
11491 item_list[1].buflen = 0;
11492 item_list[1].itmcode = 0;
11494 ret_val = sys$crelnm
11496 (const struct dsc$descriptor_s *)&proc_table_dsc,
11497 (const struct dsc$descriptor_s *)&logname_dsc,
11499 (const struct item_list_3 *) item_list);
11505 /* C RTL Feature settings */
11507 static int set_features
11508 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
11509 int (* cli_routine)(void), /* Not documented */
11510 void *image_info) /* Not documented */
11517 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
11518 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
11519 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
11520 unsigned long case_perm;
11521 unsigned long case_image;
11524 /* Allow an exception to bring Perl into the VMS debugger */
11525 vms_debug_on_exception = 0;
11526 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
11527 if ($VMS_STATUS_SUCCESS(status)) {
11528 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11529 vms_debug_on_exception = 1;
11531 vms_debug_on_exception = 0;
11535 /* hacks to see if known bugs are still present for testing */
11537 /* Readdir is returning filenames in VMS syntax always */
11538 decc_bug_readdir_efs1 = 1;
11539 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
11540 if ($VMS_STATUS_SUCCESS(status)) {
11541 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11542 decc_bug_readdir_efs1 = 1;
11544 decc_bug_readdir_efs1 = 0;
11547 /* PCP mode requires creating /dev/null special device file */
11548 decc_bug_devnull = 0;
11549 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
11550 if ($VMS_STATUS_SUCCESS(status)) {
11551 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11552 decc_bug_devnull = 1;
11554 decc_bug_devnull = 0;
11557 /* fgetname returning a VMS name in UNIX mode */
11558 decc_bug_fgetname = 1;
11559 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
11560 if ($VMS_STATUS_SUCCESS(status)) {
11561 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11562 decc_bug_fgetname = 1;
11564 decc_bug_fgetname = 0;
11567 /* UNIX directory names with no paths are broken in a lot of places */
11568 decc_dir_barename = 1;
11569 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
11570 if ($VMS_STATUS_SUCCESS(status)) {
11571 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11572 decc_dir_barename = 1;
11574 decc_dir_barename = 0;
11577 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11578 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
11580 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
11581 if (decc_disable_to_vms_logname_translation < 0)
11582 decc_disable_to_vms_logname_translation = 0;
11585 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
11587 decc_efs_case_preserve = decc$feature_get_value(s, 1);
11588 if (decc_efs_case_preserve < 0)
11589 decc_efs_case_preserve = 0;
11592 s = decc$feature_get_index("DECC$EFS_CHARSET");
11594 decc_efs_charset = decc$feature_get_value(s, 1);
11595 if (decc_efs_charset < 0)
11596 decc_efs_charset = 0;
11599 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
11601 decc_filename_unix_report = decc$feature_get_value(s, 1);
11602 if (decc_filename_unix_report > 0)
11603 decc_filename_unix_report = 1;
11605 decc_filename_unix_report = 0;
11608 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
11610 decc_filename_unix_only = decc$feature_get_value(s, 1);
11611 if (decc_filename_unix_only > 0) {
11612 decc_filename_unix_only = 1;
11615 decc_filename_unix_only = 0;
11619 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
11621 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
11622 if (decc_filename_unix_no_version < 0)
11623 decc_filename_unix_no_version = 0;
11626 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
11628 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
11629 if (decc_readdir_dropdotnotype < 0)
11630 decc_readdir_dropdotnotype = 0;
11633 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
11634 if ($VMS_STATUS_SUCCESS(status)) {
11635 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
11637 dflt = decc$feature_get_value(s, 4);
11639 decc_disable_posix_root = decc$feature_get_value(s, 1);
11640 if (decc_disable_posix_root <= 0) {
11641 decc$feature_set_value(s, 1, 1);
11642 decc_disable_posix_root = 1;
11646 /* Traditionally Perl assumes this is off */
11647 decc_disable_posix_root = 1;
11648 decc$feature_set_value(s, 1, 1);
11653 #if __CRTL_VER >= 80200000
11654 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
11656 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
11657 if (decc_posix_compliant_pathnames < 0)
11658 decc_posix_compliant_pathnames = 0;
11659 if (decc_posix_compliant_pathnames > 4)
11660 decc_posix_compliant_pathnames = 0;
11665 status = sys_trnlnm
11666 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
11667 if ($VMS_STATUS_SUCCESS(status)) {
11668 val_str[0] = _toupper(val_str[0]);
11669 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11670 decc_disable_to_vms_logname_translation = 1;
11675 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
11676 if ($VMS_STATUS_SUCCESS(status)) {
11677 val_str[0] = _toupper(val_str[0]);
11678 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11679 decc_efs_case_preserve = 1;
11684 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
11685 if ($VMS_STATUS_SUCCESS(status)) {
11686 val_str[0] = _toupper(val_str[0]);
11687 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11688 decc_filename_unix_report = 1;
11691 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
11692 if ($VMS_STATUS_SUCCESS(status)) {
11693 val_str[0] = _toupper(val_str[0]);
11694 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11695 decc_filename_unix_only = 1;
11696 decc_filename_unix_report = 1;
11699 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
11700 if ($VMS_STATUS_SUCCESS(status)) {
11701 val_str[0] = _toupper(val_str[0]);
11702 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11703 decc_filename_unix_no_version = 1;
11706 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
11707 if ($VMS_STATUS_SUCCESS(status)) {
11708 val_str[0] = _toupper(val_str[0]);
11709 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11710 decc_readdir_dropdotnotype = 1;
11715 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
11717 /* Report true case tolerance */
11718 /*----------------------------*/
11719 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
11720 if (!$VMS_STATUS_SUCCESS(status))
11721 case_perm = PPROP$K_CASE_BLIND;
11722 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
11723 if (!$VMS_STATUS_SUCCESS(status))
11724 case_image = PPROP$K_CASE_BLIND;
11725 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
11726 (case_image == PPROP$K_CASE_SENSITIVE))
11727 vms_process_case_tolerant = 0;
11732 /* CRTL can be initialized past this point, but not before. */
11733 /* DECC$CRTL_INIT(); */
11739 /* DECC dependent attributes */
11740 #if __DECC_VER < 60560002
11742 #define not_executable
11744 #define relative ,rel
11745 #define not_executable ,noexe
11748 #pragma extern_model save
11749 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
11751 const __align (LONGWORD) int spare[8] = {0};
11752 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
11755 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
11756 nowrt,noshr relative not_executable
11758 const long vms_cc_features = (const long)set_features;
11761 ** Force a reference to LIB$INITIALIZE to ensure it
11762 ** exists in the image.
11764 int lib$initialize(void);
11766 #pragma extern_model strict_refdef
11768 int lib_init_ref = (int) lib$initialize;
11771 #pragma extern_model restore