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 #pragma message restore
172 #pragma member_alignment restore
175 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
176 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
177 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
178 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
179 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
180 #define do_vms_realpath(a,b) mp_do_vms_realpath(aTHX_ a,b)
181 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
182 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
183 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
184 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
185 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
187 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts);
188 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts);
189 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
190 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts);
192 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
193 #define PERL_LNM_MAX_ALLOWED_INDEX 127
195 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
196 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
199 #define PERL_LNM_MAX_ITER 10
201 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
202 #if __CRTL_VER >= 70302000 && !defined(__VAX)
203 #define MAX_DCL_SYMBOL (8192)
204 #define MAX_DCL_LINE_LENGTH (4096 - 4)
206 #define MAX_DCL_SYMBOL (1024)
207 #define MAX_DCL_LINE_LENGTH (1024 - 4)
210 static char *__mystrtolower(char *str)
212 if (str) for (; *str; ++str) *str= tolower(*str);
216 static struct dsc$descriptor_s fildevdsc =
217 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
218 static struct dsc$descriptor_s crtlenvdsc =
219 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
220 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
221 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
222 static struct dsc$descriptor_s **env_tables = defenv;
223 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
225 /* True if we shouldn't treat barewords as logicals during directory */
227 static int no_translate_barewords;
230 static int tz_updated = 1;
233 /* DECC Features that may need to affect how Perl interprets
234 * displays filename information
236 static int decc_disable_to_vms_logname_translation = 1;
237 static int decc_disable_posix_root = 1;
238 int decc_efs_case_preserve = 0;
239 static int decc_efs_charset = 0;
240 static int decc_filename_unix_no_version = 0;
241 static int decc_filename_unix_only = 0;
242 int decc_filename_unix_report = 0;
243 int decc_posix_compliant_pathnames = 0;
244 int decc_readdir_dropdotnotype = 0;
245 static int vms_process_case_tolerant = 1;
247 /* bug workarounds if needed */
248 int decc_bug_readdir_efs1 = 0;
249 int decc_bug_devnull = 0;
250 int decc_bug_fgetname = 0;
251 int decc_dir_barename = 0;
253 /* Is this a UNIX file specification?
254 * No longer a simple check with EFS file specs
255 * For now, not a full check, but need to
256 * handle POSIX ^UP^ specifications
257 * Fixing to handle ^/ cases would require
258 * changes to many other conversion routines.
261 static is_unix_filespec(const char *path)
267 if (strncmp(path,"\"^UP^",5) != 0) {
268 pch1 = strchr(path, '/');
273 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
274 if (decc_filename_unix_report || decc_filename_unix_only) {
275 if (strcmp(path,".") == 0)
285 * Routine to retrieve the maximum equivalence index for an input
286 * logical name. Some calls to this routine have no knowledge if
287 * the variable is a logical or not. So on error we return a max
290 /*{{{int my_maxidx(const char *lnm) */
292 my_maxidx(const char *lnm)
296 int attr = LNM$M_CASE_BLIND;
297 struct dsc$descriptor lnmdsc;
298 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
301 lnmdsc.dsc$w_length = strlen(lnm);
302 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
303 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
304 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
306 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
307 if ((status & 1) == 0)
314 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
316 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
317 struct dsc$descriptor_s **tabvec, unsigned long int flags)
320 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
321 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
322 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
324 unsigned char acmode;
325 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
326 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
327 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
328 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
330 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
331 #if defined(PERL_IMPLICIT_CONTEXT)
334 aTHX = PERL_GET_INTERP;
340 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
341 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
343 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
344 *cp2 = _toupper(*cp1);
345 if (cp1 - lnm > LNM$C_NAMLENGTH) {
346 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
350 lnmdsc.dsc$w_length = cp1 - lnm;
351 lnmdsc.dsc$a_pointer = uplnm;
352 uplnm[lnmdsc.dsc$w_length] = '\0';
353 secure = flags & PERL__TRNENV_SECURE;
354 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
355 if (!tabvec || !*tabvec) tabvec = env_tables;
357 for (curtab = 0; tabvec[curtab]; curtab++) {
358 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
359 if (!ivenv && !secure) {
364 Perl_warn(aTHX_ "Can't read CRTL environ\n");
367 retsts = SS$_NOLOGNAM;
368 for (i = 0; environ[i]; i++) {
369 if ((eq = strchr(environ[i],'=')) &&
370 lnmdsc.dsc$w_length == (eq - environ[i]) &&
371 !strncmp(environ[i],uplnm,eq - environ[i])) {
373 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
374 if (!eqvlen) continue;
379 if (retsts != SS$_NOLOGNAM) break;
382 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
383 !str$case_blind_compare(&tmpdsc,&clisym)) {
384 if (!ivsym && !secure) {
385 unsigned short int deflen = LNM$C_NAMLENGTH;
386 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
387 /* dynamic dsc to accomodate possible long value */
388 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
389 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
391 if (eqvlen > MAX_DCL_SYMBOL) {
392 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
393 eqvlen = MAX_DCL_SYMBOL;
394 /* Special hack--we might be called before the interpreter's */
395 /* fully initialized, in which case either thr or PL_curcop */
396 /* might be bogus. We have to check, since ckWARN needs them */
397 /* both to be valid if running threaded */
398 if (ckWARN(WARN_MISC)) {
399 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
402 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
404 _ckvmssts(lib$sfree1_dd(&eqvdsc));
405 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
406 if (retsts == LIB$_NOSUCHSYM) continue;
411 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
412 midx = my_maxidx(lnm);
413 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
414 lnmlst[1].bufadr = cp2;
416 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
417 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
418 if (retsts == SS$_NOLOGNAM) break;
419 /* PPFs have a prefix */
422 *((int *)uplnm) == *((int *)"SYS$") &&
424 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
425 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
426 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
427 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
428 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
429 memmove(eqv,eqv+4,eqvlen-4);
435 if ((retsts == SS$_IVLOGNAM) ||
436 (retsts == SS$_NOLOGNAM)) { continue; }
439 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
440 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
441 if (retsts == SS$_NOLOGNAM) continue;
444 eqvlen = strlen(eqv);
448 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
449 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
450 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
451 retsts == SS$_NOLOGNAM) {
452 set_errno(EINVAL); set_vaxc_errno(retsts);
454 else _ckvmssts(retsts);
456 } /* end of vmstrnenv */
459 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
460 /* Define as a function so we can access statics. */
461 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
463 return vmstrnenv(lnm,eqv,idx,fildev,
464 #ifdef SECURE_INTERNAL_GETENV
465 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
474 * Note: Uses Perl temp to store result so char * can be returned to
475 * caller; this pointer will be invalidated at next Perl statement
477 * We define this as a function rather than a macro in terms of my_getenv_len()
478 * so that it'll work when PL_curinterp is undefined (and we therefore can't
481 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
483 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
486 static char *__my_getenv_eqv = NULL;
487 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
488 unsigned long int idx = 0;
489 int trnsuccess, success, secure, saverr, savvmserr;
493 midx = my_maxidx(lnm) + 1;
495 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
496 /* Set up a temporary buffer for the return value; Perl will
497 * clean it up at the next statement transition */
498 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
499 if (!tmpsv) return NULL;
503 /* Assume no interpreter ==> single thread */
504 if (__my_getenv_eqv != NULL) {
505 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
508 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
510 eqv = __my_getenv_eqv;
513 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
514 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
516 getcwd(eqv,LNM$C_NAMLENGTH);
520 /* Get rid of "000000/ in rooted filespecs */
523 zeros = strstr(eqv, "/000000/");
526 mlen = len - (zeros - eqv) - 7;
527 memmove(zeros, &zeros[7], mlen);
535 /* Impose security constraints only if tainting */
537 /* Impose security constraints only if tainting */
538 secure = PL_curinterp ? PL_tainting : will_taint;
539 saverr = errno; savvmserr = vaxc$errno;
546 #ifdef SECURE_INTERNAL_GETENV
547 secure ? PERL__TRNENV_SECURE : 0
553 /* For the getenv interface we combine all the equivalence names
554 * of a search list logical into one value to acquire a maximum
555 * value length of 255*128 (assuming %ENV is using logicals).
557 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
559 /* If the name contains a semicolon-delimited index, parse it
560 * off and make sure we only retrieve the equivalence name for
562 if ((cp2 = strchr(lnm,';')) != NULL) {
564 uplnm[cp2-lnm] = '\0';
565 idx = strtoul(cp2+1,NULL,0);
567 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
570 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
572 /* Discard NOLOGNAM on internal calls since we're often looking
573 * for an optional name, and this "error" often shows up as the
574 * (bogus) exit status for a die() call later on. */
575 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
576 return success ? eqv : Nullch;
579 } /* end of my_getenv() */
583 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
585 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
589 unsigned long idx = 0;
591 static char *__my_getenv_len_eqv = NULL;
592 int secure, saverr, savvmserr;
595 midx = my_maxidx(lnm) + 1;
597 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
598 /* Set up a temporary buffer for the return value; Perl will
599 * clean it up at the next statement transition */
600 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
601 if (!tmpsv) return NULL;
605 /* Assume no interpreter ==> single thread */
606 if (__my_getenv_len_eqv != NULL) {
607 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
610 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
612 buf = __my_getenv_len_eqv;
615 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
616 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
619 getcwd(buf,LNM$C_NAMLENGTH);
622 /* Get rid of "000000/ in rooted filespecs */
624 zeros = strstr(buf, "/000000/");
627 mlen = *len - (zeros - buf) - 7;
628 memmove(zeros, &zeros[7], mlen);
637 /* Impose security constraints only if tainting */
638 secure = PL_curinterp ? PL_tainting : will_taint;
639 saverr = errno; savvmserr = vaxc$errno;
646 #ifdef SECURE_INTERNAL_GETENV
647 secure ? PERL__TRNENV_SECURE : 0
653 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
655 if ((cp2 = strchr(lnm,';')) != NULL) {
658 idx = strtoul(cp2+1,NULL,0);
660 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
663 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
665 /* Get rid of "000000/ in rooted filespecs */
668 zeros = strstr(buf, "/000000/");
671 mlen = *len - (zeros - buf) - 7;
672 memmove(zeros, &zeros[7], mlen);
678 /* Discard NOLOGNAM on internal calls since we're often looking
679 * for an optional name, and this "error" often shows up as the
680 * (bogus) exit status for a die() call later on. */
681 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
682 return *len ? buf : Nullch;
685 } /* end of my_getenv_len() */
688 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
690 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
692 /*{{{ void prime_env_iter() */
695 /* Fill the %ENV associative array with all logical names we can
696 * find, in preparation for iterating over it.
699 static int primed = 0;
700 HV *seenhv = NULL, *envhv;
702 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
703 unsigned short int chan;
704 #ifndef CLI$M_TRUSTED
705 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
707 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
708 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
710 bool have_sym = FALSE, have_lnm = FALSE;
711 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
712 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
713 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
714 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
715 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
716 #if defined(PERL_IMPLICIT_CONTEXT)
719 #if defined(USE_ITHREADS)
720 static perl_mutex primenv_mutex;
721 MUTEX_INIT(&primenv_mutex);
724 #if defined(PERL_IMPLICIT_CONTEXT)
725 /* We jump through these hoops because we can be called at */
726 /* platform-specific initialization time, which is before anything is */
727 /* set up--we can't even do a plain dTHX since that relies on the */
728 /* interpreter structure to be initialized */
730 aTHX = PERL_GET_INTERP;
736 if (primed || !PL_envgv) return;
737 MUTEX_LOCK(&primenv_mutex);
738 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
739 envhv = GvHVn(PL_envgv);
740 /* Perform a dummy fetch as an lval to insure that the hash table is
741 * set up. Otherwise, the hv_store() will turn into a nullop. */
742 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
744 for (i = 0; env_tables[i]; i++) {
745 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
746 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
747 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
749 if (have_sym || have_lnm) {
750 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
751 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
752 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
753 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
756 for (i--; i >= 0; i--) {
757 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
760 for (j = 0; environ[j]; j++) {
761 if (!(start = strchr(environ[j],'='))) {
762 if (ckWARN(WARN_INTERNAL))
763 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
767 sv = newSVpv(start,0);
769 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
774 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
775 !str$case_blind_compare(&tmpdsc,&clisym)) {
776 strcpy(cmd,"Show Symbol/Global *");
777 cmddsc.dsc$w_length = 20;
778 if (env_tables[i]->dsc$w_length == 12 &&
779 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
780 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
781 flags = defflags | CLI$M_NOLOGNAM;
784 strcpy(cmd,"Show Logical *");
785 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
786 strcat(cmd," /Table=");
787 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
788 cmddsc.dsc$w_length = strlen(cmd);
790 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
791 flags = defflags | CLI$M_NOCLISYM;
794 /* Create a new subprocess to execute each command, to exclude the
795 * remote possibility that someone could subvert a mbx or file used
796 * to write multiple commands to a single subprocess.
799 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
800 0,&riseandshine,0,0,&clidsc,&clitabdsc);
801 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
802 defflags &= ~CLI$M_TRUSTED;
803 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
805 if (!buf) Newx(buf,mbxbufsiz + 1,char);
806 if (seenhv) SvREFCNT_dec(seenhv);
809 char *cp1, *cp2, *key;
810 unsigned long int sts, iosb[2], retlen, keylen;
813 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
814 if (sts & 1) sts = iosb[0] & 0xffff;
815 if (sts == SS$_ENDOFFILE) {
817 while (substs == 0) { sys$hiber(); wakect++;}
818 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
823 retlen = iosb[0] >> 16;
824 if (!retlen) continue; /* blank line */
826 if (iosb[1] != subpid) {
828 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
832 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
833 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
835 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
836 if (*cp1 == '(' || /* Logical name table name */
837 *cp1 == '=' /* Next eqv of searchlist */) continue;
838 if (*cp1 == '"') cp1++;
839 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
840 key = cp1; keylen = cp2 - cp1;
841 if (keylen && hv_exists(seenhv,key,keylen)) continue;
842 while (*cp2 && *cp2 != '=') cp2++;
843 while (*cp2 && *cp2 == '=') cp2++;
844 while (*cp2 && *cp2 == ' ') cp2++;
845 if (*cp2 == '"') { /* String translation; may embed "" */
846 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
847 cp2++; cp1--; /* Skip "" surrounding translation */
849 else { /* Numeric translation */
850 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
851 cp1--; /* stop on last non-space char */
853 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
854 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
857 PERL_HASH(hash,key,keylen);
859 if (cp1 == cp2 && *cp2 == '.') {
860 /* A single dot usually means an unprintable character, such as a null
861 * to indicate a zero-length value. Get the actual value to make sure.
863 char lnm[LNM$C_NAMLENGTH+1];
864 char eqv[MAX_DCL_SYMBOL+1];
865 strncpy(lnm, key, keylen);
866 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
867 sv = newSVpvn(eqv, strlen(eqv));
870 sv = newSVpvn(cp2,cp1 - cp2 + 1);
874 hv_store(envhv,key,keylen,sv,hash);
875 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
877 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
878 /* get the PPFs for this process, not the subprocess */
879 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
880 char eqv[LNM$C_NAMLENGTH+1];
882 for (i = 0; ppfs[i]; i++) {
883 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
884 sv = newSVpv(eqv,trnlen);
886 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
891 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
892 if (buf) Safefree(buf);
893 if (seenhv) SvREFCNT_dec(seenhv);
894 MUTEX_UNLOCK(&primenv_mutex);
897 } /* end of prime_env_iter */
901 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
902 /* Define or delete an element in the same "environment" as
903 * vmstrnenv(). If an element is to be deleted, it's removed from
904 * the first place it's found. If it's to be set, it's set in the
905 * place designated by the first element of the table vector.
906 * Like setenv() returns 0 for success, non-zero on error.
909 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
912 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
913 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
915 unsigned long int retsts, usermode = PSL$C_USER;
916 struct itmlst_3 *ile, *ilist;
917 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
918 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
919 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
920 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
921 $DESCRIPTOR(local,"_LOCAL");
924 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
928 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
929 *cp2 = _toupper(*cp1);
930 if (cp1 - lnm > LNM$C_NAMLENGTH) {
931 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
935 lnmdsc.dsc$w_length = cp1 - lnm;
936 if (!tabvec || !*tabvec) tabvec = env_tables;
938 if (!eqv) { /* we're deleting n element */
939 for (curtab = 0; tabvec[curtab]; curtab++) {
940 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
942 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
943 if ((cp1 = strchr(environ[i],'=')) &&
944 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
945 !strncmp(environ[i],lnm,cp1 - environ[i])) {
947 return setenv(lnm,"",1) ? vaxc$errno : 0;
950 ivenv = 1; retsts = SS$_NOLOGNAM;
952 if (ckWARN(WARN_INTERNAL))
953 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
954 ivenv = 1; retsts = SS$_NOSUCHPGM;
960 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
961 !str$case_blind_compare(&tmpdsc,&clisym)) {
962 unsigned int symtype;
963 if (tabvec[curtab]->dsc$w_length == 12 &&
964 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
965 !str$case_blind_compare(&tmpdsc,&local))
966 symtype = LIB$K_CLI_LOCAL_SYM;
967 else symtype = LIB$K_CLI_GLOBAL_SYM;
968 retsts = lib$delete_symbol(&lnmdsc,&symtype);
969 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
970 if (retsts == LIB$_NOSUCHSYM) continue;
974 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
975 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
976 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
977 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
978 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
982 else { /* we're defining a value */
983 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
985 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
987 if (ckWARN(WARN_INTERNAL))
988 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
989 retsts = SS$_NOSUCHPGM;
993 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
994 eqvdsc.dsc$w_length = strlen(eqv);
995 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
996 !str$case_blind_compare(&tmpdsc,&clisym)) {
997 unsigned int symtype;
998 if (tabvec[0]->dsc$w_length == 12 &&
999 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1000 !str$case_blind_compare(&tmpdsc,&local))
1001 symtype = LIB$K_CLI_LOCAL_SYM;
1002 else symtype = LIB$K_CLI_GLOBAL_SYM;
1003 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1006 if (!*eqv) eqvdsc.dsc$w_length = 1;
1007 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1009 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1010 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1011 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1012 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1013 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1014 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1017 Newx(ilist,nseg+1,struct itmlst_3);
1020 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1023 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1025 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1026 ile->itmcode = LNM$_STRING;
1028 if ((j+1) == nseg) {
1029 ile->buflen = strlen(c);
1030 /* in case we are truncating one that's too long */
1031 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1034 ile->buflen = LNM$C_NAMLENGTH;
1038 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1042 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1047 if (!(retsts & 1)) {
1049 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1050 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1051 set_errno(EVMSERR); break;
1052 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1053 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1054 set_errno(EINVAL); break;
1061 set_vaxc_errno(retsts);
1062 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1065 /* We reset error values on success because Perl does an hv_fetch()
1066 * before each hv_store(), and if the thing we're setting didn't
1067 * previously exist, we've got a leftover error message. (Of course,
1068 * this fails in the face of
1069 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1070 * in that the error reported in $! isn't spurious,
1071 * but it's right more often than not.)
1073 set_errno(0); set_vaxc_errno(retsts);
1077 } /* end of vmssetenv() */
1080 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1081 /* This has to be a function since there's a prototype for it in proto.h */
1083 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1086 int len = strlen(lnm);
1090 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1091 if (!strcmp(uplnm,"DEFAULT")) {
1092 if (eqv && *eqv) my_chdir(eqv);
1096 #ifndef RTL_USES_UTC
1097 if (len == 6 || len == 2) {
1100 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1102 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1103 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1107 (void) vmssetenv(lnm,eqv,NULL);
1111 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1113 * sets a user-mode logical in the process logical name table
1114 * used for redirection of sys$error
1117 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1119 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1120 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1121 unsigned long int iss, attr = LNM$M_CONFINE;
1122 unsigned char acmode = PSL$C_USER;
1123 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1125 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1126 d_name.dsc$w_length = strlen(name);
1128 lnmlst[0].buflen = strlen(eqv);
1129 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1131 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1132 if (!(iss&1)) lib$signal(iss);
1137 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1138 /* my_crypt - VMS password hashing
1139 * my_crypt() provides an interface compatible with the Unix crypt()
1140 * C library function, and uses sys$hash_password() to perform VMS
1141 * password hashing. The quadword hashed password value is returned
1142 * as a NUL-terminated 8 character string. my_crypt() does not change
1143 * the case of its string arguments; in order to match the behavior
1144 * of LOGINOUT et al., alphabetic characters in both arguments must
1145 * be upcased by the caller.
1147 * - fix me to call ACM services when available
1150 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1152 # ifndef UAI$C_PREFERRED_ALGORITHM
1153 # define UAI$C_PREFERRED_ALGORITHM 127
1155 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1156 unsigned short int salt = 0;
1157 unsigned long int sts;
1159 unsigned short int dsc$w_length;
1160 unsigned char dsc$b_type;
1161 unsigned char dsc$b_class;
1162 const char * dsc$a_pointer;
1163 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1164 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1165 struct itmlst_3 uailst[3] = {
1166 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1167 { sizeof salt, UAI$_SALT, &salt, 0},
1168 { 0, 0, NULL, NULL}};
1169 static char hash[9];
1171 usrdsc.dsc$w_length = strlen(usrname);
1172 usrdsc.dsc$a_pointer = usrname;
1173 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1175 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1179 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1184 set_vaxc_errno(sts);
1185 if (sts != RMS$_RNF) return NULL;
1188 txtdsc.dsc$w_length = strlen(textpasswd);
1189 txtdsc.dsc$a_pointer = textpasswd;
1190 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1191 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1194 return (char *) hash;
1196 } /* end of my_crypt() */
1200 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1201 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1202 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1204 /* fixup barenames that are directories for internal use.
1205 * There have been problems with the consistent handling of UNIX
1206 * style directory names when routines are presented with a name that
1207 * has no directory delimitors at all. So this routine will eventually
1210 static char * fixup_bare_dirnames(const char * name)
1212 if (decc_disable_to_vms_logname_translation) {
1219 * A little hack to get around a bug in some implemenation of remove()
1220 * that do not know how to delete a directory
1222 * Delete any file to which user has control access, regardless of whether
1223 * delete access is explicitly allowed.
1224 * Limitations: User must have write access to parent directory.
1225 * Does not block signals or ASTs; if interrupted in midstream
1226 * may leave file with an altered ACL.
1229 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1231 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1233 char *vmsname, *rspec;
1235 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1236 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1237 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1239 unsigned char myace$b_length;
1240 unsigned char myace$b_type;
1241 unsigned short int myace$w_flags;
1242 unsigned long int myace$l_access;
1243 unsigned long int myace$l_ident;
1244 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1245 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1246 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1248 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1249 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1250 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1251 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1252 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1253 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1255 /* Expand the input spec using RMS, since the CRTL remove() and
1256 * system services won't do this by themselves, so we may miss
1257 * a file "hiding" behind a logical name or search list. */
1258 Newx(vmsname, NAM$C_MAXRSS+1, char);
1259 if (do_tovmsspec(name,vmsname,0) == NULL) {
1264 if (decc_posix_compliant_pathnames) {
1265 /* In POSIX mode, we prefer to remove the UNIX name */
1267 remove_name = (char *)name;
1270 Newx(rspec, NAM$C_MAXRSS+1, char);
1271 if (do_rmsexpand(vmsname, rspec, 1, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1277 remove_name = rspec;
1280 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1282 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1283 Newx(remove_name, NAM$C_MAXRSS+1, char);
1284 do_pathify_dirspec(name, remove_name, 0);
1285 if (!rmdir(remove_name)) {
1287 Safefree(remove_name);
1289 return 0; /* Can we just get rid of it? */
1293 if (!rmdir(remove_name)) {
1295 return 0; /* Can we just get rid of it? */
1301 if (!remove(remove_name)) {
1303 return 0; /* Can we just get rid of it? */
1306 /* If not, can changing protections help? */
1307 if (vaxc$errno != RMS$_PRV) {
1312 /* No, so we get our own UIC to use as a rights identifier,
1313 * and the insert an ACE at the head of the ACL which allows us
1314 * to delete the file.
1316 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1317 fildsc.dsc$w_length = strlen(rspec);
1318 fildsc.dsc$a_pointer = rspec;
1320 newace.myace$l_ident = oldace.myace$l_ident;
1321 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1323 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1324 set_errno(ENOENT); break;
1326 set_errno(ENOTDIR); break;
1328 set_errno(ENODEV); break;
1329 case RMS$_SYN: case SS$_INVFILFOROP:
1330 set_errno(EINVAL); break;
1332 set_errno(EACCES); break;
1336 set_vaxc_errno(aclsts);
1340 /* Grab any existing ACEs with this identifier in case we fail */
1341 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1342 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1343 || fndsts == SS$_NOMOREACE ) {
1344 /* Add the new ACE . . . */
1345 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1348 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1350 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1351 Newx(remove_name, NAM$C_MAXRSS+1, char);
1352 do_pathify_dirspec(name, remove_name, 0);
1353 rmsts = rmdir(remove_name);
1354 Safefree(remove_name);
1357 rmsts = rmdir(remove_name);
1361 rmsts = remove(remove_name);
1363 /* We blew it - dir with files in it, no write priv for
1364 * parent directory, etc. Put things back the way they were. */
1365 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1368 addlst[0].bufadr = &oldace;
1369 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1376 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1377 /* We just deleted it, so of course it's not there. Some versions of
1378 * VMS seem to return success on the unlock operation anyhow (after all
1379 * the unlock is successful), but others don't.
1381 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1382 if (aclsts & 1) aclsts = fndsts;
1383 if (!(aclsts & 1)) {
1385 set_vaxc_errno(aclsts);
1393 } /* end of kill_file() */
1397 /*{{{int do_rmdir(char *name)*/
1399 Perl_do_rmdir(pTHX_ const char *name)
1401 char dirfile[NAM$C_MAXRSS+1];
1405 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1406 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1407 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1410 } /* end of do_rmdir */
1414 * Delete any file to which user has control access, regardless of whether
1415 * delete access is explicitly allowed.
1416 * Limitations: User must have write access to parent directory.
1417 * Does not block signals or ASTs; if interrupted in midstream
1418 * may leave file with an altered ACL.
1421 /*{{{int kill_file(char *name)*/
1423 Perl_kill_file(pTHX_ const char *name)
1425 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
1426 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1427 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1428 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1430 unsigned char myace$b_length;
1431 unsigned char myace$b_type;
1432 unsigned short int myace$w_flags;
1433 unsigned long int myace$l_access;
1434 unsigned long int myace$l_ident;
1435 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1436 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1437 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1439 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1440 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1441 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1442 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1443 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1444 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1446 /* Expand the input spec using RMS, since the CRTL remove() and
1447 * system services won't do this by themselves, so we may miss
1448 * a file "hiding" behind a logical name or search list. */
1449 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
1450 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
1451 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1452 /* If not, can changing protections help? */
1453 if (vaxc$errno != RMS$_PRV) return -1;
1455 /* No, so we get our own UIC to use as a rights identifier,
1456 * and the insert an ACE at the head of the ACL which allows us
1457 * to delete the file.
1459 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1460 fildsc.dsc$w_length = strlen(rspec);
1461 fildsc.dsc$a_pointer = rspec;
1463 newace.myace$l_ident = oldace.myace$l_ident;
1464 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1466 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1467 set_errno(ENOENT); break;
1469 set_errno(ENOTDIR); break;
1471 set_errno(ENODEV); break;
1472 case RMS$_SYN: case SS$_INVFILFOROP:
1473 set_errno(EINVAL); break;
1475 set_errno(EACCES); break;
1479 set_vaxc_errno(aclsts);
1482 /* Grab any existing ACEs with this identifier in case we fail */
1483 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1484 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1485 || fndsts == SS$_NOMOREACE ) {
1486 /* Add the new ACE . . . */
1487 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1489 if ((rmsts = remove(name))) {
1490 /* We blew it - dir with files in it, no write priv for
1491 * parent directory, etc. Put things back the way they were. */
1492 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1495 addlst[0].bufadr = &oldace;
1496 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1503 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1504 /* We just deleted it, so of course it's not there. Some versions of
1505 * VMS seem to return success on the unlock operation anyhow (after all
1506 * the unlock is successful), but others don't.
1508 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1509 if (aclsts & 1) aclsts = fndsts;
1510 if (!(aclsts & 1)) {
1512 set_vaxc_errno(aclsts);
1518 } /* end of kill_file() */
1522 /*{{{int my_mkdir(char *,Mode_t)*/
1524 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1526 STRLEN dirlen = strlen(dir);
1528 /* zero length string sometimes gives ACCVIO */
1529 if (dirlen == 0) return -1;
1531 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1532 * null file name/type. However, it's commonplace under Unix,
1533 * so we'll allow it for a gain in portability.
1535 if (dir[dirlen-1] == '/') {
1536 char *newdir = savepvn(dir,dirlen-1);
1537 int ret = mkdir(newdir,mode);
1541 else return mkdir(dir,mode);
1542 } /* end of my_mkdir */
1545 /*{{{int my_chdir(char *)*/
1547 Perl_my_chdir(pTHX_ const char *dir)
1549 STRLEN dirlen = strlen(dir);
1551 /* zero length string sometimes gives ACCVIO */
1552 if (dirlen == 0) return -1;
1555 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
1556 * This does not work if DECC$EFS_CHARSET is active. Hack it here
1557 * so that existing scripts do not need to be changed.
1560 while ((dirlen > 0) && (*dir1 == ' ')) {
1565 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1567 * null file name/type. However, it's commonplace under Unix,
1568 * so we'll allow it for a gain in portability.
1570 * - Preview- '/' will be valid soon on VMS
1572 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
1573 char *newdir = savepvn(dir,dirlen-1);
1574 int ret = chdir(newdir);
1578 else return chdir(dir);
1579 } /* end of my_chdir */
1583 /*{{{FILE *my_tmpfile()*/
1590 if ((fp = tmpfile())) return fp;
1592 Newx(cp,L_tmpnam+24,char);
1593 if (decc_filename_unix_only == 0)
1594 strcpy(cp,"Sys$Scratch:");
1597 tmpnam(cp+strlen(cp));
1598 strcat(cp,".Perltmp");
1599 fp = fopen(cp,"w+","fop=dlt");
1606 #ifndef HOMEGROWN_POSIX_SIGNALS
1608 * The C RTL's sigaction fails to check for invalid signal numbers so we
1609 * help it out a bit. The docs are correct, but the actual routine doesn't
1610 * do what the docs say it will.
1612 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1614 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1615 struct sigaction* oact)
1617 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1618 SETERRNO(EINVAL, SS$_INVARG);
1621 return sigaction(sig, act, oact);
1626 #ifdef KILL_BY_SIGPRC
1627 #include <errnodef.h>
1629 /* We implement our own kill() using the undocumented system service
1630 sys$sigprc for one of two reasons:
1632 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1633 target process to do a sys$exit, which usually can't be handled
1634 gracefully...certainly not by Perl and the %SIG{} mechanism.
1636 2.) If the kill() in the CRTL can't be called from a signal
1637 handler without disappearing into the ether, i.e., the signal
1638 it purportedly sends is never trapped. Still true as of VMS 7.3.
1640 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1641 in the target process rather than calling sys$exit.
1643 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1644 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1645 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1646 with condition codes C$_SIG0+nsig*8, catching the exception on the
1647 target process and resignaling with appropriate arguments.
1649 But we don't have that VMS 7.0+ exception handler, so if you
1650 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1652 Also note that SIGTERM is listed in the docs as being "unimplemented",
1653 yet always seems to be signaled with a VMS condition code of 4 (and
1654 correctly handled for that code). So we hardwire it in.
1656 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1657 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1658 than signalling with an unrecognized (and unhandled by CRTL) code.
1661 #define _MY_SIG_MAX 17
1664 Perl_sig_to_vmscondition(int sig)
1666 static unsigned int sig_code[_MY_SIG_MAX+1] =
1669 SS$_HANGUP, /* 1 SIGHUP */
1670 SS$_CONTROLC, /* 2 SIGINT */
1671 SS$_CONTROLY, /* 3 SIGQUIT */
1672 SS$_RADRMOD, /* 4 SIGILL */
1673 SS$_BREAK, /* 5 SIGTRAP */
1674 SS$_OPCCUS, /* 6 SIGABRT */
1675 SS$_COMPAT, /* 7 SIGEMT */
1677 SS$_FLTOVF, /* 8 SIGFPE VAX */
1679 SS$_HPARITH, /* 8 SIGFPE AXP */
1681 SS$_ABORT, /* 9 SIGKILL */
1682 SS$_ACCVIO, /* 10 SIGBUS */
1683 SS$_ACCVIO, /* 11 SIGSEGV */
1684 SS$_BADPARAM, /* 12 SIGSYS */
1685 SS$_NOMBX, /* 13 SIGPIPE */
1686 SS$_ASTFLT, /* 14 SIGALRM */
1692 #if __VMS_VER >= 60200000
1693 static int initted = 0;
1696 sig_code[16] = C$_SIGUSR1;
1697 sig_code[17] = C$_SIGUSR2;
1701 if (sig < _SIG_MIN) return 0;
1702 if (sig > _MY_SIG_MAX) return 0;
1703 return sig_code[sig];
1707 Perl_my_kill(int pid, int sig)
1712 int sys$sigprc(unsigned int *pidadr,
1713 struct dsc$descriptor_s *prcname,
1716 /* sig 0 means validate the PID */
1717 /*------------------------------*/
1719 const unsigned long int jpicode = JPI$_PID;
1722 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
1723 if ($VMS_STATUS_SUCCESS(status))
1726 case SS$_NOSUCHNODE:
1727 case SS$_UNREACHABLE:
1741 code = Perl_sig_to_vmscondition(sig);
1744 SETERRNO(EINVAL, SS$_BADPARAM);
1748 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
1749 * signals are to be sent to multiple processes.
1750 * pid = 0 - all processes in group except ones that the system exempts
1751 * pid = -1 - all processes except ones that the system exempts
1752 * pid = -n - all processes in group (abs(n)) except ...
1753 * For now, just report as not supported.
1757 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
1761 iss = sys$sigprc((unsigned int *)&pid,0,code);
1762 if (iss&1) return 0;
1766 set_errno(EPERM); break;
1768 case SS$_NOSUCHNODE:
1769 case SS$_UNREACHABLE:
1770 set_errno(ESRCH); break;
1772 set_errno(ENOMEM); break;
1777 set_vaxc_errno(iss);
1783 /* Routine to convert a VMS status code to a UNIX status code.
1784 ** More tricky than it appears because of conflicting conventions with
1787 ** VMS status codes are a bit mask, with the least significant bit set for
1790 ** Special UNIX status of EVMSERR indicates that no translation is currently
1791 ** available, and programs should check the VMS status code.
1793 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
1797 #ifndef C_FACILITY_NO
1798 #define C_FACILITY_NO 0x350000
1801 #define DCL_IVVERB 0x38090
1804 int Perl_vms_status_to_unix(int vms_status, int child_flag)
1812 /* Assume the best or the worst */
1813 if (vms_status & STS$M_SUCCESS)
1816 unix_status = EVMSERR;
1818 msg_status = vms_status & ~STS$M_CONTROL;
1820 facility = vms_status & STS$M_FAC_NO;
1821 fac_sp = vms_status & STS$M_FAC_SP;
1822 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
1824 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
1830 unix_status = EFAULT;
1832 case SS$_DEVOFFLINE:
1833 unix_status = EBUSY;
1836 unix_status = ENOTCONN;
1844 case SS$_INVFILFOROP:
1848 unix_status = EINVAL;
1850 case SS$_UNSUPPORTED:
1851 unix_status = ENOTSUP;
1856 unix_status = EACCES;
1858 case SS$_DEVICEFULL:
1859 unix_status = ENOSPC;
1862 unix_status = ENODEV;
1864 case SS$_NOSUCHFILE:
1865 case SS$_NOSUCHOBJECT:
1866 unix_status = ENOENT;
1868 case SS$_ABORT: /* Fatal case */
1869 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
1870 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
1871 unix_status = EINTR;
1874 unix_status = E2BIG;
1877 unix_status = ENOMEM;
1880 unix_status = EPERM;
1882 case SS$_NOSUCHNODE:
1883 case SS$_UNREACHABLE:
1884 unix_status = ESRCH;
1887 unix_status = ECHILD;
1890 if ((facility == 0) && (msg_no < 8)) {
1891 /* These are not real VMS status codes so assume that they are
1892 ** already UNIX status codes
1894 unix_status = msg_no;
1900 /* Translate a POSIX exit code to a UNIX exit code */
1901 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
1902 unix_status = (msg_no & 0x07F8) >> 3;
1906 /* Documented traditional behavior for handling VMS child exits */
1907 /*--------------------------------------------------------------*/
1908 if (child_flag != 0) {
1910 /* Success / Informational return 0 */
1911 /*----------------------------------*/
1912 if (msg_no & STS$K_SUCCESS)
1915 /* Warning returns 1 */
1916 /*-------------------*/
1917 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
1920 /* Everything else pass through the severity bits */
1921 /*------------------------------------------------*/
1922 return (msg_no & STS$M_SEVERITY);
1925 /* Normal VMS status to ERRNO mapping attempt */
1926 /*--------------------------------------------*/
1927 switch(msg_status) {
1928 /* case RMS$_EOF: */ /* End of File */
1929 case RMS$_FNF: /* File Not Found */
1930 case RMS$_DNF: /* Dir Not Found */
1931 unix_status = ENOENT;
1933 case RMS$_RNF: /* Record Not Found */
1934 unix_status = ESRCH;
1937 unix_status = ENOTDIR;
1940 unix_status = ENODEV;
1945 unix_status = EBADF;
1948 unix_status = EEXIST;
1952 case LIB$_INVSTRDES:
1954 case LIB$_NOSUCHSYM:
1955 case LIB$_INVSYMNAM:
1957 unix_status = EINVAL;
1963 unix_status = E2BIG;
1965 case RMS$_PRV: /* No privilege */
1966 case RMS$_ACC: /* ACP file access failed */
1967 case RMS$_WLK: /* Device write locked */
1968 unix_status = EACCES;
1970 /* case RMS$_NMF: */ /* No more files */
1978 /* Try to guess at what VMS error status should go with a UNIX errno
1979 * value. This is hard to do as there could be many possible VMS
1980 * error statuses that caused the errno value to be set.
1983 int Perl_unix_status_to_vms(int unix_status)
1985 int test_unix_status;
1987 /* Trivial cases first */
1988 /*---------------------*/
1989 if (unix_status == EVMSERR)
1992 /* Is vaxc$errno sane? */
1993 /*---------------------*/
1994 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
1995 if (test_unix_status == unix_status)
1998 /* If way out of range, must be VMS code already */
1999 /*-----------------------------------------------*/
2000 if (unix_status > EVMSERR)
2003 /* If out of range, punt */
2004 /*-----------------------*/
2005 if (unix_status > __ERRNO_MAX)
2009 /* Ok, now we have to do it the hard way. */
2010 /*----------------------------------------*/
2011 switch(unix_status) {
2012 case 0: return SS$_NORMAL;
2013 case EPERM: return SS$_NOPRIV;
2014 case ENOENT: return SS$_NOSUCHOBJECT;
2015 case ESRCH: return SS$_UNREACHABLE;
2016 case EINTR: return SS$_ABORT;
2019 case E2BIG: return SS$_BUFFEROVF;
2021 case EBADF: return RMS$_IFI;
2022 case ECHILD: return SS$_NONEXPR;
2024 case ENOMEM: return SS$_INSFMEM;
2025 case EACCES: return SS$_FILACCERR;
2026 case EFAULT: return SS$_ACCVIO;
2028 case EBUSY: return SS$_DEVOFFLINE;
2029 case EEXIST: return RMS$_FEX;
2031 case ENODEV: return SS$_NOSUCHDEV;
2032 case ENOTDIR: return RMS$_DIR;
2034 case EINVAL: return SS$_INVARG;
2040 case ENOSPC: return SS$_DEVICEFULL;
2041 case ESPIPE: return LIB$_INVARG;
2046 case ERANGE: return LIB$_INVARG;
2047 /* case EWOULDBLOCK */
2048 /* case EINPROGRESS */
2051 /* case EDESTADDRREQ */
2053 /* case EPROTOTYPE */
2054 /* case ENOPROTOOPT */
2055 /* case EPROTONOSUPPORT */
2056 /* case ESOCKTNOSUPPORT */
2057 /* case EOPNOTSUPP */
2058 /* case EPFNOSUPPORT */
2059 /* case EAFNOSUPPORT */
2060 /* case EADDRINUSE */
2061 /* case EADDRNOTAVAIL */
2063 /* case ENETUNREACH */
2064 /* case ENETRESET */
2065 /* case ECONNABORTED */
2066 /* case ECONNRESET */
2069 case ENOTCONN: return SS$_CLEARED;
2070 /* case ESHUTDOWN */
2071 /* case ETOOMANYREFS */
2072 /* case ETIMEDOUT */
2073 /* case ECONNREFUSED */
2075 /* case ENAMETOOLONG */
2076 /* case EHOSTDOWN */
2077 /* case EHOSTUNREACH */
2078 /* case ENOTEMPTY */
2090 /* case ECANCELED */
2094 return SS$_UNSUPPORTED;
2100 /* case EABANDONED */
2102 return SS$_ABORT; /* punt */
2105 return SS$_ABORT; /* Should not get here */
2109 /* default piping mailbox size */
2110 #define PERL_BUFSIZ 512
2114 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2116 unsigned long int mbxbufsiz;
2117 static unsigned long int syssize = 0;
2118 unsigned long int dviitm = DVI$_DEVNAM;
2119 char csize[LNM$C_NAMLENGTH+1];
2123 unsigned long syiitm = SYI$_MAXBUF;
2125 * Get the SYSGEN parameter MAXBUF
2127 * If the logical 'PERL_MBX_SIZE' is defined
2128 * use the value of the logical instead of PERL_BUFSIZ, but
2129 * keep the size between 128 and MAXBUF.
2132 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2135 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2136 mbxbufsiz = atoi(csize);
2138 mbxbufsiz = PERL_BUFSIZ;
2140 if (mbxbufsiz < 128) mbxbufsiz = 128;
2141 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2143 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2145 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2146 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2148 } /* end of create_mbx() */
2151 /*{{{ my_popen and my_pclose*/
2153 typedef struct _iosb IOSB;
2154 typedef struct _iosb* pIOSB;
2155 typedef struct _pipe Pipe;
2156 typedef struct _pipe* pPipe;
2157 typedef struct pipe_details Info;
2158 typedef struct pipe_details* pInfo;
2159 typedef struct _srqp RQE;
2160 typedef struct _srqp* pRQE;
2161 typedef struct _tochildbuf CBuf;
2162 typedef struct _tochildbuf* pCBuf;
2165 unsigned short status;
2166 unsigned short count;
2167 unsigned long dvispec;
2170 #pragma member_alignment save
2171 #pragma nomember_alignment quadword
2172 struct _srqp { /* VMS self-relative queue entry */
2173 unsigned long qptr[2];
2175 #pragma member_alignment restore
2176 static RQE RQE_ZERO = {0,0};
2178 struct _tochildbuf {
2181 unsigned short size;
2189 unsigned short chan_in;
2190 unsigned short chan_out;
2192 unsigned int bufsize;
2204 #if defined(PERL_IMPLICIT_CONTEXT)
2205 void *thx; /* Either a thread or an interpreter */
2206 /* pointer, depending on how we're built */
2214 PerlIO *fp; /* file pointer to pipe mailbox */
2215 int useFILE; /* using stdio, not perlio */
2216 int pid; /* PID of subprocess */
2217 int mode; /* == 'r' if pipe open for reading */
2218 int done; /* subprocess has completed */
2219 int waiting; /* waiting for completion/closure */
2220 int closing; /* my_pclose is closing this pipe */
2221 unsigned long completion; /* termination status of subprocess */
2222 pPipe in; /* pipe in to sub */
2223 pPipe out; /* pipe out of sub */
2224 pPipe err; /* pipe of sub's sys$error */
2225 int in_done; /* true when in pipe finished */
2230 struct exit_control_block
2232 struct exit_control_block *flink;
2233 unsigned long int (*exit_routine)();
2234 unsigned long int arg_count;
2235 unsigned long int *status_address;
2236 unsigned long int exit_status;
2239 typedef struct _closed_pipes Xpipe;
2240 typedef struct _closed_pipes* pXpipe;
2242 struct _closed_pipes {
2243 int pid; /* PID of subprocess */
2244 unsigned long completion; /* termination status of subprocess */
2246 #define NKEEPCLOSED 50
2247 static Xpipe closed_list[NKEEPCLOSED];
2248 static int closed_index = 0;
2249 static int closed_num = 0;
2251 #define RETRY_DELAY "0 ::0.20"
2252 #define MAX_RETRY 50
2254 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2255 static unsigned long mypid;
2256 static unsigned long delaytime[2];
2258 static pInfo open_pipes = NULL;
2259 static $DESCRIPTOR(nl_desc, "NL:");
2261 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2265 static unsigned long int
2266 pipe_exit_routine(pTHX)
2269 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2270 int sts, did_stuff, need_eof, j;
2273 flush any pending i/o
2279 PerlIO_flush(info->fp); /* first, flush data */
2281 fflush((FILE *)info->fp);
2287 next we try sending an EOF...ignore if doesn't work, make sure we
2295 _ckvmssts(sys$setast(0));
2296 if (info->in && !info->in->shut_on_empty) {
2297 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2302 _ckvmssts(sys$setast(1));
2306 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2308 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2313 _ckvmssts(sys$setast(0));
2314 if (info->waiting && info->done)
2316 nwait += info->waiting;
2317 _ckvmssts(sys$setast(1));
2327 _ckvmssts(sys$setast(0));
2328 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2329 sts = sys$forcex(&info->pid,0,&abort);
2330 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
2333 _ckvmssts(sys$setast(1));
2337 /* again, wait for effect */
2339 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2344 _ckvmssts(sys$setast(0));
2345 if (info->waiting && info->done)
2347 nwait += info->waiting;
2348 _ckvmssts(sys$setast(1));
2357 _ckvmssts(sys$setast(0));
2358 if (!info->done) { /* We tried to be nice . . . */
2359 sts = sys$delprc(&info->pid,0);
2360 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
2362 _ckvmssts(sys$setast(1));
2367 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2368 else if (!(sts & 1)) retsts = sts;
2373 static struct exit_control_block pipe_exitblock =
2374 {(struct exit_control_block *) 0,
2375 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2377 static void pipe_mbxtofd_ast(pPipe p);
2378 static void pipe_tochild1_ast(pPipe p);
2379 static void pipe_tochild2_ast(pPipe p);
2382 popen_completion_ast(pInfo info)
2384 pInfo i = open_pipes;
2389 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2390 closed_list[closed_index].pid = info->pid;
2391 closed_list[closed_index].completion = info->completion;
2393 if (closed_index == NKEEPCLOSED)
2398 if (i == info) break;
2401 if (!i) return; /* unlinked, probably freed too */
2406 Writing to subprocess ...
2407 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2409 chan_out may be waiting for "done" flag, or hung waiting
2410 for i/o completion to child...cancel the i/o. This will
2411 put it into "snarf mode" (done but no EOF yet) that discards
2414 Output from subprocess (stdout, stderr) needs to be flushed and
2415 shut down. We try sending an EOF, but if the mbx is full the pipe
2416 routine should still catch the "shut_on_empty" flag, telling it to
2417 use immediate-style reads so that "mbx empty" -> EOF.
2421 if (info->in && !info->in_done) { /* only for mode=w */
2422 if (info->in->shut_on_empty && info->in->need_wake) {
2423 info->in->need_wake = FALSE;
2424 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2426 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2430 if (info->out && !info->out_done) { /* were we also piping output? */
2431 info->out->shut_on_empty = TRUE;
2432 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2433 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2434 _ckvmssts_noperl(iss);
2437 if (info->err && !info->err_done) { /* we were piping stderr */
2438 info->err->shut_on_empty = TRUE;
2439 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2440 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2441 _ckvmssts_noperl(iss);
2443 _ckvmssts_noperl(sys$setef(pipe_ef));
2447 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2448 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2451 we actually differ from vmstrnenv since we use this to
2452 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2453 are pointing to the same thing
2456 static unsigned short
2457 popen_translate(pTHX_ char *logical, char *result)
2460 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2461 $DESCRIPTOR(d_log,"");
2463 unsigned short length;
2464 unsigned short code;
2466 unsigned short *retlenaddr;
2468 unsigned short l, ifi;
2470 d_log.dsc$a_pointer = logical;
2471 d_log.dsc$w_length = strlen(logical);
2473 itmlst[0].code = LNM$_STRING;
2474 itmlst[0].length = 255;
2475 itmlst[0].buffer_addr = result;
2476 itmlst[0].retlenaddr = &l;
2479 itmlst[1].length = 0;
2480 itmlst[1].buffer_addr = 0;
2481 itmlst[1].retlenaddr = 0;
2483 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
2484 if (iss == SS$_NOLOGNAM) {
2488 if (!(iss&1)) lib$signal(iss);
2491 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
2492 strip it off and return the ifi, if any
2495 if (result[0] == 0x1b && result[1] == 0x00) {
2496 memmove(&ifi,result+2,2);
2497 strcpy(result,result+4);
2499 return ifi; /* this is the RMS internal file id */
2502 static void pipe_infromchild_ast(pPipe p);
2505 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
2506 inside an AST routine without worrying about reentrancy and which Perl
2507 memory allocator is being used.
2509 We read data and queue up the buffers, then spit them out one at a
2510 time to the output mailbox when the output mailbox is ready for one.
2513 #define INITIAL_TOCHILDQUEUE 2
2516 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
2520 char mbx1[64], mbx2[64];
2521 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2522 DSC$K_CLASS_S, mbx1},
2523 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2524 DSC$K_CLASS_S, mbx2};
2525 unsigned int dviitm = DVI$_DEVBUFSIZ;
2530 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2531 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2532 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2535 p->shut_on_empty = FALSE;
2536 p->need_wake = FALSE;
2539 p->iosb.status = SS$_NORMAL;
2540 p->iosb2.status = SS$_NORMAL;
2546 #ifdef PERL_IMPLICIT_CONTEXT
2550 n = sizeof(CBuf) + p->bufsize;
2552 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
2553 _ckvmssts(lib$get_vm(&n, &b));
2554 b->buf = (char *) b + sizeof(CBuf);
2555 _ckvmssts(lib$insqhi(b, &p->free));
2558 pipe_tochild2_ast(p);
2559 pipe_tochild1_ast(p);
2565 /* reads the MBX Perl is writing, and queues */
2568 pipe_tochild1_ast(pPipe p)
2571 int iss = p->iosb.status;
2572 int eof = (iss == SS$_ENDOFFILE);
2574 #ifdef PERL_IMPLICIT_CONTEXT
2580 p->shut_on_empty = TRUE;
2582 _ckvmssts(sys$dassgn(p->chan_in));
2588 b->size = p->iosb.count;
2589 _ckvmssts(sts = lib$insqhi(b, &p->wait));
2591 p->need_wake = FALSE;
2592 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
2595 p->retry = 1; /* initial call */
2598 if (eof) { /* flush the free queue, return when done */
2599 int n = sizeof(CBuf) + p->bufsize;
2601 iss = lib$remqti(&p->free, &b);
2602 if (iss == LIB$_QUEWASEMP) return;
2604 _ckvmssts(lib$free_vm(&n, &b));
2608 iss = lib$remqti(&p->free, &b);
2609 if (iss == LIB$_QUEWASEMP) {
2610 int n = sizeof(CBuf) + p->bufsize;
2611 _ckvmssts(lib$get_vm(&n, &b));
2612 b->buf = (char *) b + sizeof(CBuf);
2618 iss = sys$qio(0,p->chan_in,
2619 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2621 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2622 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2627 /* writes queued buffers to output, waits for each to complete before
2631 pipe_tochild2_ast(pPipe p)
2634 int iss = p->iosb2.status;
2635 int n = sizeof(CBuf) + p->bufsize;
2636 int done = (p->info && p->info->done) ||
2637 iss == SS$_CANCEL || iss == SS$_ABORT;
2638 #if defined(PERL_IMPLICIT_CONTEXT)
2643 if (p->type) { /* type=1 has old buffer, dispose */
2644 if (p->shut_on_empty) {
2645 _ckvmssts(lib$free_vm(&n, &b));
2647 _ckvmssts(lib$insqhi(b, &p->free));
2652 iss = lib$remqti(&p->wait, &b);
2653 if (iss == LIB$_QUEWASEMP) {
2654 if (p->shut_on_empty) {
2656 _ckvmssts(sys$dassgn(p->chan_out));
2657 *p->pipe_done = TRUE;
2658 _ckvmssts(sys$setef(pipe_ef));
2660 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2661 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2665 p->need_wake = TRUE;
2675 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2676 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2678 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2679 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2688 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2691 char mbx1[64], mbx2[64];
2692 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2693 DSC$K_CLASS_S, mbx1},
2694 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2695 DSC$K_CLASS_S, mbx2};
2696 unsigned int dviitm = DVI$_DEVBUFSIZ;
2699 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2700 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2702 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2703 Newx(p->buf, p->bufsize, char);
2704 p->shut_on_empty = FALSE;
2707 p->iosb.status = SS$_NORMAL;
2708 #if defined(PERL_IMPLICIT_CONTEXT)
2711 pipe_infromchild_ast(p);
2719 pipe_infromchild_ast(pPipe p)
2721 int iss = p->iosb.status;
2722 int eof = (iss == SS$_ENDOFFILE);
2723 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
2724 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
2725 #if defined(PERL_IMPLICIT_CONTEXT)
2729 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
2730 _ckvmssts(sys$dassgn(p->chan_out));
2735 input shutdown if EOF from self (done or shut_on_empty)
2736 output shutdown if closing flag set (my_pclose)
2737 send data/eof from child or eof from self
2738 otherwise, re-read (snarf of data from child)
2743 if (myeof && p->chan_in) { /* input shutdown */
2744 _ckvmssts(sys$dassgn(p->chan_in));
2749 if (myeof || kideof) { /* pass EOF to parent */
2750 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
2751 pipe_infromchild_ast, p,
2754 } else if (eof) { /* eat EOF --- fall through to read*/
2756 } else { /* transmit data */
2757 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
2758 pipe_infromchild_ast,p,
2759 p->buf, p->iosb.count, 0, 0, 0, 0));
2765 /* everything shut? flag as done */
2767 if (!p->chan_in && !p->chan_out) {
2768 *p->pipe_done = TRUE;
2769 _ckvmssts(sys$setef(pipe_ef));
2773 /* write completed (or read, if snarfing from child)
2774 if still have input active,
2775 queue read...immediate mode if shut_on_empty so we get EOF if empty
2777 check if Perl reading, generate EOFs as needed
2783 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
2784 pipe_infromchild_ast,p,
2785 p->buf, p->bufsize, 0, 0, 0, 0);
2786 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
2788 } else { /* send EOFs for extra reads */
2789 p->iosb.status = SS$_ENDOFFILE;
2790 p->iosb.dvispec = 0;
2791 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
2793 pipe_infromchild_ast, p, 0, 0, 0, 0));
2799 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
2803 unsigned long dviitm = DVI$_DEVBUFSIZ;
2805 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
2806 DSC$K_CLASS_S, mbx};
2808 /* things like terminals and mbx's don't need this filter */
2809 if (fd && fstat(fd,&s) == 0) {
2810 unsigned long dviitm = DVI$_DEVCHAR, devchar;
2811 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
2812 DSC$K_CLASS_S, s.st_dev};
2814 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
2815 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
2816 strcpy(out, s.st_dev);
2822 p->fd_out = dup(fd);
2823 create_mbx(aTHX_ &p->chan_in, &d_mbx);
2824 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2825 Newx(p->buf, p->bufsize+1, char);
2826 p->shut_on_empty = FALSE;
2831 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
2832 pipe_mbxtofd_ast, p,
2833 p->buf, p->bufsize, 0, 0, 0, 0));
2839 pipe_mbxtofd_ast(pPipe p)
2841 int iss = p->iosb.status;
2842 int done = p->info->done;
2844 int eof = (iss == SS$_ENDOFFILE);
2845 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
2846 int err = !(iss&1) && !eof;
2847 #if defined(PERL_IMPLICIT_CONTEXT)
2851 if (done && myeof) { /* end piping */
2853 sys$dassgn(p->chan_in);
2854 *p->pipe_done = TRUE;
2855 _ckvmssts(sys$setef(pipe_ef));
2859 if (!err && !eof) { /* good data to send to file */
2860 p->buf[p->iosb.count] = '\n';
2861 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
2864 if (p->retry < MAX_RETRY) {
2865 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
2875 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
2876 pipe_mbxtofd_ast, p,
2877 p->buf, p->bufsize, 0, 0, 0, 0);
2878 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
2883 typedef struct _pipeloc PLOC;
2884 typedef struct _pipeloc* pPLOC;
2888 char dir[NAM$C_MAXRSS+1];
2890 static pPLOC head_PLOC = 0;
2893 free_pipelocs(pTHX_ void *head)
2896 pPLOC *pHead = (pPLOC *)head;
2908 store_pipelocs(pTHX)
2917 char temp[NAM$C_MAXRSS+1];
2921 free_pipelocs(aTHX_ &head_PLOC);
2923 /* the . directory from @INC comes last */
2925 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2926 p->next = head_PLOC;
2928 strcpy(p->dir,"./");
2930 /* get the directory from $^X */
2932 #ifdef PERL_IMPLICIT_CONTEXT
2933 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2935 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2937 strcpy(temp, PL_origargv[0]);
2938 x = strrchr(temp,']');
2940 x = strrchr(temp,'>');
2942 /* It could be a UNIX path */
2943 x = strrchr(temp,'/');
2949 /* Got a bare name, so use default directory */
2954 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2955 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2956 p->next = head_PLOC;
2958 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2959 p->dir[NAM$C_MAXRSS] = '\0';
2963 /* reverse order of @INC entries, skip "." since entered above */
2965 #ifdef PERL_IMPLICIT_CONTEXT
2968 if (PL_incgv) av = GvAVn(PL_incgv);
2970 for (i = 0; av && i <= AvFILL(av); i++) {
2971 dirsv = *av_fetch(av,i,TRUE);
2973 if (SvROK(dirsv)) continue;
2974 dir = SvPVx(dirsv,n_a);
2975 if (strcmp(dir,".") == 0) continue;
2976 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2979 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2980 p->next = head_PLOC;
2982 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2983 p->dir[NAM$C_MAXRSS] = '\0';
2986 /* most likely spot (ARCHLIB) put first in the list */
2989 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
2990 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2991 p->next = head_PLOC;
2993 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2994 p->dir[NAM$C_MAXRSS] = '\0';
3003 static int vmspipe_file_status = 0;
3004 static char vmspipe_file[NAM$C_MAXRSS+1];
3006 /* already found? Check and use ... need read+execute permission */
3008 if (vmspipe_file_status == 1) {
3009 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3010 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3011 return vmspipe_file;
3013 vmspipe_file_status = 0;
3016 /* scan through stored @INC, $^X */
3018 if (vmspipe_file_status == 0) {
3019 char file[NAM$C_MAXRSS+1];
3020 pPLOC p = head_PLOC;
3023 strcpy(file, p->dir);
3024 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
3025 file[NAM$C_MAXRSS] = '\0';
3028 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
3030 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3031 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3032 vmspipe_file_status = 1;
3033 return vmspipe_file;
3036 vmspipe_file_status = -1; /* failed, use tempfiles */
3043 vmspipe_tempfile(pTHX)
3045 char file[NAM$C_MAXRSS+1];
3047 static int index = 0;
3051 /* create a tempfile */
3053 /* we can't go from W, shr=get to R, shr=get without
3054 an intermediate vulnerable state, so don't bother trying...
3056 and lib$spawn doesn't shr=put, so have to close the write
3058 So... match up the creation date/time and the FID to
3059 make sure we're dealing with the same file
3064 if (!decc_filename_unix_only) {
3065 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3066 fp = fopen(file,"w");
3068 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3069 fp = fopen(file,"w");
3071 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3072 fp = fopen(file,"w");
3077 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3078 fp = fopen(file,"w");
3080 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3081 fp = fopen(file,"w");
3083 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3084 fp = fopen(file,"w");
3088 if (!fp) return 0; /* we're hosed */
3090 fprintf(fp,"$! 'f$verify(0)'\n");
3091 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3092 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3093 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3094 fprintf(fp,"$ perl_on = \"set noon\"\n");
3095 fprintf(fp,"$ perl_exit = \"exit\"\n");
3096 fprintf(fp,"$ perl_del = \"delete\"\n");
3097 fprintf(fp,"$ pif = \"if\"\n");
3098 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3099 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3100 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3101 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3102 fprintf(fp,"$! --- build command line to get max possible length\n");
3103 fprintf(fp,"$c=perl_popen_cmd0\n");
3104 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3105 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3106 fprintf(fp,"$x=perl_popen_cmd3\n");
3107 fprintf(fp,"$c=c+x\n");
3108 fprintf(fp,"$ perl_on\n");
3109 fprintf(fp,"$ 'c'\n");
3110 fprintf(fp,"$ perl_status = $STATUS\n");
3111 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3112 fprintf(fp,"$ perl_exit 'perl_status'\n");
3115 fgetname(fp, file, 1);
3116 fstat(fileno(fp), (struct stat *)&s0);
3119 if (decc_filename_unix_only)
3120 do_tounixspec(file, file, 0);
3121 fp = fopen(file,"r","shr=get");
3123 fstat(fileno(fp), (struct stat *)&s1);
3125 #if defined(_USE_STD_STAT)
3126 cmp_result = s0.crtl_stat.st_ino != s1.crtl_stat.st_ino;
3128 cmp_result = memcmp(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino, 6);
3130 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3141 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3143 static int handler_set_up = FALSE;
3144 unsigned long int sts, flags = CLI$M_NOWAIT;
3145 /* The use of a GLOBAL table (as was done previously) rendered
3146 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3147 * environment. Hence we've switched to LOCAL symbol table.
3149 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3151 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3152 char in[512], out[512], err[512], mbx[512];
3154 char tfilebuf[NAM$C_MAXRSS+1];
3156 char cmd_sym_name[20];
3157 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3158 DSC$K_CLASS_S, symbol};
3159 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3161 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3162 DSC$K_CLASS_S, cmd_sym_name};
3163 struct dsc$descriptor_s *vmscmd;
3164 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3165 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3166 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3168 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3170 /* once-per-program initialization...
3171 note that the SETAST calls and the dual test of pipe_ef
3172 makes sure that only the FIRST thread through here does
3173 the initialization...all other threads wait until it's
3176 Yeah, uglier than a pthread call, it's got all the stuff inline
3177 rather than in a separate routine.
3181 _ckvmssts(sys$setast(0));
3183 unsigned long int pidcode = JPI$_PID;
3184 $DESCRIPTOR(d_delay, RETRY_DELAY);
3185 _ckvmssts(lib$get_ef(&pipe_ef));
3186 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3187 _ckvmssts(sys$bintim(&d_delay, delaytime));
3189 if (!handler_set_up) {
3190 _ckvmssts(sys$dclexh(&pipe_exitblock));
3191 handler_set_up = TRUE;
3193 _ckvmssts(sys$setast(1));
3196 /* see if we can find a VMSPIPE.COM */
3199 vmspipe = find_vmspipe(aTHX);
3201 strcpy(tfilebuf+1,vmspipe);
3202 } else { /* uh, oh...we're in tempfile hell */
3203 tpipe = vmspipe_tempfile(aTHX);
3204 if (!tpipe) { /* a fish popular in Boston */
3205 if (ckWARN(WARN_PIPE)) {
3206 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3210 fgetname(tpipe,tfilebuf+1,1);
3212 vmspipedsc.dsc$a_pointer = tfilebuf;
3213 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3215 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3218 case RMS$_FNF: case RMS$_DNF:
3219 set_errno(ENOENT); break;
3221 set_errno(ENOTDIR); break;
3223 set_errno(ENODEV); break;
3225 set_errno(EACCES); break;
3227 set_errno(EINVAL); break;
3228 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3229 set_errno(E2BIG); break;
3230 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3231 _ckvmssts(sts); /* fall through */
3232 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3235 set_vaxc_errno(sts);
3236 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3237 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3244 strcpy(mode,in_mode);
3247 info->completion = 0;
3248 info->closing = FALSE;
3255 info->in_done = TRUE;
3256 info->out_done = TRUE;
3257 info->err_done = TRUE;
3258 in[0] = out[0] = err[0] = '\0';
3260 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3264 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3269 if (*mode == 'r') { /* piping from subroutine */
3271 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3273 info->out->pipe_done = &info->out_done;
3274 info->out_done = FALSE;
3275 info->out->info = info;
3277 if (!info->useFILE) {
3278 info->fp = PerlIO_open(mbx, mode);
3280 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3281 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3284 if (!info->fp && info->out) {
3285 sys$cancel(info->out->chan_out);
3287 while (!info->out_done) {
3289 _ckvmssts(sys$setast(0));
3290 done = info->out_done;
3291 if (!done) _ckvmssts(sys$clref(pipe_ef));
3292 _ckvmssts(sys$setast(1));
3293 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3296 if (info->out->buf) Safefree(info->out->buf);
3297 Safefree(info->out);
3303 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3305 info->err->pipe_done = &info->err_done;
3306 info->err_done = FALSE;
3307 info->err->info = info;
3310 } else if (*mode == 'w') { /* piping to subroutine */
3312 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3314 info->out->pipe_done = &info->out_done;
3315 info->out_done = FALSE;
3316 info->out->info = info;
3319 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3321 info->err->pipe_done = &info->err_done;
3322 info->err_done = FALSE;
3323 info->err->info = info;
3326 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3327 if (!info->useFILE) {
3328 info->fp = PerlIO_open(mbx, mode);
3330 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3331 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3335 info->in->pipe_done = &info->in_done;
3336 info->in_done = FALSE;
3337 info->in->info = info;
3341 if (!info->fp && info->in) {
3343 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3344 0, 0, 0, 0, 0, 0, 0, 0));
3346 while (!info->in_done) {
3348 _ckvmssts(sys$setast(0));
3349 done = info->in_done;
3350 if (!done) _ckvmssts(sys$clref(pipe_ef));
3351 _ckvmssts(sys$setast(1));
3352 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3355 if (info->in->buf) Safefree(info->in->buf);
3363 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3364 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3366 info->out->pipe_done = &info->out_done;
3367 info->out_done = FALSE;
3368 info->out->info = info;
3371 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3373 info->err->pipe_done = &info->err_done;
3374 info->err_done = FALSE;
3375 info->err->info = info;
3379 symbol[MAX_DCL_SYMBOL] = '\0';
3381 strncpy(symbol, in, MAX_DCL_SYMBOL);
3382 d_symbol.dsc$w_length = strlen(symbol);
3383 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3385 strncpy(symbol, err, MAX_DCL_SYMBOL);
3386 d_symbol.dsc$w_length = strlen(symbol);
3387 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3389 strncpy(symbol, out, MAX_DCL_SYMBOL);
3390 d_symbol.dsc$w_length = strlen(symbol);
3391 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3393 p = vmscmd->dsc$a_pointer;
3394 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3395 if (*p == '$') p++; /* remove leading $ */
3396 while (*p == ' ' || *p == '\t') p++;
3398 for (j = 0; j < 4; j++) {
3399 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3400 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3402 strncpy(symbol, p, MAX_DCL_SYMBOL);
3403 d_symbol.dsc$w_length = strlen(symbol);
3404 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3406 if (strlen(p) > MAX_DCL_SYMBOL) {
3407 p += MAX_DCL_SYMBOL;
3412 _ckvmssts(sys$setast(0));
3413 info->next=open_pipes; /* prepend to list */
3415 _ckvmssts(sys$setast(1));
3416 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
3417 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
3418 * have SYS$COMMAND if we need it.
3420 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
3421 0, &info->pid, &info->completion,
3422 0, popen_completion_ast,info,0,0,0));
3424 /* if we were using a tempfile, close it now */
3426 if (tpipe) fclose(tpipe);
3428 /* once the subprocess is spawned, it has copied the symbols and
3429 we can get rid of ours */
3431 for (j = 0; j < 4; j++) {
3432 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3433 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3434 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
3436 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
3437 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
3438 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
3439 vms_execfree(vmscmd);
3441 #ifdef PERL_IMPLICIT_CONTEXT
3444 PL_forkprocess = info->pid;
3449 _ckvmssts(sys$setast(0));
3451 if (!done) _ckvmssts(sys$clref(pipe_ef));
3452 _ckvmssts(sys$setast(1));
3453 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3455 *psts = info->completion;
3456 /* Caller thinks it is open and tries to close it. */
3457 /* This causes some problems, as it changes the error status */
3458 /* my_pclose(info->fp); */
3463 } /* end of safe_popen */
3466 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
3468 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
3472 TAINT_PROPER("popen");
3473 PERL_FLUSHALL_FOR_CHILD;
3474 return safe_popen(aTHX_ cmd,mode,&sts);
3479 /*{{{ I32 my_pclose(PerlIO *fp)*/
3480 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
3482 pInfo info, last = NULL;
3483 unsigned long int retsts;
3486 for (info = open_pipes; info != NULL; last = info, info = info->next)
3487 if (info->fp == fp) break;
3489 if (info == NULL) { /* no such pipe open */
3490 set_errno(ECHILD); /* quoth POSIX */
3491 set_vaxc_errno(SS$_NONEXPR);
3495 /* If we were writing to a subprocess, insure that someone reading from
3496 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
3497 * produce an EOF record in the mailbox.
3499 * well, at least sometimes it *does*, so we have to watch out for
3500 * the first EOF closing the pipe (and DASSGN'ing the channel)...
3504 PerlIO_flush(info->fp); /* first, flush data */
3506 fflush((FILE *)info->fp);
3509 _ckvmssts(sys$setast(0));
3510 info->closing = TRUE;
3511 done = info->done && info->in_done && info->out_done && info->err_done;
3512 /* hanging on write to Perl's input? cancel it */
3513 if (info->mode == 'r' && info->out && !info->out_done) {
3514 if (info->out->chan_out) {
3515 _ckvmssts(sys$cancel(info->out->chan_out));
3516 if (!info->out->chan_in) { /* EOF generation, need AST */
3517 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
3521 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
3522 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3524 _ckvmssts(sys$setast(1));
3527 PerlIO_close(info->fp);
3529 fclose((FILE *)info->fp);
3532 we have to wait until subprocess completes, but ALSO wait until all
3533 the i/o completes...otherwise we'll be freeing the "info" structure
3534 that the i/o ASTs could still be using...
3538 _ckvmssts(sys$setast(0));
3539 done = info->done && info->in_done && info->out_done && info->err_done;
3540 if (!done) _ckvmssts(sys$clref(pipe_ef));
3541 _ckvmssts(sys$setast(1));
3542 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3544 retsts = info->completion;
3546 /* remove from list of open pipes */
3547 _ckvmssts(sys$setast(0));
3548 if (last) last->next = info->next;
3549 else open_pipes = info->next;
3550 _ckvmssts(sys$setast(1));
3552 /* free buffers and structures */
3555 if (info->in->buf) Safefree(info->in->buf);
3559 if (info->out->buf) Safefree(info->out->buf);
3560 Safefree(info->out);
3563 if (info->err->buf) Safefree(info->err->buf);
3564 Safefree(info->err);
3570 } /* end of my_pclose() */
3572 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3573 /* Roll our own prototype because we want this regardless of whether
3574 * _VMS_WAIT is defined.
3576 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
3578 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
3579 created with popen(); otherwise partially emulate waitpid() unless
3580 we have a suitable one from the CRTL that came with VMS 7.2 and later.
3581 Also check processes not considered by the CRTL waitpid().
3583 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
3585 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
3592 if (statusp) *statusp = 0;
3594 for (info = open_pipes; info != NULL; info = info->next)
3595 if (info->pid == pid) break;
3597 if (info != NULL) { /* we know about this child */
3598 while (!info->done) {
3599 _ckvmssts(sys$setast(0));
3601 if (!done) _ckvmssts(sys$clref(pipe_ef));
3602 _ckvmssts(sys$setast(1));
3603 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3606 if (statusp) *statusp = info->completion;
3610 /* child that already terminated? */
3612 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
3613 if (closed_list[j].pid == pid) {
3614 if (statusp) *statusp = closed_list[j].completion;
3619 /* fall through if this child is not one of our own pipe children */
3621 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3623 /* waitpid() became available in the CRTL as of VMS 7.0, but only
3624 * in 7.2 did we get a version that fills in the VMS completion
3625 * status as Perl has always tried to do.
3628 sts = __vms_waitpid( pid, statusp, flags );
3630 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
3633 /* If the real waitpid tells us the child does not exist, we
3634 * fall through here to implement waiting for a child that
3635 * was created by some means other than exec() (say, spawned
3636 * from DCL) or to wait for a process that is not a subprocess
3637 * of the current process.
3640 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3643 $DESCRIPTOR(intdsc,"0 00:00:01");
3644 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3645 unsigned long int pidcode = JPI$_PID, mypid;
3646 unsigned long int interval[2];
3647 unsigned int jpi_iosb[2];
3648 struct itmlst_3 jpilist[2] = {
3649 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3654 /* Sorry folks, we don't presently implement rooting around for
3655 the first child we can find, and we definitely don't want to
3656 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3662 /* Get the owner of the child so I can warn if it's not mine. If the
3663 * process doesn't exist or I don't have the privs to look at it,
3664 * I can go home early.
3666 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3667 if (sts & 1) sts = jpi_iosb[0];
3679 set_vaxc_errno(sts);
3683 if (ckWARN(WARN_EXEC)) {
3684 /* remind folks they are asking for non-standard waitpid behavior */
3685 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3686 if (ownerpid != mypid)
3687 Perl_warner(aTHX_ packWARN(WARN_EXEC),
3688 "waitpid: process %x is not a child of process %x",
3692 /* simply check on it once a second until it's not there anymore. */
3694 _ckvmssts(sys$bintim(&intdsc,interval));
3695 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
3696 _ckvmssts(sys$schdwk(0,0,interval,0));
3697 _ckvmssts(sys$hiber());
3699 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
3704 } /* end of waitpid() */
3709 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
3711 my_gconvert(double val, int ndig, int trail, char *buf)
3713 static char __gcvtbuf[DBL_DIG+1];
3716 loc = buf ? buf : __gcvtbuf;
3718 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
3720 sprintf(loc,"%.*g",ndig,val);
3726 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
3727 return gcvt(val,ndig,loc);
3730 loc[0] = '0'; loc[1] = '\0';
3738 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
3739 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
3740 * to expand file specification. Allows for a single default file
3741 * specification and a simple mask of options. If outbuf is non-NULL,
3742 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
3743 * the resultant file specification is placed. If outbuf is NULL, the
3744 * resultant file specification is placed into a static buffer.
3745 * The third argument, if non-NULL, is taken to be a default file
3746 * specification string. The fourth argument is unused at present.
3747 * rmesexpand() returns the address of the resultant string if
3748 * successful, and NULL on error.
3750 * New functionality for previously unused opts value:
3751 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
3753 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
3755 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
3756 /* ODS-2 only version */
3758 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3760 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
3761 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
3762 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
3763 struct FAB myfab = cc$rms_fab;
3764 struct NAM mynam = cc$rms_nam;
3766 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3769 if (!filespec || !*filespec) {
3770 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3774 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
3775 else outbuf = __rmsexpand_retbuf;
3777 isunix = is_unix_filespec(filespec);
3779 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3784 filespec = vmsfspec;
3787 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
3788 myfab.fab$b_fns = strlen(filespec);
3789 myfab.fab$l_nam = &mynam;
3791 if (defspec && *defspec) {
3792 if (strchr(defspec,'/') != NULL) {
3793 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
3800 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
3801 myfab.fab$b_dns = strlen(defspec);
3804 mynam.nam$l_esa = esa;
3805 mynam.nam$b_ess = sizeof esa;
3806 mynam.nam$l_rsa = outbuf;
3807 mynam.nam$b_rss = NAM$C_MAXRSS;
3809 #ifdef NAM$M_NO_SHORT_UPCASE
3810 if (decc_efs_case_preserve)
3811 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3814 retsts = sys$parse(&myfab,0,0);
3815 if (!(retsts & 1)) {
3816 mynam.nam$b_nop |= NAM$M_SYNCHK;
3817 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
3818 retsts = sys$parse(&myfab,0,0);
3819 if (retsts & 1) goto expanded;
3821 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
3822 sts = sys$parse(&myfab,0,0); /* Free search context */
3823 if (out) Safefree(out);
3824 set_vaxc_errno(retsts);
3825 if (retsts == RMS$_PRV) set_errno(EACCES);
3826 else if (retsts == RMS$_DEV) set_errno(ENODEV);
3827 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3828 else set_errno(EVMSERR);
3831 retsts = sys$search(&myfab,0,0);
3832 if (!(retsts & 1) && retsts != RMS$_FNF) {
3833 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3834 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3835 if (out) Safefree(out);
3836 set_vaxc_errno(retsts);
3837 if (retsts == RMS$_PRV) set_errno(EACCES);
3838 else set_errno(EVMSERR);
3842 /* If the input filespec contained any lowercase characters,
3843 * downcase the result for compatibility with Unix-minded code. */
3845 if (!decc_efs_case_preserve) {
3846 for (out = myfab.fab$l_fna; *out; out++)
3847 if (islower(*out)) { haslower = 1; break; }
3849 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
3850 else { out = esa; speclen = mynam.nam$b_esl; }
3851 /* Trim off null fields added by $PARSE
3852 * If type > 1 char, must have been specified in original or default spec
3853 * (not true for version; $SEARCH may have added version of existing file).
3855 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
3856 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
3857 (mynam.nam$l_ver - mynam.nam$l_type == 1);
3858 if (trimver || trimtype) {
3859 if (defspec && *defspec) {
3860 char defesa[NAM$C_MAXRSS];
3861 struct FAB deffab = cc$rms_fab;
3862 struct NAM defnam = cc$rms_nam;
3864 deffab.fab$l_nam = &defnam;
3865 /* cast below ok for read only pointer */
3866 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
3867 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
3868 defnam.nam$b_nop = NAM$M_SYNCHK;
3869 #ifdef NAM$M_NO_SHORT_UPCASE
3870 if (decc_efs_case_preserve)
3871 defnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3873 if (sys$parse(&deffab,0,0) & 1) {
3874 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
3875 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
3879 if (*mynam.nam$l_ver != '\"')
3880 speclen = mynam.nam$l_ver - out;
3883 /* If we didn't already trim version, copy down */
3884 if (speclen > mynam.nam$l_ver - out)
3885 memmove(mynam.nam$l_type, mynam.nam$l_ver,
3886 speclen - (mynam.nam$l_ver - out));
3887 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
3890 /* If we just had a directory spec on input, $PARSE "helpfully"
3891 * adds an empty name and type for us */
3892 if (mynam.nam$l_name == mynam.nam$l_type &&
3893 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
3894 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
3895 speclen = mynam.nam$l_name - out;
3897 /* Posix format specifications must have matching quotes */
3898 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
3899 if ((speclen > 1) && (out[speclen-1] != '\"')) {
3900 out[speclen] = '\"';
3905 out[speclen] = '\0';
3906 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
3908 /* Have we been working with an expanded, but not resultant, spec? */
3909 /* Also, convert back to Unix syntax if necessary. */
3910 if ((opts & PERL_RMSEXPAND_M_VMS) != 0)
3913 if (!mynam.nam$b_rsl) {
3915 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
3917 else strcpy(outbuf,esa);
3920 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
3921 strcpy(outbuf,tmpfspec);
3923 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3924 mynam.nam$l_rsa = NULL;
3925 mynam.nam$b_rss = 0;
3926 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3930 /* ODS-5 supporting routine */
3932 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3934 static char __rmsexpand_retbuf[NAML$C_MAXRSS+1];
3935 char * vmsfspec, *tmpfspec;
3936 char * esa, *cp, *out = NULL;
3939 struct FAB myfab = cc$rms_fab;
3940 struct NAML mynam = cc$rms_naml;
3942 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3945 if (!filespec || !*filespec) {
3946 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3950 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
3951 else outbuf = __rmsexpand_retbuf;
3957 isunix = is_unix_filespec(filespec);
3959 Newx(vmsfspec, VMS_MAXRSS, char);
3960 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3966 filespec = vmsfspec;
3968 /* Unless we are forcing to VMS format, a UNIX input means
3969 * UNIX output, and that requires long names to be used
3971 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
3972 opts |= PERL_RMSEXPAND_M_LONG;
3978 myfab.fab$l_fna = (char *)-1; /* cast ok */
3979 myfab.fab$b_fns = 0;
3980 mynam.naml$l_long_filename = (char *)filespec; /* cast ok */
3981 mynam.naml$l_long_filename_size = strlen(filespec);
3982 myfab.fab$l_naml = &mynam;
3984 if (defspec && *defspec) {
3986 t_isunix = is_unix_filespec(defspec);
3988 Newx(tmpfspec, VMS_MAXRSS, char);
3989 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
3991 if (vmsfspec != NULL)
3999 myfab.fab$l_dna = (char *) -1; /* cast ok */
4000 myfab.fab$b_dns = 0;
4001 mynam.naml$l_long_defname = (char *)defspec; /* cast ok */
4002 mynam.naml$l_long_defname_size = strlen(defspec);
4005 Newx(esa, NAM$C_MAXRSS + 1, char);
4006 Newx(esal, NAML$C_MAXRSS + 1, char);
4007 mynam.naml$l_esa = esa;
4008 mynam.naml$b_ess = NAM$C_MAXRSS;
4009 mynam.naml$l_long_expand = esal;
4010 mynam.naml$l_long_expand_alloc = NAML$C_MAXRSS;
4012 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4013 mynam.naml$l_rsa = NULL;
4014 mynam.naml$b_rss = 0;
4015 mynam.naml$l_long_result = outbuf;
4016 mynam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
4019 mynam.naml$l_rsa = outbuf;
4020 mynam.naml$b_rss = NAM$C_MAXRSS;
4021 Newx(outbufl, VMS_MAXRSS, char);
4022 mynam.naml$l_long_result = outbufl;
4023 mynam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
4026 #ifdef NAM$M_NO_SHORT_UPCASE
4027 if (decc_efs_case_preserve)
4028 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
4031 /* First attempt to parse as an existing file */
4032 retsts = sys$parse(&myfab,0,0);
4033 if (!(retsts & STS$K_SUCCESS)) {
4035 /* Could not find the file, try as syntax only if error is not fatal */
4036 mynam.naml$b_nop |= NAM$M_SYNCHK;
4037 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4038 retsts = sys$parse(&myfab,0,0);
4039 if (retsts & STS$K_SUCCESS) goto expanded;
4042 /* Still could not parse the file specification */
4043 /*----------------------------------------------*/
4044 mynam.naml$l_rlf = NULL;
4045 myfab.fab$b_dns = 0;
4046 mynam.naml$l_long_defname_size = 0;
4047 sts = sys$parse(&myfab,0,0); /* Free search context */
4048 if (out) Safefree(out);
4049 if (tmpfspec != NULL)
4051 if (vmsfspec != NULL)
4055 set_vaxc_errno(retsts);
4056 if (retsts == RMS$_PRV) set_errno(EACCES);
4057 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4058 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4059 else set_errno(EVMSERR);
4062 retsts = sys$search(&myfab,0,0);
4063 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4064 mynam.naml$b_nop |= NAM$M_SYNCHK;
4065 mynam.naml$l_rlf = NULL;
4066 myfab.fab$b_dns = 0;
4067 mynam.naml$l_long_defname_size = 0;
4068 sts = sys$parse(&myfab,0,0); /* Free search context */
4069 if (out) Safefree(out);
4070 if (tmpfspec != NULL)
4072 if (vmsfspec != NULL)
4076 set_vaxc_errno(retsts);
4077 if (retsts == RMS$_PRV) set_errno(EACCES);
4078 else set_errno(EVMSERR);
4082 /* If the input filespec contained any lowercase characters,
4083 * downcase the result for compatibility with Unix-minded code. */
4085 if (!decc_efs_case_preserve) {
4086 for (out = mynam.naml$l_long_filename; *out; out++)
4087 if (islower(*out)) { haslower = 1; break; }
4090 /* Is a long or a short name expected */
4091 /*------------------------------------*/
4092 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4093 if (mynam.naml$l_long_result_size) {
4095 speclen = mynam.naml$l_long_result_size;
4098 out = esal; /* Not esa */
4099 speclen = mynam.naml$l_long_expand_size;
4103 if (mynam.naml$b_rsl) {
4105 speclen = mynam.naml$b_rsl;
4108 out = esa; /* Not esal */
4109 speclen = mynam.naml$b_esl;
4112 /* Trim off null fields added by $PARSE
4113 * If type > 1 char, must have been specified in original or default spec
4114 * (not true for version; $SEARCH may have added version of existing file).
4116 trimver = !(mynam.naml$l_fnb & NAM$M_EXP_VER);
4117 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4118 trimtype = !(mynam.naml$l_fnb & NAM$M_EXP_TYPE) &&
4119 (mynam.naml$l_long_ver - mynam.naml$l_long_type == 1);
4122 trimtype = !(mynam.naml$l_fnb & NAM$M_EXP_TYPE) &&
4123 (mynam.naml$l_ver - mynam.naml$l_type == 1);
4125 if (trimver || trimtype) {
4126 if (defspec && *defspec) {
4127 char *defesal = NULL;
4128 Newx(defesal, NAML$C_MAXRSS + 1, char);
4129 if (defesal != NULL) {
4130 struct FAB deffab = cc$rms_fab;
4131 struct NAML defnam = cc$rms_naml;
4133 deffab.fab$l_naml = &defnam;
4135 deffab.fab$l_fna = (char *) - 1; /* Cast ok */
4136 deffab.fab$b_fns = 0;
4137 defnam.naml$l_long_filename = (char *)defspec; /* Cast ok */
4138 defnam.naml$l_long_filename_size = mynam.naml$l_long_defname_size;
4139 defnam.naml$l_esa = NULL;
4140 defnam.naml$b_ess = 0;
4141 defnam.naml$l_long_expand = defesal;
4142 defnam.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
4143 defnam.naml$b_nop = NAM$M_SYNCHK;
4144 #ifdef NAM$M_NO_SHORT_UPCASE
4145 if (decc_efs_case_preserve)
4146 defnam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
4148 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4150 trimver = !(defnam.naml$l_fnb & NAM$M_EXP_VER);
4153 trimtype = !(defnam.naml$l_fnb & NAM$M_EXP_TYPE);
4160 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4161 if (*mynam.naml$l_long_ver != '\"')
4162 speclen = mynam.naml$l_long_ver - out;
4165 if (*mynam.naml$l_ver != '\"')
4166 speclen = mynam.naml$l_ver - out;
4170 /* If we didn't already trim version, copy down */
4171 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4172 if (speclen > mynam.naml$l_long_ver - out)
4174 (mynam.naml$l_long_type,
4175 mynam.naml$l_long_ver,
4176 speclen - (mynam.naml$l_long_ver - out));
4177 speclen -= mynam.naml$l_long_ver - mynam.naml$l_long_type;
4180 if (speclen > mynam.naml$l_ver - out)
4184 speclen - (mynam.naml$l_ver - out));
4185 speclen -= mynam.naml$l_ver - mynam.naml$l_type;
4190 /* Done with these copies of the input files */
4191 /*-------------------------------------------*/
4192 if (vmsfspec != NULL)
4194 if (tmpfspec != NULL)
4197 /* If we just had a directory spec on input, $PARSE "helpfully"
4198 * adds an empty name and type for us */
4199 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4200 if (mynam.naml$l_long_name == mynam.naml$l_long_type &&
4201 mynam.naml$l_long_ver == mynam.naml$l_long_type + 1 &&
4202 !(mynam.naml$l_fnb & NAM$M_EXP_NAME))
4203 speclen = mynam.naml$l_long_name - out;
4206 if (mynam.naml$l_name == mynam.naml$l_type &&
4207 mynam.naml$l_ver == mynam.naml$l_type + 1 &&
4208 !(mynam.naml$l_fnb & NAM$M_EXP_NAME))
4209 speclen = mynam.naml$l_name - out;
4212 /* Posix format specifications must have matching quotes */
4213 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4214 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4215 out[speclen] = '\"';
4219 out[speclen] = '\0';
4220 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4222 /* Have we been working with an expanded, but not resultant, spec? */
4223 /* Also, convert back to Unix syntax if necessary. */
4225 if (!mynam.naml$l_long_result_size) {
4227 if (do_tounixspec(esa,outbuf,0) == NULL) {
4233 else strcpy(outbuf,esa);
4236 Newx(tmpfspec, VMS_MAXRSS, char);
4237 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) {
4243 strcpy(outbuf,tmpfspec);
4247 mynam.naml$b_nop |= NAM$M_SYNCHK;
4248 mynam.naml$l_rlf = NULL;
4249 mynam.naml$l_rsa = NULL;
4250 mynam.naml$b_rss = 0;
4251 mynam.naml$l_long_result = NULL;
4252 mynam.naml$l_long_result_size = 0;
4253 myfab.fab$b_dns = 0;
4254 mynam.naml$l_long_defname_size = 0;
4255 sts = sys$parse(&myfab,0,0); /* Free search context */
4262 /* External entry points */
4263 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4264 { return do_rmsexpand(spec,buf,0,def,opt); }
4265 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4266 { return do_rmsexpand(spec,buf,1,def,opt); }
4270 ** The following routines are provided to make life easier when
4271 ** converting among VMS-style and Unix-style directory specifications.
4272 ** All will take input specifications in either VMS or Unix syntax. On
4273 ** failure, all return NULL. If successful, the routines listed below
4274 ** return a pointer to a buffer containing the appropriately
4275 ** reformatted spec (and, therefore, subsequent calls to that routine
4276 ** will clobber the result), while the routines of the same names with
4277 ** a _ts suffix appended will return a pointer to a mallocd string
4278 ** containing the appropriately reformatted spec.
4279 ** In all cases, only explicit syntax is altered; no check is made that
4280 ** the resulting string is valid or that the directory in question
4283 ** fileify_dirspec() - convert a directory spec into the name of the
4284 ** directory file (i.e. what you can stat() to see if it's a dir).
4285 ** The style (VMS or Unix) of the result is the same as the style
4286 ** of the parameter passed in.
4287 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4288 ** what you prepend to a filename to indicate what directory it's in).
4289 ** The style (VMS or Unix) of the result is the same as the style
4290 ** of the parameter passed in.
4291 ** tounixpath() - convert a directory spec into a Unix-style path.
4292 ** tovmspath() - convert a directory spec into a VMS-style path.
4293 ** tounixspec() - convert any file spec into a Unix-style file spec.
4294 ** tovmsspec() - convert any file spec into a VMS-style spec.
4296 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4297 ** Permission is given to distribute this code as part of the Perl
4298 ** standard distribution under the terms of the GNU General Public
4299 ** License or the Perl Artistic License. Copies of each may be
4300 ** found in the Perl standard distribution.
4303 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
4304 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
4306 static char __fileify_retbuf[NAM$C_MAXRSS+1];
4307 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4308 char *retspec, *cp1, *cp2, *lastdir;
4309 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
4310 unsigned short int trnlnm_iter_count;
4313 if (!dir || !*dir) {
4314 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4316 dirlen = strlen(dir);
4317 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4318 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4319 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4326 if (dirlen > NAM$C_MAXRSS) {
4327 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
4329 if (!strpbrk(dir+1,"/]>:") &&
4330 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4331 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4332 trnlnm_iter_count = 0;
4333 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4334 trnlnm_iter_count++;
4335 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4337 dirlen = strlen(trndir);
4340 strncpy(trndir,dir,dirlen);
4341 trndir[dirlen] = '\0';
4344 /* At this point we are done with *dir and use *trndir which is a
4345 * copy that can be modified. *dir must not be modified.
4348 /* If we were handed a rooted logical name or spec, treat it like a
4349 * simple directory, so that
4350 * $ Define myroot dev:[dir.]
4351 * ... do_fileify_dirspec("myroot",buf,1) ...
4352 * does something useful.
4354 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4355 trndir[--dirlen] = '\0';
4356 trndir[dirlen-1] = ']';
4358 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4359 trndir[--dirlen] = '\0';
4360 trndir[dirlen-1] = '>';
4363 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4364 /* If we've got an explicit filename, we can just shuffle the string. */
4365 if (*(cp1+1)) hasfilename = 1;
4366 /* Similarly, we can just back up a level if we've got multiple levels
4367 of explicit directories in a VMS spec which ends with directories. */
4369 for (cp2 = cp1; cp2 > trndir; cp2--) {
4371 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4372 *cp2 = *cp1; *cp1 = '\0';
4377 if (*cp2 == '[' || *cp2 == '<') break;
4382 cp1 = strpbrk(trndir,"]:>"); /* Prepare for future change */
4383 if (hasfilename || !cp1) { /* Unix-style path or filename */
4384 if (trndir[0] == '.') {
4385 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0'))
4386 return do_fileify_dirspec("[]",buf,ts);
4387 else if (trndir[1] == '.' &&
4388 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0')))
4389 return do_fileify_dirspec("[-]",buf,ts);
4391 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4392 dirlen -= 1; /* to last element */
4393 lastdir = strrchr(trndir,'/');
4395 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4396 /* If we have "/." or "/..", VMSify it and let the VMS code
4397 * below expand it, rather than repeating the code to handle
4398 * relative components of a filespec here */
4400 if (*(cp1+2) == '.') cp1++;
4401 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4402 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
4403 if (strchr(vmsdir,'/') != NULL) {
4404 /* If do_tovmsspec() returned it, it must have VMS syntax
4405 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4406 * the time to check this here only so we avoid a recursion
4407 * loop; otherwise, gigo.
4409 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
4411 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
4412 return do_tounixspec(trndir,buf,ts);
4415 } while ((cp1 = strstr(cp1,"/.")) != NULL);
4416 lastdir = strrchr(trndir,'/');
4418 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
4419 /* Ditto for specs that end in an MFD -- let the VMS code
4420 * figure out whether it's a real device or a rooted logical. */
4422 /* This should not happen any more. Allowing the fake /000000
4423 * in a UNIX pathname causes all sorts of problems when trying
4424 * to run in UNIX emulation. So the VMS to UNIX conversions
4425 * now remove the fake /000000 directories.
4428 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
4429 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
4430 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
4431 return do_tounixspec(trndir,buf,ts);
4435 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
4436 !(lastdir = cp1 = strrchr(trndir,']')) &&
4437 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
4438 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
4441 /* For EFS or ODS-5 look for the last dot */
4442 if (decc_efs_charset) {
4443 cp2 = strrchr(cp1,'.');
4445 if (vms_process_case_tolerant) {
4446 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4447 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4448 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4449 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4450 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4451 (ver || *cp3)))))) {
4453 set_vaxc_errno(RMS$_DIR);
4458 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4459 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4460 !*(cp2+3) || *(cp2+3) != 'R' ||
4461 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4462 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4463 (ver || *cp3)))))) {
4465 set_vaxc_errno(RMS$_DIR);
4469 dirlen = cp2 - trndir;
4473 retlen = dirlen + 6;
4474 if (buf) retspec = buf;
4475 else if (ts) Newx(retspec,retlen+1,char);
4476 else retspec = __fileify_retbuf;
4477 memcpy(retspec,trndir,dirlen);
4478 retspec[dirlen] = '\0';
4480 /* We've picked up everything up to the directory file name.
4481 Now just add the type and version, and we're set. */
4482 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
4483 strcat(retspec,".dir;1");
4485 strcat(retspec,".DIR;1");
4488 else { /* VMS-style directory spec */
4489 char esa[NAM$C_MAXRSS+1], term, *cp;
4490 unsigned long int sts, cmplen, haslower = 0;
4491 struct FAB dirfab = cc$rms_fab;
4492 struct NAM savnam, dirnam = cc$rms_nam;
4494 dirfab.fab$b_fns = strlen(trndir);
4495 dirfab.fab$l_fna = trndir;
4496 dirfab.fab$l_nam = &dirnam;
4497 dirfab.fab$l_dna = ".DIR;1";
4498 dirfab.fab$b_dns = 6;
4499 dirnam.nam$b_ess = NAM$C_MAXRSS;
4500 dirnam.nam$l_esa = esa;
4501 #ifdef NAM$M_NO_SHORT_UPCASE
4502 if (decc_efs_case_preserve)
4503 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4506 for (cp = trndir; *cp; cp++)
4507 if (islower(*cp)) { haslower = 1; break; }
4508 if (!((sts = sys$parse(&dirfab))&1)) {
4509 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4510 dirnam.nam$b_nop |= NAM$M_SYNCHK;
4511 sts = sys$parse(&dirfab) & 1;
4515 set_vaxc_errno(dirfab.fab$l_sts);
4521 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
4522 /* Yes; fake the fnb bits so we'll check type below */
4523 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
4525 else { /* No; just work with potential name */
4526 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
4528 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
4529 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4530 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4535 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
4536 cp1 = strchr(esa,']');
4537 if (!cp1) cp1 = strchr(esa,'>');
4538 if (cp1) { /* Should always be true */
4539 dirnam.nam$b_esl -= cp1 - esa - 1;
4540 memmove(esa,cp1 + 1,dirnam.nam$b_esl);
4543 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
4544 /* Yep; check version while we're at it, if it's there. */
4545 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
4546 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
4547 /* Something other than .DIR[;1]. Bzzt. */
4548 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4549 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4551 set_vaxc_errno(RMS$_DIR);
4555 esa[dirnam.nam$b_esl] = '\0';
4556 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
4557 /* They provided at least the name; we added the type, if necessary, */
4558 if (buf) retspec = buf; /* in sys$parse() */
4559 else if (ts) Newx(retspec,dirnam.nam$b_esl+1,char);
4560 else retspec = __fileify_retbuf;
4561 strcpy(retspec,esa);
4562 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4563 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4566 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
4567 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
4569 dirnam.nam$b_esl -= 9;
4571 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
4572 if (cp1 == NULL) { /* should never happen */
4573 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4574 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4579 retlen = strlen(esa);
4580 cp1 = strrchr(esa,'.');
4581 /* ODS-5 directory specifications can have extra "." in them. */
4582 while (cp1 != NULL) {
4583 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
4587 while ((cp1 > esa) && (*cp1 != '.'))
4594 if ((cp1) != NULL) {
4595 /* There's more than one directory in the path. Just roll back. */
4597 if (buf) retspec = buf;
4598 else if (ts) Newx(retspec,retlen+7,char);
4599 else retspec = __fileify_retbuf;
4600 strcpy(retspec,esa);
4603 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
4604 /* Go back and expand rooted logical name */
4605 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
4606 #ifdef NAM$M_NO_SHORT_UPCASE
4607 if (decc_efs_case_preserve)
4608 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4610 if (!(sys$parse(&dirfab) & 1)) {
4611 dirnam.nam$l_rlf = NULL;
4612 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4614 set_vaxc_errno(dirfab.fab$l_sts);
4617 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
4618 if (buf) retspec = buf;
4619 else if (ts) Newx(retspec,retlen+16,char);
4620 else retspec = __fileify_retbuf;
4621 cp1 = strstr(esa,"][");
4622 if (!cp1) cp1 = strstr(esa,"]<");
4624 memcpy(retspec,esa,dirlen);
4625 if (!strncmp(cp1+2,"000000]",7)) {
4626 retspec[dirlen-1] = '\0';
4627 /* Not full ODS-5, just extra dots in directories for now */
4628 cp1 = retspec + dirlen - 1;
4629 while (cp1 > retspec)
4634 if (*(cp1-1) != '^')
4639 if (*cp1 == '.') *cp1 = ']';
4641 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4642 memmove(cp1+1,"000000]",7);
4646 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
4647 retspec[retlen] = '\0';
4648 /* Convert last '.' to ']' */
4649 cp1 = retspec+retlen-1;
4650 while (*cp != '[') {
4653 /* Do not trip on extra dots in ODS-5 directories */
4654 if ((cp1 == retspec) || (*(cp1-1) != '^'))
4658 if (*cp1 == '.') *cp1 = ']';
4660 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4661 memmove(cp1+1,"000000]",7);
4665 else { /* This is a top-level dir. Add the MFD to the path. */
4666 if (buf) retspec = buf;
4667 else if (ts) Newx(retspec,retlen+16,char);
4668 else retspec = __fileify_retbuf;
4671 while (*cp1 != ':') *(cp2++) = *(cp1++);
4672 strcpy(cp2,":[000000]");
4677 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4678 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4679 /* We've set up the string up through the filename. Add the
4680 type and version, and we're done. */
4681 strcat(retspec,".DIR;1");
4683 /* $PARSE may have upcased filespec, so convert output to lower
4684 * case if input contained any lowercase characters. */
4685 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
4688 } /* end of do_fileify_dirspec() */
4690 /* External entry points */
4691 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
4692 { return do_fileify_dirspec(dir,buf,0); }
4693 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
4694 { return do_fileify_dirspec(dir,buf,1); }
4696 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
4697 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
4699 static char __pathify_retbuf[NAM$C_MAXRSS+1];
4700 unsigned long int retlen;
4701 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
4702 unsigned short int trnlnm_iter_count;
4706 if (!dir || !*dir) {
4707 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4710 if (*dir) strcpy(trndir,dir);
4711 else getcwd(trndir,sizeof trndir - 1);
4713 trnlnm_iter_count = 0;
4714 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
4715 && my_trnlnm(trndir,trndir,0)) {
4716 trnlnm_iter_count++;
4717 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4718 trnlen = strlen(trndir);
4720 /* Trap simple rooted lnms, and return lnm:[000000] */
4721 if (!strcmp(trndir+trnlen-2,".]")) {
4722 if (buf) retpath = buf;
4723 else if (ts) Newx(retpath,strlen(dir)+10,char);
4724 else retpath = __pathify_retbuf;
4725 strcpy(retpath,dir);
4726 strcat(retpath,":[000000]");
4731 /* At this point we do not work with *dir, but the copy in
4732 * *trndir that is modifiable.
4735 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
4736 if (*trndir == '.' && (*(trndir+1) == '\0' ||
4737 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
4738 retlen = 2 + (*(trndir+1) != '\0');
4740 if ( !(cp1 = strrchr(trndir,'/')) &&
4741 !(cp1 = strrchr(trndir,']')) &&
4742 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
4743 if ((cp2 = strchr(cp1,'.')) != NULL &&
4744 (*(cp2-1) != '/' || /* Trailing '.', '..', */
4745 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
4746 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
4747 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
4750 /* For EFS or ODS-5 look for the last dot */
4751 if (decc_efs_charset) {
4752 cp2 = strrchr(cp1,'.');
4754 if (vms_process_case_tolerant) {
4755 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4756 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4757 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4758 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4759 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4760 (ver || *cp3)))))) {
4762 set_vaxc_errno(RMS$_DIR);
4767 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4768 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4769 !*(cp2+3) || *(cp2+3) != 'R' ||
4770 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4771 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4772 (ver || *cp3)))))) {
4774 set_vaxc_errno(RMS$_DIR);
4778 retlen = cp2 - trndir + 1;
4780 else { /* No file type present. Treat the filename as a directory. */
4781 retlen = strlen(trndir) + 1;
4784 if (buf) retpath = buf;
4785 else if (ts) Newx(retpath,retlen+1,char);
4786 else retpath = __pathify_retbuf;
4787 strncpy(retpath, trndir, retlen-1);
4788 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
4789 retpath[retlen-1] = '/'; /* with '/', add it. */
4790 retpath[retlen] = '\0';
4792 else retpath[retlen-1] = '\0';
4794 else { /* VMS-style directory spec */
4795 char esa[NAM$C_MAXRSS+1], *cp;
4796 unsigned long int sts, cmplen, haslower;
4797 struct FAB dirfab = cc$rms_fab;
4798 struct NAM savnam, dirnam = cc$rms_nam;
4800 /* If we've got an explicit filename, we can just shuffle the string. */
4801 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
4802 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
4803 if ((cp2 = strchr(cp1,'.')) != NULL) {
4805 if (vms_process_case_tolerant) {
4806 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4807 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4808 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4809 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4810 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4811 (ver || *cp3)))))) {
4813 set_vaxc_errno(RMS$_DIR);
4818 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4819 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4820 !*(cp2+3) || *(cp2+3) != 'R' ||
4821 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4822 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4823 (ver || *cp3)))))) {
4825 set_vaxc_errno(RMS$_DIR);
4830 else { /* No file type, so just draw name into directory part */
4831 for (cp2 = cp1; *cp2; cp2++) ;
4834 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
4836 /* We've now got a VMS 'path'; fall through */
4838 dirfab.fab$b_fns = strlen(trndir);
4839 dirfab.fab$l_fna = trndir;
4840 if (trndir[dirfab.fab$b_fns-1] == ']' ||
4841 trndir[dirfab.fab$b_fns-1] == '>' ||
4842 trndir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
4843 if (buf) retpath = buf;
4844 else if (ts) Newx(retpath,strlen(trndir)+1,char);
4845 else retpath = __pathify_retbuf;
4846 strcpy(retpath,trndir);
4849 dirfab.fab$l_dna = ".DIR;1";
4850 dirfab.fab$b_dns = 6;
4851 dirfab.fab$l_nam = &dirnam;
4852 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
4853 dirnam.nam$l_esa = esa;
4854 #ifdef NAM$M_NO_SHORT_UPCASE
4855 if (decc_efs_case_preserve)
4856 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4859 for (cp = trndir; *cp; cp++)
4860 if (islower(*cp)) { haslower = 1; break; }
4862 if (!(sts = (sys$parse(&dirfab)&1))) {
4863 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4864 dirnam.nam$b_nop |= NAM$M_SYNCHK;
4865 sts = sys$parse(&dirfab) & 1;
4869 set_vaxc_errno(dirfab.fab$l_sts);
4875 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
4876 if (dirfab.fab$l_sts != RMS$_FNF) {
4878 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4879 dirfab.fab$b_dns = 0;
4880 sts1 = sys$parse(&dirfab,0,0);
4882 set_vaxc_errno(dirfab.fab$l_sts);
4885 dirnam = savnam; /* No; just work with potential name */
4888 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
4889 /* Yep; check version while we're at it, if it's there. */
4890 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
4891 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
4893 /* Something other than .DIR[;1]. Bzzt. */
4894 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4895 dirfab.fab$b_dns = 0;
4896 sts2 = sys$parse(&dirfab,0,0);
4898 set_vaxc_errno(RMS$_DIR);
4902 /* OK, the type was fine. Now pull any file name into the
4904 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
4906 cp1 = strrchr(esa,'>');
4907 *dirnam.nam$l_type = '>';
4910 *(dirnam.nam$l_type + 1) = '\0';
4911 retlen = dirnam.nam$l_type - esa + 2;
4912 if (buf) retpath = buf;
4913 else if (ts) Newx(retpath,retlen,char);
4914 else retpath = __pathify_retbuf;
4915 strcpy(retpath,esa);
4916 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4917 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4918 /* $PARSE may have upcased filespec, so convert output to lower
4919 * case if input contained any lowercase characters. */
4920 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
4924 } /* end of do_pathify_dirspec() */
4926 /* External entry points */
4927 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
4928 { return do_pathify_dirspec(dir,buf,0); }
4929 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
4930 { return do_pathify_dirspec(dir,buf,1); }
4932 /*{{{ char *tounixspec[_ts](char *spec, char *buf)*/
4933 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
4935 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
4936 char *dirend, *rslt, *cp1, *cp3, tmp[NAM$C_MAXRSS+1];
4938 int devlen, dirlen, retlen = NAM$C_MAXRSS+1;
4939 int expand = 1; /* guarantee room for leading and trailing slashes */
4940 unsigned short int trnlnm_iter_count;
4943 if (spec == NULL) return NULL;
4944 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
4945 if (buf) rslt = buf;
4947 retlen = strlen(spec);
4948 cp1 = strchr(spec,'[');
4949 if (!cp1) cp1 = strchr(spec,'<');
4951 for (cp1++; *cp1; cp1++) {
4952 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
4953 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
4954 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
4957 Newx(rslt,retlen+2+2*expand,char);
4959 else rslt = __tounixspec_retbuf;
4961 /* New VMS specific format needs translation
4962 * glob passes filenames with trailing '\n' and expects this preserved.
4964 if (decc_posix_compliant_pathnames) {
4965 if (strncmp(spec, "\"^UP^", 5) == 0) {
4971 Newx(tunix, VMS_MAXRSS + 1,char);
4972 strcpy(tunix, spec);
4973 tunix_len = strlen(tunix);
4975 if (tunix[tunix_len - 1] == '\n') {
4976 tunix[tunix_len - 1] = '\"';
4977 tunix[tunix_len] = '\0';
4981 uspec = decc$translate_vms(tunix);
4983 if ((int)uspec > 0) {
4989 /* If we can not translate it, makemaker wants as-is */
4997 cmp_rslt = 0; /* Presume VMS */
4998 cp1 = strchr(spec, '/');
5002 /* Look for EFS ^/ */
5003 if (decc_efs_charset) {
5004 while (cp1 != NULL) {
5007 /* Found illegal VMS, assume UNIX */
5012 cp1 = strchr(cp1, '/');
5016 /* Look for "." and ".." */
5017 if (decc_filename_unix_report) {
5018 if (spec[0] == '.') {
5019 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5023 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5029 /* This is already UNIX or at least nothing VMS understands */
5037 dirend = strrchr(spec,']');
5038 if (dirend == NULL) dirend = strrchr(spec,'>');
5039 if (dirend == NULL) dirend = strchr(spec,':');
5040 if (dirend == NULL) {
5045 /* Special case 1 - sys$posix_root = / */
5046 #if __CRTL_VER >= 70000000
5047 if (!decc_disable_posix_root) {
5048 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5056 /* Special case 2 - Convert NLA0: to /dev/null */
5057 #if __CRTL_VER < 70000000
5058 cmp_rslt = strncmp(spec,"NLA0:", 5);
5060 cmp_rslt = strncmp(spec,"nla0:", 5);
5062 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5064 if (cmp_rslt == 0) {
5065 strcpy(rslt, "/dev/null");
5068 if (spec[6] != '\0') {
5075 /* Also handle special case "SYS$SCRATCH:" */
5076 #if __CRTL_VER < 70000000
5077 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5079 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5081 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5083 if (cmp_rslt == 0) {
5086 islnm = my_trnlnm(tmp, "TMP", 0);
5088 strcpy(rslt, "/tmp");
5091 if (spec[12] != '\0') {
5099 if (*cp2 != '[' && *cp2 != '<') {
5102 else { /* the VMS spec begins with directories */
5104 if (*cp2 == ']' || *cp2 == '>') {
5105 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5108 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5109 if (getcwd(tmp,sizeof tmp,1) == NULL) {
5110 if (ts) Safefree(rslt);
5113 trnlnm_iter_count = 0;
5116 while (*cp3 != ':' && *cp3) cp3++;
5118 if (strchr(cp3,']') != NULL) break;
5119 trnlnm_iter_count++;
5120 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5121 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5123 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5124 retlen = devlen + dirlen;
5125 Renew(rslt,retlen+1+2*expand,char);
5131 *(cp1++) = *(cp3++);
5132 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
5136 if ((*cp2 == '^')) {
5137 /* EFS file escape, pass the next character as is */
5138 /* Fix me: HEX encoding for UNICODE not implemented */
5141 else if ( *cp2 == '.') {
5142 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5143 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5149 for (; cp2 <= dirend; cp2++) {
5150 if ((*cp2 == '^')) {
5151 /* EFS file escape, pass the next character as is */
5152 /* Fix me: HEX encoding for UNICODE not implemented */
5158 if (*(cp2+1) == '[') cp2++;
5160 else if (*cp2 == ']' || *cp2 == '>') {
5161 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5163 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5165 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5166 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5167 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5168 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5169 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5171 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5172 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5176 else if (*cp2 == '-') {
5177 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5178 while (*cp2 == '-') {
5180 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5182 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5183 if (ts) Safefree(rslt); /* filespecs like */
5184 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5188 else *(cp1++) = *cp2;
5190 else *(cp1++) = *cp2;
5192 while (*cp2) *(cp1++) = *(cp2++);
5195 /* This still leaves /000000/ when working with a
5196 * VMS device root or concealed root.
5202 ulen = strlen(rslt);
5204 /* Get rid of "000000/ in rooted filespecs */
5206 zeros = strstr(rslt, "/000000/");
5207 if (zeros != NULL) {
5209 mlen = ulen - (zeros - rslt) - 7;
5210 memmove(zeros, &zeros[7], mlen);
5219 } /* end of do_tounixspec() */
5221 /* External entry points */
5222 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
5223 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
5225 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5227 static int posix_to_vmsspec
5228 (char *vmspath, int vmspath_len, const char *unixpath) {
5230 struct FAB myfab = cc$rms_fab;
5231 struct NAML mynam = cc$rms_naml;
5232 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5233 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5239 /* If not a posix spec already, convert it */
5241 unixlen = strlen(unixpath);
5246 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5247 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5250 /* This is already a VMS specification, no conversion */
5252 strncpy(vmspath,unixpath, vmspath_len);
5254 vmspath[vmspath_len] = 0;
5255 if (unixpath[unixlen - 1] == '/')
5257 Newx(esa, VMS_MAXRSS+1, char);
5258 myfab.fab$l_fna = vmspath;
5259 myfab.fab$b_fns = strlen(vmspath);
5260 myfab.fab$l_naml = &mynam;
5261 mynam.naml$l_esa = NULL;
5262 mynam.naml$b_ess = 0;
5263 mynam.naml$l_long_expand = esa;
5264 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS;
5265 mynam.naml$l_rsa = NULL;
5266 mynam.naml$b_rss = 0;
5267 if (decc_efs_case_preserve)
5268 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
5269 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
5271 /* Set up the remaining naml fields */
5272 sts = sys$parse(&myfab);
5274 /* It failed! Try again as a UNIX filespec */
5280 /* get the Device ID and the FID */
5281 sts = sys$search(&myfab);
5282 /* on any failure, returned the POSIX ^UP^ filespec */
5287 specdsc.dsc$a_pointer = vmspath;
5288 specdsc.dsc$w_length = vmspath_len;
5290 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
5291 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
5292 sts = lib$fid_to_name
5293 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
5295 /* on any failure, returned the POSIX ^UP^ filespec */
5297 /* This can happen if user does not have permission to read directories */
5298 if (strncmp(unixpath,"\"^UP^",5) != 0)
5299 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5301 strcpy(vmspath, unixpath);
5304 vmspath[specdsc.dsc$w_length] = 0;
5306 /* Are we expecting a directory? */
5307 if (dir_flag != 0) {
5313 i = specdsc.dsc$w_length - 1;
5317 /* Version must be '1' */
5318 if (vmspath[i--] != '1')
5320 /* Version delimiter is one of ".;" */
5321 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
5324 if (vmspath[i--] != 'R')
5326 if (vmspath[i--] != 'I')
5328 if (vmspath[i--] != 'D')
5330 if (vmspath[i--] != '.')
5332 eptr = &vmspath[i+1];
5334 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
5335 if (vmspath[i-1] != '^') {
5343 /* Get rid of 6 imaginary zero directory filename */
5344 vmspath[i+1] = '\0';
5348 if (vmspath[i] == '0')
5362 /* Can not use LIB$FID_TO_NAME, so doing a manual conversion */
5363 static int posix_to_vmsspec_hardway
5364 (char *vmspath, int vmspath_len, const char *unixpath) {
5367 const char *unixptr;
5369 const char *lastslash;
5370 const char *lastdot;
5381 /* Ignore leading "/" characters */
5382 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
5385 unixlen = strlen(unixptr);
5387 /* Do nothing with blank paths */
5393 lastslash = strrchr(unixptr,'/');
5394 lastdot = strrchr(unixptr,'.');
5397 /* last dot is last dot or past end of string */
5398 if (lastdot == NULL)
5399 lastdot = unixptr + unixlen;
5401 /* if no directories, set last slash to beginning of string */
5402 if (lastslash == NULL) {
5403 lastslash = unixptr;
5406 /* Watch out for trailing "." after last slash, still a directory */
5407 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
5408 lastslash = unixptr + unixlen;
5411 /* Watch out for traiing ".." after last slash, still a directory */
5412 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
5413 lastslash = unixptr + unixlen;
5416 /* dots in directories are aways escaped */
5417 if (lastdot < lastslash)
5418 lastdot = unixptr + unixlen;
5421 /* if (unixptr < lastslash) then we are in a directory */
5429 /* This could have a "^UP^ on the front */
5430 if (strncmp(unixptr,"\"^UP^",5) == 0) {
5435 /* Start with the UNIX path */
5436 if (*unixptr != '/') {
5437 /* relative paths */
5438 if (lastslash > unixptr) {
5441 /* skip leading ./ */
5443 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
5449 /* Are we still in a directory? */
5450 if (unixptr <= lastslash) {
5455 /* if not backing up, then it is relative forward. */
5456 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
5457 ((unixptr[2] == '/') || (unixptr[2] == '\0')))) {
5465 /* Perl wants an empty directory here to tell the difference
5466 * between a DCL commmand and a filename
5475 /* Handle two special files . and .. */
5476 if (unixptr[0] == '.') {
5477 if (unixptr[1] == '\0') {
5484 if ((unixptr[1] == '.') && (unixptr[2] == '\0')) {
5495 else { /* Absolute PATH handling */
5499 /* Need to find out where root is */
5501 /* In theory, this procedure should never get an absolute POSIX pathname
5502 * that can not be found on the POSIX root.
5503 * In practice, that can not be relied on, and things will show up
5504 * here that are a VMS device name or concealed logical name instead.
5505 * So to make things work, this procedure must be tolerant.
5507 Newx(esa, vmspath_len, char);
5510 nextslash = strchr(&unixptr[1],'/');
5512 if (nextslash != NULL) {
5513 seg_len = nextslash - &unixptr[1];
5514 strncpy(vmspath, unixptr, seg_len + 1);
5515 vmspath[seg_len+1] = 0;
5516 sts = posix_to_vmsspec(esa, vmspath_len, vmspath);
5520 /* This is verified to be a real path */
5522 sts = posix_to_vmsspec(esa, vmspath_len, "/");
5523 strcpy(vmspath, esa);
5524 vmslen = strlen(vmspath);
5525 vmsptr = vmspath + vmslen;
5527 if (unixptr < lastslash) {
5536 cmp = strcmp(rptr,"000000.");
5541 } /* removing 6 zeros */
5542 } /* vmslen < 7, no 6 zeros possible */
5543 } /* Not in a directory */
5544 } /* end of verified real path handling */
5549 /* Ok, we have a device or a concealed root that is not in POSIX
5550 * or we have garbage. Make the best of it.
5553 /* Posix to VMS destroyed this, so copy it again */
5554 strncpy(vmspath, &unixptr[1], seg_len);
5555 vmspath[seg_len] = 0;
5557 vmsptr = &vmsptr[vmslen];
5560 /* Now do we need to add the fake 6 zero directory to it? */
5562 if ((*lastslash == '/') && (nextslash < lastslash)) {
5563 /* No there is another directory */
5569 /* now we have foo:bar or foo:[000000]bar to decide from */
5570 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
5571 trnend = islnm ? islnm - 1 : 0;
5573 /* if this was a logical name, ']' or '>' must be present */
5574 /* if not a logical name, then assume a device and hope. */
5575 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
5577 /* if log name and trailing '.' then rooted - treat as device */
5578 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
5580 /* Fix me, if not a logical name, a device lookup should be
5581 * done to see if the device is file structured. If the device
5582 * is not file structured, the 6 zeros should not be put on.
5584 * As it is, perl is occasionally looking for dev:[000000]tty.
5585 * which looks a little strange.
5588 if ((add_6zero == 0) && (*nextslash == '/') && (nextslash[1] == '\0')) {
5589 /* No real directory present */
5594 /* Put the device delimiter on */
5597 unixptr = nextslash;
5600 /* Start directory if needed */
5601 if (!islnm || add_6zero) {
5607 /* add fake 000000] if needed */
5620 } /* non-POSIX translation */
5622 } /* End of relative/absolute path handling */
5624 while ((*unixptr) && (vmslen < vmspath_len)){
5629 if (dir_start != 0) {
5631 /* First characters in a directory are handled special */
5632 while ((*unixptr == '/') ||
5633 ((*unixptr == '.') &&
5634 ((unixptr[1]=='.') || (unixptr[1]=='/') || (unixptr[1]=='\0')))) {
5639 /* Skip redundant / in specification */
5640 while ((*unixptr == '/') && (dir_start != 0)) {
5643 if (unixptr == lastslash)
5646 if (unixptr == lastslash)
5649 /* Skip redundant ./ characters */
5650 while ((*unixptr == '.') &&
5651 ((unixptr[1] == '/')||(unixptr[1] == '\0'))) {
5654 if (unixptr == lastslash)
5656 if (*unixptr == '/')
5659 if (unixptr == lastslash)
5662 /* Skip redundant ../ characters */
5663 while ((*unixptr == '.') && (unixptr[1] == '.') &&
5664 ((unixptr[2] == '/') || (unixptr[2] == '\0'))) {
5665 /* Set the backing up flag */
5671 unixptr++; /* first . */
5672 unixptr++; /* second . */
5673 if (unixptr == lastslash)
5675 if (*unixptr == '/') /* The slash */
5678 if (unixptr == lastslash)
5681 /* To do: Perl expects /.../ to be translated to [...] on VMS */
5682 /* Not needed when VMS is pretending to be UNIX. */
5684 /* Is this loop stuck because of too many dots? */
5685 if (loop_flag == 0) {
5686 /* Exit the loop and pass the rest through */
5691 /* Are we done with directories yet? */
5692 if (unixptr >= lastslash) {
5694 /* Watch out for trailing dots */
5703 if (*unixptr == '/')
5707 /* Have we stopped backing up? */
5712 /* dir_start continues to be = 1 */
5714 if (*unixptr == '-') {
5716 *vmsptr++ = *unixptr++;
5720 /* Now are we done with directories yet? */
5721 if (unixptr >= lastslash) {
5723 /* Watch out for trailing dots */
5739 if (*unixptr == '\0')
5742 /* Normal characters - More EFS work probably needed */
5748 /* remove multiple / */
5749 while (unixptr[1] == '/') {
5752 if (unixptr == lastslash) {
5753 /* Watch out for trailing dots */
5765 /* To do: Perl expects /.../ to be translated to [...] on VMS */
5766 /* Not needed when VMS is pretending to be UNIX. */
5770 if (*unixptr != '\0')
5786 if ((unixptr < lastdot) || (unixptr[1] == '\0')) {
5792 /* trailing dot ==> '^..' on VMS */
5793 if (*unixptr == '\0') {
5797 *vmsptr++ = *unixptr++;
5800 if (quoted && (unixptr[1] == '\0')) {
5805 *vmsptr++ = *unixptr++;
5812 *vmsptr++ = *unixptr++;
5816 if (*unixptr != '\0') {
5817 *vmsptr++ = *unixptr++;
5824 /* Make sure directory is closed */
5825 if (unixptr == lastslash) {
5827 vmsptr2 = vmsptr - 1;
5829 if (*vmsptr2 != ']') {
5832 /* directories do not end in a dot bracket */
5833 if (*vmsptr2 == '.') {
5837 if (*vmsptr2 != '^') {
5838 vmsptr--; /* back up over the dot */
5846 /* Add a trailing dot if a file with no extension */
5847 vmsptr2 = vmsptr - 1;
5848 if ((*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
5849 (*lastdot != '.')) {
5860 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
5861 static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
5862 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
5863 char *rslt, *dirend;
5868 unsigned long int infront = 0, hasdir = 1;
5872 if (path == NULL) return NULL;
5873 rslt_len = VMS_MAXRSS;
5874 if (buf) rslt = buf;
5875 else if (ts) Newx(rslt,NAM$C_MAXRSS+1,char);
5876 else rslt = __tovmsspec_retbuf;
5877 if (strpbrk(path,"]:>") ||
5878 (dirend = strrchr(path,'/')) == NULL) {
5879 if (path[0] == '.') {
5880 if (path[1] == '\0') strcpy(rslt,"[]");
5881 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
5882 else strcpy(rslt,path); /* probably garbage */
5884 else strcpy(rslt,path);
5888 /* Posix specifications are now a native VMS format */
5889 /*--------------------------------------------------*/
5890 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5891 if (decc_posix_compliant_pathnames) {
5892 if (strncmp(path,"\"^UP^",5) == 0) {
5893 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5899 vms_delim = strpbrk(path,"]:>");
5901 if ((vms_delim != NULL) ||
5902 ((dirend = strrchr(path,'/')) == NULL)) {
5904 /* VMS special characters found! */
5906 if (path[0] == '.') {
5907 if (path[1] == '\0') strcpy(rslt,"[]");
5908 else if (path[1] == '.' && path[2] == '\0')
5911 /* Dot preceeding a device or directory ? */
5913 /* If not in POSIX mode, pass it through and hope it works */
5914 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5915 if (!decc_posix_compliant_pathnames)
5916 strcpy(rslt,path); /* probably garbage */
5918 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5920 strcpy(rslt,path); /* probably garbage */
5926 /* If no VMS characters and in POSIX mode, convert it!
5927 * This is the easiest way to get directory specifications
5928 * handled correctly in POSIX mode
5930 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5931 if ((vms_delim == NULL) && decc_posix_compliant_pathnames)
5932 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5934 /* No unix path separators - presume VMS already */
5938 strcpy(rslt,path); /* probably garbage */
5944 /* If POSIX mode active, handle the conversion */
5945 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5946 if (decc_posix_compliant_pathnames) {
5947 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5952 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
5953 if (!*(dirend+2)) dirend +=2;
5954 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
5955 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
5960 lastdot = strrchr(cp2,'.');
5962 char trndev[NAM$C_MAXRSS+1];
5966 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
5968 if (decc_disable_posix_root) {
5969 strcpy(rslt,"sys$disk:[000000]");
5972 strcpy(rslt,"sys$posix_root:[000000]");
5976 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
5978 islnm = my_trnlnm(rslt,trndev,0);
5980 /* DECC special handling */
5982 if (strcmp(rslt,"bin") == 0) {
5983 strcpy(rslt,"sys$system");
5986 islnm = my_trnlnm(rslt,trndev,0);
5988 else if (strcmp(rslt,"tmp") == 0) {
5989 strcpy(rslt,"sys$scratch");
5992 islnm = my_trnlnm(rslt,trndev,0);
5994 else if (!decc_disable_posix_root) {
5995 strcpy(rslt, "sys$posix_root");
5999 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6000 islnm = my_trnlnm(rslt,trndev,0);
6002 else if (strcmp(rslt,"dev") == 0) {
6003 if (strncmp(cp2,"/null", 5) == 0) {
6004 if ((cp2[5] == 0) || (cp2[5] == '/')) {
6005 strcpy(rslt,"NLA0");
6009 islnm = my_trnlnm(rslt,trndev,0);
6015 trnend = islnm ? strlen(trndev) - 1 : 0;
6016 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
6017 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
6018 /* If the first element of the path is a logical name, determine
6019 * whether it has to be translated so we can add more directories. */
6020 if (!islnm || rooted) {
6023 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
6027 if (cp2 != dirend) {
6028 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
6029 strcpy(rslt,trndev);
6030 cp1 = rslt + trnend;
6037 if (decc_disable_posix_root) {
6047 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
6048 cp2 += 2; /* skip over "./" - it's redundant */
6049 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
6051 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6052 *(cp1++) = '-'; /* "../" --> "-" */
6055 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
6056 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
6057 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6058 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
6061 else if ((cp2 != lastdot) || (lastdot < dirend)) {
6062 /* Escape the extra dots in EFS file specifications */
6065 if (cp2 > dirend) cp2 = dirend;
6067 else *(cp1++) = '.';
6069 for (; cp2 < dirend; cp2++) {
6071 if (*(cp2-1) == '/') continue;
6072 if (*(cp1-1) != '.') *(cp1++) = '.';
6075 else if (!infront && *cp2 == '.') {
6076 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
6077 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
6078 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6079 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
6080 else if (*(cp1-2) == '[') *(cp1-1) = '-';
6081 else { /* back up over previous directory name */
6083 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
6084 if (*(cp1-1) == '[') {
6085 memcpy(cp1,"000000.",7);
6090 if (cp2 == dirend) break;
6092 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
6093 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
6094 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
6095 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6097 *(cp1++) = '.'; /* Simulate trailing '/' */
6098 cp2 += 2; /* for loop will incr this to == dirend */
6100 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
6103 if (decc_efs_charset == 0)
6104 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
6106 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
6112 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
6114 if (decc_efs_charset == 0)
6121 else *(cp1++) = *cp2;
6125 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
6126 if (hasdir) *(cp1++) = ']';
6127 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
6128 /* fixme for ODS5 */
6143 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
6144 decc_readdir_dropdotnotype) {
6149 /* trailing dot ==> '^..' on VMS */
6156 *(cp1++) = *(cp2++);
6184 *(cp1++) = *(cp2++);
6187 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
6188 * which is wrong. UNIX notation should be ".dir. unless
6189 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
6190 * changing this behavior could break more things at this time.
6191 * efs character set effectively does not allow "." to be a version
6192 * delimiter as a further complication about changing this.
6194 if (decc_filename_unix_report != 0) {
6197 *(cp1++) = *(cp2++);
6200 *(cp1++) = *(cp2++);
6203 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
6207 /* Fix me for "^]", but that requires making sure that you do
6208 * not back up past the start of the filename
6210 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
6217 } /* end of do_tovmsspec() */
6219 /* External entry points */
6220 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,0); }
6221 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,1); }
6223 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
6224 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts) {
6225 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
6227 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
6229 if (path == NULL) return NULL;
6230 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
6231 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
6232 if (buf) return buf;
6234 vmslen = strlen(vmsified);
6235 Newx(cp,vmslen+1,char);
6236 memcpy(cp,vmsified,vmslen);
6241 strcpy(__tovmspath_retbuf,vmsified);
6242 return __tovmspath_retbuf;
6245 } /* end of do_tovmspath() */
6247 /* External entry points */
6248 char *Perl_tovmspath(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,0); }
6249 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,1); }
6252 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
6253 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts) {
6254 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
6256 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
6258 if (path == NULL) return NULL;
6259 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
6260 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
6261 if (buf) return buf;
6263 unixlen = strlen(unixified);
6264 Newx(cp,unixlen+1,char);
6265 memcpy(cp,unixified,unixlen);
6270 strcpy(__tounixpath_retbuf,unixified);
6271 return __tounixpath_retbuf;
6274 } /* end of do_tounixpath() */
6276 /* External entry points */
6277 char *Perl_tounixpath(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,0); }
6278 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,1); }
6281 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
6283 *****************************************************************************
6285 * Copyright (C) 1989-1994 by *
6286 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
6288 * Permission is hereby granted for the reproduction of this software, *
6289 * on condition that this copyright notice is included in the reproduction, *
6290 * and that such reproduction is not for purposes of profit or material *
6293 * 27-Aug-1994 Modified for inclusion in perl5 *
6294 * by Charles Bailey bailey@newman.upenn.edu *
6295 *****************************************************************************
6299 * getredirection() is intended to aid in porting C programs
6300 * to VMS (Vax-11 C). The native VMS environment does not support
6301 * '>' and '<' I/O redirection, or command line wild card expansion,
6302 * or a command line pipe mechanism using the '|' AND background
6303 * command execution '&'. All of these capabilities are provided to any
6304 * C program which calls this procedure as the first thing in the
6306 * The piping mechanism will probably work with almost any 'filter' type
6307 * of program. With suitable modification, it may useful for other
6308 * portability problems as well.
6310 * Author: Mark Pizzolato mark@infocomm.com
6314 struct list_item *next;
6318 static void add_item(struct list_item **head,
6319 struct list_item **tail,
6323 static void mp_expand_wild_cards(pTHX_ char *item,
6324 struct list_item **head,
6325 struct list_item **tail,
6328 static int background_process(pTHX_ int argc, char **argv);
6330 static void pipe_and_fork(pTHX_ char **cmargv);
6332 /*{{{ void getredirection(int *ac, char ***av)*/
6334 mp_getredirection(pTHX_ int *ac, char ***av)
6336 * Process vms redirection arg's. Exit if any error is seen.
6337 * If getredirection() processes an argument, it is erased
6338 * from the vector. getredirection() returns a new argc and argv value.
6339 * In the event that a background command is requested (by a trailing "&"),
6340 * this routine creates a background subprocess, and simply exits the program.
6342 * Warning: do not try to simplify the code for vms. The code
6343 * presupposes that getredirection() is called before any data is
6344 * read from stdin or written to stdout.
6346 * Normal usage is as follows:
6352 * getredirection(&argc, &argv);
6356 int argc = *ac; /* Argument Count */
6357 char **argv = *av; /* Argument Vector */
6358 char *ap; /* Argument pointer */
6359 int j; /* argv[] index */
6360 int item_count = 0; /* Count of Items in List */
6361 struct list_item *list_head = 0; /* First Item in List */
6362 struct list_item *list_tail; /* Last Item in List */
6363 char *in = NULL; /* Input File Name */
6364 char *out = NULL; /* Output File Name */
6365 char *outmode = "w"; /* Mode to Open Output File */
6366 char *err = NULL; /* Error File Name */
6367 char *errmode = "w"; /* Mode to Open Error File */
6368 int cmargc = 0; /* Piped Command Arg Count */
6369 char **cmargv = NULL;/* Piped Command Arg Vector */
6372 * First handle the case where the last thing on the line ends with
6373 * a '&'. This indicates the desire for the command to be run in a
6374 * subprocess, so we satisfy that desire.
6377 if (0 == strcmp("&", ap))
6378 exit(background_process(aTHX_ --argc, argv));
6379 if (*ap && '&' == ap[strlen(ap)-1])
6381 ap[strlen(ap)-1] = '\0';
6382 exit(background_process(aTHX_ argc, argv));
6385 * Now we handle the general redirection cases that involve '>', '>>',
6386 * '<', and pipes '|'.
6388 for (j = 0; j < argc; ++j)
6390 if (0 == strcmp("<", argv[j]))
6394 fprintf(stderr,"No input file after < on command line");
6395 exit(LIB$_WRONUMARG);
6400 if ('<' == *(ap = argv[j]))
6405 if (0 == strcmp(">", ap))
6409 fprintf(stderr,"No output file after > on command line");
6410 exit(LIB$_WRONUMARG);
6429 fprintf(stderr,"No output file after > or >> on command line");
6430 exit(LIB$_WRONUMARG);
6434 if (('2' == *ap) && ('>' == ap[1]))
6451 fprintf(stderr,"No output file after 2> or 2>> on command line");
6452 exit(LIB$_WRONUMARG);
6456 if (0 == strcmp("|", argv[j]))
6460 fprintf(stderr,"No command into which to pipe on command line");
6461 exit(LIB$_WRONUMARG);
6463 cmargc = argc-(j+1);
6464 cmargv = &argv[j+1];
6468 if ('|' == *(ap = argv[j]))
6476 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
6479 * Allocate and fill in the new argument vector, Some Unix's terminate
6480 * the list with an extra null pointer.
6482 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
6484 for (j = 0; j < item_count; ++j, list_head = list_head->next)
6485 argv[j] = list_head->value;
6491 fprintf(stderr,"'|' and '>' may not both be specified on command line");
6492 exit(LIB$_INVARGORD);
6494 pipe_and_fork(aTHX_ cmargv);
6497 /* Check for input from a pipe (mailbox) */
6499 if (in == NULL && 1 == isapipe(0))
6501 char mbxname[L_tmpnam];
6503 long int dvi_item = DVI$_DEVBUFSIZ;
6504 $DESCRIPTOR(mbxnam, "");
6505 $DESCRIPTOR(mbxdevnam, "");
6507 /* Input from a pipe, reopen it in binary mode to disable */
6508 /* carriage control processing. */
6510 fgetname(stdin, mbxname);
6511 mbxnam.dsc$a_pointer = mbxname;
6512 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
6513 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
6514 mbxdevnam.dsc$a_pointer = mbxname;
6515 mbxdevnam.dsc$w_length = sizeof(mbxname);
6516 dvi_item = DVI$_DEVNAM;
6517 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
6518 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
6521 freopen(mbxname, "rb", stdin);
6524 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
6528 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
6530 fprintf(stderr,"Can't open input file %s as stdin",in);
6533 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
6535 fprintf(stderr,"Can't open output file %s as stdout",out);
6538 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
6541 if (strcmp(err,"&1") == 0) {
6542 dup2(fileno(stdout), fileno(stderr));
6543 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
6546 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
6548 fprintf(stderr,"Can't open error file %s as stderr",err);
6552 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
6556 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
6559 #ifdef ARGPROC_DEBUG
6560 PerlIO_printf(Perl_debug_log, "Arglist:\n");
6561 for (j = 0; j < *ac; ++j)
6562 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
6564 /* Clear errors we may have hit expanding wildcards, so they don't
6565 show up in Perl's $! later */
6566 set_errno(0); set_vaxc_errno(1);
6567 } /* end of getredirection() */
6570 static void add_item(struct list_item **head,
6571 struct list_item **tail,
6577 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
6581 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
6582 *tail = (*tail)->next;
6584 (*tail)->value = value;
6588 static void mp_expand_wild_cards(pTHX_ char *item,
6589 struct list_item **head,
6590 struct list_item **tail,
6594 unsigned long int context = 0;
6601 char vmsspec[NAM$C_MAXRSS+1];
6602 $DESCRIPTOR(filespec, "");
6603 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
6604 $DESCRIPTOR(resultspec, "");
6605 unsigned long int zero = 0, sts;
6607 for (cp = item; *cp; cp++) {
6608 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
6609 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
6611 if (!*cp || isspace(*cp))
6613 add_item(head, tail, item, count);
6618 /* "double quoted" wild card expressions pass as is */
6619 /* From DCL that means using e.g.: */
6620 /* perl program """perl.*""" */
6621 item_len = strlen(item);
6622 if ( '"' == *item && '"' == item[item_len-1] )
6625 item[item_len-2] = '\0';
6626 add_item(head, tail, item, count);
6630 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
6631 resultspec.dsc$b_class = DSC$K_CLASS_D;
6632 resultspec.dsc$a_pointer = NULL;
6633 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
6634 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
6635 if (!isunix || !filespec.dsc$a_pointer)
6636 filespec.dsc$a_pointer = item;
6637 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
6639 * Only return version specs, if the caller specified a version
6641 had_version = strchr(item, ';');
6643 * Only return device and directory specs, if the caller specifed either.
6645 had_device = strchr(item, ':');
6646 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
6648 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
6649 &defaultspec, 0, 0, &zero))))
6654 Newx(string,resultspec.dsc$w_length+1,char);
6655 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
6656 string[resultspec.dsc$w_length] = '\0';
6657 if (NULL == had_version)
6658 *(strrchr(string, ';')) = '\0';
6659 if ((!had_directory) && (had_device == NULL))
6661 if (NULL == (devdir = strrchr(string, ']')))
6662 devdir = strrchr(string, '>');
6663 strcpy(string, devdir + 1);
6666 * Be consistent with what the C RTL has already done to the rest of
6667 * the argv items and lowercase all of these names.
6669 if (!decc_efs_case_preserve) {
6670 for (c = string; *c; ++c)
6674 if (isunix) trim_unixpath(string,item,1);
6675 add_item(head, tail, string, count);
6678 if (sts != RMS$_NMF)
6680 set_vaxc_errno(sts);
6683 case RMS$_FNF: case RMS$_DNF:
6684 set_errno(ENOENT); break;
6686 set_errno(ENOTDIR); break;
6688 set_errno(ENODEV); break;
6689 case RMS$_FNM: case RMS$_SYN:
6690 set_errno(EINVAL); break;
6692 set_errno(EACCES); break;
6694 _ckvmssts_noperl(sts);
6698 add_item(head, tail, item, count);
6699 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
6700 _ckvmssts_noperl(lib$find_file_end(&context));
6703 static int child_st[2];/* Event Flag set when child process completes */
6705 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
6707 static unsigned long int exit_handler(int *status)
6711 if (0 == child_st[0])
6713 #ifdef ARGPROC_DEBUG
6714 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
6716 fflush(stdout); /* Have to flush pipe for binary data to */
6717 /* terminate properly -- <tp@mccall.com> */
6718 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
6719 sys$dassgn(child_chan);
6721 sys$synch(0, child_st);
6726 static void sig_child(int chan)
6728 #ifdef ARGPROC_DEBUG
6729 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
6731 if (child_st[0] == 0)
6735 static struct exit_control_block exit_block =
6740 &exit_block.exit_status,
6745 pipe_and_fork(pTHX_ char **cmargv)
6748 struct dsc$descriptor_s *vmscmd;
6749 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
6750 int sts, j, l, ismcr, quote, tquote = 0;
6752 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
6753 vms_execfree(vmscmd);
6758 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
6759 && toupper(*(q+2)) == 'R' && !*(q+3);
6761 while (q && l < MAX_DCL_LINE_LENGTH) {
6763 if (j > 0 && quote) {
6769 if (ismcr && j > 1) quote = 1;
6770 tquote = (strchr(q,' ')) != NULL || *q == '\0';
6773 if (quote || tquote) {
6779 if ((quote||tquote) && *q == '"') {
6789 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
6791 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
6795 static int background_process(pTHX_ int argc, char **argv)
6797 char command[2048] = "$";
6798 $DESCRIPTOR(value, "");
6799 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
6800 static $DESCRIPTOR(null, "NLA0:");
6801 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
6803 $DESCRIPTOR(pidstr, "");
6805 unsigned long int flags = 17, one = 1, retsts;
6807 strcat(command, argv[0]);
6810 strcat(command, " \"");
6811 strcat(command, *(++argv));
6812 strcat(command, "\"");
6814 value.dsc$a_pointer = command;
6815 value.dsc$w_length = strlen(value.dsc$a_pointer);
6816 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
6817 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
6818 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
6819 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
6822 _ckvmssts_noperl(retsts);
6824 #ifdef ARGPROC_DEBUG
6825 PerlIO_printf(Perl_debug_log, "%s\n", command);
6827 sprintf(pidstring, "%08X", pid);
6828 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
6829 pidstr.dsc$a_pointer = pidstring;
6830 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
6831 lib$set_symbol(&pidsymbol, &pidstr);
6835 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
6838 /* OS-specific initialization at image activation (not thread startup) */
6839 /* Older VAXC header files lack these constants */
6840 #ifndef JPI$_RIGHTS_SIZE
6841 # define JPI$_RIGHTS_SIZE 817
6843 #ifndef KGB$M_SUBSYSTEM
6844 # define KGB$M_SUBSYSTEM 0x8
6847 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
6849 /*{{{void vms_image_init(int *, char ***)*/
6851 vms_image_init(int *argcp, char ***argvp)
6853 char eqv[LNM$C_NAMLENGTH+1] = "";
6854 unsigned int len, tabct = 8, tabidx = 0;
6855 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
6856 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
6857 unsigned short int dummy, rlen;
6858 struct dsc$descriptor_s **tabvec;
6859 #if defined(PERL_IMPLICIT_CONTEXT)
6862 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
6863 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
6864 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
6867 #ifdef KILL_BY_SIGPRC
6868 Perl_csighandler_init();
6871 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
6872 _ckvmssts_noperl(iosb[0]);
6873 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
6874 if (iprv[i]) { /* Running image installed with privs? */
6875 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
6880 /* Rights identifiers might trigger tainting as well. */
6881 if (!will_taint && (rlen || rsz)) {
6882 while (rlen < rsz) {
6883 /* We didn't get all the identifiers on the first pass. Allocate a
6884 * buffer much larger than $GETJPI wants (rsz is size in bytes that
6885 * were needed to hold all identifiers at time of last call; we'll
6886 * allocate that many unsigned long ints), and go back and get 'em.
6887 * If it gave us less than it wanted to despite ample buffer space,
6888 * something's broken. Is your system missing a system identifier?
6890 if (rsz <= jpilist[1].buflen) {
6891 /* Perl_croak accvios when used this early in startup. */
6892 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
6893 rsz, (unsigned long) jpilist[1].buflen,
6894 "Check your rights database for corruption.\n");
6897 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
6898 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
6899 jpilist[1].buflen = rsz * sizeof(unsigned long int);
6900 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
6901 _ckvmssts_noperl(iosb[0]);
6903 mask = jpilist[1].bufadr;
6904 /* Check attribute flags for each identifier (2nd longword); protected
6905 * subsystem identifiers trigger tainting.
6907 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
6908 if (mask[i] & KGB$M_SUBSYSTEM) {
6913 if (mask != rlst) Safefree(mask);
6916 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
6917 * logical, some versions of the CRTL will add a phanthom /000000/
6918 * directory. This needs to be removed.
6920 if (decc_filename_unix_report) {
6923 ulen = strlen(argvp[0][0]);
6925 zeros = strstr(argvp[0][0], "/000000/");
6926 if (zeros != NULL) {
6928 mlen = ulen - (zeros - argvp[0][0]) - 7;
6929 memmove(zeros, &zeros[7], mlen);
6931 argvp[0][0][ulen] = '\0';
6934 /* It also may have a trailing dot that needs to be removed otherwise
6935 * it will be converted to VMS mode incorrectly.
6938 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
6939 argvp[0][0][ulen] = '\0';
6942 /* We need to use this hack to tell Perl it should run with tainting,
6943 * since its tainting flag may be part of the PL_curinterp struct, which
6944 * hasn't been allocated when vms_image_init() is called.
6947 char **newargv, **oldargv;
6949 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
6950 newargv[0] = oldargv[0];
6951 newargv[1] = (char *) PerlMem_malloc(3 * sizeof(char));
6952 strcpy(newargv[1], "-T");
6953 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
6955 newargv[*argcp] = NULL;
6956 /* We orphan the old argv, since we don't know where it's come from,
6957 * so we don't know how to free it.
6961 else { /* Did user explicitly request tainting? */
6963 char *cp, **av = *argvp;
6964 for (i = 1; i < *argcp; i++) {
6965 if (*av[i] != '-') break;
6966 for (cp = av[i]+1; *cp; cp++) {
6967 if (*cp == 'T') { will_taint = 1; break; }
6968 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
6969 strchr("DFIiMmx",*cp)) break;
6971 if (will_taint) break;
6976 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
6978 if (!tabidx) tabvec = (struct dsc$descriptor_s **) PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
6979 else if (tabidx >= tabct) {
6981 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
6983 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
6984 tabvec[tabidx]->dsc$w_length = 0;
6985 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
6986 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
6987 tabvec[tabidx]->dsc$a_pointer = NULL;
6988 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
6990 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
6992 getredirection(argcp,argvp);
6993 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
6995 # include <reentrancy.h>
6996 decc$set_reentrancy(C$C_MULTITHREAD);
7005 * Trim Unix-style prefix off filespec, so it looks like what a shell
7006 * glob expansion would return (i.e. from specified prefix on, not
7007 * full path). Note that returned filespec is Unix-style, regardless
7008 * of whether input filespec was VMS-style or Unix-style.
7010 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
7011 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
7012 * vector of options; at present, only bit 0 is used, and if set tells
7013 * trim unixpath to try the current default directory as a prefix when
7014 * presented with a possibly ambiguous ... wildcard.
7016 * Returns !=0 on success, with trimmed filespec replacing contents of
7017 * fspec, and 0 on failure, with contents of fpsec unchanged.
7019 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
7021 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
7023 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
7024 *template, *base, *end, *cp1, *cp2;
7025 register int tmplen, reslen = 0, dirs = 0;
7027 if (!wildspec || !fspec) return 0;
7028 template = unixwild;
7029 if (strpbrk(wildspec,"]>:") != NULL) {
7030 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
7033 strncpy(unixwild, wildspec, NAM$C_MAXRSS);
7034 unixwild[NAM$C_MAXRSS] = 0;
7036 if (strpbrk(fspec,"]>:") != NULL) {
7037 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
7038 else base = unixified;
7039 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
7040 * check to see that final result fits into (isn't longer than) fspec */
7041 reslen = strlen(fspec);
7045 /* No prefix or absolute path on wildcard, so nothing to remove */
7046 if (!*template || *template == '/') {
7047 if (base == fspec) return 1;
7048 tmplen = strlen(unixified);
7049 if (tmplen > reslen) return 0; /* not enough space */
7050 /* Copy unixified resultant, including trailing NUL */
7051 memmove(fspec,unixified,tmplen+1);
7055 for (end = base; *end; end++) ; /* Find end of resultant filespec */
7056 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
7057 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
7058 for (cp1 = end ;cp1 >= base; cp1--)
7059 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
7061 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
7065 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
7066 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
7067 int ells = 1, totells, segdirs, match;
7068 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
7069 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7071 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
7073 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
7074 if (ellipsis == template && opts & 1) {
7075 /* Template begins with an ellipsis. Since we can't tell how many
7076 * directory names at the front of the resultant to keep for an
7077 * arbitrary starting point, we arbitrarily choose the current
7078 * default directory as a starting point. If it's there as a prefix,
7079 * clip it off. If not, fall through and act as if the leading
7080 * ellipsis weren't there (i.e. return shortest possible path that
7081 * could match template).
7083 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
7084 if (!decc_efs_case_preserve) {
7085 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7086 if (_tolower(*cp1) != _tolower(*cp2)) break;
7088 segdirs = dirs - totells; /* Min # of dirs we must have left */
7089 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
7090 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
7091 memmove(fspec,cp2+1,end - cp2);
7095 /* First off, back up over constant elements at end of path */
7097 for (front = end ; front >= base; front--)
7098 if (*front == '/' && !dirs--) { front++; break; }
7100 if (!decc_efs_case_preserve) {
7101 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
7102 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
7104 if (cp1 != '\0') return 0; /* Path too long. */
7106 *cp2 = '\0'; /* Pick up with memcpy later */
7107 lcfront = lcres + (front - base);
7108 /* Now skip over each ellipsis and try to match the path in front of it. */
7110 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
7111 if (*(cp1) == '.' && *(cp1+1) == '.' &&
7112 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
7113 if (cp1 < template) break; /* template started with an ellipsis */
7114 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
7115 ellipsis = cp1; continue;
7117 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
7119 for (segdirs = 0, cp2 = tpl;
7120 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
7122 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
7124 if (!decc_efs_case_preserve) {
7125 *cp2 = _tolower(*cp1); /* else lowercase for match */
7128 *cp2 = *cp1; /* else preserve case for match */
7131 if (*cp2 == '/') segdirs++;
7133 if (cp1 != ellipsis - 1) return 0; /* Path too long */
7134 /* Back up at least as many dirs as in template before matching */
7135 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
7136 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
7137 for (match = 0; cp1 > lcres;) {
7138 resdsc.dsc$a_pointer = cp1;
7139 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
7141 if (match == 1) lcfront = cp1;
7143 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
7145 if (!match) return 0; /* Can't find prefix ??? */
7146 if (match > 1 && opts & 1) {
7147 /* This ... wildcard could cover more than one set of dirs (i.e.
7148 * a set of similar dir names is repeated). If the template
7149 * contains more than 1 ..., upstream elements could resolve the
7150 * ambiguity, but it's not worth a full backtracking setup here.
7151 * As a quick heuristic, clip off the current default directory
7152 * if it's present to find the trimmed spec, else use the
7153 * shortest string that this ... could cover.
7155 char def[NAM$C_MAXRSS+1], *st;
7157 if (getcwd(def, sizeof def,0) == NULL) return 0;
7158 if (!decc_efs_case_preserve) {
7159 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7160 if (_tolower(*cp1) != _tolower(*cp2)) break;
7162 segdirs = dirs - totells; /* Min # of dirs we must have left */
7163 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
7164 if (*cp1 == '\0' && *cp2 == '/') {
7165 memmove(fspec,cp2+1,end - cp2);
7168 /* Nope -- stick with lcfront from above and keep going. */
7171 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
7176 } /* end of trim_unixpath() */
7181 * VMS readdir() routines.
7182 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
7184 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
7185 * Minor modifications to original routines.
7188 /* readdir may have been redefined by reentr.h, so make sure we get
7189 * the local version for what we do here.
7194 #if !defined(PERL_IMPLICIT_CONTEXT)
7195 # define readdir Perl_readdir
7197 # define readdir(a) Perl_readdir(aTHX_ a)
7200 /* Number of elements in vms_versions array */
7201 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
7204 * Open a directory, return a handle for later use.
7206 /*{{{ DIR *opendir(char*name) */
7208 Perl_opendir(pTHX_ const char *name)
7211 char dir[NAM$C_MAXRSS+1];
7214 if (do_tovmspath(name,dir,0) == NULL) {
7217 /* Check access before stat; otherwise stat does not
7218 * accurately report whether it's a directory.
7220 if (!cando_by_name(S_IRUSR,0,dir)) {
7221 /* cando_by_name has already set errno */
7224 if (flex_stat(dir,&sb) == -1) return NULL;
7225 if (!S_ISDIR(sb.st_mode)) {
7226 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
7229 /* Get memory for the handle, and the pattern. */
7231 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
7233 /* Fill in the fields; mainly playing with the descriptor. */
7234 sprintf(dd->pattern, "%s*.*",dir);
7237 dd->vms_wantversions = 0;
7238 dd->pat.dsc$a_pointer = dd->pattern;
7239 dd->pat.dsc$w_length = strlen(dd->pattern);
7240 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
7241 dd->pat.dsc$b_class = DSC$K_CLASS_S;
7242 #if defined(USE_ITHREADS)
7243 Newx(dd->mutex,1,perl_mutex);
7244 MUTEX_INIT( (perl_mutex *) dd->mutex );
7250 } /* end of opendir() */
7254 * Set the flag to indicate we want versions or not.
7256 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
7258 vmsreaddirversions(MY_DIR *dd, int flag)
7260 dd->vms_wantversions = flag;
7265 * Free up an opened directory.
7267 /*{{{ void closedir(DIR *dd)*/
7269 Perl_closedir(MY_DIR *dd)
7273 sts = lib$find_file_end(&dd->context);
7274 Safefree(dd->pattern);
7275 #if defined(USE_ITHREADS)
7276 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
7277 Safefree(dd->mutex);
7284 * Collect all the version numbers for the current file.
7287 collectversions(pTHX_ MY_DIR *dd)
7289 struct dsc$descriptor_s pat;
7290 struct dsc$descriptor_s res;
7291 struct my_dirent *e;
7292 char *p, *text, buff[sizeof dd->entry.d_name];
7294 unsigned long context, tmpsts;
7296 /* Convenient shorthand. */
7299 /* Add the version wildcard, ignoring the "*.*" put on before */
7300 i = strlen(dd->pattern);
7301 Newx(text,i + e->d_namlen + 3,char);
7302 strcpy(text, dd->pattern);
7303 sprintf(&text[i - 3], "%s;*", e->d_name);
7305 /* Set up the pattern descriptor. */
7306 pat.dsc$a_pointer = text;
7307 pat.dsc$w_length = i + e->d_namlen - 1;
7308 pat.dsc$b_dtype = DSC$K_DTYPE_T;
7309 pat.dsc$b_class = DSC$K_CLASS_S;
7311 /* Set up result descriptor. */
7312 res.dsc$a_pointer = buff;
7313 res.dsc$w_length = sizeof buff - 2;
7314 res.dsc$b_dtype = DSC$K_DTYPE_T;
7315 res.dsc$b_class = DSC$K_CLASS_S;
7317 /* Read files, collecting versions. */
7318 for (context = 0, e->vms_verscount = 0;
7319 e->vms_verscount < VERSIZE(e);
7320 e->vms_verscount++) {
7321 tmpsts = lib$find_file(&pat, &res, &context);
7322 if (tmpsts == RMS$_NMF || context == 0) break;
7324 buff[sizeof buff - 1] = '\0';
7325 if ((p = strchr(buff, ';')))
7326 e->vms_versions[e->vms_verscount] = atoi(p + 1);
7328 e->vms_versions[e->vms_verscount] = -1;
7331 _ckvmssts(lib$find_file_end(&context));
7334 } /* end of collectversions() */
7337 * Read the next entry from the directory.
7339 /*{{{ struct dirent *readdir(DIR *dd)*/
7341 Perl_readdir(pTHX_ MY_DIR *dd)
7343 struct dsc$descriptor_s res;
7344 char *p, buff[sizeof dd->entry.d_name];
7345 unsigned long int tmpsts;
7347 /* Set up result descriptor, and get next file. */
7348 res.dsc$a_pointer = buff;
7349 res.dsc$w_length = sizeof buff - 2;
7350 res.dsc$b_dtype = DSC$K_DTYPE_T;
7351 res.dsc$b_class = DSC$K_CLASS_S;
7352 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
7353 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
7354 if (!(tmpsts & 1)) {
7355 set_vaxc_errno(tmpsts);
7358 set_errno(EACCES); break;
7360 set_errno(ENODEV); break;
7362 set_errno(ENOTDIR); break;
7363 case RMS$_FNF: case RMS$_DNF:
7364 set_errno(ENOENT); break;
7371 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
7372 if (!decc_efs_case_preserve) {
7373 buff[sizeof buff - 1] = '\0';
7374 for (p = buff; *p; p++) *p = _tolower(*p);
7375 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7379 /* we don't want to force to lowercase, just null terminate */
7380 buff[res.dsc$w_length] = '\0';
7382 for (p = buff; *p; p++) *p = _tolower(*p);
7383 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7386 /* Skip any directory component and just copy the name. */
7387 if ((p = strchr(buff, ']'))) strcpy(dd->entry.d_name, p + 1);
7388 else strcpy(dd->entry.d_name, buff);
7390 /* Clobber the version. */
7391 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
7393 dd->entry.d_namlen = strlen(dd->entry.d_name);
7394 dd->entry.vms_verscount = 0;
7395 if (dd->vms_wantversions) collectversions(aTHX_ dd);
7398 } /* end of readdir() */
7402 * Read the next entry from the directory -- thread-safe version.
7404 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
7406 Perl_readdir_r(pTHX_ MY_DIR *dd, struct my_dirent *entry, struct my_dirent **result)
7410 MUTEX_LOCK( (perl_mutex *) dd->mutex );
7412 entry = readdir(dd);
7414 retval = ( *result == NULL ? errno : 0 );
7416 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
7420 } /* end of readdir_r() */
7424 * Return something that can be used in a seekdir later.
7426 /*{{{ long telldir(DIR *dd)*/
7428 Perl_telldir(MY_DIR *dd)
7435 * Return to a spot where we used to be. Brute force.
7437 /*{{{ void seekdir(DIR *dd,long count)*/
7439 Perl_seekdir(pTHX_ MY_DIR *dd, long count)
7441 int vms_wantversions;
7443 /* If we haven't done anything yet... */
7447 /* Remember some state, and clear it. */
7448 vms_wantversions = dd->vms_wantversions;
7449 dd->vms_wantversions = 0;
7450 _ckvmssts(lib$find_file_end(&dd->context));
7453 /* The increment is in readdir(). */
7454 for (dd->count = 0; dd->count < count; )
7457 dd->vms_wantversions = vms_wantversions;
7459 } /* end of seekdir() */
7462 /* VMS subprocess management
7464 * my_vfork() - just a vfork(), after setting a flag to record that
7465 * the current script is trying a Unix-style fork/exec.
7467 * vms_do_aexec() and vms_do_exec() are called in response to the
7468 * perl 'exec' function. If this follows a vfork call, then they
7469 * call out the regular perl routines in doio.c which do an
7470 * execvp (for those who really want to try this under VMS).
7471 * Otherwise, they do exactly what the perl docs say exec should
7472 * do - terminate the current script and invoke a new command
7473 * (See below for notes on command syntax.)
7475 * do_aspawn() and do_spawn() implement the VMS side of the perl
7476 * 'system' function.
7478 * Note on command arguments to perl 'exec' and 'system': When handled
7479 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
7480 * are concatenated to form a DCL command string. If the first arg
7481 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
7482 * the command string is handed off to DCL directly. Otherwise,
7483 * the first token of the command is taken as the filespec of an image
7484 * to run. The filespec is expanded using a default type of '.EXE' and
7485 * the process defaults for device, directory, etc., and if found, the resultant
7486 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
7487 * the command string as parameters. This is perhaps a bit complicated,
7488 * but I hope it will form a happy medium between what VMS folks expect
7489 * from lib$spawn and what Unix folks expect from exec.
7492 static int vfork_called;
7494 /*{{{int my_vfork()*/
7505 vms_execfree(struct dsc$descriptor_s *vmscmd)
7508 if (vmscmd->dsc$a_pointer) {
7509 Safefree(vmscmd->dsc$a_pointer);
7516 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
7518 char *junk, *tmps = Nullch;
7519 register size_t cmdlen = 0;
7526 tmps = SvPV(really,rlen);
7533 for (idx++; idx <= sp; idx++) {
7535 junk = SvPVx(*idx,rlen);
7536 cmdlen += rlen ? rlen + 1 : 0;
7539 Newx(PL_Cmd,cmdlen+1,char);
7541 if (tmps && *tmps) {
7542 strcpy(PL_Cmd,tmps);
7545 else *PL_Cmd = '\0';
7546 while (++mark <= sp) {
7548 char *s = SvPVx(*mark,n_a);
7550 if (*PL_Cmd) strcat(PL_Cmd," ");
7556 } /* end of setup_argstr() */
7559 static unsigned long int
7560 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
7561 struct dsc$descriptor_s **pvmscmd)
7563 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
7564 char image_name[NAM$C_MAXRSS+1];
7565 char image_argv[NAM$C_MAXRSS+1];
7566 $DESCRIPTOR(defdsc,".EXE");
7567 $DESCRIPTOR(defdsc2,".");
7568 $DESCRIPTOR(resdsc,resspec);
7569 struct dsc$descriptor_s *vmscmd;
7570 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7571 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
7572 register char *s, *rest, *cp, *wordbreak;
7577 Newx(vmscmd,sizeof(struct dsc$descriptor_s),struct dsc$descriptor_s);
7579 /* Make a copy for modification */
7580 cmdlen = strlen(incmd);
7581 Newx(cmd, cmdlen+1, char);
7582 strncpy(cmd, incmd, cmdlen);
7587 vmscmd->dsc$a_pointer = NULL;
7588 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
7589 vmscmd->dsc$b_class = DSC$K_CLASS_S;
7590 vmscmd->dsc$w_length = 0;
7591 if (pvmscmd) *pvmscmd = vmscmd;
7593 if (suggest_quote) *suggest_quote = 0;
7595 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
7596 return CLI$_BUFOVF; /* continuation lines currently unsupported */
7602 while (*s && isspace(*s)) s++;
7604 if (*s == '@' || *s == '$') {
7605 vmsspec[0] = *s; rest = s + 1;
7606 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
7608 else { cp = vmsspec; rest = s; }
7609 if (*rest == '.' || *rest == '/') {
7612 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
7613 rest++, cp2++) *cp2 = *rest;
7615 if (do_tovmsspec(resspec,cp,0)) {
7618 for (cp2 = vmsspec + strlen(vmsspec);
7619 *rest && cp2 - vmsspec < sizeof vmsspec;
7620 rest++, cp2++) *cp2 = *rest;
7625 /* Intuit whether verb (first word of cmd) is a DCL command:
7626 * - if first nonspace char is '@', it's a DCL indirection
7628 * - if verb contains a filespec separator, it's not a DCL command
7629 * - if it doesn't, caller tells us whether to default to a DCL
7630 * command, or to a local image unless told it's DCL (by leading '$')
7634 if (suggest_quote) *suggest_quote = 1;
7636 register char *filespec = strpbrk(s,":<[.;");
7637 rest = wordbreak = strpbrk(s," \"\t/");
7638 if (!wordbreak) wordbreak = s + strlen(s);
7639 if (*s == '$') check_img = 0;
7640 if (filespec && (filespec < wordbreak)) isdcl = 0;
7641 else isdcl = !check_img;
7645 imgdsc.dsc$a_pointer = s;
7646 imgdsc.dsc$w_length = wordbreak - s;
7647 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
7649 _ckvmssts(lib$find_file_end(&cxt));
7650 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
7651 if (!(retsts & 1) && *s == '$') {
7652 _ckvmssts(lib$find_file_end(&cxt));
7653 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
7654 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
7656 _ckvmssts(lib$find_file_end(&cxt));
7657 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
7661 _ckvmssts(lib$find_file_end(&cxt));
7666 while (*s && !isspace(*s)) s++;
7669 /* check that it's really not DCL with no file extension */
7670 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
7672 char b[256] = {0,0,0,0};
7673 read(fileno(fp), b, 256);
7674 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
7678 /* Check for script */
7680 if ((b[0] == '#') && (b[1] == '!'))
7682 #ifdef ALTERNATE_SHEBANG
7684 shebang_len = strlen(ALTERNATE_SHEBANG);
7685 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
7687 perlstr = strstr("perl",b);
7688 if (perlstr == NULL)
7696 if (shebang_len > 0) {
7699 char tmpspec[NAM$C_MAXRSS + 1];
7702 /* Image is following after white space */
7703 /*--------------------------------------*/
7704 while (isprint(b[i]) && isspace(b[i]))
7708 while (isprint(b[i]) && !isspace(b[i])) {
7709 tmpspec[j++] = b[i++];
7710 if (j >= NAM$C_MAXRSS)
7715 /* There may be some default parameters to the image */
7716 /*---------------------------------------------------*/
7718 while (isprint(b[i])) {
7719 image_argv[j++] = b[i++];
7720 if (j >= NAM$C_MAXRSS)
7723 while ((j > 0) && !isprint(image_argv[j-1]))
7727 /* It will need to be converted to VMS format and validated */
7728 if (tmpspec[0] != '\0') {
7731 /* Try to find the exact program requested to be run */
7732 /*---------------------------------------------------*/
7733 iname = do_rmsexpand
7734 (tmpspec, image_name, 0, ".exe", PERL_RMSEXPAND_M_VMS);
7735 if (iname != NULL) {
7736 if (cando_by_name(S_IXUSR,0,image_name)) {
7737 /* MCR prefix needed */
7741 /* Try again with a null type */
7742 /*----------------------------*/
7743 iname = do_rmsexpand
7744 (tmpspec, image_name, 0, ".", PERL_RMSEXPAND_M_VMS);
7745 if (iname != NULL) {
7746 if (cando_by_name(S_IXUSR,0,image_name)) {
7747 /* MCR prefix needed */
7753 /* Did we find the image to run the script? */
7754 /*------------------------------------------*/
7758 /* Assume DCL or foreign command exists */
7759 /*--------------------------------------*/
7760 tchr = strrchr(tmpspec, '/');
7767 strcpy(image_name, tchr);
7775 if (check_img && isdcl) return RMS$_FNF;
7777 if (cando_by_name(S_IXUSR,0,resspec)) {
7778 Newx(vmscmd->dsc$a_pointer, MAX_DCL_LINE_LENGTH ,char);
7780 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
7781 if (image_name[0] != 0) {
7782 strcat(vmscmd->dsc$a_pointer, image_name);
7783 strcat(vmscmd->dsc$a_pointer, " ");
7785 } else if (image_name[0] != 0) {
7786 strcpy(vmscmd->dsc$a_pointer, image_name);
7787 strcat(vmscmd->dsc$a_pointer, " ");
7789 strcpy(vmscmd->dsc$a_pointer,"@");
7791 if (suggest_quote) *suggest_quote = 1;
7793 /* If there is an image name, use original command */
7794 if (image_name[0] == 0)
7795 strcat(vmscmd->dsc$a_pointer,resspec);
7798 while (*rest && isspace(*rest)) rest++;
7801 if (image_argv[0] != 0) {
7802 strcat(vmscmd->dsc$a_pointer,image_argv);
7803 strcat(vmscmd->dsc$a_pointer, " ");
7809 rest_len = strlen(rest);
7810 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
7811 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
7812 strcat(vmscmd->dsc$a_pointer,rest);
7814 retsts = CLI$_BUFOVF;
7816 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
7818 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
7820 else retsts = RMS$_PRV;
7823 /* It's either a DCL command or we couldn't find a suitable image */
7824 vmscmd->dsc$w_length = strlen(cmd);
7825 /* if (cmd == PL_Cmd) {
7826 vmscmd->dsc$a_pointer = PL_Cmd;
7827 if (suggest_quote) *suggest_quote = 1;
7830 vmscmd->dsc$a_pointer = savepvn(cmd,vmscmd->dsc$w_length);
7834 /* check if it's a symbol (for quoting purposes) */
7835 if (suggest_quote && !*suggest_quote) {
7837 char equiv[LNM$C_NAMLENGTH];
7838 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7839 eqvdsc.dsc$a_pointer = equiv;
7841 iss = lib$get_symbol(vmscmd,&eqvdsc);
7842 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
7844 if (!(retsts & 1)) {
7845 /* just hand off status values likely to be due to user error */
7846 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
7847 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
7848 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
7849 else { _ckvmssts(retsts); }
7852 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
7854 } /* end of setup_cmddsc() */
7857 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
7859 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
7862 if (vfork_called) { /* this follows a vfork - act Unixish */
7864 if (vfork_called < 0) {
7865 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
7868 else return do_aexec(really,mark,sp);
7870 /* no vfork - act VMSish */
7871 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
7876 } /* end of vms_do_aexec() */
7879 /* {{{bool vms_do_exec(char *cmd) */
7881 Perl_vms_do_exec(pTHX_ const char *cmd)
7883 struct dsc$descriptor_s *vmscmd;
7885 if (vfork_called) { /* this follows a vfork - act Unixish */
7887 if (vfork_called < 0) {
7888 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
7891 else return do_exec(cmd);
7894 { /* no vfork - act VMSish */
7895 unsigned long int retsts;
7898 TAINT_PROPER("exec");
7899 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
7900 retsts = lib$do_command(vmscmd);
7903 case RMS$_FNF: case RMS$_DNF:
7904 set_errno(ENOENT); break;
7906 set_errno(ENOTDIR); break;
7908 set_errno(ENODEV); break;
7910 set_errno(EACCES); break;
7912 set_errno(EINVAL); break;
7913 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
7914 set_errno(E2BIG); break;
7915 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
7916 _ckvmssts(retsts); /* fall through */
7917 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
7920 set_vaxc_errno(retsts);
7921 if (ckWARN(WARN_EXEC)) {
7922 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
7923 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
7925 vms_execfree(vmscmd);
7930 } /* end of vms_do_exec() */
7933 unsigned long int Perl_do_spawn(pTHX_ const char *);
7935 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
7937 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
7939 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
7942 } /* end of do_aspawn() */
7945 /* {{{unsigned long int do_spawn(char *cmd) */
7947 Perl_do_spawn(pTHX_ const char *cmd)
7949 unsigned long int sts, substs;
7952 TAINT_PROPER("spawn");
7953 if (!cmd || !*cmd) {
7954 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
7957 case RMS$_FNF: case RMS$_DNF:
7958 set_errno(ENOENT); break;
7960 set_errno(ENOTDIR); break;
7962 set_errno(ENODEV); break;
7964 set_errno(EACCES); break;
7966 set_errno(EINVAL); break;
7967 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
7968 set_errno(E2BIG); break;
7969 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
7970 _ckvmssts(sts); /* fall through */
7971 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
7974 set_vaxc_errno(sts);
7975 if (ckWARN(WARN_EXEC)) {
7976 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
7984 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
7989 } /* end of do_spawn() */
7993 static unsigned int *sockflags, sockflagsize;
7996 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
7997 * routines found in some versions of the CRTL can't deal with sockets.
7998 * We don't shim the other file open routines since a socket isn't
7999 * likely to be opened by a name.
8001 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
8002 FILE *my_fdopen(int fd, const char *mode)
8004 FILE *fp = fdopen(fd, mode);
8007 unsigned int fdoff = fd / sizeof(unsigned int);
8008 Stat_t sbuf; /* native stat; we don't need flex_stat */
8009 if (!sockflagsize || fdoff > sockflagsize) {
8010 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
8011 else Newx (sockflags,fdoff+2,unsigned int);
8012 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
8013 sockflagsize = fdoff + 2;
8015 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
8016 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
8025 * Clear the corresponding bit when the (possibly) socket stream is closed.
8026 * There still a small hole: we miss an implicit close which might occur
8027 * via freopen(). >> Todo
8029 /*{{{ int my_fclose(FILE *fp)*/
8030 int my_fclose(FILE *fp) {
8032 unsigned int fd = fileno(fp);
8033 unsigned int fdoff = fd / sizeof(unsigned int);
8035 if (sockflagsize && fdoff <= sockflagsize)
8036 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
8044 * A simple fwrite replacement which outputs itmsz*nitm chars without
8045 * introducing record boundaries every itmsz chars.
8046 * We are using fputs, which depends on a terminating null. We may
8047 * well be writing binary data, so we need to accommodate not only
8048 * data with nulls sprinkled in the middle but also data with no null
8051 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
8053 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
8055 register char *cp, *end, *cpd, *data;
8056 register unsigned int fd = fileno(dest);
8057 register unsigned int fdoff = fd / sizeof(unsigned int);
8059 int bufsize = itmsz * nitm + 1;
8061 if (fdoff < sockflagsize &&
8062 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
8063 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
8067 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
8068 memcpy( data, src, itmsz*nitm );
8069 data[itmsz*nitm] = '\0';
8071 end = data + itmsz * nitm;
8072 retval = (int) nitm; /* on success return # items written */
8075 while (cpd <= end) {
8076 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
8077 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
8079 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
8083 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
8086 } /* end of my_fwrite() */
8089 /*{{{ int my_flush(FILE *fp)*/
8091 Perl_my_flush(pTHX_ FILE *fp)
8094 if ((res = fflush(fp)) == 0 && fp) {
8095 #ifdef VMS_DO_SOCKETS
8097 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
8099 res = fsync(fileno(fp));
8102 * If the flush succeeded but set end-of-file, we need to clear
8103 * the error because our caller may check ferror(). BTW, this
8104 * probably means we just flushed an empty file.
8106 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
8113 * Here are replacements for the following Unix routines in the VMS environment:
8114 * getpwuid Get information for a particular UIC or UID
8115 * getpwnam Get information for a named user
8116 * getpwent Get information for each user in the rights database
8117 * setpwent Reset search to the start of the rights database
8118 * endpwent Finish searching for users in the rights database
8120 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
8121 * (defined in pwd.h), which contains the following fields:-
8123 * char *pw_name; Username (in lower case)
8124 * char *pw_passwd; Hashed password
8125 * unsigned int pw_uid; UIC
8126 * unsigned int pw_gid; UIC group number
8127 * char *pw_unixdir; Default device/directory (VMS-style)
8128 * char *pw_gecos; Owner name
8129 * char *pw_dir; Default device/directory (Unix-style)
8130 * char *pw_shell; Default CLI name (eg. DCL)
8132 * If the specified user does not exist, getpwuid and getpwnam return NULL.
8134 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
8135 * not the UIC member number (eg. what's returned by getuid()),
8136 * getpwuid() can accept either as input (if uid is specified, the caller's
8137 * UIC group is used), though it won't recognise gid=0.
8139 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
8140 * information about other users in your group or in other groups, respectively.
8141 * If the required privilege is not available, then these routines fill only
8142 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
8145 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
8148 /* sizes of various UAF record fields */
8149 #define UAI$S_USERNAME 12
8150 #define UAI$S_IDENT 31
8151 #define UAI$S_OWNER 31
8152 #define UAI$S_DEFDEV 31
8153 #define UAI$S_DEFDIR 63
8154 #define UAI$S_DEFCLI 31
8157 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
8158 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
8159 (uic).uic$v_group != UIC$K_WILD_GROUP)
8161 static char __empty[]= "";
8162 static struct passwd __passwd_empty=
8163 {(char *) __empty, (char *) __empty, 0, 0,
8164 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
8165 static int contxt= 0;
8166 static struct passwd __pwdcache;
8167 static char __pw_namecache[UAI$S_IDENT+1];
8170 * This routine does most of the work extracting the user information.
8172 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
8175 unsigned char length;
8176 char pw_gecos[UAI$S_OWNER+1];
8178 static union uicdef uic;
8180 unsigned char length;
8181 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
8184 unsigned char length;
8185 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
8188 unsigned char length;
8189 char pw_shell[UAI$S_DEFCLI+1];
8191 static char pw_passwd[UAI$S_PWD+1];
8193 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
8194 struct dsc$descriptor_s name_desc;
8195 unsigned long int sts;
8197 static struct itmlst_3 itmlst[]= {
8198 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
8199 {sizeof(uic), UAI$_UIC, &uic, &luic},
8200 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
8201 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
8202 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
8203 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
8204 {0, 0, NULL, NULL}};
8206 name_desc.dsc$w_length= strlen(name);
8207 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8208 name_desc.dsc$b_class= DSC$K_CLASS_S;
8209 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
8211 /* Note that sys$getuai returns many fields as counted strings. */
8212 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
8213 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
8214 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
8216 else { _ckvmssts(sts); }
8217 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
8219 if ((int) owner.length < lowner) lowner= (int) owner.length;
8220 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
8221 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
8222 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
8223 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
8224 owner.pw_gecos[lowner]= '\0';
8225 defdev.pw_dir[ldefdev+ldefdir]= '\0';
8226 defcli.pw_shell[ldefcli]= '\0';
8227 if (valid_uic(uic)) {
8228 pwd->pw_uid= uic.uic$l_uic;
8229 pwd->pw_gid= uic.uic$v_group;
8232 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
8233 pwd->pw_passwd= pw_passwd;
8234 pwd->pw_gecos= owner.pw_gecos;
8235 pwd->pw_dir= defdev.pw_dir;
8236 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
8237 pwd->pw_shell= defcli.pw_shell;
8238 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
8240 ldir= strlen(pwd->pw_unixdir) - 1;
8241 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
8244 strcpy(pwd->pw_unixdir, pwd->pw_dir);
8245 if (!decc_efs_case_preserve)
8246 __mystrtolower(pwd->pw_unixdir);
8251 * Get information for a named user.
8253 /*{{{struct passwd *getpwnam(char *name)*/
8254 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
8256 struct dsc$descriptor_s name_desc;
8258 unsigned long int status, sts;
8260 __pwdcache = __passwd_empty;
8261 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
8262 /* We still may be able to determine pw_uid and pw_gid */
8263 name_desc.dsc$w_length= strlen(name);
8264 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8265 name_desc.dsc$b_class= DSC$K_CLASS_S;
8266 name_desc.dsc$a_pointer= (char *) name;
8267 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
8268 __pwdcache.pw_uid= uic.uic$l_uic;
8269 __pwdcache.pw_gid= uic.uic$v_group;
8272 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
8273 set_vaxc_errno(sts);
8274 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
8277 else { _ckvmssts(sts); }
8280 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
8281 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
8282 __pwdcache.pw_name= __pw_namecache;
8284 } /* end of my_getpwnam() */
8288 * Get information for a particular UIC or UID.
8289 * Called by my_getpwent with uid=-1 to list all users.
8291 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
8292 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
8294 const $DESCRIPTOR(name_desc,__pw_namecache);
8295 unsigned short lname;
8297 unsigned long int status;
8299 if (uid == (unsigned int) -1) {
8301 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
8302 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
8303 set_vaxc_errno(status);
8304 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8308 else { _ckvmssts(status); }
8309 } while (!valid_uic (uic));
8313 if (!uic.uic$v_group)
8314 uic.uic$v_group= PerlProc_getgid();
8316 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
8317 else status = SS$_IVIDENT;
8318 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
8319 status == RMS$_PRV) {
8320 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8323 else { _ckvmssts(status); }
8325 __pw_namecache[lname]= '\0';
8326 __mystrtolower(__pw_namecache);
8328 __pwdcache = __passwd_empty;
8329 __pwdcache.pw_name = __pw_namecache;
8331 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
8332 The identifier's value is usually the UIC, but it doesn't have to be,
8333 so if we can, we let fillpasswd update this. */
8334 __pwdcache.pw_uid = uic.uic$l_uic;
8335 __pwdcache.pw_gid = uic.uic$v_group;
8337 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
8340 } /* end of my_getpwuid() */
8344 * Get information for next user.
8346 /*{{{struct passwd *my_getpwent()*/
8347 struct passwd *Perl_my_getpwent(pTHX)
8349 return (my_getpwuid((unsigned int) -1));
8354 * Finish searching rights database for users.
8356 /*{{{void my_endpwent()*/
8357 void Perl_my_endpwent(pTHX)
8360 _ckvmssts(sys$finish_rdb(&contxt));
8366 #ifdef HOMEGROWN_POSIX_SIGNALS
8367 /* Signal handling routines, pulled into the core from POSIX.xs.
8369 * We need these for threads, so they've been rolled into the core,
8370 * rather than left in POSIX.xs.
8372 * (DRS, Oct 23, 1997)
8375 /* sigset_t is atomic under VMS, so these routines are easy */
8376 /*{{{int my_sigemptyset(sigset_t *) */
8377 int my_sigemptyset(sigset_t *set) {
8378 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8384 /*{{{int my_sigfillset(sigset_t *)*/
8385 int my_sigfillset(sigset_t *set) {
8387 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8388 for (i = 0; i < NSIG; i++) *set |= (1 << i);
8394 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
8395 int my_sigaddset(sigset_t *set, int sig) {
8396 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8397 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8398 *set |= (1 << (sig - 1));
8404 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
8405 int my_sigdelset(sigset_t *set, int sig) {
8406 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8407 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8408 *set &= ~(1 << (sig - 1));
8414 /*{{{int my_sigismember(sigset_t *set, int sig)*/
8415 int my_sigismember(sigset_t *set, int sig) {
8416 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8417 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8418 return *set & (1 << (sig - 1));
8423 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
8424 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
8427 /* If set and oset are both null, then things are badly wrong. Bail out. */
8428 if ((oset == NULL) && (set == NULL)) {
8429 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
8433 /* If set's null, then we're just handling a fetch. */
8435 tempmask = sigblock(0);
8440 tempmask = sigsetmask(*set);
8443 tempmask = sigblock(*set);
8446 tempmask = sigblock(0);
8447 sigsetmask(*oset & ~tempmask);
8450 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8455 /* Did they pass us an oset? If so, stick our holding mask into it */
8462 #endif /* HOMEGROWN_POSIX_SIGNALS */
8465 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
8466 * my_utime(), and flex_stat(), all of which operate on UTC unless
8467 * VMSISH_TIMES is true.
8469 /* method used to handle UTC conversions:
8470 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
8472 static int gmtime_emulation_type;
8473 /* number of secs to add to UTC POSIX-style time to get local time */
8474 static long int utc_offset_secs;
8476 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
8477 * in vmsish.h. #undef them here so we can call the CRTL routines
8486 * DEC C previous to 6.0 corrupts the behavior of the /prefix
8487 * qualifier with the extern prefix pragma. This provisional
8488 * hack circumvents this prefix pragma problem in previous
8491 #if defined(__VMS_VER) && __VMS_VER >= 70000000
8492 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
8493 # pragma __extern_prefix save
8494 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
8495 # define gmtime decc$__utctz_gmtime
8496 # define localtime decc$__utctz_localtime
8497 # define time decc$__utc_time
8498 # pragma __extern_prefix restore
8500 struct tm *gmtime(), *localtime();
8506 static time_t toutc_dst(time_t loc) {
8509 if ((rsltmp = localtime(&loc)) == NULL) return -1;
8510 loc -= utc_offset_secs;
8511 if (rsltmp->tm_isdst) loc -= 3600;
8514 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
8515 ((gmtime_emulation_type || my_time(NULL)), \
8516 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
8517 ((secs) - utc_offset_secs))))
8519 static time_t toloc_dst(time_t utc) {
8522 utc += utc_offset_secs;
8523 if ((rsltmp = localtime(&utc)) == NULL) return -1;
8524 if (rsltmp->tm_isdst) utc += 3600;
8527 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
8528 ((gmtime_emulation_type || my_time(NULL)), \
8529 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
8530 ((secs) + utc_offset_secs))))
8532 #ifndef RTL_USES_UTC
8535 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
8536 DST starts on 1st sun of april at 02:00 std time
8537 ends on last sun of october at 02:00 dst time
8538 see the UCX management command reference, SET CONFIG TIMEZONE
8539 for formatting info.
8541 No, it's not as general as it should be, but then again, NOTHING
8542 will handle UK times in a sensible way.
8547 parse the DST start/end info:
8548 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
8552 tz_parse_startend(char *s, struct tm *w, int *past)
8554 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
8555 int ly, dozjd, d, m, n, hour, min, sec, j, k;
8560 if (!past) return 0;
8563 if (w->tm_year % 4 == 0) ly = 1;
8564 if (w->tm_year % 100 == 0) ly = 0;
8565 if (w->tm_year+1900 % 400 == 0) ly = 1;
8568 dozjd = isdigit(*s);
8569 if (*s == 'J' || *s == 'j' || dozjd) {
8570 if (!dozjd && !isdigit(*++s)) return 0;
8573 d = d*10 + *s++ - '0';
8575 d = d*10 + *s++ - '0';
8578 if (d == 0) return 0;
8579 if (d > 366) return 0;
8581 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
8584 } else if (*s == 'M' || *s == 'm') {
8585 if (!isdigit(*++s)) return 0;
8587 if (isdigit(*s)) m = 10*m + *s++ - '0';
8588 if (*s != '.') return 0;
8589 if (!isdigit(*++s)) return 0;
8591 if (n < 1 || n > 5) return 0;
8592 if (*s != '.') return 0;
8593 if (!isdigit(*++s)) return 0;
8595 if (d > 6) return 0;
8599 if (!isdigit(*++s)) return 0;
8601 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
8603 if (!isdigit(*++s)) return 0;
8605 if (isdigit(*s)) min = 10*min + *s++ - '0';
8607 if (!isdigit(*++s)) return 0;
8609 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
8619 if (w->tm_yday < d) goto before;
8620 if (w->tm_yday > d) goto after;
8622 if (w->tm_mon+1 < m) goto before;
8623 if (w->tm_mon+1 > m) goto after;
8625 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
8626 k = d - j; /* mday of first d */
8628 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
8629 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
8630 if (w->tm_mday < k) goto before;
8631 if (w->tm_mday > k) goto after;
8634 if (w->tm_hour < hour) goto before;
8635 if (w->tm_hour > hour) goto after;
8636 if (w->tm_min < min) goto before;
8637 if (w->tm_min > min) goto after;
8638 if (w->tm_sec < sec) goto before;
8652 /* parse the offset: (+|-)hh[:mm[:ss]] */
8655 tz_parse_offset(char *s, int *offset)
8657 int hour = 0, min = 0, sec = 0;
8660 if (!offset) return 0;
8662 if (*s == '-') {neg++; s++;}
8664 if (!isdigit(*s)) return 0;
8666 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
8667 if (hour > 24) return 0;
8669 if (!isdigit(*++s)) return 0;
8671 if (isdigit(*s)) min = min*10 + (*s++ - '0');
8672 if (min > 59) return 0;
8674 if (!isdigit(*++s)) return 0;
8676 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
8677 if (sec > 59) return 0;
8681 *offset = (hour*60+min)*60 + sec;
8682 if (neg) *offset = -*offset;
8687 input time is w, whatever type of time the CRTL localtime() uses.
8688 sets dst, the zone, and the gmtoff (seconds)
8690 caches the value of TZ and UCX$TZ env variables; note that
8691 my_setenv looks for these and sets a flag if they're changed
8694 We have to watch out for the "australian" case (dst starts in
8695 october, ends in april)...flagged by "reverse" and checked by
8696 scanning through the months of the previous year.
8701 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
8706 char *dstzone, *tz, *s_start, *s_end;
8707 int std_off, dst_off, isdst;
8708 int y, dststart, dstend;
8709 static char envtz[1025]; /* longer than any logical, symbol, ... */
8710 static char ucxtz[1025];
8711 static char reversed = 0;
8717 reversed = -1; /* flag need to check */
8718 envtz[0] = ucxtz[0] = '\0';
8719 tz = my_getenv("TZ",0);
8720 if (tz) strcpy(envtz, tz);
8721 tz = my_getenv("UCX$TZ",0);
8722 if (tz) strcpy(ucxtz, tz);
8723 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
8726 if (!*tz) tz = ucxtz;
8729 while (isalpha(*s)) s++;
8730 s = tz_parse_offset(s, &std_off);
8732 if (!*s) { /* no DST, hurray we're done! */
8738 while (isalpha(*s)) s++;
8739 s2 = tz_parse_offset(s, &dst_off);
8743 dst_off = std_off - 3600;
8746 if (!*s) { /* default dst start/end?? */
8747 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
8748 s = strchr(ucxtz,',');
8750 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
8752 if (*s != ',') return 0;
8755 when = _toutc(when); /* convert to utc */
8756 when = when - std_off; /* convert to pseudolocal time*/
8758 w2 = localtime(&when);
8761 s = tz_parse_startend(s_start,w2,&dststart);
8763 if (*s != ',') return 0;
8766 when = _toutc(when); /* convert to utc */
8767 when = when - dst_off; /* convert to pseudolocal time*/
8768 w2 = localtime(&when);
8769 if (w2->tm_year != y) { /* spans a year, just check one time */
8770 when += dst_off - std_off;
8771 w2 = localtime(&when);
8774 s = tz_parse_startend(s_end,w2,&dstend);
8777 if (reversed == -1) { /* need to check if start later than end */
8781 if (when < 2*365*86400) {
8782 when += 2*365*86400;
8786 w2 =localtime(&when);
8787 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
8789 for (j = 0; j < 12; j++) {
8790 w2 =localtime(&when);
8791 tz_parse_startend(s_start,w2,&ds);
8792 tz_parse_startend(s_end,w2,&de);
8793 if (ds != de) break;
8797 if (de && !ds) reversed = 1;
8800 isdst = dststart && !dstend;
8801 if (reversed) isdst = dststart || !dstend;
8804 if (dst) *dst = isdst;
8805 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
8806 if (isdst) tz = dstzone;
8808 while(isalpha(*tz)) *zone++ = *tz++;
8814 #endif /* !RTL_USES_UTC */
8816 /* my_time(), my_localtime(), my_gmtime()
8817 * By default traffic in UTC time values, using CRTL gmtime() or
8818 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
8819 * Note: We need to use these functions even when the CRTL has working
8820 * UTC support, since they also handle C<use vmsish qw(times);>
8822 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
8823 * Modified by Charles Bailey <bailey@newman.upenn.edu>
8826 /*{{{time_t my_time(time_t *timep)*/
8827 time_t Perl_my_time(pTHX_ time_t *timep)
8832 if (gmtime_emulation_type == 0) {
8834 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
8835 /* results of calls to gmtime() and localtime() */
8836 /* for same &base */
8838 gmtime_emulation_type++;
8839 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
8840 char off[LNM$C_NAMLENGTH+1];;
8842 gmtime_emulation_type++;
8843 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
8844 gmtime_emulation_type++;
8845 utc_offset_secs = 0;
8846 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
8848 else { utc_offset_secs = atol(off); }
8850 else { /* We've got a working gmtime() */
8851 struct tm gmt, local;
8854 tm_p = localtime(&base);
8856 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
8857 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
8858 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
8859 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
8865 # ifdef RTL_USES_UTC
8866 if (VMSISH_TIME) when = _toloc(when);
8868 if (!VMSISH_TIME) when = _toutc(when);
8871 if (timep != NULL) *timep = when;
8874 } /* end of my_time() */
8878 /*{{{struct tm *my_gmtime(const time_t *timep)*/
8880 Perl_my_gmtime(pTHX_ const time_t *timep)
8886 if (timep == NULL) {
8887 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8890 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
8894 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
8896 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
8897 return gmtime(&when);
8899 /* CRTL localtime() wants local time as input, so does no tz correction */
8900 rsltmp = localtime(&when);
8901 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
8904 } /* end of my_gmtime() */
8908 /*{{{struct tm *my_localtime(const time_t *timep)*/
8910 Perl_my_localtime(pTHX_ const time_t *timep)
8912 time_t when, whenutc;
8916 if (timep == NULL) {
8917 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8920 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
8921 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
8924 # ifdef RTL_USES_UTC
8926 if (VMSISH_TIME) when = _toutc(when);
8928 /* CRTL localtime() wants UTC as input, does tz correction itself */
8929 return localtime(&when);
8931 # else /* !RTL_USES_UTC */
8934 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
8935 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
8938 #ifndef RTL_USES_UTC
8939 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
8940 when = whenutc - offset; /* pseudolocal time*/
8943 /* CRTL localtime() wants local time as input, so does no tz correction */
8944 rsltmp = localtime(&when);
8945 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
8949 } /* end of my_localtime() */
8952 /* Reset definitions for later calls */
8953 #define gmtime(t) my_gmtime(t)
8954 #define localtime(t) my_localtime(t)
8955 #define time(t) my_time(t)
8958 /* my_utime - update modification time of a file
8959 * calling sequence is identical to POSIX utime(), but under
8960 * VMS only the modification time is changed; ODS-2 does not
8961 * maintain access times. Restrictions differ from the POSIX
8962 * definition in that the time can be changed as long as the
8963 * caller has permission to execute the necessary IO$_MODIFY $QIO;
8964 * no separate checks are made to insure that the caller is the
8965 * owner of the file or has special privs enabled.
8966 * Code here is based on Joe Meadows' FILE utility.
8969 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
8970 * to VMS epoch (01-JAN-1858 00:00:00.00)
8971 * in 100 ns intervals.
8973 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
8975 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
8976 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
8980 long int bintime[2], len = 2, lowbit, unixtime,
8981 secscale = 10000000; /* seconds --> 100 ns intervals */
8982 unsigned long int chan, iosb[2], retsts;
8983 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
8984 struct FAB myfab = cc$rms_fab;
8985 struct NAM mynam = cc$rms_nam;
8986 #if defined (__DECC) && defined (__VAX)
8987 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
8988 * at least through VMS V6.1, which causes a type-conversion warning.
8990 # pragma message save
8991 # pragma message disable cvtdiftypes
8993 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
8994 struct fibdef myfib;
8995 #if defined (__DECC) && defined (__VAX)
8996 /* This should be right after the declaration of myatr, but due
8997 * to a bug in VAX DEC C, this takes effect a statement early.
8999 # pragma message restore
9001 /* cast ok for read only parameter */
9002 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
9003 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
9004 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
9006 if (file == NULL || *file == '\0') {
9008 set_vaxc_errno(LIB$_INVARG);
9011 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
9013 if (utimes != NULL) {
9014 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
9015 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
9016 * Since time_t is unsigned long int, and lib$emul takes a signed long int
9017 * as input, we force the sign bit to be clear by shifting unixtime right
9018 * one bit, then multiplying by an extra factor of 2 in lib$emul().
9020 lowbit = (utimes->modtime & 1) ? secscale : 0;
9021 unixtime = (long int) utimes->modtime;
9023 /* If input was UTC; convert to local for sys svc */
9024 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
9026 unixtime >>= 1; secscale <<= 1;
9027 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
9028 if (!(retsts & 1)) {
9030 set_vaxc_errno(retsts);
9033 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
9034 if (!(retsts & 1)) {
9036 set_vaxc_errno(retsts);
9041 /* Just get the current time in VMS format directly */
9042 retsts = sys$gettim(bintime);
9043 if (!(retsts & 1)) {
9045 set_vaxc_errno(retsts);
9050 myfab.fab$l_fna = vmsspec;
9051 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
9052 myfab.fab$l_nam = &mynam;
9053 mynam.nam$l_esa = esa;
9054 mynam.nam$b_ess = (unsigned char) sizeof esa;
9055 mynam.nam$l_rsa = rsa;
9056 mynam.nam$b_rss = (unsigned char) sizeof rsa;
9057 if (decc_efs_case_preserve)
9058 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9060 /* Look for the file to be affected, letting RMS parse the file
9061 * specification for us as well. I have set errno using only
9062 * values documented in the utime() man page for VMS POSIX.
9064 retsts = sys$parse(&myfab,0,0);
9065 if (!(retsts & 1)) {
9066 set_vaxc_errno(retsts);
9067 if (retsts == RMS$_PRV) set_errno(EACCES);
9068 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
9069 else set_errno(EVMSERR);
9072 retsts = sys$search(&myfab,0,0);
9073 if (!(retsts & 1)) {
9074 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9075 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9076 set_vaxc_errno(retsts);
9077 if (retsts == RMS$_PRV) set_errno(EACCES);
9078 else if (retsts == RMS$_FNF) set_errno(ENOENT);
9079 else set_errno(EVMSERR);
9083 devdsc.dsc$w_length = mynam.nam$b_dev;
9084 /* cast ok for read only parameter */
9085 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
9087 retsts = sys$assign(&devdsc,&chan,0,0);
9088 if (!(retsts & 1)) {
9089 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9090 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9091 set_vaxc_errno(retsts);
9092 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
9093 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
9094 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
9095 else set_errno(EVMSERR);
9099 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
9100 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
9102 memset((void *) &myfib, 0, sizeof myfib);
9103 #if defined(__DECC) || defined(__DECCXX)
9104 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
9105 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
9106 /* This prevents the revision time of the file being reset to the current
9107 * time as a result of our IO$_MODIFY $QIO. */
9108 myfib.fib$l_acctl = FIB$M_NORECORD;
9110 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
9111 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
9112 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
9114 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
9115 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9116 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9117 _ckvmssts(sys$dassgn(chan));
9118 if (retsts & 1) retsts = iosb[0];
9119 if (!(retsts & 1)) {
9120 set_vaxc_errno(retsts);
9121 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9122 else set_errno(EVMSERR);
9127 } /* end of my_utime() */
9131 * flex_stat, flex_lstat, flex_fstat
9132 * basic stat, but gets it right when asked to stat
9133 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
9136 #ifndef _USE_STD_STAT
9137 /* encode_dev packs a VMS device name string into an integer to allow
9138 * simple comparisons. This can be used, for example, to check whether two
9139 * files are located on the same device, by comparing their encoded device
9140 * names. Even a string comparison would not do, because stat() reuses the
9141 * device name buffer for each call; so without encode_dev, it would be
9142 * necessary to save the buffer and use strcmp (this would mean a number of
9143 * changes to the standard Perl code, to say nothing of what a Perl script
9146 * The device lock id, if it exists, should be unique (unless perhaps compared
9147 * with lock ids transferred from other nodes). We have a lock id if the disk is
9148 * mounted cluster-wide, which is when we tend to get long (host-qualified)
9149 * device names. Thus we use the lock id in preference, and only if that isn't
9150 * available, do we try to pack the device name into an integer (flagged by
9151 * the sign bit (LOCKID_MASK) being set).
9153 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
9154 * name and its encoded form, but it seems very unlikely that we will find
9155 * two files on different disks that share the same encoded device names,
9156 * and even more remote that they will share the same file id (if the test
9157 * is to check for the same file).
9159 * A better method might be to use sys$device_scan on the first call, and to
9160 * search for the device, returning an index into the cached array.
9161 * The number returned would be more intelligable.
9162 * This is probably not worth it, and anyway would take quite a bit longer
9163 * on the first call.
9165 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
9166 static mydev_t encode_dev (pTHX_ const char *dev)
9169 unsigned long int f;
9174 if (!dev || !dev[0]) return 0;
9178 struct dsc$descriptor_s dev_desc;
9179 unsigned long int status, lockid, item = DVI$_LOCKID;
9181 /* For cluster-mounted disks, the disk lock identifier is unique, so we
9182 can try that first. */
9183 dev_desc.dsc$w_length = strlen (dev);
9184 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
9185 dev_desc.dsc$b_class = DSC$K_CLASS_S;
9186 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
9187 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
9188 if (lockid) return (lockid & ~LOCKID_MASK);
9192 /* Otherwise we try to encode the device name */
9196 for (q = dev + strlen(dev); q--; q >= dev) {
9199 else if (isalpha (toupper (*q)))
9200 c= toupper (*q) - 'A' + (char)10;
9202 continue; /* Skip '$'s */
9204 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
9206 enc += f * (unsigned long int) c;
9208 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
9210 } /* end of encode_dev() */
9213 static char namecache[NAM$C_MAXRSS+1];
9216 is_null_device(name)
9219 if (decc_bug_devnull != 0) {
9220 if (strcmp("/dev/null", name) == 0) /* temp hack */
9223 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
9224 The underscore prefix, controller letter, and unit number are
9225 independently optional; for our purposes, the colon punctuation
9226 is not. The colon can be trailed by optional directory and/or
9227 filename, but two consecutive colons indicates a nodename rather
9228 than a device. [pr] */
9229 if (*name == '_') ++name;
9230 if (tolower(*name++) != 'n') return 0;
9231 if (tolower(*name++) != 'l') return 0;
9232 if (tolower(*name) == 'a') ++name;
9233 if (*name == '0') ++name;
9234 return (*name++ == ':') && (*name != ':');
9237 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
9238 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
9239 * subset of the applicable information.
9242 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
9244 char fname_phdev[NAM$C_MAXRSS+1];
9245 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9246 /* Namecache not workable with symbolic links, as symbolic links do
9247 * not have extensions and directories do in VMS mode. So in order
9248 * to test this, the did and ino_t must be used.
9250 * Fix-me - Hide the information in the new stat structure
9251 * Get rid of the namecache.
9253 if (decc_posix_compliant_pathnames == 0)
9255 if (statbufp == &PL_statcache)
9256 return cando_by_name(bit,effective,namecache);
9258 char fname[NAM$C_MAXRSS+1];
9259 unsigned long int retsts;
9260 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
9261 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9263 /* If the struct mystat is stale, we're OOL; stat() overwrites the
9264 device name on successive calls */
9265 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
9266 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
9267 namdsc.dsc$a_pointer = fname;
9268 namdsc.dsc$w_length = sizeof fname - 1;
9270 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
9271 &namdsc,&namdsc.dsc$w_length,0,0);
9273 fname[namdsc.dsc$w_length] = '\0';
9275 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
9276 * but if someone has redefined that logical, Perl gets very lost. Since
9277 * we have the physical device name from the stat buffer, just paste it on.
9279 strcpy( fname_phdev, statbufp->st_devnam );
9280 strcat( fname_phdev, strrchr(fname, ':') );
9282 return cando_by_name(bit,effective,fname_phdev);
9284 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
9285 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
9289 return FALSE; /* Should never get to here */
9291 } /* end of cando() */
9295 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
9297 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
9299 static char usrname[L_cuserid];
9300 static struct dsc$descriptor_s usrdsc =
9301 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
9302 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
9303 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
9304 unsigned short int retlen, trnlnm_iter_count;
9305 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9306 union prvdef curprv;
9307 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
9308 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
9309 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
9310 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
9312 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
9314 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9316 if (!fname || !*fname) return FALSE;
9317 /* Make sure we expand logical names, since sys$check_access doesn't */
9318 if (!strpbrk(fname,"/]>:")) {
9319 strcpy(fileified,fname);
9320 trnlnm_iter_count = 0;
9321 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
9322 trnlnm_iter_count++;
9323 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
9327 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
9328 retlen = namdsc.dsc$w_length = strlen(vmsname);
9329 namdsc.dsc$a_pointer = vmsname;
9330 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
9331 vmsname[retlen-1] == ':') {
9332 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
9333 namdsc.dsc$w_length = strlen(fileified);
9334 namdsc.dsc$a_pointer = fileified;
9338 case S_IXUSR: case S_IXGRP: case S_IXOTH:
9339 access = ARM$M_EXECUTE; break;
9340 case S_IRUSR: case S_IRGRP: case S_IROTH:
9341 access = ARM$M_READ; break;
9342 case S_IWUSR: case S_IWGRP: case S_IWOTH:
9343 access = ARM$M_WRITE; break;
9344 case S_IDUSR: case S_IDGRP: case S_IDOTH:
9345 access = ARM$M_DELETE; break;
9350 /* Before we call $check_access, create a user profile with the current
9351 * process privs since otherwise it just uses the default privs from the
9352 * UAF and might give false positives or negatives. This only works on
9353 * VMS versions v6.0 and later since that's when sys$create_user_profile
9357 /* get current process privs and username */
9358 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
9361 #if defined(__VMS_VER) && __VMS_VER >= 60000000
9363 /* find out the space required for the profile */
9364 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
9365 &usrprodsc.dsc$w_length,0));
9367 /* allocate space for the profile and get it filled in */
9368 Newx(usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
9369 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
9370 &usrprodsc.dsc$w_length,0));
9372 /* use the profile to check access to the file; free profile & analyze results */
9373 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
9374 Safefree(usrprodsc.dsc$a_pointer);
9375 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
9379 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
9383 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
9384 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
9385 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
9386 set_vaxc_errno(retsts);
9387 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9388 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
9389 else set_errno(ENOENT);
9392 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
9397 return FALSE; /* Should never get here */
9399 } /* end of cando_by_name() */
9403 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
9405 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
9407 if (!fstat(fd,(stat_t *) statbufp)) {
9408 if (statbufp == (Stat_t *) &PL_statcache) {
9411 /* Save name for cando by name in VMS format */
9412 cptr = getname(fd, namecache, 1);
9414 /* This should not happen, but just in case */
9416 namecache[0] = '\0';
9418 #ifdef _USE_STD_STAT
9419 memcpy(&statbufp->st_ino, &statbufp->crtl_stat.st_ino, 8);
9421 memcpy(&statbufp->st_ino, statbufp->crtl_stat.st_ino, 8);
9423 #ifndef _USE_STD_STAT
9424 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
9425 statbufp->st_devnam[63] = 0;
9426 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
9429 * The device is only encoded so that Perl_cando can use it to
9430 * look up ACLS. So rmsexpand it to the 255 character version
9431 * and store it in ->st_devnam. rmsexpand needs to be fixed
9432 * for long filenames and symbolic links first. This also seems
9433 * to remove the need for a namecache that could be stale.
9437 # ifdef RTL_USES_UTC
9440 statbufp->st_mtime = _toloc(statbufp->st_mtime);
9441 statbufp->st_atime = _toloc(statbufp->st_atime);
9442 statbufp->st_ctime = _toloc(statbufp->st_ctime);
9447 if (!VMSISH_TIME) { /* Return UTC instead of local time */
9451 statbufp->st_mtime = _toutc(statbufp->st_mtime);
9452 statbufp->st_atime = _toutc(statbufp->st_atime);
9453 statbufp->st_ctime = _toutc(statbufp->st_ctime);
9460 } /* end of flex_fstat() */
9463 #if !defined(__VAX) && __CRTL_VER >= 80200000
9471 #define lstat(_x, _y) stat(_x, _y)
9474 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
9477 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
9479 char fileified[NAM$C_MAXRSS+1];
9480 char temp_fspec[NAM$C_MAXRSS+300];
9482 int saved_errno, saved_vaxc_errno;
9484 if (!fspec) return retval;
9485 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
9486 strcpy(temp_fspec, fspec);
9487 if (statbufp == (Stat_t *) &PL_statcache)
9488 do_tovmsspec(temp_fspec,namecache,0);
9489 if (decc_bug_devnull != 0) {
9490 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
9491 memset(statbufp,0,sizeof *statbufp);
9492 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
9493 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
9494 statbufp->st_uid = 0x00010001;
9495 statbufp->st_gid = 0x0001;
9496 time((time_t *)&statbufp->st_mtime);
9497 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
9502 /* Try for a directory name first. If fspec contains a filename without
9503 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
9504 * and sea:[wine.dark]water. exist, we prefer the directory here.
9505 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
9506 * not sea:[wine.dark]., if the latter exists. If the intended target is
9507 * the file with null type, specify this by calling flex_stat() with
9508 * a '.' at the end of fspec.
9510 * If we are in Posix filespec mode, accept the filename as is.
9512 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9513 if (decc_posix_compliant_pathnames == 0) {
9515 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
9516 if (lstat_flag == 0)
9517 retval = stat(fileified,(stat_t *) statbufp);
9519 retval = lstat(fileified,(stat_t *) statbufp);
9520 if (!retval && statbufp == (Stat_t *) &PL_statcache)
9521 strcpy(namecache,fileified);
9524 if (lstat_flag == 0)
9525 retval = stat(temp_fspec,(stat_t *) statbufp);
9527 retval = lstat(temp_fspec,(stat_t *) statbufp);
9529 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9531 if (lstat_flag == 0)
9532 retval = stat(temp_fspec,(stat_t *) statbufp);
9534 retval = lstat(temp_fspec,(stat_t *) statbufp);
9538 #ifdef _USE_STD_STAT
9539 memcpy(&statbufp->st_ino, &statbufp->crtl_stat.st_ino, 8);
9541 memcpy(&statbufp->st_ino, statbufp->crtl_stat.st_ino, 8);
9543 #ifndef _USE_STD_STAT
9544 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
9545 statbufp->st_devnam[63] = 0;
9546 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
9549 * The device is only encoded so that Perl_cando can use it to
9550 * look up ACLS. So rmsexpand it to the 255 character version
9551 * and store it in ->st_devnam. rmsexpand needs to be fixed
9552 * for long filenames and symbolic links first. This also seems
9553 * to remove the need for a namecache that could be stale.
9556 # ifdef RTL_USES_UTC
9559 statbufp->st_mtime = _toloc(statbufp->st_mtime);
9560 statbufp->st_atime = _toloc(statbufp->st_atime);
9561 statbufp->st_ctime = _toloc(statbufp->st_ctime);
9566 if (!VMSISH_TIME) { /* Return UTC instead of local time */
9570 statbufp->st_mtime = _toutc(statbufp->st_mtime);
9571 statbufp->st_atime = _toutc(statbufp->st_atime);
9572 statbufp->st_ctime = _toutc(statbufp->st_ctime);
9576 /* If we were successful, leave errno where we found it */
9577 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
9580 } /* end of flex_stat_int() */
9583 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
9585 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
9587 return flex_stat_int(fspec, statbufp, 0);
9591 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
9593 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
9595 return flex_stat_int(fspec, statbufp, 1);
9600 /*{{{char *my_getlogin()*/
9601 /* VMS cuserid == Unix getlogin, except calling sequence */
9605 static char user[L_cuserid];
9606 return cuserid(user);
9611 /* rmscopy - copy a file using VMS RMS routines
9613 * Copies contents and attributes of spec_in to spec_out, except owner
9614 * and protection information. Name and type of spec_in are used as
9615 * defaults for spec_out. The third parameter specifies whether rmscopy()
9616 * should try to propagate timestamps from the input file to the output file.
9617 * If it is less than 0, no timestamps are preserved. If it is 0, then
9618 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
9619 * propagated to the output file at creation iff the output file specification
9620 * did not contain an explicit name or type, and the revision date is always
9621 * updated at the end of the copy operation. If it is greater than 0, then
9622 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
9623 * other than the revision date should be propagated, and bit 1 indicates
9624 * that the revision date should be propagated.
9626 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
9628 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
9629 * Incorporates, with permission, some code from EZCOPY by Tim Adye
9630 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
9631 * as part of the Perl standard distribution under the terms of the
9632 * GNU General Public License or the Perl Artistic License. Copies
9633 * of each may be found in the Perl standard distribution.
9635 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
9637 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
9639 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
9640 rsa[NAM$C_MAXRSS], ubf[32256];
9641 unsigned long int i, sts, sts2;
9642 struct FAB fab_in, fab_out;
9643 struct RAB rab_in, rab_out;
9645 struct XABDAT xabdat;
9646 struct XABFHC xabfhc;
9647 struct XABRDT xabrdt;
9648 struct XABSUM xabsum;
9650 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
9651 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
9652 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9656 fab_in = cc$rms_fab;
9657 fab_in.fab$l_fna = vmsin;
9658 fab_in.fab$b_fns = strlen(vmsin);
9659 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
9660 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
9661 fab_in.fab$l_fop = FAB$M_SQO;
9662 fab_in.fab$l_nam = &nam;
9663 fab_in.fab$l_xab = (void *) &xabdat;
9666 nam.nam$l_rsa = rsa;
9667 nam.nam$b_rss = sizeof(rsa);
9668 nam.nam$l_esa = esa;
9669 nam.nam$b_ess = sizeof (esa);
9670 nam.nam$b_esl = nam.nam$b_rsl = 0;
9671 #ifdef NAM$M_NO_SHORT_UPCASE
9672 if (decc_efs_case_preserve)
9673 nam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9676 xabdat = cc$rms_xabdat; /* To get creation date */
9677 xabdat.xab$l_nxt = (void *) &xabfhc;
9679 xabfhc = cc$rms_xabfhc; /* To get record length */
9680 xabfhc.xab$l_nxt = (void *) &xabsum;
9682 xabsum = cc$rms_xabsum; /* To get key and area information */
9684 if (!((sts = sys$open(&fab_in)) & 1)) {
9685 set_vaxc_errno(sts);
9687 case RMS$_FNF: case RMS$_DNF:
9688 set_errno(ENOENT); break;
9690 set_errno(ENOTDIR); break;
9692 set_errno(ENODEV); break;
9694 set_errno(EINVAL); break;
9696 set_errno(EACCES); break;
9704 fab_out.fab$w_ifi = 0;
9705 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
9706 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
9707 fab_out.fab$l_fop = FAB$M_SQO;
9708 fab_out.fab$l_fna = vmsout;
9709 fab_out.fab$b_fns = strlen(vmsout);
9710 fab_out.fab$l_dna = nam.nam$l_name;
9711 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
9713 if (preserve_dates == 0) { /* Act like DCL COPY */
9714 nam.nam$b_nop |= NAM$M_SYNCHK;
9715 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
9716 if (!((sts = sys$parse(&fab_out)) & 1)) {
9717 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
9718 set_vaxc_errno(sts);
9721 fab_out.fab$l_xab = (void *) &xabdat;
9722 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
9724 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
9725 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
9726 preserve_dates =0; /* bitmask from this point forward */
9728 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
9729 if (!((sts = sys$create(&fab_out)) & 1)) {
9730 set_vaxc_errno(sts);
9733 set_errno(ENOENT); break;
9735 set_errno(ENOTDIR); break;
9737 set_errno(ENODEV); break;
9739 set_errno(EINVAL); break;
9741 set_errno(EACCES); break;
9747 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
9748 if (preserve_dates & 2) {
9749 /* sys$close() will process xabrdt, not xabdat */
9750 xabrdt = cc$rms_xabrdt;
9752 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
9754 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
9755 * is unsigned long[2], while DECC & VAXC use a struct */
9756 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
9758 fab_out.fab$l_xab = (void *) &xabrdt;
9761 rab_in = cc$rms_rab;
9762 rab_in.rab$l_fab = &fab_in;
9763 rab_in.rab$l_rop = RAB$M_BIO;
9764 rab_in.rab$l_ubf = ubf;
9765 rab_in.rab$w_usz = sizeof ubf;
9766 if (!((sts = sys$connect(&rab_in)) & 1)) {
9767 sys$close(&fab_in); sys$close(&fab_out);
9768 set_errno(EVMSERR); set_vaxc_errno(sts);
9772 rab_out = cc$rms_rab;
9773 rab_out.rab$l_fab = &fab_out;
9774 rab_out.rab$l_rbf = ubf;
9775 if (!((sts = sys$connect(&rab_out)) & 1)) {
9776 sys$close(&fab_in); sys$close(&fab_out);
9777 set_errno(EVMSERR); set_vaxc_errno(sts);
9781 while ((sts = sys$read(&rab_in))) { /* always true */
9782 if (sts == RMS$_EOF) break;
9783 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
9784 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
9785 sys$close(&fab_in); sys$close(&fab_out);
9786 set_errno(EVMSERR); set_vaxc_errno(sts);
9791 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
9792 sys$close(&fab_in); sys$close(&fab_out);
9793 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
9795 set_errno(EVMSERR); set_vaxc_errno(sts);
9801 } /* end of rmscopy() */
9805 /*** The following glue provides 'hooks' to make some of the routines
9806 * from this file available from Perl. These routines are sufficiently
9807 * basic, and are required sufficiently early in the build process,
9808 * that's it's nice to have them available to miniperl as well as the
9809 * full Perl, so they're set up here instead of in an extension. The
9810 * Perl code which handles importation of these names into a given
9811 * package lives in [.VMS]Filespec.pm in @INC.
9815 rmsexpand_fromperl(pTHX_ CV *cv)
9818 char *fspec, *defspec = NULL, *rslt;
9821 if (!items || items > 2)
9822 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
9823 fspec = SvPV(ST(0),n_a);
9824 if (!fspec || !*fspec) XSRETURN_UNDEF;
9825 if (items == 2) defspec = SvPV(ST(1),n_a);
9827 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
9828 ST(0) = sv_newmortal();
9829 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
9834 vmsify_fromperl(pTHX_ CV *cv)
9840 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
9841 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
9842 ST(0) = sv_newmortal();
9843 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
9848 unixify_fromperl(pTHX_ CV *cv)
9854 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
9855 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
9856 ST(0) = sv_newmortal();
9857 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
9862 fileify_fromperl(pTHX_ CV *cv)
9868 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
9869 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
9870 ST(0) = sv_newmortal();
9871 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
9876 pathify_fromperl(pTHX_ CV *cv)
9882 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
9883 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
9884 ST(0) = sv_newmortal();
9885 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
9890 vmspath_fromperl(pTHX_ CV *cv)
9896 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
9897 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
9898 ST(0) = sv_newmortal();
9899 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
9904 unixpath_fromperl(pTHX_ CV *cv)
9910 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
9911 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
9912 ST(0) = sv_newmortal();
9913 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
9918 candelete_fromperl(pTHX_ CV *cv)
9921 char fspec[NAM$C_MAXRSS+1], *fsp;
9926 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
9928 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
9929 if (SvTYPE(mysv) == SVt_PVGV) {
9930 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
9931 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9938 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
9939 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9945 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
9950 rmscopy_fromperl(pTHX_ CV *cv)
9953 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
9955 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
9956 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9957 unsigned long int sts;
9962 if (items < 2 || items > 3)
9963 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
9965 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
9966 if (SvTYPE(mysv) == SVt_PVGV) {
9967 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
9968 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9975 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
9976 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9981 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
9982 if (SvTYPE(mysv) == SVt_PVGV) {
9983 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
9984 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9991 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
9992 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9997 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
9999 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
10005 mod2fname(pTHX_ CV *cv)
10008 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
10009 workbuff[NAM$C_MAXRSS*1 + 1];
10010 int total_namelen = 3, counter, num_entries;
10011 /* ODS-5 ups this, but we want to be consistent, so... */
10012 int max_name_len = 39;
10013 AV *in_array = (AV *)SvRV(ST(0));
10015 num_entries = av_len(in_array);
10017 /* All the names start with PL_. */
10018 strcpy(ultimate_name, "PL_");
10020 /* Clean up our working buffer */
10021 Zero(work_name, sizeof(work_name), char);
10023 /* Run through the entries and build up a working name */
10024 for(counter = 0; counter <= num_entries; counter++) {
10025 /* If it's not the first name then tack on a __ */
10027 strcat(work_name, "__");
10029 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
10033 /* Check to see if we actually have to bother...*/
10034 if (strlen(work_name) + 3 <= max_name_len) {
10035 strcat(ultimate_name, work_name);
10037 /* It's too darned big, so we need to go strip. We use the same */
10038 /* algorithm as xsubpp does. First, strip out doubled __ */
10039 char *source, *dest, last;
10042 for (source = work_name; *source; source++) {
10043 if (last == *source && last == '_') {
10049 /* Go put it back */
10050 strcpy(work_name, workbuff);
10051 /* Is it still too big? */
10052 if (strlen(work_name) + 3 > max_name_len) {
10053 /* Strip duplicate letters */
10056 for (source = work_name; *source; source++) {
10057 if (last == toupper(*source)) {
10061 last = toupper(*source);
10063 strcpy(work_name, workbuff);
10066 /* Is it *still* too big? */
10067 if (strlen(work_name) + 3 > max_name_len) {
10068 /* Too bad, we truncate */
10069 work_name[max_name_len - 2] = 0;
10071 strcat(ultimate_name, work_name);
10074 /* Okay, return it */
10075 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
10080 hushexit_fromperl(pTHX_ CV *cv)
10085 VMSISH_HUSHED = SvTRUE(ST(0));
10087 ST(0) = boolSV(VMSISH_HUSHED);
10093 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec);
10096 vms_realpath_fromperl(pTHX_ CV *cv)
10099 char *fspec, *rslt_spec, *rslt;
10102 if (!items || items != 1)
10103 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
10105 fspec = SvPV(ST(0),n_a);
10106 if (!fspec || !*fspec) XSRETURN_UNDEF;
10108 Newx(rslt_spec, VMS_MAXRSS + 1, char);
10109 rslt = do_vms_realpath(fspec, rslt_spec);
10110 ST(0) = sv_newmortal();
10112 sv_usepvn(ST(0),rslt,strlen(rslt));
10114 Safefree(rslt_spec);
10119 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10120 int do_vms_case_tolerant(void);
10123 vms_case_tolerant_fromperl(pTHX_ CV *cv)
10126 ST(0) = boolSV(do_vms_case_tolerant());
10132 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
10133 struct interp_intern *dst)
10135 memcpy(dst,src,sizeof(struct interp_intern));
10139 Perl_sys_intern_clear(pTHX)
10144 Perl_sys_intern_init(pTHX)
10146 unsigned int ix = RAND_MAX;
10151 /* fix me later to track running under GNV */
10152 /* this allows some limited testing */
10153 MY_POSIX_EXIT = decc_filename_unix_report;
10156 MY_INV_RAND_MAX = 1./x;
10160 init_os_extras(void)
10163 char* file = __FILE__;
10164 char temp_buff[512];
10165 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
10166 no_translate_barewords = TRUE;
10168 no_translate_barewords = FALSE;
10171 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
10172 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
10173 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
10174 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
10175 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
10176 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
10177 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
10178 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
10179 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
10180 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
10181 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
10183 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
10185 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10186 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
10189 store_pipelocs(aTHX); /* will redo any earlier attempts */
10196 #if __CRTL_VER == 80200000
10197 /* This missed getting in to the DECC SDK for 8.2 */
10198 char *realpath(const char *file_name, char * resolved_name, ...);
10201 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
10202 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
10203 * The perl fallback routine to provide realpath() is not as efficient
10207 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10209 return realpath(filespec, outbuf);
10213 /* External entry points */
10214 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10215 { return do_vms_realpath(filespec, outbuf); }
10217 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10222 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10223 /* case_tolerant */
10225 /*{{{int do_vms_case_tolerant(void)*/
10226 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
10227 * controlled by a process setting.
10229 int do_vms_case_tolerant(void)
10231 return vms_process_case_tolerant;
10234 /* External entry points */
10235 int Perl_vms_case_tolerant(void)
10236 { return do_vms_case_tolerant(); }
10238 int Perl_vms_case_tolerant(void)
10239 { return vms_process_case_tolerant; }
10243 /* Start of DECC RTL Feature handling */
10245 static int sys_trnlnm
10246 (const char * logname,
10250 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
10251 const unsigned long attr = LNM$M_CASE_BLIND;
10252 struct dsc$descriptor_s name_dsc;
10254 unsigned short result;
10255 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
10258 name_dsc.dsc$w_length = strlen(logname);
10259 name_dsc.dsc$a_pointer = (char *)logname;
10260 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10261 name_dsc.dsc$b_class = DSC$K_CLASS_S;
10263 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
10265 if ($VMS_STATUS_SUCCESS(status)) {
10267 /* Null terminate and return the string */
10268 /*--------------------------------------*/
10275 static int sys_crelnm
10276 (const char * logname,
10277 const char * value)
10280 const char * proc_table = "LNM$PROCESS_TABLE";
10281 struct dsc$descriptor_s proc_table_dsc;
10282 struct dsc$descriptor_s logname_dsc;
10283 struct itmlst_3 item_list[2];
10285 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
10286 proc_table_dsc.dsc$w_length = strlen(proc_table);
10287 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10288 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
10290 logname_dsc.dsc$a_pointer = (char *) logname;
10291 logname_dsc.dsc$w_length = strlen(logname);
10292 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10293 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
10295 item_list[0].buflen = strlen(value);
10296 item_list[0].itmcode = LNM$_STRING;
10297 item_list[0].bufadr = (char *)value;
10298 item_list[0].retlen = NULL;
10300 item_list[1].buflen = 0;
10301 item_list[1].itmcode = 0;
10303 ret_val = sys$crelnm
10305 (const struct dsc$descriptor_s *)&proc_table_dsc,
10306 (const struct dsc$descriptor_s *)&logname_dsc,
10308 (const struct item_list_3 *) item_list);
10314 /* C RTL Feature settings */
10316 static int set_features
10317 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
10318 int (* cli_routine)(void), /* Not documented */
10319 void *image_info) /* Not documented */
10326 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
10327 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
10328 unsigned long case_perm;
10329 unsigned long case_image;
10331 /* hacks to see if known bugs are still present for testing */
10333 /* Readdir is returning filenames in VMS syntax always */
10334 decc_bug_readdir_efs1 = 1;
10335 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
10336 if ($VMS_STATUS_SUCCESS(status)) {
10337 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10338 decc_bug_readdir_efs1 = 1;
10340 decc_bug_readdir_efs1 = 0;
10343 /* PCP mode requires creating /dev/null special device file */
10344 decc_bug_devnull = 0;
10345 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
10346 if ($VMS_STATUS_SUCCESS(status)) {
10347 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10348 decc_bug_devnull = 1;
10351 /* fgetname returning a VMS name in UNIX mode */
10352 decc_bug_fgetname = 1;
10353 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
10354 if ($VMS_STATUS_SUCCESS(status)) {
10355 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10356 decc_bug_fgetname = 1;
10358 decc_bug_fgetname = 0;
10361 /* UNIX directory names with no paths are broken in a lot of places */
10362 decc_dir_barename = 1;
10363 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
10364 if ($VMS_STATUS_SUCCESS(status)) {
10365 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10366 decc_dir_barename = 1;
10368 decc_dir_barename = 0;
10371 #if __CRTL_VER >= 70300000 && !defined(__VAX)
10372 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
10374 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
10375 if (decc_disable_to_vms_logname_translation < 0)
10376 decc_disable_to_vms_logname_translation = 0;
10379 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
10381 decc_efs_case_preserve = decc$feature_get_value(s, 1);
10382 if (decc_efs_case_preserve < 0)
10383 decc_efs_case_preserve = 0;
10386 s = decc$feature_get_index("DECC$EFS_CHARSET");
10388 decc_efs_charset = decc$feature_get_value(s, 1);
10389 if (decc_efs_charset < 0)
10390 decc_efs_charset = 0;
10393 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
10395 decc_filename_unix_report = decc$feature_get_value(s, 1);
10396 if (decc_filename_unix_report > 0)
10397 decc_filename_unix_report = 1;
10399 decc_filename_unix_report = 0;
10402 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
10404 decc_filename_unix_only = decc$feature_get_value(s, 1);
10405 if (decc_filename_unix_only > 0) {
10406 decc_filename_unix_only = 1;
10409 decc_filename_unix_only = 0;
10413 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
10415 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
10416 if (decc_filename_unix_no_version < 0)
10417 decc_filename_unix_no_version = 0;
10420 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
10422 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
10423 if (decc_readdir_dropdotnotype < 0)
10424 decc_readdir_dropdotnotype = 0;
10427 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
10428 if ($VMS_STATUS_SUCCESS(status)) {
10429 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
10431 dflt = decc$feature_get_value(s, 4);
10433 decc_disable_posix_root = decc$feature_get_value(s, 1);
10434 if (decc_disable_posix_root <= 0) {
10435 decc$feature_set_value(s, 1, 1);
10436 decc_disable_posix_root = 1;
10440 /* Traditionally Perl assumes this is off */
10441 decc_disable_posix_root = 1;
10442 decc$feature_set_value(s, 1, 1);
10447 #if __CRTL_VER >= 80200000
10448 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
10450 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
10451 if (decc_posix_compliant_pathnames < 0)
10452 decc_posix_compliant_pathnames = 0;
10453 if (decc_posix_compliant_pathnames > 4)
10454 decc_posix_compliant_pathnames = 0;
10459 status = sys_trnlnm
10460 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
10461 if ($VMS_STATUS_SUCCESS(status)) {
10462 val_str[0] = _toupper(val_str[0]);
10463 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10464 decc_disable_to_vms_logname_translation = 1;
10469 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
10470 if ($VMS_STATUS_SUCCESS(status)) {
10471 val_str[0] = _toupper(val_str[0]);
10472 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10473 decc_efs_case_preserve = 1;
10478 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
10479 if ($VMS_STATUS_SUCCESS(status)) {
10480 val_str[0] = _toupper(val_str[0]);
10481 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10482 decc_filename_unix_report = 1;
10485 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
10486 if ($VMS_STATUS_SUCCESS(status)) {
10487 val_str[0] = _toupper(val_str[0]);
10488 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10489 decc_filename_unix_only = 1;
10490 decc_filename_unix_report = 1;
10493 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
10494 if ($VMS_STATUS_SUCCESS(status)) {
10495 val_str[0] = _toupper(val_str[0]);
10496 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10497 decc_filename_unix_no_version = 1;
10500 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
10501 if ($VMS_STATUS_SUCCESS(status)) {
10502 val_str[0] = _toupper(val_str[0]);
10503 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10504 decc_readdir_dropdotnotype = 1;
10511 /* Report true case tolerance */
10512 /*----------------------------*/
10513 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
10514 if (!$VMS_STATUS_SUCCESS(status))
10515 case_perm = PPROP$K_CASE_BLIND;
10516 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
10517 if (!$VMS_STATUS_SUCCESS(status))
10518 case_image = PPROP$K_CASE_BLIND;
10519 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
10520 (case_image == PPROP$K_CASE_SENSITIVE))
10521 vms_process_case_tolerant = 0;
10526 /* CRTL can be initialized past this point, but not before. */
10527 /* DECC$CRTL_INIT(); */
10533 /* DECC dependent attributes */
10534 #if __DECC_VER < 60560002
10536 #define not_executable
10538 #define relative ,rel
10539 #define not_executable ,noexe
10542 #pragma extern_model save
10543 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
10545 const __align (LONGWORD) int spare[8] = {0};
10546 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
10549 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
10550 nowrt,noshr relative not_executable
10552 const long vms_cc_features = (const long)set_features;
10555 ** Force a reference to LIB$INITIALIZE to ensure it
10556 ** exists in the image.
10558 int lib$initialize(void);
10560 #pragma extern_model strict_refdef
10562 int lib_init_ref = (int) lib$initialize;
10565 #pragma extern_model restore