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 = 1;
250 int decc_bug_fgetname = 0;
251 int decc_dir_barename = 0;
253 static int vms_debug_on_exception = 0;
255 /* Is this a UNIX file specification?
256 * No longer a simple check with EFS file specs
257 * For now, not a full check, but need to
258 * handle POSIX ^UP^ specifications
259 * Fixing to handle ^/ cases would require
260 * changes to many other conversion routines.
263 static is_unix_filespec(const char *path)
269 if (strncmp(path,"\"^UP^",5) != 0) {
270 pch1 = strchr(path, '/');
275 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
276 if (decc_filename_unix_report || decc_filename_unix_only) {
277 if (strcmp(path,".") == 0)
287 * Routine to retrieve the maximum equivalence index for an input
288 * logical name. Some calls to this routine have no knowledge if
289 * the variable is a logical or not. So on error we return a max
292 /*{{{int my_maxidx(const char *lnm) */
294 my_maxidx(const char *lnm)
298 int attr = LNM$M_CASE_BLIND;
299 struct dsc$descriptor lnmdsc;
300 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
303 lnmdsc.dsc$w_length = strlen(lnm);
304 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
305 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
306 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
308 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
309 if ((status & 1) == 0)
316 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
318 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
319 struct dsc$descriptor_s **tabvec, unsigned long int flags)
322 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
323 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
324 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
326 unsigned char acmode;
327 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
328 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
329 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
330 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
332 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
333 #if defined(PERL_IMPLICIT_CONTEXT)
336 aTHX = PERL_GET_INTERP;
342 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
343 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
345 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
346 *cp2 = _toupper(*cp1);
347 if (cp1 - lnm > LNM$C_NAMLENGTH) {
348 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
352 lnmdsc.dsc$w_length = cp1 - lnm;
353 lnmdsc.dsc$a_pointer = uplnm;
354 uplnm[lnmdsc.dsc$w_length] = '\0';
355 secure = flags & PERL__TRNENV_SECURE;
356 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
357 if (!tabvec || !*tabvec) tabvec = env_tables;
359 for (curtab = 0; tabvec[curtab]; curtab++) {
360 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
361 if (!ivenv && !secure) {
366 Perl_warn(aTHX_ "Can't read CRTL environ\n");
369 retsts = SS$_NOLOGNAM;
370 for (i = 0; environ[i]; i++) {
371 if ((eq = strchr(environ[i],'=')) &&
372 lnmdsc.dsc$w_length == (eq - environ[i]) &&
373 !strncmp(environ[i],uplnm,eq - environ[i])) {
375 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
376 if (!eqvlen) continue;
381 if (retsts != SS$_NOLOGNAM) break;
384 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
385 !str$case_blind_compare(&tmpdsc,&clisym)) {
386 if (!ivsym && !secure) {
387 unsigned short int deflen = LNM$C_NAMLENGTH;
388 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
389 /* dynamic dsc to accomodate possible long value */
390 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
391 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
393 if (eqvlen > MAX_DCL_SYMBOL) {
394 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
395 eqvlen = MAX_DCL_SYMBOL;
396 /* Special hack--we might be called before the interpreter's */
397 /* fully initialized, in which case either thr or PL_curcop */
398 /* might be bogus. We have to check, since ckWARN needs them */
399 /* both to be valid if running threaded */
400 if (ckWARN(WARN_MISC)) {
401 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
404 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
406 _ckvmssts(lib$sfree1_dd(&eqvdsc));
407 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
408 if (retsts == LIB$_NOSUCHSYM) continue;
413 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
414 midx = my_maxidx(lnm);
415 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
416 lnmlst[1].bufadr = cp2;
418 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
419 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
420 if (retsts == SS$_NOLOGNAM) break;
421 /* PPFs have a prefix */
424 *((int *)uplnm) == *((int *)"SYS$") &&
426 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
427 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
428 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
429 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
430 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
431 memmove(eqv,eqv+4,eqvlen-4);
437 if ((retsts == SS$_IVLOGNAM) ||
438 (retsts == SS$_NOLOGNAM)) { continue; }
441 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
442 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
443 if (retsts == SS$_NOLOGNAM) continue;
446 eqvlen = strlen(eqv);
450 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
451 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
452 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
453 retsts == SS$_NOLOGNAM) {
454 set_errno(EINVAL); set_vaxc_errno(retsts);
456 else _ckvmssts(retsts);
458 } /* end of vmstrnenv */
461 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
462 /* Define as a function so we can access statics. */
463 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
465 return vmstrnenv(lnm,eqv,idx,fildev,
466 #ifdef SECURE_INTERNAL_GETENV
467 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
476 * Note: Uses Perl temp to store result so char * can be returned to
477 * caller; this pointer will be invalidated at next Perl statement
479 * We define this as a function rather than a macro in terms of my_getenv_len()
480 * so that it'll work when PL_curinterp is undefined (and we therefore can't
483 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
485 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
488 static char *__my_getenv_eqv = NULL;
489 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
490 unsigned long int idx = 0;
491 int trnsuccess, success, secure, saverr, savvmserr;
495 midx = my_maxidx(lnm) + 1;
497 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
498 /* Set up a temporary buffer for the return value; Perl will
499 * clean it up at the next statement transition */
500 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
501 if (!tmpsv) return NULL;
505 /* Assume no interpreter ==> single thread */
506 if (__my_getenv_eqv != NULL) {
507 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
510 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
512 eqv = __my_getenv_eqv;
515 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
516 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
518 getcwd(eqv,LNM$C_NAMLENGTH);
522 /* Get rid of "000000/ in rooted filespecs */
525 zeros = strstr(eqv, "/000000/");
528 mlen = len - (zeros - eqv) - 7;
529 memmove(zeros, &zeros[7], mlen);
537 /* Impose security constraints only if tainting */
539 /* Impose security constraints only if tainting */
540 secure = PL_curinterp ? PL_tainting : will_taint;
541 saverr = errno; savvmserr = vaxc$errno;
548 #ifdef SECURE_INTERNAL_GETENV
549 secure ? PERL__TRNENV_SECURE : 0
555 /* For the getenv interface we combine all the equivalence names
556 * of a search list logical into one value to acquire a maximum
557 * value length of 255*128 (assuming %ENV is using logicals).
559 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
561 /* If the name contains a semicolon-delimited index, parse it
562 * off and make sure we only retrieve the equivalence name for
564 if ((cp2 = strchr(lnm,';')) != NULL) {
566 uplnm[cp2-lnm] = '\0';
567 idx = strtoul(cp2+1,NULL,0);
569 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
572 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
574 /* Discard NOLOGNAM on internal calls since we're often looking
575 * for an optional name, and this "error" often shows up as the
576 * (bogus) exit status for a die() call later on. */
577 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
578 return success ? eqv : Nullch;
581 } /* end of my_getenv() */
585 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
587 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
591 unsigned long idx = 0;
593 static char *__my_getenv_len_eqv = NULL;
594 int secure, saverr, savvmserr;
597 midx = my_maxidx(lnm) + 1;
599 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
600 /* Set up a temporary buffer for the return value; Perl will
601 * clean it up at the next statement transition */
602 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
603 if (!tmpsv) return NULL;
607 /* Assume no interpreter ==> single thread */
608 if (__my_getenv_len_eqv != NULL) {
609 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
612 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
614 buf = __my_getenv_len_eqv;
617 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
618 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
621 getcwd(buf,LNM$C_NAMLENGTH);
624 /* Get rid of "000000/ in rooted filespecs */
626 zeros = strstr(buf, "/000000/");
629 mlen = *len - (zeros - buf) - 7;
630 memmove(zeros, &zeros[7], mlen);
639 /* Impose security constraints only if tainting */
640 secure = PL_curinterp ? PL_tainting : will_taint;
641 saverr = errno; savvmserr = vaxc$errno;
648 #ifdef SECURE_INTERNAL_GETENV
649 secure ? PERL__TRNENV_SECURE : 0
655 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
657 if ((cp2 = strchr(lnm,';')) != NULL) {
660 idx = strtoul(cp2+1,NULL,0);
662 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
665 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
667 /* Get rid of "000000/ in rooted filespecs */
670 zeros = strstr(buf, "/000000/");
673 mlen = *len - (zeros - buf) - 7;
674 memmove(zeros, &zeros[7], mlen);
680 /* Discard NOLOGNAM on internal calls since we're often looking
681 * for an optional name, and this "error" often shows up as the
682 * (bogus) exit status for a die() call later on. */
683 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
684 return *len ? buf : Nullch;
687 } /* end of my_getenv_len() */
690 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
692 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
694 /*{{{ void prime_env_iter() */
697 /* Fill the %ENV associative array with all logical names we can
698 * find, in preparation for iterating over it.
701 static int primed = 0;
702 HV *seenhv = NULL, *envhv;
704 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
705 unsigned short int chan;
706 #ifndef CLI$M_TRUSTED
707 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
709 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
710 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
712 bool have_sym = FALSE, have_lnm = FALSE;
713 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
714 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
715 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
716 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
717 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
718 #if defined(PERL_IMPLICIT_CONTEXT)
721 #if defined(USE_ITHREADS)
722 static perl_mutex primenv_mutex;
723 MUTEX_INIT(&primenv_mutex);
726 #if defined(PERL_IMPLICIT_CONTEXT)
727 /* We jump through these hoops because we can be called at */
728 /* platform-specific initialization time, which is before anything is */
729 /* set up--we can't even do a plain dTHX since that relies on the */
730 /* interpreter structure to be initialized */
732 aTHX = PERL_GET_INTERP;
738 if (primed || !PL_envgv) return;
739 MUTEX_LOCK(&primenv_mutex);
740 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
741 envhv = GvHVn(PL_envgv);
742 /* Perform a dummy fetch as an lval to insure that the hash table is
743 * set up. Otherwise, the hv_store() will turn into a nullop. */
744 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
746 for (i = 0; env_tables[i]; i++) {
747 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
748 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
749 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
751 if (have_sym || have_lnm) {
752 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
753 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
754 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
755 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
758 for (i--; i >= 0; i--) {
759 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
762 for (j = 0; environ[j]; j++) {
763 if (!(start = strchr(environ[j],'='))) {
764 if (ckWARN(WARN_INTERNAL))
765 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
769 sv = newSVpv(start,0);
771 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
776 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
777 !str$case_blind_compare(&tmpdsc,&clisym)) {
778 strcpy(cmd,"Show Symbol/Global *");
779 cmddsc.dsc$w_length = 20;
780 if (env_tables[i]->dsc$w_length == 12 &&
781 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
782 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
783 flags = defflags | CLI$M_NOLOGNAM;
786 strcpy(cmd,"Show Logical *");
787 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
788 strcat(cmd," /Table=");
789 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
790 cmddsc.dsc$w_length = strlen(cmd);
792 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
793 flags = defflags | CLI$M_NOCLISYM;
796 /* Create a new subprocess to execute each command, to exclude the
797 * remote possibility that someone could subvert a mbx or file used
798 * to write multiple commands to a single subprocess.
801 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
802 0,&riseandshine,0,0,&clidsc,&clitabdsc);
803 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
804 defflags &= ~CLI$M_TRUSTED;
805 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
807 if (!buf) Newx(buf,mbxbufsiz + 1,char);
808 if (seenhv) SvREFCNT_dec(seenhv);
811 char *cp1, *cp2, *key;
812 unsigned long int sts, iosb[2], retlen, keylen;
815 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
816 if (sts & 1) sts = iosb[0] & 0xffff;
817 if (sts == SS$_ENDOFFILE) {
819 while (substs == 0) { sys$hiber(); wakect++;}
820 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
825 retlen = iosb[0] >> 16;
826 if (!retlen) continue; /* blank line */
828 if (iosb[1] != subpid) {
830 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
834 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
835 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
837 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
838 if (*cp1 == '(' || /* Logical name table name */
839 *cp1 == '=' /* Next eqv of searchlist */) continue;
840 if (*cp1 == '"') cp1++;
841 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
842 key = cp1; keylen = cp2 - cp1;
843 if (keylen && hv_exists(seenhv,key,keylen)) continue;
844 while (*cp2 && *cp2 != '=') cp2++;
845 while (*cp2 && *cp2 == '=') cp2++;
846 while (*cp2 && *cp2 == ' ') cp2++;
847 if (*cp2 == '"') { /* String translation; may embed "" */
848 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
849 cp2++; cp1--; /* Skip "" surrounding translation */
851 else { /* Numeric translation */
852 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
853 cp1--; /* stop on last non-space char */
855 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
856 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
859 PERL_HASH(hash,key,keylen);
861 if (cp1 == cp2 && *cp2 == '.') {
862 /* A single dot usually means an unprintable character, such as a null
863 * to indicate a zero-length value. Get the actual value to make sure.
865 char lnm[LNM$C_NAMLENGTH+1];
866 char eqv[MAX_DCL_SYMBOL+1];
867 strncpy(lnm, key, keylen);
868 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
869 sv = newSVpvn(eqv, strlen(eqv));
872 sv = newSVpvn(cp2,cp1 - cp2 + 1);
876 hv_store(envhv,key,keylen,sv,hash);
877 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
879 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
880 /* get the PPFs for this process, not the subprocess */
881 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
882 char eqv[LNM$C_NAMLENGTH+1];
884 for (i = 0; ppfs[i]; i++) {
885 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
886 sv = newSVpv(eqv,trnlen);
888 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
893 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
894 if (buf) Safefree(buf);
895 if (seenhv) SvREFCNT_dec(seenhv);
896 MUTEX_UNLOCK(&primenv_mutex);
899 } /* end of prime_env_iter */
903 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
904 /* Define or delete an element in the same "environment" as
905 * vmstrnenv(). If an element is to be deleted, it's removed from
906 * the first place it's found. If it's to be set, it's set in the
907 * place designated by the first element of the table vector.
908 * Like setenv() returns 0 for success, non-zero on error.
911 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
914 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
915 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
917 unsigned long int retsts, usermode = PSL$C_USER;
918 struct itmlst_3 *ile, *ilist;
919 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
920 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
921 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
922 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
923 $DESCRIPTOR(local,"_LOCAL");
926 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
930 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
931 *cp2 = _toupper(*cp1);
932 if (cp1 - lnm > LNM$C_NAMLENGTH) {
933 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
937 lnmdsc.dsc$w_length = cp1 - lnm;
938 if (!tabvec || !*tabvec) tabvec = env_tables;
940 if (!eqv) { /* we're deleting n element */
941 for (curtab = 0; tabvec[curtab]; curtab++) {
942 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
944 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
945 if ((cp1 = strchr(environ[i],'=')) &&
946 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
947 !strncmp(environ[i],lnm,cp1 - environ[i])) {
949 return setenv(lnm,"",1) ? vaxc$errno : 0;
952 ivenv = 1; retsts = SS$_NOLOGNAM;
954 if (ckWARN(WARN_INTERNAL))
955 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
956 ivenv = 1; retsts = SS$_NOSUCHPGM;
962 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
963 !str$case_blind_compare(&tmpdsc,&clisym)) {
964 unsigned int symtype;
965 if (tabvec[curtab]->dsc$w_length == 12 &&
966 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
967 !str$case_blind_compare(&tmpdsc,&local))
968 symtype = LIB$K_CLI_LOCAL_SYM;
969 else symtype = LIB$K_CLI_GLOBAL_SYM;
970 retsts = lib$delete_symbol(&lnmdsc,&symtype);
971 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
972 if (retsts == LIB$_NOSUCHSYM) continue;
976 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
977 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
978 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
979 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
980 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
984 else { /* we're defining a value */
985 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
987 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
989 if (ckWARN(WARN_INTERNAL))
990 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
991 retsts = SS$_NOSUCHPGM;
995 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
996 eqvdsc.dsc$w_length = strlen(eqv);
997 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
998 !str$case_blind_compare(&tmpdsc,&clisym)) {
999 unsigned int symtype;
1000 if (tabvec[0]->dsc$w_length == 12 &&
1001 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1002 !str$case_blind_compare(&tmpdsc,&local))
1003 symtype = LIB$K_CLI_LOCAL_SYM;
1004 else symtype = LIB$K_CLI_GLOBAL_SYM;
1005 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1008 if (!*eqv) eqvdsc.dsc$w_length = 1;
1009 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1011 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1012 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1013 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1014 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1015 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1016 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1019 Newx(ilist,nseg+1,struct itmlst_3);
1022 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1025 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1027 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1028 ile->itmcode = LNM$_STRING;
1030 if ((j+1) == nseg) {
1031 ile->buflen = strlen(c);
1032 /* in case we are truncating one that's too long */
1033 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1036 ile->buflen = LNM$C_NAMLENGTH;
1040 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1044 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1049 if (!(retsts & 1)) {
1051 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1052 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1053 set_errno(EVMSERR); break;
1054 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1055 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1056 set_errno(EINVAL); break;
1063 set_vaxc_errno(retsts);
1064 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1067 /* We reset error values on success because Perl does an hv_fetch()
1068 * before each hv_store(), and if the thing we're setting didn't
1069 * previously exist, we've got a leftover error message. (Of course,
1070 * this fails in the face of
1071 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1072 * in that the error reported in $! isn't spurious,
1073 * but it's right more often than not.)
1075 set_errno(0); set_vaxc_errno(retsts);
1079 } /* end of vmssetenv() */
1082 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1083 /* This has to be a function since there's a prototype for it in proto.h */
1085 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1088 int len = strlen(lnm);
1092 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1093 if (!strcmp(uplnm,"DEFAULT")) {
1094 if (eqv && *eqv) my_chdir(eqv);
1098 #ifndef RTL_USES_UTC
1099 if (len == 6 || len == 2) {
1102 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1104 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1105 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1109 (void) vmssetenv(lnm,eqv,NULL);
1113 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1115 * sets a user-mode logical in the process logical name table
1116 * used for redirection of sys$error
1119 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1121 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1122 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1123 unsigned long int iss, attr = LNM$M_CONFINE;
1124 unsigned char acmode = PSL$C_USER;
1125 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1127 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1128 d_name.dsc$w_length = strlen(name);
1130 lnmlst[0].buflen = strlen(eqv);
1131 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1133 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1134 if (!(iss&1)) lib$signal(iss);
1139 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1140 /* my_crypt - VMS password hashing
1141 * my_crypt() provides an interface compatible with the Unix crypt()
1142 * C library function, and uses sys$hash_password() to perform VMS
1143 * password hashing. The quadword hashed password value is returned
1144 * as a NUL-terminated 8 character string. my_crypt() does not change
1145 * the case of its string arguments; in order to match the behavior
1146 * of LOGINOUT et al., alphabetic characters in both arguments must
1147 * be upcased by the caller.
1149 * - fix me to call ACM services when available
1152 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1154 # ifndef UAI$C_PREFERRED_ALGORITHM
1155 # define UAI$C_PREFERRED_ALGORITHM 127
1157 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1158 unsigned short int salt = 0;
1159 unsigned long int sts;
1161 unsigned short int dsc$w_length;
1162 unsigned char dsc$b_type;
1163 unsigned char dsc$b_class;
1164 const char * dsc$a_pointer;
1165 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1166 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1167 struct itmlst_3 uailst[3] = {
1168 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1169 { sizeof salt, UAI$_SALT, &salt, 0},
1170 { 0, 0, NULL, NULL}};
1171 static char hash[9];
1173 usrdsc.dsc$w_length = strlen(usrname);
1174 usrdsc.dsc$a_pointer = usrname;
1175 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1177 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1181 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1186 set_vaxc_errno(sts);
1187 if (sts != RMS$_RNF) return NULL;
1190 txtdsc.dsc$w_length = strlen(textpasswd);
1191 txtdsc.dsc$a_pointer = textpasswd;
1192 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1193 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1196 return (char *) hash;
1198 } /* end of my_crypt() */
1202 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1203 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1204 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1206 /* fixup barenames that are directories for internal use.
1207 * There have been problems with the consistent handling of UNIX
1208 * style directory names when routines are presented with a name that
1209 * has no directory delimitors at all. So this routine will eventually
1212 static char * fixup_bare_dirnames(const char * name)
1214 if (decc_disable_to_vms_logname_translation) {
1221 * A little hack to get around a bug in some implemenation of remove()
1222 * that do not know how to delete a directory
1224 * Delete any file to which user has control access, regardless of whether
1225 * delete access is explicitly allowed.
1226 * Limitations: User must have write access to parent directory.
1227 * Does not block signals or ASTs; if interrupted in midstream
1228 * may leave file with an altered ACL.
1231 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1233 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1235 char *vmsname, *rspec;
1237 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1238 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1239 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1241 unsigned char myace$b_length;
1242 unsigned char myace$b_type;
1243 unsigned short int myace$w_flags;
1244 unsigned long int myace$l_access;
1245 unsigned long int myace$l_ident;
1246 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1247 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1248 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1250 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1251 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1252 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1253 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1254 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1255 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1257 /* Expand the input spec using RMS, since the CRTL remove() and
1258 * system services won't do this by themselves, so we may miss
1259 * a file "hiding" behind a logical name or search list. */
1260 Newx(vmsname, NAM$C_MAXRSS+1, char);
1261 if (do_tovmsspec(name,vmsname,0) == NULL) {
1266 if (decc_posix_compliant_pathnames) {
1267 /* In POSIX mode, we prefer to remove the UNIX name */
1269 remove_name = (char *)name;
1272 Newx(rspec, NAM$C_MAXRSS+1, char);
1273 if (do_rmsexpand(vmsname, rspec, 1, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1279 remove_name = rspec;
1282 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1284 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1285 Newx(remove_name, NAM$C_MAXRSS+1, char);
1286 do_pathify_dirspec(name, remove_name, 0);
1287 if (!rmdir(remove_name)) {
1289 Safefree(remove_name);
1291 return 0; /* Can we just get rid of it? */
1295 if (!rmdir(remove_name)) {
1297 return 0; /* Can we just get rid of it? */
1303 if (!remove(remove_name)) {
1305 return 0; /* Can we just get rid of it? */
1308 /* If not, can changing protections help? */
1309 if (vaxc$errno != RMS$_PRV) {
1314 /* No, so we get our own UIC to use as a rights identifier,
1315 * and the insert an ACE at the head of the ACL which allows us
1316 * to delete the file.
1318 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1319 fildsc.dsc$w_length = strlen(rspec);
1320 fildsc.dsc$a_pointer = rspec;
1322 newace.myace$l_ident = oldace.myace$l_ident;
1323 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1325 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1326 set_errno(ENOENT); break;
1328 set_errno(ENOTDIR); break;
1330 set_errno(ENODEV); break;
1331 case RMS$_SYN: case SS$_INVFILFOROP:
1332 set_errno(EINVAL); break;
1334 set_errno(EACCES); break;
1338 set_vaxc_errno(aclsts);
1342 /* Grab any existing ACEs with this identifier in case we fail */
1343 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1344 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1345 || fndsts == SS$_NOMOREACE ) {
1346 /* Add the new ACE . . . */
1347 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1350 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1352 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1353 Newx(remove_name, NAM$C_MAXRSS+1, char);
1354 do_pathify_dirspec(name, remove_name, 0);
1355 rmsts = rmdir(remove_name);
1356 Safefree(remove_name);
1359 rmsts = rmdir(remove_name);
1363 rmsts = remove(remove_name);
1365 /* We blew it - dir with files in it, no write priv for
1366 * parent directory, etc. Put things back the way they were. */
1367 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1370 addlst[0].bufadr = &oldace;
1371 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1378 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1379 /* We just deleted it, so of course it's not there. Some versions of
1380 * VMS seem to return success on the unlock operation anyhow (after all
1381 * the unlock is successful), but others don't.
1383 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1384 if (aclsts & 1) aclsts = fndsts;
1385 if (!(aclsts & 1)) {
1387 set_vaxc_errno(aclsts);
1395 } /* end of kill_file() */
1399 /*{{{int do_rmdir(char *name)*/
1401 Perl_do_rmdir(pTHX_ const char *name)
1403 char dirfile[NAM$C_MAXRSS+1];
1407 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1408 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1409 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1412 } /* end of do_rmdir */
1416 * Delete any file to which user has control access, regardless of whether
1417 * delete access is explicitly allowed.
1418 * Limitations: User must have write access to parent directory.
1419 * Does not block signals or ASTs; if interrupted in midstream
1420 * may leave file with an altered ACL.
1423 /*{{{int kill_file(char *name)*/
1425 Perl_kill_file(pTHX_ const char *name)
1427 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
1428 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1429 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1430 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1432 unsigned char myace$b_length;
1433 unsigned char myace$b_type;
1434 unsigned short int myace$w_flags;
1435 unsigned long int myace$l_access;
1436 unsigned long int myace$l_ident;
1437 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1438 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1439 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1441 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1442 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1443 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1444 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1445 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1446 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1448 /* Expand the input spec using RMS, since the CRTL remove() and
1449 * system services won't do this by themselves, so we may miss
1450 * a file "hiding" behind a logical name or search list. */
1451 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
1452 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
1453 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1454 /* If not, can changing protections help? */
1455 if (vaxc$errno != RMS$_PRV) return -1;
1457 /* No, so we get our own UIC to use as a rights identifier,
1458 * and the insert an ACE at the head of the ACL which allows us
1459 * to delete the file.
1461 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1462 fildsc.dsc$w_length = strlen(rspec);
1463 fildsc.dsc$a_pointer = rspec;
1465 newace.myace$l_ident = oldace.myace$l_ident;
1466 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1468 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1469 set_errno(ENOENT); break;
1471 set_errno(ENOTDIR); break;
1473 set_errno(ENODEV); break;
1474 case RMS$_SYN: case SS$_INVFILFOROP:
1475 set_errno(EINVAL); break;
1477 set_errno(EACCES); break;
1481 set_vaxc_errno(aclsts);
1484 /* Grab any existing ACEs with this identifier in case we fail */
1485 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1486 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1487 || fndsts == SS$_NOMOREACE ) {
1488 /* Add the new ACE . . . */
1489 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1491 if ((rmsts = remove(name))) {
1492 /* We blew it - dir with files in it, no write priv for
1493 * parent directory, etc. Put things back the way they were. */
1494 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1497 addlst[0].bufadr = &oldace;
1498 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1505 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1506 /* We just deleted it, so of course it's not there. Some versions of
1507 * VMS seem to return success on the unlock operation anyhow (after all
1508 * the unlock is successful), but others don't.
1510 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1511 if (aclsts & 1) aclsts = fndsts;
1512 if (!(aclsts & 1)) {
1514 set_vaxc_errno(aclsts);
1520 } /* end of kill_file() */
1524 /*{{{int my_mkdir(char *,Mode_t)*/
1526 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1528 STRLEN dirlen = strlen(dir);
1530 /* zero length string sometimes gives ACCVIO */
1531 if (dirlen == 0) return -1;
1533 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1534 * null file name/type. However, it's commonplace under Unix,
1535 * so we'll allow it for a gain in portability.
1537 if (dir[dirlen-1] == '/') {
1538 char *newdir = savepvn(dir,dirlen-1);
1539 int ret = mkdir(newdir,mode);
1543 else return mkdir(dir,mode);
1544 } /* end of my_mkdir */
1547 /*{{{int my_chdir(char *)*/
1549 Perl_my_chdir(pTHX_ const char *dir)
1551 STRLEN dirlen = strlen(dir);
1553 /* zero length string sometimes gives ACCVIO */
1554 if (dirlen == 0) return -1;
1557 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
1558 * This does not work if DECC$EFS_CHARSET is active. Hack it here
1559 * so that existing scripts do not need to be changed.
1562 while ((dirlen > 0) && (*dir1 == ' ')) {
1567 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1569 * null file name/type. However, it's commonplace under Unix,
1570 * so we'll allow it for a gain in portability.
1572 * - Preview- '/' will be valid soon on VMS
1574 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
1575 char *newdir = savepvn(dir,dirlen-1);
1576 int ret = chdir(newdir);
1580 else return chdir(dir);
1581 } /* end of my_chdir */
1585 /*{{{FILE *my_tmpfile()*/
1592 if ((fp = tmpfile())) return fp;
1594 Newx(cp,L_tmpnam+24,char);
1595 if (decc_filename_unix_only == 0)
1596 strcpy(cp,"Sys$Scratch:");
1599 tmpnam(cp+strlen(cp));
1600 strcat(cp,".Perltmp");
1601 fp = fopen(cp,"w+","fop=dlt");
1608 #ifndef HOMEGROWN_POSIX_SIGNALS
1610 * The C RTL's sigaction fails to check for invalid signal numbers so we
1611 * help it out a bit. The docs are correct, but the actual routine doesn't
1612 * do what the docs say it will.
1614 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1616 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1617 struct sigaction* oact)
1619 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1620 SETERRNO(EINVAL, SS$_INVARG);
1623 return sigaction(sig, act, oact);
1628 #ifdef KILL_BY_SIGPRC
1629 #include <errnodef.h>
1631 /* We implement our own kill() using the undocumented system service
1632 sys$sigprc for one of two reasons:
1634 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1635 target process to do a sys$exit, which usually can't be handled
1636 gracefully...certainly not by Perl and the %SIG{} mechanism.
1638 2.) If the kill() in the CRTL can't be called from a signal
1639 handler without disappearing into the ether, i.e., the signal
1640 it purportedly sends is never trapped. Still true as of VMS 7.3.
1642 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1643 in the target process rather than calling sys$exit.
1645 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1646 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1647 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1648 with condition codes C$_SIG0+nsig*8, catching the exception on the
1649 target process and resignaling with appropriate arguments.
1651 But we don't have that VMS 7.0+ exception handler, so if you
1652 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1654 Also note that SIGTERM is listed in the docs as being "unimplemented",
1655 yet always seems to be signaled with a VMS condition code of 4 (and
1656 correctly handled for that code). So we hardwire it in.
1658 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1659 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1660 than signalling with an unrecognized (and unhandled by CRTL) code.
1663 #define _MY_SIG_MAX 17
1666 Perl_sig_to_vmscondition_int(int sig)
1668 static unsigned int sig_code[_MY_SIG_MAX+1] =
1671 SS$_HANGUP, /* 1 SIGHUP */
1672 SS$_CONTROLC, /* 2 SIGINT */
1673 SS$_CONTROLY, /* 3 SIGQUIT */
1674 SS$_RADRMOD, /* 4 SIGILL */
1675 SS$_BREAK, /* 5 SIGTRAP */
1676 SS$_OPCCUS, /* 6 SIGABRT */
1677 SS$_COMPAT, /* 7 SIGEMT */
1679 SS$_FLTOVF, /* 8 SIGFPE VAX */
1681 SS$_HPARITH, /* 8 SIGFPE AXP */
1683 SS$_ABORT, /* 9 SIGKILL */
1684 SS$_ACCVIO, /* 10 SIGBUS */
1685 SS$_ACCVIO, /* 11 SIGSEGV */
1686 SS$_BADPARAM, /* 12 SIGSYS */
1687 SS$_NOMBX, /* 13 SIGPIPE */
1688 SS$_ASTFLT, /* 14 SIGALRM */
1694 #if __VMS_VER >= 60200000
1695 static int initted = 0;
1698 sig_code[16] = C$_SIGUSR1;
1699 sig_code[17] = C$_SIGUSR2;
1703 if (sig < _SIG_MIN) return 0;
1704 if (sig > _MY_SIG_MAX) return 0;
1705 return sig_code[sig];
1709 Perl_sig_to_vmscondition(int sig)
1712 if (vms_debug_on_exception != 0)
1713 lib$signal(SS$_DEBUG);
1715 return Perl_sig_to_vmscondition_int(sig);
1720 Perl_my_kill(int pid, int sig)
1725 int sys$sigprc(unsigned int *pidadr,
1726 struct dsc$descriptor_s *prcname,
1729 /* sig 0 means validate the PID */
1730 /*------------------------------*/
1732 const unsigned long int jpicode = JPI$_PID;
1735 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
1736 if ($VMS_STATUS_SUCCESS(status))
1739 case SS$_NOSUCHNODE:
1740 case SS$_UNREACHABLE:
1754 code = Perl_sig_to_vmscondition_int(sig);
1757 SETERRNO(EINVAL, SS$_BADPARAM);
1761 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
1762 * signals are to be sent to multiple processes.
1763 * pid = 0 - all processes in group except ones that the system exempts
1764 * pid = -1 - all processes except ones that the system exempts
1765 * pid = -n - all processes in group (abs(n)) except ...
1766 * For now, just report as not supported.
1770 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
1774 iss = sys$sigprc((unsigned int *)&pid,0,code);
1775 if (iss&1) return 0;
1779 set_errno(EPERM); break;
1781 case SS$_NOSUCHNODE:
1782 case SS$_UNREACHABLE:
1783 set_errno(ESRCH); break;
1785 set_errno(ENOMEM); break;
1790 set_vaxc_errno(iss);
1796 /* Routine to convert a VMS status code to a UNIX status code.
1797 ** More tricky than it appears because of conflicting conventions with
1800 ** VMS status codes are a bit mask, with the least significant bit set for
1803 ** Special UNIX status of EVMSERR indicates that no translation is currently
1804 ** available, and programs should check the VMS status code.
1806 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
1810 #ifndef C_FACILITY_NO
1811 #define C_FACILITY_NO 0x350000
1814 #define DCL_IVVERB 0x38090
1817 int Perl_vms_status_to_unix(int vms_status, int child_flag)
1825 /* Assume the best or the worst */
1826 if (vms_status & STS$M_SUCCESS)
1829 unix_status = EVMSERR;
1831 msg_status = vms_status & ~STS$M_CONTROL;
1833 facility = vms_status & STS$M_FAC_NO;
1834 fac_sp = vms_status & STS$M_FAC_SP;
1835 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
1837 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
1843 unix_status = EFAULT;
1845 case SS$_DEVOFFLINE:
1846 unix_status = EBUSY;
1849 unix_status = ENOTCONN;
1857 case SS$_INVFILFOROP:
1861 unix_status = EINVAL;
1863 case SS$_UNSUPPORTED:
1864 unix_status = ENOTSUP;
1869 unix_status = EACCES;
1871 case SS$_DEVICEFULL:
1872 unix_status = ENOSPC;
1875 unix_status = ENODEV;
1877 case SS$_NOSUCHFILE:
1878 case SS$_NOSUCHOBJECT:
1879 unix_status = ENOENT;
1881 case SS$_ABORT: /* Fatal case */
1882 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
1883 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
1884 unix_status = EINTR;
1887 unix_status = E2BIG;
1890 unix_status = ENOMEM;
1893 unix_status = EPERM;
1895 case SS$_NOSUCHNODE:
1896 case SS$_UNREACHABLE:
1897 unix_status = ESRCH;
1900 unix_status = ECHILD;
1903 if ((facility == 0) && (msg_no < 8)) {
1904 /* These are not real VMS status codes so assume that they are
1905 ** already UNIX status codes
1907 unix_status = msg_no;
1913 /* Translate a POSIX exit code to a UNIX exit code */
1914 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
1915 unix_status = (msg_no & 0x07F8) >> 3;
1919 /* Documented traditional behavior for handling VMS child exits */
1920 /*--------------------------------------------------------------*/
1921 if (child_flag != 0) {
1923 /* Success / Informational return 0 */
1924 /*----------------------------------*/
1925 if (msg_no & STS$K_SUCCESS)
1928 /* Warning returns 1 */
1929 /*-------------------*/
1930 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
1933 /* Everything else pass through the severity bits */
1934 /*------------------------------------------------*/
1935 return (msg_no & STS$M_SEVERITY);
1938 /* Normal VMS status to ERRNO mapping attempt */
1939 /*--------------------------------------------*/
1940 switch(msg_status) {
1941 /* case RMS$_EOF: */ /* End of File */
1942 case RMS$_FNF: /* File Not Found */
1943 case RMS$_DNF: /* Dir Not Found */
1944 unix_status = ENOENT;
1946 case RMS$_RNF: /* Record Not Found */
1947 unix_status = ESRCH;
1950 unix_status = ENOTDIR;
1953 unix_status = ENODEV;
1958 unix_status = EBADF;
1961 unix_status = EEXIST;
1965 case LIB$_INVSTRDES:
1967 case LIB$_NOSUCHSYM:
1968 case LIB$_INVSYMNAM:
1970 unix_status = EINVAL;
1976 unix_status = E2BIG;
1978 case RMS$_PRV: /* No privilege */
1979 case RMS$_ACC: /* ACP file access failed */
1980 case RMS$_WLK: /* Device write locked */
1981 unix_status = EACCES;
1983 /* case RMS$_NMF: */ /* No more files */
1991 /* Try to guess at what VMS error status should go with a UNIX errno
1992 * value. This is hard to do as there could be many possible VMS
1993 * error statuses that caused the errno value to be set.
1996 int Perl_unix_status_to_vms(int unix_status)
1998 int test_unix_status;
2000 /* Trivial cases first */
2001 /*---------------------*/
2002 if (unix_status == EVMSERR)
2005 /* Is vaxc$errno sane? */
2006 /*---------------------*/
2007 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2008 if (test_unix_status == unix_status)
2011 /* If way out of range, must be VMS code already */
2012 /*-----------------------------------------------*/
2013 if (unix_status > EVMSERR)
2016 /* If out of range, punt */
2017 /*-----------------------*/
2018 if (unix_status > __ERRNO_MAX)
2022 /* Ok, now we have to do it the hard way. */
2023 /*----------------------------------------*/
2024 switch(unix_status) {
2025 case 0: return SS$_NORMAL;
2026 case EPERM: return SS$_NOPRIV;
2027 case ENOENT: return SS$_NOSUCHOBJECT;
2028 case ESRCH: return SS$_UNREACHABLE;
2029 case EINTR: return SS$_ABORT;
2032 case E2BIG: return SS$_BUFFEROVF;
2034 case EBADF: return RMS$_IFI;
2035 case ECHILD: return SS$_NONEXPR;
2037 case ENOMEM: return SS$_INSFMEM;
2038 case EACCES: return SS$_FILACCERR;
2039 case EFAULT: return SS$_ACCVIO;
2041 case EBUSY: return SS$_DEVOFFLINE;
2042 case EEXIST: return RMS$_FEX;
2044 case ENODEV: return SS$_NOSUCHDEV;
2045 case ENOTDIR: return RMS$_DIR;
2047 case EINVAL: return SS$_INVARG;
2053 case ENOSPC: return SS$_DEVICEFULL;
2054 case ESPIPE: return LIB$_INVARG;
2059 case ERANGE: return LIB$_INVARG;
2060 /* case EWOULDBLOCK */
2061 /* case EINPROGRESS */
2064 /* case EDESTADDRREQ */
2066 /* case EPROTOTYPE */
2067 /* case ENOPROTOOPT */
2068 /* case EPROTONOSUPPORT */
2069 /* case ESOCKTNOSUPPORT */
2070 /* case EOPNOTSUPP */
2071 /* case EPFNOSUPPORT */
2072 /* case EAFNOSUPPORT */
2073 /* case EADDRINUSE */
2074 /* case EADDRNOTAVAIL */
2076 /* case ENETUNREACH */
2077 /* case ENETRESET */
2078 /* case ECONNABORTED */
2079 /* case ECONNRESET */
2082 case ENOTCONN: return SS$_CLEARED;
2083 /* case ESHUTDOWN */
2084 /* case ETOOMANYREFS */
2085 /* case ETIMEDOUT */
2086 /* case ECONNREFUSED */
2088 /* case ENAMETOOLONG */
2089 /* case EHOSTDOWN */
2090 /* case EHOSTUNREACH */
2091 /* case ENOTEMPTY */
2103 /* case ECANCELED */
2107 return SS$_UNSUPPORTED;
2113 /* case EABANDONED */
2115 return SS$_ABORT; /* punt */
2118 return SS$_ABORT; /* Should not get here */
2122 /* default piping mailbox size */
2123 #define PERL_BUFSIZ 512
2127 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2129 unsigned long int mbxbufsiz;
2130 static unsigned long int syssize = 0;
2131 unsigned long int dviitm = DVI$_DEVNAM;
2132 char csize[LNM$C_NAMLENGTH+1];
2136 unsigned long syiitm = SYI$_MAXBUF;
2138 * Get the SYSGEN parameter MAXBUF
2140 * If the logical 'PERL_MBX_SIZE' is defined
2141 * use the value of the logical instead of PERL_BUFSIZ, but
2142 * keep the size between 128 and MAXBUF.
2145 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2148 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2149 mbxbufsiz = atoi(csize);
2151 mbxbufsiz = PERL_BUFSIZ;
2153 if (mbxbufsiz < 128) mbxbufsiz = 128;
2154 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2156 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2158 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2159 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2161 } /* end of create_mbx() */
2164 /*{{{ my_popen and my_pclose*/
2166 typedef struct _iosb IOSB;
2167 typedef struct _iosb* pIOSB;
2168 typedef struct _pipe Pipe;
2169 typedef struct _pipe* pPipe;
2170 typedef struct pipe_details Info;
2171 typedef struct pipe_details* pInfo;
2172 typedef struct _srqp RQE;
2173 typedef struct _srqp* pRQE;
2174 typedef struct _tochildbuf CBuf;
2175 typedef struct _tochildbuf* pCBuf;
2178 unsigned short status;
2179 unsigned short count;
2180 unsigned long dvispec;
2183 #pragma member_alignment save
2184 #pragma nomember_alignment quadword
2185 struct _srqp { /* VMS self-relative queue entry */
2186 unsigned long qptr[2];
2188 #pragma member_alignment restore
2189 static RQE RQE_ZERO = {0,0};
2191 struct _tochildbuf {
2194 unsigned short size;
2202 unsigned short chan_in;
2203 unsigned short chan_out;
2205 unsigned int bufsize;
2217 #if defined(PERL_IMPLICIT_CONTEXT)
2218 void *thx; /* Either a thread or an interpreter */
2219 /* pointer, depending on how we're built */
2227 PerlIO *fp; /* file pointer to pipe mailbox */
2228 int useFILE; /* using stdio, not perlio */
2229 int pid; /* PID of subprocess */
2230 int mode; /* == 'r' if pipe open for reading */
2231 int done; /* subprocess has completed */
2232 int waiting; /* waiting for completion/closure */
2233 int closing; /* my_pclose is closing this pipe */
2234 unsigned long completion; /* termination status of subprocess */
2235 pPipe in; /* pipe in to sub */
2236 pPipe out; /* pipe out of sub */
2237 pPipe err; /* pipe of sub's sys$error */
2238 int in_done; /* true when in pipe finished */
2243 struct exit_control_block
2245 struct exit_control_block *flink;
2246 unsigned long int (*exit_routine)();
2247 unsigned long int arg_count;
2248 unsigned long int *status_address;
2249 unsigned long int exit_status;
2252 typedef struct _closed_pipes Xpipe;
2253 typedef struct _closed_pipes* pXpipe;
2255 struct _closed_pipes {
2256 int pid; /* PID of subprocess */
2257 unsigned long completion; /* termination status of subprocess */
2259 #define NKEEPCLOSED 50
2260 static Xpipe closed_list[NKEEPCLOSED];
2261 static int closed_index = 0;
2262 static int closed_num = 0;
2264 #define RETRY_DELAY "0 ::0.20"
2265 #define MAX_RETRY 50
2267 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2268 static unsigned long mypid;
2269 static unsigned long delaytime[2];
2271 static pInfo open_pipes = NULL;
2272 static $DESCRIPTOR(nl_desc, "NL:");
2274 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2278 static unsigned long int
2279 pipe_exit_routine(pTHX)
2282 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2283 int sts, did_stuff, need_eof, j;
2286 flush any pending i/o
2292 PerlIO_flush(info->fp); /* first, flush data */
2294 fflush((FILE *)info->fp);
2300 next we try sending an EOF...ignore if doesn't work, make sure we
2308 _ckvmssts_noperl(sys$setast(0));
2309 if (info->in && !info->in->shut_on_empty) {
2310 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2315 _ckvmssts_noperl(sys$setast(1));
2319 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2321 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2326 _ckvmssts_noperl(sys$setast(0));
2327 if (info->waiting && info->done)
2329 nwait += info->waiting;
2330 _ckvmssts_noperl(sys$setast(1));
2340 _ckvmssts_noperl(sys$setast(0));
2341 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2342 sts = sys$forcex(&info->pid,0,&abort);
2343 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2346 _ckvmssts_noperl(sys$setast(1));
2350 /* again, wait for effect */
2352 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2357 _ckvmssts_noperl(sys$setast(0));
2358 if (info->waiting && info->done)
2360 nwait += info->waiting;
2361 _ckvmssts_noperl(sys$setast(1));
2370 _ckvmssts_noperl(sys$setast(0));
2371 if (!info->done) { /* We tried to be nice . . . */
2372 sts = sys$delprc(&info->pid,0);
2373 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2375 _ckvmssts_noperl(sys$setast(1));
2380 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2381 else if (!(sts & 1)) retsts = sts;
2386 static struct exit_control_block pipe_exitblock =
2387 {(struct exit_control_block *) 0,
2388 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2390 static void pipe_mbxtofd_ast(pPipe p);
2391 static void pipe_tochild1_ast(pPipe p);
2392 static void pipe_tochild2_ast(pPipe p);
2395 popen_completion_ast(pInfo info)
2397 pInfo i = open_pipes;
2402 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2403 closed_list[closed_index].pid = info->pid;
2404 closed_list[closed_index].completion = info->completion;
2406 if (closed_index == NKEEPCLOSED)
2411 if (i == info) break;
2414 if (!i) return; /* unlinked, probably freed too */
2419 Writing to subprocess ...
2420 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2422 chan_out may be waiting for "done" flag, or hung waiting
2423 for i/o completion to child...cancel the i/o. This will
2424 put it into "snarf mode" (done but no EOF yet) that discards
2427 Output from subprocess (stdout, stderr) needs to be flushed and
2428 shut down. We try sending an EOF, but if the mbx is full the pipe
2429 routine should still catch the "shut_on_empty" flag, telling it to
2430 use immediate-style reads so that "mbx empty" -> EOF.
2434 if (info->in && !info->in_done) { /* only for mode=w */
2435 if (info->in->shut_on_empty && info->in->need_wake) {
2436 info->in->need_wake = FALSE;
2437 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2439 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2443 if (info->out && !info->out_done) { /* were we also piping output? */
2444 info->out->shut_on_empty = TRUE;
2445 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2446 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2447 _ckvmssts_noperl(iss);
2450 if (info->err && !info->err_done) { /* we were piping stderr */
2451 info->err->shut_on_empty = TRUE;
2452 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2453 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2454 _ckvmssts_noperl(iss);
2456 _ckvmssts_noperl(sys$setef(pipe_ef));
2460 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2461 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2464 we actually differ from vmstrnenv since we use this to
2465 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2466 are pointing to the same thing
2469 static unsigned short
2470 popen_translate(pTHX_ char *logical, char *result)
2473 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2474 $DESCRIPTOR(d_log,"");
2476 unsigned short length;
2477 unsigned short code;
2479 unsigned short *retlenaddr;
2481 unsigned short l, ifi;
2483 d_log.dsc$a_pointer = logical;
2484 d_log.dsc$w_length = strlen(logical);
2486 itmlst[0].code = LNM$_STRING;
2487 itmlst[0].length = 255;
2488 itmlst[0].buffer_addr = result;
2489 itmlst[0].retlenaddr = &l;
2492 itmlst[1].length = 0;
2493 itmlst[1].buffer_addr = 0;
2494 itmlst[1].retlenaddr = 0;
2496 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
2497 if (iss == SS$_NOLOGNAM) {
2501 if (!(iss&1)) lib$signal(iss);
2504 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
2505 strip it off and return the ifi, if any
2508 if (result[0] == 0x1b && result[1] == 0x00) {
2509 memmove(&ifi,result+2,2);
2510 strcpy(result,result+4);
2512 return ifi; /* this is the RMS internal file id */
2515 static void pipe_infromchild_ast(pPipe p);
2518 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
2519 inside an AST routine without worrying about reentrancy and which Perl
2520 memory allocator is being used.
2522 We read data and queue up the buffers, then spit them out one at a
2523 time to the output mailbox when the output mailbox is ready for one.
2526 #define INITIAL_TOCHILDQUEUE 2
2529 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
2533 char mbx1[64], mbx2[64];
2534 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2535 DSC$K_CLASS_S, mbx1},
2536 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2537 DSC$K_CLASS_S, mbx2};
2538 unsigned int dviitm = DVI$_DEVBUFSIZ;
2542 _ckvmssts(lib$get_vm(&n, &p));
2544 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2545 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2546 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2549 p->shut_on_empty = FALSE;
2550 p->need_wake = FALSE;
2553 p->iosb.status = SS$_NORMAL;
2554 p->iosb2.status = SS$_NORMAL;
2560 #ifdef PERL_IMPLICIT_CONTEXT
2564 n = sizeof(CBuf) + p->bufsize;
2566 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
2567 _ckvmssts(lib$get_vm(&n, &b));
2568 b->buf = (char *) b + sizeof(CBuf);
2569 _ckvmssts(lib$insqhi(b, &p->free));
2572 pipe_tochild2_ast(p);
2573 pipe_tochild1_ast(p);
2579 /* reads the MBX Perl is writing, and queues */
2582 pipe_tochild1_ast(pPipe p)
2585 int iss = p->iosb.status;
2586 int eof = (iss == SS$_ENDOFFILE);
2588 #ifdef PERL_IMPLICIT_CONTEXT
2594 p->shut_on_empty = TRUE;
2596 _ckvmssts(sys$dassgn(p->chan_in));
2602 b->size = p->iosb.count;
2603 _ckvmssts(sts = lib$insqhi(b, &p->wait));
2605 p->need_wake = FALSE;
2606 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
2609 p->retry = 1; /* initial call */
2612 if (eof) { /* flush the free queue, return when done */
2613 int n = sizeof(CBuf) + p->bufsize;
2615 iss = lib$remqti(&p->free, &b);
2616 if (iss == LIB$_QUEWASEMP) return;
2618 _ckvmssts(lib$free_vm(&n, &b));
2622 iss = lib$remqti(&p->free, &b);
2623 if (iss == LIB$_QUEWASEMP) {
2624 int n = sizeof(CBuf) + p->bufsize;
2625 _ckvmssts(lib$get_vm(&n, &b));
2626 b->buf = (char *) b + sizeof(CBuf);
2632 iss = sys$qio(0,p->chan_in,
2633 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2635 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2636 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2641 /* writes queued buffers to output, waits for each to complete before
2645 pipe_tochild2_ast(pPipe p)
2648 int iss = p->iosb2.status;
2649 int n = sizeof(CBuf) + p->bufsize;
2650 int done = (p->info && p->info->done) ||
2651 iss == SS$_CANCEL || iss == SS$_ABORT;
2652 #if defined(PERL_IMPLICIT_CONTEXT)
2657 if (p->type) { /* type=1 has old buffer, dispose */
2658 if (p->shut_on_empty) {
2659 _ckvmssts(lib$free_vm(&n, &b));
2661 _ckvmssts(lib$insqhi(b, &p->free));
2666 iss = lib$remqti(&p->wait, &b);
2667 if (iss == LIB$_QUEWASEMP) {
2668 if (p->shut_on_empty) {
2670 _ckvmssts(sys$dassgn(p->chan_out));
2671 *p->pipe_done = TRUE;
2672 _ckvmssts(sys$setef(pipe_ef));
2674 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2675 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2679 p->need_wake = TRUE;
2689 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2690 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2692 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2693 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2702 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2705 char mbx1[64], mbx2[64];
2706 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2707 DSC$K_CLASS_S, mbx1},
2708 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2709 DSC$K_CLASS_S, mbx2};
2710 unsigned int dviitm = DVI$_DEVBUFSIZ;
2712 int n = sizeof(Pipe);
2713 _ckvmssts(lib$get_vm(&n, &p));
2714 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2715 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2717 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2718 n = p->bufsize * sizeof(char);
2719 _ckvmssts(lib$get_vm(&n, &p->buf));
2720 p->shut_on_empty = FALSE;
2723 p->iosb.status = SS$_NORMAL;
2724 #if defined(PERL_IMPLICIT_CONTEXT)
2727 pipe_infromchild_ast(p);
2735 pipe_infromchild_ast(pPipe p)
2737 int iss = p->iosb.status;
2738 int eof = (iss == SS$_ENDOFFILE);
2739 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
2740 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
2741 #if defined(PERL_IMPLICIT_CONTEXT)
2745 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
2746 _ckvmssts(sys$dassgn(p->chan_out));
2751 input shutdown if EOF from self (done or shut_on_empty)
2752 output shutdown if closing flag set (my_pclose)
2753 send data/eof from child or eof from self
2754 otherwise, re-read (snarf of data from child)
2759 if (myeof && p->chan_in) { /* input shutdown */
2760 _ckvmssts(sys$dassgn(p->chan_in));
2765 if (myeof || kideof) { /* pass EOF to parent */
2766 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
2767 pipe_infromchild_ast, p,
2770 } else if (eof) { /* eat EOF --- fall through to read*/
2772 } else { /* transmit data */
2773 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
2774 pipe_infromchild_ast,p,
2775 p->buf, p->iosb.count, 0, 0, 0, 0));
2781 /* everything shut? flag as done */
2783 if (!p->chan_in && !p->chan_out) {
2784 *p->pipe_done = TRUE;
2785 _ckvmssts(sys$setef(pipe_ef));
2789 /* write completed (or read, if snarfing from child)
2790 if still have input active,
2791 queue read...immediate mode if shut_on_empty so we get EOF if empty
2793 check if Perl reading, generate EOFs as needed
2799 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
2800 pipe_infromchild_ast,p,
2801 p->buf, p->bufsize, 0, 0, 0, 0);
2802 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
2804 } else { /* send EOFs for extra reads */
2805 p->iosb.status = SS$_ENDOFFILE;
2806 p->iosb.dvispec = 0;
2807 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
2809 pipe_infromchild_ast, p, 0, 0, 0, 0));
2815 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
2819 unsigned long dviitm = DVI$_DEVBUFSIZ;
2821 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
2822 DSC$K_CLASS_S, mbx};
2823 int n = sizeof(Pipe);
2825 /* things like terminals and mbx's don't need this filter */
2826 if (fd && fstat(fd,&s) == 0) {
2827 unsigned long dviitm = DVI$_DEVCHAR, devchar;
2828 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
2829 DSC$K_CLASS_S, s.st_dev};
2831 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
2832 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
2833 strcpy(out, s.st_dev);
2838 _ckvmssts(lib$get_vm(&n, &p));
2839 p->fd_out = dup(fd);
2840 create_mbx(aTHX_ &p->chan_in, &d_mbx);
2841 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2842 n = (p->bufsize+1) * sizeof(char);
2843 _ckvmssts(lib$get_vm(&n, &p->buf));
2844 p->shut_on_empty = FALSE;
2849 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
2850 pipe_mbxtofd_ast, p,
2851 p->buf, p->bufsize, 0, 0, 0, 0));
2857 pipe_mbxtofd_ast(pPipe p)
2859 int iss = p->iosb.status;
2860 int done = p->info->done;
2862 int eof = (iss == SS$_ENDOFFILE);
2863 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
2864 int err = !(iss&1) && !eof;
2865 #if defined(PERL_IMPLICIT_CONTEXT)
2869 if (done && myeof) { /* end piping */
2871 sys$dassgn(p->chan_in);
2872 *p->pipe_done = TRUE;
2873 _ckvmssts(sys$setef(pipe_ef));
2877 if (!err && !eof) { /* good data to send to file */
2878 p->buf[p->iosb.count] = '\n';
2879 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
2882 if (p->retry < MAX_RETRY) {
2883 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
2893 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
2894 pipe_mbxtofd_ast, p,
2895 p->buf, p->bufsize, 0, 0, 0, 0);
2896 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
2901 typedef struct _pipeloc PLOC;
2902 typedef struct _pipeloc* pPLOC;
2906 char dir[NAM$C_MAXRSS+1];
2908 static pPLOC head_PLOC = 0;
2911 free_pipelocs(pTHX_ void *head)
2914 pPLOC *pHead = (pPLOC *)head;
2926 store_pipelocs(pTHX)
2935 char temp[NAM$C_MAXRSS+1];
2939 free_pipelocs(aTHX_ &head_PLOC);
2941 /* the . directory from @INC comes last */
2943 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2944 p->next = head_PLOC;
2946 strcpy(p->dir,"./");
2948 /* get the directory from $^X */
2950 #ifdef PERL_IMPLICIT_CONTEXT
2951 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2953 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2955 strcpy(temp, PL_origargv[0]);
2956 x = strrchr(temp,']');
2958 x = strrchr(temp,'>');
2960 /* It could be a UNIX path */
2961 x = strrchr(temp,'/');
2967 /* Got a bare name, so use default directory */
2972 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2973 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2974 p->next = head_PLOC;
2976 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2977 p->dir[NAM$C_MAXRSS] = '\0';
2981 /* reverse order of @INC entries, skip "." since entered above */
2983 #ifdef PERL_IMPLICIT_CONTEXT
2986 if (PL_incgv) av = GvAVn(PL_incgv);
2988 for (i = 0; av && i <= AvFILL(av); i++) {
2989 dirsv = *av_fetch(av,i,TRUE);
2991 if (SvROK(dirsv)) continue;
2992 dir = SvPVx(dirsv,n_a);
2993 if (strcmp(dir,".") == 0) continue;
2994 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2997 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2998 p->next = head_PLOC;
3000 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3001 p->dir[NAM$C_MAXRSS] = '\0';
3004 /* most likely spot (ARCHLIB) put first in the list */
3007 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
3008 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3009 p->next = head_PLOC;
3011 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3012 p->dir[NAM$C_MAXRSS] = '\0';
3021 static int vmspipe_file_status = 0;
3022 static char vmspipe_file[NAM$C_MAXRSS+1];
3024 /* already found? Check and use ... need read+execute permission */
3026 if (vmspipe_file_status == 1) {
3027 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3028 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3029 return vmspipe_file;
3031 vmspipe_file_status = 0;
3034 /* scan through stored @INC, $^X */
3036 if (vmspipe_file_status == 0) {
3037 char file[NAM$C_MAXRSS+1];
3038 pPLOC p = head_PLOC;
3041 strcpy(file, p->dir);
3042 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
3043 file[NAM$C_MAXRSS] = '\0';
3046 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
3048 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3049 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3050 vmspipe_file_status = 1;
3051 return vmspipe_file;
3054 vmspipe_file_status = -1; /* failed, use tempfiles */
3061 vmspipe_tempfile(pTHX)
3063 char file[NAM$C_MAXRSS+1];
3065 static int index = 0;
3069 /* create a tempfile */
3071 /* we can't go from W, shr=get to R, shr=get without
3072 an intermediate vulnerable state, so don't bother trying...
3074 and lib$spawn doesn't shr=put, so have to close the write
3076 So... match up the creation date/time and the FID to
3077 make sure we're dealing with the same file
3082 if (!decc_filename_unix_only) {
3083 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3084 fp = fopen(file,"w");
3086 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3087 fp = fopen(file,"w");
3089 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3090 fp = fopen(file,"w");
3095 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3096 fp = fopen(file,"w");
3098 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3099 fp = fopen(file,"w");
3101 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3102 fp = fopen(file,"w");
3106 if (!fp) return 0; /* we're hosed */
3108 fprintf(fp,"$! 'f$verify(0)'\n");
3109 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3110 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3111 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3112 fprintf(fp,"$ perl_on = \"set noon\"\n");
3113 fprintf(fp,"$ perl_exit = \"exit\"\n");
3114 fprintf(fp,"$ perl_del = \"delete\"\n");
3115 fprintf(fp,"$ pif = \"if\"\n");
3116 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3117 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3118 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3119 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3120 fprintf(fp,"$! --- build command line to get max possible length\n");
3121 fprintf(fp,"$c=perl_popen_cmd0\n");
3122 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3123 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3124 fprintf(fp,"$x=perl_popen_cmd3\n");
3125 fprintf(fp,"$c=c+x\n");
3126 fprintf(fp,"$ perl_on\n");
3127 fprintf(fp,"$ 'c'\n");
3128 fprintf(fp,"$ perl_status = $STATUS\n");
3129 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3130 fprintf(fp,"$ perl_exit 'perl_status'\n");
3133 fgetname(fp, file, 1);
3134 fstat(fileno(fp), (struct stat *)&s0);
3137 if (decc_filename_unix_only)
3138 do_tounixspec(file, file, 0);
3139 fp = fopen(file,"r","shr=get");
3141 fstat(fileno(fp), (struct stat *)&s1);
3143 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3144 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3155 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3157 static int handler_set_up = FALSE;
3158 unsigned long int sts, flags = CLI$M_NOWAIT;
3159 /* The use of a GLOBAL table (as was done previously) rendered
3160 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3161 * environment. Hence we've switched to LOCAL symbol table.
3163 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3165 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3166 char in[512], out[512], err[512], mbx[512];
3168 char tfilebuf[NAM$C_MAXRSS+1];
3170 char cmd_sym_name[20];
3171 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3172 DSC$K_CLASS_S, symbol};
3173 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3175 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3176 DSC$K_CLASS_S, cmd_sym_name};
3177 struct dsc$descriptor_s *vmscmd;
3178 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3179 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3180 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3182 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3184 /* once-per-program initialization...
3185 note that the SETAST calls and the dual test of pipe_ef
3186 makes sure that only the FIRST thread through here does
3187 the initialization...all other threads wait until it's
3190 Yeah, uglier than a pthread call, it's got all the stuff inline
3191 rather than in a separate routine.
3195 _ckvmssts(sys$setast(0));
3197 unsigned long int pidcode = JPI$_PID;
3198 $DESCRIPTOR(d_delay, RETRY_DELAY);
3199 _ckvmssts(lib$get_ef(&pipe_ef));
3200 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3201 _ckvmssts(sys$bintim(&d_delay, delaytime));
3203 if (!handler_set_up) {
3204 _ckvmssts(sys$dclexh(&pipe_exitblock));
3205 handler_set_up = TRUE;
3207 _ckvmssts(sys$setast(1));
3210 /* see if we can find a VMSPIPE.COM */
3213 vmspipe = find_vmspipe(aTHX);
3215 strcpy(tfilebuf+1,vmspipe);
3216 } else { /* uh, oh...we're in tempfile hell */
3217 tpipe = vmspipe_tempfile(aTHX);
3218 if (!tpipe) { /* a fish popular in Boston */
3219 if (ckWARN(WARN_PIPE)) {
3220 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3224 fgetname(tpipe,tfilebuf+1,1);
3226 vmspipedsc.dsc$a_pointer = tfilebuf;
3227 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3229 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3232 case RMS$_FNF: case RMS$_DNF:
3233 set_errno(ENOENT); break;
3235 set_errno(ENOTDIR); break;
3237 set_errno(ENODEV); break;
3239 set_errno(EACCES); break;
3241 set_errno(EINVAL); break;
3242 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3243 set_errno(E2BIG); break;
3244 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3245 _ckvmssts(sts); /* fall through */
3246 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3249 set_vaxc_errno(sts);
3250 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3251 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3257 _ckvmssts(lib$get_vm(&n, &info));
3259 strcpy(mode,in_mode);
3262 info->completion = 0;
3263 info->closing = FALSE;
3270 info->in_done = TRUE;
3271 info->out_done = TRUE;
3272 info->err_done = TRUE;
3273 in[0] = out[0] = err[0] = '\0';
3275 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3279 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3284 if (*mode == 'r') { /* piping from subroutine */
3286 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3288 info->out->pipe_done = &info->out_done;
3289 info->out_done = FALSE;
3290 info->out->info = info;
3292 if (!info->useFILE) {
3293 info->fp = PerlIO_open(mbx, mode);
3295 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3296 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3299 if (!info->fp && info->out) {
3300 sys$cancel(info->out->chan_out);
3302 while (!info->out_done) {
3304 _ckvmssts(sys$setast(0));
3305 done = info->out_done;
3306 if (!done) _ckvmssts(sys$clref(pipe_ef));
3307 _ckvmssts(sys$setast(1));
3308 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3311 if (info->out->buf) {
3312 n = info->out->bufsize * sizeof(char);
3313 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3316 _ckvmssts(lib$free_vm(&n, &info->out));
3318 _ckvmssts(lib$free_vm(&n, &info));
3323 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3325 info->err->pipe_done = &info->err_done;
3326 info->err_done = FALSE;
3327 info->err->info = info;
3330 } else if (*mode == 'w') { /* piping to subroutine */
3332 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3334 info->out->pipe_done = &info->out_done;
3335 info->out_done = FALSE;
3336 info->out->info = info;
3339 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3341 info->err->pipe_done = &info->err_done;
3342 info->err_done = FALSE;
3343 info->err->info = info;
3346 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3347 if (!info->useFILE) {
3348 info->fp = PerlIO_open(mbx, mode);
3350 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3351 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3355 info->in->pipe_done = &info->in_done;
3356 info->in_done = FALSE;
3357 info->in->info = info;
3361 if (!info->fp && info->in) {
3363 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3364 0, 0, 0, 0, 0, 0, 0, 0));
3366 while (!info->in_done) {
3368 _ckvmssts(sys$setast(0));
3369 done = info->in_done;
3370 if (!done) _ckvmssts(sys$clref(pipe_ef));
3371 _ckvmssts(sys$setast(1));
3372 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3375 if (info->in->buf) {
3376 n = info->in->bufsize * sizeof(char);
3377 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3380 _ckvmssts(lib$free_vm(&n, &info->in));
3382 _ckvmssts(lib$free_vm(&n, &info));
3388 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3389 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3391 info->out->pipe_done = &info->out_done;
3392 info->out_done = FALSE;
3393 info->out->info = info;
3396 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3398 info->err->pipe_done = &info->err_done;
3399 info->err_done = FALSE;
3400 info->err->info = info;
3404 symbol[MAX_DCL_SYMBOL] = '\0';
3406 strncpy(symbol, in, MAX_DCL_SYMBOL);
3407 d_symbol.dsc$w_length = strlen(symbol);
3408 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3410 strncpy(symbol, err, MAX_DCL_SYMBOL);
3411 d_symbol.dsc$w_length = strlen(symbol);
3412 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3414 strncpy(symbol, out, MAX_DCL_SYMBOL);
3415 d_symbol.dsc$w_length = strlen(symbol);
3416 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3418 p = vmscmd->dsc$a_pointer;
3419 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3420 if (*p == '$') p++; /* remove leading $ */
3421 while (*p == ' ' || *p == '\t') p++;
3423 for (j = 0; j < 4; j++) {
3424 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3425 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3427 strncpy(symbol, p, MAX_DCL_SYMBOL);
3428 d_symbol.dsc$w_length = strlen(symbol);
3429 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3431 if (strlen(p) > MAX_DCL_SYMBOL) {
3432 p += MAX_DCL_SYMBOL;
3437 _ckvmssts(sys$setast(0));
3438 info->next=open_pipes; /* prepend to list */
3440 _ckvmssts(sys$setast(1));
3441 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
3442 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
3443 * have SYS$COMMAND if we need it.
3445 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
3446 0, &info->pid, &info->completion,
3447 0, popen_completion_ast,info,0,0,0));
3449 /* if we were using a tempfile, close it now */
3451 if (tpipe) fclose(tpipe);
3453 /* once the subprocess is spawned, it has copied the symbols and
3454 we can get rid of ours */
3456 for (j = 0; j < 4; j++) {
3457 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3458 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3459 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
3461 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
3462 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
3463 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
3464 vms_execfree(vmscmd);
3466 #ifdef PERL_IMPLICIT_CONTEXT
3469 PL_forkprocess = info->pid;
3474 _ckvmssts(sys$setast(0));
3476 if (!done) _ckvmssts(sys$clref(pipe_ef));
3477 _ckvmssts(sys$setast(1));
3478 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3480 *psts = info->completion;
3481 /* Caller thinks it is open and tries to close it. */
3482 /* This causes some problems, as it changes the error status */
3483 /* my_pclose(info->fp); */
3488 } /* end of safe_popen */
3491 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
3493 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
3497 TAINT_PROPER("popen");
3498 PERL_FLUSHALL_FOR_CHILD;
3499 return safe_popen(aTHX_ cmd,mode,&sts);
3504 /*{{{ I32 my_pclose(PerlIO *fp)*/
3505 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
3507 pInfo info, last = NULL;
3508 unsigned long int retsts;
3511 for (info = open_pipes; info != NULL; last = info, info = info->next)
3512 if (info->fp == fp) break;
3514 if (info == NULL) { /* no such pipe open */
3515 set_errno(ECHILD); /* quoth POSIX */
3516 set_vaxc_errno(SS$_NONEXPR);
3520 /* If we were writing to a subprocess, insure that someone reading from
3521 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
3522 * produce an EOF record in the mailbox.
3524 * well, at least sometimes it *does*, so we have to watch out for
3525 * the first EOF closing the pipe (and DASSGN'ing the channel)...
3529 PerlIO_flush(info->fp); /* first, flush data */
3531 fflush((FILE *)info->fp);
3534 _ckvmssts(sys$setast(0));
3535 info->closing = TRUE;
3536 done = info->done && info->in_done && info->out_done && info->err_done;
3537 /* hanging on write to Perl's input? cancel it */
3538 if (info->mode == 'r' && info->out && !info->out_done) {
3539 if (info->out->chan_out) {
3540 _ckvmssts(sys$cancel(info->out->chan_out));
3541 if (!info->out->chan_in) { /* EOF generation, need AST */
3542 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
3546 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
3547 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3549 _ckvmssts(sys$setast(1));
3552 PerlIO_close(info->fp);
3554 fclose((FILE *)info->fp);
3557 we have to wait until subprocess completes, but ALSO wait until all
3558 the i/o completes...otherwise we'll be freeing the "info" structure
3559 that the i/o ASTs could still be using...
3563 _ckvmssts(sys$setast(0));
3564 done = info->done && info->in_done && info->out_done && info->err_done;
3565 if (!done) _ckvmssts(sys$clref(pipe_ef));
3566 _ckvmssts(sys$setast(1));
3567 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3569 retsts = info->completion;
3571 /* remove from list of open pipes */
3572 _ckvmssts(sys$setast(0));
3573 if (last) last->next = info->next;
3574 else open_pipes = info->next;
3575 _ckvmssts(sys$setast(1));
3577 /* free buffers and structures */
3580 if (info->in->buf) {
3581 n = info->in->bufsize * sizeof(char);
3582 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3585 _ckvmssts(lib$free_vm(&n, &info->in));
3588 if (info->out->buf) {
3589 n = info->out->bufsize * sizeof(char);
3590 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3593 _ckvmssts(lib$free_vm(&n, &info->out));
3596 if (info->err->buf) {
3597 n = info->err->bufsize * sizeof(char);
3598 _ckvmssts(lib$free_vm(&n, &info->err->buf));
3601 _ckvmssts(lib$free_vm(&n, &info->err));
3604 _ckvmssts(lib$free_vm(&n, &info));
3608 } /* end of my_pclose() */
3610 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3611 /* Roll our own prototype because we want this regardless of whether
3612 * _VMS_WAIT is defined.
3614 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
3616 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
3617 created with popen(); otherwise partially emulate waitpid() unless
3618 we have a suitable one from the CRTL that came with VMS 7.2 and later.
3619 Also check processes not considered by the CRTL waitpid().
3621 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
3623 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
3630 if (statusp) *statusp = 0;
3632 for (info = open_pipes; info != NULL; info = info->next)
3633 if (info->pid == pid) break;
3635 if (info != NULL) { /* we know about this child */
3636 while (!info->done) {
3637 _ckvmssts(sys$setast(0));
3639 if (!done) _ckvmssts(sys$clref(pipe_ef));
3640 _ckvmssts(sys$setast(1));
3641 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3644 if (statusp) *statusp = info->completion;
3648 /* child that already terminated? */
3650 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
3651 if (closed_list[j].pid == pid) {
3652 if (statusp) *statusp = closed_list[j].completion;
3657 /* fall through if this child is not one of our own pipe children */
3659 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3661 /* waitpid() became available in the CRTL as of VMS 7.0, but only
3662 * in 7.2 did we get a version that fills in the VMS completion
3663 * status as Perl has always tried to do.
3666 sts = __vms_waitpid( pid, statusp, flags );
3668 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
3671 /* If the real waitpid tells us the child does not exist, we
3672 * fall through here to implement waiting for a child that
3673 * was created by some means other than exec() (say, spawned
3674 * from DCL) or to wait for a process that is not a subprocess
3675 * of the current process.
3678 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3681 $DESCRIPTOR(intdsc,"0 00:00:01");
3682 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3683 unsigned long int pidcode = JPI$_PID, mypid;
3684 unsigned long int interval[2];
3685 unsigned int jpi_iosb[2];
3686 struct itmlst_3 jpilist[2] = {
3687 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3692 /* Sorry folks, we don't presently implement rooting around for
3693 the first child we can find, and we definitely don't want to
3694 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3700 /* Get the owner of the child so I can warn if it's not mine. If the
3701 * process doesn't exist or I don't have the privs to look at it,
3702 * I can go home early.
3704 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3705 if (sts & 1) sts = jpi_iosb[0];
3717 set_vaxc_errno(sts);
3721 if (ckWARN(WARN_EXEC)) {
3722 /* remind folks they are asking for non-standard waitpid behavior */
3723 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3724 if (ownerpid != mypid)
3725 Perl_warner(aTHX_ packWARN(WARN_EXEC),
3726 "waitpid: process %x is not a child of process %x",
3730 /* simply check on it once a second until it's not there anymore. */
3732 _ckvmssts(sys$bintim(&intdsc,interval));
3733 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
3734 _ckvmssts(sys$schdwk(0,0,interval,0));
3735 _ckvmssts(sys$hiber());
3737 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
3742 } /* end of waitpid() */
3747 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
3749 my_gconvert(double val, int ndig, int trail, char *buf)
3751 static char __gcvtbuf[DBL_DIG+1];
3754 loc = buf ? buf : __gcvtbuf;
3756 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
3758 sprintf(loc,"%.*g",ndig,val);
3764 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
3765 return gcvt(val,ndig,loc);
3768 loc[0] = '0'; loc[1] = '\0';
3775 #if 1 /* defined(__VAX) || !defined(NAML$C_MAXRSS) */
3776 static int rms_free_search_context(struct FAB * fab)
3780 nam = fab->fab$l_nam;
3781 nam->nam$b_nop |= NAM$M_SYNCHK;
3782 nam->nam$l_rlf = NULL;
3784 return sys$parse(fab, NULL, NULL);
3787 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
3788 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
3789 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
3790 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
3791 #define rms_nam_esll(nam) nam.nam$b_esl
3792 #define rms_nam_esl(nam) nam.nam$b_esl
3793 #define rms_nam_name(nam) nam.nam$l_name
3794 #define rms_nam_namel(nam) nam.nam$l_name
3795 #define rms_nam_type(nam) nam.nam$l_type
3796 #define rms_nam_typel(nam) nam.nam$l_type
3797 #define rms_nam_ver(nam) nam.nam$l_ver
3798 #define rms_nam_verl(nam) nam.nam$l_ver
3799 #define rms_nam_rsll(nam) nam.nam$b_rsl
3800 #define rms_nam_rsl(nam) nam.nam$b_rsl
3801 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
3802 #define rms_set_fna(fab, nam, name, size) \
3803 fab.fab$b_fns = size; fab.fab$l_fna = name;
3804 #define rms_get_fna(fab, nam) fab.fab$l_fna
3805 #define rms_set_dna(fab, nam, name, size) \
3806 fab.fab$b_dns = size; fab.fab$l_dna = name;
3807 #define rms_nam_dns(fab, nam) fab.fab$b_dns;
3808 #define rms_set_esa(fab, nam, name, size) \
3809 nam.nam$b_ess = size; nam.nam$l_esa = name;
3810 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
3811 nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;
3812 #define rms_set_rsa(nam, name, size) \
3813 nam.nam$l_rsa = name; nam.nam$b_rss = size;
3814 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
3815 nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size;
3818 static int rms_free_search_context(struct FAB * fab)
3822 nam = fab->fab$l_naml;
3823 nam->naml$b_nop |= NAM$M_SYNCHK;
3824 nam->naml$l_rlf = NULL;
3825 nam->naml$l_long_defname_size = 0;
3827 return sys$parse(fab, NULL, NULL);
3830 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
3831 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
3832 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
3833 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
3834 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
3835 #define rms_nam_esl(nam) nam.naml$b_esl
3836 #define rms_nam_name(nam) nam.naml$l_name
3837 #define rms_nam_namel(nam) nam.naml$l_long_name
3838 #define rms_nam_type(nam) nam.naml$l_type
3839 #define rms_nam_typel(nam) nam.naml$l_long_type
3840 #define rms_nam_ver(nam) nam.naml$l_ver
3841 #define rms_nam_verl(nam) nam.naml$l_long_ver
3842 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
3843 #define rms_nam_rsl(nam) nam.naml$b_rsl
3844 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
3845 #define rms_set_fna(fab, nam, name, size) \
3846 fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
3847 nam.naml$l_long_filename_size = size; \
3848 nam.naml$l_long_filename = name
3849 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
3850 #define rms_set_dna(fab, nam, name, size) \
3851 fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
3852 nam.naml$l_long_defname_size = size; \
3853 nam.naml$l_long_defname = name
3854 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
3855 #define rms_set_esa(fab, nam, name, size) \
3856 nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
3857 nam.naml$l_long_expand_alloc = size; \
3858 nam.naml$l_long_expand = name
3859 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
3860 nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
3861 nam.naml$l_long_expand = l_name; \
3862 nam.naml$l_long_expand_alloc = l_size;
3863 #define rms_set_rsa(nam, name, size) \
3864 nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
3865 nam.naml$l_long_result = name; \
3866 nam.naml$l_long_result_alloc = size;
3867 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
3868 nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
3869 nam.naml$l_long_result = l_name; \
3870 nam.naml$l_long_result_alloc = l_size;
3875 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
3876 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
3877 * to expand file specification. Allows for a single default file
3878 * specification and a simple mask of options. If outbuf is non-NULL,
3879 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
3880 * the resultant file specification is placed. If outbuf is NULL, the
3881 * resultant file specification is placed into a static buffer.
3882 * The third argument, if non-NULL, is taken to be a default file
3883 * specification string. The fourth argument is unused at present.
3884 * rmesexpand() returns the address of the resultant string if
3885 * successful, and NULL on error.
3887 * New functionality for previously unused opts value:
3888 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
3890 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
3892 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
3893 /* ODS-2 only version */
3895 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3897 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
3898 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
3899 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
3900 struct FAB myfab = cc$rms_fab;
3901 struct NAM mynam = cc$rms_nam;
3903 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3906 if (!filespec || !*filespec) {
3907 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3911 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
3912 else outbuf = __rmsexpand_retbuf;
3914 isunix = is_unix_filespec(filespec);
3916 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3921 filespec = vmsfspec;
3924 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
3925 myfab.fab$b_fns = strlen(filespec);
3926 myfab.fab$l_nam = &mynam;
3928 if (defspec && *defspec) {
3929 if (strchr(defspec,'/') != NULL) {
3930 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
3937 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
3938 myfab.fab$b_dns = strlen(defspec);
3941 mynam.nam$l_esa = esa;
3942 mynam.nam$b_ess = sizeof esa;
3943 mynam.nam$l_rsa = outbuf;
3944 mynam.nam$b_rss = NAM$C_MAXRSS;
3946 #ifdef NAM$M_NO_SHORT_UPCASE
3947 if (decc_efs_case_preserve)
3948 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3951 retsts = sys$parse(&myfab,0,0);
3952 if (!(retsts & 1)) {
3953 mynam.nam$b_nop |= NAM$M_SYNCHK;
3954 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
3955 retsts = sys$parse(&myfab,0,0);
3956 if (retsts & 1) goto expanded;
3958 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
3959 sts = sys$parse(&myfab,0,0); /* Free search context */
3960 if (out) Safefree(out);
3961 set_vaxc_errno(retsts);
3962 if (retsts == RMS$_PRV) set_errno(EACCES);
3963 else if (retsts == RMS$_DEV) set_errno(ENODEV);
3964 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3965 else set_errno(EVMSERR);
3968 retsts = sys$search(&myfab,0,0);
3969 if (!(retsts & 1) && retsts != RMS$_FNF) {
3970 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3971 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3972 if (out) Safefree(out);
3973 set_vaxc_errno(retsts);
3974 if (retsts == RMS$_PRV) set_errno(EACCES);
3975 else set_errno(EVMSERR);
3979 /* If the input filespec contained any lowercase characters,
3980 * downcase the result for compatibility with Unix-minded code. */
3982 if (!decc_efs_case_preserve) {
3983 for (out = myfab.fab$l_fna; *out; out++)
3984 if (islower(*out)) { haslower = 1; break; }
3986 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
3987 else { out = esa; speclen = mynam.nam$b_esl; }
3988 /* Trim off null fields added by $PARSE
3989 * If type > 1 char, must have been specified in original or default spec
3990 * (not true for version; $SEARCH may have added version of existing file).
3992 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
3993 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
3994 (mynam.nam$l_ver - mynam.nam$l_type == 1);
3995 if (trimver || trimtype) {
3996 if (defspec && *defspec) {
3997 char defesa[NAM$C_MAXRSS];
3998 struct FAB deffab = cc$rms_fab;
3999 struct NAM defnam = cc$rms_nam;
4001 deffab.fab$l_nam = &defnam;
4002 /* cast below ok for read only pointer */
4003 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
4004 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
4005 defnam.nam$b_nop = NAM$M_SYNCHK;
4006 #ifdef NAM$M_NO_SHORT_UPCASE
4007 if (decc_efs_case_preserve)
4008 defnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4010 if (sys$parse(&deffab,0,0) & 1) {
4011 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
4012 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
4016 if (*mynam.nam$l_ver != '\"')
4017 speclen = mynam.nam$l_ver - out;
4020 /* If we didn't already trim version, copy down */
4021 if (speclen > mynam.nam$l_ver - out)
4022 memmove(mynam.nam$l_type, mynam.nam$l_ver,
4023 speclen - (mynam.nam$l_ver - out));
4024 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
4027 /* If we just had a directory spec on input, $PARSE "helpfully"
4028 * adds an empty name and type for us */
4029 if (mynam.nam$l_name == mynam.nam$l_type &&
4030 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
4031 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
4032 speclen = mynam.nam$l_name - out;
4034 /* Posix format specifications must have matching quotes */
4035 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4036 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4037 out[speclen] = '\"';
4042 out[speclen] = '\0';
4043 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4045 /* Have we been working with an expanded, but not resultant, spec? */
4046 /* Also, convert back to Unix syntax if necessary. */
4047 if ((opts & PERL_RMSEXPAND_M_VMS) != 0)
4050 if (!mynam.nam$b_rsl) {
4052 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
4054 else strcpy(outbuf,esa);
4057 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
4058 strcpy(outbuf,tmpfspec);
4060 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4061 mynam.nam$l_rsa = NULL;
4062 mynam.nam$b_rss = 0;
4063 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
4067 /* ODS-5 supporting routine */
4069 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
4071 static char __rmsexpand_retbuf[NAML$C_MAXRSS+1];
4072 char * vmsfspec, *tmpfspec;
4073 char * esa, *cp, *out = NULL;
4076 struct FAB myfab = cc$rms_fab;
4077 rms_setup_nam(mynam);
4079 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4082 if (!filespec || !*filespec) {
4083 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4087 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
4088 else outbuf = __rmsexpand_retbuf;
4094 isunix = is_unix_filespec(filespec);
4096 Newx(vmsfspec, VMS_MAXRSS, char);
4097 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
4103 filespec = vmsfspec;
4105 /* Unless we are forcing to VMS format, a UNIX input means
4106 * UNIX output, and that requires long names to be used
4108 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4109 opts |= PERL_RMSEXPAND_M_LONG;
4115 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
4116 rms_bind_fab_nam(myfab, mynam);
4118 if (defspec && *defspec) {
4120 t_isunix = is_unix_filespec(defspec);
4122 Newx(tmpfspec, VMS_MAXRSS, char);
4123 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
4125 if (vmsfspec != NULL)
4133 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4136 Newx(esa, NAM$C_MAXRSS + 1, char);
4137 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4138 Newx(esal, NAML$C_MAXRSS + 1, char);
4140 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, NAML$C_MAXRSS);
4142 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4143 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
4146 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4147 Newx(outbufl, VMS_MAXRSS, char);
4148 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
4150 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
4154 #ifdef NAM$M_NO_SHORT_UPCASE
4155 if (decc_efs_case_preserve)
4156 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
4159 /* First attempt to parse as an existing file */
4160 retsts = sys$parse(&myfab,0,0);
4161 if (!(retsts & STS$K_SUCCESS)) {
4163 /* Could not find the file, try as syntax only if error is not fatal */
4164 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
4165 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4166 retsts = sys$parse(&myfab,0,0);
4167 if (retsts & STS$K_SUCCESS) goto expanded;
4170 /* Still could not parse the file specification */
4171 /*----------------------------------------------*/
4172 sts = rms_free_search_context(&myfab); /* Free search context */
4173 if (out) Safefree(out);
4174 if (tmpfspec != NULL)
4176 if (vmsfspec != NULL)
4180 set_vaxc_errno(retsts);
4181 if (retsts == RMS$_PRV) set_errno(EACCES);
4182 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4183 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4184 else set_errno(EVMSERR);
4187 retsts = sys$search(&myfab,0,0);
4188 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4189 sts = rms_free_search_context(&myfab); /* Free search context */
4190 if (out) Safefree(out);
4191 if (tmpfspec != NULL)
4193 if (vmsfspec != NULL)
4197 set_vaxc_errno(retsts);
4198 if (retsts == RMS$_PRV) set_errno(EACCES);
4199 else set_errno(EVMSERR);
4203 /* If the input filespec contained any lowercase characters,
4204 * downcase the result for compatibility with Unix-minded code. */
4206 if (!decc_efs_case_preserve) {
4207 for (out = rms_get_fna(myfab, mynam); *out; out++)
4208 if (islower(*out)) { haslower = 1; break; }
4211 /* Is a long or a short name expected */
4212 /*------------------------------------*/
4213 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4214 if (rms_nam_rsll(mynam)) {
4216 speclen = rms_nam_rsll(mynam);
4219 out = esal; /* Not esa */
4220 speclen = rms_nam_esll(mynam);
4224 if (rms_nam_rsl(mynam)) {
4226 speclen = rms_nam_rsl(mynam);
4229 out = esa; /* Not esal */
4230 speclen = rms_nam_esl(mynam);
4233 /* Trim off null fields added by $PARSE
4234 * If type > 1 char, must have been specified in original or default spec
4235 * (not true for version; $SEARCH may have added version of existing file).
4237 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
4238 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4239 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4240 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
4243 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4244 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
4246 if (trimver || trimtype) {
4247 if (defspec && *defspec) {
4248 char *defesal = NULL;
4249 Newx(defesal, NAML$C_MAXRSS + 1, char);
4250 if (defesal != NULL) {
4251 struct FAB deffab = cc$rms_fab;
4252 rms_setup_nam(defnam);
4254 rms_bind_fab_nam(deffab, defnam);
4258 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
4260 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
4262 rms_set_nam_nop(defnam, 0);
4263 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
4264 #ifdef NAM$M_NO_SHORT_UPCASE
4265 if (decc_efs_case_preserve)
4266 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
4268 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4270 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
4273 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
4280 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4281 if (*(rms_nam_verl(mynam)) != '\"')
4282 speclen = rms_nam_verl(mynam) - out;
4285 if (*(rms_nam_ver(mynam)) != '\"')
4286 speclen = rms_nam_ver(mynam) - out;
4290 /* If we didn't already trim version, copy down */
4291 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4292 if (speclen > rms_nam_verl(mynam) - out)
4294 (rms_nam_typel(mynam),
4295 rms_nam_verl(mynam),
4296 speclen - (rms_nam_verl(mynam) - out));
4297 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
4300 if (speclen > rms_nam_ver(mynam) - out)
4302 (rms_nam_type(mynam),
4304 speclen - (rms_nam_ver(mynam) - out));
4305 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
4310 /* Done with these copies of the input files */
4311 /*-------------------------------------------*/
4312 if (vmsfspec != NULL)
4314 if (tmpfspec != NULL)
4317 /* If we just had a directory spec on input, $PARSE "helpfully"
4318 * adds an empty name and type for us */
4319 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4320 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
4321 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
4322 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4323 speclen = rms_nam_namel(mynam) - out;
4326 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
4327 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
4328 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4329 speclen = rms_nam_name(mynam) - out;
4332 /* Posix format specifications must have matching quotes */
4333 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4334 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4335 out[speclen] = '\"';
4339 out[speclen] = '\0';
4340 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4342 /* Have we been working with an expanded, but not resultant, spec? */
4343 /* Also, convert back to Unix syntax if necessary. */
4345 if (!rms_nam_rsll(mynam)) {
4347 if (do_tounixspec(esa,outbuf,0) == NULL) {
4353 else strcpy(outbuf,esa);
4356 Newx(tmpfspec, VMS_MAXRSS, char);
4357 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) {
4363 strcpy(outbuf,tmpfspec);
4367 rms_set_rsal(mynam, NULL, 0, NULL, 0);
4368 sts = rms_free_search_context(&myfab); /* Free search context */
4375 /* External entry points */
4376 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4377 { return do_rmsexpand(spec,buf,0,def,opt); }
4378 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4379 { return do_rmsexpand(spec,buf,1,def,opt); }
4383 ** The following routines are provided to make life easier when
4384 ** converting among VMS-style and Unix-style directory specifications.
4385 ** All will take input specifications in either VMS or Unix syntax. On
4386 ** failure, all return NULL. If successful, the routines listed below
4387 ** return a pointer to a buffer containing the appropriately
4388 ** reformatted spec (and, therefore, subsequent calls to that routine
4389 ** will clobber the result), while the routines of the same names with
4390 ** a _ts suffix appended will return a pointer to a mallocd string
4391 ** containing the appropriately reformatted spec.
4392 ** In all cases, only explicit syntax is altered; no check is made that
4393 ** the resulting string is valid or that the directory in question
4396 ** fileify_dirspec() - convert a directory spec into the name of the
4397 ** directory file (i.e. what you can stat() to see if it's a dir).
4398 ** The style (VMS or Unix) of the result is the same as the style
4399 ** of the parameter passed in.
4400 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4401 ** what you prepend to a filename to indicate what directory it's in).
4402 ** The style (VMS or Unix) of the result is the same as the style
4403 ** of the parameter passed in.
4404 ** tounixpath() - convert a directory spec into a Unix-style path.
4405 ** tovmspath() - convert a directory spec into a VMS-style path.
4406 ** tounixspec() - convert any file spec into a Unix-style file spec.
4407 ** tovmsspec() - convert any file spec into a VMS-style spec.
4409 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4410 ** Permission is given to distribute this code as part of the Perl
4411 ** standard distribution under the terms of the GNU General Public
4412 ** License or the Perl Artistic License. Copies of each may be
4413 ** found in the Perl standard distribution.
4416 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf)*/
4417 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
4419 static char __fileify_retbuf[VMS_MAXRSS];
4420 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4421 char *retspec, *cp1, *cp2, *lastdir;
4422 char *trndir, *vmsdir;
4423 unsigned short int trnlnm_iter_count;
4426 if (!dir || !*dir) {
4427 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4429 dirlen = strlen(dir);
4430 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4431 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4432 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4439 if (dirlen > (VMS_MAXRSS - 1)) {
4440 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
4443 Newx(trndir, VMS_MAXRSS + 1, char);
4444 if (!strpbrk(dir+1,"/]>:") &&
4445 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4446 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4447 trnlnm_iter_count = 0;
4448 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4449 trnlnm_iter_count++;
4450 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4452 dirlen = strlen(trndir);
4455 strncpy(trndir,dir,dirlen);
4456 trndir[dirlen] = '\0';
4459 /* At this point we are done with *dir and use *trndir which is a
4460 * copy that can be modified. *dir must not be modified.
4463 /* If we were handed a rooted logical name or spec, treat it like a
4464 * simple directory, so that
4465 * $ Define myroot dev:[dir.]
4466 * ... do_fileify_dirspec("myroot",buf,1) ...
4467 * does something useful.
4469 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4470 trndir[--dirlen] = '\0';
4471 trndir[dirlen-1] = ']';
4473 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4474 trndir[--dirlen] = '\0';
4475 trndir[dirlen-1] = '>';
4478 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4479 /* If we've got an explicit filename, we can just shuffle the string. */
4480 if (*(cp1+1)) hasfilename = 1;
4481 /* Similarly, we can just back up a level if we've got multiple levels
4482 of explicit directories in a VMS spec which ends with directories. */
4484 for (cp2 = cp1; cp2 > trndir; cp2--) {
4486 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4487 *cp2 = *cp1; *cp1 = '\0';
4492 if (*cp2 == '[' || *cp2 == '<') break;
4497 Newx(vmsdir, VMS_MAXRSS + 1, char);
4498 cp1 = strpbrk(trndir,"]:>");
4499 if (hasfilename || !cp1) { /* Unix-style path or filename */
4500 if (trndir[0] == '.') {
4501 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
4504 return do_fileify_dirspec("[]",buf,ts);
4506 else if (trndir[1] == '.' &&
4507 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
4510 return do_fileify_dirspec("[-]",buf,ts);
4513 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4514 dirlen -= 1; /* to last element */
4515 lastdir = strrchr(trndir,'/');
4517 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4518 /* If we have "/." or "/..", VMSify it and let the VMS code
4519 * below expand it, rather than repeating the code to handle
4520 * relative components of a filespec here */
4522 if (*(cp1+2) == '.') cp1++;
4523 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4525 if (do_tovmsspec(trndir,vmsdir,0) == NULL) {
4530 if (strchr(vmsdir,'/') != NULL) {
4531 /* If do_tovmsspec() returned it, it must have VMS syntax
4532 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4533 * the time to check this here only so we avoid a recursion
4534 * loop; otherwise, gigo.
4538 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
4541 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) {
4546 ret_chr = do_tounixspec(trndir,buf,ts);
4552 } while ((cp1 = strstr(cp1,"/.")) != NULL);
4553 lastdir = strrchr(trndir,'/');
4555 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
4557 /* Ditto for specs that end in an MFD -- let the VMS code
4558 * figure out whether it's a real device or a rooted logical. */
4560 /* This should not happen any more. Allowing the fake /000000
4561 * in a UNIX pathname causes all sorts of problems when trying
4562 * to run in UNIX emulation. So the VMS to UNIX conversions
4563 * now remove the fake /000000 directories.
4566 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
4567 if (do_tovmsspec(trndir,vmsdir,0) == NULL) {
4572 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) {
4577 ret_chr = do_tounixspec(trndir,buf,ts);
4584 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
4585 !(lastdir = cp1 = strrchr(trndir,']')) &&
4586 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
4587 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
4590 /* For EFS or ODS-5 look for the last dot */
4591 if (decc_efs_charset) {
4592 cp2 = strrchr(cp1,'.');
4594 if (vms_process_case_tolerant) {
4595 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4596 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4597 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4598 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4599 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4600 (ver || *cp3)))))) {
4604 set_vaxc_errno(RMS$_DIR);
4609 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4610 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4611 !*(cp2+3) || *(cp2+3) != 'R' ||
4612 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4613 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4614 (ver || *cp3)))))) {
4618 set_vaxc_errno(RMS$_DIR);
4622 dirlen = cp2 - trndir;
4626 retlen = dirlen + 6;
4627 if (buf) retspec = buf;
4628 else if (ts) Newx(retspec,retlen+1,char);
4629 else retspec = __fileify_retbuf;
4630 memcpy(retspec,trndir,dirlen);
4631 retspec[dirlen] = '\0';
4633 /* We've picked up everything up to the directory file name.
4634 Now just add the type and version, and we're set. */
4635 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
4636 strcat(retspec,".dir;1");
4638 strcat(retspec,".DIR;1");
4643 else { /* VMS-style directory spec */
4645 char *esa, term, *cp;
4646 unsigned long int sts, cmplen, haslower = 0;
4647 unsigned int nam_fnb;
4649 struct FAB dirfab = cc$rms_fab;
4650 rms_setup_nam(savnam);
4651 rms_setup_nam(dirnam);
4653 Newx(esa, VMS_MAXRSS + 1, char);
4654 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
4655 rms_bind_fab_nam(dirfab, dirnam);
4656 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
4657 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
4658 #ifdef NAM$M_NO_SHORT_UPCASE
4659 if (decc_efs_case_preserve)
4660 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
4663 for (cp = trndir; *cp; cp++)
4664 if (islower(*cp)) { haslower = 1; break; }
4665 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
4666 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4667 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
4668 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
4675 set_vaxc_errno(dirfab.fab$l_sts);
4681 /* Does the file really exist? */
4682 if (sys$search(&dirfab)& STS$K_SUCCESS) {
4683 /* Yes; fake the fnb bits so we'll check type below */
4684 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
4686 else { /* No; just work with potential name */
4687 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
4692 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
4693 sts = rms_free_search_context(&dirfab);
4698 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
4699 cp1 = strchr(esa,']');
4700 if (!cp1) cp1 = strchr(esa,'>');
4701 if (cp1) { /* Should always be true */
4702 rms_nam_esll(dirnam) -= cp1 - esa - 1;
4703 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
4706 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
4707 /* Yep; check version while we're at it, if it's there. */
4708 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
4709 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
4710 /* Something other than .DIR[;1]. Bzzt. */
4711 sts = rms_free_search_context(&dirfab);
4716 set_vaxc_errno(RMS$_DIR);
4720 esa[rms_nam_esll(dirnam)] = '\0';
4721 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
4722 /* They provided at least the name; we added the type, if necessary, */
4723 if (buf) retspec = buf; /* in sys$parse() */
4724 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
4725 else retspec = __fileify_retbuf;
4726 strcpy(retspec,esa);
4727 sts = rms_free_search_context(&dirfab);
4733 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
4734 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
4736 rms_nam_esll(dirnam) -= 9;
4738 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
4739 if (cp1 == NULL) { /* should never happen */
4740 sts = rms_free_search_context(&dirfab);
4748 retlen = strlen(esa);
4749 cp1 = strrchr(esa,'.');
4750 /* ODS-5 directory specifications can have extra "." in them. */
4751 while (cp1 != NULL) {
4752 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
4756 while ((cp1 > esa) && (*cp1 != '.'))
4763 if ((cp1) != NULL) {
4764 /* There's more than one directory in the path. Just roll back. */
4766 if (buf) retspec = buf;
4767 else if (ts) Newx(retspec,retlen+7,char);
4768 else retspec = __fileify_retbuf;
4769 strcpy(retspec,esa);
4772 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
4773 /* Go back and expand rooted logical name */
4774 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
4775 #ifdef NAM$M_NO_SHORT_UPCASE
4776 if (decc_efs_case_preserve)
4777 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
4779 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
4780 sts = rms_free_search_context(&dirfab);
4785 set_vaxc_errno(dirfab.fab$l_sts);
4788 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
4789 if (buf) retspec = buf;
4790 else if (ts) Newx(retspec,retlen+16,char);
4791 else retspec = __fileify_retbuf;
4792 cp1 = strstr(esa,"][");
4793 if (!cp1) cp1 = strstr(esa,"]<");
4795 memcpy(retspec,esa,dirlen);
4796 if (!strncmp(cp1+2,"000000]",7)) {
4797 retspec[dirlen-1] = '\0';
4798 /* Not full ODS-5, just extra dots in directories for now */
4799 cp1 = retspec + dirlen - 1;
4800 while (cp1 > retspec)
4805 if (*(cp1-1) != '^')
4810 if (*cp1 == '.') *cp1 = ']';
4812 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4813 memmove(cp1+1,"000000]",7);
4817 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
4818 retspec[retlen] = '\0';
4819 /* Convert last '.' to ']' */
4820 cp1 = retspec+retlen-1;
4821 while (*cp != '[') {
4824 /* Do not trip on extra dots in ODS-5 directories */
4825 if ((cp1 == retspec) || (*(cp1-1) != '^'))
4829 if (*cp1 == '.') *cp1 = ']';
4831 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4832 memmove(cp1+1,"000000]",7);
4836 else { /* This is a top-level dir. Add the MFD to the path. */
4837 if (buf) retspec = buf;
4838 else if (ts) Newx(retspec,retlen+16,char);
4839 else retspec = __fileify_retbuf;
4842 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
4843 strcpy(cp2,":[000000]");
4848 sts = rms_free_search_context(&dirfab);
4849 /* We've set up the string up through the filename. Add the
4850 type and version, and we're done. */
4851 strcat(retspec,".DIR;1");
4853 /* $PARSE may have upcased filespec, so convert output to lower
4854 * case if input contained any lowercase characters. */
4855 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
4861 } /* end of do_fileify_dirspec() */
4863 /* External entry points */
4864 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
4865 { return do_fileify_dirspec(dir,buf,0); }
4866 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
4867 { return do_fileify_dirspec(dir,buf,1); }
4869 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
4870 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
4872 static char __pathify_retbuf[VMS_MAXRSS];
4873 unsigned long int retlen;
4874 char *retpath, *cp1, *cp2, *trndir;
4875 unsigned short int trnlnm_iter_count;
4879 if (!dir || !*dir) {
4880 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4883 Newx(trndir, VMS_MAXRSS, char);
4884 if (*dir) strcpy(trndir,dir);
4885 else getcwd(trndir,VMS_MAXRSS - 1);
4887 trnlnm_iter_count = 0;
4888 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
4889 && my_trnlnm(trndir,trndir,0)) {
4890 trnlnm_iter_count++;
4891 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4892 trnlen = strlen(trndir);
4894 /* Trap simple rooted lnms, and return lnm:[000000] */
4895 if (!strcmp(trndir+trnlen-2,".]")) {
4896 if (buf) retpath = buf;
4897 else if (ts) Newx(retpath,strlen(dir)+10,char);
4898 else retpath = __pathify_retbuf;
4899 strcpy(retpath,dir);
4900 strcat(retpath,":[000000]");
4906 /* At this point we do not work with *dir, but the copy in
4907 * *trndir that is modifiable.
4910 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
4911 if (*trndir == '.' && (*(trndir+1) == '\0' ||
4912 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
4913 retlen = 2 + (*(trndir+1) != '\0');
4915 if ( !(cp1 = strrchr(trndir,'/')) &&
4916 !(cp1 = strrchr(trndir,']')) &&
4917 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
4918 if ((cp2 = strchr(cp1,'.')) != NULL &&
4919 (*(cp2-1) != '/' || /* Trailing '.', '..', */
4920 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
4921 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
4922 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
4925 /* For EFS or ODS-5 look for the last dot */
4926 if (decc_efs_charset) {
4927 cp2 = strrchr(cp1,'.');
4929 if (vms_process_case_tolerant) {
4930 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4931 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4932 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4933 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4934 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4935 (ver || *cp3)))))) {
4938 set_vaxc_errno(RMS$_DIR);
4943 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4944 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4945 !*(cp2+3) || *(cp2+3) != 'R' ||
4946 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4947 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4948 (ver || *cp3)))))) {
4951 set_vaxc_errno(RMS$_DIR);
4955 retlen = cp2 - trndir + 1;
4957 else { /* No file type present. Treat the filename as a directory. */
4958 retlen = strlen(trndir) + 1;
4961 if (buf) retpath = buf;
4962 else if (ts) Newx(retpath,retlen+1,char);
4963 else retpath = __pathify_retbuf;
4964 strncpy(retpath, trndir, retlen-1);
4965 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
4966 retpath[retlen-1] = '/'; /* with '/', add it. */
4967 retpath[retlen] = '\0';
4969 else retpath[retlen-1] = '\0';
4971 else { /* VMS-style directory spec */
4973 unsigned long int sts, cmplen, haslower;
4974 struct FAB dirfab = cc$rms_fab;
4976 rms_setup_nam(savnam);
4977 rms_setup_nam(dirnam);
4979 /* If we've got an explicit filename, we can just shuffle the string. */
4980 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
4981 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
4982 if ((cp2 = strchr(cp1,'.')) != NULL) {
4984 if (vms_process_case_tolerant) {
4985 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4986 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4987 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4988 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4989 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4990 (ver || *cp3)))))) {
4993 set_vaxc_errno(RMS$_DIR);
4998 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4999 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5000 !*(cp2+3) || *(cp2+3) != 'R' ||
5001 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5002 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5003 (ver || *cp3)))))) {
5006 set_vaxc_errno(RMS$_DIR);
5011 else { /* No file type, so just draw name into directory part */
5012 for (cp2 = cp1; *cp2; cp2++) ;
5015 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5017 /* We've now got a VMS 'path'; fall through */
5020 dirlen = strlen(trndir);
5021 if (trndir[dirlen-1] == ']' ||
5022 trndir[dirlen-1] == '>' ||
5023 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5024 if (buf) retpath = buf;
5025 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5026 else retpath = __pathify_retbuf;
5027 strcpy(retpath,trndir);
5031 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5032 Newx(esa, VMS_MAXRSS, char);
5033 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5034 rms_bind_fab_nam(dirfab, dirnam);
5035 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5036 #ifdef NAM$M_NO_SHORT_UPCASE
5037 if (decc_efs_case_preserve)
5038 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5041 for (cp = trndir; *cp; cp++)
5042 if (islower(*cp)) { haslower = 1; break; }
5044 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5045 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5046 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5047 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5053 set_vaxc_errno(dirfab.fab$l_sts);
5059 /* Does the file really exist? */
5060 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5061 if (dirfab.fab$l_sts != RMS$_FNF) {
5063 sts1 = rms_free_search_context(&dirfab);
5067 set_vaxc_errno(dirfab.fab$l_sts);
5070 dirnam = savnam; /* No; just work with potential name */
5073 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5074 /* Yep; check version while we're at it, if it's there. */
5075 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5076 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5078 /* Something other than .DIR[;1]. Bzzt. */
5079 sts2 = rms_free_search_context(&dirfab);
5083 set_vaxc_errno(RMS$_DIR);
5087 /* OK, the type was fine. Now pull any file name into the
5089 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5091 cp1 = strrchr(esa,'>');
5092 *(rms_nam_typel(dirnam)) = '>';
5095 *(rms_nam_typel(dirnam) + 1) = '\0';
5096 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5097 if (buf) retpath = buf;
5098 else if (ts) Newx(retpath,retlen,char);
5099 else retpath = __pathify_retbuf;
5100 strcpy(retpath,esa);
5102 sts = rms_free_search_context(&dirfab);
5103 /* $PARSE may have upcased filespec, so convert output to lower
5104 * case if input contained any lowercase characters. */
5105 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5110 } /* end of do_pathify_dirspec() */
5112 /* External entry points */
5113 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5114 { return do_pathify_dirspec(dir,buf,0); }
5115 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5116 { return do_pathify_dirspec(dir,buf,1); }
5118 /*{{{ char *tounixspec[_ts](char *spec, char *buf)*/
5119 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
5121 static char __tounixspec_retbuf[VMS_MAXRSS];
5122 char *dirend, *rslt, *cp1, *cp3, tmp[VMS_MAXRSS];
5124 int devlen, dirlen, retlen = VMS_MAXRSS;
5125 int expand = 1; /* guarantee room for leading and trailing slashes */
5126 unsigned short int trnlnm_iter_count;
5129 if (spec == NULL) return NULL;
5130 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
5131 if (buf) rslt = buf;
5133 retlen = strlen(spec);
5134 cp1 = strchr(spec,'[');
5135 if (!cp1) cp1 = strchr(spec,'<');
5137 for (cp1++; *cp1; cp1++) {
5138 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
5139 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
5140 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
5143 Newx(rslt,retlen+2+2*expand,char);
5145 else rslt = __tounixspec_retbuf;
5147 /* New VMS specific format needs translation
5148 * glob passes filenames with trailing '\n' and expects this preserved.
5150 if (decc_posix_compliant_pathnames) {
5151 if (strncmp(spec, "\"^UP^", 5) == 0) {
5157 Newx(tunix, VMS_MAXRSS + 1,char);
5158 strcpy(tunix, spec);
5159 tunix_len = strlen(tunix);
5161 if (tunix[tunix_len - 1] == '\n') {
5162 tunix[tunix_len - 1] = '\"';
5163 tunix[tunix_len] = '\0';
5167 uspec = decc$translate_vms(tunix);
5169 if ((int)uspec > 0) {
5175 /* If we can not translate it, makemaker wants as-is */
5183 cmp_rslt = 0; /* Presume VMS */
5184 cp1 = strchr(spec, '/');
5188 /* Look for EFS ^/ */
5189 if (decc_efs_charset) {
5190 while (cp1 != NULL) {
5193 /* Found illegal VMS, assume UNIX */
5198 cp1 = strchr(cp1, '/');
5202 /* Look for "." and ".." */
5203 if (decc_filename_unix_report) {
5204 if (spec[0] == '.') {
5205 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5209 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5215 /* This is already UNIX or at least nothing VMS understands */
5223 dirend = strrchr(spec,']');
5224 if (dirend == NULL) dirend = strrchr(spec,'>');
5225 if (dirend == NULL) dirend = strchr(spec,':');
5226 if (dirend == NULL) {
5231 /* Special case 1 - sys$posix_root = / */
5232 #if __CRTL_VER >= 70000000
5233 if (!decc_disable_posix_root) {
5234 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5242 /* Special case 2 - Convert NLA0: to /dev/null */
5243 #if __CRTL_VER < 70000000
5244 cmp_rslt = strncmp(spec,"NLA0:", 5);
5246 cmp_rslt = strncmp(spec,"nla0:", 5);
5248 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5250 if (cmp_rslt == 0) {
5251 strcpy(rslt, "/dev/null");
5254 if (spec[6] != '\0') {
5261 /* Also handle special case "SYS$SCRATCH:" */
5262 #if __CRTL_VER < 70000000
5263 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5265 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5267 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5269 if (cmp_rslt == 0) {
5272 islnm = my_trnlnm(tmp, "TMP", 0);
5274 strcpy(rslt, "/tmp");
5277 if (spec[12] != '\0') {
5285 if (*cp2 != '[' && *cp2 != '<') {
5288 else { /* the VMS spec begins with directories */
5290 if (*cp2 == ']' || *cp2 == '>') {
5291 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5294 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5295 if (getcwd(tmp,sizeof tmp,1) == NULL) {
5296 if (ts) Safefree(rslt);
5299 trnlnm_iter_count = 0;
5302 while (*cp3 != ':' && *cp3) cp3++;
5304 if (strchr(cp3,']') != NULL) break;
5305 trnlnm_iter_count++;
5306 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5307 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5309 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5310 retlen = devlen + dirlen;
5311 Renew(rslt,retlen+1+2*expand,char);
5317 *(cp1++) = *(cp3++);
5318 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
5322 if ((*cp2 == '^')) {
5323 /* EFS file escape, pass the next character as is */
5324 /* Fix me: HEX encoding for UNICODE not implemented */
5327 else if ( *cp2 == '.') {
5328 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5329 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5335 for (; cp2 <= dirend; cp2++) {
5336 if ((*cp2 == '^')) {
5337 /* EFS file escape, pass the next character as is */
5338 /* Fix me: HEX encoding for UNICODE not implemented */
5344 if (*(cp2+1) == '[') cp2++;
5346 else if (*cp2 == ']' || *cp2 == '>') {
5347 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5349 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5351 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5352 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5353 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5354 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5355 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5357 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5358 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5362 else if (*cp2 == '-') {
5363 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5364 while (*cp2 == '-') {
5366 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5368 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5369 if (ts) Safefree(rslt); /* filespecs like */
5370 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5374 else *(cp1++) = *cp2;
5376 else *(cp1++) = *cp2;
5378 while (*cp2) *(cp1++) = *(cp2++);
5381 /* This still leaves /000000/ when working with a
5382 * VMS device root or concealed root.
5388 ulen = strlen(rslt);
5390 /* Get rid of "000000/ in rooted filespecs */
5392 zeros = strstr(rslt, "/000000/");
5393 if (zeros != NULL) {
5395 mlen = ulen - (zeros - rslt) - 7;
5396 memmove(zeros, &zeros[7], mlen);
5405 } /* end of do_tounixspec() */
5407 /* External entry points */
5408 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
5409 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
5411 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5413 static int posix_to_vmsspec
5414 (char *vmspath, int vmspath_len, const char *unixpath) {
5416 struct FAB myfab = cc$rms_fab;
5417 struct NAML mynam = cc$rms_naml;
5418 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5419 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5425 /* If not a posix spec already, convert it */
5427 unixlen = strlen(unixpath);
5432 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5433 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5436 /* This is already a VMS specification, no conversion */
5438 strncpy(vmspath,unixpath, vmspath_len);
5440 vmspath[vmspath_len] = 0;
5441 if (unixpath[unixlen - 1] == '/')
5443 Newx(esa, VMS_MAXRSS, char);
5444 myfab.fab$l_fna = vmspath;
5445 myfab.fab$b_fns = strlen(vmspath);
5446 myfab.fab$l_naml = &mynam;
5447 mynam.naml$l_esa = NULL;
5448 mynam.naml$b_ess = 0;
5449 mynam.naml$l_long_expand = esa;
5450 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
5451 mynam.naml$l_rsa = NULL;
5452 mynam.naml$b_rss = 0;
5453 if (decc_efs_case_preserve)
5454 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
5455 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
5457 /* Set up the remaining naml fields */
5458 sts = sys$parse(&myfab);
5460 /* It failed! Try again as a UNIX filespec */
5466 /* get the Device ID and the FID */
5467 sts = sys$search(&myfab);
5468 /* on any failure, returned the POSIX ^UP^ filespec */
5473 specdsc.dsc$a_pointer = vmspath;
5474 specdsc.dsc$w_length = vmspath_len;
5476 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
5477 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
5478 sts = lib$fid_to_name
5479 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
5481 /* on any failure, returned the POSIX ^UP^ filespec */
5483 /* This can happen if user does not have permission to read directories */
5484 if (strncmp(unixpath,"\"^UP^",5) != 0)
5485 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5487 strcpy(vmspath, unixpath);
5490 vmspath[specdsc.dsc$w_length] = 0;
5492 /* Are we expecting a directory? */
5493 if (dir_flag != 0) {
5499 i = specdsc.dsc$w_length - 1;
5503 /* Version must be '1' */
5504 if (vmspath[i--] != '1')
5506 /* Version delimiter is one of ".;" */
5507 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
5510 if (vmspath[i--] != 'R')
5512 if (vmspath[i--] != 'I')
5514 if (vmspath[i--] != 'D')
5516 if (vmspath[i--] != '.')
5518 eptr = &vmspath[i+1];
5520 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
5521 if (vmspath[i-1] != '^') {
5529 /* Get rid of 6 imaginary zero directory filename */
5530 vmspath[i+1] = '\0';
5534 if (vmspath[i] == '0')
5548 /* Can not use LIB$FID_TO_NAME, so doing a manual conversion */
5549 static int posix_to_vmsspec_hardway
5550 (char *vmspath, int vmspath_len, const char *unixpath) {
5553 const char *unixptr;
5555 const char *lastslash;
5556 const char *lastdot;
5567 /* Ignore leading "/" characters */
5568 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
5571 unixlen = strlen(unixptr);
5573 /* Do nothing with blank paths */
5579 lastslash = strrchr(unixptr,'/');
5580 lastdot = strrchr(unixptr,'.');
5583 /* last dot is last dot or past end of string */
5584 if (lastdot == NULL)
5585 lastdot = unixptr + unixlen;
5587 /* if no directories, set last slash to beginning of string */
5588 if (lastslash == NULL) {
5589 lastslash = unixptr;
5592 /* Watch out for trailing "." after last slash, still a directory */
5593 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
5594 lastslash = unixptr + unixlen;
5597 /* Watch out for traiing ".." after last slash, still a directory */
5598 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
5599 lastslash = unixptr + unixlen;
5602 /* dots in directories are aways escaped */
5603 if (lastdot < lastslash)
5604 lastdot = unixptr + unixlen;
5607 /* if (unixptr < lastslash) then we are in a directory */
5615 /* This could have a "^UP^ on the front */
5616 if (strncmp(unixptr,"\"^UP^",5) == 0) {
5621 /* Start with the UNIX path */
5622 if (*unixptr != '/') {
5623 /* relative paths */
5624 if (lastslash > unixptr) {
5627 /* skip leading ./ */
5629 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
5635 /* Are we still in a directory? */
5636 if (unixptr <= lastslash) {
5641 /* if not backing up, then it is relative forward. */
5642 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
5643 ((unixptr[2] == '/') || (unixptr[2] == '\0')))) {
5651 /* Perl wants an empty directory here to tell the difference
5652 * between a DCL commmand and a filename
5661 /* Handle two special files . and .. */
5662 if (unixptr[0] == '.') {
5663 if (unixptr[1] == '\0') {
5670 if ((unixptr[1] == '.') && (unixptr[2] == '\0')) {
5681 else { /* Absolute PATH handling */
5685 /* Need to find out where root is */
5687 /* In theory, this procedure should never get an absolute POSIX pathname
5688 * that can not be found on the POSIX root.
5689 * In practice, that can not be relied on, and things will show up
5690 * here that are a VMS device name or concealed logical name instead.
5691 * So to make things work, this procedure must be tolerant.
5693 Newx(esa, vmspath_len, char);
5696 nextslash = strchr(&unixptr[1],'/');
5698 if (nextslash != NULL) {
5699 seg_len = nextslash - &unixptr[1];
5700 strncpy(vmspath, unixptr, seg_len + 1);
5701 vmspath[seg_len+1] = 0;
5702 sts = posix_to_vmsspec(esa, vmspath_len, vmspath);
5706 /* This is verified to be a real path */
5708 sts = posix_to_vmsspec(esa, vmspath_len, "/");
5709 strcpy(vmspath, esa);
5710 vmslen = strlen(vmspath);
5711 vmsptr = vmspath + vmslen;
5713 if (unixptr < lastslash) {
5722 cmp = strcmp(rptr,"000000.");
5727 } /* removing 6 zeros */
5728 } /* vmslen < 7, no 6 zeros possible */
5729 } /* Not in a directory */
5730 } /* end of verified real path handling */
5735 /* Ok, we have a device or a concealed root that is not in POSIX
5736 * or we have garbage. Make the best of it.
5739 /* Posix to VMS destroyed this, so copy it again */
5740 strncpy(vmspath, &unixptr[1], seg_len);
5741 vmspath[seg_len] = 0;
5743 vmsptr = &vmsptr[vmslen];
5746 /* Now do we need to add the fake 6 zero directory to it? */
5748 if ((*lastslash == '/') && (nextslash < lastslash)) {
5749 /* No there is another directory */
5755 /* now we have foo:bar or foo:[000000]bar to decide from */
5756 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
5757 trnend = islnm ? islnm - 1 : 0;
5759 /* if this was a logical name, ']' or '>' must be present */
5760 /* if not a logical name, then assume a device and hope. */
5761 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
5763 /* if log name and trailing '.' then rooted - treat as device */
5764 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
5766 /* Fix me, if not a logical name, a device lookup should be
5767 * done to see if the device is file structured. If the device
5768 * is not file structured, the 6 zeros should not be put on.
5770 * As it is, perl is occasionally looking for dev:[000000]tty.
5771 * which looks a little strange.
5774 if ((add_6zero == 0) && (*nextslash == '/') && (nextslash[1] == '\0')) {
5775 /* No real directory present */
5780 /* Put the device delimiter on */
5783 unixptr = nextslash;
5786 /* Start directory if needed */
5787 if (!islnm || add_6zero) {
5793 /* add fake 000000] if needed */
5806 } /* non-POSIX translation */
5808 } /* End of relative/absolute path handling */
5810 while ((*unixptr) && (vmslen < vmspath_len)){
5815 if (dir_start != 0) {
5817 /* First characters in a directory are handled special */
5818 while ((*unixptr == '/') ||
5819 ((*unixptr == '.') &&
5820 ((unixptr[1]=='.') || (unixptr[1]=='/') || (unixptr[1]=='\0')))) {
5825 /* Skip redundant / in specification */
5826 while ((*unixptr == '/') && (dir_start != 0)) {
5829 if (unixptr == lastslash)
5832 if (unixptr == lastslash)
5835 /* Skip redundant ./ characters */
5836 while ((*unixptr == '.') &&
5837 ((unixptr[1] == '/')||(unixptr[1] == '\0'))) {
5840 if (unixptr == lastslash)
5842 if (*unixptr == '/')
5845 if (unixptr == lastslash)
5848 /* Skip redundant ../ characters */
5849 while ((*unixptr == '.') && (unixptr[1] == '.') &&
5850 ((unixptr[2] == '/') || (unixptr[2] == '\0'))) {
5851 /* Set the backing up flag */
5857 unixptr++; /* first . */
5858 unixptr++; /* second . */
5859 if (unixptr == lastslash)
5861 if (*unixptr == '/') /* The slash */
5864 if (unixptr == lastslash)
5867 /* To do: Perl expects /.../ to be translated to [...] on VMS */
5868 /* Not needed when VMS is pretending to be UNIX. */
5870 /* Is this loop stuck because of too many dots? */
5871 if (loop_flag == 0) {
5872 /* Exit the loop and pass the rest through */
5877 /* Are we done with directories yet? */
5878 if (unixptr >= lastslash) {
5880 /* Watch out for trailing dots */
5889 if (*unixptr == '/')
5893 /* Have we stopped backing up? */
5898 /* dir_start continues to be = 1 */
5900 if (*unixptr == '-') {
5902 *vmsptr++ = *unixptr++;
5906 /* Now are we done with directories yet? */
5907 if (unixptr >= lastslash) {
5909 /* Watch out for trailing dots */
5925 if (*unixptr == '\0')
5928 /* Normal characters - More EFS work probably needed */
5934 /* remove multiple / */
5935 while (unixptr[1] == '/') {
5938 if (unixptr == lastslash) {
5939 /* Watch out for trailing dots */
5951 /* To do: Perl expects /.../ to be translated to [...] on VMS */
5952 /* Not needed when VMS is pretending to be UNIX. */
5956 if (*unixptr != '\0')
5972 if ((unixptr < lastdot) || (unixptr[1] == '\0')) {
5978 /* trailing dot ==> '^..' on VMS */
5979 if (*unixptr == '\0') {
5983 *vmsptr++ = *unixptr++;
5986 if (quoted && (unixptr[1] == '\0')) {
5991 *vmsptr++ = *unixptr++;
5998 *vmsptr++ = *unixptr++;
6002 if (*unixptr != '\0') {
6003 *vmsptr++ = *unixptr++;
6010 /* Make sure directory is closed */
6011 if (unixptr == lastslash) {
6013 vmsptr2 = vmsptr - 1;
6015 if (*vmsptr2 != ']') {
6018 /* directories do not end in a dot bracket */
6019 if (*vmsptr2 == '.') {
6023 if (*vmsptr2 != '^') {
6024 vmsptr--; /* back up over the dot */
6032 /* Add a trailing dot if a file with no extension */
6033 vmsptr2 = vmsptr - 1;
6034 if ((*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
6035 (*lastdot != '.')) {
6046 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
6047 static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
6048 static char __tovmsspec_retbuf[VMS_MAXRSS];
6049 char *rslt, *dirend;
6054 unsigned long int infront = 0, hasdir = 1;
6058 if (path == NULL) return NULL;
6059 rslt_len = VMS_MAXRSS;
6060 if (buf) rslt = buf;
6061 else if (ts) Newx(rslt, VMS_MAXRSS, char);
6062 else rslt = __tovmsspec_retbuf;
6063 if (strpbrk(path,"]:>") ||
6064 (dirend = strrchr(path,'/')) == NULL) {
6065 if (path[0] == '.') {
6066 if (path[1] == '\0') strcpy(rslt,"[]");
6067 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
6068 else strcpy(rslt,path); /* probably garbage */
6070 else strcpy(rslt,path);
6074 /* Posix specifications are now a native VMS format */
6075 /*--------------------------------------------------*/
6076 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6077 if (decc_posix_compliant_pathnames) {
6078 if (strncmp(path,"\"^UP^",5) == 0) {
6079 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6085 vms_delim = strpbrk(path,"]:>");
6087 if ((vms_delim != NULL) ||
6088 ((dirend = strrchr(path,'/')) == NULL)) {
6090 /* VMS special characters found! */
6092 if (path[0] == '.') {
6093 if (path[1] == '\0') strcpy(rslt,"[]");
6094 else if (path[1] == '.' && path[2] == '\0')
6097 /* Dot preceeding a device or directory ? */
6099 /* If not in POSIX mode, pass it through and hope it works */
6100 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6101 if (!decc_posix_compliant_pathnames)
6102 strcpy(rslt,path); /* probably garbage */
6104 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6106 strcpy(rslt,path); /* probably garbage */
6112 /* If no VMS characters and in POSIX mode, convert it!
6113 * This is the easiest way to get directory specifications
6114 * handled correctly in POSIX mode
6116 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6117 if ((vms_delim == NULL) && decc_posix_compliant_pathnames)
6118 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6120 /* No unix path separators - presume VMS already */
6124 strcpy(rslt,path); /* probably garbage */
6130 /* If POSIX mode active, handle the conversion */
6131 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6132 if (decc_posix_compliant_pathnames) {
6133 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6138 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
6139 if (!*(dirend+2)) dirend +=2;
6140 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
6141 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
6146 lastdot = strrchr(cp2,'.');
6152 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6154 if (decc_disable_posix_root) {
6155 strcpy(rslt,"sys$disk:[000000]");
6158 strcpy(rslt,"sys$posix_root:[000000]");
6162 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
6164 Newx(trndev, VMS_MAXRSS, char);
6165 islnm = my_trnlnm(rslt,trndev,0);
6167 /* DECC special handling */
6169 if (strcmp(rslt,"bin") == 0) {
6170 strcpy(rslt,"sys$system");
6173 islnm = my_trnlnm(rslt,trndev,0);
6175 else if (strcmp(rslt,"tmp") == 0) {
6176 strcpy(rslt,"sys$scratch");
6179 islnm = my_trnlnm(rslt,trndev,0);
6181 else if (!decc_disable_posix_root) {
6182 strcpy(rslt, "sys$posix_root");
6186 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6187 islnm = my_trnlnm(rslt,trndev,0);
6189 else if (strcmp(rslt,"dev") == 0) {
6190 if (strncmp(cp2,"/null", 5) == 0) {
6191 if ((cp2[5] == 0) || (cp2[5] == '/')) {
6192 strcpy(rslt,"NLA0");
6196 islnm = my_trnlnm(rslt,trndev,0);
6202 trnend = islnm ? strlen(trndev) - 1 : 0;
6203 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
6204 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
6205 /* If the first element of the path is a logical name, determine
6206 * whether it has to be translated so we can add more directories. */
6207 if (!islnm || rooted) {
6210 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
6214 if (cp2 != dirend) {
6215 strcpy(rslt,trndev);
6216 cp1 = rslt + trnend;
6223 if (decc_disable_posix_root) {
6234 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
6235 cp2 += 2; /* skip over "./" - it's redundant */
6236 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
6238 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6239 *(cp1++) = '-'; /* "../" --> "-" */
6242 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
6243 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
6244 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6245 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
6248 else if ((cp2 != lastdot) || (lastdot < dirend)) {
6249 /* Escape the extra dots in EFS file specifications */
6252 if (cp2 > dirend) cp2 = dirend;
6254 else *(cp1++) = '.';
6256 for (; cp2 < dirend; cp2++) {
6258 if (*(cp2-1) == '/') continue;
6259 if (*(cp1-1) != '.') *(cp1++) = '.';
6262 else if (!infront && *cp2 == '.') {
6263 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
6264 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
6265 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6266 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
6267 else if (*(cp1-2) == '[') *(cp1-1) = '-';
6268 else { /* back up over previous directory name */
6270 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
6271 if (*(cp1-1) == '[') {
6272 memcpy(cp1,"000000.",7);
6277 if (cp2 == dirend) break;
6279 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
6280 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
6281 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
6282 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6284 *(cp1++) = '.'; /* Simulate trailing '/' */
6285 cp2 += 2; /* for loop will incr this to == dirend */
6287 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
6290 if (decc_efs_charset == 0)
6291 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
6293 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
6299 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
6301 if (decc_efs_charset == 0)
6308 else *(cp1++) = *cp2;
6312 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
6313 if (hasdir) *(cp1++) = ']';
6314 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
6315 /* fixme for ODS5 */
6330 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
6331 decc_readdir_dropdotnotype) {
6336 /* trailing dot ==> '^..' on VMS */
6343 *(cp1++) = *(cp2++);
6371 *(cp1++) = *(cp2++);
6374 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
6375 * which is wrong. UNIX notation should be ".dir. unless
6376 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
6377 * changing this behavior could break more things at this time.
6378 * efs character set effectively does not allow "." to be a version
6379 * delimiter as a further complication about changing this.
6381 if (decc_filename_unix_report != 0) {
6384 *(cp1++) = *(cp2++);
6387 *(cp1++) = *(cp2++);
6390 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
6394 /* Fix me for "^]", but that requires making sure that you do
6395 * not back up past the start of the filename
6397 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
6404 } /* end of do_tovmsspec() */
6406 /* External entry points */
6407 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,0); }
6408 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,1); }
6410 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
6411 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts) {
6412 static char __tovmspath_retbuf[VMS_MAXRSS];
6414 char *pathified, *vmsified, *cp;
6416 if (path == NULL) return NULL;
6417 Newx(pathified, VMS_MAXRSS, char);
6418 if (do_pathify_dirspec(path,pathified,0) == NULL) {
6419 Safefree(pathified);
6422 Newx(vmsified, VMS_MAXRSS, char);
6423 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) {
6424 Safefree(pathified);
6428 Safefree(pathified);
6434 vmslen = strlen(vmsified);
6435 Newx(cp,vmslen+1,char);
6436 memcpy(cp,vmsified,vmslen);
6442 strcpy(__tovmspath_retbuf,vmsified);
6444 return __tovmspath_retbuf;
6447 } /* end of do_tovmspath() */
6449 /* External entry points */
6450 char *Perl_tovmspath(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,0); }
6451 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,1); }
6454 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
6455 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts) {
6456 static char __tounixpath_retbuf[VMS_MAXRSS];
6458 char *pathified, *unixified, *cp;
6460 if (path == NULL) return NULL;
6461 Newx(pathified, VMS_MAXRSS, char);
6462 if (do_pathify_dirspec(path,pathified,0) == NULL) {
6463 Safefree(pathified);
6466 Newx(unixified, VMS_MAXRSS, char);
6467 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) {
6468 Safefree(pathified);
6469 Safefree(unixified);
6472 Safefree(pathified);
6474 Safefree(unixified);
6478 unixlen = strlen(unixified);
6479 Newx(cp,unixlen+1,char);
6480 memcpy(cp,unixified,unixlen);
6482 Safefree(unixified);
6486 strcpy(__tounixpath_retbuf,unixified);
6487 Safefree(unixified);
6488 return __tounixpath_retbuf;
6491 } /* end of do_tounixpath() */
6493 /* External entry points */
6494 char *Perl_tounixpath(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,0); }
6495 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,1); }
6498 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
6500 *****************************************************************************
6502 * Copyright (C) 1989-1994 by *
6503 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
6505 * Permission is hereby granted for the reproduction of this software, *
6506 * on condition that this copyright notice is included in the reproduction, *
6507 * and that such reproduction is not for purposes of profit or material *
6510 * 27-Aug-1994 Modified for inclusion in perl5 *
6511 * by Charles Bailey bailey@newman.upenn.edu *
6512 *****************************************************************************
6516 * getredirection() is intended to aid in porting C programs
6517 * to VMS (Vax-11 C). The native VMS environment does not support
6518 * '>' and '<' I/O redirection, or command line wild card expansion,
6519 * or a command line pipe mechanism using the '|' AND background
6520 * command execution '&'. All of these capabilities are provided to any
6521 * C program which calls this procedure as the first thing in the
6523 * The piping mechanism will probably work with almost any 'filter' type
6524 * of program. With suitable modification, it may useful for other
6525 * portability problems as well.
6527 * Author: Mark Pizzolato mark@infocomm.com
6531 struct list_item *next;
6535 static void add_item(struct list_item **head,
6536 struct list_item **tail,
6540 static void mp_expand_wild_cards(pTHX_ char *item,
6541 struct list_item **head,
6542 struct list_item **tail,
6545 static int background_process(pTHX_ int argc, char **argv);
6547 static void pipe_and_fork(pTHX_ char **cmargv);
6549 /*{{{ void getredirection(int *ac, char ***av)*/
6551 mp_getredirection(pTHX_ int *ac, char ***av)
6553 * Process vms redirection arg's. Exit if any error is seen.
6554 * If getredirection() processes an argument, it is erased
6555 * from the vector. getredirection() returns a new argc and argv value.
6556 * In the event that a background command is requested (by a trailing "&"),
6557 * this routine creates a background subprocess, and simply exits the program.
6559 * Warning: do not try to simplify the code for vms. The code
6560 * presupposes that getredirection() is called before any data is
6561 * read from stdin or written to stdout.
6563 * Normal usage is as follows:
6569 * getredirection(&argc, &argv);
6573 int argc = *ac; /* Argument Count */
6574 char **argv = *av; /* Argument Vector */
6575 char *ap; /* Argument pointer */
6576 int j; /* argv[] index */
6577 int item_count = 0; /* Count of Items in List */
6578 struct list_item *list_head = 0; /* First Item in List */
6579 struct list_item *list_tail; /* Last Item in List */
6580 char *in = NULL; /* Input File Name */
6581 char *out = NULL; /* Output File Name */
6582 char *outmode = "w"; /* Mode to Open Output File */
6583 char *err = NULL; /* Error File Name */
6584 char *errmode = "w"; /* Mode to Open Error File */
6585 int cmargc = 0; /* Piped Command Arg Count */
6586 char **cmargv = NULL;/* Piped Command Arg Vector */
6589 * First handle the case where the last thing on the line ends with
6590 * a '&'. This indicates the desire for the command to be run in a
6591 * subprocess, so we satisfy that desire.
6594 if (0 == strcmp("&", ap))
6595 exit(background_process(aTHX_ --argc, argv));
6596 if (*ap && '&' == ap[strlen(ap)-1])
6598 ap[strlen(ap)-1] = '\0';
6599 exit(background_process(aTHX_ argc, argv));
6602 * Now we handle the general redirection cases that involve '>', '>>',
6603 * '<', and pipes '|'.
6605 for (j = 0; j < argc; ++j)
6607 if (0 == strcmp("<", argv[j]))
6611 fprintf(stderr,"No input file after < on command line");
6612 exit(LIB$_WRONUMARG);
6617 if ('<' == *(ap = argv[j]))
6622 if (0 == strcmp(">", ap))
6626 fprintf(stderr,"No output file after > on command line");
6627 exit(LIB$_WRONUMARG);
6646 fprintf(stderr,"No output file after > or >> on command line");
6647 exit(LIB$_WRONUMARG);
6651 if (('2' == *ap) && ('>' == ap[1]))
6668 fprintf(stderr,"No output file after 2> or 2>> on command line");
6669 exit(LIB$_WRONUMARG);
6673 if (0 == strcmp("|", argv[j]))
6677 fprintf(stderr,"No command into which to pipe on command line");
6678 exit(LIB$_WRONUMARG);
6680 cmargc = argc-(j+1);
6681 cmargv = &argv[j+1];
6685 if ('|' == *(ap = argv[j]))
6693 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
6696 * Allocate and fill in the new argument vector, Some Unix's terminate
6697 * the list with an extra null pointer.
6699 Newx(argv, item_count+1, char *);
6700 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
6702 for (j = 0; j < item_count; ++j, list_head = list_head->next)
6703 argv[j] = list_head->value;
6709 fprintf(stderr,"'|' and '>' may not both be specified on command line");
6710 exit(LIB$_INVARGORD);
6712 pipe_and_fork(aTHX_ cmargv);
6715 /* Check for input from a pipe (mailbox) */
6717 if (in == NULL && 1 == isapipe(0))
6719 char mbxname[L_tmpnam];
6721 long int dvi_item = DVI$_DEVBUFSIZ;
6722 $DESCRIPTOR(mbxnam, "");
6723 $DESCRIPTOR(mbxdevnam, "");
6725 /* Input from a pipe, reopen it in binary mode to disable */
6726 /* carriage control processing. */
6728 fgetname(stdin, mbxname);
6729 mbxnam.dsc$a_pointer = mbxname;
6730 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
6731 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
6732 mbxdevnam.dsc$a_pointer = mbxname;
6733 mbxdevnam.dsc$w_length = sizeof(mbxname);
6734 dvi_item = DVI$_DEVNAM;
6735 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
6736 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
6739 freopen(mbxname, "rb", stdin);
6742 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
6746 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
6748 fprintf(stderr,"Can't open input file %s as stdin",in);
6751 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
6753 fprintf(stderr,"Can't open output file %s as stdout",out);
6756 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
6759 if (strcmp(err,"&1") == 0) {
6760 dup2(fileno(stdout), fileno(stderr));
6761 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
6764 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
6766 fprintf(stderr,"Can't open error file %s as stderr",err);
6770 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
6774 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
6777 #ifdef ARGPROC_DEBUG
6778 PerlIO_printf(Perl_debug_log, "Arglist:\n");
6779 for (j = 0; j < *ac; ++j)
6780 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
6782 /* Clear errors we may have hit expanding wildcards, so they don't
6783 show up in Perl's $! later */
6784 set_errno(0); set_vaxc_errno(1);
6785 } /* end of getredirection() */
6788 static void add_item(struct list_item **head,
6789 struct list_item **tail,
6795 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
6799 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
6800 *tail = (*tail)->next;
6802 (*tail)->value = value;
6806 static void mp_expand_wild_cards(pTHX_ char *item,
6807 struct list_item **head,
6808 struct list_item **tail,
6812 unsigned long int context = 0;
6820 $DESCRIPTOR(filespec, "");
6821 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
6822 $DESCRIPTOR(resultspec, "");
6823 unsigned long int lff_flags = 0;
6826 #ifdef VMS_LONGNAME_SUPPORT
6827 lff_flags = LIB$M_FIL_LONG_NAMES;
6830 for (cp = item; *cp; cp++) {
6831 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
6832 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
6834 if (!*cp || isspace(*cp))
6836 add_item(head, tail, item, count);
6841 /* "double quoted" wild card expressions pass as is */
6842 /* From DCL that means using e.g.: */
6843 /* perl program """perl.*""" */
6844 item_len = strlen(item);
6845 if ( '"' == *item && '"' == item[item_len-1] )
6848 item[item_len-2] = '\0';
6849 add_item(head, tail, item, count);
6853 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
6854 resultspec.dsc$b_class = DSC$K_CLASS_D;
6855 resultspec.dsc$a_pointer = NULL;
6856 Newx(vmsspec, VMS_MAXRSS, char);
6857 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
6858 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
6859 if (!isunix || !filespec.dsc$a_pointer)
6860 filespec.dsc$a_pointer = item;
6861 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
6863 * Only return version specs, if the caller specified a version
6865 had_version = strchr(item, ';');
6867 * Only return device and directory specs, if the caller specifed either.
6869 had_device = strchr(item, ':');
6870 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
6872 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
6873 (&filespec, &resultspec, &context,
6874 &defaultspec, 0, 0, &lff_flags)))
6879 Newx(string,resultspec.dsc$w_length+1,char);
6880 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
6881 string[resultspec.dsc$w_length] = '\0';
6882 if (NULL == had_version)
6883 *(strrchr(string, ';')) = '\0';
6884 if ((!had_directory) && (had_device == NULL))
6886 if (NULL == (devdir = strrchr(string, ']')))
6887 devdir = strrchr(string, '>');
6888 strcpy(string, devdir + 1);
6891 * Be consistent with what the C RTL has already done to the rest of
6892 * the argv items and lowercase all of these names.
6894 if (!decc_efs_case_preserve) {
6895 for (c = string; *c; ++c)
6899 if (isunix) trim_unixpath(string,item,1);
6900 add_item(head, tail, string, count);
6904 if (sts != RMS$_NMF)
6906 set_vaxc_errno(sts);
6909 case RMS$_FNF: case RMS$_DNF:
6910 set_errno(ENOENT); break;
6912 set_errno(ENOTDIR); break;
6914 set_errno(ENODEV); break;
6915 case RMS$_FNM: case RMS$_SYN:
6916 set_errno(EINVAL); break;
6918 set_errno(EACCES); break;
6920 _ckvmssts_noperl(sts);
6924 add_item(head, tail, item, count);
6925 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
6926 _ckvmssts_noperl(lib$find_file_end(&context));
6929 static int child_st[2];/* Event Flag set when child process completes */
6931 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
6933 static unsigned long int exit_handler(int *status)
6937 if (0 == child_st[0])
6939 #ifdef ARGPROC_DEBUG
6940 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
6942 fflush(stdout); /* Have to flush pipe for binary data to */
6943 /* terminate properly -- <tp@mccall.com> */
6944 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
6945 sys$dassgn(child_chan);
6947 sys$synch(0, child_st);
6952 static void sig_child(int chan)
6954 #ifdef ARGPROC_DEBUG
6955 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
6957 if (child_st[0] == 0)
6961 static struct exit_control_block exit_block =
6966 &exit_block.exit_status,
6971 pipe_and_fork(pTHX_ char **cmargv)
6974 struct dsc$descriptor_s *vmscmd;
6975 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
6976 int sts, j, l, ismcr, quote, tquote = 0;
6978 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
6979 vms_execfree(vmscmd);
6984 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
6985 && toupper(*(q+2)) == 'R' && !*(q+3);
6987 while (q && l < MAX_DCL_LINE_LENGTH) {
6989 if (j > 0 && quote) {
6995 if (ismcr && j > 1) quote = 1;
6996 tquote = (strchr(q,' ')) != NULL || *q == '\0';
6999 if (quote || tquote) {
7005 if ((quote||tquote) && *q == '"') {
7015 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
7017 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
7021 static int background_process(pTHX_ int argc, char **argv)
7023 char command[MAX_DCL_SYMBOL + 1] = "$";
7024 $DESCRIPTOR(value, "");
7025 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
7026 static $DESCRIPTOR(null, "NLA0:");
7027 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
7029 $DESCRIPTOR(pidstr, "");
7031 unsigned long int flags = 17, one = 1, retsts;
7034 strcat(command, argv[0]);
7035 len = strlen(command);
7036 while (--argc && (len < MAX_DCL_SYMBOL))
7038 strcat(command, " \"");
7039 strcat(command, *(++argv));
7040 strcat(command, "\"");
7041 len = strlen(command);
7043 value.dsc$a_pointer = command;
7044 value.dsc$w_length = strlen(value.dsc$a_pointer);
7045 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
7046 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
7047 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
7048 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
7051 _ckvmssts_noperl(retsts);
7053 #ifdef ARGPROC_DEBUG
7054 PerlIO_printf(Perl_debug_log, "%s\n", command);
7056 sprintf(pidstring, "%08X", pid);
7057 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
7058 pidstr.dsc$a_pointer = pidstring;
7059 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
7060 lib$set_symbol(&pidsymbol, &pidstr);
7064 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
7067 /* OS-specific initialization at image activation (not thread startup) */
7068 /* Older VAXC header files lack these constants */
7069 #ifndef JPI$_RIGHTS_SIZE
7070 # define JPI$_RIGHTS_SIZE 817
7072 #ifndef KGB$M_SUBSYSTEM
7073 # define KGB$M_SUBSYSTEM 0x8
7076 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
7078 /*{{{void vms_image_init(int *, char ***)*/
7080 vms_image_init(int *argcp, char ***argvp)
7082 char eqv[LNM$C_NAMLENGTH+1] = "";
7083 unsigned int len, tabct = 8, tabidx = 0;
7084 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
7085 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
7086 unsigned short int dummy, rlen;
7087 struct dsc$descriptor_s **tabvec;
7088 #if defined(PERL_IMPLICIT_CONTEXT)
7091 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
7092 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
7093 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
7096 #ifdef KILL_BY_SIGPRC
7097 Perl_csighandler_init();
7100 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
7101 _ckvmssts_noperl(iosb[0]);
7102 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
7103 if (iprv[i]) { /* Running image installed with privs? */
7104 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
7109 /* Rights identifiers might trigger tainting as well. */
7110 if (!will_taint && (rlen || rsz)) {
7111 while (rlen < rsz) {
7112 /* We didn't get all the identifiers on the first pass. Allocate a
7113 * buffer much larger than $GETJPI wants (rsz is size in bytes that
7114 * were needed to hold all identifiers at time of last call; we'll
7115 * allocate that many unsigned long ints), and go back and get 'em.
7116 * If it gave us less than it wanted to despite ample buffer space,
7117 * something's broken. Is your system missing a system identifier?
7119 if (rsz <= jpilist[1].buflen) {
7120 /* Perl_croak accvios when used this early in startup. */
7121 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
7122 rsz, (unsigned long) jpilist[1].buflen,
7123 "Check your rights database for corruption.\n");
7126 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
7127 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
7128 jpilist[1].buflen = rsz * sizeof(unsigned long int);
7129 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
7130 _ckvmssts_noperl(iosb[0]);
7132 mask = jpilist[1].bufadr;
7133 /* Check attribute flags for each identifier (2nd longword); protected
7134 * subsystem identifiers trigger tainting.
7136 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
7137 if (mask[i] & KGB$M_SUBSYSTEM) {
7142 if (mask != rlst) Safefree(mask);
7145 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
7146 * logical, some versions of the CRTL will add a phanthom /000000/
7147 * directory. This needs to be removed.
7149 if (decc_filename_unix_report) {
7152 ulen = strlen(argvp[0][0]);
7154 zeros = strstr(argvp[0][0], "/000000/");
7155 if (zeros != NULL) {
7157 mlen = ulen - (zeros - argvp[0][0]) - 7;
7158 memmove(zeros, &zeros[7], mlen);
7160 argvp[0][0][ulen] = '\0';
7163 /* It also may have a trailing dot that needs to be removed otherwise
7164 * it will be converted to VMS mode incorrectly.
7167 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
7168 argvp[0][0][ulen] = '\0';
7171 /* We need to use this hack to tell Perl it should run with tainting,
7172 * since its tainting flag may be part of the PL_curinterp struct, which
7173 * hasn't been allocated when vms_image_init() is called.
7176 char **newargv, **oldargv;
7178 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
7179 newargv[0] = oldargv[0];
7180 newargv[1] = (char *) PerlMem_malloc(3 * sizeof(char));
7181 strcpy(newargv[1], "-T");
7182 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
7184 newargv[*argcp] = NULL;
7185 /* We orphan the old argv, since we don't know where it's come from,
7186 * so we don't know how to free it.
7190 else { /* Did user explicitly request tainting? */
7192 char *cp, **av = *argvp;
7193 for (i = 1; i < *argcp; i++) {
7194 if (*av[i] != '-') break;
7195 for (cp = av[i]+1; *cp; cp++) {
7196 if (*cp == 'T') { will_taint = 1; break; }
7197 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
7198 strchr("DFIiMmx",*cp)) break;
7200 if (will_taint) break;
7205 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
7207 if (!tabidx) tabvec = (struct dsc$descriptor_s **) PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
7208 else if (tabidx >= tabct) {
7210 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
7212 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
7213 tabvec[tabidx]->dsc$w_length = 0;
7214 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
7215 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
7216 tabvec[tabidx]->dsc$a_pointer = NULL;
7217 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
7219 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
7221 getredirection(argcp,argvp);
7222 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
7224 # include <reentrancy.h>
7225 decc$set_reentrancy(C$C_MULTITHREAD);
7234 * Trim Unix-style prefix off filespec, so it looks like what a shell
7235 * glob expansion would return (i.e. from specified prefix on, not
7236 * full path). Note that returned filespec is Unix-style, regardless
7237 * of whether input filespec was VMS-style or Unix-style.
7239 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
7240 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
7241 * vector of options; at present, only bit 0 is used, and if set tells
7242 * trim unixpath to try the current default directory as a prefix when
7243 * presented with a possibly ambiguous ... wildcard.
7245 * Returns !=0 on success, with trimmed filespec replacing contents of
7246 * fspec, and 0 on failure, with contents of fpsec unchanged.
7248 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
7250 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
7252 char *unixified, *unixwild,
7253 *template, *base, *end, *cp1, *cp2;
7254 register int tmplen, reslen = 0, dirs = 0;
7256 Newx(unixwild, VMS_MAXRSS, char);
7257 if (!wildspec || !fspec) return 0;
7258 template = unixwild;
7259 if (strpbrk(wildspec,"]>:") != NULL) {
7260 if (do_tounixspec(wildspec,unixwild,0) == NULL) {
7266 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
7267 unixwild[VMS_MAXRSS-1] = 0;
7269 Newx(unixified, VMS_MAXRSS, char);
7270 if (strpbrk(fspec,"]>:") != NULL) {
7271 if (do_tounixspec(fspec,unixified,0) == NULL) {
7273 Safefree(unixified);
7276 else base = unixified;
7277 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
7278 * check to see that final result fits into (isn't longer than) fspec */
7279 reslen = strlen(fspec);
7283 /* No prefix or absolute path on wildcard, so nothing to remove */
7284 if (!*template || *template == '/') {
7286 if (base == fspec) {
7287 Safefree(unixified);
7290 tmplen = strlen(unixified);
7291 if (tmplen > reslen) {
7292 Safefree(unixified);
7293 return 0; /* not enough space */
7295 /* Copy unixified resultant, including trailing NUL */
7296 memmove(fspec,unixified,tmplen+1);
7297 Safefree(unixified);
7301 for (end = base; *end; end++) ; /* Find end of resultant filespec */
7302 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
7303 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
7304 for (cp1 = end ;cp1 >= base; cp1--)
7305 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
7307 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
7308 Safefree(unixified);
7314 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
7315 int ells = 1, totells, segdirs, match;
7316 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
7317 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7319 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
7321 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
7322 Newx(tpl, VMS_MAXRSS, char);
7323 if (ellipsis == template && opts & 1) {
7324 /* Template begins with an ellipsis. Since we can't tell how many
7325 * directory names at the front of the resultant to keep for an
7326 * arbitrary starting point, we arbitrarily choose the current
7327 * default directory as a starting point. If it's there as a prefix,
7328 * clip it off. If not, fall through and act as if the leading
7329 * ellipsis weren't there (i.e. return shortest possible path that
7330 * could match template).
7332 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
7334 Safefree(unixified);
7338 if (!decc_efs_case_preserve) {
7339 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7340 if (_tolower(*cp1) != _tolower(*cp2)) break;
7342 segdirs = dirs - totells; /* Min # of dirs we must have left */
7343 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
7344 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
7345 memmove(fspec,cp2+1,end - cp2);
7346 Safefree(unixified);
7352 /* First off, back up over constant elements at end of path */
7354 for (front = end ; front >= base; front--)
7355 if (*front == '/' && !dirs--) { front++; break; }
7357 Newx(lcres, VMS_MAXRSS, char);
7358 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
7360 if (!decc_efs_case_preserve) {
7361 *cp2 = _tolower(*cp1); /* Make lc copy for match */
7368 Safefree(unixified);
7372 return 0; /* Path too long. */
7375 *cp2 = '\0'; /* Pick up with memcpy later */
7376 lcfront = lcres + (front - base);
7377 /* Now skip over each ellipsis and try to match the path in front of it. */
7379 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
7380 if (*(cp1) == '.' && *(cp1+1) == '.' &&
7381 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
7382 if (cp1 < template) break; /* template started with an ellipsis */
7383 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
7384 ellipsis = cp1; continue;
7386 wilddsc.dsc$a_pointer = tpl;
7387 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
7389 for (segdirs = 0, cp2 = tpl;
7390 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
7392 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
7394 if (!decc_efs_case_preserve) {
7395 *cp2 = _tolower(*cp1); /* else lowercase for match */
7398 *cp2 = *cp1; /* else preserve case for match */
7401 if (*cp2 == '/') segdirs++;
7403 if (cp1 != ellipsis - 1) {
7404 Safefree(unixified);
7408 return 0; /* Path too long */
7410 /* Back up at least as many dirs as in template before matching */
7411 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
7412 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
7413 for (match = 0; cp1 > lcres;) {
7414 resdsc.dsc$a_pointer = cp1;
7415 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
7417 if (match == 1) lcfront = cp1;
7419 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
7422 Safefree(unixified);
7426 return 0; /* Can't find prefix ??? */
7428 if (match > 1 && opts & 1) {
7429 /* This ... wildcard could cover more than one set of dirs (i.e.
7430 * a set of similar dir names is repeated). If the template
7431 * contains more than 1 ..., upstream elements could resolve the
7432 * ambiguity, but it's not worth a full backtracking setup here.
7433 * As a quick heuristic, clip off the current default directory
7434 * if it's present to find the trimmed spec, else use the
7435 * shortest string that this ... could cover.
7437 char def[NAM$C_MAXRSS+1], *st;
7439 if (getcwd(def, sizeof def,0) == NULL) {
7440 Safefree(unixified);
7446 if (!decc_efs_case_preserve) {
7447 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7448 if (_tolower(*cp1) != _tolower(*cp2)) break;
7450 segdirs = dirs - totells; /* Min # of dirs we must have left */
7451 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
7452 if (*cp1 == '\0' && *cp2 == '/') {
7453 memmove(fspec,cp2+1,end - cp2);
7455 Safefree(unixified);
7460 /* Nope -- stick with lcfront from above and keep going. */
7463 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
7464 Safefree(unixified);
7472 } /* end of trim_unixpath() */
7477 * VMS readdir() routines.
7478 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
7480 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
7481 * Minor modifications to original routines.
7484 /* readdir may have been redefined by reentr.h, so make sure we get
7485 * the local version for what we do here.
7490 #if !defined(PERL_IMPLICIT_CONTEXT)
7491 # define readdir Perl_readdir
7493 # define readdir(a) Perl_readdir(aTHX_ a)
7496 /* Number of elements in vms_versions array */
7497 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
7500 * Open a directory, return a handle for later use.
7502 /*{{{ DIR *opendir(char*name) */
7504 Perl_opendir(pTHX_ const char *name)
7507 char dir[NAM$C_MAXRSS+1];
7510 if (do_tovmspath(name,dir,0) == NULL) {
7513 /* Check access before stat; otherwise stat does not
7514 * accurately report whether it's a directory.
7516 if (!cando_by_name(S_IRUSR,0,dir)) {
7517 /* cando_by_name has already set errno */
7520 if (flex_stat(dir,&sb) == -1) return NULL;
7521 if (!S_ISDIR(sb.st_mode)) {
7522 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
7525 /* Get memory for the handle, and the pattern. */
7527 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
7529 /* Fill in the fields; mainly playing with the descriptor. */
7530 sprintf(dd->pattern, "%s*.*",dir);
7533 dd->vms_wantversions = 0;
7534 dd->pat.dsc$a_pointer = dd->pattern;
7535 dd->pat.dsc$w_length = strlen(dd->pattern);
7536 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
7537 dd->pat.dsc$b_class = DSC$K_CLASS_S;
7538 #if defined(USE_ITHREADS)
7539 Newx(dd->mutex,1,perl_mutex);
7540 MUTEX_INIT( (perl_mutex *) dd->mutex );
7546 } /* end of opendir() */
7550 * Set the flag to indicate we want versions or not.
7552 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
7554 vmsreaddirversions(DIR *dd, int flag)
7556 dd->vms_wantversions = flag;
7561 * Free up an opened directory.
7563 /*{{{ void closedir(DIR *dd)*/
7565 Perl_closedir(DIR *dd)
7569 sts = lib$find_file_end(&dd->context);
7570 Safefree(dd->pattern);
7571 #if defined(USE_ITHREADS)
7572 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
7573 Safefree(dd->mutex);
7580 * Collect all the version numbers for the current file.
7583 collectversions(pTHX_ DIR *dd)
7585 struct dsc$descriptor_s pat;
7586 struct dsc$descriptor_s res;
7588 char *p, *text, buff[sizeof dd->entry.d_name];
7590 unsigned long context, tmpsts;
7592 /* Convenient shorthand. */
7595 /* Add the version wildcard, ignoring the "*.*" put on before */
7596 i = strlen(dd->pattern);
7597 Newx(text,i + e->d_namlen + 3,char);
7598 strcpy(text, dd->pattern);
7599 sprintf(&text[i - 3], "%s;*", e->d_name);
7601 /* Set up the pattern descriptor. */
7602 pat.dsc$a_pointer = text;
7603 pat.dsc$w_length = i + e->d_namlen - 1;
7604 pat.dsc$b_dtype = DSC$K_DTYPE_T;
7605 pat.dsc$b_class = DSC$K_CLASS_S;
7607 /* Set up result descriptor. */
7608 res.dsc$a_pointer = buff;
7609 res.dsc$w_length = sizeof buff - 2;
7610 res.dsc$b_dtype = DSC$K_DTYPE_T;
7611 res.dsc$b_class = DSC$K_CLASS_S;
7613 /* Read files, collecting versions. */
7614 for (context = 0, e->vms_verscount = 0;
7615 e->vms_verscount < VERSIZE(e);
7616 e->vms_verscount++) {
7617 tmpsts = lib$find_file(&pat, &res, &context);
7618 if (tmpsts == RMS$_NMF || context == 0) break;
7620 buff[sizeof buff - 1] = '\0';
7621 if ((p = strchr(buff, ';')))
7622 e->vms_versions[e->vms_verscount] = atoi(p + 1);
7624 e->vms_versions[e->vms_verscount] = -1;
7627 _ckvmssts(lib$find_file_end(&context));
7630 } /* end of collectversions() */
7633 * Read the next entry from the directory.
7635 /*{{{ struct dirent *readdir(DIR *dd)*/
7637 Perl_readdir(pTHX_ DIR *dd)
7639 struct dsc$descriptor_s res;
7640 char *p, buff[sizeof dd->entry.d_name];
7641 unsigned long int tmpsts;
7643 /* Set up result descriptor, and get next file. */
7644 res.dsc$a_pointer = buff;
7645 res.dsc$w_length = sizeof buff - 2;
7646 res.dsc$b_dtype = DSC$K_DTYPE_T;
7647 res.dsc$b_class = DSC$K_CLASS_S;
7648 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
7649 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
7650 if (!(tmpsts & 1)) {
7651 set_vaxc_errno(tmpsts);
7654 set_errno(EACCES); break;
7656 set_errno(ENODEV); break;
7658 set_errno(ENOTDIR); break;
7659 case RMS$_FNF: case RMS$_DNF:
7660 set_errno(ENOENT); break;
7667 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
7668 if (!decc_efs_case_preserve) {
7669 buff[sizeof buff - 1] = '\0';
7670 for (p = buff; *p; p++) *p = _tolower(*p);
7671 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7675 /* we don't want to force to lowercase, just null terminate */
7676 buff[res.dsc$w_length] = '\0';
7678 for (p = buff; *p; p++) *p = _tolower(*p);
7679 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7682 /* Skip any directory component and just copy the name. */
7683 if ((p = strchr(buff, ']'))) strcpy(dd->entry.d_name, p + 1);
7684 else strcpy(dd->entry.d_name, buff);
7686 /* Clobber the version. */
7687 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
7689 dd->entry.d_namlen = strlen(dd->entry.d_name);
7690 dd->entry.vms_verscount = 0;
7691 if (dd->vms_wantversions) collectversions(aTHX_ dd);
7694 } /* end of readdir() */
7698 * Read the next entry from the directory -- thread-safe version.
7700 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
7702 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
7706 MUTEX_LOCK( (perl_mutex *) dd->mutex );
7708 entry = readdir(dd);
7710 retval = ( *result == NULL ? errno : 0 );
7712 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
7716 } /* end of readdir_r() */
7720 * Return something that can be used in a seekdir later.
7722 /*{{{ long telldir(DIR *dd)*/
7724 Perl_telldir(DIR *dd)
7731 * Return to a spot where we used to be. Brute force.
7733 /*{{{ void seekdir(DIR *dd,long count)*/
7735 Perl_seekdir(pTHX_ DIR *dd, long count)
7737 int vms_wantversions;
7739 /* If we haven't done anything yet... */
7743 /* Remember some state, and clear it. */
7744 vms_wantversions = dd->vms_wantversions;
7745 dd->vms_wantversions = 0;
7746 _ckvmssts(lib$find_file_end(&dd->context));
7749 /* The increment is in readdir(). */
7750 for (dd->count = 0; dd->count < count; )
7753 dd->vms_wantversions = vms_wantversions;
7755 } /* end of seekdir() */
7758 /* VMS subprocess management
7760 * my_vfork() - just a vfork(), after setting a flag to record that
7761 * the current script is trying a Unix-style fork/exec.
7763 * vms_do_aexec() and vms_do_exec() are called in response to the
7764 * perl 'exec' function. If this follows a vfork call, then they
7765 * call out the regular perl routines in doio.c which do an
7766 * execvp (for those who really want to try this under VMS).
7767 * Otherwise, they do exactly what the perl docs say exec should
7768 * do - terminate the current script and invoke a new command
7769 * (See below for notes on command syntax.)
7771 * do_aspawn() and do_spawn() implement the VMS side of the perl
7772 * 'system' function.
7774 * Note on command arguments to perl 'exec' and 'system': When handled
7775 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
7776 * are concatenated to form a DCL command string. If the first arg
7777 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
7778 * the command string is handed off to DCL directly. Otherwise,
7779 * the first token of the command is taken as the filespec of an image
7780 * to run. The filespec is expanded using a default type of '.EXE' and
7781 * the process defaults for device, directory, etc., and if found, the resultant
7782 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
7783 * the command string as parameters. This is perhaps a bit complicated,
7784 * but I hope it will form a happy medium between what VMS folks expect
7785 * from lib$spawn and what Unix folks expect from exec.
7788 static int vfork_called;
7790 /*{{{int my_vfork()*/
7801 vms_execfree(struct dsc$descriptor_s *vmscmd)
7804 if (vmscmd->dsc$a_pointer) {
7805 Safefree(vmscmd->dsc$a_pointer);
7812 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
7814 char *junk, *tmps = Nullch;
7815 register size_t cmdlen = 0;
7822 tmps = SvPV(really,rlen);
7829 for (idx++; idx <= sp; idx++) {
7831 junk = SvPVx(*idx,rlen);
7832 cmdlen += rlen ? rlen + 1 : 0;
7835 Newx(PL_Cmd,cmdlen+1,char);
7837 if (tmps && *tmps) {
7838 strcpy(PL_Cmd,tmps);
7841 else *PL_Cmd = '\0';
7842 while (++mark <= sp) {
7844 char *s = SvPVx(*mark,n_a);
7846 if (*PL_Cmd) strcat(PL_Cmd," ");
7852 } /* end of setup_argstr() */
7855 static unsigned long int
7856 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
7857 struct dsc$descriptor_s **pvmscmd)
7859 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
7860 char image_name[NAM$C_MAXRSS+1];
7861 char image_argv[NAM$C_MAXRSS+1];
7862 $DESCRIPTOR(defdsc,".EXE");
7863 $DESCRIPTOR(defdsc2,".");
7864 $DESCRIPTOR(resdsc,resspec);
7865 struct dsc$descriptor_s *vmscmd;
7866 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7867 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
7868 register char *s, *rest, *cp, *wordbreak;
7873 Newx(vmscmd,sizeof(struct dsc$descriptor_s),struct dsc$descriptor_s);
7875 /* Make a copy for modification */
7876 cmdlen = strlen(incmd);
7877 Newx(cmd, cmdlen+1, char);
7878 strncpy(cmd, incmd, cmdlen);
7883 vmscmd->dsc$a_pointer = NULL;
7884 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
7885 vmscmd->dsc$b_class = DSC$K_CLASS_S;
7886 vmscmd->dsc$w_length = 0;
7887 if (pvmscmd) *pvmscmd = vmscmd;
7889 if (suggest_quote) *suggest_quote = 0;
7891 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
7892 return CLI$_BUFOVF; /* continuation lines currently unsupported */
7898 while (*s && isspace(*s)) s++;
7900 if (*s == '@' || *s == '$') {
7901 vmsspec[0] = *s; rest = s + 1;
7902 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
7904 else { cp = vmsspec; rest = s; }
7905 if (*rest == '.' || *rest == '/') {
7908 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
7909 rest++, cp2++) *cp2 = *rest;
7911 if (do_tovmsspec(resspec,cp,0)) {
7914 for (cp2 = vmsspec + strlen(vmsspec);
7915 *rest && cp2 - vmsspec < sizeof vmsspec;
7916 rest++, cp2++) *cp2 = *rest;
7921 /* Intuit whether verb (first word of cmd) is a DCL command:
7922 * - if first nonspace char is '@', it's a DCL indirection
7924 * - if verb contains a filespec separator, it's not a DCL command
7925 * - if it doesn't, caller tells us whether to default to a DCL
7926 * command, or to a local image unless told it's DCL (by leading '$')
7930 if (suggest_quote) *suggest_quote = 1;
7932 register char *filespec = strpbrk(s,":<[.;");
7933 rest = wordbreak = strpbrk(s," \"\t/");
7934 if (!wordbreak) wordbreak = s + strlen(s);
7935 if (*s == '$') check_img = 0;
7936 if (filespec && (filespec < wordbreak)) isdcl = 0;
7937 else isdcl = !check_img;
7941 imgdsc.dsc$a_pointer = s;
7942 imgdsc.dsc$w_length = wordbreak - s;
7943 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
7945 _ckvmssts(lib$find_file_end(&cxt));
7946 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
7947 if (!(retsts & 1) && *s == '$') {
7948 _ckvmssts(lib$find_file_end(&cxt));
7949 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
7950 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
7952 _ckvmssts(lib$find_file_end(&cxt));
7953 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
7957 _ckvmssts(lib$find_file_end(&cxt));
7962 while (*s && !isspace(*s)) s++;
7965 /* check that it's really not DCL with no file extension */
7966 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
7968 char b[256] = {0,0,0,0};
7969 read(fileno(fp), b, 256);
7970 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
7974 /* Check for script */
7976 if ((b[0] == '#') && (b[1] == '!'))
7978 #ifdef ALTERNATE_SHEBANG
7980 shebang_len = strlen(ALTERNATE_SHEBANG);
7981 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
7983 perlstr = strstr("perl",b);
7984 if (perlstr == NULL)
7992 if (shebang_len > 0) {
7995 char tmpspec[NAM$C_MAXRSS + 1];
7998 /* Image is following after white space */
7999 /*--------------------------------------*/
8000 while (isprint(b[i]) && isspace(b[i]))
8004 while (isprint(b[i]) && !isspace(b[i])) {
8005 tmpspec[j++] = b[i++];
8006 if (j >= NAM$C_MAXRSS)
8011 /* There may be some default parameters to the image */
8012 /*---------------------------------------------------*/
8014 while (isprint(b[i])) {
8015 image_argv[j++] = b[i++];
8016 if (j >= NAM$C_MAXRSS)
8019 while ((j > 0) && !isprint(image_argv[j-1]))
8023 /* It will need to be converted to VMS format and validated */
8024 if (tmpspec[0] != '\0') {
8027 /* Try to find the exact program requested to be run */
8028 /*---------------------------------------------------*/
8029 iname = do_rmsexpand
8030 (tmpspec, image_name, 0, ".exe", PERL_RMSEXPAND_M_VMS);
8031 if (iname != NULL) {
8032 if (cando_by_name(S_IXUSR,0,image_name)) {
8033 /* MCR prefix needed */
8037 /* Try again with a null type */
8038 /*----------------------------*/
8039 iname = do_rmsexpand
8040 (tmpspec, image_name, 0, ".", PERL_RMSEXPAND_M_VMS);
8041 if (iname != NULL) {
8042 if (cando_by_name(S_IXUSR,0,image_name)) {
8043 /* MCR prefix needed */
8049 /* Did we find the image to run the script? */
8050 /*------------------------------------------*/
8054 /* Assume DCL or foreign command exists */
8055 /*--------------------------------------*/
8056 tchr = strrchr(tmpspec, '/');
8063 strcpy(image_name, tchr);
8071 if (check_img && isdcl) return RMS$_FNF;
8073 if (cando_by_name(S_IXUSR,0,resspec)) {
8074 Newx(vmscmd->dsc$a_pointer, MAX_DCL_LINE_LENGTH ,char);
8076 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
8077 if (image_name[0] != 0) {
8078 strcat(vmscmd->dsc$a_pointer, image_name);
8079 strcat(vmscmd->dsc$a_pointer, " ");
8081 } else if (image_name[0] != 0) {
8082 strcpy(vmscmd->dsc$a_pointer, image_name);
8083 strcat(vmscmd->dsc$a_pointer, " ");
8085 strcpy(vmscmd->dsc$a_pointer,"@");
8087 if (suggest_quote) *suggest_quote = 1;
8089 /* If there is an image name, use original command */
8090 if (image_name[0] == 0)
8091 strcat(vmscmd->dsc$a_pointer,resspec);
8094 while (*rest && isspace(*rest)) rest++;
8097 if (image_argv[0] != 0) {
8098 strcat(vmscmd->dsc$a_pointer,image_argv);
8099 strcat(vmscmd->dsc$a_pointer, " ");
8105 rest_len = strlen(rest);
8106 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
8107 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
8108 strcat(vmscmd->dsc$a_pointer,rest);
8110 retsts = CLI$_BUFOVF;
8112 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
8114 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
8116 else retsts = RMS$_PRV;
8119 /* It's either a DCL command or we couldn't find a suitable image */
8120 vmscmd->dsc$w_length = strlen(cmd);
8121 /* if (cmd == PL_Cmd) {
8122 vmscmd->dsc$a_pointer = PL_Cmd;
8123 if (suggest_quote) *suggest_quote = 1;
8126 vmscmd->dsc$a_pointer = savepvn(cmd,vmscmd->dsc$w_length);
8130 /* check if it's a symbol (for quoting purposes) */
8131 if (suggest_quote && !*suggest_quote) {
8133 char equiv[LNM$C_NAMLENGTH];
8134 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8135 eqvdsc.dsc$a_pointer = equiv;
8137 iss = lib$get_symbol(vmscmd,&eqvdsc);
8138 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
8140 if (!(retsts & 1)) {
8141 /* just hand off status values likely to be due to user error */
8142 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
8143 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
8144 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
8145 else { _ckvmssts(retsts); }
8148 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
8150 } /* end of setup_cmddsc() */
8153 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
8155 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
8158 if (vfork_called) { /* this follows a vfork - act Unixish */
8160 if (vfork_called < 0) {
8161 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
8164 else return do_aexec(really,mark,sp);
8166 /* no vfork - act VMSish */
8167 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
8172 } /* end of vms_do_aexec() */
8175 /* {{{bool vms_do_exec(char *cmd) */
8177 Perl_vms_do_exec(pTHX_ const char *cmd)
8179 struct dsc$descriptor_s *vmscmd;
8181 if (vfork_called) { /* this follows a vfork - act Unixish */
8183 if (vfork_called < 0) {
8184 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
8187 else return do_exec(cmd);
8190 { /* no vfork - act VMSish */
8191 unsigned long int retsts;
8194 TAINT_PROPER("exec");
8195 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
8196 retsts = lib$do_command(vmscmd);
8199 case RMS$_FNF: case RMS$_DNF:
8200 set_errno(ENOENT); break;
8202 set_errno(ENOTDIR); break;
8204 set_errno(ENODEV); break;
8206 set_errno(EACCES); break;
8208 set_errno(EINVAL); break;
8209 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
8210 set_errno(E2BIG); break;
8211 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
8212 _ckvmssts(retsts); /* fall through */
8213 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8216 set_vaxc_errno(retsts);
8217 if (ckWARN(WARN_EXEC)) {
8218 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
8219 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
8221 vms_execfree(vmscmd);
8226 } /* end of vms_do_exec() */
8229 unsigned long int Perl_do_spawn(pTHX_ const char *);
8231 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
8233 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
8235 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
8238 } /* end of do_aspawn() */
8241 /* {{{unsigned long int do_spawn(char *cmd) */
8243 Perl_do_spawn(pTHX_ const char *cmd)
8245 unsigned long int sts, substs;
8248 TAINT_PROPER("spawn");
8249 if (!cmd || !*cmd) {
8250 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
8253 case RMS$_FNF: case RMS$_DNF:
8254 set_errno(ENOENT); break;
8256 set_errno(ENOTDIR); break;
8258 set_errno(ENODEV); break;
8260 set_errno(EACCES); break;
8262 set_errno(EINVAL); break;
8263 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
8264 set_errno(E2BIG); break;
8265 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
8266 _ckvmssts(sts); /* fall through */
8267 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8270 set_vaxc_errno(sts);
8271 if (ckWARN(WARN_EXEC)) {
8272 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
8280 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
8285 } /* end of do_spawn() */
8289 static unsigned int *sockflags, sockflagsize;
8292 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
8293 * routines found in some versions of the CRTL can't deal with sockets.
8294 * We don't shim the other file open routines since a socket isn't
8295 * likely to be opened by a name.
8297 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
8298 FILE *my_fdopen(int fd, const char *mode)
8300 FILE *fp = fdopen(fd, mode);
8303 unsigned int fdoff = fd / sizeof(unsigned int);
8304 Stat_t sbuf; /* native stat; we don't need flex_stat */
8305 if (!sockflagsize || fdoff > sockflagsize) {
8306 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
8307 else Newx (sockflags,fdoff+2,unsigned int);
8308 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
8309 sockflagsize = fdoff + 2;
8311 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
8312 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
8321 * Clear the corresponding bit when the (possibly) socket stream is closed.
8322 * There still a small hole: we miss an implicit close which might occur
8323 * via freopen(). >> Todo
8325 /*{{{ int my_fclose(FILE *fp)*/
8326 int my_fclose(FILE *fp) {
8328 unsigned int fd = fileno(fp);
8329 unsigned int fdoff = fd / sizeof(unsigned int);
8331 if (sockflagsize && fdoff <= sockflagsize)
8332 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
8340 * A simple fwrite replacement which outputs itmsz*nitm chars without
8341 * introducing record boundaries every itmsz chars.
8342 * We are using fputs, which depends on a terminating null. We may
8343 * well be writing binary data, so we need to accommodate not only
8344 * data with nulls sprinkled in the middle but also data with no null
8347 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
8349 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
8351 register char *cp, *end, *cpd, *data;
8352 register unsigned int fd = fileno(dest);
8353 register unsigned int fdoff = fd / sizeof(unsigned int);
8355 int bufsize = itmsz * nitm + 1;
8357 if (fdoff < sockflagsize &&
8358 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
8359 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
8363 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
8364 memcpy( data, src, itmsz*nitm );
8365 data[itmsz*nitm] = '\0';
8367 end = data + itmsz * nitm;
8368 retval = (int) nitm; /* on success return # items written */
8371 while (cpd <= end) {
8372 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
8373 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
8375 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
8379 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
8382 } /* end of my_fwrite() */
8385 /*{{{ int my_flush(FILE *fp)*/
8387 Perl_my_flush(pTHX_ FILE *fp)
8390 if ((res = fflush(fp)) == 0 && fp) {
8391 #ifdef VMS_DO_SOCKETS
8393 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
8395 res = fsync(fileno(fp));
8398 * If the flush succeeded but set end-of-file, we need to clear
8399 * the error because our caller may check ferror(). BTW, this
8400 * probably means we just flushed an empty file.
8402 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
8409 * Here are replacements for the following Unix routines in the VMS environment:
8410 * getpwuid Get information for a particular UIC or UID
8411 * getpwnam Get information for a named user
8412 * getpwent Get information for each user in the rights database
8413 * setpwent Reset search to the start of the rights database
8414 * endpwent Finish searching for users in the rights database
8416 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
8417 * (defined in pwd.h), which contains the following fields:-
8419 * char *pw_name; Username (in lower case)
8420 * char *pw_passwd; Hashed password
8421 * unsigned int pw_uid; UIC
8422 * unsigned int pw_gid; UIC group number
8423 * char *pw_unixdir; Default device/directory (VMS-style)
8424 * char *pw_gecos; Owner name
8425 * char *pw_dir; Default device/directory (Unix-style)
8426 * char *pw_shell; Default CLI name (eg. DCL)
8428 * If the specified user does not exist, getpwuid and getpwnam return NULL.
8430 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
8431 * not the UIC member number (eg. what's returned by getuid()),
8432 * getpwuid() can accept either as input (if uid is specified, the caller's
8433 * UIC group is used), though it won't recognise gid=0.
8435 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
8436 * information about other users in your group or in other groups, respectively.
8437 * If the required privilege is not available, then these routines fill only
8438 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
8441 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
8444 /* sizes of various UAF record fields */
8445 #define UAI$S_USERNAME 12
8446 #define UAI$S_IDENT 31
8447 #define UAI$S_OWNER 31
8448 #define UAI$S_DEFDEV 31
8449 #define UAI$S_DEFDIR 63
8450 #define UAI$S_DEFCLI 31
8453 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
8454 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
8455 (uic).uic$v_group != UIC$K_WILD_GROUP)
8457 static char __empty[]= "";
8458 static struct passwd __passwd_empty=
8459 {(char *) __empty, (char *) __empty, 0, 0,
8460 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
8461 static int contxt= 0;
8462 static struct passwd __pwdcache;
8463 static char __pw_namecache[UAI$S_IDENT+1];
8466 * This routine does most of the work extracting the user information.
8468 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
8471 unsigned char length;
8472 char pw_gecos[UAI$S_OWNER+1];
8474 static union uicdef uic;
8476 unsigned char length;
8477 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
8480 unsigned char length;
8481 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
8484 unsigned char length;
8485 char pw_shell[UAI$S_DEFCLI+1];
8487 static char pw_passwd[UAI$S_PWD+1];
8489 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
8490 struct dsc$descriptor_s name_desc;
8491 unsigned long int sts;
8493 static struct itmlst_3 itmlst[]= {
8494 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
8495 {sizeof(uic), UAI$_UIC, &uic, &luic},
8496 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
8497 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
8498 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
8499 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
8500 {0, 0, NULL, NULL}};
8502 name_desc.dsc$w_length= strlen(name);
8503 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8504 name_desc.dsc$b_class= DSC$K_CLASS_S;
8505 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
8507 /* Note that sys$getuai returns many fields as counted strings. */
8508 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
8509 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
8510 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
8512 else { _ckvmssts(sts); }
8513 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
8515 if ((int) owner.length < lowner) lowner= (int) owner.length;
8516 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
8517 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
8518 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
8519 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
8520 owner.pw_gecos[lowner]= '\0';
8521 defdev.pw_dir[ldefdev+ldefdir]= '\0';
8522 defcli.pw_shell[ldefcli]= '\0';
8523 if (valid_uic(uic)) {
8524 pwd->pw_uid= uic.uic$l_uic;
8525 pwd->pw_gid= uic.uic$v_group;
8528 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
8529 pwd->pw_passwd= pw_passwd;
8530 pwd->pw_gecos= owner.pw_gecos;
8531 pwd->pw_dir= defdev.pw_dir;
8532 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
8533 pwd->pw_shell= defcli.pw_shell;
8534 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
8536 ldir= strlen(pwd->pw_unixdir) - 1;
8537 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
8540 strcpy(pwd->pw_unixdir, pwd->pw_dir);
8541 if (!decc_efs_case_preserve)
8542 __mystrtolower(pwd->pw_unixdir);
8547 * Get information for a named user.
8549 /*{{{struct passwd *getpwnam(char *name)*/
8550 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
8552 struct dsc$descriptor_s name_desc;
8554 unsigned long int status, sts;
8556 __pwdcache = __passwd_empty;
8557 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
8558 /* We still may be able to determine pw_uid and pw_gid */
8559 name_desc.dsc$w_length= strlen(name);
8560 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8561 name_desc.dsc$b_class= DSC$K_CLASS_S;
8562 name_desc.dsc$a_pointer= (char *) name;
8563 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
8564 __pwdcache.pw_uid= uic.uic$l_uic;
8565 __pwdcache.pw_gid= uic.uic$v_group;
8568 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
8569 set_vaxc_errno(sts);
8570 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
8573 else { _ckvmssts(sts); }
8576 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
8577 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
8578 __pwdcache.pw_name= __pw_namecache;
8580 } /* end of my_getpwnam() */
8584 * Get information for a particular UIC or UID.
8585 * Called by my_getpwent with uid=-1 to list all users.
8587 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
8588 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
8590 const $DESCRIPTOR(name_desc,__pw_namecache);
8591 unsigned short lname;
8593 unsigned long int status;
8595 if (uid == (unsigned int) -1) {
8597 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
8598 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
8599 set_vaxc_errno(status);
8600 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8604 else { _ckvmssts(status); }
8605 } while (!valid_uic (uic));
8609 if (!uic.uic$v_group)
8610 uic.uic$v_group= PerlProc_getgid();
8612 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
8613 else status = SS$_IVIDENT;
8614 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
8615 status == RMS$_PRV) {
8616 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8619 else { _ckvmssts(status); }
8621 __pw_namecache[lname]= '\0';
8622 __mystrtolower(__pw_namecache);
8624 __pwdcache = __passwd_empty;
8625 __pwdcache.pw_name = __pw_namecache;
8627 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
8628 The identifier's value is usually the UIC, but it doesn't have to be,
8629 so if we can, we let fillpasswd update this. */
8630 __pwdcache.pw_uid = uic.uic$l_uic;
8631 __pwdcache.pw_gid = uic.uic$v_group;
8633 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
8636 } /* end of my_getpwuid() */
8640 * Get information for next user.
8642 /*{{{struct passwd *my_getpwent()*/
8643 struct passwd *Perl_my_getpwent(pTHX)
8645 return (my_getpwuid((unsigned int) -1));
8650 * Finish searching rights database for users.
8652 /*{{{void my_endpwent()*/
8653 void Perl_my_endpwent(pTHX)
8656 _ckvmssts(sys$finish_rdb(&contxt));
8662 #ifdef HOMEGROWN_POSIX_SIGNALS
8663 /* Signal handling routines, pulled into the core from POSIX.xs.
8665 * We need these for threads, so they've been rolled into the core,
8666 * rather than left in POSIX.xs.
8668 * (DRS, Oct 23, 1997)
8671 /* sigset_t is atomic under VMS, so these routines are easy */
8672 /*{{{int my_sigemptyset(sigset_t *) */
8673 int my_sigemptyset(sigset_t *set) {
8674 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8680 /*{{{int my_sigfillset(sigset_t *)*/
8681 int my_sigfillset(sigset_t *set) {
8683 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8684 for (i = 0; i < NSIG; i++) *set |= (1 << i);
8690 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
8691 int my_sigaddset(sigset_t *set, int sig) {
8692 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8693 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8694 *set |= (1 << (sig - 1));
8700 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
8701 int my_sigdelset(sigset_t *set, int sig) {
8702 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8703 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8704 *set &= ~(1 << (sig - 1));
8710 /*{{{int my_sigismember(sigset_t *set, int sig)*/
8711 int my_sigismember(sigset_t *set, int sig) {
8712 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8713 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8714 return *set & (1 << (sig - 1));
8719 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
8720 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
8723 /* If set and oset are both null, then things are badly wrong. Bail out. */
8724 if ((oset == NULL) && (set == NULL)) {
8725 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
8729 /* If set's null, then we're just handling a fetch. */
8731 tempmask = sigblock(0);
8736 tempmask = sigsetmask(*set);
8739 tempmask = sigblock(*set);
8742 tempmask = sigblock(0);
8743 sigsetmask(*oset & ~tempmask);
8746 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8751 /* Did they pass us an oset? If so, stick our holding mask into it */
8758 #endif /* HOMEGROWN_POSIX_SIGNALS */
8761 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
8762 * my_utime(), and flex_stat(), all of which operate on UTC unless
8763 * VMSISH_TIMES is true.
8765 /* method used to handle UTC conversions:
8766 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
8768 static int gmtime_emulation_type;
8769 /* number of secs to add to UTC POSIX-style time to get local time */
8770 static long int utc_offset_secs;
8772 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
8773 * in vmsish.h. #undef them here so we can call the CRTL routines
8782 * DEC C previous to 6.0 corrupts the behavior of the /prefix
8783 * qualifier with the extern prefix pragma. This provisional
8784 * hack circumvents this prefix pragma problem in previous
8787 #if defined(__VMS_VER) && __VMS_VER >= 70000000
8788 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
8789 # pragma __extern_prefix save
8790 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
8791 # define gmtime decc$__utctz_gmtime
8792 # define localtime decc$__utctz_localtime
8793 # define time decc$__utc_time
8794 # pragma __extern_prefix restore
8796 struct tm *gmtime(), *localtime();
8802 static time_t toutc_dst(time_t loc) {
8805 if ((rsltmp = localtime(&loc)) == NULL) return -1;
8806 loc -= utc_offset_secs;
8807 if (rsltmp->tm_isdst) loc -= 3600;
8810 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
8811 ((gmtime_emulation_type || my_time(NULL)), \
8812 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
8813 ((secs) - utc_offset_secs))))
8815 static time_t toloc_dst(time_t utc) {
8818 utc += utc_offset_secs;
8819 if ((rsltmp = localtime(&utc)) == NULL) return -1;
8820 if (rsltmp->tm_isdst) utc += 3600;
8823 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
8824 ((gmtime_emulation_type || my_time(NULL)), \
8825 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
8826 ((secs) + utc_offset_secs))))
8828 #ifndef RTL_USES_UTC
8831 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
8832 DST starts on 1st sun of april at 02:00 std time
8833 ends on last sun of october at 02:00 dst time
8834 see the UCX management command reference, SET CONFIG TIMEZONE
8835 for formatting info.
8837 No, it's not as general as it should be, but then again, NOTHING
8838 will handle UK times in a sensible way.
8843 parse the DST start/end info:
8844 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
8848 tz_parse_startend(char *s, struct tm *w, int *past)
8850 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
8851 int ly, dozjd, d, m, n, hour, min, sec, j, k;
8856 if (!past) return 0;
8859 if (w->tm_year % 4 == 0) ly = 1;
8860 if (w->tm_year % 100 == 0) ly = 0;
8861 if (w->tm_year+1900 % 400 == 0) ly = 1;
8864 dozjd = isdigit(*s);
8865 if (*s == 'J' || *s == 'j' || dozjd) {
8866 if (!dozjd && !isdigit(*++s)) return 0;
8869 d = d*10 + *s++ - '0';
8871 d = d*10 + *s++ - '0';
8874 if (d == 0) return 0;
8875 if (d > 366) return 0;
8877 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
8880 } else if (*s == 'M' || *s == 'm') {
8881 if (!isdigit(*++s)) return 0;
8883 if (isdigit(*s)) m = 10*m + *s++ - '0';
8884 if (*s != '.') return 0;
8885 if (!isdigit(*++s)) return 0;
8887 if (n < 1 || n > 5) return 0;
8888 if (*s != '.') return 0;
8889 if (!isdigit(*++s)) return 0;
8891 if (d > 6) return 0;
8895 if (!isdigit(*++s)) return 0;
8897 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
8899 if (!isdigit(*++s)) return 0;
8901 if (isdigit(*s)) min = 10*min + *s++ - '0';
8903 if (!isdigit(*++s)) return 0;
8905 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
8915 if (w->tm_yday < d) goto before;
8916 if (w->tm_yday > d) goto after;
8918 if (w->tm_mon+1 < m) goto before;
8919 if (w->tm_mon+1 > m) goto after;
8921 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
8922 k = d - j; /* mday of first d */
8924 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
8925 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
8926 if (w->tm_mday < k) goto before;
8927 if (w->tm_mday > k) goto after;
8930 if (w->tm_hour < hour) goto before;
8931 if (w->tm_hour > hour) goto after;
8932 if (w->tm_min < min) goto before;
8933 if (w->tm_min > min) goto after;
8934 if (w->tm_sec < sec) goto before;
8948 /* parse the offset: (+|-)hh[:mm[:ss]] */
8951 tz_parse_offset(char *s, int *offset)
8953 int hour = 0, min = 0, sec = 0;
8956 if (!offset) return 0;
8958 if (*s == '-') {neg++; s++;}
8960 if (!isdigit(*s)) return 0;
8962 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
8963 if (hour > 24) return 0;
8965 if (!isdigit(*++s)) return 0;
8967 if (isdigit(*s)) min = min*10 + (*s++ - '0');
8968 if (min > 59) return 0;
8970 if (!isdigit(*++s)) return 0;
8972 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
8973 if (sec > 59) return 0;
8977 *offset = (hour*60+min)*60 + sec;
8978 if (neg) *offset = -*offset;
8983 input time is w, whatever type of time the CRTL localtime() uses.
8984 sets dst, the zone, and the gmtoff (seconds)
8986 caches the value of TZ and UCX$TZ env variables; note that
8987 my_setenv looks for these and sets a flag if they're changed
8990 We have to watch out for the "australian" case (dst starts in
8991 october, ends in april)...flagged by "reverse" and checked by
8992 scanning through the months of the previous year.
8997 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
9002 char *dstzone, *tz, *s_start, *s_end;
9003 int std_off, dst_off, isdst;
9004 int y, dststart, dstend;
9005 static char envtz[1025]; /* longer than any logical, symbol, ... */
9006 static char ucxtz[1025];
9007 static char reversed = 0;
9013 reversed = -1; /* flag need to check */
9014 envtz[0] = ucxtz[0] = '\0';
9015 tz = my_getenv("TZ",0);
9016 if (tz) strcpy(envtz, tz);
9017 tz = my_getenv("UCX$TZ",0);
9018 if (tz) strcpy(ucxtz, tz);
9019 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
9022 if (!*tz) tz = ucxtz;
9025 while (isalpha(*s)) s++;
9026 s = tz_parse_offset(s, &std_off);
9028 if (!*s) { /* no DST, hurray we're done! */
9034 while (isalpha(*s)) s++;
9035 s2 = tz_parse_offset(s, &dst_off);
9039 dst_off = std_off - 3600;
9042 if (!*s) { /* default dst start/end?? */
9043 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
9044 s = strchr(ucxtz,',');
9046 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
9048 if (*s != ',') return 0;
9051 when = _toutc(when); /* convert to utc */
9052 when = when - std_off; /* convert to pseudolocal time*/
9054 w2 = localtime(&when);
9057 s = tz_parse_startend(s_start,w2,&dststart);
9059 if (*s != ',') return 0;
9062 when = _toutc(when); /* convert to utc */
9063 when = when - dst_off; /* convert to pseudolocal time*/
9064 w2 = localtime(&when);
9065 if (w2->tm_year != y) { /* spans a year, just check one time */
9066 when += dst_off - std_off;
9067 w2 = localtime(&when);
9070 s = tz_parse_startend(s_end,w2,&dstend);
9073 if (reversed == -1) { /* need to check if start later than end */
9077 if (when < 2*365*86400) {
9078 when += 2*365*86400;
9082 w2 =localtime(&when);
9083 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
9085 for (j = 0; j < 12; j++) {
9086 w2 =localtime(&when);
9087 tz_parse_startend(s_start,w2,&ds);
9088 tz_parse_startend(s_end,w2,&de);
9089 if (ds != de) break;
9093 if (de && !ds) reversed = 1;
9096 isdst = dststart && !dstend;
9097 if (reversed) isdst = dststart || !dstend;
9100 if (dst) *dst = isdst;
9101 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
9102 if (isdst) tz = dstzone;
9104 while(isalpha(*tz)) *zone++ = *tz++;
9110 #endif /* !RTL_USES_UTC */
9112 /* my_time(), my_localtime(), my_gmtime()
9113 * By default traffic in UTC time values, using CRTL gmtime() or
9114 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
9115 * Note: We need to use these functions even when the CRTL has working
9116 * UTC support, since they also handle C<use vmsish qw(times);>
9118 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
9119 * Modified by Charles Bailey <bailey@newman.upenn.edu>
9122 /*{{{time_t my_time(time_t *timep)*/
9123 time_t Perl_my_time(pTHX_ time_t *timep)
9128 if (gmtime_emulation_type == 0) {
9130 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
9131 /* results of calls to gmtime() and localtime() */
9132 /* for same &base */
9134 gmtime_emulation_type++;
9135 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
9136 char off[LNM$C_NAMLENGTH+1];;
9138 gmtime_emulation_type++;
9139 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
9140 gmtime_emulation_type++;
9141 utc_offset_secs = 0;
9142 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
9144 else { utc_offset_secs = atol(off); }
9146 else { /* We've got a working gmtime() */
9147 struct tm gmt, local;
9150 tm_p = localtime(&base);
9152 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
9153 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
9154 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
9155 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
9161 # ifdef RTL_USES_UTC
9162 if (VMSISH_TIME) when = _toloc(when);
9164 if (!VMSISH_TIME) when = _toutc(when);
9167 if (timep != NULL) *timep = when;
9170 } /* end of my_time() */
9174 /*{{{struct tm *my_gmtime(const time_t *timep)*/
9176 Perl_my_gmtime(pTHX_ const time_t *timep)
9182 if (timep == NULL) {
9183 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9186 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
9190 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
9192 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
9193 return gmtime(&when);
9195 /* CRTL localtime() wants local time as input, so does no tz correction */
9196 rsltmp = localtime(&when);
9197 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
9200 } /* end of my_gmtime() */
9204 /*{{{struct tm *my_localtime(const time_t *timep)*/
9206 Perl_my_localtime(pTHX_ const time_t *timep)
9208 time_t when, whenutc;
9212 if (timep == NULL) {
9213 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9216 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
9217 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
9220 # ifdef RTL_USES_UTC
9222 if (VMSISH_TIME) when = _toutc(when);
9224 /* CRTL localtime() wants UTC as input, does tz correction itself */
9225 return localtime(&when);
9227 # else /* !RTL_USES_UTC */
9230 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
9231 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
9234 #ifndef RTL_USES_UTC
9235 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
9236 when = whenutc - offset; /* pseudolocal time*/
9239 /* CRTL localtime() wants local time as input, so does no tz correction */
9240 rsltmp = localtime(&when);
9241 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
9245 } /* end of my_localtime() */
9248 /* Reset definitions for later calls */
9249 #define gmtime(t) my_gmtime(t)
9250 #define localtime(t) my_localtime(t)
9251 #define time(t) my_time(t)
9254 /* my_utime - update modification time of a file
9255 * calling sequence is identical to POSIX utime(), but under
9256 * VMS only the modification time is changed; ODS-2 does not
9257 * maintain access times. Restrictions differ from the POSIX
9258 * definition in that the time can be changed as long as the
9259 * caller has permission to execute the necessary IO$_MODIFY $QIO;
9260 * no separate checks are made to insure that the caller is the
9261 * owner of the file or has special privs enabled.
9262 * Code here is based on Joe Meadows' FILE utility.
9265 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
9266 * to VMS epoch (01-JAN-1858 00:00:00.00)
9267 * in 100 ns intervals.
9269 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
9271 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
9272 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
9276 long int bintime[2], len = 2, lowbit, unixtime,
9277 secscale = 10000000; /* seconds --> 100 ns intervals */
9278 unsigned long int chan, iosb[2], retsts;
9279 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
9280 struct FAB myfab = cc$rms_fab;
9281 struct NAM mynam = cc$rms_nam;
9282 #if defined (__DECC) && defined (__VAX)
9283 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
9284 * at least through VMS V6.1, which causes a type-conversion warning.
9286 # pragma message save
9287 # pragma message disable cvtdiftypes
9289 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
9290 struct fibdef myfib;
9291 #if defined (__DECC) && defined (__VAX)
9292 /* This should be right after the declaration of myatr, but due
9293 * to a bug in VAX DEC C, this takes effect a statement early.
9295 # pragma message restore
9297 /* cast ok for read only parameter */
9298 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
9299 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
9300 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
9302 if (file == NULL || *file == '\0') {
9304 set_vaxc_errno(LIB$_INVARG);
9307 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
9309 if (utimes != NULL) {
9310 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
9311 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
9312 * Since time_t is unsigned long int, and lib$emul takes a signed long int
9313 * as input, we force the sign bit to be clear by shifting unixtime right
9314 * one bit, then multiplying by an extra factor of 2 in lib$emul().
9316 lowbit = (utimes->modtime & 1) ? secscale : 0;
9317 unixtime = (long int) utimes->modtime;
9319 /* If input was UTC; convert to local for sys svc */
9320 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
9322 unixtime >>= 1; secscale <<= 1;
9323 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
9324 if (!(retsts & 1)) {
9326 set_vaxc_errno(retsts);
9329 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
9330 if (!(retsts & 1)) {
9332 set_vaxc_errno(retsts);
9337 /* Just get the current time in VMS format directly */
9338 retsts = sys$gettim(bintime);
9339 if (!(retsts & 1)) {
9341 set_vaxc_errno(retsts);
9346 myfab.fab$l_fna = vmsspec;
9347 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
9348 myfab.fab$l_nam = &mynam;
9349 mynam.nam$l_esa = esa;
9350 mynam.nam$b_ess = (unsigned char) sizeof esa;
9351 mynam.nam$l_rsa = rsa;
9352 mynam.nam$b_rss = (unsigned char) sizeof rsa;
9353 if (decc_efs_case_preserve)
9354 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9356 /* Look for the file to be affected, letting RMS parse the file
9357 * specification for us as well. I have set errno using only
9358 * values documented in the utime() man page for VMS POSIX.
9360 retsts = sys$parse(&myfab,0,0);
9361 if (!(retsts & 1)) {
9362 set_vaxc_errno(retsts);
9363 if (retsts == RMS$_PRV) set_errno(EACCES);
9364 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
9365 else set_errno(EVMSERR);
9368 retsts = sys$search(&myfab,0,0);
9369 if (!(retsts & 1)) {
9370 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9371 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9372 set_vaxc_errno(retsts);
9373 if (retsts == RMS$_PRV) set_errno(EACCES);
9374 else if (retsts == RMS$_FNF) set_errno(ENOENT);
9375 else set_errno(EVMSERR);
9379 devdsc.dsc$w_length = mynam.nam$b_dev;
9380 /* cast ok for read only parameter */
9381 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
9383 retsts = sys$assign(&devdsc,&chan,0,0);
9384 if (!(retsts & 1)) {
9385 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9386 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9387 set_vaxc_errno(retsts);
9388 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
9389 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
9390 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
9391 else set_errno(EVMSERR);
9395 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
9396 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
9398 memset((void *) &myfib, 0, sizeof myfib);
9399 #if defined(__DECC) || defined(__DECCXX)
9400 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
9401 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
9402 /* This prevents the revision time of the file being reset to the current
9403 * time as a result of our IO$_MODIFY $QIO. */
9404 myfib.fib$l_acctl = FIB$M_NORECORD;
9406 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
9407 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
9408 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
9410 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
9411 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9412 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9413 _ckvmssts(sys$dassgn(chan));
9414 if (retsts & 1) retsts = iosb[0];
9415 if (!(retsts & 1)) {
9416 set_vaxc_errno(retsts);
9417 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9418 else set_errno(EVMSERR);
9423 } /* end of my_utime() */
9427 * flex_stat, flex_lstat, flex_fstat
9428 * basic stat, but gets it right when asked to stat
9429 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
9432 #ifndef _USE_STD_STAT
9433 /* encode_dev packs a VMS device name string into an integer to allow
9434 * simple comparisons. This can be used, for example, to check whether two
9435 * files are located on the same device, by comparing their encoded device
9436 * names. Even a string comparison would not do, because stat() reuses the
9437 * device name buffer for each call; so without encode_dev, it would be
9438 * necessary to save the buffer and use strcmp (this would mean a number of
9439 * changes to the standard Perl code, to say nothing of what a Perl script
9442 * The device lock id, if it exists, should be unique (unless perhaps compared
9443 * with lock ids transferred from other nodes). We have a lock id if the disk is
9444 * mounted cluster-wide, which is when we tend to get long (host-qualified)
9445 * device names. Thus we use the lock id in preference, and only if that isn't
9446 * available, do we try to pack the device name into an integer (flagged by
9447 * the sign bit (LOCKID_MASK) being set).
9449 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
9450 * name and its encoded form, but it seems very unlikely that we will find
9451 * two files on different disks that share the same encoded device names,
9452 * and even more remote that they will share the same file id (if the test
9453 * is to check for the same file).
9455 * A better method might be to use sys$device_scan on the first call, and to
9456 * search for the device, returning an index into the cached array.
9457 * The number returned would be more intelligable.
9458 * This is probably not worth it, and anyway would take quite a bit longer
9459 * on the first call.
9461 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
9462 static mydev_t encode_dev (pTHX_ const char *dev)
9465 unsigned long int f;
9470 if (!dev || !dev[0]) return 0;
9474 struct dsc$descriptor_s dev_desc;
9475 unsigned long int status, lockid, item = DVI$_LOCKID;
9477 /* For cluster-mounted disks, the disk lock identifier is unique, so we
9478 can try that first. */
9479 dev_desc.dsc$w_length = strlen (dev);
9480 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
9481 dev_desc.dsc$b_class = DSC$K_CLASS_S;
9482 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
9483 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
9484 if (lockid) return (lockid & ~LOCKID_MASK);
9488 /* Otherwise we try to encode the device name */
9492 for (q = dev + strlen(dev); q--; q >= dev) {
9495 else if (isalpha (toupper (*q)))
9496 c= toupper (*q) - 'A' + (char)10;
9498 continue; /* Skip '$'s */
9500 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
9502 enc += f * (unsigned long int) c;
9504 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
9506 } /* end of encode_dev() */
9509 static char namecache[NAM$C_MAXRSS+1];
9512 is_null_device(name)
9515 if (decc_bug_devnull != 0) {
9516 if (strncmp("/dev/null", name, 9) == 0)
9519 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
9520 The underscore prefix, controller letter, and unit number are
9521 independently optional; for our purposes, the colon punctuation
9522 is not. The colon can be trailed by optional directory and/or
9523 filename, but two consecutive colons indicates a nodename rather
9524 than a device. [pr] */
9525 if (*name == '_') ++name;
9526 if (tolower(*name++) != 'n') return 0;
9527 if (tolower(*name++) != 'l') return 0;
9528 if (tolower(*name) == 'a') ++name;
9529 if (*name == '0') ++name;
9530 return (*name++ == ':') && (*name != ':');
9533 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
9534 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
9535 * subset of the applicable information.
9538 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
9540 char fname_phdev[NAM$C_MAXRSS+1];
9541 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9542 /* Namecache not workable with symbolic links, as symbolic links do
9543 * not have extensions and directories do in VMS mode. So in order
9544 * to test this, the did and ino_t must be used.
9546 * Fix-me - Hide the information in the new stat structure
9547 * Get rid of the namecache.
9549 if (decc_posix_compliant_pathnames == 0)
9551 if (statbufp == &PL_statcache)
9552 return cando_by_name(bit,effective,namecache);
9554 char fname[NAM$C_MAXRSS+1];
9555 unsigned long int retsts;
9556 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
9557 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9559 /* If the struct mystat is stale, we're OOL; stat() overwrites the
9560 device name on successive calls */
9561 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
9562 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
9563 namdsc.dsc$a_pointer = fname;
9564 namdsc.dsc$w_length = sizeof fname - 1;
9566 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
9567 &namdsc,&namdsc.dsc$w_length,0,0);
9569 fname[namdsc.dsc$w_length] = '\0';
9571 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
9572 * but if someone has redefined that logical, Perl gets very lost. Since
9573 * we have the physical device name from the stat buffer, just paste it on.
9575 strcpy( fname_phdev, statbufp->st_devnam );
9576 strcat( fname_phdev, strrchr(fname, ':') );
9578 return cando_by_name(bit,effective,fname_phdev);
9580 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
9581 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
9585 return FALSE; /* Should never get to here */
9587 } /* end of cando() */
9591 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
9593 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
9595 static char usrname[L_cuserid];
9596 static struct dsc$descriptor_s usrdsc =
9597 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
9598 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
9599 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
9600 unsigned short int retlen, trnlnm_iter_count;
9601 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9602 union prvdef curprv;
9603 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
9604 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
9605 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
9606 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
9608 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
9610 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9612 if (!fname || !*fname) return FALSE;
9613 /* Make sure we expand logical names, since sys$check_access doesn't */
9614 if (!strpbrk(fname,"/]>:")) {
9615 strcpy(fileified,fname);
9616 trnlnm_iter_count = 0;
9617 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
9618 trnlnm_iter_count++;
9619 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
9623 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
9624 retlen = namdsc.dsc$w_length = strlen(vmsname);
9625 namdsc.dsc$a_pointer = vmsname;
9626 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
9627 vmsname[retlen-1] == ':') {
9628 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
9629 namdsc.dsc$w_length = strlen(fileified);
9630 namdsc.dsc$a_pointer = fileified;
9634 case S_IXUSR: case S_IXGRP: case S_IXOTH:
9635 access = ARM$M_EXECUTE; break;
9636 case S_IRUSR: case S_IRGRP: case S_IROTH:
9637 access = ARM$M_READ; break;
9638 case S_IWUSR: case S_IWGRP: case S_IWOTH:
9639 access = ARM$M_WRITE; break;
9640 case S_IDUSR: case S_IDGRP: case S_IDOTH:
9641 access = ARM$M_DELETE; break;
9646 /* Before we call $check_access, create a user profile with the current
9647 * process privs since otherwise it just uses the default privs from the
9648 * UAF and might give false positives or negatives. This only works on
9649 * VMS versions v6.0 and later since that's when sys$create_user_profile
9653 /* get current process privs and username */
9654 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
9657 #if defined(__VMS_VER) && __VMS_VER >= 60000000
9659 /* find out the space required for the profile */
9660 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
9661 &usrprodsc.dsc$w_length,0));
9663 /* allocate space for the profile and get it filled in */
9664 Newx(usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
9665 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
9666 &usrprodsc.dsc$w_length,0));
9668 /* use the profile to check access to the file; free profile & analyze results */
9669 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
9670 Safefree(usrprodsc.dsc$a_pointer);
9671 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
9675 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
9679 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
9680 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
9681 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
9682 set_vaxc_errno(retsts);
9683 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9684 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
9685 else set_errno(ENOENT);
9688 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
9693 return FALSE; /* Should never get here */
9695 } /* end of cando_by_name() */
9699 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
9701 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
9703 if (!fstat(fd,(stat_t *) statbufp)) {
9704 if (statbufp == (Stat_t *) &PL_statcache) {
9707 /* Save name for cando by name in VMS format */
9708 cptr = getname(fd, namecache, 1);
9710 /* This should not happen, but just in case */
9712 namecache[0] = '\0';
9715 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
9716 #ifndef _USE_STD_STAT
9717 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
9718 statbufp->st_devnam[63] = 0;
9719 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
9722 * The device is only encoded so that Perl_cando can use it to
9723 * look up ACLS. So rmsexpand it to the 255 character version
9724 * and store it in ->st_devnam. rmsexpand needs to be fixed
9725 * for long filenames and symbolic links first. This also seems
9726 * to remove the need for a namecache that could be stale.
9730 # ifdef RTL_USES_UTC
9733 statbufp->st_mtime = _toloc(statbufp->st_mtime);
9734 statbufp->st_atime = _toloc(statbufp->st_atime);
9735 statbufp->st_ctime = _toloc(statbufp->st_ctime);
9740 if (!VMSISH_TIME) { /* Return UTC instead of local time */
9744 statbufp->st_mtime = _toutc(statbufp->st_mtime);
9745 statbufp->st_atime = _toutc(statbufp->st_atime);
9746 statbufp->st_ctime = _toutc(statbufp->st_ctime);
9753 } /* end of flex_fstat() */
9756 #if !defined(__VAX) && __CRTL_VER >= 80200000
9764 #define lstat(_x, _y) stat(_x, _y)
9767 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
9770 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
9772 char fileified[NAM$C_MAXRSS+1];
9773 char temp_fspec[NAM$C_MAXRSS+300];
9775 int saved_errno, saved_vaxc_errno;
9777 if (!fspec) return retval;
9778 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
9779 strcpy(temp_fspec, fspec);
9780 if (statbufp == (Stat_t *) &PL_statcache)
9781 do_tovmsspec(temp_fspec,namecache,0);
9782 if (decc_bug_devnull != 0) {
9783 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
9784 memset(statbufp,0,sizeof *statbufp);
9785 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
9786 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
9787 statbufp->st_uid = 0x00010001;
9788 statbufp->st_gid = 0x0001;
9789 time((time_t *)&statbufp->st_mtime);
9790 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
9795 /* Try for a directory name first. If fspec contains a filename without
9796 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
9797 * and sea:[wine.dark]water. exist, we prefer the directory here.
9798 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
9799 * not sea:[wine.dark]., if the latter exists. If the intended target is
9800 * the file with null type, specify this by calling flex_stat() with
9801 * a '.' at the end of fspec.
9803 * If we are in Posix filespec mode, accept the filename as is.
9805 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9806 if (decc_posix_compliant_pathnames == 0) {
9808 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
9809 if (lstat_flag == 0)
9810 retval = stat(fileified,(stat_t *) statbufp);
9812 retval = lstat(fileified,(stat_t *) statbufp);
9813 if (!retval && statbufp == (Stat_t *) &PL_statcache)
9814 strcpy(namecache,fileified);
9817 if (lstat_flag == 0)
9818 retval = stat(temp_fspec,(stat_t *) statbufp);
9820 retval = lstat(temp_fspec,(stat_t *) statbufp);
9822 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9824 if (lstat_flag == 0)
9825 retval = stat(temp_fspec,(stat_t *) statbufp);
9827 retval = lstat(temp_fspec,(stat_t *) statbufp);
9831 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
9832 #ifndef _USE_STD_STAT
9833 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
9834 statbufp->st_devnam[63] = 0;
9835 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
9838 * The device is only encoded so that Perl_cando can use it to
9839 * look up ACLS. So rmsexpand it to the 255 character version
9840 * and store it in ->st_devnam. rmsexpand needs to be fixed
9841 * for long filenames and symbolic links first. This also seems
9842 * to remove the need for a namecache that could be stale.
9845 # ifdef RTL_USES_UTC
9848 statbufp->st_mtime = _toloc(statbufp->st_mtime);
9849 statbufp->st_atime = _toloc(statbufp->st_atime);
9850 statbufp->st_ctime = _toloc(statbufp->st_ctime);
9855 if (!VMSISH_TIME) { /* Return UTC instead of local time */
9859 statbufp->st_mtime = _toutc(statbufp->st_mtime);
9860 statbufp->st_atime = _toutc(statbufp->st_atime);
9861 statbufp->st_ctime = _toutc(statbufp->st_ctime);
9865 /* If we were successful, leave errno where we found it */
9866 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
9869 } /* end of flex_stat_int() */
9872 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
9874 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
9876 return flex_stat_int(fspec, statbufp, 0);
9880 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
9882 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
9884 return flex_stat_int(fspec, statbufp, 1);
9889 /*{{{char *my_getlogin()*/
9890 /* VMS cuserid == Unix getlogin, except calling sequence */
9894 static char user[L_cuserid];
9895 return cuserid(user);
9900 /* rmscopy - copy a file using VMS RMS routines
9902 * Copies contents and attributes of spec_in to spec_out, except owner
9903 * and protection information. Name and type of spec_in are used as
9904 * defaults for spec_out. The third parameter specifies whether rmscopy()
9905 * should try to propagate timestamps from the input file to the output file.
9906 * If it is less than 0, no timestamps are preserved. If it is 0, then
9907 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
9908 * propagated to the output file at creation iff the output file specification
9909 * did not contain an explicit name or type, and the revision date is always
9910 * updated at the end of the copy operation. If it is greater than 0, then
9911 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
9912 * other than the revision date should be propagated, and bit 1 indicates
9913 * that the revision date should be propagated.
9915 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
9917 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
9918 * Incorporates, with permission, some code from EZCOPY by Tim Adye
9919 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
9920 * as part of the Perl standard distribution under the terms of the
9921 * GNU General Public License or the Perl Artistic License. Copies
9922 * of each may be found in the Perl standard distribution.
9924 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
9925 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
9927 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
9929 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
9930 rsa[NAM$C_MAXRSS], ubf[32256];
9931 unsigned long int i, sts, sts2;
9932 struct FAB fab_in, fab_out;
9933 struct RAB rab_in, rab_out;
9935 struct XABDAT xabdat;
9936 struct XABFHC xabfhc;
9937 struct XABRDT xabrdt;
9938 struct XABSUM xabsum;
9940 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
9941 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
9942 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9946 fab_in = cc$rms_fab;
9947 fab_in.fab$l_fna = vmsin;
9948 fab_in.fab$b_fns = strlen(vmsin);
9949 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
9950 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
9951 fab_in.fab$l_fop = FAB$M_SQO;
9952 fab_in.fab$l_nam = &nam;
9953 fab_in.fab$l_xab = (void *) &xabdat;
9956 nam.nam$l_rsa = rsa;
9957 nam.nam$b_rss = sizeof(rsa);
9958 nam.nam$l_esa = esa;
9959 nam.nam$b_ess = sizeof (esa);
9960 nam.nam$b_esl = nam.nam$b_rsl = 0;
9961 #ifdef NAM$M_NO_SHORT_UPCASE
9962 if (decc_efs_case_preserve)
9963 nam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9966 xabdat = cc$rms_xabdat; /* To get creation date */
9967 xabdat.xab$l_nxt = (void *) &xabfhc;
9969 xabfhc = cc$rms_xabfhc; /* To get record length */
9970 xabfhc.xab$l_nxt = (void *) &xabsum;
9972 xabsum = cc$rms_xabsum; /* To get key and area information */
9974 if (!((sts = sys$open(&fab_in)) & 1)) {
9975 set_vaxc_errno(sts);
9977 case RMS$_FNF: case RMS$_DNF:
9978 set_errno(ENOENT); break;
9980 set_errno(ENOTDIR); break;
9982 set_errno(ENODEV); break;
9984 set_errno(EINVAL); break;
9986 set_errno(EACCES); break;
9994 fab_out.fab$w_ifi = 0;
9995 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
9996 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
9997 fab_out.fab$l_fop = FAB$M_SQO;
9998 fab_out.fab$l_fna = vmsout;
9999 fab_out.fab$b_fns = strlen(vmsout);
10000 fab_out.fab$l_dna = nam.nam$l_name;
10001 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
10003 if (preserve_dates == 0) { /* Act like DCL COPY */
10004 nam.nam$b_nop |= NAM$M_SYNCHK;
10005 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
10006 if (!((sts = sys$parse(&fab_out)) & 1)) {
10007 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
10008 set_vaxc_errno(sts);
10011 fab_out.fab$l_xab = (void *) &xabdat;
10012 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
10014 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
10015 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
10016 preserve_dates =0; /* bitmask from this point forward */
10018 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
10019 if (!((sts = sys$create(&fab_out)) & 1)) {
10020 set_vaxc_errno(sts);
10023 set_errno(ENOENT); break;
10025 set_errno(ENOTDIR); break;
10027 set_errno(ENODEV); break;
10029 set_errno(EINVAL); break;
10031 set_errno(EACCES); break;
10033 set_errno(EVMSERR);
10037 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
10038 if (preserve_dates & 2) {
10039 /* sys$close() will process xabrdt, not xabdat */
10040 xabrdt = cc$rms_xabrdt;
10042 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
10044 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
10045 * is unsigned long[2], while DECC & VAXC use a struct */
10046 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
10048 fab_out.fab$l_xab = (void *) &xabrdt;
10051 rab_in = cc$rms_rab;
10052 rab_in.rab$l_fab = &fab_in;
10053 rab_in.rab$l_rop = RAB$M_BIO;
10054 rab_in.rab$l_ubf = ubf;
10055 rab_in.rab$w_usz = sizeof ubf;
10056 if (!((sts = sys$connect(&rab_in)) & 1)) {
10057 sys$close(&fab_in); sys$close(&fab_out);
10058 set_errno(EVMSERR); set_vaxc_errno(sts);
10062 rab_out = cc$rms_rab;
10063 rab_out.rab$l_fab = &fab_out;
10064 rab_out.rab$l_rbf = ubf;
10065 if (!((sts = sys$connect(&rab_out)) & 1)) {
10066 sys$close(&fab_in); sys$close(&fab_out);
10067 set_errno(EVMSERR); set_vaxc_errno(sts);
10071 while ((sts = sys$read(&rab_in))) { /* always true */
10072 if (sts == RMS$_EOF) break;
10073 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
10074 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
10075 sys$close(&fab_in); sys$close(&fab_out);
10076 set_errno(EVMSERR); set_vaxc_errno(sts);
10081 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
10082 sys$close(&fab_in); sys$close(&fab_out);
10083 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
10085 set_errno(EVMSERR); set_vaxc_errno(sts);
10091 } /* end of rmscopy() */
10093 /* ODS-5 support version */
10095 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
10097 char *vmsin, * vmsout, *esa, *esa_out,
10099 unsigned long int i, sts, sts2;
10100 struct FAB fab_in, fab_out;
10101 struct RAB rab_in, rab_out;
10103 struct NAML nam_out;
10104 struct XABDAT xabdat;
10105 struct XABFHC xabfhc;
10106 struct XABRDT xabrdt;
10107 struct XABSUM xabsum;
10109 Newx(vmsin, VMS_MAXRSS, char);
10110 Newx(vmsout, VMS_MAXRSS, char);
10111 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
10112 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
10115 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10119 Newx(esa, VMS_MAXRSS, char);
10121 fab_in = cc$rms_fab;
10122 fab_in.fab$l_fna = (char *) -1;
10123 fab_in.fab$b_fns = 0;
10124 nam.naml$l_long_filename = vmsin;
10125 nam.naml$l_long_filename_size = strlen(vmsin);
10126 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
10127 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
10128 fab_in.fab$l_fop = FAB$M_SQO;
10129 fab_in.fab$l_naml = &nam;
10130 fab_in.fab$l_xab = (void *) &xabdat;
10132 Newx(rsa, VMS_MAXRSS, char);
10133 nam.naml$l_rsa = NULL;
10134 nam.naml$b_rss = 0;
10135 nam.naml$l_long_result = rsa;
10136 nam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
10137 nam.naml$l_esa = NULL;
10138 nam.naml$b_ess = 0;
10139 nam.naml$l_long_expand = esa;
10140 nam.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
10141 nam.naml$b_esl = nam.naml$b_rsl = 0;
10142 nam.naml$l_long_expand_size = 0;
10143 nam.naml$l_long_result_size = 0;
10144 #ifdef NAM$M_NO_SHORT_UPCASE
10145 if (decc_efs_case_preserve)
10146 nam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
10149 xabdat = cc$rms_xabdat; /* To get creation date */
10150 xabdat.xab$l_nxt = (void *) &xabfhc;
10152 xabfhc = cc$rms_xabfhc; /* To get record length */
10153 xabfhc.xab$l_nxt = (void *) &xabsum;
10155 xabsum = cc$rms_xabsum; /* To get key and area information */
10157 if (!((sts = sys$open(&fab_in)) & 1)) {
10162 set_vaxc_errno(sts);
10164 case RMS$_FNF: case RMS$_DNF:
10165 set_errno(ENOENT); break;
10167 set_errno(ENOTDIR); break;
10169 set_errno(ENODEV); break;
10171 set_errno(EINVAL); break;
10173 set_errno(EACCES); break;
10175 set_errno(EVMSERR);
10182 fab_out.fab$w_ifi = 0;
10183 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
10184 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
10185 fab_out.fab$l_fop = FAB$M_SQO;
10186 fab_out.fab$l_naml = &nam_out;
10187 fab_out.fab$l_fna = (char *) -1;
10188 fab_out.fab$b_fns = 0;
10189 nam_out.naml$l_long_filename = vmsout;
10190 nam_out.naml$l_long_filename_size = strlen(vmsout);
10191 fab_out.fab$l_dna = (char *) -1;
10192 fab_out.fab$b_dns = 0;
10193 nam_out.naml$l_long_defname = nam.naml$l_long_name;
10194 nam_out.naml$l_long_defname_size =
10195 nam.naml$l_long_name ?
10196 nam.naml$l_long_name_size + nam.naml$l_long_type_size : 0;
10198 Newx(esa_out, VMS_MAXRSS, char);
10199 nam_out.naml$l_rsa = NULL;
10200 nam_out.naml$b_rss = 0;
10201 nam_out.naml$l_long_result = NULL;
10202 nam_out.naml$l_long_result_alloc = 0;
10203 nam_out.naml$l_esa = NULL;
10204 nam_out.naml$b_ess = 0;
10205 nam_out.naml$l_long_expand = esa_out;
10206 nam_out.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
10208 if (preserve_dates == 0) { /* Act like DCL COPY */
10209 nam_out.naml$b_nop |= NAM$M_SYNCHK;
10210 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
10211 if (!((sts = sys$parse(&fab_out)) & 1)) {
10217 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
10218 set_vaxc_errno(sts);
10221 fab_out.fab$l_xab = (void *) &xabdat;
10222 if (nam.naml$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
10224 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
10225 preserve_dates =0; /* bitmask from this point forward */
10227 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
10228 if (!((sts = sys$create(&fab_out)) & 1)) {
10234 set_vaxc_errno(sts);
10237 set_errno(ENOENT); break;
10239 set_errno(ENOTDIR); break;
10241 set_errno(ENODEV); break;
10243 set_errno(EINVAL); break;
10245 set_errno(EACCES); break;
10247 set_errno(EVMSERR);
10251 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
10252 if (preserve_dates & 2) {
10253 /* sys$close() will process xabrdt, not xabdat */
10254 xabrdt = cc$rms_xabrdt;
10256 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
10258 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
10259 * is unsigned long[2], while DECC & VAXC use a struct */
10260 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
10262 fab_out.fab$l_xab = (void *) &xabrdt;
10265 Newx(ubf, 32256, char);
10266 rab_in = cc$rms_rab;
10267 rab_in.rab$l_fab = &fab_in;
10268 rab_in.rab$l_rop = RAB$M_BIO;
10269 rab_in.rab$l_ubf = ubf;
10270 rab_in.rab$w_usz = 32256;
10271 if (!((sts = sys$connect(&rab_in)) & 1)) {
10272 sys$close(&fab_in); sys$close(&fab_out);
10279 set_errno(EVMSERR); set_vaxc_errno(sts);
10283 rab_out = cc$rms_rab;
10284 rab_out.rab$l_fab = &fab_out;
10285 rab_out.rab$l_rbf = ubf;
10286 if (!((sts = sys$connect(&rab_out)) & 1)) {
10287 sys$close(&fab_in); sys$close(&fab_out);
10294 set_errno(EVMSERR); set_vaxc_errno(sts);
10298 while ((sts = sys$read(&rab_in))) { /* always true */
10299 if (sts == RMS$_EOF) break;
10300 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
10301 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
10302 sys$close(&fab_in); sys$close(&fab_out);
10309 set_errno(EVMSERR); set_vaxc_errno(sts);
10315 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
10316 sys$close(&fab_in); sys$close(&fab_out);
10317 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
10325 set_errno(EVMSERR); set_vaxc_errno(sts);
10337 } /* end of rmscopy() */
10342 /*** The following glue provides 'hooks' to make some of the routines
10343 * from this file available from Perl. These routines are sufficiently
10344 * basic, and are required sufficiently early in the build process,
10345 * that's it's nice to have them available to miniperl as well as the
10346 * full Perl, so they're set up here instead of in an extension. The
10347 * Perl code which handles importation of these names into a given
10348 * package lives in [.VMS]Filespec.pm in @INC.
10352 rmsexpand_fromperl(pTHX_ CV *cv)
10355 char *fspec, *defspec = NULL, *rslt;
10358 if (!items || items > 2)
10359 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
10360 fspec = SvPV(ST(0),n_a);
10361 if (!fspec || !*fspec) XSRETURN_UNDEF;
10362 if (items == 2) defspec = SvPV(ST(1),n_a);
10364 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
10365 ST(0) = sv_newmortal();
10366 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
10371 vmsify_fromperl(pTHX_ CV *cv)
10377 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
10378 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
10379 ST(0) = sv_newmortal();
10380 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
10385 unixify_fromperl(pTHX_ CV *cv)
10391 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
10392 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
10393 ST(0) = sv_newmortal();
10394 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
10399 fileify_fromperl(pTHX_ CV *cv)
10405 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
10406 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
10407 ST(0) = sv_newmortal();
10408 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
10413 pathify_fromperl(pTHX_ CV *cv)
10419 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
10420 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
10421 ST(0) = sv_newmortal();
10422 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
10427 vmspath_fromperl(pTHX_ CV *cv)
10433 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
10434 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
10435 ST(0) = sv_newmortal();
10436 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
10441 unixpath_fromperl(pTHX_ CV *cv)
10447 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
10448 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
10449 ST(0) = sv_newmortal();
10450 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
10455 candelete_fromperl(pTHX_ CV *cv)
10458 char fspec[NAM$C_MAXRSS+1], *fsp;
10463 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
10465 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
10466 if (SvTYPE(mysv) == SVt_PVGV) {
10467 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
10468 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10475 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
10476 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10482 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
10487 rmscopy_fromperl(pTHX_ CV *cv)
10490 char *inspec, *outspec, *inp, *outp;
10492 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
10493 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10494 unsigned long int sts;
10499 if (items < 2 || items > 3)
10500 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
10502 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
10503 Newx(inspec, VMS_MAXRSS, char);
10504 if (SvTYPE(mysv) == SVt_PVGV) {
10505 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
10506 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10514 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
10515 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10521 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
10522 Newx(outspec, VMS_MAXRSS, char);
10523 if (SvTYPE(mysv) == SVt_PVGV) {
10524 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
10525 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10534 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
10535 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10542 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
10544 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
10550 /* The mod2fname is limited to shorter filenames by design, so it should
10551 * not be modified to support longer EFS pathnames
10554 mod2fname(pTHX_ CV *cv)
10557 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
10558 workbuff[NAM$C_MAXRSS*1 + 1];
10559 int total_namelen = 3, counter, num_entries;
10560 /* ODS-5 ups this, but we want to be consistent, so... */
10561 int max_name_len = 39;
10562 AV *in_array = (AV *)SvRV(ST(0));
10564 num_entries = av_len(in_array);
10566 /* All the names start with PL_. */
10567 strcpy(ultimate_name, "PL_");
10569 /* Clean up our working buffer */
10570 Zero(work_name, sizeof(work_name), char);
10572 /* Run through the entries and build up a working name */
10573 for(counter = 0; counter <= num_entries; counter++) {
10574 /* If it's not the first name then tack on a __ */
10576 strcat(work_name, "__");
10578 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
10582 /* Check to see if we actually have to bother...*/
10583 if (strlen(work_name) + 3 <= max_name_len) {
10584 strcat(ultimate_name, work_name);
10586 /* It's too darned big, so we need to go strip. We use the same */
10587 /* algorithm as xsubpp does. First, strip out doubled __ */
10588 char *source, *dest, last;
10591 for (source = work_name; *source; source++) {
10592 if (last == *source && last == '_') {
10598 /* Go put it back */
10599 strcpy(work_name, workbuff);
10600 /* Is it still too big? */
10601 if (strlen(work_name) + 3 > max_name_len) {
10602 /* Strip duplicate letters */
10605 for (source = work_name; *source; source++) {
10606 if (last == toupper(*source)) {
10610 last = toupper(*source);
10612 strcpy(work_name, workbuff);
10615 /* Is it *still* too big? */
10616 if (strlen(work_name) + 3 > max_name_len) {
10617 /* Too bad, we truncate */
10618 work_name[max_name_len - 2] = 0;
10620 strcat(ultimate_name, work_name);
10623 /* Okay, return it */
10624 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
10629 hushexit_fromperl(pTHX_ CV *cv)
10634 VMSISH_HUSHED = SvTRUE(ST(0));
10636 ST(0) = boolSV(VMSISH_HUSHED);
10642 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec);
10645 vms_realpath_fromperl(pTHX_ CV *cv)
10648 char *fspec, *rslt_spec, *rslt;
10651 if (!items || items != 1)
10652 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
10654 fspec = SvPV(ST(0),n_a);
10655 if (!fspec || !*fspec) XSRETURN_UNDEF;
10657 Newx(rslt_spec, VMS_MAXRSS + 1, char);
10658 rslt = do_vms_realpath(fspec, rslt_spec);
10659 ST(0) = sv_newmortal();
10661 sv_usepvn(ST(0),rslt,strlen(rslt));
10663 Safefree(rslt_spec);
10668 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10669 int do_vms_case_tolerant(void);
10672 vms_case_tolerant_fromperl(pTHX_ CV *cv)
10675 ST(0) = boolSV(do_vms_case_tolerant());
10681 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
10682 struct interp_intern *dst)
10684 memcpy(dst,src,sizeof(struct interp_intern));
10688 Perl_sys_intern_clear(pTHX)
10693 Perl_sys_intern_init(pTHX)
10695 unsigned int ix = RAND_MAX;
10700 /* fix me later to track running under GNV */
10701 /* this allows some limited testing */
10702 MY_POSIX_EXIT = decc_filename_unix_report;
10705 MY_INV_RAND_MAX = 1./x;
10709 init_os_extras(void)
10712 char* file = __FILE__;
10713 char temp_buff[512];
10714 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
10715 no_translate_barewords = TRUE;
10717 no_translate_barewords = FALSE;
10720 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
10721 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
10722 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
10723 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
10724 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
10725 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
10726 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
10727 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
10728 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
10729 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
10730 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
10732 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
10734 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10735 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
10738 store_pipelocs(aTHX); /* will redo any earlier attempts */
10745 #if __CRTL_VER == 80200000
10746 /* This missed getting in to the DECC SDK for 8.2 */
10747 char *realpath(const char *file_name, char * resolved_name, ...);
10750 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
10751 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
10752 * The perl fallback routine to provide realpath() is not as efficient
10756 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10758 return realpath(filespec, outbuf);
10762 /* External entry points */
10763 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10764 { return do_vms_realpath(filespec, outbuf); }
10766 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10771 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10772 /* case_tolerant */
10774 /*{{{int do_vms_case_tolerant(void)*/
10775 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
10776 * controlled by a process setting.
10778 int do_vms_case_tolerant(void)
10780 return vms_process_case_tolerant;
10783 /* External entry points */
10784 int Perl_vms_case_tolerant(void)
10785 { return do_vms_case_tolerant(); }
10787 int Perl_vms_case_tolerant(void)
10788 { return vms_process_case_tolerant; }
10792 /* Start of DECC RTL Feature handling */
10794 static int sys_trnlnm
10795 (const char * logname,
10799 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
10800 const unsigned long attr = LNM$M_CASE_BLIND;
10801 struct dsc$descriptor_s name_dsc;
10803 unsigned short result;
10804 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
10807 name_dsc.dsc$w_length = strlen(logname);
10808 name_dsc.dsc$a_pointer = (char *)logname;
10809 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10810 name_dsc.dsc$b_class = DSC$K_CLASS_S;
10812 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
10814 if ($VMS_STATUS_SUCCESS(status)) {
10816 /* Null terminate and return the string */
10817 /*--------------------------------------*/
10824 static int sys_crelnm
10825 (const char * logname,
10826 const char * value)
10829 const char * proc_table = "LNM$PROCESS_TABLE";
10830 struct dsc$descriptor_s proc_table_dsc;
10831 struct dsc$descriptor_s logname_dsc;
10832 struct itmlst_3 item_list[2];
10834 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
10835 proc_table_dsc.dsc$w_length = strlen(proc_table);
10836 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10837 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
10839 logname_dsc.dsc$a_pointer = (char *) logname;
10840 logname_dsc.dsc$w_length = strlen(logname);
10841 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10842 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
10844 item_list[0].buflen = strlen(value);
10845 item_list[0].itmcode = LNM$_STRING;
10846 item_list[0].bufadr = (char *)value;
10847 item_list[0].retlen = NULL;
10849 item_list[1].buflen = 0;
10850 item_list[1].itmcode = 0;
10852 ret_val = sys$crelnm
10854 (const struct dsc$descriptor_s *)&proc_table_dsc,
10855 (const struct dsc$descriptor_s *)&logname_dsc,
10857 (const struct item_list_3 *) item_list);
10863 /* C RTL Feature settings */
10865 static int set_features
10866 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
10867 int (* cli_routine)(void), /* Not documented */
10868 void *image_info) /* Not documented */
10875 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
10876 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
10877 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
10878 unsigned long case_perm;
10879 unsigned long case_image;
10882 /* Allow an exception to bring Perl into the VMS debugger */
10883 vms_debug_on_exception = 0;
10884 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
10885 if ($VMS_STATUS_SUCCESS(status)) {
10886 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10887 vms_debug_on_exception = 1;
10889 vms_debug_on_exception = 0;
10893 /* hacks to see if known bugs are still present for testing */
10895 /* Readdir is returning filenames in VMS syntax always */
10896 decc_bug_readdir_efs1 = 1;
10897 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
10898 if ($VMS_STATUS_SUCCESS(status)) {
10899 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10900 decc_bug_readdir_efs1 = 1;
10902 decc_bug_readdir_efs1 = 0;
10905 /* PCP mode requires creating /dev/null special device file */
10906 decc_bug_devnull = 1;
10907 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
10908 if ($VMS_STATUS_SUCCESS(status)) {
10909 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10910 decc_bug_devnull = 1;
10912 decc_bug_devnull = 0;
10915 /* fgetname returning a VMS name in UNIX mode */
10916 decc_bug_fgetname = 1;
10917 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
10918 if ($VMS_STATUS_SUCCESS(status)) {
10919 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10920 decc_bug_fgetname = 1;
10922 decc_bug_fgetname = 0;
10925 /* UNIX directory names with no paths are broken in a lot of places */
10926 decc_dir_barename = 1;
10927 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
10928 if ($VMS_STATUS_SUCCESS(status)) {
10929 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10930 decc_dir_barename = 1;
10932 decc_dir_barename = 0;
10935 #if __CRTL_VER >= 70300000 && !defined(__VAX)
10936 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
10938 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
10939 if (decc_disable_to_vms_logname_translation < 0)
10940 decc_disable_to_vms_logname_translation = 0;
10943 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
10945 decc_efs_case_preserve = decc$feature_get_value(s, 1);
10946 if (decc_efs_case_preserve < 0)
10947 decc_efs_case_preserve = 0;
10950 s = decc$feature_get_index("DECC$EFS_CHARSET");
10952 decc_efs_charset = decc$feature_get_value(s, 1);
10953 if (decc_efs_charset < 0)
10954 decc_efs_charset = 0;
10957 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
10959 decc_filename_unix_report = decc$feature_get_value(s, 1);
10960 if (decc_filename_unix_report > 0)
10961 decc_filename_unix_report = 1;
10963 decc_filename_unix_report = 0;
10966 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
10968 decc_filename_unix_only = decc$feature_get_value(s, 1);
10969 if (decc_filename_unix_only > 0) {
10970 decc_filename_unix_only = 1;
10973 decc_filename_unix_only = 0;
10977 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
10979 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
10980 if (decc_filename_unix_no_version < 0)
10981 decc_filename_unix_no_version = 0;
10984 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
10986 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
10987 if (decc_readdir_dropdotnotype < 0)
10988 decc_readdir_dropdotnotype = 0;
10991 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
10992 if ($VMS_STATUS_SUCCESS(status)) {
10993 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
10995 dflt = decc$feature_get_value(s, 4);
10997 decc_disable_posix_root = decc$feature_get_value(s, 1);
10998 if (decc_disable_posix_root <= 0) {
10999 decc$feature_set_value(s, 1, 1);
11000 decc_disable_posix_root = 1;
11004 /* Traditionally Perl assumes this is off */
11005 decc_disable_posix_root = 1;
11006 decc$feature_set_value(s, 1, 1);
11011 #if __CRTL_VER >= 80200000
11012 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
11014 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
11015 if (decc_posix_compliant_pathnames < 0)
11016 decc_posix_compliant_pathnames = 0;
11017 if (decc_posix_compliant_pathnames > 4)
11018 decc_posix_compliant_pathnames = 0;
11023 status = sys_trnlnm
11024 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
11025 if ($VMS_STATUS_SUCCESS(status)) {
11026 val_str[0] = _toupper(val_str[0]);
11027 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11028 decc_disable_to_vms_logname_translation = 1;
11033 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
11034 if ($VMS_STATUS_SUCCESS(status)) {
11035 val_str[0] = _toupper(val_str[0]);
11036 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11037 decc_efs_case_preserve = 1;
11042 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
11043 if ($VMS_STATUS_SUCCESS(status)) {
11044 val_str[0] = _toupper(val_str[0]);
11045 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11046 decc_filename_unix_report = 1;
11049 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
11050 if ($VMS_STATUS_SUCCESS(status)) {
11051 val_str[0] = _toupper(val_str[0]);
11052 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11053 decc_filename_unix_only = 1;
11054 decc_filename_unix_report = 1;
11057 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
11058 if ($VMS_STATUS_SUCCESS(status)) {
11059 val_str[0] = _toupper(val_str[0]);
11060 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11061 decc_filename_unix_no_version = 1;
11064 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
11065 if ($VMS_STATUS_SUCCESS(status)) {
11066 val_str[0] = _toupper(val_str[0]);
11067 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11068 decc_readdir_dropdotnotype = 1;
11073 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
11075 /* Report true case tolerance */
11076 /*----------------------------*/
11077 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
11078 if (!$VMS_STATUS_SUCCESS(status))
11079 case_perm = PPROP$K_CASE_BLIND;
11080 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
11081 if (!$VMS_STATUS_SUCCESS(status))
11082 case_image = PPROP$K_CASE_BLIND;
11083 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
11084 (case_image == PPROP$K_CASE_SENSITIVE))
11085 vms_process_case_tolerant = 0;
11090 /* CRTL can be initialized past this point, but not before. */
11091 /* DECC$CRTL_INIT(); */
11097 /* DECC dependent attributes */
11098 #if __DECC_VER < 60560002
11100 #define not_executable
11102 #define relative ,rel
11103 #define not_executable ,noexe
11106 #pragma extern_model save
11107 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
11109 const __align (LONGWORD) int spare[8] = {0};
11110 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
11113 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
11114 nowrt,noshr relative not_executable
11116 const long vms_cc_features = (const long)set_features;
11119 ** Force a reference to LIB$INITIALIZE to ensure it
11120 ** exists in the image.
11122 int lib$initialize(void);
11124 #pragma extern_model strict_refdef
11126 int lib_init_ref = (int) lib$initialize;
11129 #pragma extern_model restore