3 * VMS-specific routines for perl5
6 * August 2005 Convert VMS status code to UNIX status codes
7 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
8 * and Perl_cando by Craig Berry
9 * 29-Aug-2000 Charles Lane's piping improvements rolled in
10 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
19 #include <climsgdef.h>
29 #include <libclidef.h>
31 #include <lib$routines.h>
34 #if __CRTL_VER >= 70301000 && !defined(__VAX)
44 #include <str$routines.h>
51 /* Set the maximum filespec size here as it is larger for EFS file
53 * Not fully implemented at this time because the larger size
54 * will likely impact the stack local storage requirements of
55 * threaded code, and probably cause hard to diagnose failures.
56 * To implement the larger sizes, all places where filename
57 * storage is put on the stack need to be changed to use
58 * New()/SafeFree() instead.
63 #define VMS_MAXRSS (NAML$C_MAXRSS+1)
64 #ifndef VMS_LONGNAME_SUPPORT
65 #define VMS_LONGNAME_SUPPORT 1
66 #endif /* VMS_LONGNAME_SUPPORT */
67 #endif /* NAML$C_MAXRSS */
68 #endif /* VMS_MAXRSS */
71 /* temporary hack until support is complete */
72 #ifdef VMS_LONGNAME_SUPPORT
73 #undef VMS_LONGNAME_SUPPORT
76 /* end of temporary hack until support is complete */
79 #define VMS_MAXRSS (NAM$C_MAXRSS + 1)
82 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
83 int decc$feature_get_index(const char *name);
84 char* decc$feature_get_name(int index);
85 int decc$feature_get_value(int index, int mode);
86 int decc$feature_set_value(int index, int mode, int value);
91 #if __CRTL_VER >= 70300000 && !defined(__VAX)
93 static int set_feature_default(const char *name, int value)
98 index = decc$feature_get_index(name);
100 status = decc$feature_set_value(index, 1, value);
101 if (index == -1 || (status == -1)) {
105 status = decc$feature_get_value(index, 1);
106 if (status != value) {
114 /* Older versions of ssdef.h don't have these */
115 #ifndef SS$_INVFILFOROP
116 # define SS$_INVFILFOROP 3930
118 #ifndef SS$_NOSUCHOBJECT
119 # define SS$_NOSUCHOBJECT 2696
122 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
123 #define PERLIO_NOT_STDIO 0
125 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
126 * code below needs to get to the underlying CRTL routines. */
127 #define DONT_MASK_RTL_CALLS
131 /* Anticipating future expansion in lexical warnings . . . */
132 #ifndef WARN_INTERNAL
133 # define WARN_INTERNAL WARN_MISC
136 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
137 # define RTL_USES_UTC 1
141 /* gcc's header files don't #define direct access macros
142 * corresponding to VAXC's variant structs */
144 # define uic$v_format uic$r_uic_form.uic$v_format
145 # define uic$v_group uic$r_uic_form.uic$v_group
146 # define uic$v_member uic$r_uic_form.uic$v_member
147 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
148 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
149 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
150 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
153 #if defined(NEED_AN_H_ERRNO)
158 #pragma message disable pragma
159 #pragma member_alignment save
160 #pragma nomember_alignment longword
162 #pragma message disable misalgndmem
165 unsigned short int buflen;
166 unsigned short int itmcode;
168 unsigned short int *retlen;
171 #pragma message restore
172 #pragma member_alignment restore
175 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
176 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
177 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
178 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
179 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
180 #define do_vms_realpath(a,b) mp_do_vms_realpath(aTHX_ a,b)
181 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
182 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
183 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
184 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
185 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
187 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts);
188 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts);
189 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
190 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts);
192 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
193 #define PERL_LNM_MAX_ALLOWED_INDEX 127
195 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
196 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
199 #define PERL_LNM_MAX_ITER 10
201 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
202 #if __CRTL_VER >= 70302000 && !defined(__VAX)
203 #define MAX_DCL_SYMBOL (8192)
204 #define MAX_DCL_LINE_LENGTH (4096 - 4)
206 #define MAX_DCL_SYMBOL (1024)
207 #define MAX_DCL_LINE_LENGTH (1024 - 4)
210 static char *__mystrtolower(char *str)
212 if (str) for (; *str; ++str) *str= tolower(*str);
216 static struct dsc$descriptor_s fildevdsc =
217 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
218 static struct dsc$descriptor_s crtlenvdsc =
219 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
220 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
221 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
222 static struct dsc$descriptor_s **env_tables = defenv;
223 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
225 /* True if we shouldn't treat barewords as logicals during directory */
227 static int no_translate_barewords;
230 static int tz_updated = 1;
233 /* DECC Features that may need to affect how Perl interprets
234 * displays filename information
236 static int decc_disable_to_vms_logname_translation = 1;
237 static int decc_disable_posix_root = 1;
238 int decc_efs_case_preserve = 0;
239 static int decc_efs_charset = 0;
240 static int decc_filename_unix_no_version = 0;
241 static int decc_filename_unix_only = 0;
242 int decc_filename_unix_report = 0;
243 int decc_posix_compliant_pathnames = 0;
244 int decc_readdir_dropdotnotype = 0;
245 static int vms_process_case_tolerant = 1;
247 /* bug workarounds if needed */
248 int decc_bug_readdir_efs1 = 0;
249 int decc_bug_devnull = 0;
250 int decc_bug_fgetname = 0;
251 int decc_dir_barename = 0;
253 /* Is this a UNIX file specification?
254 * No longer a simple check with EFS file specs
255 * For now, not a full check, but need to
256 * handle POSIX ^UP^ specifications
257 * Fixing to handle ^/ cases would require
258 * changes to many other conversion routines.
261 static is_unix_filespec(const char *path)
267 if (strncmp(path,"\"^UP^",5) != 0) {
268 pch1 = strchr(path, '/');
273 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
274 if (decc_filename_unix_report || decc_filename_unix_only) {
275 if (strcmp(path,".") == 0)
285 * Routine to retrieve the maximum equivalence index for an input
286 * logical name. Some calls to this routine have no knowledge if
287 * the variable is a logical or not. So on error we return a max
290 /*{{{int my_maxidx(const char *lnm) */
292 my_maxidx(const char *lnm)
296 int attr = LNM$M_CASE_BLIND;
297 struct dsc$descriptor lnmdsc;
298 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
301 lnmdsc.dsc$w_length = strlen(lnm);
302 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
303 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
304 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
306 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
307 if ((status & 1) == 0)
314 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
316 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
317 struct dsc$descriptor_s **tabvec, unsigned long int flags)
320 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
321 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
322 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
324 unsigned char acmode;
325 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
326 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
327 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
328 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
330 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
331 #if defined(PERL_IMPLICIT_CONTEXT)
334 aTHX = PERL_GET_INTERP;
340 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
341 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
343 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
344 *cp2 = _toupper(*cp1);
345 if (cp1 - lnm > LNM$C_NAMLENGTH) {
346 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
350 lnmdsc.dsc$w_length = cp1 - lnm;
351 lnmdsc.dsc$a_pointer = uplnm;
352 uplnm[lnmdsc.dsc$w_length] = '\0';
353 secure = flags & PERL__TRNENV_SECURE;
354 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
355 if (!tabvec || !*tabvec) tabvec = env_tables;
357 for (curtab = 0; tabvec[curtab]; curtab++) {
358 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
359 if (!ivenv && !secure) {
364 Perl_warn(aTHX_ "Can't read CRTL environ\n");
367 retsts = SS$_NOLOGNAM;
368 for (i = 0; environ[i]; i++) {
369 if ((eq = strchr(environ[i],'=')) &&
370 lnmdsc.dsc$w_length == (eq - environ[i]) &&
371 !strncmp(environ[i],uplnm,eq - environ[i])) {
373 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
374 if (!eqvlen) continue;
379 if (retsts != SS$_NOLOGNAM) break;
382 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
383 !str$case_blind_compare(&tmpdsc,&clisym)) {
384 if (!ivsym && !secure) {
385 unsigned short int deflen = LNM$C_NAMLENGTH;
386 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
387 /* dynamic dsc to accomodate possible long value */
388 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
389 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
391 if (eqvlen > MAX_DCL_SYMBOL) {
392 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
393 eqvlen = MAX_DCL_SYMBOL;
394 /* Special hack--we might be called before the interpreter's */
395 /* fully initialized, in which case either thr or PL_curcop */
396 /* might be bogus. We have to check, since ckWARN needs them */
397 /* both to be valid if running threaded */
398 if (ckWARN(WARN_MISC)) {
399 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
402 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
404 _ckvmssts(lib$sfree1_dd(&eqvdsc));
405 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
406 if (retsts == LIB$_NOSUCHSYM) continue;
411 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
412 midx = my_maxidx(lnm);
413 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
414 lnmlst[1].bufadr = cp2;
416 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
417 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
418 if (retsts == SS$_NOLOGNAM) break;
419 /* PPFs have a prefix */
422 *((int *)uplnm) == *((int *)"SYS$") &&
424 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
425 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
426 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
427 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
428 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
429 memmove(eqv,eqv+4,eqvlen-4);
435 if ((retsts == SS$_IVLOGNAM) ||
436 (retsts == SS$_NOLOGNAM)) { continue; }
439 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
440 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
441 if (retsts == SS$_NOLOGNAM) continue;
444 eqvlen = strlen(eqv);
448 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
449 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
450 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
451 retsts == SS$_NOLOGNAM) {
452 set_errno(EINVAL); set_vaxc_errno(retsts);
454 else _ckvmssts(retsts);
456 } /* end of vmstrnenv */
459 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
460 /* Define as a function so we can access statics. */
461 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
463 return vmstrnenv(lnm,eqv,idx,fildev,
464 #ifdef SECURE_INTERNAL_GETENV
465 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
474 * Note: Uses Perl temp to store result so char * can be returned to
475 * caller; this pointer will be invalidated at next Perl statement
477 * We define this as a function rather than a macro in terms of my_getenv_len()
478 * so that it'll work when PL_curinterp is undefined (and we therefore can't
481 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
483 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
486 static char *__my_getenv_eqv = NULL;
487 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
488 unsigned long int idx = 0;
489 int trnsuccess, success, secure, saverr, savvmserr;
493 midx = my_maxidx(lnm) + 1;
495 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
496 /* Set up a temporary buffer for the return value; Perl will
497 * clean it up at the next statement transition */
498 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
499 if (!tmpsv) return NULL;
503 /* Assume no interpreter ==> single thread */
504 if (__my_getenv_eqv != NULL) {
505 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
508 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
510 eqv = __my_getenv_eqv;
513 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
514 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
516 getcwd(eqv,LNM$C_NAMLENGTH);
520 /* Get rid of "000000/ in rooted filespecs */
523 zeros = strstr(eqv, "/000000/");
526 mlen = len - (zeros - eqv) - 7;
527 memmove(zeros, &zeros[7], mlen);
535 /* Impose security constraints only if tainting */
537 /* Impose security constraints only if tainting */
538 secure = PL_curinterp ? PL_tainting : will_taint;
539 saverr = errno; savvmserr = vaxc$errno;
546 #ifdef SECURE_INTERNAL_GETENV
547 secure ? PERL__TRNENV_SECURE : 0
553 /* For the getenv interface we combine all the equivalence names
554 * of a search list logical into one value to acquire a maximum
555 * value length of 255*128 (assuming %ENV is using logicals).
557 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
559 /* If the name contains a semicolon-delimited index, parse it
560 * off and make sure we only retrieve the equivalence name for
562 if ((cp2 = strchr(lnm,';')) != NULL) {
564 uplnm[cp2-lnm] = '\0';
565 idx = strtoul(cp2+1,NULL,0);
567 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
570 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
572 /* Discard NOLOGNAM on internal calls since we're often looking
573 * for an optional name, and this "error" often shows up as the
574 * (bogus) exit status for a die() call later on. */
575 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
576 return success ? eqv : Nullch;
579 } /* end of my_getenv() */
583 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
585 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
589 unsigned long idx = 0;
591 static char *__my_getenv_len_eqv = NULL;
592 int secure, saverr, savvmserr;
595 midx = my_maxidx(lnm) + 1;
597 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
598 /* Set up a temporary buffer for the return value; Perl will
599 * clean it up at the next statement transition */
600 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
601 if (!tmpsv) return NULL;
605 /* Assume no interpreter ==> single thread */
606 if (__my_getenv_len_eqv != NULL) {
607 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
610 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
612 buf = __my_getenv_len_eqv;
615 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
616 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
619 getcwd(buf,LNM$C_NAMLENGTH);
622 /* Get rid of "000000/ in rooted filespecs */
624 zeros = strstr(buf, "/000000/");
627 mlen = *len - (zeros - buf) - 7;
628 memmove(zeros, &zeros[7], mlen);
637 /* Impose security constraints only if tainting */
638 secure = PL_curinterp ? PL_tainting : will_taint;
639 saverr = errno; savvmserr = vaxc$errno;
646 #ifdef SECURE_INTERNAL_GETENV
647 secure ? PERL__TRNENV_SECURE : 0
653 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
655 if ((cp2 = strchr(lnm,';')) != NULL) {
658 idx = strtoul(cp2+1,NULL,0);
660 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
663 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
665 /* Get rid of "000000/ in rooted filespecs */
668 zeros = strstr(buf, "/000000/");
671 mlen = *len - (zeros - buf) - 7;
672 memmove(zeros, &zeros[7], mlen);
678 /* Discard NOLOGNAM on internal calls since we're often looking
679 * for an optional name, and this "error" often shows up as the
680 * (bogus) exit status for a die() call later on. */
681 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
682 return *len ? buf : Nullch;
685 } /* end of my_getenv_len() */
688 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
690 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
692 /*{{{ void prime_env_iter() */
695 /* Fill the %ENV associative array with all logical names we can
696 * find, in preparation for iterating over it.
699 static int primed = 0;
700 HV *seenhv = NULL, *envhv;
702 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
703 unsigned short int chan;
704 #ifndef CLI$M_TRUSTED
705 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
707 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
708 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
710 bool have_sym = FALSE, have_lnm = FALSE;
711 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
712 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
713 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
714 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
715 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
716 #if defined(PERL_IMPLICIT_CONTEXT)
719 #if defined(USE_ITHREADS)
720 static perl_mutex primenv_mutex;
721 MUTEX_INIT(&primenv_mutex);
724 #if defined(PERL_IMPLICIT_CONTEXT)
725 /* We jump through these hoops because we can be called at */
726 /* platform-specific initialization time, which is before anything is */
727 /* set up--we can't even do a plain dTHX since that relies on the */
728 /* interpreter structure to be initialized */
730 aTHX = PERL_GET_INTERP;
736 if (primed || !PL_envgv) return;
737 MUTEX_LOCK(&primenv_mutex);
738 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
739 envhv = GvHVn(PL_envgv);
740 /* Perform a dummy fetch as an lval to insure that the hash table is
741 * set up. Otherwise, the hv_store() will turn into a nullop. */
742 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
744 for (i = 0; env_tables[i]; i++) {
745 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
746 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
747 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
749 if (have_sym || have_lnm) {
750 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
751 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
752 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
753 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
756 for (i--; i >= 0; i--) {
757 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
760 for (j = 0; environ[j]; j++) {
761 if (!(start = strchr(environ[j],'='))) {
762 if (ckWARN(WARN_INTERNAL))
763 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
767 sv = newSVpv(start,0);
769 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
774 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
775 !str$case_blind_compare(&tmpdsc,&clisym)) {
776 strcpy(cmd,"Show Symbol/Global *");
777 cmddsc.dsc$w_length = 20;
778 if (env_tables[i]->dsc$w_length == 12 &&
779 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
780 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
781 flags = defflags | CLI$M_NOLOGNAM;
784 strcpy(cmd,"Show Logical *");
785 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
786 strcat(cmd," /Table=");
787 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
788 cmddsc.dsc$w_length = strlen(cmd);
790 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
791 flags = defflags | CLI$M_NOCLISYM;
794 /* Create a new subprocess to execute each command, to exclude the
795 * remote possibility that someone could subvert a mbx or file used
796 * to write multiple commands to a single subprocess.
799 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
800 0,&riseandshine,0,0,&clidsc,&clitabdsc);
801 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
802 defflags &= ~CLI$M_TRUSTED;
803 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
805 if (!buf) Newx(buf,mbxbufsiz + 1,char);
806 if (seenhv) SvREFCNT_dec(seenhv);
809 char *cp1, *cp2, *key;
810 unsigned long int sts, iosb[2], retlen, keylen;
813 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
814 if (sts & 1) sts = iosb[0] & 0xffff;
815 if (sts == SS$_ENDOFFILE) {
817 while (substs == 0) { sys$hiber(); wakect++;}
818 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
823 retlen = iosb[0] >> 16;
824 if (!retlen) continue; /* blank line */
826 if (iosb[1] != subpid) {
828 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
832 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
833 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
835 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
836 if (*cp1 == '(' || /* Logical name table name */
837 *cp1 == '=' /* Next eqv of searchlist */) continue;
838 if (*cp1 == '"') cp1++;
839 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
840 key = cp1; keylen = cp2 - cp1;
841 if (keylen && hv_exists(seenhv,key,keylen)) continue;
842 while (*cp2 && *cp2 != '=') cp2++;
843 while (*cp2 && *cp2 == '=') cp2++;
844 while (*cp2 && *cp2 == ' ') cp2++;
845 if (*cp2 == '"') { /* String translation; may embed "" */
846 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
847 cp2++; cp1--; /* Skip "" surrounding translation */
849 else { /* Numeric translation */
850 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
851 cp1--; /* stop on last non-space char */
853 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
854 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
857 PERL_HASH(hash,key,keylen);
859 if (cp1 == cp2 && *cp2 == '.') {
860 /* A single dot usually means an unprintable character, such as a null
861 * to indicate a zero-length value. Get the actual value to make sure.
863 char lnm[LNM$C_NAMLENGTH+1];
864 char eqv[MAX_DCL_SYMBOL+1];
865 strncpy(lnm, key, keylen);
866 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
867 sv = newSVpvn(eqv, strlen(eqv));
870 sv = newSVpvn(cp2,cp1 - cp2 + 1);
874 hv_store(envhv,key,keylen,sv,hash);
875 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
877 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
878 /* get the PPFs for this process, not the subprocess */
879 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
880 char eqv[LNM$C_NAMLENGTH+1];
882 for (i = 0; ppfs[i]; i++) {
883 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
884 sv = newSVpv(eqv,trnlen);
886 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
891 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
892 if (buf) Safefree(buf);
893 if (seenhv) SvREFCNT_dec(seenhv);
894 MUTEX_UNLOCK(&primenv_mutex);
897 } /* end of prime_env_iter */
901 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
902 /* Define or delete an element in the same "environment" as
903 * vmstrnenv(). If an element is to be deleted, it's removed from
904 * the first place it's found. If it's to be set, it's set in the
905 * place designated by the first element of the table vector.
906 * Like setenv() returns 0 for success, non-zero on error.
909 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
912 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
913 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
915 unsigned long int retsts, usermode = PSL$C_USER;
916 struct itmlst_3 *ile, *ilist;
917 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
918 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
919 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
920 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
921 $DESCRIPTOR(local,"_LOCAL");
924 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
928 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
929 *cp2 = _toupper(*cp1);
930 if (cp1 - lnm > LNM$C_NAMLENGTH) {
931 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
935 lnmdsc.dsc$w_length = cp1 - lnm;
936 if (!tabvec || !*tabvec) tabvec = env_tables;
938 if (!eqv) { /* we're deleting n element */
939 for (curtab = 0; tabvec[curtab]; curtab++) {
940 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
942 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
943 if ((cp1 = strchr(environ[i],'=')) &&
944 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
945 !strncmp(environ[i],lnm,cp1 - environ[i])) {
947 return setenv(lnm,"",1) ? vaxc$errno : 0;
950 ivenv = 1; retsts = SS$_NOLOGNAM;
952 if (ckWARN(WARN_INTERNAL))
953 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
954 ivenv = 1; retsts = SS$_NOSUCHPGM;
960 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
961 !str$case_blind_compare(&tmpdsc,&clisym)) {
962 unsigned int symtype;
963 if (tabvec[curtab]->dsc$w_length == 12 &&
964 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
965 !str$case_blind_compare(&tmpdsc,&local))
966 symtype = LIB$K_CLI_LOCAL_SYM;
967 else symtype = LIB$K_CLI_GLOBAL_SYM;
968 retsts = lib$delete_symbol(&lnmdsc,&symtype);
969 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
970 if (retsts == LIB$_NOSUCHSYM) continue;
974 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
975 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
976 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
977 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
978 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
982 else { /* we're defining a value */
983 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
985 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
987 if (ckWARN(WARN_INTERNAL))
988 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
989 retsts = SS$_NOSUCHPGM;
993 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
994 eqvdsc.dsc$w_length = strlen(eqv);
995 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
996 !str$case_blind_compare(&tmpdsc,&clisym)) {
997 unsigned int symtype;
998 if (tabvec[0]->dsc$w_length == 12 &&
999 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1000 !str$case_blind_compare(&tmpdsc,&local))
1001 symtype = LIB$K_CLI_LOCAL_SYM;
1002 else symtype = LIB$K_CLI_GLOBAL_SYM;
1003 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1006 if (!*eqv) eqvdsc.dsc$w_length = 1;
1007 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1009 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1010 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1011 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1012 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1013 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1014 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1017 Newx(ilist,nseg+1,struct itmlst_3);
1020 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1023 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1025 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1026 ile->itmcode = LNM$_STRING;
1028 if ((j+1) == nseg) {
1029 ile->buflen = strlen(c);
1030 /* in case we are truncating one that's too long */
1031 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1034 ile->buflen = LNM$C_NAMLENGTH;
1038 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1042 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1047 if (!(retsts & 1)) {
1049 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1050 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1051 set_errno(EVMSERR); break;
1052 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1053 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1054 set_errno(EINVAL); break;
1061 set_vaxc_errno(retsts);
1062 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1065 /* We reset error values on success because Perl does an hv_fetch()
1066 * before each hv_store(), and if the thing we're setting didn't
1067 * previously exist, we've got a leftover error message. (Of course,
1068 * this fails in the face of
1069 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1070 * in that the error reported in $! isn't spurious,
1071 * but it's right more often than not.)
1073 set_errno(0); set_vaxc_errno(retsts);
1077 } /* end of vmssetenv() */
1080 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1081 /* This has to be a function since there's a prototype for it in proto.h */
1083 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1086 int len = strlen(lnm);
1090 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1091 if (!strcmp(uplnm,"DEFAULT")) {
1092 if (eqv && *eqv) my_chdir(eqv);
1096 #ifndef RTL_USES_UTC
1097 if (len == 6 || len == 2) {
1100 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1102 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1103 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1107 (void) vmssetenv(lnm,eqv,NULL);
1111 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1113 * sets a user-mode logical in the process logical name table
1114 * used for redirection of sys$error
1117 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1119 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1120 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1121 unsigned long int iss, attr = LNM$M_CONFINE;
1122 unsigned char acmode = PSL$C_USER;
1123 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1125 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1126 d_name.dsc$w_length = strlen(name);
1128 lnmlst[0].buflen = strlen(eqv);
1129 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1131 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1132 if (!(iss&1)) lib$signal(iss);
1137 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1138 /* my_crypt - VMS password hashing
1139 * my_crypt() provides an interface compatible with the Unix crypt()
1140 * C library function, and uses sys$hash_password() to perform VMS
1141 * password hashing. The quadword hashed password value is returned
1142 * as a NUL-terminated 8 character string. my_crypt() does not change
1143 * the case of its string arguments; in order to match the behavior
1144 * of LOGINOUT et al., alphabetic characters in both arguments must
1145 * be upcased by the caller.
1147 * - fix me to call ACM services when available
1150 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1152 # ifndef UAI$C_PREFERRED_ALGORITHM
1153 # define UAI$C_PREFERRED_ALGORITHM 127
1155 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1156 unsigned short int salt = 0;
1157 unsigned long int sts;
1159 unsigned short int dsc$w_length;
1160 unsigned char dsc$b_type;
1161 unsigned char dsc$b_class;
1162 const char * dsc$a_pointer;
1163 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1164 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1165 struct itmlst_3 uailst[3] = {
1166 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1167 { sizeof salt, UAI$_SALT, &salt, 0},
1168 { 0, 0, NULL, NULL}};
1169 static char hash[9];
1171 usrdsc.dsc$w_length = strlen(usrname);
1172 usrdsc.dsc$a_pointer = usrname;
1173 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1175 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1179 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1184 set_vaxc_errno(sts);
1185 if (sts != RMS$_RNF) return NULL;
1188 txtdsc.dsc$w_length = strlen(textpasswd);
1189 txtdsc.dsc$a_pointer = textpasswd;
1190 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1191 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1194 return (char *) hash;
1196 } /* end of my_crypt() */
1200 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1201 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1202 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1204 /* fixup barenames that are directories for internal use.
1205 * There have been problems with the consistent handling of UNIX
1206 * style directory names when routines are presented with a name that
1207 * has no directory delimitors at all. So this routine will eventually
1210 static char * fixup_bare_dirnames(const char * name)
1212 if (decc_disable_to_vms_logname_translation) {
1219 * A little hack to get around a bug in some implemenation of remove()
1220 * that do not know how to delete a directory
1222 * Delete any file to which user has control access, regardless of whether
1223 * delete access is explicitly allowed.
1224 * Limitations: User must have write access to parent directory.
1225 * Does not block signals or ASTs; if interrupted in midstream
1226 * may leave file with an altered ACL.
1229 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1231 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1233 char *vmsname, *rspec;
1235 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1236 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1237 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1239 unsigned char myace$b_length;
1240 unsigned char myace$b_type;
1241 unsigned short int myace$w_flags;
1242 unsigned long int myace$l_access;
1243 unsigned long int myace$l_ident;
1244 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1245 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1246 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1248 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1249 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1250 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1251 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1252 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1253 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1255 /* Expand the input spec using RMS, since the CRTL remove() and
1256 * system services won't do this by themselves, so we may miss
1257 * a file "hiding" behind a logical name or search list. */
1258 Newx(vmsname, NAM$C_MAXRSS+1, char);
1259 if (do_tovmsspec(name,vmsname,0) == NULL) {
1264 if (decc_posix_compliant_pathnames) {
1265 /* In POSIX mode, we prefer to remove the UNIX name */
1267 remove_name = (char *)name;
1270 Newx(rspec, NAM$C_MAXRSS+1, char);
1271 if (do_rmsexpand(vmsname, rspec, 1, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1277 remove_name = rspec;
1280 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1282 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1283 Newx(remove_name, NAM$C_MAXRSS+1, char);
1284 do_pathify_dirspec(name, remove_name, 0);
1285 if (!rmdir(remove_name)) {
1287 Safefree(remove_name);
1289 return 0; /* Can we just get rid of it? */
1293 if (!rmdir(remove_name)) {
1295 return 0; /* Can we just get rid of it? */
1301 if (!remove(remove_name)) {
1303 return 0; /* Can we just get rid of it? */
1306 /* If not, can changing protections help? */
1307 if (vaxc$errno != RMS$_PRV) {
1312 /* No, so we get our own UIC to use as a rights identifier,
1313 * and the insert an ACE at the head of the ACL which allows us
1314 * to delete the file.
1316 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1317 fildsc.dsc$w_length = strlen(rspec);
1318 fildsc.dsc$a_pointer = rspec;
1320 newace.myace$l_ident = oldace.myace$l_ident;
1321 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1323 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1324 set_errno(ENOENT); break;
1326 set_errno(ENOTDIR); break;
1328 set_errno(ENODEV); break;
1329 case RMS$_SYN: case SS$_INVFILFOROP:
1330 set_errno(EINVAL); break;
1332 set_errno(EACCES); break;
1336 set_vaxc_errno(aclsts);
1340 /* Grab any existing ACEs with this identifier in case we fail */
1341 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1342 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1343 || fndsts == SS$_NOMOREACE ) {
1344 /* Add the new ACE . . . */
1345 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1348 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1350 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1351 Newx(remove_name, NAM$C_MAXRSS+1, char);
1352 do_pathify_dirspec(name, remove_name, 0);
1353 rmsts = rmdir(remove_name);
1354 Safefree(remove_name);
1357 rmsts = rmdir(remove_name);
1361 rmsts = remove(remove_name);
1363 /* We blew it - dir with files in it, no write priv for
1364 * parent directory, etc. Put things back the way they were. */
1365 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1368 addlst[0].bufadr = &oldace;
1369 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1376 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1377 /* We just deleted it, so of course it's not there. Some versions of
1378 * VMS seem to return success on the unlock operation anyhow (after all
1379 * the unlock is successful), but others don't.
1381 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1382 if (aclsts & 1) aclsts = fndsts;
1383 if (!(aclsts & 1)) {
1385 set_vaxc_errno(aclsts);
1393 } /* end of kill_file() */
1397 /*{{{int do_rmdir(char *name)*/
1399 Perl_do_rmdir(pTHX_ const char *name)
1401 char dirfile[NAM$C_MAXRSS+1];
1405 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1406 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1407 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1410 } /* end of do_rmdir */
1414 * Delete any file to which user has control access, regardless of whether
1415 * delete access is explicitly allowed.
1416 * Limitations: User must have write access to parent directory.
1417 * Does not block signals or ASTs; if interrupted in midstream
1418 * may leave file with an altered ACL.
1421 /*{{{int kill_file(char *name)*/
1423 Perl_kill_file(pTHX_ const char *name)
1425 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
1426 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1427 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1428 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1430 unsigned char myace$b_length;
1431 unsigned char myace$b_type;
1432 unsigned short int myace$w_flags;
1433 unsigned long int myace$l_access;
1434 unsigned long int myace$l_ident;
1435 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1436 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1437 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1439 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1440 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1441 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1442 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1443 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1444 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1446 /* Expand the input spec using RMS, since the CRTL remove() and
1447 * system services won't do this by themselves, so we may miss
1448 * a file "hiding" behind a logical name or search list. */
1449 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
1450 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
1451 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1452 /* If not, can changing protections help? */
1453 if (vaxc$errno != RMS$_PRV) return -1;
1455 /* No, so we get our own UIC to use as a rights identifier,
1456 * and the insert an ACE at the head of the ACL which allows us
1457 * to delete the file.
1459 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1460 fildsc.dsc$w_length = strlen(rspec);
1461 fildsc.dsc$a_pointer = rspec;
1463 newace.myace$l_ident = oldace.myace$l_ident;
1464 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1466 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1467 set_errno(ENOENT); break;
1469 set_errno(ENOTDIR); break;
1471 set_errno(ENODEV); break;
1472 case RMS$_SYN: case SS$_INVFILFOROP:
1473 set_errno(EINVAL); break;
1475 set_errno(EACCES); break;
1479 set_vaxc_errno(aclsts);
1482 /* Grab any existing ACEs with this identifier in case we fail */
1483 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1484 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1485 || fndsts == SS$_NOMOREACE ) {
1486 /* Add the new ACE . . . */
1487 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1489 if ((rmsts = remove(name))) {
1490 /* We blew it - dir with files in it, no write priv for
1491 * parent directory, etc. Put things back the way they were. */
1492 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1495 addlst[0].bufadr = &oldace;
1496 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1503 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1504 /* We just deleted it, so of course it's not there. Some versions of
1505 * VMS seem to return success on the unlock operation anyhow (after all
1506 * the unlock is successful), but others don't.
1508 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1509 if (aclsts & 1) aclsts = fndsts;
1510 if (!(aclsts & 1)) {
1512 set_vaxc_errno(aclsts);
1518 } /* end of kill_file() */
1522 /*{{{int my_mkdir(char *,Mode_t)*/
1524 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1526 STRLEN dirlen = strlen(dir);
1528 /* zero length string sometimes gives ACCVIO */
1529 if (dirlen == 0) return -1;
1531 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1532 * null file name/type. However, it's commonplace under Unix,
1533 * so we'll allow it for a gain in portability.
1535 if (dir[dirlen-1] == '/') {
1536 char *newdir = savepvn(dir,dirlen-1);
1537 int ret = mkdir(newdir,mode);
1541 else return mkdir(dir,mode);
1542 } /* end of my_mkdir */
1545 /*{{{int my_chdir(char *)*/
1547 Perl_my_chdir(pTHX_ const char *dir)
1549 STRLEN dirlen = strlen(dir);
1551 /* zero length string sometimes gives ACCVIO */
1552 if (dirlen == 0) return -1;
1555 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
1556 * This does not work if DECC$EFS_CHARSET is active. Hack it here
1557 * so that existing scripts do not need to be changed.
1560 while ((dirlen > 0) && (*dir1 == ' ')) {
1565 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1567 * null file name/type. However, it's commonplace under Unix,
1568 * so we'll allow it for a gain in portability.
1570 * - Preview- '/' will be valid soon on VMS
1572 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
1573 char *newdir = savepvn(dir,dirlen-1);
1574 int ret = chdir(newdir);
1578 else return chdir(dir);
1579 } /* end of my_chdir */
1583 /*{{{FILE *my_tmpfile()*/
1590 if ((fp = tmpfile())) return fp;
1592 Newx(cp,L_tmpnam+24,char);
1593 if (decc_filename_unix_only == 0)
1594 strcpy(cp,"Sys$Scratch:");
1597 tmpnam(cp+strlen(cp));
1598 strcat(cp,".Perltmp");
1599 fp = fopen(cp,"w+","fop=dlt");
1606 #ifndef HOMEGROWN_POSIX_SIGNALS
1608 * The C RTL's sigaction fails to check for invalid signal numbers so we
1609 * help it out a bit. The docs are correct, but the actual routine doesn't
1610 * do what the docs say it will.
1612 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1614 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1615 struct sigaction* oact)
1617 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1618 SETERRNO(EINVAL, SS$_INVARG);
1621 return sigaction(sig, act, oact);
1626 #ifdef KILL_BY_SIGPRC
1627 #include <errnodef.h>
1629 /* We implement our own kill() using the undocumented system service
1630 sys$sigprc for one of two reasons:
1632 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1633 target process to do a sys$exit, which usually can't be handled
1634 gracefully...certainly not by Perl and the %SIG{} mechanism.
1636 2.) If the kill() in the CRTL can't be called from a signal
1637 handler without disappearing into the ether, i.e., the signal
1638 it purportedly sends is never trapped. Still true as of VMS 7.3.
1640 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1641 in the target process rather than calling sys$exit.
1643 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1644 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1645 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1646 with condition codes C$_SIG0+nsig*8, catching the exception on the
1647 target process and resignaling with appropriate arguments.
1649 But we don't have that VMS 7.0+ exception handler, so if you
1650 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1652 Also note that SIGTERM is listed in the docs as being "unimplemented",
1653 yet always seems to be signaled with a VMS condition code of 4 (and
1654 correctly handled for that code). So we hardwire it in.
1656 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1657 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1658 than signalling with an unrecognized (and unhandled by CRTL) code.
1661 #define _MY_SIG_MAX 17
1664 Perl_sig_to_vmscondition(int sig)
1666 static unsigned int sig_code[_MY_SIG_MAX+1] =
1669 SS$_HANGUP, /* 1 SIGHUP */
1670 SS$_CONTROLC, /* 2 SIGINT */
1671 SS$_CONTROLY, /* 3 SIGQUIT */
1672 SS$_RADRMOD, /* 4 SIGILL */
1673 SS$_BREAK, /* 5 SIGTRAP */
1674 SS$_OPCCUS, /* 6 SIGABRT */
1675 SS$_COMPAT, /* 7 SIGEMT */
1677 SS$_FLTOVF, /* 8 SIGFPE VAX */
1679 SS$_HPARITH, /* 8 SIGFPE AXP */
1681 SS$_ABORT, /* 9 SIGKILL */
1682 SS$_ACCVIO, /* 10 SIGBUS */
1683 SS$_ACCVIO, /* 11 SIGSEGV */
1684 SS$_BADPARAM, /* 12 SIGSYS */
1685 SS$_NOMBX, /* 13 SIGPIPE */
1686 SS$_ASTFLT, /* 14 SIGALRM */
1692 #if __VMS_VER >= 60200000
1693 static int initted = 0;
1696 sig_code[16] = C$_SIGUSR1;
1697 sig_code[17] = C$_SIGUSR2;
1701 if (sig < _SIG_MIN) return 0;
1702 if (sig > _MY_SIG_MAX) return 0;
1703 return sig_code[sig];
1707 Perl_my_kill(int pid, int sig)
1712 int sys$sigprc(unsigned int *pidadr,
1713 struct dsc$descriptor_s *prcname,
1716 /* sig 0 means validate the PID */
1717 /*------------------------------*/
1719 const unsigned long int jpicode = JPI$_PID;
1722 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
1723 if ($VMS_STATUS_SUCCESS(status))
1726 case SS$_NOSUCHNODE:
1727 case SS$_UNREACHABLE:
1741 code = Perl_sig_to_vmscondition(sig);
1744 SETERRNO(EINVAL, SS$_BADPARAM);
1748 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
1749 * signals are to be sent to multiple processes.
1750 * pid = 0 - all processes in group except ones that the system exempts
1751 * pid = -1 - all processes except ones that the system exempts
1752 * pid = -n - all processes in group (abs(n)) except ...
1753 * For now, just report as not supported.
1757 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
1761 iss = sys$sigprc((unsigned int *)&pid,0,code);
1762 if (iss&1) return 0;
1766 set_errno(EPERM); break;
1768 case SS$_NOSUCHNODE:
1769 case SS$_UNREACHABLE:
1770 set_errno(ESRCH); break;
1772 set_errno(ENOMEM); break;
1777 set_vaxc_errno(iss);
1783 /* Routine to convert a VMS status code to a UNIX status code.
1784 ** More tricky than it appears because of conflicting conventions with
1787 ** VMS status codes are a bit mask, with the least significant bit set for
1790 ** Special UNIX status of EVMSERR indicates that no translation is currently
1791 ** available, and programs should check the VMS status code.
1793 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
1797 #ifndef C_FACILITY_NO
1798 #define C_FACILITY_NO 0x350000
1801 #define DCL_IVVERB 0x38090
1804 int Perl_vms_status_to_unix(int vms_status, int child_flag)
1812 /* Assume the best or the worst */
1813 if (vms_status & STS$M_SUCCESS)
1816 unix_status = EVMSERR;
1818 msg_status = vms_status & ~STS$M_CONTROL;
1820 facility = vms_status & STS$M_FAC_NO;
1821 fac_sp = vms_status & STS$M_FAC_SP;
1822 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
1824 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
1830 unix_status = EFAULT;
1832 case SS$_DEVOFFLINE:
1833 unix_status = EBUSY;
1836 unix_status = ENOTCONN;
1844 case SS$_INVFILFOROP:
1848 unix_status = EINVAL;
1850 case SS$_UNSUPPORTED:
1851 unix_status = ENOTSUP;
1856 unix_status = EACCES;
1858 case SS$_DEVICEFULL:
1859 unix_status = ENOSPC;
1862 unix_status = ENODEV;
1864 case SS$_NOSUCHFILE:
1865 case SS$_NOSUCHOBJECT:
1866 unix_status = ENOENT;
1868 case SS$_ABORT: /* Fatal case */
1869 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
1870 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
1871 unix_status = EINTR;
1874 unix_status = E2BIG;
1877 unix_status = ENOMEM;
1880 unix_status = EPERM;
1882 case SS$_NOSUCHNODE:
1883 case SS$_UNREACHABLE:
1884 unix_status = ESRCH;
1887 unix_status = ECHILD;
1890 if ((facility == 0) && (msg_no < 8)) {
1891 /* These are not real VMS status codes so assume that they are
1892 ** already UNIX status codes
1894 unix_status = msg_no;
1900 /* Translate a POSIX exit code to a UNIX exit code */
1901 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
1902 unix_status = (msg_no & 0x07F8) >> 3;
1906 /* Documented traditional behavior for handling VMS child exits */
1907 /*--------------------------------------------------------------*/
1908 if (child_flag != 0) {
1910 /* Success / Informational return 0 */
1911 /*----------------------------------*/
1912 if (msg_no & STS$K_SUCCESS)
1915 /* Warning returns 1 */
1916 /*-------------------*/
1917 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
1920 /* Everything else pass through the severity bits */
1921 /*------------------------------------------------*/
1922 return (msg_no & STS$M_SEVERITY);
1925 /* Normal VMS status to ERRNO mapping attempt */
1926 /*--------------------------------------------*/
1927 switch(msg_status) {
1928 /* case RMS$_EOF: */ /* End of File */
1929 case RMS$_FNF: /* File Not Found */
1930 case RMS$_DNF: /* Dir Not Found */
1931 unix_status = ENOENT;
1933 case RMS$_RNF: /* Record Not Found */
1934 unix_status = ESRCH;
1937 unix_status = ENOTDIR;
1940 unix_status = ENODEV;
1945 unix_status = EBADF;
1948 unix_status = EEXIST;
1952 case LIB$_INVSTRDES:
1954 case LIB$_NOSUCHSYM:
1955 case LIB$_INVSYMNAM:
1957 unix_status = EINVAL;
1963 unix_status = E2BIG;
1965 case RMS$_PRV: /* No privilege */
1966 case RMS$_ACC: /* ACP file access failed */
1967 case RMS$_WLK: /* Device write locked */
1968 unix_status = EACCES;
1970 /* case RMS$_NMF: */ /* No more files */
1978 /* Try to guess at what VMS error status should go with a UNIX errno
1979 * value. This is hard to do as there could be many possible VMS
1980 * error statuses that caused the errno value to be set.
1983 int Perl_unix_status_to_vms(int unix_status)
1985 int test_unix_status;
1987 /* Trivial cases first */
1988 /*---------------------*/
1989 if (unix_status == EVMSERR)
1992 /* Is vaxc$errno sane? */
1993 /*---------------------*/
1994 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
1995 if (test_unix_status == unix_status)
1998 /* If way out of range, must be VMS code already */
1999 /*-----------------------------------------------*/
2000 if (unix_status > EVMSERR)
2003 /* If out of range, punt */
2004 /*-----------------------*/
2005 if (unix_status > __ERRNO_MAX)
2009 /* Ok, now we have to do it the hard way. */
2010 /*----------------------------------------*/
2011 switch(unix_status) {
2012 case 0: return SS$_NORMAL;
2013 case EPERM: return SS$_NOPRIV;
2014 case ENOENT: return SS$_NOSUCHOBJECT;
2015 case ESRCH: return SS$_UNREACHABLE;
2016 case EINTR: return SS$_ABORT;
2019 case E2BIG: return SS$_BUFFEROVF;
2021 case EBADF: return RMS$_IFI;
2022 case ECHILD: return SS$_NONEXPR;
2024 case ENOMEM: return SS$_INSFMEM;
2025 case EACCES: return SS$_FILACCERR;
2026 case EFAULT: return SS$_ACCVIO;
2028 case EBUSY: return SS$_DEVOFFLINE;
2029 case EEXIST: return RMS$_FEX;
2031 case ENODEV: return SS$_NOSUCHDEV;
2032 case ENOTDIR: return RMS$_DIR;
2034 case EINVAL: return SS$_INVARG;
2040 case ENOSPC: return SS$_DEVICEFULL;
2041 case ESPIPE: return LIB$_INVARG;
2046 case ERANGE: return LIB$_INVARG;
2047 /* case EWOULDBLOCK */
2048 /* case EINPROGRESS */
2051 /* case EDESTADDRREQ */
2053 /* case EPROTOTYPE */
2054 /* case ENOPROTOOPT */
2055 /* case EPROTONOSUPPORT */
2056 /* case ESOCKTNOSUPPORT */
2057 /* case EOPNOTSUPP */
2058 /* case EPFNOSUPPORT */
2059 /* case EAFNOSUPPORT */
2060 /* case EADDRINUSE */
2061 /* case EADDRNOTAVAIL */
2063 /* case ENETUNREACH */
2064 /* case ENETRESET */
2065 /* case ECONNABORTED */
2066 /* case ECONNRESET */
2069 case ENOTCONN: return SS$_CLEARED;
2070 /* case ESHUTDOWN */
2071 /* case ETOOMANYREFS */
2072 /* case ETIMEDOUT */
2073 /* case ECONNREFUSED */
2075 /* case ENAMETOOLONG */
2076 /* case EHOSTDOWN */
2077 /* case EHOSTUNREACH */
2078 /* case ENOTEMPTY */
2090 /* case ECANCELED */
2094 return SS$_UNSUPPORTED;
2100 /* case EABANDONED */
2102 return SS$_ABORT; /* punt */
2105 return SS$_ABORT; /* Should not get here */
2109 /* default piping mailbox size */
2110 #define PERL_BUFSIZ 512
2114 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2116 unsigned long int mbxbufsiz;
2117 static unsigned long int syssize = 0;
2118 unsigned long int dviitm = DVI$_DEVNAM;
2119 char csize[LNM$C_NAMLENGTH+1];
2123 unsigned long syiitm = SYI$_MAXBUF;
2125 * Get the SYSGEN parameter MAXBUF
2127 * If the logical 'PERL_MBX_SIZE' is defined
2128 * use the value of the logical instead of PERL_BUFSIZ, but
2129 * keep the size between 128 and MAXBUF.
2132 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2135 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2136 mbxbufsiz = atoi(csize);
2138 mbxbufsiz = PERL_BUFSIZ;
2140 if (mbxbufsiz < 128) mbxbufsiz = 128;
2141 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2143 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2145 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2146 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2148 } /* end of create_mbx() */
2151 /*{{{ my_popen and my_pclose*/
2153 typedef struct _iosb IOSB;
2154 typedef struct _iosb* pIOSB;
2155 typedef struct _pipe Pipe;
2156 typedef struct _pipe* pPipe;
2157 typedef struct pipe_details Info;
2158 typedef struct pipe_details* pInfo;
2159 typedef struct _srqp RQE;
2160 typedef struct _srqp* pRQE;
2161 typedef struct _tochildbuf CBuf;
2162 typedef struct _tochildbuf* pCBuf;
2165 unsigned short status;
2166 unsigned short count;
2167 unsigned long dvispec;
2170 #pragma member_alignment save
2171 #pragma nomember_alignment quadword
2172 struct _srqp { /* VMS self-relative queue entry */
2173 unsigned long qptr[2];
2175 #pragma member_alignment restore
2176 static RQE RQE_ZERO = {0,0};
2178 struct _tochildbuf {
2181 unsigned short size;
2189 unsigned short chan_in;
2190 unsigned short chan_out;
2192 unsigned int bufsize;
2204 #if defined(PERL_IMPLICIT_CONTEXT)
2205 void *thx; /* Either a thread or an interpreter */
2206 /* pointer, depending on how we're built */
2214 PerlIO *fp; /* file pointer to pipe mailbox */
2215 int useFILE; /* using stdio, not perlio */
2216 int pid; /* PID of subprocess */
2217 int mode; /* == 'r' if pipe open for reading */
2218 int done; /* subprocess has completed */
2219 int waiting; /* waiting for completion/closure */
2220 int closing; /* my_pclose is closing this pipe */
2221 unsigned long completion; /* termination status of subprocess */
2222 pPipe in; /* pipe in to sub */
2223 pPipe out; /* pipe out of sub */
2224 pPipe err; /* pipe of sub's sys$error */
2225 int in_done; /* true when in pipe finished */
2230 struct exit_control_block
2232 struct exit_control_block *flink;
2233 unsigned long int (*exit_routine)();
2234 unsigned long int arg_count;
2235 unsigned long int *status_address;
2236 unsigned long int exit_status;
2239 typedef struct _closed_pipes Xpipe;
2240 typedef struct _closed_pipes* pXpipe;
2242 struct _closed_pipes {
2243 int pid; /* PID of subprocess */
2244 unsigned long completion; /* termination status of subprocess */
2246 #define NKEEPCLOSED 50
2247 static Xpipe closed_list[NKEEPCLOSED];
2248 static int closed_index = 0;
2249 static int closed_num = 0;
2251 #define RETRY_DELAY "0 ::0.20"
2252 #define MAX_RETRY 50
2254 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2255 static unsigned long mypid;
2256 static unsigned long delaytime[2];
2258 static pInfo open_pipes = NULL;
2259 static $DESCRIPTOR(nl_desc, "NL:");
2261 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2265 static unsigned long int
2266 pipe_exit_routine(pTHX)
2269 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2270 int sts, did_stuff, need_eof, j;
2273 flush any pending i/o
2279 PerlIO_flush(info->fp); /* first, flush data */
2281 fflush((FILE *)info->fp);
2287 next we try sending an EOF...ignore if doesn't work, make sure we
2295 _ckvmssts_noperl(sys$setast(0));
2296 if (info->in && !info->in->shut_on_empty) {
2297 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2302 _ckvmssts_noperl(sys$setast(1));
2306 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2308 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2313 _ckvmssts_noperl(sys$setast(0));
2314 if (info->waiting && info->done)
2316 nwait += info->waiting;
2317 _ckvmssts_noperl(sys$setast(1));
2327 _ckvmssts_noperl(sys$setast(0));
2328 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2329 sts = sys$forcex(&info->pid,0,&abort);
2330 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2333 _ckvmssts_noperl(sys$setast(1));
2337 /* again, wait for effect */
2339 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2344 _ckvmssts_noperl(sys$setast(0));
2345 if (info->waiting && info->done)
2347 nwait += info->waiting;
2348 _ckvmssts_noperl(sys$setast(1));
2357 _ckvmssts_noperl(sys$setast(0));
2358 if (!info->done) { /* We tried to be nice . . . */
2359 sts = sys$delprc(&info->pid,0);
2360 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2362 _ckvmssts_noperl(sys$setast(1));
2367 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2368 else if (!(sts & 1)) retsts = sts;
2373 static struct exit_control_block pipe_exitblock =
2374 {(struct exit_control_block *) 0,
2375 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2377 static void pipe_mbxtofd_ast(pPipe p);
2378 static void pipe_tochild1_ast(pPipe p);
2379 static void pipe_tochild2_ast(pPipe p);
2382 popen_completion_ast(pInfo info)
2384 pInfo i = open_pipes;
2389 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2390 closed_list[closed_index].pid = info->pid;
2391 closed_list[closed_index].completion = info->completion;
2393 if (closed_index == NKEEPCLOSED)
2398 if (i == info) break;
2401 if (!i) return; /* unlinked, probably freed too */
2406 Writing to subprocess ...
2407 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2409 chan_out may be waiting for "done" flag, or hung waiting
2410 for i/o completion to child...cancel the i/o. This will
2411 put it into "snarf mode" (done but no EOF yet) that discards
2414 Output from subprocess (stdout, stderr) needs to be flushed and
2415 shut down. We try sending an EOF, but if the mbx is full the pipe
2416 routine should still catch the "shut_on_empty" flag, telling it to
2417 use immediate-style reads so that "mbx empty" -> EOF.
2421 if (info->in && !info->in_done) { /* only for mode=w */
2422 if (info->in->shut_on_empty && info->in->need_wake) {
2423 info->in->need_wake = FALSE;
2424 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2426 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2430 if (info->out && !info->out_done) { /* were we also piping output? */
2431 info->out->shut_on_empty = TRUE;
2432 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2433 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2434 _ckvmssts_noperl(iss);
2437 if (info->err && !info->err_done) { /* we were piping stderr */
2438 info->err->shut_on_empty = TRUE;
2439 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2440 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2441 _ckvmssts_noperl(iss);
2443 _ckvmssts_noperl(sys$setef(pipe_ef));
2447 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2448 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2451 we actually differ from vmstrnenv since we use this to
2452 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2453 are pointing to the same thing
2456 static unsigned short
2457 popen_translate(pTHX_ char *logical, char *result)
2460 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2461 $DESCRIPTOR(d_log,"");
2463 unsigned short length;
2464 unsigned short code;
2466 unsigned short *retlenaddr;
2468 unsigned short l, ifi;
2470 d_log.dsc$a_pointer = logical;
2471 d_log.dsc$w_length = strlen(logical);
2473 itmlst[0].code = LNM$_STRING;
2474 itmlst[0].length = 255;
2475 itmlst[0].buffer_addr = result;
2476 itmlst[0].retlenaddr = &l;
2479 itmlst[1].length = 0;
2480 itmlst[1].buffer_addr = 0;
2481 itmlst[1].retlenaddr = 0;
2483 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
2484 if (iss == SS$_NOLOGNAM) {
2488 if (!(iss&1)) lib$signal(iss);
2491 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
2492 strip it off and return the ifi, if any
2495 if (result[0] == 0x1b && result[1] == 0x00) {
2496 memmove(&ifi,result+2,2);
2497 strcpy(result,result+4);
2499 return ifi; /* this is the RMS internal file id */
2502 static void pipe_infromchild_ast(pPipe p);
2505 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
2506 inside an AST routine without worrying about reentrancy and which Perl
2507 memory allocator is being used.
2509 We read data and queue up the buffers, then spit them out one at a
2510 time to the output mailbox when the output mailbox is ready for one.
2513 #define INITIAL_TOCHILDQUEUE 2
2516 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
2520 char mbx1[64], mbx2[64];
2521 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2522 DSC$K_CLASS_S, mbx1},
2523 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2524 DSC$K_CLASS_S, mbx2};
2525 unsigned int dviitm = DVI$_DEVBUFSIZ;
2529 _ckvmssts(lib$get_vm(&n, &p));
2531 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2532 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2533 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2536 p->shut_on_empty = FALSE;
2537 p->need_wake = FALSE;
2540 p->iosb.status = SS$_NORMAL;
2541 p->iosb2.status = SS$_NORMAL;
2547 #ifdef PERL_IMPLICIT_CONTEXT
2551 n = sizeof(CBuf) + p->bufsize;
2553 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
2554 _ckvmssts(lib$get_vm(&n, &b));
2555 b->buf = (char *) b + sizeof(CBuf);
2556 _ckvmssts(lib$insqhi(b, &p->free));
2559 pipe_tochild2_ast(p);
2560 pipe_tochild1_ast(p);
2566 /* reads the MBX Perl is writing, and queues */
2569 pipe_tochild1_ast(pPipe p)
2572 int iss = p->iosb.status;
2573 int eof = (iss == SS$_ENDOFFILE);
2575 #ifdef PERL_IMPLICIT_CONTEXT
2581 p->shut_on_empty = TRUE;
2583 _ckvmssts(sys$dassgn(p->chan_in));
2589 b->size = p->iosb.count;
2590 _ckvmssts(sts = lib$insqhi(b, &p->wait));
2592 p->need_wake = FALSE;
2593 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
2596 p->retry = 1; /* initial call */
2599 if (eof) { /* flush the free queue, return when done */
2600 int n = sizeof(CBuf) + p->bufsize;
2602 iss = lib$remqti(&p->free, &b);
2603 if (iss == LIB$_QUEWASEMP) return;
2605 _ckvmssts(lib$free_vm(&n, &b));
2609 iss = lib$remqti(&p->free, &b);
2610 if (iss == LIB$_QUEWASEMP) {
2611 int n = sizeof(CBuf) + p->bufsize;
2612 _ckvmssts(lib$get_vm(&n, &b));
2613 b->buf = (char *) b + sizeof(CBuf);
2619 iss = sys$qio(0,p->chan_in,
2620 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2622 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2623 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2628 /* writes queued buffers to output, waits for each to complete before
2632 pipe_tochild2_ast(pPipe p)
2635 int iss = p->iosb2.status;
2636 int n = sizeof(CBuf) + p->bufsize;
2637 int done = (p->info && p->info->done) ||
2638 iss == SS$_CANCEL || iss == SS$_ABORT;
2639 #if defined(PERL_IMPLICIT_CONTEXT)
2644 if (p->type) { /* type=1 has old buffer, dispose */
2645 if (p->shut_on_empty) {
2646 _ckvmssts(lib$free_vm(&n, &b));
2648 _ckvmssts(lib$insqhi(b, &p->free));
2653 iss = lib$remqti(&p->wait, &b);
2654 if (iss == LIB$_QUEWASEMP) {
2655 if (p->shut_on_empty) {
2657 _ckvmssts(sys$dassgn(p->chan_out));
2658 *p->pipe_done = TRUE;
2659 _ckvmssts(sys$setef(pipe_ef));
2661 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2662 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2666 p->need_wake = TRUE;
2676 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2677 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2679 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2680 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2689 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2692 char mbx1[64], mbx2[64];
2693 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2694 DSC$K_CLASS_S, mbx1},
2695 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2696 DSC$K_CLASS_S, mbx2};
2697 unsigned int dviitm = DVI$_DEVBUFSIZ;
2699 int n = sizeof(Pipe);
2700 _ckvmssts(lib$get_vm(&n, &p));
2701 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2702 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2704 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2705 n = p->bufsize * sizeof(char);
2706 _ckvmssts(lib$get_vm(&n, &p->buf));
2707 p->shut_on_empty = FALSE;
2710 p->iosb.status = SS$_NORMAL;
2711 #if defined(PERL_IMPLICIT_CONTEXT)
2714 pipe_infromchild_ast(p);
2722 pipe_infromchild_ast(pPipe p)
2724 int iss = p->iosb.status;
2725 int eof = (iss == SS$_ENDOFFILE);
2726 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
2727 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
2728 #if defined(PERL_IMPLICIT_CONTEXT)
2732 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
2733 _ckvmssts(sys$dassgn(p->chan_out));
2738 input shutdown if EOF from self (done or shut_on_empty)
2739 output shutdown if closing flag set (my_pclose)
2740 send data/eof from child or eof from self
2741 otherwise, re-read (snarf of data from child)
2746 if (myeof && p->chan_in) { /* input shutdown */
2747 _ckvmssts(sys$dassgn(p->chan_in));
2752 if (myeof || kideof) { /* pass EOF to parent */
2753 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
2754 pipe_infromchild_ast, p,
2757 } else if (eof) { /* eat EOF --- fall through to read*/
2759 } else { /* transmit data */
2760 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
2761 pipe_infromchild_ast,p,
2762 p->buf, p->iosb.count, 0, 0, 0, 0));
2768 /* everything shut? flag as done */
2770 if (!p->chan_in && !p->chan_out) {
2771 *p->pipe_done = TRUE;
2772 _ckvmssts(sys$setef(pipe_ef));
2776 /* write completed (or read, if snarfing from child)
2777 if still have input active,
2778 queue read...immediate mode if shut_on_empty so we get EOF if empty
2780 check if Perl reading, generate EOFs as needed
2786 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
2787 pipe_infromchild_ast,p,
2788 p->buf, p->bufsize, 0, 0, 0, 0);
2789 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
2791 } else { /* send EOFs for extra reads */
2792 p->iosb.status = SS$_ENDOFFILE;
2793 p->iosb.dvispec = 0;
2794 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
2796 pipe_infromchild_ast, p, 0, 0, 0, 0));
2802 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
2806 unsigned long dviitm = DVI$_DEVBUFSIZ;
2808 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
2809 DSC$K_CLASS_S, mbx};
2811 /* things like terminals and mbx's don't need this filter */
2812 if (fd && fstat(fd,&s) == 0) {
2813 unsigned long dviitm = DVI$_DEVCHAR, devchar;
2814 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
2815 DSC$K_CLASS_S, s.st_dev};
2817 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
2818 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
2819 strcpy(out, s.st_dev);
2824 int n = sizeof(Pipe);
2825 _ckvmssts(lib$get_vm(&n, &p));
2826 p->fd_out = dup(fd);
2827 create_mbx(aTHX_ &p->chan_in, &d_mbx);
2828 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2829 n = (p->bufsize+1) * sizeof(char);
2830 _ckvmssts(lib$get_vm(&n, &p->buf));
2831 p->shut_on_empty = FALSE;
2836 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
2837 pipe_mbxtofd_ast, p,
2838 p->buf, p->bufsize, 0, 0, 0, 0));
2844 pipe_mbxtofd_ast(pPipe p)
2846 int iss = p->iosb.status;
2847 int done = p->info->done;
2849 int eof = (iss == SS$_ENDOFFILE);
2850 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
2851 int err = !(iss&1) && !eof;
2852 #if defined(PERL_IMPLICIT_CONTEXT)
2856 if (done && myeof) { /* end piping */
2858 sys$dassgn(p->chan_in);
2859 *p->pipe_done = TRUE;
2860 _ckvmssts(sys$setef(pipe_ef));
2864 if (!err && !eof) { /* good data to send to file */
2865 p->buf[p->iosb.count] = '\n';
2866 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
2869 if (p->retry < MAX_RETRY) {
2870 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
2880 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
2881 pipe_mbxtofd_ast, p,
2882 p->buf, p->bufsize, 0, 0, 0, 0);
2883 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
2888 typedef struct _pipeloc PLOC;
2889 typedef struct _pipeloc* pPLOC;
2893 char dir[NAM$C_MAXRSS+1];
2895 static pPLOC head_PLOC = 0;
2898 free_pipelocs(pTHX_ void *head)
2901 pPLOC *pHead = (pPLOC *)head;
2913 store_pipelocs(pTHX)
2922 char temp[NAM$C_MAXRSS+1];
2926 free_pipelocs(aTHX_ &head_PLOC);
2928 /* the . directory from @INC comes last */
2930 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2931 p->next = head_PLOC;
2933 strcpy(p->dir,"./");
2935 /* get the directory from $^X */
2937 #ifdef PERL_IMPLICIT_CONTEXT
2938 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2940 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2942 strcpy(temp, PL_origargv[0]);
2943 x = strrchr(temp,']');
2945 x = strrchr(temp,'>');
2947 /* It could be a UNIX path */
2948 x = strrchr(temp,'/');
2954 /* Got a bare name, so use default directory */
2959 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2960 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2961 p->next = head_PLOC;
2963 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2964 p->dir[NAM$C_MAXRSS] = '\0';
2968 /* reverse order of @INC entries, skip "." since entered above */
2970 #ifdef PERL_IMPLICIT_CONTEXT
2973 if (PL_incgv) av = GvAVn(PL_incgv);
2975 for (i = 0; av && i <= AvFILL(av); i++) {
2976 dirsv = *av_fetch(av,i,TRUE);
2978 if (SvROK(dirsv)) continue;
2979 dir = SvPVx(dirsv,n_a);
2980 if (strcmp(dir,".") == 0) continue;
2981 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2984 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2985 p->next = head_PLOC;
2987 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2988 p->dir[NAM$C_MAXRSS] = '\0';
2991 /* most likely spot (ARCHLIB) put first in the list */
2994 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
2995 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
2996 p->next = head_PLOC;
2998 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2999 p->dir[NAM$C_MAXRSS] = '\0';
3008 static int vmspipe_file_status = 0;
3009 static char vmspipe_file[NAM$C_MAXRSS+1];
3011 /* already found? Check and use ... need read+execute permission */
3013 if (vmspipe_file_status == 1) {
3014 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3015 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3016 return vmspipe_file;
3018 vmspipe_file_status = 0;
3021 /* scan through stored @INC, $^X */
3023 if (vmspipe_file_status == 0) {
3024 char file[NAM$C_MAXRSS+1];
3025 pPLOC p = head_PLOC;
3028 strcpy(file, p->dir);
3029 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
3030 file[NAM$C_MAXRSS] = '\0';
3033 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
3035 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3036 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3037 vmspipe_file_status = 1;
3038 return vmspipe_file;
3041 vmspipe_file_status = -1; /* failed, use tempfiles */
3048 vmspipe_tempfile(pTHX)
3050 char file[NAM$C_MAXRSS+1];
3052 static int index = 0;
3056 /* create a tempfile */
3058 /* we can't go from W, shr=get to R, shr=get without
3059 an intermediate vulnerable state, so don't bother trying...
3061 and lib$spawn doesn't shr=put, so have to close the write
3063 So... match up the creation date/time and the FID to
3064 make sure we're dealing with the same file
3069 if (!decc_filename_unix_only) {
3070 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3071 fp = fopen(file,"w");
3073 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3074 fp = fopen(file,"w");
3076 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3077 fp = fopen(file,"w");
3082 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3083 fp = fopen(file,"w");
3085 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3086 fp = fopen(file,"w");
3088 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3089 fp = fopen(file,"w");
3093 if (!fp) return 0; /* we're hosed */
3095 fprintf(fp,"$! 'f$verify(0)'\n");
3096 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3097 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3098 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3099 fprintf(fp,"$ perl_on = \"set noon\"\n");
3100 fprintf(fp,"$ perl_exit = \"exit\"\n");
3101 fprintf(fp,"$ perl_del = \"delete\"\n");
3102 fprintf(fp,"$ pif = \"if\"\n");
3103 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3104 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3105 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3106 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3107 fprintf(fp,"$! --- build command line to get max possible length\n");
3108 fprintf(fp,"$c=perl_popen_cmd0\n");
3109 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3110 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3111 fprintf(fp,"$x=perl_popen_cmd3\n");
3112 fprintf(fp,"$c=c+x\n");
3113 fprintf(fp,"$ perl_on\n");
3114 fprintf(fp,"$ 'c'\n");
3115 fprintf(fp,"$ perl_status = $STATUS\n");
3116 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3117 fprintf(fp,"$ perl_exit 'perl_status'\n");
3120 fgetname(fp, file, 1);
3121 fstat(fileno(fp), (struct stat *)&s0);
3124 if (decc_filename_unix_only)
3125 do_tounixspec(file, file, 0);
3126 fp = fopen(file,"r","shr=get");
3128 fstat(fileno(fp), (struct stat *)&s1);
3130 #if defined(_USE_STD_STAT)
3131 cmp_result = s0.crtl_stat.st_ino != s1.crtl_stat.st_ino;
3133 cmp_result = memcmp(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino, 6);
3135 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3146 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3148 static int handler_set_up = FALSE;
3149 unsigned long int sts, flags = CLI$M_NOWAIT;
3150 /* The use of a GLOBAL table (as was done previously) rendered
3151 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3152 * environment. Hence we've switched to LOCAL symbol table.
3154 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3156 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3157 char in[512], out[512], err[512], mbx[512];
3159 char tfilebuf[NAM$C_MAXRSS+1];
3161 char cmd_sym_name[20];
3162 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3163 DSC$K_CLASS_S, symbol};
3164 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3166 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3167 DSC$K_CLASS_S, cmd_sym_name};
3168 struct dsc$descriptor_s *vmscmd;
3169 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3170 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3171 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3173 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3175 /* once-per-program initialization...
3176 note that the SETAST calls and the dual test of pipe_ef
3177 makes sure that only the FIRST thread through here does
3178 the initialization...all other threads wait until it's
3181 Yeah, uglier than a pthread call, it's got all the stuff inline
3182 rather than in a separate routine.
3186 _ckvmssts(sys$setast(0));
3188 unsigned long int pidcode = JPI$_PID;
3189 $DESCRIPTOR(d_delay, RETRY_DELAY);
3190 _ckvmssts(lib$get_ef(&pipe_ef));
3191 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3192 _ckvmssts(sys$bintim(&d_delay, delaytime));
3194 if (!handler_set_up) {
3195 _ckvmssts(sys$dclexh(&pipe_exitblock));
3196 handler_set_up = TRUE;
3198 _ckvmssts(sys$setast(1));
3201 /* see if we can find a VMSPIPE.COM */
3204 vmspipe = find_vmspipe(aTHX);
3206 strcpy(tfilebuf+1,vmspipe);
3207 } else { /* uh, oh...we're in tempfile hell */
3208 tpipe = vmspipe_tempfile(aTHX);
3209 if (!tpipe) { /* a fish popular in Boston */
3210 if (ckWARN(WARN_PIPE)) {
3211 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3215 fgetname(tpipe,tfilebuf+1,1);
3217 vmspipedsc.dsc$a_pointer = tfilebuf;
3218 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3220 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3223 case RMS$_FNF: case RMS$_DNF:
3224 set_errno(ENOENT); break;
3226 set_errno(ENOTDIR); break;
3228 set_errno(ENODEV); break;
3230 set_errno(EACCES); break;
3232 set_errno(EINVAL); break;
3233 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3234 set_errno(E2BIG); break;
3235 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3236 _ckvmssts(sts); /* fall through */
3237 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3240 set_vaxc_errno(sts);
3241 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3242 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3248 _ckvmssts(lib$get_vm(&n, &info));
3250 strcpy(mode,in_mode);
3253 info->completion = 0;
3254 info->closing = FALSE;
3261 info->in_done = TRUE;
3262 info->out_done = TRUE;
3263 info->err_done = TRUE;
3264 in[0] = out[0] = err[0] = '\0';
3266 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3270 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3275 if (*mode == 'r') { /* piping from subroutine */
3277 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3279 info->out->pipe_done = &info->out_done;
3280 info->out_done = FALSE;
3281 info->out->info = info;
3283 if (!info->useFILE) {
3284 info->fp = PerlIO_open(mbx, mode);
3286 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3287 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3290 if (!info->fp && info->out) {
3291 sys$cancel(info->out->chan_out);
3293 while (!info->out_done) {
3295 _ckvmssts(sys$setast(0));
3296 done = info->out_done;
3297 if (!done) _ckvmssts(sys$clref(pipe_ef));
3298 _ckvmssts(sys$setast(1));
3299 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3302 if (info->out->buf) {
3303 n = info->out->bufsize * sizeof(char);
3304 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3307 _ckvmssts(lib$free_vm(&n, &info->out));
3309 _ckvmssts(lib$free_vm(&n, &info));
3314 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3316 info->err->pipe_done = &info->err_done;
3317 info->err_done = FALSE;
3318 info->err->info = info;
3321 } else if (*mode == 'w') { /* piping to subroutine */
3323 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3325 info->out->pipe_done = &info->out_done;
3326 info->out_done = FALSE;
3327 info->out->info = info;
3330 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3332 info->err->pipe_done = &info->err_done;
3333 info->err_done = FALSE;
3334 info->err->info = info;
3337 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3338 if (!info->useFILE) {
3339 info->fp = PerlIO_open(mbx, mode);
3341 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3342 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3346 info->in->pipe_done = &info->in_done;
3347 info->in_done = FALSE;
3348 info->in->info = info;
3352 if (!info->fp && info->in) {
3354 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3355 0, 0, 0, 0, 0, 0, 0, 0));
3357 while (!info->in_done) {
3359 _ckvmssts(sys$setast(0));
3360 done = info->in_done;
3361 if (!done) _ckvmssts(sys$clref(pipe_ef));
3362 _ckvmssts(sys$setast(1));
3363 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3366 if (info->in->buf) {
3367 n = info->in->bufsize * sizeof(char);
3368 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3371 _ckvmssts(lib$free_vm(&n, &info->in));
3373 _ckvmssts(lib$free_vm(&n, &info));
3379 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3380 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3382 info->out->pipe_done = &info->out_done;
3383 info->out_done = FALSE;
3384 info->out->info = info;
3387 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3389 info->err->pipe_done = &info->err_done;
3390 info->err_done = FALSE;
3391 info->err->info = info;
3395 symbol[MAX_DCL_SYMBOL] = '\0';
3397 strncpy(symbol, in, MAX_DCL_SYMBOL);
3398 d_symbol.dsc$w_length = strlen(symbol);
3399 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3401 strncpy(symbol, err, MAX_DCL_SYMBOL);
3402 d_symbol.dsc$w_length = strlen(symbol);
3403 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3405 strncpy(symbol, out, MAX_DCL_SYMBOL);
3406 d_symbol.dsc$w_length = strlen(symbol);
3407 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3409 p = vmscmd->dsc$a_pointer;
3410 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3411 if (*p == '$') p++; /* remove leading $ */
3412 while (*p == ' ' || *p == '\t') p++;
3414 for (j = 0; j < 4; j++) {
3415 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3416 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3418 strncpy(symbol, p, MAX_DCL_SYMBOL);
3419 d_symbol.dsc$w_length = strlen(symbol);
3420 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3422 if (strlen(p) > MAX_DCL_SYMBOL) {
3423 p += MAX_DCL_SYMBOL;
3428 _ckvmssts(sys$setast(0));
3429 info->next=open_pipes; /* prepend to list */
3431 _ckvmssts(sys$setast(1));
3432 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
3433 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
3434 * have SYS$COMMAND if we need it.
3436 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
3437 0, &info->pid, &info->completion,
3438 0, popen_completion_ast,info,0,0,0));
3440 /* if we were using a tempfile, close it now */
3442 if (tpipe) fclose(tpipe);
3444 /* once the subprocess is spawned, it has copied the symbols and
3445 we can get rid of ours */
3447 for (j = 0; j < 4; j++) {
3448 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3449 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3450 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
3452 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
3453 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
3454 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
3455 vms_execfree(vmscmd);
3457 #ifdef PERL_IMPLICIT_CONTEXT
3460 PL_forkprocess = info->pid;
3465 _ckvmssts(sys$setast(0));
3467 if (!done) _ckvmssts(sys$clref(pipe_ef));
3468 _ckvmssts(sys$setast(1));
3469 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3471 *psts = info->completion;
3472 /* Caller thinks it is open and tries to close it. */
3473 /* This causes some problems, as it changes the error status */
3474 /* my_pclose(info->fp); */
3479 } /* end of safe_popen */
3482 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
3484 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
3488 TAINT_PROPER("popen");
3489 PERL_FLUSHALL_FOR_CHILD;
3490 return safe_popen(aTHX_ cmd,mode,&sts);
3495 /*{{{ I32 my_pclose(PerlIO *fp)*/
3496 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
3498 pInfo info, last = NULL;
3499 unsigned long int retsts;
3502 for (info = open_pipes; info != NULL; last = info, info = info->next)
3503 if (info->fp == fp) break;
3505 if (info == NULL) { /* no such pipe open */
3506 set_errno(ECHILD); /* quoth POSIX */
3507 set_vaxc_errno(SS$_NONEXPR);
3511 /* If we were writing to a subprocess, insure that someone reading from
3512 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
3513 * produce an EOF record in the mailbox.
3515 * well, at least sometimes it *does*, so we have to watch out for
3516 * the first EOF closing the pipe (and DASSGN'ing the channel)...
3520 PerlIO_flush(info->fp); /* first, flush data */
3522 fflush((FILE *)info->fp);
3525 _ckvmssts(sys$setast(0));
3526 info->closing = TRUE;
3527 done = info->done && info->in_done && info->out_done && info->err_done;
3528 /* hanging on write to Perl's input? cancel it */
3529 if (info->mode == 'r' && info->out && !info->out_done) {
3530 if (info->out->chan_out) {
3531 _ckvmssts(sys$cancel(info->out->chan_out));
3532 if (!info->out->chan_in) { /* EOF generation, need AST */
3533 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
3537 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
3538 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3540 _ckvmssts(sys$setast(1));
3543 PerlIO_close(info->fp);
3545 fclose((FILE *)info->fp);
3548 we have to wait until subprocess completes, but ALSO wait until all
3549 the i/o completes...otherwise we'll be freeing the "info" structure
3550 that the i/o ASTs could still be using...
3554 _ckvmssts(sys$setast(0));
3555 done = info->done && info->in_done && info->out_done && info->err_done;
3556 if (!done) _ckvmssts(sys$clref(pipe_ef));
3557 _ckvmssts(sys$setast(1));
3558 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3560 retsts = info->completion;
3562 /* remove from list of open pipes */
3563 _ckvmssts(sys$setast(0));
3564 if (last) last->next = info->next;
3565 else open_pipes = info->next;
3566 _ckvmssts(sys$setast(1));
3568 /* free buffers and structures */
3571 if (info->in->buf) {
3572 n = info->in->bufsize * sizeof(char);
3573 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3576 _ckvmssts(lib$free_vm(&n, &info->in));
3579 if (info->out->buf) {
3580 n = info->out->bufsize * sizeof(char);
3581 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3584 _ckvmssts(lib$free_vm(&n, &info->out));
3587 if (info->err->buf) {
3588 n = info->err->bufsize * sizeof(char);
3589 _ckvmssts(lib$free_vm(&n, &info->err->buf));
3592 _ckvmssts(lib$free_vm(&n, &info->err));
3595 _ckvmssts(lib$free_vm(&n, &info));
3599 } /* end of my_pclose() */
3601 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3602 /* Roll our own prototype because we want this regardless of whether
3603 * _VMS_WAIT is defined.
3605 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
3607 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
3608 created with popen(); otherwise partially emulate waitpid() unless
3609 we have a suitable one from the CRTL that came with VMS 7.2 and later.
3610 Also check processes not considered by the CRTL waitpid().
3612 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
3614 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
3621 if (statusp) *statusp = 0;
3623 for (info = open_pipes; info != NULL; info = info->next)
3624 if (info->pid == pid) break;
3626 if (info != NULL) { /* we know about this child */
3627 while (!info->done) {
3628 _ckvmssts(sys$setast(0));
3630 if (!done) _ckvmssts(sys$clref(pipe_ef));
3631 _ckvmssts(sys$setast(1));
3632 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3635 if (statusp) *statusp = info->completion;
3639 /* child that already terminated? */
3641 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
3642 if (closed_list[j].pid == pid) {
3643 if (statusp) *statusp = closed_list[j].completion;
3648 /* fall through if this child is not one of our own pipe children */
3650 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3652 /* waitpid() became available in the CRTL as of VMS 7.0, but only
3653 * in 7.2 did we get a version that fills in the VMS completion
3654 * status as Perl has always tried to do.
3657 sts = __vms_waitpid( pid, statusp, flags );
3659 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
3662 /* If the real waitpid tells us the child does not exist, we
3663 * fall through here to implement waiting for a child that
3664 * was created by some means other than exec() (say, spawned
3665 * from DCL) or to wait for a process that is not a subprocess
3666 * of the current process.
3669 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3672 $DESCRIPTOR(intdsc,"0 00:00:01");
3673 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3674 unsigned long int pidcode = JPI$_PID, mypid;
3675 unsigned long int interval[2];
3676 unsigned int jpi_iosb[2];
3677 struct itmlst_3 jpilist[2] = {
3678 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3683 /* Sorry folks, we don't presently implement rooting around for
3684 the first child we can find, and we definitely don't want to
3685 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3691 /* Get the owner of the child so I can warn if it's not mine. If the
3692 * process doesn't exist or I don't have the privs to look at it,
3693 * I can go home early.
3695 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3696 if (sts & 1) sts = jpi_iosb[0];
3708 set_vaxc_errno(sts);
3712 if (ckWARN(WARN_EXEC)) {
3713 /* remind folks they are asking for non-standard waitpid behavior */
3714 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3715 if (ownerpid != mypid)
3716 Perl_warner(aTHX_ packWARN(WARN_EXEC),
3717 "waitpid: process %x is not a child of process %x",
3721 /* simply check on it once a second until it's not there anymore. */
3723 _ckvmssts(sys$bintim(&intdsc,interval));
3724 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
3725 _ckvmssts(sys$schdwk(0,0,interval,0));
3726 _ckvmssts(sys$hiber());
3728 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
3733 } /* end of waitpid() */
3738 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
3740 my_gconvert(double val, int ndig, int trail, char *buf)
3742 static char __gcvtbuf[DBL_DIG+1];
3745 loc = buf ? buf : __gcvtbuf;
3747 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
3749 sprintf(loc,"%.*g",ndig,val);
3755 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
3756 return gcvt(val,ndig,loc);
3759 loc[0] = '0'; loc[1] = '\0';
3767 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
3768 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
3769 * to expand file specification. Allows for a single default file
3770 * specification and a simple mask of options. If outbuf is non-NULL,
3771 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
3772 * the resultant file specification is placed. If outbuf is NULL, the
3773 * resultant file specification is placed into a static buffer.
3774 * The third argument, if non-NULL, is taken to be a default file
3775 * specification string. The fourth argument is unused at present.
3776 * rmesexpand() returns the address of the resultant string if
3777 * successful, and NULL on error.
3779 * New functionality for previously unused opts value:
3780 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
3782 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
3784 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
3785 /* ODS-2 only version */
3787 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3789 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
3790 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
3791 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
3792 struct FAB myfab = cc$rms_fab;
3793 struct NAM mynam = cc$rms_nam;
3795 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3798 if (!filespec || !*filespec) {
3799 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3803 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
3804 else outbuf = __rmsexpand_retbuf;
3806 isunix = is_unix_filespec(filespec);
3808 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3813 filespec = vmsfspec;
3816 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
3817 myfab.fab$b_fns = strlen(filespec);
3818 myfab.fab$l_nam = &mynam;
3820 if (defspec && *defspec) {
3821 if (strchr(defspec,'/') != NULL) {
3822 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
3829 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
3830 myfab.fab$b_dns = strlen(defspec);
3833 mynam.nam$l_esa = esa;
3834 mynam.nam$b_ess = sizeof esa;
3835 mynam.nam$l_rsa = outbuf;
3836 mynam.nam$b_rss = NAM$C_MAXRSS;
3838 #ifdef NAM$M_NO_SHORT_UPCASE
3839 if (decc_efs_case_preserve)
3840 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3843 retsts = sys$parse(&myfab,0,0);
3844 if (!(retsts & 1)) {
3845 mynam.nam$b_nop |= NAM$M_SYNCHK;
3846 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
3847 retsts = sys$parse(&myfab,0,0);
3848 if (retsts & 1) goto expanded;
3850 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
3851 sts = sys$parse(&myfab,0,0); /* Free search context */
3852 if (out) Safefree(out);
3853 set_vaxc_errno(retsts);
3854 if (retsts == RMS$_PRV) set_errno(EACCES);
3855 else if (retsts == RMS$_DEV) set_errno(ENODEV);
3856 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3857 else set_errno(EVMSERR);
3860 retsts = sys$search(&myfab,0,0);
3861 if (!(retsts & 1) && retsts != RMS$_FNF) {
3862 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3863 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3864 if (out) Safefree(out);
3865 set_vaxc_errno(retsts);
3866 if (retsts == RMS$_PRV) set_errno(EACCES);
3867 else set_errno(EVMSERR);
3871 /* If the input filespec contained any lowercase characters,
3872 * downcase the result for compatibility with Unix-minded code. */
3874 if (!decc_efs_case_preserve) {
3875 for (out = myfab.fab$l_fna; *out; out++)
3876 if (islower(*out)) { haslower = 1; break; }
3878 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
3879 else { out = esa; speclen = mynam.nam$b_esl; }
3880 /* Trim off null fields added by $PARSE
3881 * If type > 1 char, must have been specified in original or default spec
3882 * (not true for version; $SEARCH may have added version of existing file).
3884 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
3885 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
3886 (mynam.nam$l_ver - mynam.nam$l_type == 1);
3887 if (trimver || trimtype) {
3888 if (defspec && *defspec) {
3889 char defesa[NAM$C_MAXRSS];
3890 struct FAB deffab = cc$rms_fab;
3891 struct NAM defnam = cc$rms_nam;
3893 deffab.fab$l_nam = &defnam;
3894 /* cast below ok for read only pointer */
3895 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
3896 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
3897 defnam.nam$b_nop = NAM$M_SYNCHK;
3898 #ifdef NAM$M_NO_SHORT_UPCASE
3899 if (decc_efs_case_preserve)
3900 defnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3902 if (sys$parse(&deffab,0,0) & 1) {
3903 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
3904 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
3908 if (*mynam.nam$l_ver != '\"')
3909 speclen = mynam.nam$l_ver - out;
3912 /* If we didn't already trim version, copy down */
3913 if (speclen > mynam.nam$l_ver - out)
3914 memmove(mynam.nam$l_type, mynam.nam$l_ver,
3915 speclen - (mynam.nam$l_ver - out));
3916 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
3919 /* If we just had a directory spec on input, $PARSE "helpfully"
3920 * adds an empty name and type for us */
3921 if (mynam.nam$l_name == mynam.nam$l_type &&
3922 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
3923 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
3924 speclen = mynam.nam$l_name - out;
3926 /* Posix format specifications must have matching quotes */
3927 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
3928 if ((speclen > 1) && (out[speclen-1] != '\"')) {
3929 out[speclen] = '\"';
3934 out[speclen] = '\0';
3935 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
3937 /* Have we been working with an expanded, but not resultant, spec? */
3938 /* Also, convert back to Unix syntax if necessary. */
3939 if ((opts & PERL_RMSEXPAND_M_VMS) != 0)
3942 if (!mynam.nam$b_rsl) {
3944 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
3946 else strcpy(outbuf,esa);
3949 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
3950 strcpy(outbuf,tmpfspec);
3952 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3953 mynam.nam$l_rsa = NULL;
3954 mynam.nam$b_rss = 0;
3955 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3959 /* ODS-5 supporting routine */
3961 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3963 static char __rmsexpand_retbuf[NAML$C_MAXRSS+1];
3964 char * vmsfspec, *tmpfspec;
3965 char * esa, *cp, *out = NULL;
3968 struct FAB myfab = cc$rms_fab;
3969 struct NAML mynam = cc$rms_naml;
3971 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3974 if (!filespec || !*filespec) {
3975 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3979 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
3980 else outbuf = __rmsexpand_retbuf;
3986 isunix = is_unix_filespec(filespec);
3988 Newx(vmsfspec, VMS_MAXRSS, char);
3989 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3995 filespec = vmsfspec;
3997 /* Unless we are forcing to VMS format, a UNIX input means
3998 * UNIX output, and that requires long names to be used
4000 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4001 opts |= PERL_RMSEXPAND_M_LONG;
4007 myfab.fab$l_fna = (char *)-1; /* cast ok */
4008 myfab.fab$b_fns = 0;
4009 mynam.naml$l_long_filename = (char *)filespec; /* cast ok */
4010 mynam.naml$l_long_filename_size = strlen(filespec);
4011 myfab.fab$l_naml = &mynam;
4013 if (defspec && *defspec) {
4015 t_isunix = is_unix_filespec(defspec);
4017 Newx(tmpfspec, VMS_MAXRSS, char);
4018 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
4020 if (vmsfspec != NULL)
4028 myfab.fab$l_dna = (char *) -1; /* cast ok */
4029 myfab.fab$b_dns = 0;
4030 mynam.naml$l_long_defname = (char *)defspec; /* cast ok */
4031 mynam.naml$l_long_defname_size = strlen(defspec);
4034 Newx(esa, NAM$C_MAXRSS + 1, char);
4035 Newx(esal, NAML$C_MAXRSS + 1, char);
4036 mynam.naml$l_esa = esa;
4037 mynam.naml$b_ess = NAM$C_MAXRSS;
4038 mynam.naml$l_long_expand = esal;
4039 mynam.naml$l_long_expand_alloc = NAML$C_MAXRSS;
4041 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4042 mynam.naml$l_rsa = NULL;
4043 mynam.naml$b_rss = 0;
4044 mynam.naml$l_long_result = outbuf;
4045 mynam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
4048 mynam.naml$l_rsa = outbuf;
4049 mynam.naml$b_rss = NAM$C_MAXRSS;
4050 Newx(outbufl, VMS_MAXRSS, char);
4051 mynam.naml$l_long_result = outbufl;
4052 mynam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
4055 #ifdef NAM$M_NO_SHORT_UPCASE
4056 if (decc_efs_case_preserve)
4057 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
4060 /* First attempt to parse as an existing file */
4061 retsts = sys$parse(&myfab,0,0);
4062 if (!(retsts & STS$K_SUCCESS)) {
4064 /* Could not find the file, try as syntax only if error is not fatal */
4065 mynam.naml$b_nop |= NAM$M_SYNCHK;
4066 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4067 retsts = sys$parse(&myfab,0,0);
4068 if (retsts & STS$K_SUCCESS) goto expanded;
4071 /* Still could not parse the file specification */
4072 /*----------------------------------------------*/
4073 mynam.naml$l_rlf = NULL;
4074 myfab.fab$b_dns = 0;
4075 mynam.naml$l_long_defname_size = 0;
4076 sts = sys$parse(&myfab,0,0); /* Free search context */
4077 if (out) Safefree(out);
4078 if (tmpfspec != NULL)
4080 if (vmsfspec != NULL)
4084 set_vaxc_errno(retsts);
4085 if (retsts == RMS$_PRV) set_errno(EACCES);
4086 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4087 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4088 else set_errno(EVMSERR);
4091 retsts = sys$search(&myfab,0,0);
4092 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4093 mynam.naml$b_nop |= NAM$M_SYNCHK;
4094 mynam.naml$l_rlf = NULL;
4095 myfab.fab$b_dns = 0;
4096 mynam.naml$l_long_defname_size = 0;
4097 sts = sys$parse(&myfab,0,0); /* Free search context */
4098 if (out) Safefree(out);
4099 if (tmpfspec != NULL)
4101 if (vmsfspec != NULL)
4105 set_vaxc_errno(retsts);
4106 if (retsts == RMS$_PRV) set_errno(EACCES);
4107 else set_errno(EVMSERR);
4111 /* If the input filespec contained any lowercase characters,
4112 * downcase the result for compatibility with Unix-minded code. */
4114 if (!decc_efs_case_preserve) {
4115 for (out = mynam.naml$l_long_filename; *out; out++)
4116 if (islower(*out)) { haslower = 1; break; }
4119 /* Is a long or a short name expected */
4120 /*------------------------------------*/
4121 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4122 if (mynam.naml$l_long_result_size) {
4124 speclen = mynam.naml$l_long_result_size;
4127 out = esal; /* Not esa */
4128 speclen = mynam.naml$l_long_expand_size;
4132 if (mynam.naml$b_rsl) {
4134 speclen = mynam.naml$b_rsl;
4137 out = esa; /* Not esal */
4138 speclen = mynam.naml$b_esl;
4141 /* Trim off null fields added by $PARSE
4142 * If type > 1 char, must have been specified in original or default spec
4143 * (not true for version; $SEARCH may have added version of existing file).
4145 trimver = !(mynam.naml$l_fnb & NAM$M_EXP_VER);
4146 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4147 trimtype = !(mynam.naml$l_fnb & NAM$M_EXP_TYPE) &&
4148 (mynam.naml$l_long_ver - mynam.naml$l_long_type == 1);
4151 trimtype = !(mynam.naml$l_fnb & NAM$M_EXP_TYPE) &&
4152 (mynam.naml$l_ver - mynam.naml$l_type == 1);
4154 if (trimver || trimtype) {
4155 if (defspec && *defspec) {
4156 char *defesal = NULL;
4157 Newx(defesal, NAML$C_MAXRSS + 1, char);
4158 if (defesal != NULL) {
4159 struct FAB deffab = cc$rms_fab;
4160 struct NAML defnam = cc$rms_naml;
4162 deffab.fab$l_naml = &defnam;
4164 deffab.fab$l_fna = (char *) - 1; /* Cast ok */
4165 deffab.fab$b_fns = 0;
4166 defnam.naml$l_long_filename = (char *)defspec; /* Cast ok */
4167 defnam.naml$l_long_filename_size = mynam.naml$l_long_defname_size;
4168 defnam.naml$l_esa = NULL;
4169 defnam.naml$b_ess = 0;
4170 defnam.naml$l_long_expand = defesal;
4171 defnam.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
4172 defnam.naml$b_nop = NAM$M_SYNCHK;
4173 #ifdef NAM$M_NO_SHORT_UPCASE
4174 if (decc_efs_case_preserve)
4175 defnam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
4177 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4179 trimver = !(defnam.naml$l_fnb & NAM$M_EXP_VER);
4182 trimtype = !(defnam.naml$l_fnb & NAM$M_EXP_TYPE);
4189 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4190 if (*mynam.naml$l_long_ver != '\"')
4191 speclen = mynam.naml$l_long_ver - out;
4194 if (*mynam.naml$l_ver != '\"')
4195 speclen = mynam.naml$l_ver - out;
4199 /* If we didn't already trim version, copy down */
4200 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4201 if (speclen > mynam.naml$l_long_ver - out)
4203 (mynam.naml$l_long_type,
4204 mynam.naml$l_long_ver,
4205 speclen - (mynam.naml$l_long_ver - out));
4206 speclen -= mynam.naml$l_long_ver - mynam.naml$l_long_type;
4209 if (speclen > mynam.naml$l_ver - out)
4213 speclen - (mynam.naml$l_ver - out));
4214 speclen -= mynam.naml$l_ver - mynam.naml$l_type;
4219 /* Done with these copies of the input files */
4220 /*-------------------------------------------*/
4221 if (vmsfspec != NULL)
4223 if (tmpfspec != NULL)
4226 /* If we just had a directory spec on input, $PARSE "helpfully"
4227 * adds an empty name and type for us */
4228 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4229 if (mynam.naml$l_long_name == mynam.naml$l_long_type &&
4230 mynam.naml$l_long_ver == mynam.naml$l_long_type + 1 &&
4231 !(mynam.naml$l_fnb & NAM$M_EXP_NAME))
4232 speclen = mynam.naml$l_long_name - out;
4235 if (mynam.naml$l_name == mynam.naml$l_type &&
4236 mynam.naml$l_ver == mynam.naml$l_type + 1 &&
4237 !(mynam.naml$l_fnb & NAM$M_EXP_NAME))
4238 speclen = mynam.naml$l_name - out;
4241 /* Posix format specifications must have matching quotes */
4242 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4243 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4244 out[speclen] = '\"';
4248 out[speclen] = '\0';
4249 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4251 /* Have we been working with an expanded, but not resultant, spec? */
4252 /* Also, convert back to Unix syntax if necessary. */
4254 if (!mynam.naml$l_long_result_size) {
4256 if (do_tounixspec(esa,outbuf,0) == NULL) {
4262 else strcpy(outbuf,esa);
4265 Newx(tmpfspec, VMS_MAXRSS, char);
4266 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) {
4272 strcpy(outbuf,tmpfspec);
4276 mynam.naml$b_nop |= NAM$M_SYNCHK;
4277 mynam.naml$l_rlf = NULL;
4278 mynam.naml$l_rsa = NULL;
4279 mynam.naml$b_rss = 0;
4280 mynam.naml$l_long_result = NULL;
4281 mynam.naml$l_long_result_size = 0;
4282 myfab.fab$b_dns = 0;
4283 mynam.naml$l_long_defname_size = 0;
4284 sts = sys$parse(&myfab,0,0); /* Free search context */
4291 /* External entry points */
4292 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4293 { return do_rmsexpand(spec,buf,0,def,opt); }
4294 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4295 { return do_rmsexpand(spec,buf,1,def,opt); }
4299 ** The following routines are provided to make life easier when
4300 ** converting among VMS-style and Unix-style directory specifications.
4301 ** All will take input specifications in either VMS or Unix syntax. On
4302 ** failure, all return NULL. If successful, the routines listed below
4303 ** return a pointer to a buffer containing the appropriately
4304 ** reformatted spec (and, therefore, subsequent calls to that routine
4305 ** will clobber the result), while the routines of the same names with
4306 ** a _ts suffix appended will return a pointer to a mallocd string
4307 ** containing the appropriately reformatted spec.
4308 ** In all cases, only explicit syntax is altered; no check is made that
4309 ** the resulting string is valid or that the directory in question
4312 ** fileify_dirspec() - convert a directory spec into the name of the
4313 ** directory file (i.e. what you can stat() to see if it's a dir).
4314 ** The style (VMS or Unix) of the result is the same as the style
4315 ** of the parameter passed in.
4316 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4317 ** what you prepend to a filename to indicate what directory it's in).
4318 ** The style (VMS or Unix) of the result is the same as the style
4319 ** of the parameter passed in.
4320 ** tounixpath() - convert a directory spec into a Unix-style path.
4321 ** tovmspath() - convert a directory spec into a VMS-style path.
4322 ** tounixspec() - convert any file spec into a Unix-style file spec.
4323 ** tovmsspec() - convert any file spec into a VMS-style spec.
4325 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4326 ** Permission is given to distribute this code as part of the Perl
4327 ** standard distribution under the terms of the GNU General Public
4328 ** License or the Perl Artistic License. Copies of each may be
4329 ** found in the Perl standard distribution.
4332 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
4333 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
4335 static char __fileify_retbuf[NAM$C_MAXRSS+1];
4336 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4337 char *retspec, *cp1, *cp2, *lastdir;
4338 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
4339 unsigned short int trnlnm_iter_count;
4342 if (!dir || !*dir) {
4343 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4345 dirlen = strlen(dir);
4346 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4347 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4348 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4355 if (dirlen > NAM$C_MAXRSS) {
4356 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
4358 if (!strpbrk(dir+1,"/]>:") &&
4359 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4360 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4361 trnlnm_iter_count = 0;
4362 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4363 trnlnm_iter_count++;
4364 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4366 dirlen = strlen(trndir);
4369 strncpy(trndir,dir,dirlen);
4370 trndir[dirlen] = '\0';
4373 /* At this point we are done with *dir and use *trndir which is a
4374 * copy that can be modified. *dir must not be modified.
4377 /* If we were handed a rooted logical name or spec, treat it like a
4378 * simple directory, so that
4379 * $ Define myroot dev:[dir.]
4380 * ... do_fileify_dirspec("myroot",buf,1) ...
4381 * does something useful.
4383 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4384 trndir[--dirlen] = '\0';
4385 trndir[dirlen-1] = ']';
4387 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4388 trndir[--dirlen] = '\0';
4389 trndir[dirlen-1] = '>';
4392 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4393 /* If we've got an explicit filename, we can just shuffle the string. */
4394 if (*(cp1+1)) hasfilename = 1;
4395 /* Similarly, we can just back up a level if we've got multiple levels
4396 of explicit directories in a VMS spec which ends with directories. */
4398 for (cp2 = cp1; cp2 > trndir; cp2--) {
4400 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4401 *cp2 = *cp1; *cp1 = '\0';
4406 if (*cp2 == '[' || *cp2 == '<') break;
4411 cp1 = strpbrk(trndir,"]:>"); /* Prepare for future change */
4412 if (hasfilename || !cp1) { /* Unix-style path or filename */
4413 if (trndir[0] == '.') {
4414 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0'))
4415 return do_fileify_dirspec("[]",buf,ts);
4416 else if (trndir[1] == '.' &&
4417 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0')))
4418 return do_fileify_dirspec("[-]",buf,ts);
4420 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4421 dirlen -= 1; /* to last element */
4422 lastdir = strrchr(trndir,'/');
4424 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4425 /* If we have "/." or "/..", VMSify it and let the VMS code
4426 * below expand it, rather than repeating the code to handle
4427 * relative components of a filespec here */
4429 if (*(cp1+2) == '.') cp1++;
4430 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4431 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
4432 if (strchr(vmsdir,'/') != NULL) {
4433 /* If do_tovmsspec() returned it, it must have VMS syntax
4434 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4435 * the time to check this here only so we avoid a recursion
4436 * loop; otherwise, gigo.
4438 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
4440 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
4441 return do_tounixspec(trndir,buf,ts);
4444 } while ((cp1 = strstr(cp1,"/.")) != NULL);
4445 lastdir = strrchr(trndir,'/');
4447 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
4448 /* Ditto for specs that end in an MFD -- let the VMS code
4449 * figure out whether it's a real device or a rooted logical. */
4451 /* This should not happen any more. Allowing the fake /000000
4452 * in a UNIX pathname causes all sorts of problems when trying
4453 * to run in UNIX emulation. So the VMS to UNIX conversions
4454 * now remove the fake /000000 directories.
4457 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
4458 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
4459 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
4460 return do_tounixspec(trndir,buf,ts);
4464 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
4465 !(lastdir = cp1 = strrchr(trndir,']')) &&
4466 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
4467 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
4470 /* For EFS or ODS-5 look for the last dot */
4471 if (decc_efs_charset) {
4472 cp2 = strrchr(cp1,'.');
4474 if (vms_process_case_tolerant) {
4475 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4476 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4477 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4478 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4479 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4480 (ver || *cp3)))))) {
4482 set_vaxc_errno(RMS$_DIR);
4487 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4488 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4489 !*(cp2+3) || *(cp2+3) != 'R' ||
4490 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4491 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4492 (ver || *cp3)))))) {
4494 set_vaxc_errno(RMS$_DIR);
4498 dirlen = cp2 - trndir;
4502 retlen = dirlen + 6;
4503 if (buf) retspec = buf;
4504 else if (ts) Newx(retspec,retlen+1,char);
4505 else retspec = __fileify_retbuf;
4506 memcpy(retspec,trndir,dirlen);
4507 retspec[dirlen] = '\0';
4509 /* We've picked up everything up to the directory file name.
4510 Now just add the type and version, and we're set. */
4511 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
4512 strcat(retspec,".dir;1");
4514 strcat(retspec,".DIR;1");
4517 else { /* VMS-style directory spec */
4518 char esa[NAM$C_MAXRSS+1], term, *cp;
4519 unsigned long int sts, cmplen, haslower = 0;
4520 struct FAB dirfab = cc$rms_fab;
4521 struct NAM savnam, dirnam = cc$rms_nam;
4523 dirfab.fab$b_fns = strlen(trndir);
4524 dirfab.fab$l_fna = trndir;
4525 dirfab.fab$l_nam = &dirnam;
4526 dirfab.fab$l_dna = ".DIR;1";
4527 dirfab.fab$b_dns = 6;
4528 dirnam.nam$b_ess = NAM$C_MAXRSS;
4529 dirnam.nam$l_esa = esa;
4530 #ifdef NAM$M_NO_SHORT_UPCASE
4531 if (decc_efs_case_preserve)
4532 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4535 for (cp = trndir; *cp; cp++)
4536 if (islower(*cp)) { haslower = 1; break; }
4537 if (!((sts = sys$parse(&dirfab))&1)) {
4538 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4539 dirnam.nam$b_nop |= NAM$M_SYNCHK;
4540 sts = sys$parse(&dirfab) & 1;
4544 set_vaxc_errno(dirfab.fab$l_sts);
4550 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
4551 /* Yes; fake the fnb bits so we'll check type below */
4552 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
4554 else { /* No; just work with potential name */
4555 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
4557 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
4558 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4559 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4564 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
4565 cp1 = strchr(esa,']');
4566 if (!cp1) cp1 = strchr(esa,'>');
4567 if (cp1) { /* Should always be true */
4568 dirnam.nam$b_esl -= cp1 - esa - 1;
4569 memmove(esa,cp1 + 1,dirnam.nam$b_esl);
4572 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
4573 /* Yep; check version while we're at it, if it's there. */
4574 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
4575 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
4576 /* Something other than .DIR[;1]. Bzzt. */
4577 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4578 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4580 set_vaxc_errno(RMS$_DIR);
4584 esa[dirnam.nam$b_esl] = '\0';
4585 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
4586 /* They provided at least the name; we added the type, if necessary, */
4587 if (buf) retspec = buf; /* in sys$parse() */
4588 else if (ts) Newx(retspec,dirnam.nam$b_esl+1,char);
4589 else retspec = __fileify_retbuf;
4590 strcpy(retspec,esa);
4591 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4592 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4595 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
4596 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
4598 dirnam.nam$b_esl -= 9;
4600 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
4601 if (cp1 == NULL) { /* should never happen */
4602 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4603 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4608 retlen = strlen(esa);
4609 cp1 = strrchr(esa,'.');
4610 /* ODS-5 directory specifications can have extra "." in them. */
4611 while (cp1 != NULL) {
4612 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
4616 while ((cp1 > esa) && (*cp1 != '.'))
4623 if ((cp1) != NULL) {
4624 /* There's more than one directory in the path. Just roll back. */
4626 if (buf) retspec = buf;
4627 else if (ts) Newx(retspec,retlen+7,char);
4628 else retspec = __fileify_retbuf;
4629 strcpy(retspec,esa);
4632 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
4633 /* Go back and expand rooted logical name */
4634 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
4635 #ifdef NAM$M_NO_SHORT_UPCASE
4636 if (decc_efs_case_preserve)
4637 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4639 if (!(sys$parse(&dirfab) & 1)) {
4640 dirnam.nam$l_rlf = NULL;
4641 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4643 set_vaxc_errno(dirfab.fab$l_sts);
4646 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
4647 if (buf) retspec = buf;
4648 else if (ts) Newx(retspec,retlen+16,char);
4649 else retspec = __fileify_retbuf;
4650 cp1 = strstr(esa,"][");
4651 if (!cp1) cp1 = strstr(esa,"]<");
4653 memcpy(retspec,esa,dirlen);
4654 if (!strncmp(cp1+2,"000000]",7)) {
4655 retspec[dirlen-1] = '\0';
4656 /* Not full ODS-5, just extra dots in directories for now */
4657 cp1 = retspec + dirlen - 1;
4658 while (cp1 > retspec)
4663 if (*(cp1-1) != '^')
4668 if (*cp1 == '.') *cp1 = ']';
4670 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4671 memmove(cp1+1,"000000]",7);
4675 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
4676 retspec[retlen] = '\0';
4677 /* Convert last '.' to ']' */
4678 cp1 = retspec+retlen-1;
4679 while (*cp != '[') {
4682 /* Do not trip on extra dots in ODS-5 directories */
4683 if ((cp1 == retspec) || (*(cp1-1) != '^'))
4687 if (*cp1 == '.') *cp1 = ']';
4689 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4690 memmove(cp1+1,"000000]",7);
4694 else { /* This is a top-level dir. Add the MFD to the path. */
4695 if (buf) retspec = buf;
4696 else if (ts) Newx(retspec,retlen+16,char);
4697 else retspec = __fileify_retbuf;
4700 while (*cp1 != ':') *(cp2++) = *(cp1++);
4701 strcpy(cp2,":[000000]");
4706 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4707 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4708 /* We've set up the string up through the filename. Add the
4709 type and version, and we're done. */
4710 strcat(retspec,".DIR;1");
4712 /* $PARSE may have upcased filespec, so convert output to lower
4713 * case if input contained any lowercase characters. */
4714 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
4717 } /* end of do_fileify_dirspec() */
4719 /* External entry points */
4720 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
4721 { return do_fileify_dirspec(dir,buf,0); }
4722 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
4723 { return do_fileify_dirspec(dir,buf,1); }
4725 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
4726 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
4728 static char __pathify_retbuf[NAM$C_MAXRSS+1];
4729 unsigned long int retlen;
4730 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
4731 unsigned short int trnlnm_iter_count;
4735 if (!dir || !*dir) {
4736 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4739 if (*dir) strcpy(trndir,dir);
4740 else getcwd(trndir,sizeof trndir - 1);
4742 trnlnm_iter_count = 0;
4743 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
4744 && my_trnlnm(trndir,trndir,0)) {
4745 trnlnm_iter_count++;
4746 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4747 trnlen = strlen(trndir);
4749 /* Trap simple rooted lnms, and return lnm:[000000] */
4750 if (!strcmp(trndir+trnlen-2,".]")) {
4751 if (buf) retpath = buf;
4752 else if (ts) Newx(retpath,strlen(dir)+10,char);
4753 else retpath = __pathify_retbuf;
4754 strcpy(retpath,dir);
4755 strcat(retpath,":[000000]");
4760 /* At this point we do not work with *dir, but the copy in
4761 * *trndir that is modifiable.
4764 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
4765 if (*trndir == '.' && (*(trndir+1) == '\0' ||
4766 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
4767 retlen = 2 + (*(trndir+1) != '\0');
4769 if ( !(cp1 = strrchr(trndir,'/')) &&
4770 !(cp1 = strrchr(trndir,']')) &&
4771 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
4772 if ((cp2 = strchr(cp1,'.')) != NULL &&
4773 (*(cp2-1) != '/' || /* Trailing '.', '..', */
4774 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
4775 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
4776 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
4779 /* For EFS or ODS-5 look for the last dot */
4780 if (decc_efs_charset) {
4781 cp2 = strrchr(cp1,'.');
4783 if (vms_process_case_tolerant) {
4784 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4785 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4786 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4787 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4788 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4789 (ver || *cp3)))))) {
4791 set_vaxc_errno(RMS$_DIR);
4796 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4797 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4798 !*(cp2+3) || *(cp2+3) != 'R' ||
4799 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4800 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4801 (ver || *cp3)))))) {
4803 set_vaxc_errno(RMS$_DIR);
4807 retlen = cp2 - trndir + 1;
4809 else { /* No file type present. Treat the filename as a directory. */
4810 retlen = strlen(trndir) + 1;
4813 if (buf) retpath = buf;
4814 else if (ts) Newx(retpath,retlen+1,char);
4815 else retpath = __pathify_retbuf;
4816 strncpy(retpath, trndir, retlen-1);
4817 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
4818 retpath[retlen-1] = '/'; /* with '/', add it. */
4819 retpath[retlen] = '\0';
4821 else retpath[retlen-1] = '\0';
4823 else { /* VMS-style directory spec */
4824 char esa[NAM$C_MAXRSS+1], *cp;
4825 unsigned long int sts, cmplen, haslower;
4826 struct FAB dirfab = cc$rms_fab;
4827 struct NAM savnam, dirnam = cc$rms_nam;
4829 /* If we've got an explicit filename, we can just shuffle the string. */
4830 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
4831 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
4832 if ((cp2 = strchr(cp1,'.')) != NULL) {
4834 if (vms_process_case_tolerant) {
4835 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4836 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4837 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4838 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4839 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4840 (ver || *cp3)))))) {
4842 set_vaxc_errno(RMS$_DIR);
4847 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4848 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4849 !*(cp2+3) || *(cp2+3) != 'R' ||
4850 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4851 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4852 (ver || *cp3)))))) {
4854 set_vaxc_errno(RMS$_DIR);
4859 else { /* No file type, so just draw name into directory part */
4860 for (cp2 = cp1; *cp2; cp2++) ;
4863 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
4865 /* We've now got a VMS 'path'; fall through */
4867 dirfab.fab$b_fns = strlen(trndir);
4868 dirfab.fab$l_fna = trndir;
4869 if (trndir[dirfab.fab$b_fns-1] == ']' ||
4870 trndir[dirfab.fab$b_fns-1] == '>' ||
4871 trndir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
4872 if (buf) retpath = buf;
4873 else if (ts) Newx(retpath,strlen(trndir)+1,char);
4874 else retpath = __pathify_retbuf;
4875 strcpy(retpath,trndir);
4878 dirfab.fab$l_dna = ".DIR;1";
4879 dirfab.fab$b_dns = 6;
4880 dirfab.fab$l_nam = &dirnam;
4881 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
4882 dirnam.nam$l_esa = esa;
4883 #ifdef NAM$M_NO_SHORT_UPCASE
4884 if (decc_efs_case_preserve)
4885 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4888 for (cp = trndir; *cp; cp++)
4889 if (islower(*cp)) { haslower = 1; break; }
4891 if (!(sts = (sys$parse(&dirfab)&1))) {
4892 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4893 dirnam.nam$b_nop |= NAM$M_SYNCHK;
4894 sts = sys$parse(&dirfab) & 1;
4898 set_vaxc_errno(dirfab.fab$l_sts);
4904 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
4905 if (dirfab.fab$l_sts != RMS$_FNF) {
4907 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4908 dirfab.fab$b_dns = 0;
4909 sts1 = sys$parse(&dirfab,0,0);
4911 set_vaxc_errno(dirfab.fab$l_sts);
4914 dirnam = savnam; /* No; just work with potential name */
4917 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
4918 /* Yep; check version while we're at it, if it's there. */
4919 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
4920 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
4922 /* Something other than .DIR[;1]. Bzzt. */
4923 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4924 dirfab.fab$b_dns = 0;
4925 sts2 = sys$parse(&dirfab,0,0);
4927 set_vaxc_errno(RMS$_DIR);
4931 /* OK, the type was fine. Now pull any file name into the
4933 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
4935 cp1 = strrchr(esa,'>');
4936 *dirnam.nam$l_type = '>';
4939 *(dirnam.nam$l_type + 1) = '\0';
4940 retlen = dirnam.nam$l_type - esa + 2;
4941 if (buf) retpath = buf;
4942 else if (ts) Newx(retpath,retlen,char);
4943 else retpath = __pathify_retbuf;
4944 strcpy(retpath,esa);
4945 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4946 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4947 /* $PARSE may have upcased filespec, so convert output to lower
4948 * case if input contained any lowercase characters. */
4949 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
4953 } /* end of do_pathify_dirspec() */
4955 /* External entry points */
4956 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
4957 { return do_pathify_dirspec(dir,buf,0); }
4958 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
4959 { return do_pathify_dirspec(dir,buf,1); }
4961 /*{{{ char *tounixspec[_ts](char *spec, char *buf)*/
4962 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
4964 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
4965 char *dirend, *rslt, *cp1, *cp3, tmp[NAM$C_MAXRSS+1];
4967 int devlen, dirlen, retlen = NAM$C_MAXRSS+1;
4968 int expand = 1; /* guarantee room for leading and trailing slashes */
4969 unsigned short int trnlnm_iter_count;
4972 if (spec == NULL) return NULL;
4973 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
4974 if (buf) rslt = buf;
4976 retlen = strlen(spec);
4977 cp1 = strchr(spec,'[');
4978 if (!cp1) cp1 = strchr(spec,'<');
4980 for (cp1++; *cp1; cp1++) {
4981 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
4982 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
4983 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
4986 Newx(rslt,retlen+2+2*expand,char);
4988 else rslt = __tounixspec_retbuf;
4990 /* New VMS specific format needs translation
4991 * glob passes filenames with trailing '\n' and expects this preserved.
4993 if (decc_posix_compliant_pathnames) {
4994 if (strncmp(spec, "\"^UP^", 5) == 0) {
5000 Newx(tunix, VMS_MAXRSS + 1,char);
5001 strcpy(tunix, spec);
5002 tunix_len = strlen(tunix);
5004 if (tunix[tunix_len - 1] == '\n') {
5005 tunix[tunix_len - 1] = '\"';
5006 tunix[tunix_len] = '\0';
5010 uspec = decc$translate_vms(tunix);
5012 if ((int)uspec > 0) {
5018 /* If we can not translate it, makemaker wants as-is */
5026 cmp_rslt = 0; /* Presume VMS */
5027 cp1 = strchr(spec, '/');
5031 /* Look for EFS ^/ */
5032 if (decc_efs_charset) {
5033 while (cp1 != NULL) {
5036 /* Found illegal VMS, assume UNIX */
5041 cp1 = strchr(cp1, '/');
5045 /* Look for "." and ".." */
5046 if (decc_filename_unix_report) {
5047 if (spec[0] == '.') {
5048 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5052 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5058 /* This is already UNIX or at least nothing VMS understands */
5066 dirend = strrchr(spec,']');
5067 if (dirend == NULL) dirend = strrchr(spec,'>');
5068 if (dirend == NULL) dirend = strchr(spec,':');
5069 if (dirend == NULL) {
5074 /* Special case 1 - sys$posix_root = / */
5075 #if __CRTL_VER >= 70000000
5076 if (!decc_disable_posix_root) {
5077 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5085 /* Special case 2 - Convert NLA0: to /dev/null */
5086 #if __CRTL_VER < 70000000
5087 cmp_rslt = strncmp(spec,"NLA0:", 5);
5089 cmp_rslt = strncmp(spec,"nla0:", 5);
5091 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5093 if (cmp_rslt == 0) {
5094 strcpy(rslt, "/dev/null");
5097 if (spec[6] != '\0') {
5104 /* Also handle special case "SYS$SCRATCH:" */
5105 #if __CRTL_VER < 70000000
5106 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5108 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5110 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5112 if (cmp_rslt == 0) {
5115 islnm = my_trnlnm(tmp, "TMP", 0);
5117 strcpy(rslt, "/tmp");
5120 if (spec[12] != '\0') {
5128 if (*cp2 != '[' && *cp2 != '<') {
5131 else { /* the VMS spec begins with directories */
5133 if (*cp2 == ']' || *cp2 == '>') {
5134 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5137 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5138 if (getcwd(tmp,sizeof tmp,1) == NULL) {
5139 if (ts) Safefree(rslt);
5142 trnlnm_iter_count = 0;
5145 while (*cp3 != ':' && *cp3) cp3++;
5147 if (strchr(cp3,']') != NULL) break;
5148 trnlnm_iter_count++;
5149 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5150 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5152 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5153 retlen = devlen + dirlen;
5154 Renew(rslt,retlen+1+2*expand,char);
5160 *(cp1++) = *(cp3++);
5161 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
5165 if ((*cp2 == '^')) {
5166 /* EFS file escape, pass the next character as is */
5167 /* Fix me: HEX encoding for UNICODE not implemented */
5170 else if ( *cp2 == '.') {
5171 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5172 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5178 for (; cp2 <= dirend; cp2++) {
5179 if ((*cp2 == '^')) {
5180 /* EFS file escape, pass the next character as is */
5181 /* Fix me: HEX encoding for UNICODE not implemented */
5187 if (*(cp2+1) == '[') cp2++;
5189 else if (*cp2 == ']' || *cp2 == '>') {
5190 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5192 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5194 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5195 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5196 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5197 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5198 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5200 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5201 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5205 else if (*cp2 == '-') {
5206 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5207 while (*cp2 == '-') {
5209 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5211 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5212 if (ts) Safefree(rslt); /* filespecs like */
5213 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5217 else *(cp1++) = *cp2;
5219 else *(cp1++) = *cp2;
5221 while (*cp2) *(cp1++) = *(cp2++);
5224 /* This still leaves /000000/ when working with a
5225 * VMS device root or concealed root.
5231 ulen = strlen(rslt);
5233 /* Get rid of "000000/ in rooted filespecs */
5235 zeros = strstr(rslt, "/000000/");
5236 if (zeros != NULL) {
5238 mlen = ulen - (zeros - rslt) - 7;
5239 memmove(zeros, &zeros[7], mlen);
5248 } /* end of do_tounixspec() */
5250 /* External entry points */
5251 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
5252 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
5254 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5256 static int posix_to_vmsspec
5257 (char *vmspath, int vmspath_len, const char *unixpath) {
5259 struct FAB myfab = cc$rms_fab;
5260 struct NAML mynam = cc$rms_naml;
5261 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5262 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5268 /* If not a posix spec already, convert it */
5270 unixlen = strlen(unixpath);
5275 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5276 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5279 /* This is already a VMS specification, no conversion */
5281 strncpy(vmspath,unixpath, vmspath_len);
5283 vmspath[vmspath_len] = 0;
5284 if (unixpath[unixlen - 1] == '/')
5286 Newx(esa, VMS_MAXRSS+1, char);
5287 myfab.fab$l_fna = vmspath;
5288 myfab.fab$b_fns = strlen(vmspath);
5289 myfab.fab$l_naml = &mynam;
5290 mynam.naml$l_esa = NULL;
5291 mynam.naml$b_ess = 0;
5292 mynam.naml$l_long_expand = esa;
5293 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS;
5294 mynam.naml$l_rsa = NULL;
5295 mynam.naml$b_rss = 0;
5296 if (decc_efs_case_preserve)
5297 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
5298 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
5300 /* Set up the remaining naml fields */
5301 sts = sys$parse(&myfab);
5303 /* It failed! Try again as a UNIX filespec */
5309 /* get the Device ID and the FID */
5310 sts = sys$search(&myfab);
5311 /* on any failure, returned the POSIX ^UP^ filespec */
5316 specdsc.dsc$a_pointer = vmspath;
5317 specdsc.dsc$w_length = vmspath_len;
5319 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
5320 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
5321 sts = lib$fid_to_name
5322 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
5324 /* on any failure, returned the POSIX ^UP^ filespec */
5326 /* This can happen if user does not have permission to read directories */
5327 if (strncmp(unixpath,"\"^UP^",5) != 0)
5328 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5330 strcpy(vmspath, unixpath);
5333 vmspath[specdsc.dsc$w_length] = 0;
5335 /* Are we expecting a directory? */
5336 if (dir_flag != 0) {
5342 i = specdsc.dsc$w_length - 1;
5346 /* Version must be '1' */
5347 if (vmspath[i--] != '1')
5349 /* Version delimiter is one of ".;" */
5350 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
5353 if (vmspath[i--] != 'R')
5355 if (vmspath[i--] != 'I')
5357 if (vmspath[i--] != 'D')
5359 if (vmspath[i--] != '.')
5361 eptr = &vmspath[i+1];
5363 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
5364 if (vmspath[i-1] != '^') {
5372 /* Get rid of 6 imaginary zero directory filename */
5373 vmspath[i+1] = '\0';
5377 if (vmspath[i] == '0')
5391 /* Can not use LIB$FID_TO_NAME, so doing a manual conversion */
5392 static int posix_to_vmsspec_hardway
5393 (char *vmspath, int vmspath_len, const char *unixpath) {
5396 const char *unixptr;
5398 const char *lastslash;
5399 const char *lastdot;
5410 /* Ignore leading "/" characters */
5411 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
5414 unixlen = strlen(unixptr);
5416 /* Do nothing with blank paths */
5422 lastslash = strrchr(unixptr,'/');
5423 lastdot = strrchr(unixptr,'.');
5426 /* last dot is last dot or past end of string */
5427 if (lastdot == NULL)
5428 lastdot = unixptr + unixlen;
5430 /* if no directories, set last slash to beginning of string */
5431 if (lastslash == NULL) {
5432 lastslash = unixptr;
5435 /* Watch out for trailing "." after last slash, still a directory */
5436 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
5437 lastslash = unixptr + unixlen;
5440 /* Watch out for traiing ".." after last slash, still a directory */
5441 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
5442 lastslash = unixptr + unixlen;
5445 /* dots in directories are aways escaped */
5446 if (lastdot < lastslash)
5447 lastdot = unixptr + unixlen;
5450 /* if (unixptr < lastslash) then we are in a directory */
5458 /* This could have a "^UP^ on the front */
5459 if (strncmp(unixptr,"\"^UP^",5) == 0) {
5464 /* Start with the UNIX path */
5465 if (*unixptr != '/') {
5466 /* relative paths */
5467 if (lastslash > unixptr) {
5470 /* skip leading ./ */
5472 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
5478 /* Are we still in a directory? */
5479 if (unixptr <= lastslash) {
5484 /* if not backing up, then it is relative forward. */
5485 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
5486 ((unixptr[2] == '/') || (unixptr[2] == '\0')))) {
5494 /* Perl wants an empty directory here to tell the difference
5495 * between a DCL commmand and a filename
5504 /* Handle two special files . and .. */
5505 if (unixptr[0] == '.') {
5506 if (unixptr[1] == '\0') {
5513 if ((unixptr[1] == '.') && (unixptr[2] == '\0')) {
5524 else { /* Absolute PATH handling */
5528 /* Need to find out where root is */
5530 /* In theory, this procedure should never get an absolute POSIX pathname
5531 * that can not be found on the POSIX root.
5532 * In practice, that can not be relied on, and things will show up
5533 * here that are a VMS device name or concealed logical name instead.
5534 * So to make things work, this procedure must be tolerant.
5536 Newx(esa, vmspath_len, char);
5539 nextslash = strchr(&unixptr[1],'/');
5541 if (nextslash != NULL) {
5542 seg_len = nextslash - &unixptr[1];
5543 strncpy(vmspath, unixptr, seg_len + 1);
5544 vmspath[seg_len+1] = 0;
5545 sts = posix_to_vmsspec(esa, vmspath_len, vmspath);
5549 /* This is verified to be a real path */
5551 sts = posix_to_vmsspec(esa, vmspath_len, "/");
5552 strcpy(vmspath, esa);
5553 vmslen = strlen(vmspath);
5554 vmsptr = vmspath + vmslen;
5556 if (unixptr < lastslash) {
5565 cmp = strcmp(rptr,"000000.");
5570 } /* removing 6 zeros */
5571 } /* vmslen < 7, no 6 zeros possible */
5572 } /* Not in a directory */
5573 } /* end of verified real path handling */
5578 /* Ok, we have a device or a concealed root that is not in POSIX
5579 * or we have garbage. Make the best of it.
5582 /* Posix to VMS destroyed this, so copy it again */
5583 strncpy(vmspath, &unixptr[1], seg_len);
5584 vmspath[seg_len] = 0;
5586 vmsptr = &vmsptr[vmslen];
5589 /* Now do we need to add the fake 6 zero directory to it? */
5591 if ((*lastslash == '/') && (nextslash < lastslash)) {
5592 /* No there is another directory */
5598 /* now we have foo:bar or foo:[000000]bar to decide from */
5599 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
5600 trnend = islnm ? islnm - 1 : 0;
5602 /* if this was a logical name, ']' or '>' must be present */
5603 /* if not a logical name, then assume a device and hope. */
5604 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
5606 /* if log name and trailing '.' then rooted - treat as device */
5607 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
5609 /* Fix me, if not a logical name, a device lookup should be
5610 * done to see if the device is file structured. If the device
5611 * is not file structured, the 6 zeros should not be put on.
5613 * As it is, perl is occasionally looking for dev:[000000]tty.
5614 * which looks a little strange.
5617 if ((add_6zero == 0) && (*nextslash == '/') && (nextslash[1] == '\0')) {
5618 /* No real directory present */
5623 /* Put the device delimiter on */
5626 unixptr = nextslash;
5629 /* Start directory if needed */
5630 if (!islnm || add_6zero) {
5636 /* add fake 000000] if needed */
5649 } /* non-POSIX translation */
5651 } /* End of relative/absolute path handling */
5653 while ((*unixptr) && (vmslen < vmspath_len)){
5658 if (dir_start != 0) {
5660 /* First characters in a directory are handled special */
5661 while ((*unixptr == '/') ||
5662 ((*unixptr == '.') &&
5663 ((unixptr[1]=='.') || (unixptr[1]=='/') || (unixptr[1]=='\0')))) {
5668 /* Skip redundant / in specification */
5669 while ((*unixptr == '/') && (dir_start != 0)) {
5672 if (unixptr == lastslash)
5675 if (unixptr == lastslash)
5678 /* Skip redundant ./ characters */
5679 while ((*unixptr == '.') &&
5680 ((unixptr[1] == '/')||(unixptr[1] == '\0'))) {
5683 if (unixptr == lastslash)
5685 if (*unixptr == '/')
5688 if (unixptr == lastslash)
5691 /* Skip redundant ../ characters */
5692 while ((*unixptr == '.') && (unixptr[1] == '.') &&
5693 ((unixptr[2] == '/') || (unixptr[2] == '\0'))) {
5694 /* Set the backing up flag */
5700 unixptr++; /* first . */
5701 unixptr++; /* second . */
5702 if (unixptr == lastslash)
5704 if (*unixptr == '/') /* The slash */
5707 if (unixptr == lastslash)
5710 /* To do: Perl expects /.../ to be translated to [...] on VMS */
5711 /* Not needed when VMS is pretending to be UNIX. */
5713 /* Is this loop stuck because of too many dots? */
5714 if (loop_flag == 0) {
5715 /* Exit the loop and pass the rest through */
5720 /* Are we done with directories yet? */
5721 if (unixptr >= lastslash) {
5723 /* Watch out for trailing dots */
5732 if (*unixptr == '/')
5736 /* Have we stopped backing up? */
5741 /* dir_start continues to be = 1 */
5743 if (*unixptr == '-') {
5745 *vmsptr++ = *unixptr++;
5749 /* Now are we done with directories yet? */
5750 if (unixptr >= lastslash) {
5752 /* Watch out for trailing dots */
5768 if (*unixptr == '\0')
5771 /* Normal characters - More EFS work probably needed */
5777 /* remove multiple / */
5778 while (unixptr[1] == '/') {
5781 if (unixptr == lastslash) {
5782 /* Watch out for trailing dots */
5794 /* To do: Perl expects /.../ to be translated to [...] on VMS */
5795 /* Not needed when VMS is pretending to be UNIX. */
5799 if (*unixptr != '\0')
5815 if ((unixptr < lastdot) || (unixptr[1] == '\0')) {
5821 /* trailing dot ==> '^..' on VMS */
5822 if (*unixptr == '\0') {
5826 *vmsptr++ = *unixptr++;
5829 if (quoted && (unixptr[1] == '\0')) {
5834 *vmsptr++ = *unixptr++;
5841 *vmsptr++ = *unixptr++;
5845 if (*unixptr != '\0') {
5846 *vmsptr++ = *unixptr++;
5853 /* Make sure directory is closed */
5854 if (unixptr == lastslash) {
5856 vmsptr2 = vmsptr - 1;
5858 if (*vmsptr2 != ']') {
5861 /* directories do not end in a dot bracket */
5862 if (*vmsptr2 == '.') {
5866 if (*vmsptr2 != '^') {
5867 vmsptr--; /* back up over the dot */
5875 /* Add a trailing dot if a file with no extension */
5876 vmsptr2 = vmsptr - 1;
5877 if ((*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
5878 (*lastdot != '.')) {
5889 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
5890 static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
5891 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
5892 char *rslt, *dirend;
5897 unsigned long int infront = 0, hasdir = 1;
5901 if (path == NULL) return NULL;
5902 rslt_len = VMS_MAXRSS;
5903 if (buf) rslt = buf;
5904 else if (ts) Newx(rslt,NAM$C_MAXRSS+1,char);
5905 else rslt = __tovmsspec_retbuf;
5906 if (strpbrk(path,"]:>") ||
5907 (dirend = strrchr(path,'/')) == NULL) {
5908 if (path[0] == '.') {
5909 if (path[1] == '\0') strcpy(rslt,"[]");
5910 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
5911 else strcpy(rslt,path); /* probably garbage */
5913 else strcpy(rslt,path);
5917 /* Posix specifications are now a native VMS format */
5918 /*--------------------------------------------------*/
5919 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5920 if (decc_posix_compliant_pathnames) {
5921 if (strncmp(path,"\"^UP^",5) == 0) {
5922 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5928 vms_delim = strpbrk(path,"]:>");
5930 if ((vms_delim != NULL) ||
5931 ((dirend = strrchr(path,'/')) == NULL)) {
5933 /* VMS special characters found! */
5935 if (path[0] == '.') {
5936 if (path[1] == '\0') strcpy(rslt,"[]");
5937 else if (path[1] == '.' && path[2] == '\0')
5940 /* Dot preceeding a device or directory ? */
5942 /* If not in POSIX mode, pass it through and hope it works */
5943 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5944 if (!decc_posix_compliant_pathnames)
5945 strcpy(rslt,path); /* probably garbage */
5947 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5949 strcpy(rslt,path); /* probably garbage */
5955 /* If no VMS characters and in POSIX mode, convert it!
5956 * This is the easiest way to get directory specifications
5957 * handled correctly in POSIX mode
5959 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5960 if ((vms_delim == NULL) && decc_posix_compliant_pathnames)
5961 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5963 /* No unix path separators - presume VMS already */
5967 strcpy(rslt,path); /* probably garbage */
5973 /* If POSIX mode active, handle the conversion */
5974 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5975 if (decc_posix_compliant_pathnames) {
5976 posix_to_vmsspec_hardway(rslt, rslt_len, path);
5981 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
5982 if (!*(dirend+2)) dirend +=2;
5983 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
5984 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
5989 lastdot = strrchr(cp2,'.');
5991 char trndev[NAM$C_MAXRSS+1];
5995 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
5997 if (decc_disable_posix_root) {
5998 strcpy(rslt,"sys$disk:[000000]");
6001 strcpy(rslt,"sys$posix_root:[000000]");
6005 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
6007 islnm = my_trnlnm(rslt,trndev,0);
6009 /* DECC special handling */
6011 if (strcmp(rslt,"bin") == 0) {
6012 strcpy(rslt,"sys$system");
6015 islnm = my_trnlnm(rslt,trndev,0);
6017 else if (strcmp(rslt,"tmp") == 0) {
6018 strcpy(rslt,"sys$scratch");
6021 islnm = my_trnlnm(rslt,trndev,0);
6023 else if (!decc_disable_posix_root) {
6024 strcpy(rslt, "sys$posix_root");
6028 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6029 islnm = my_trnlnm(rslt,trndev,0);
6031 else if (strcmp(rslt,"dev") == 0) {
6032 if (strncmp(cp2,"/null", 5) == 0) {
6033 if ((cp2[5] == 0) || (cp2[5] == '/')) {
6034 strcpy(rslt,"NLA0");
6038 islnm = my_trnlnm(rslt,trndev,0);
6044 trnend = islnm ? strlen(trndev) - 1 : 0;
6045 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
6046 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
6047 /* If the first element of the path is a logical name, determine
6048 * whether it has to be translated so we can add more directories. */
6049 if (!islnm || rooted) {
6052 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
6056 if (cp2 != dirend) {
6057 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
6058 strcpy(rslt,trndev);
6059 cp1 = rslt + trnend;
6066 if (decc_disable_posix_root) {
6076 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
6077 cp2 += 2; /* skip over "./" - it's redundant */
6078 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
6080 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6081 *(cp1++) = '-'; /* "../" --> "-" */
6084 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
6085 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
6086 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6087 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
6090 else if ((cp2 != lastdot) || (lastdot < dirend)) {
6091 /* Escape the extra dots in EFS file specifications */
6094 if (cp2 > dirend) cp2 = dirend;
6096 else *(cp1++) = '.';
6098 for (; cp2 < dirend; cp2++) {
6100 if (*(cp2-1) == '/') continue;
6101 if (*(cp1-1) != '.') *(cp1++) = '.';
6104 else if (!infront && *cp2 == '.') {
6105 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
6106 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
6107 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6108 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
6109 else if (*(cp1-2) == '[') *(cp1-1) = '-';
6110 else { /* back up over previous directory name */
6112 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
6113 if (*(cp1-1) == '[') {
6114 memcpy(cp1,"000000.",7);
6119 if (cp2 == dirend) break;
6121 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
6122 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
6123 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
6124 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6126 *(cp1++) = '.'; /* Simulate trailing '/' */
6127 cp2 += 2; /* for loop will incr this to == dirend */
6129 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
6132 if (decc_efs_charset == 0)
6133 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
6135 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
6141 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
6143 if (decc_efs_charset == 0)
6150 else *(cp1++) = *cp2;
6154 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
6155 if (hasdir) *(cp1++) = ']';
6156 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
6157 /* fixme for ODS5 */
6172 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
6173 decc_readdir_dropdotnotype) {
6178 /* trailing dot ==> '^..' on VMS */
6185 *(cp1++) = *(cp2++);
6213 *(cp1++) = *(cp2++);
6216 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
6217 * which is wrong. UNIX notation should be ".dir. unless
6218 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
6219 * changing this behavior could break more things at this time.
6220 * efs character set effectively does not allow "." to be a version
6221 * delimiter as a further complication about changing this.
6223 if (decc_filename_unix_report != 0) {
6226 *(cp1++) = *(cp2++);
6229 *(cp1++) = *(cp2++);
6232 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
6236 /* Fix me for "^]", but that requires making sure that you do
6237 * not back up past the start of the filename
6239 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
6246 } /* end of do_tovmsspec() */
6248 /* External entry points */
6249 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,0); }
6250 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,1); }
6252 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
6253 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts) {
6254 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
6256 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
6258 if (path == NULL) return NULL;
6259 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
6260 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
6261 if (buf) return buf;
6263 vmslen = strlen(vmsified);
6264 Newx(cp,vmslen+1,char);
6265 memcpy(cp,vmsified,vmslen);
6270 strcpy(__tovmspath_retbuf,vmsified);
6271 return __tovmspath_retbuf;
6274 } /* end of do_tovmspath() */
6276 /* External entry points */
6277 char *Perl_tovmspath(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,0); }
6278 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,1); }
6281 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
6282 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts) {
6283 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
6285 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
6287 if (path == NULL) return NULL;
6288 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
6289 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
6290 if (buf) return buf;
6292 unixlen = strlen(unixified);
6293 Newx(cp,unixlen+1,char);
6294 memcpy(cp,unixified,unixlen);
6299 strcpy(__tounixpath_retbuf,unixified);
6300 return __tounixpath_retbuf;
6303 } /* end of do_tounixpath() */
6305 /* External entry points */
6306 char *Perl_tounixpath(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,0); }
6307 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,1); }
6310 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
6312 *****************************************************************************
6314 * Copyright (C) 1989-1994 by *
6315 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
6317 * Permission is hereby granted for the reproduction of this software, *
6318 * on condition that this copyright notice is included in the reproduction, *
6319 * and that such reproduction is not for purposes of profit or material *
6322 * 27-Aug-1994 Modified for inclusion in perl5 *
6323 * by Charles Bailey bailey@newman.upenn.edu *
6324 *****************************************************************************
6328 * getredirection() is intended to aid in porting C programs
6329 * to VMS (Vax-11 C). The native VMS environment does not support
6330 * '>' and '<' I/O redirection, or command line wild card expansion,
6331 * or a command line pipe mechanism using the '|' AND background
6332 * command execution '&'. All of these capabilities are provided to any
6333 * C program which calls this procedure as the first thing in the
6335 * The piping mechanism will probably work with almost any 'filter' type
6336 * of program. With suitable modification, it may useful for other
6337 * portability problems as well.
6339 * Author: Mark Pizzolato mark@infocomm.com
6343 struct list_item *next;
6347 static void add_item(struct list_item **head,
6348 struct list_item **tail,
6352 static void mp_expand_wild_cards(pTHX_ char *item,
6353 struct list_item **head,
6354 struct list_item **tail,
6357 static int background_process(pTHX_ int argc, char **argv);
6359 static void pipe_and_fork(pTHX_ char **cmargv);
6361 /*{{{ void getredirection(int *ac, char ***av)*/
6363 mp_getredirection(pTHX_ int *ac, char ***av)
6365 * Process vms redirection arg's. Exit if any error is seen.
6366 * If getredirection() processes an argument, it is erased
6367 * from the vector. getredirection() returns a new argc and argv value.
6368 * In the event that a background command is requested (by a trailing "&"),
6369 * this routine creates a background subprocess, and simply exits the program.
6371 * Warning: do not try to simplify the code for vms. The code
6372 * presupposes that getredirection() is called before any data is
6373 * read from stdin or written to stdout.
6375 * Normal usage is as follows:
6381 * getredirection(&argc, &argv);
6385 int argc = *ac; /* Argument Count */
6386 char **argv = *av; /* Argument Vector */
6387 char *ap; /* Argument pointer */
6388 int j; /* argv[] index */
6389 int item_count = 0; /* Count of Items in List */
6390 struct list_item *list_head = 0; /* First Item in List */
6391 struct list_item *list_tail; /* Last Item in List */
6392 char *in = NULL; /* Input File Name */
6393 char *out = NULL; /* Output File Name */
6394 char *outmode = "w"; /* Mode to Open Output File */
6395 char *err = NULL; /* Error File Name */
6396 char *errmode = "w"; /* Mode to Open Error File */
6397 int cmargc = 0; /* Piped Command Arg Count */
6398 char **cmargv = NULL;/* Piped Command Arg Vector */
6401 * First handle the case where the last thing on the line ends with
6402 * a '&'. This indicates the desire for the command to be run in a
6403 * subprocess, so we satisfy that desire.
6406 if (0 == strcmp("&", ap))
6407 exit(background_process(aTHX_ --argc, argv));
6408 if (*ap && '&' == ap[strlen(ap)-1])
6410 ap[strlen(ap)-1] = '\0';
6411 exit(background_process(aTHX_ argc, argv));
6414 * Now we handle the general redirection cases that involve '>', '>>',
6415 * '<', and pipes '|'.
6417 for (j = 0; j < argc; ++j)
6419 if (0 == strcmp("<", argv[j]))
6423 fprintf(stderr,"No input file after < on command line");
6424 exit(LIB$_WRONUMARG);
6429 if ('<' == *(ap = argv[j]))
6434 if (0 == strcmp(">", ap))
6438 fprintf(stderr,"No output file after > on command line");
6439 exit(LIB$_WRONUMARG);
6458 fprintf(stderr,"No output file after > or >> on command line");
6459 exit(LIB$_WRONUMARG);
6463 if (('2' == *ap) && ('>' == ap[1]))
6480 fprintf(stderr,"No output file after 2> or 2>> on command line");
6481 exit(LIB$_WRONUMARG);
6485 if (0 == strcmp("|", argv[j]))
6489 fprintf(stderr,"No command into which to pipe on command line");
6490 exit(LIB$_WRONUMARG);
6492 cmargc = argc-(j+1);
6493 cmargv = &argv[j+1];
6497 if ('|' == *(ap = argv[j]))
6505 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
6508 * Allocate and fill in the new argument vector, Some Unix's terminate
6509 * the list with an extra null pointer.
6511 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
6513 for (j = 0; j < item_count; ++j, list_head = list_head->next)
6514 argv[j] = list_head->value;
6520 fprintf(stderr,"'|' and '>' may not both be specified on command line");
6521 exit(LIB$_INVARGORD);
6523 pipe_and_fork(aTHX_ cmargv);
6526 /* Check for input from a pipe (mailbox) */
6528 if (in == NULL && 1 == isapipe(0))
6530 char mbxname[L_tmpnam];
6532 long int dvi_item = DVI$_DEVBUFSIZ;
6533 $DESCRIPTOR(mbxnam, "");
6534 $DESCRIPTOR(mbxdevnam, "");
6536 /* Input from a pipe, reopen it in binary mode to disable */
6537 /* carriage control processing. */
6539 fgetname(stdin, mbxname);
6540 mbxnam.dsc$a_pointer = mbxname;
6541 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
6542 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
6543 mbxdevnam.dsc$a_pointer = mbxname;
6544 mbxdevnam.dsc$w_length = sizeof(mbxname);
6545 dvi_item = DVI$_DEVNAM;
6546 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
6547 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
6550 freopen(mbxname, "rb", stdin);
6553 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
6557 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
6559 fprintf(stderr,"Can't open input file %s as stdin",in);
6562 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
6564 fprintf(stderr,"Can't open output file %s as stdout",out);
6567 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
6570 if (strcmp(err,"&1") == 0) {
6571 dup2(fileno(stdout), fileno(stderr));
6572 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
6575 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
6577 fprintf(stderr,"Can't open error file %s as stderr",err);
6581 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
6585 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
6588 #ifdef ARGPROC_DEBUG
6589 PerlIO_printf(Perl_debug_log, "Arglist:\n");
6590 for (j = 0; j < *ac; ++j)
6591 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
6593 /* Clear errors we may have hit expanding wildcards, so they don't
6594 show up in Perl's $! later */
6595 set_errno(0); set_vaxc_errno(1);
6596 } /* end of getredirection() */
6599 static void add_item(struct list_item **head,
6600 struct list_item **tail,
6606 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
6610 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
6611 *tail = (*tail)->next;
6613 (*tail)->value = value;
6617 static void mp_expand_wild_cards(pTHX_ char *item,
6618 struct list_item **head,
6619 struct list_item **tail,
6623 unsigned long int context = 0;
6630 char vmsspec[NAM$C_MAXRSS+1];
6631 $DESCRIPTOR(filespec, "");
6632 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
6633 $DESCRIPTOR(resultspec, "");
6634 unsigned long int zero = 0, sts;
6636 for (cp = item; *cp; cp++) {
6637 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
6638 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
6640 if (!*cp || isspace(*cp))
6642 add_item(head, tail, item, count);
6647 /* "double quoted" wild card expressions pass as is */
6648 /* From DCL that means using e.g.: */
6649 /* perl program """perl.*""" */
6650 item_len = strlen(item);
6651 if ( '"' == *item && '"' == item[item_len-1] )
6654 item[item_len-2] = '\0';
6655 add_item(head, tail, item, count);
6659 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
6660 resultspec.dsc$b_class = DSC$K_CLASS_D;
6661 resultspec.dsc$a_pointer = NULL;
6662 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
6663 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
6664 if (!isunix || !filespec.dsc$a_pointer)
6665 filespec.dsc$a_pointer = item;
6666 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
6668 * Only return version specs, if the caller specified a version
6670 had_version = strchr(item, ';');
6672 * Only return device and directory specs, if the caller specifed either.
6674 had_device = strchr(item, ':');
6675 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
6677 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
6678 &defaultspec, 0, 0, &zero))))
6683 Newx(string,resultspec.dsc$w_length+1,char);
6684 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
6685 string[resultspec.dsc$w_length] = '\0';
6686 if (NULL == had_version)
6687 *(strrchr(string, ';')) = '\0';
6688 if ((!had_directory) && (had_device == NULL))
6690 if (NULL == (devdir = strrchr(string, ']')))
6691 devdir = strrchr(string, '>');
6692 strcpy(string, devdir + 1);
6695 * Be consistent with what the C RTL has already done to the rest of
6696 * the argv items and lowercase all of these names.
6698 if (!decc_efs_case_preserve) {
6699 for (c = string; *c; ++c)
6703 if (isunix) trim_unixpath(string,item,1);
6704 add_item(head, tail, string, count);
6707 if (sts != RMS$_NMF)
6709 set_vaxc_errno(sts);
6712 case RMS$_FNF: case RMS$_DNF:
6713 set_errno(ENOENT); break;
6715 set_errno(ENOTDIR); break;
6717 set_errno(ENODEV); break;
6718 case RMS$_FNM: case RMS$_SYN:
6719 set_errno(EINVAL); break;
6721 set_errno(EACCES); break;
6723 _ckvmssts_noperl(sts);
6727 add_item(head, tail, item, count);
6728 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
6729 _ckvmssts_noperl(lib$find_file_end(&context));
6732 static int child_st[2];/* Event Flag set when child process completes */
6734 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
6736 static unsigned long int exit_handler(int *status)
6740 if (0 == child_st[0])
6742 #ifdef ARGPROC_DEBUG
6743 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
6745 fflush(stdout); /* Have to flush pipe for binary data to */
6746 /* terminate properly -- <tp@mccall.com> */
6747 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
6748 sys$dassgn(child_chan);
6750 sys$synch(0, child_st);
6755 static void sig_child(int chan)
6757 #ifdef ARGPROC_DEBUG
6758 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
6760 if (child_st[0] == 0)
6764 static struct exit_control_block exit_block =
6769 &exit_block.exit_status,
6774 pipe_and_fork(pTHX_ char **cmargv)
6777 struct dsc$descriptor_s *vmscmd;
6778 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
6779 int sts, j, l, ismcr, quote, tquote = 0;
6781 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
6782 vms_execfree(vmscmd);
6787 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
6788 && toupper(*(q+2)) == 'R' && !*(q+3);
6790 while (q && l < MAX_DCL_LINE_LENGTH) {
6792 if (j > 0 && quote) {
6798 if (ismcr && j > 1) quote = 1;
6799 tquote = (strchr(q,' ')) != NULL || *q == '\0';
6802 if (quote || tquote) {
6808 if ((quote||tquote) && *q == '"') {
6818 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
6820 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
6824 static int background_process(pTHX_ int argc, char **argv)
6826 char command[2048] = "$";
6827 $DESCRIPTOR(value, "");
6828 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
6829 static $DESCRIPTOR(null, "NLA0:");
6830 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
6832 $DESCRIPTOR(pidstr, "");
6834 unsigned long int flags = 17, one = 1, retsts;
6836 strcat(command, argv[0]);
6839 strcat(command, " \"");
6840 strcat(command, *(++argv));
6841 strcat(command, "\"");
6843 value.dsc$a_pointer = command;
6844 value.dsc$w_length = strlen(value.dsc$a_pointer);
6845 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
6846 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
6847 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
6848 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
6851 _ckvmssts_noperl(retsts);
6853 #ifdef ARGPROC_DEBUG
6854 PerlIO_printf(Perl_debug_log, "%s\n", command);
6856 sprintf(pidstring, "%08X", pid);
6857 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
6858 pidstr.dsc$a_pointer = pidstring;
6859 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
6860 lib$set_symbol(&pidsymbol, &pidstr);
6864 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
6867 /* OS-specific initialization at image activation (not thread startup) */
6868 /* Older VAXC header files lack these constants */
6869 #ifndef JPI$_RIGHTS_SIZE
6870 # define JPI$_RIGHTS_SIZE 817
6872 #ifndef KGB$M_SUBSYSTEM
6873 # define KGB$M_SUBSYSTEM 0x8
6876 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
6878 /*{{{void vms_image_init(int *, char ***)*/
6880 vms_image_init(int *argcp, char ***argvp)
6882 char eqv[LNM$C_NAMLENGTH+1] = "";
6883 unsigned int len, tabct = 8, tabidx = 0;
6884 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
6885 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
6886 unsigned short int dummy, rlen;
6887 struct dsc$descriptor_s **tabvec;
6888 #if defined(PERL_IMPLICIT_CONTEXT)
6891 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
6892 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
6893 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
6896 #ifdef KILL_BY_SIGPRC
6897 Perl_csighandler_init();
6900 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
6901 _ckvmssts_noperl(iosb[0]);
6902 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
6903 if (iprv[i]) { /* Running image installed with privs? */
6904 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
6909 /* Rights identifiers might trigger tainting as well. */
6910 if (!will_taint && (rlen || rsz)) {
6911 while (rlen < rsz) {
6912 /* We didn't get all the identifiers on the first pass. Allocate a
6913 * buffer much larger than $GETJPI wants (rsz is size in bytes that
6914 * were needed to hold all identifiers at time of last call; we'll
6915 * allocate that many unsigned long ints), and go back and get 'em.
6916 * If it gave us less than it wanted to despite ample buffer space,
6917 * something's broken. Is your system missing a system identifier?
6919 if (rsz <= jpilist[1].buflen) {
6920 /* Perl_croak accvios when used this early in startup. */
6921 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
6922 rsz, (unsigned long) jpilist[1].buflen,
6923 "Check your rights database for corruption.\n");
6926 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
6927 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
6928 jpilist[1].buflen = rsz * sizeof(unsigned long int);
6929 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
6930 _ckvmssts_noperl(iosb[0]);
6932 mask = jpilist[1].bufadr;
6933 /* Check attribute flags for each identifier (2nd longword); protected
6934 * subsystem identifiers trigger tainting.
6936 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
6937 if (mask[i] & KGB$M_SUBSYSTEM) {
6942 if (mask != rlst) Safefree(mask);
6945 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
6946 * logical, some versions of the CRTL will add a phanthom /000000/
6947 * directory. This needs to be removed.
6949 if (decc_filename_unix_report) {
6952 ulen = strlen(argvp[0][0]);
6954 zeros = strstr(argvp[0][0], "/000000/");
6955 if (zeros != NULL) {
6957 mlen = ulen - (zeros - argvp[0][0]) - 7;
6958 memmove(zeros, &zeros[7], mlen);
6960 argvp[0][0][ulen] = '\0';
6963 /* It also may have a trailing dot that needs to be removed otherwise
6964 * it will be converted to VMS mode incorrectly.
6967 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
6968 argvp[0][0][ulen] = '\0';
6971 /* We need to use this hack to tell Perl it should run with tainting,
6972 * since its tainting flag may be part of the PL_curinterp struct, which
6973 * hasn't been allocated when vms_image_init() is called.
6976 char **newargv, **oldargv;
6978 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
6979 newargv[0] = oldargv[0];
6980 newargv[1] = (char *) PerlMem_malloc(3 * sizeof(char));
6981 strcpy(newargv[1], "-T");
6982 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
6984 newargv[*argcp] = NULL;
6985 /* We orphan the old argv, since we don't know where it's come from,
6986 * so we don't know how to free it.
6990 else { /* Did user explicitly request tainting? */
6992 char *cp, **av = *argvp;
6993 for (i = 1; i < *argcp; i++) {
6994 if (*av[i] != '-') break;
6995 for (cp = av[i]+1; *cp; cp++) {
6996 if (*cp == 'T') { will_taint = 1; break; }
6997 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
6998 strchr("DFIiMmx",*cp)) break;
7000 if (will_taint) break;
7005 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
7007 if (!tabidx) tabvec = (struct dsc$descriptor_s **) PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
7008 else if (tabidx >= tabct) {
7010 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
7012 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
7013 tabvec[tabidx]->dsc$w_length = 0;
7014 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
7015 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
7016 tabvec[tabidx]->dsc$a_pointer = NULL;
7017 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
7019 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
7021 getredirection(argcp,argvp);
7022 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
7024 # include <reentrancy.h>
7025 decc$set_reentrancy(C$C_MULTITHREAD);
7034 * Trim Unix-style prefix off filespec, so it looks like what a shell
7035 * glob expansion would return (i.e. from specified prefix on, not
7036 * full path). Note that returned filespec is Unix-style, regardless
7037 * of whether input filespec was VMS-style or Unix-style.
7039 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
7040 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
7041 * vector of options; at present, only bit 0 is used, and if set tells
7042 * trim unixpath to try the current default directory as a prefix when
7043 * presented with a possibly ambiguous ... wildcard.
7045 * Returns !=0 on success, with trimmed filespec replacing contents of
7046 * fspec, and 0 on failure, with contents of fpsec unchanged.
7048 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
7050 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
7052 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
7053 *template, *base, *end, *cp1, *cp2;
7054 register int tmplen, reslen = 0, dirs = 0;
7056 if (!wildspec || !fspec) return 0;
7057 template = unixwild;
7058 if (strpbrk(wildspec,"]>:") != NULL) {
7059 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
7062 strncpy(unixwild, wildspec, NAM$C_MAXRSS);
7063 unixwild[NAM$C_MAXRSS] = 0;
7065 if (strpbrk(fspec,"]>:") != NULL) {
7066 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
7067 else base = unixified;
7068 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
7069 * check to see that final result fits into (isn't longer than) fspec */
7070 reslen = strlen(fspec);
7074 /* No prefix or absolute path on wildcard, so nothing to remove */
7075 if (!*template || *template == '/') {
7076 if (base == fspec) return 1;
7077 tmplen = strlen(unixified);
7078 if (tmplen > reslen) return 0; /* not enough space */
7079 /* Copy unixified resultant, including trailing NUL */
7080 memmove(fspec,unixified,tmplen+1);
7084 for (end = base; *end; end++) ; /* Find end of resultant filespec */
7085 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
7086 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
7087 for (cp1 = end ;cp1 >= base; cp1--)
7088 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
7090 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
7094 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
7095 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
7096 int ells = 1, totells, segdirs, match;
7097 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
7098 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7100 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
7102 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
7103 if (ellipsis == template && opts & 1) {
7104 /* Template begins with an ellipsis. Since we can't tell how many
7105 * directory names at the front of the resultant to keep for an
7106 * arbitrary starting point, we arbitrarily choose the current
7107 * default directory as a starting point. If it's there as a prefix,
7108 * clip it off. If not, fall through and act as if the leading
7109 * ellipsis weren't there (i.e. return shortest possible path that
7110 * could match template).
7112 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
7113 if (!decc_efs_case_preserve) {
7114 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7115 if (_tolower(*cp1) != _tolower(*cp2)) break;
7117 segdirs = dirs - totells; /* Min # of dirs we must have left */
7118 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
7119 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
7120 memmove(fspec,cp2+1,end - cp2);
7124 /* First off, back up over constant elements at end of path */
7126 for (front = end ; front >= base; front--)
7127 if (*front == '/' && !dirs--) { front++; break; }
7129 if (!decc_efs_case_preserve) {
7130 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
7131 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
7133 if (cp1 != '\0') return 0; /* Path too long. */
7135 *cp2 = '\0'; /* Pick up with memcpy later */
7136 lcfront = lcres + (front - base);
7137 /* Now skip over each ellipsis and try to match the path in front of it. */
7139 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
7140 if (*(cp1) == '.' && *(cp1+1) == '.' &&
7141 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
7142 if (cp1 < template) break; /* template started with an ellipsis */
7143 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
7144 ellipsis = cp1; continue;
7146 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
7148 for (segdirs = 0, cp2 = tpl;
7149 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
7151 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
7153 if (!decc_efs_case_preserve) {
7154 *cp2 = _tolower(*cp1); /* else lowercase for match */
7157 *cp2 = *cp1; /* else preserve case for match */
7160 if (*cp2 == '/') segdirs++;
7162 if (cp1 != ellipsis - 1) return 0; /* Path too long */
7163 /* Back up at least as many dirs as in template before matching */
7164 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
7165 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
7166 for (match = 0; cp1 > lcres;) {
7167 resdsc.dsc$a_pointer = cp1;
7168 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
7170 if (match == 1) lcfront = cp1;
7172 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
7174 if (!match) return 0; /* Can't find prefix ??? */
7175 if (match > 1 && opts & 1) {
7176 /* This ... wildcard could cover more than one set of dirs (i.e.
7177 * a set of similar dir names is repeated). If the template
7178 * contains more than 1 ..., upstream elements could resolve the
7179 * ambiguity, but it's not worth a full backtracking setup here.
7180 * As a quick heuristic, clip off the current default directory
7181 * if it's present to find the trimmed spec, else use the
7182 * shortest string that this ... could cover.
7184 char def[NAM$C_MAXRSS+1], *st;
7186 if (getcwd(def, sizeof def,0) == NULL) return 0;
7187 if (!decc_efs_case_preserve) {
7188 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7189 if (_tolower(*cp1) != _tolower(*cp2)) break;
7191 segdirs = dirs - totells; /* Min # of dirs we must have left */
7192 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
7193 if (*cp1 == '\0' && *cp2 == '/') {
7194 memmove(fspec,cp2+1,end - cp2);
7197 /* Nope -- stick with lcfront from above and keep going. */
7200 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
7205 } /* end of trim_unixpath() */
7210 * VMS readdir() routines.
7211 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
7213 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
7214 * Minor modifications to original routines.
7217 /* readdir may have been redefined by reentr.h, so make sure we get
7218 * the local version for what we do here.
7223 #if !defined(PERL_IMPLICIT_CONTEXT)
7224 # define readdir Perl_readdir
7226 # define readdir(a) Perl_readdir(aTHX_ a)
7229 /* Number of elements in vms_versions array */
7230 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
7233 * Open a directory, return a handle for later use.
7235 /*{{{ DIR *opendir(char*name) */
7237 Perl_opendir(pTHX_ const char *name)
7240 char dir[NAM$C_MAXRSS+1];
7243 if (do_tovmspath(name,dir,0) == NULL) {
7246 /* Check access before stat; otherwise stat does not
7247 * accurately report whether it's a directory.
7249 if (!cando_by_name(S_IRUSR,0,dir)) {
7250 /* cando_by_name has already set errno */
7253 if (flex_stat(dir,&sb) == -1) return NULL;
7254 if (!S_ISDIR(sb.st_mode)) {
7255 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
7258 /* Get memory for the handle, and the pattern. */
7260 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
7262 /* Fill in the fields; mainly playing with the descriptor. */
7263 sprintf(dd->pattern, "%s*.*",dir);
7266 dd->vms_wantversions = 0;
7267 dd->pat.dsc$a_pointer = dd->pattern;
7268 dd->pat.dsc$w_length = strlen(dd->pattern);
7269 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
7270 dd->pat.dsc$b_class = DSC$K_CLASS_S;
7271 #if defined(USE_ITHREADS)
7272 Newx(dd->mutex,1,perl_mutex);
7273 MUTEX_INIT( (perl_mutex *) dd->mutex );
7279 } /* end of opendir() */
7283 * Set the flag to indicate we want versions or not.
7285 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
7287 vmsreaddirversions(MY_DIR *dd, int flag)
7289 dd->vms_wantversions = flag;
7294 * Free up an opened directory.
7296 /*{{{ void closedir(DIR *dd)*/
7298 Perl_closedir(MY_DIR *dd)
7302 sts = lib$find_file_end(&dd->context);
7303 Safefree(dd->pattern);
7304 #if defined(USE_ITHREADS)
7305 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
7306 Safefree(dd->mutex);
7313 * Collect all the version numbers for the current file.
7316 collectversions(pTHX_ MY_DIR *dd)
7318 struct dsc$descriptor_s pat;
7319 struct dsc$descriptor_s res;
7320 struct my_dirent *e;
7321 char *p, *text, buff[sizeof dd->entry.d_name];
7323 unsigned long context, tmpsts;
7325 /* Convenient shorthand. */
7328 /* Add the version wildcard, ignoring the "*.*" put on before */
7329 i = strlen(dd->pattern);
7330 Newx(text,i + e->d_namlen + 3,char);
7331 strcpy(text, dd->pattern);
7332 sprintf(&text[i - 3], "%s;*", e->d_name);
7334 /* Set up the pattern descriptor. */
7335 pat.dsc$a_pointer = text;
7336 pat.dsc$w_length = i + e->d_namlen - 1;
7337 pat.dsc$b_dtype = DSC$K_DTYPE_T;
7338 pat.dsc$b_class = DSC$K_CLASS_S;
7340 /* Set up result descriptor. */
7341 res.dsc$a_pointer = buff;
7342 res.dsc$w_length = sizeof buff - 2;
7343 res.dsc$b_dtype = DSC$K_DTYPE_T;
7344 res.dsc$b_class = DSC$K_CLASS_S;
7346 /* Read files, collecting versions. */
7347 for (context = 0, e->vms_verscount = 0;
7348 e->vms_verscount < VERSIZE(e);
7349 e->vms_verscount++) {
7350 tmpsts = lib$find_file(&pat, &res, &context);
7351 if (tmpsts == RMS$_NMF || context == 0) break;
7353 buff[sizeof buff - 1] = '\0';
7354 if ((p = strchr(buff, ';')))
7355 e->vms_versions[e->vms_verscount] = atoi(p + 1);
7357 e->vms_versions[e->vms_verscount] = -1;
7360 _ckvmssts(lib$find_file_end(&context));
7363 } /* end of collectversions() */
7366 * Read the next entry from the directory.
7368 /*{{{ struct dirent *readdir(DIR *dd)*/
7370 Perl_readdir(pTHX_ MY_DIR *dd)
7372 struct dsc$descriptor_s res;
7373 char *p, buff[sizeof dd->entry.d_name];
7374 unsigned long int tmpsts;
7376 /* Set up result descriptor, and get next file. */
7377 res.dsc$a_pointer = buff;
7378 res.dsc$w_length = sizeof buff - 2;
7379 res.dsc$b_dtype = DSC$K_DTYPE_T;
7380 res.dsc$b_class = DSC$K_CLASS_S;
7381 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
7382 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
7383 if (!(tmpsts & 1)) {
7384 set_vaxc_errno(tmpsts);
7387 set_errno(EACCES); break;
7389 set_errno(ENODEV); break;
7391 set_errno(ENOTDIR); break;
7392 case RMS$_FNF: case RMS$_DNF:
7393 set_errno(ENOENT); break;
7400 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
7401 if (!decc_efs_case_preserve) {
7402 buff[sizeof buff - 1] = '\0';
7403 for (p = buff; *p; p++) *p = _tolower(*p);
7404 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7408 /* we don't want to force to lowercase, just null terminate */
7409 buff[res.dsc$w_length] = '\0';
7411 for (p = buff; *p; p++) *p = _tolower(*p);
7412 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7415 /* Skip any directory component and just copy the name. */
7416 if ((p = strchr(buff, ']'))) strcpy(dd->entry.d_name, p + 1);
7417 else strcpy(dd->entry.d_name, buff);
7419 /* Clobber the version. */
7420 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
7422 dd->entry.d_namlen = strlen(dd->entry.d_name);
7423 dd->entry.vms_verscount = 0;
7424 if (dd->vms_wantversions) collectversions(aTHX_ dd);
7427 } /* end of readdir() */
7431 * Read the next entry from the directory -- thread-safe version.
7433 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
7435 Perl_readdir_r(pTHX_ MY_DIR *dd, struct my_dirent *entry, struct my_dirent **result)
7439 MUTEX_LOCK( (perl_mutex *) dd->mutex );
7441 entry = readdir(dd);
7443 retval = ( *result == NULL ? errno : 0 );
7445 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
7449 } /* end of readdir_r() */
7453 * Return something that can be used in a seekdir later.
7455 /*{{{ long telldir(DIR *dd)*/
7457 Perl_telldir(MY_DIR *dd)
7464 * Return to a spot where we used to be. Brute force.
7466 /*{{{ void seekdir(DIR *dd,long count)*/
7468 Perl_seekdir(pTHX_ MY_DIR *dd, long count)
7470 int vms_wantversions;
7472 /* If we haven't done anything yet... */
7476 /* Remember some state, and clear it. */
7477 vms_wantversions = dd->vms_wantversions;
7478 dd->vms_wantversions = 0;
7479 _ckvmssts(lib$find_file_end(&dd->context));
7482 /* The increment is in readdir(). */
7483 for (dd->count = 0; dd->count < count; )
7486 dd->vms_wantversions = vms_wantversions;
7488 } /* end of seekdir() */
7491 /* VMS subprocess management
7493 * my_vfork() - just a vfork(), after setting a flag to record that
7494 * the current script is trying a Unix-style fork/exec.
7496 * vms_do_aexec() and vms_do_exec() are called in response to the
7497 * perl 'exec' function. If this follows a vfork call, then they
7498 * call out the regular perl routines in doio.c which do an
7499 * execvp (for those who really want to try this under VMS).
7500 * Otherwise, they do exactly what the perl docs say exec should
7501 * do - terminate the current script and invoke a new command
7502 * (See below for notes on command syntax.)
7504 * do_aspawn() and do_spawn() implement the VMS side of the perl
7505 * 'system' function.
7507 * Note on command arguments to perl 'exec' and 'system': When handled
7508 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
7509 * are concatenated to form a DCL command string. If the first arg
7510 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
7511 * the command string is handed off to DCL directly. Otherwise,
7512 * the first token of the command is taken as the filespec of an image
7513 * to run. The filespec is expanded using a default type of '.EXE' and
7514 * the process defaults for device, directory, etc., and if found, the resultant
7515 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
7516 * the command string as parameters. This is perhaps a bit complicated,
7517 * but I hope it will form a happy medium between what VMS folks expect
7518 * from lib$spawn and what Unix folks expect from exec.
7521 static int vfork_called;
7523 /*{{{int my_vfork()*/
7534 vms_execfree(struct dsc$descriptor_s *vmscmd)
7537 if (vmscmd->dsc$a_pointer) {
7538 Safefree(vmscmd->dsc$a_pointer);
7545 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
7547 char *junk, *tmps = Nullch;
7548 register size_t cmdlen = 0;
7555 tmps = SvPV(really,rlen);
7562 for (idx++; idx <= sp; idx++) {
7564 junk = SvPVx(*idx,rlen);
7565 cmdlen += rlen ? rlen + 1 : 0;
7568 Newx(PL_Cmd,cmdlen+1,char);
7570 if (tmps && *tmps) {
7571 strcpy(PL_Cmd,tmps);
7574 else *PL_Cmd = '\0';
7575 while (++mark <= sp) {
7577 char *s = SvPVx(*mark,n_a);
7579 if (*PL_Cmd) strcat(PL_Cmd," ");
7585 } /* end of setup_argstr() */
7588 static unsigned long int
7589 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
7590 struct dsc$descriptor_s **pvmscmd)
7592 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
7593 char image_name[NAM$C_MAXRSS+1];
7594 char image_argv[NAM$C_MAXRSS+1];
7595 $DESCRIPTOR(defdsc,".EXE");
7596 $DESCRIPTOR(defdsc2,".");
7597 $DESCRIPTOR(resdsc,resspec);
7598 struct dsc$descriptor_s *vmscmd;
7599 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7600 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
7601 register char *s, *rest, *cp, *wordbreak;
7606 Newx(vmscmd,sizeof(struct dsc$descriptor_s),struct dsc$descriptor_s);
7608 /* Make a copy for modification */
7609 cmdlen = strlen(incmd);
7610 Newx(cmd, cmdlen+1, char);
7611 strncpy(cmd, incmd, cmdlen);
7616 vmscmd->dsc$a_pointer = NULL;
7617 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
7618 vmscmd->dsc$b_class = DSC$K_CLASS_S;
7619 vmscmd->dsc$w_length = 0;
7620 if (pvmscmd) *pvmscmd = vmscmd;
7622 if (suggest_quote) *suggest_quote = 0;
7624 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
7625 return CLI$_BUFOVF; /* continuation lines currently unsupported */
7631 while (*s && isspace(*s)) s++;
7633 if (*s == '@' || *s == '$') {
7634 vmsspec[0] = *s; rest = s + 1;
7635 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
7637 else { cp = vmsspec; rest = s; }
7638 if (*rest == '.' || *rest == '/') {
7641 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
7642 rest++, cp2++) *cp2 = *rest;
7644 if (do_tovmsspec(resspec,cp,0)) {
7647 for (cp2 = vmsspec + strlen(vmsspec);
7648 *rest && cp2 - vmsspec < sizeof vmsspec;
7649 rest++, cp2++) *cp2 = *rest;
7654 /* Intuit whether verb (first word of cmd) is a DCL command:
7655 * - if first nonspace char is '@', it's a DCL indirection
7657 * - if verb contains a filespec separator, it's not a DCL command
7658 * - if it doesn't, caller tells us whether to default to a DCL
7659 * command, or to a local image unless told it's DCL (by leading '$')
7663 if (suggest_quote) *suggest_quote = 1;
7665 register char *filespec = strpbrk(s,":<[.;");
7666 rest = wordbreak = strpbrk(s," \"\t/");
7667 if (!wordbreak) wordbreak = s + strlen(s);
7668 if (*s == '$') check_img = 0;
7669 if (filespec && (filespec < wordbreak)) isdcl = 0;
7670 else isdcl = !check_img;
7674 imgdsc.dsc$a_pointer = s;
7675 imgdsc.dsc$w_length = wordbreak - s;
7676 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
7678 _ckvmssts(lib$find_file_end(&cxt));
7679 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
7680 if (!(retsts & 1) && *s == '$') {
7681 _ckvmssts(lib$find_file_end(&cxt));
7682 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
7683 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
7685 _ckvmssts(lib$find_file_end(&cxt));
7686 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
7690 _ckvmssts(lib$find_file_end(&cxt));
7695 while (*s && !isspace(*s)) s++;
7698 /* check that it's really not DCL with no file extension */
7699 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
7701 char b[256] = {0,0,0,0};
7702 read(fileno(fp), b, 256);
7703 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
7707 /* Check for script */
7709 if ((b[0] == '#') && (b[1] == '!'))
7711 #ifdef ALTERNATE_SHEBANG
7713 shebang_len = strlen(ALTERNATE_SHEBANG);
7714 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
7716 perlstr = strstr("perl",b);
7717 if (perlstr == NULL)
7725 if (shebang_len > 0) {
7728 char tmpspec[NAM$C_MAXRSS + 1];
7731 /* Image is following after white space */
7732 /*--------------------------------------*/
7733 while (isprint(b[i]) && isspace(b[i]))
7737 while (isprint(b[i]) && !isspace(b[i])) {
7738 tmpspec[j++] = b[i++];
7739 if (j >= NAM$C_MAXRSS)
7744 /* There may be some default parameters to the image */
7745 /*---------------------------------------------------*/
7747 while (isprint(b[i])) {
7748 image_argv[j++] = b[i++];
7749 if (j >= NAM$C_MAXRSS)
7752 while ((j > 0) && !isprint(image_argv[j-1]))
7756 /* It will need to be converted to VMS format and validated */
7757 if (tmpspec[0] != '\0') {
7760 /* Try to find the exact program requested to be run */
7761 /*---------------------------------------------------*/
7762 iname = do_rmsexpand
7763 (tmpspec, image_name, 0, ".exe", PERL_RMSEXPAND_M_VMS);
7764 if (iname != NULL) {
7765 if (cando_by_name(S_IXUSR,0,image_name)) {
7766 /* MCR prefix needed */
7770 /* Try again with a null type */
7771 /*----------------------------*/
7772 iname = do_rmsexpand
7773 (tmpspec, image_name, 0, ".", PERL_RMSEXPAND_M_VMS);
7774 if (iname != NULL) {
7775 if (cando_by_name(S_IXUSR,0,image_name)) {
7776 /* MCR prefix needed */
7782 /* Did we find the image to run the script? */
7783 /*------------------------------------------*/
7787 /* Assume DCL or foreign command exists */
7788 /*--------------------------------------*/
7789 tchr = strrchr(tmpspec, '/');
7796 strcpy(image_name, tchr);
7804 if (check_img && isdcl) return RMS$_FNF;
7806 if (cando_by_name(S_IXUSR,0,resspec)) {
7807 Newx(vmscmd->dsc$a_pointer, MAX_DCL_LINE_LENGTH ,char);
7809 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
7810 if (image_name[0] != 0) {
7811 strcat(vmscmd->dsc$a_pointer, image_name);
7812 strcat(vmscmd->dsc$a_pointer, " ");
7814 } else if (image_name[0] != 0) {
7815 strcpy(vmscmd->dsc$a_pointer, image_name);
7816 strcat(vmscmd->dsc$a_pointer, " ");
7818 strcpy(vmscmd->dsc$a_pointer,"@");
7820 if (suggest_quote) *suggest_quote = 1;
7822 /* If there is an image name, use original command */
7823 if (image_name[0] == 0)
7824 strcat(vmscmd->dsc$a_pointer,resspec);
7827 while (*rest && isspace(*rest)) rest++;
7830 if (image_argv[0] != 0) {
7831 strcat(vmscmd->dsc$a_pointer,image_argv);
7832 strcat(vmscmd->dsc$a_pointer, " ");
7838 rest_len = strlen(rest);
7839 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
7840 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
7841 strcat(vmscmd->dsc$a_pointer,rest);
7843 retsts = CLI$_BUFOVF;
7845 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
7847 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
7849 else retsts = RMS$_PRV;
7852 /* It's either a DCL command or we couldn't find a suitable image */
7853 vmscmd->dsc$w_length = strlen(cmd);
7854 /* if (cmd == PL_Cmd) {
7855 vmscmd->dsc$a_pointer = PL_Cmd;
7856 if (suggest_quote) *suggest_quote = 1;
7859 vmscmd->dsc$a_pointer = savepvn(cmd,vmscmd->dsc$w_length);
7863 /* check if it's a symbol (for quoting purposes) */
7864 if (suggest_quote && !*suggest_quote) {
7866 char equiv[LNM$C_NAMLENGTH];
7867 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7868 eqvdsc.dsc$a_pointer = equiv;
7870 iss = lib$get_symbol(vmscmd,&eqvdsc);
7871 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
7873 if (!(retsts & 1)) {
7874 /* just hand off status values likely to be due to user error */
7875 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
7876 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
7877 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
7878 else { _ckvmssts(retsts); }
7881 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
7883 } /* end of setup_cmddsc() */
7886 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
7888 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
7891 if (vfork_called) { /* this follows a vfork - act Unixish */
7893 if (vfork_called < 0) {
7894 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
7897 else return do_aexec(really,mark,sp);
7899 /* no vfork - act VMSish */
7900 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
7905 } /* end of vms_do_aexec() */
7908 /* {{{bool vms_do_exec(char *cmd) */
7910 Perl_vms_do_exec(pTHX_ const char *cmd)
7912 struct dsc$descriptor_s *vmscmd;
7914 if (vfork_called) { /* this follows a vfork - act Unixish */
7916 if (vfork_called < 0) {
7917 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
7920 else return do_exec(cmd);
7923 { /* no vfork - act VMSish */
7924 unsigned long int retsts;
7927 TAINT_PROPER("exec");
7928 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
7929 retsts = lib$do_command(vmscmd);
7932 case RMS$_FNF: case RMS$_DNF:
7933 set_errno(ENOENT); break;
7935 set_errno(ENOTDIR); break;
7937 set_errno(ENODEV); break;
7939 set_errno(EACCES); break;
7941 set_errno(EINVAL); break;
7942 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
7943 set_errno(E2BIG); break;
7944 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
7945 _ckvmssts(retsts); /* fall through */
7946 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
7949 set_vaxc_errno(retsts);
7950 if (ckWARN(WARN_EXEC)) {
7951 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
7952 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
7954 vms_execfree(vmscmd);
7959 } /* end of vms_do_exec() */
7962 unsigned long int Perl_do_spawn(pTHX_ const char *);
7964 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
7966 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
7968 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
7971 } /* end of do_aspawn() */
7974 /* {{{unsigned long int do_spawn(char *cmd) */
7976 Perl_do_spawn(pTHX_ const char *cmd)
7978 unsigned long int sts, substs;
7981 TAINT_PROPER("spawn");
7982 if (!cmd || !*cmd) {
7983 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
7986 case RMS$_FNF: case RMS$_DNF:
7987 set_errno(ENOENT); break;
7989 set_errno(ENOTDIR); break;
7991 set_errno(ENODEV); break;
7993 set_errno(EACCES); break;
7995 set_errno(EINVAL); break;
7996 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
7997 set_errno(E2BIG); break;
7998 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
7999 _ckvmssts(sts); /* fall through */
8000 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8003 set_vaxc_errno(sts);
8004 if (ckWARN(WARN_EXEC)) {
8005 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
8013 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
8018 } /* end of do_spawn() */
8022 static unsigned int *sockflags, sockflagsize;
8025 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
8026 * routines found in some versions of the CRTL can't deal with sockets.
8027 * We don't shim the other file open routines since a socket isn't
8028 * likely to be opened by a name.
8030 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
8031 FILE *my_fdopen(int fd, const char *mode)
8033 FILE *fp = fdopen(fd, mode);
8036 unsigned int fdoff = fd / sizeof(unsigned int);
8037 Stat_t sbuf; /* native stat; we don't need flex_stat */
8038 if (!sockflagsize || fdoff > sockflagsize) {
8039 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
8040 else Newx (sockflags,fdoff+2,unsigned int);
8041 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
8042 sockflagsize = fdoff + 2;
8044 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
8045 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
8054 * Clear the corresponding bit when the (possibly) socket stream is closed.
8055 * There still a small hole: we miss an implicit close which might occur
8056 * via freopen(). >> Todo
8058 /*{{{ int my_fclose(FILE *fp)*/
8059 int my_fclose(FILE *fp) {
8061 unsigned int fd = fileno(fp);
8062 unsigned int fdoff = fd / sizeof(unsigned int);
8064 if (sockflagsize && fdoff <= sockflagsize)
8065 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
8073 * A simple fwrite replacement which outputs itmsz*nitm chars without
8074 * introducing record boundaries every itmsz chars.
8075 * We are using fputs, which depends on a terminating null. We may
8076 * well be writing binary data, so we need to accommodate not only
8077 * data with nulls sprinkled in the middle but also data with no null
8080 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
8082 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
8084 register char *cp, *end, *cpd, *data;
8085 register unsigned int fd = fileno(dest);
8086 register unsigned int fdoff = fd / sizeof(unsigned int);
8088 int bufsize = itmsz * nitm + 1;
8090 if (fdoff < sockflagsize &&
8091 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
8092 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
8096 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
8097 memcpy( data, src, itmsz*nitm );
8098 data[itmsz*nitm] = '\0';
8100 end = data + itmsz * nitm;
8101 retval = (int) nitm; /* on success return # items written */
8104 while (cpd <= end) {
8105 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
8106 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
8108 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
8112 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
8115 } /* end of my_fwrite() */
8118 /*{{{ int my_flush(FILE *fp)*/
8120 Perl_my_flush(pTHX_ FILE *fp)
8123 if ((res = fflush(fp)) == 0 && fp) {
8124 #ifdef VMS_DO_SOCKETS
8126 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
8128 res = fsync(fileno(fp));
8131 * If the flush succeeded but set end-of-file, we need to clear
8132 * the error because our caller may check ferror(). BTW, this
8133 * probably means we just flushed an empty file.
8135 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
8142 * Here are replacements for the following Unix routines in the VMS environment:
8143 * getpwuid Get information for a particular UIC or UID
8144 * getpwnam Get information for a named user
8145 * getpwent Get information for each user in the rights database
8146 * setpwent Reset search to the start of the rights database
8147 * endpwent Finish searching for users in the rights database
8149 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
8150 * (defined in pwd.h), which contains the following fields:-
8152 * char *pw_name; Username (in lower case)
8153 * char *pw_passwd; Hashed password
8154 * unsigned int pw_uid; UIC
8155 * unsigned int pw_gid; UIC group number
8156 * char *pw_unixdir; Default device/directory (VMS-style)
8157 * char *pw_gecos; Owner name
8158 * char *pw_dir; Default device/directory (Unix-style)
8159 * char *pw_shell; Default CLI name (eg. DCL)
8161 * If the specified user does not exist, getpwuid and getpwnam return NULL.
8163 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
8164 * not the UIC member number (eg. what's returned by getuid()),
8165 * getpwuid() can accept either as input (if uid is specified, the caller's
8166 * UIC group is used), though it won't recognise gid=0.
8168 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
8169 * information about other users in your group or in other groups, respectively.
8170 * If the required privilege is not available, then these routines fill only
8171 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
8174 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
8177 /* sizes of various UAF record fields */
8178 #define UAI$S_USERNAME 12
8179 #define UAI$S_IDENT 31
8180 #define UAI$S_OWNER 31
8181 #define UAI$S_DEFDEV 31
8182 #define UAI$S_DEFDIR 63
8183 #define UAI$S_DEFCLI 31
8186 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
8187 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
8188 (uic).uic$v_group != UIC$K_WILD_GROUP)
8190 static char __empty[]= "";
8191 static struct passwd __passwd_empty=
8192 {(char *) __empty, (char *) __empty, 0, 0,
8193 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
8194 static int contxt= 0;
8195 static struct passwd __pwdcache;
8196 static char __pw_namecache[UAI$S_IDENT+1];
8199 * This routine does most of the work extracting the user information.
8201 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
8204 unsigned char length;
8205 char pw_gecos[UAI$S_OWNER+1];
8207 static union uicdef uic;
8209 unsigned char length;
8210 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
8213 unsigned char length;
8214 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
8217 unsigned char length;
8218 char pw_shell[UAI$S_DEFCLI+1];
8220 static char pw_passwd[UAI$S_PWD+1];
8222 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
8223 struct dsc$descriptor_s name_desc;
8224 unsigned long int sts;
8226 static struct itmlst_3 itmlst[]= {
8227 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
8228 {sizeof(uic), UAI$_UIC, &uic, &luic},
8229 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
8230 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
8231 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
8232 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
8233 {0, 0, NULL, NULL}};
8235 name_desc.dsc$w_length= strlen(name);
8236 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8237 name_desc.dsc$b_class= DSC$K_CLASS_S;
8238 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
8240 /* Note that sys$getuai returns many fields as counted strings. */
8241 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
8242 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
8243 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
8245 else { _ckvmssts(sts); }
8246 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
8248 if ((int) owner.length < lowner) lowner= (int) owner.length;
8249 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
8250 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
8251 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
8252 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
8253 owner.pw_gecos[lowner]= '\0';
8254 defdev.pw_dir[ldefdev+ldefdir]= '\0';
8255 defcli.pw_shell[ldefcli]= '\0';
8256 if (valid_uic(uic)) {
8257 pwd->pw_uid= uic.uic$l_uic;
8258 pwd->pw_gid= uic.uic$v_group;
8261 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
8262 pwd->pw_passwd= pw_passwd;
8263 pwd->pw_gecos= owner.pw_gecos;
8264 pwd->pw_dir= defdev.pw_dir;
8265 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
8266 pwd->pw_shell= defcli.pw_shell;
8267 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
8269 ldir= strlen(pwd->pw_unixdir) - 1;
8270 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
8273 strcpy(pwd->pw_unixdir, pwd->pw_dir);
8274 if (!decc_efs_case_preserve)
8275 __mystrtolower(pwd->pw_unixdir);
8280 * Get information for a named user.
8282 /*{{{struct passwd *getpwnam(char *name)*/
8283 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
8285 struct dsc$descriptor_s name_desc;
8287 unsigned long int status, sts;
8289 __pwdcache = __passwd_empty;
8290 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
8291 /* We still may be able to determine pw_uid and pw_gid */
8292 name_desc.dsc$w_length= strlen(name);
8293 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8294 name_desc.dsc$b_class= DSC$K_CLASS_S;
8295 name_desc.dsc$a_pointer= (char *) name;
8296 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
8297 __pwdcache.pw_uid= uic.uic$l_uic;
8298 __pwdcache.pw_gid= uic.uic$v_group;
8301 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
8302 set_vaxc_errno(sts);
8303 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
8306 else { _ckvmssts(sts); }
8309 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
8310 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
8311 __pwdcache.pw_name= __pw_namecache;
8313 } /* end of my_getpwnam() */
8317 * Get information for a particular UIC or UID.
8318 * Called by my_getpwent with uid=-1 to list all users.
8320 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
8321 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
8323 const $DESCRIPTOR(name_desc,__pw_namecache);
8324 unsigned short lname;
8326 unsigned long int status;
8328 if (uid == (unsigned int) -1) {
8330 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
8331 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
8332 set_vaxc_errno(status);
8333 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8337 else { _ckvmssts(status); }
8338 } while (!valid_uic (uic));
8342 if (!uic.uic$v_group)
8343 uic.uic$v_group= PerlProc_getgid();
8345 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
8346 else status = SS$_IVIDENT;
8347 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
8348 status == RMS$_PRV) {
8349 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8352 else { _ckvmssts(status); }
8354 __pw_namecache[lname]= '\0';
8355 __mystrtolower(__pw_namecache);
8357 __pwdcache = __passwd_empty;
8358 __pwdcache.pw_name = __pw_namecache;
8360 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
8361 The identifier's value is usually the UIC, but it doesn't have to be,
8362 so if we can, we let fillpasswd update this. */
8363 __pwdcache.pw_uid = uic.uic$l_uic;
8364 __pwdcache.pw_gid = uic.uic$v_group;
8366 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
8369 } /* end of my_getpwuid() */
8373 * Get information for next user.
8375 /*{{{struct passwd *my_getpwent()*/
8376 struct passwd *Perl_my_getpwent(pTHX)
8378 return (my_getpwuid((unsigned int) -1));
8383 * Finish searching rights database for users.
8385 /*{{{void my_endpwent()*/
8386 void Perl_my_endpwent(pTHX)
8389 _ckvmssts(sys$finish_rdb(&contxt));
8395 #ifdef HOMEGROWN_POSIX_SIGNALS
8396 /* Signal handling routines, pulled into the core from POSIX.xs.
8398 * We need these for threads, so they've been rolled into the core,
8399 * rather than left in POSIX.xs.
8401 * (DRS, Oct 23, 1997)
8404 /* sigset_t is atomic under VMS, so these routines are easy */
8405 /*{{{int my_sigemptyset(sigset_t *) */
8406 int my_sigemptyset(sigset_t *set) {
8407 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8413 /*{{{int my_sigfillset(sigset_t *)*/
8414 int my_sigfillset(sigset_t *set) {
8416 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8417 for (i = 0; i < NSIG; i++) *set |= (1 << i);
8423 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
8424 int my_sigaddset(sigset_t *set, int sig) {
8425 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8426 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8427 *set |= (1 << (sig - 1));
8433 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
8434 int my_sigdelset(sigset_t *set, int sig) {
8435 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8436 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8437 *set &= ~(1 << (sig - 1));
8443 /*{{{int my_sigismember(sigset_t *set, int sig)*/
8444 int my_sigismember(sigset_t *set, int sig) {
8445 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
8446 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
8447 return *set & (1 << (sig - 1));
8452 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
8453 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
8456 /* If set and oset are both null, then things are badly wrong. Bail out. */
8457 if ((oset == NULL) && (set == NULL)) {
8458 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
8462 /* If set's null, then we're just handling a fetch. */
8464 tempmask = sigblock(0);
8469 tempmask = sigsetmask(*set);
8472 tempmask = sigblock(*set);
8475 tempmask = sigblock(0);
8476 sigsetmask(*oset & ~tempmask);
8479 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8484 /* Did they pass us an oset? If so, stick our holding mask into it */
8491 #endif /* HOMEGROWN_POSIX_SIGNALS */
8494 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
8495 * my_utime(), and flex_stat(), all of which operate on UTC unless
8496 * VMSISH_TIMES is true.
8498 /* method used to handle UTC conversions:
8499 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
8501 static int gmtime_emulation_type;
8502 /* number of secs to add to UTC POSIX-style time to get local time */
8503 static long int utc_offset_secs;
8505 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
8506 * in vmsish.h. #undef them here so we can call the CRTL routines
8515 * DEC C previous to 6.0 corrupts the behavior of the /prefix
8516 * qualifier with the extern prefix pragma. This provisional
8517 * hack circumvents this prefix pragma problem in previous
8520 #if defined(__VMS_VER) && __VMS_VER >= 70000000
8521 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
8522 # pragma __extern_prefix save
8523 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
8524 # define gmtime decc$__utctz_gmtime
8525 # define localtime decc$__utctz_localtime
8526 # define time decc$__utc_time
8527 # pragma __extern_prefix restore
8529 struct tm *gmtime(), *localtime();
8535 static time_t toutc_dst(time_t loc) {
8538 if ((rsltmp = localtime(&loc)) == NULL) return -1;
8539 loc -= utc_offset_secs;
8540 if (rsltmp->tm_isdst) loc -= 3600;
8543 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
8544 ((gmtime_emulation_type || my_time(NULL)), \
8545 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
8546 ((secs) - utc_offset_secs))))
8548 static time_t toloc_dst(time_t utc) {
8551 utc += utc_offset_secs;
8552 if ((rsltmp = localtime(&utc)) == NULL) return -1;
8553 if (rsltmp->tm_isdst) utc += 3600;
8556 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
8557 ((gmtime_emulation_type || my_time(NULL)), \
8558 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
8559 ((secs) + utc_offset_secs))))
8561 #ifndef RTL_USES_UTC
8564 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
8565 DST starts on 1st sun of april at 02:00 std time
8566 ends on last sun of october at 02:00 dst time
8567 see the UCX management command reference, SET CONFIG TIMEZONE
8568 for formatting info.
8570 No, it's not as general as it should be, but then again, NOTHING
8571 will handle UK times in a sensible way.
8576 parse the DST start/end info:
8577 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
8581 tz_parse_startend(char *s, struct tm *w, int *past)
8583 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
8584 int ly, dozjd, d, m, n, hour, min, sec, j, k;
8589 if (!past) return 0;
8592 if (w->tm_year % 4 == 0) ly = 1;
8593 if (w->tm_year % 100 == 0) ly = 0;
8594 if (w->tm_year+1900 % 400 == 0) ly = 1;
8597 dozjd = isdigit(*s);
8598 if (*s == 'J' || *s == 'j' || dozjd) {
8599 if (!dozjd && !isdigit(*++s)) return 0;
8602 d = d*10 + *s++ - '0';
8604 d = d*10 + *s++ - '0';
8607 if (d == 0) return 0;
8608 if (d > 366) return 0;
8610 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
8613 } else if (*s == 'M' || *s == 'm') {
8614 if (!isdigit(*++s)) return 0;
8616 if (isdigit(*s)) m = 10*m + *s++ - '0';
8617 if (*s != '.') return 0;
8618 if (!isdigit(*++s)) return 0;
8620 if (n < 1 || n > 5) return 0;
8621 if (*s != '.') return 0;
8622 if (!isdigit(*++s)) return 0;
8624 if (d > 6) return 0;
8628 if (!isdigit(*++s)) return 0;
8630 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
8632 if (!isdigit(*++s)) return 0;
8634 if (isdigit(*s)) min = 10*min + *s++ - '0';
8636 if (!isdigit(*++s)) return 0;
8638 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
8648 if (w->tm_yday < d) goto before;
8649 if (w->tm_yday > d) goto after;
8651 if (w->tm_mon+1 < m) goto before;
8652 if (w->tm_mon+1 > m) goto after;
8654 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
8655 k = d - j; /* mday of first d */
8657 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
8658 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
8659 if (w->tm_mday < k) goto before;
8660 if (w->tm_mday > k) goto after;
8663 if (w->tm_hour < hour) goto before;
8664 if (w->tm_hour > hour) goto after;
8665 if (w->tm_min < min) goto before;
8666 if (w->tm_min > min) goto after;
8667 if (w->tm_sec < sec) goto before;
8681 /* parse the offset: (+|-)hh[:mm[:ss]] */
8684 tz_parse_offset(char *s, int *offset)
8686 int hour = 0, min = 0, sec = 0;
8689 if (!offset) return 0;
8691 if (*s == '-') {neg++; s++;}
8693 if (!isdigit(*s)) return 0;
8695 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
8696 if (hour > 24) return 0;
8698 if (!isdigit(*++s)) return 0;
8700 if (isdigit(*s)) min = min*10 + (*s++ - '0');
8701 if (min > 59) return 0;
8703 if (!isdigit(*++s)) return 0;
8705 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
8706 if (sec > 59) return 0;
8710 *offset = (hour*60+min)*60 + sec;
8711 if (neg) *offset = -*offset;
8716 input time is w, whatever type of time the CRTL localtime() uses.
8717 sets dst, the zone, and the gmtoff (seconds)
8719 caches the value of TZ and UCX$TZ env variables; note that
8720 my_setenv looks for these and sets a flag if they're changed
8723 We have to watch out for the "australian" case (dst starts in
8724 october, ends in april)...flagged by "reverse" and checked by
8725 scanning through the months of the previous year.
8730 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
8735 char *dstzone, *tz, *s_start, *s_end;
8736 int std_off, dst_off, isdst;
8737 int y, dststart, dstend;
8738 static char envtz[1025]; /* longer than any logical, symbol, ... */
8739 static char ucxtz[1025];
8740 static char reversed = 0;
8746 reversed = -1; /* flag need to check */
8747 envtz[0] = ucxtz[0] = '\0';
8748 tz = my_getenv("TZ",0);
8749 if (tz) strcpy(envtz, tz);
8750 tz = my_getenv("UCX$TZ",0);
8751 if (tz) strcpy(ucxtz, tz);
8752 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
8755 if (!*tz) tz = ucxtz;
8758 while (isalpha(*s)) s++;
8759 s = tz_parse_offset(s, &std_off);
8761 if (!*s) { /* no DST, hurray we're done! */
8767 while (isalpha(*s)) s++;
8768 s2 = tz_parse_offset(s, &dst_off);
8772 dst_off = std_off - 3600;
8775 if (!*s) { /* default dst start/end?? */
8776 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
8777 s = strchr(ucxtz,',');
8779 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
8781 if (*s != ',') return 0;
8784 when = _toutc(when); /* convert to utc */
8785 when = when - std_off; /* convert to pseudolocal time*/
8787 w2 = localtime(&when);
8790 s = tz_parse_startend(s_start,w2,&dststart);
8792 if (*s != ',') return 0;
8795 when = _toutc(when); /* convert to utc */
8796 when = when - dst_off; /* convert to pseudolocal time*/
8797 w2 = localtime(&when);
8798 if (w2->tm_year != y) { /* spans a year, just check one time */
8799 when += dst_off - std_off;
8800 w2 = localtime(&when);
8803 s = tz_parse_startend(s_end,w2,&dstend);
8806 if (reversed == -1) { /* need to check if start later than end */
8810 if (when < 2*365*86400) {
8811 when += 2*365*86400;
8815 w2 =localtime(&when);
8816 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
8818 for (j = 0; j < 12; j++) {
8819 w2 =localtime(&when);
8820 tz_parse_startend(s_start,w2,&ds);
8821 tz_parse_startend(s_end,w2,&de);
8822 if (ds != de) break;
8826 if (de && !ds) reversed = 1;
8829 isdst = dststart && !dstend;
8830 if (reversed) isdst = dststart || !dstend;
8833 if (dst) *dst = isdst;
8834 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
8835 if (isdst) tz = dstzone;
8837 while(isalpha(*tz)) *zone++ = *tz++;
8843 #endif /* !RTL_USES_UTC */
8845 /* my_time(), my_localtime(), my_gmtime()
8846 * By default traffic in UTC time values, using CRTL gmtime() or
8847 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
8848 * Note: We need to use these functions even when the CRTL has working
8849 * UTC support, since they also handle C<use vmsish qw(times);>
8851 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
8852 * Modified by Charles Bailey <bailey@newman.upenn.edu>
8855 /*{{{time_t my_time(time_t *timep)*/
8856 time_t Perl_my_time(pTHX_ time_t *timep)
8861 if (gmtime_emulation_type == 0) {
8863 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
8864 /* results of calls to gmtime() and localtime() */
8865 /* for same &base */
8867 gmtime_emulation_type++;
8868 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
8869 char off[LNM$C_NAMLENGTH+1];;
8871 gmtime_emulation_type++;
8872 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
8873 gmtime_emulation_type++;
8874 utc_offset_secs = 0;
8875 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
8877 else { utc_offset_secs = atol(off); }
8879 else { /* We've got a working gmtime() */
8880 struct tm gmt, local;
8883 tm_p = localtime(&base);
8885 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
8886 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
8887 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
8888 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
8894 # ifdef RTL_USES_UTC
8895 if (VMSISH_TIME) when = _toloc(when);
8897 if (!VMSISH_TIME) when = _toutc(when);
8900 if (timep != NULL) *timep = when;
8903 } /* end of my_time() */
8907 /*{{{struct tm *my_gmtime(const time_t *timep)*/
8909 Perl_my_gmtime(pTHX_ const time_t *timep)
8915 if (timep == NULL) {
8916 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8919 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
8923 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
8925 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
8926 return gmtime(&when);
8928 /* CRTL localtime() wants local time as input, so does no tz correction */
8929 rsltmp = localtime(&when);
8930 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
8933 } /* end of my_gmtime() */
8937 /*{{{struct tm *my_localtime(const time_t *timep)*/
8939 Perl_my_localtime(pTHX_ const time_t *timep)
8941 time_t when, whenutc;
8945 if (timep == NULL) {
8946 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
8949 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
8950 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
8953 # ifdef RTL_USES_UTC
8955 if (VMSISH_TIME) when = _toutc(when);
8957 /* CRTL localtime() wants UTC as input, does tz correction itself */
8958 return localtime(&when);
8960 # else /* !RTL_USES_UTC */
8963 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
8964 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
8967 #ifndef RTL_USES_UTC
8968 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
8969 when = whenutc - offset; /* pseudolocal time*/
8972 /* CRTL localtime() wants local time as input, so does no tz correction */
8973 rsltmp = localtime(&when);
8974 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
8978 } /* end of my_localtime() */
8981 /* Reset definitions for later calls */
8982 #define gmtime(t) my_gmtime(t)
8983 #define localtime(t) my_localtime(t)
8984 #define time(t) my_time(t)
8987 /* my_utime - update modification time of a file
8988 * calling sequence is identical to POSIX utime(), but under
8989 * VMS only the modification time is changed; ODS-2 does not
8990 * maintain access times. Restrictions differ from the POSIX
8991 * definition in that the time can be changed as long as the
8992 * caller has permission to execute the necessary IO$_MODIFY $QIO;
8993 * no separate checks are made to insure that the caller is the
8994 * owner of the file or has special privs enabled.
8995 * Code here is based on Joe Meadows' FILE utility.
8998 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
8999 * to VMS epoch (01-JAN-1858 00:00:00.00)
9000 * in 100 ns intervals.
9002 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
9004 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
9005 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
9009 long int bintime[2], len = 2, lowbit, unixtime,
9010 secscale = 10000000; /* seconds --> 100 ns intervals */
9011 unsigned long int chan, iosb[2], retsts;
9012 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
9013 struct FAB myfab = cc$rms_fab;
9014 struct NAM mynam = cc$rms_nam;
9015 #if defined (__DECC) && defined (__VAX)
9016 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
9017 * at least through VMS V6.1, which causes a type-conversion warning.
9019 # pragma message save
9020 # pragma message disable cvtdiftypes
9022 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
9023 struct fibdef myfib;
9024 #if defined (__DECC) && defined (__VAX)
9025 /* This should be right after the declaration of myatr, but due
9026 * to a bug in VAX DEC C, this takes effect a statement early.
9028 # pragma message restore
9030 /* cast ok for read only parameter */
9031 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
9032 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
9033 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
9035 if (file == NULL || *file == '\0') {
9037 set_vaxc_errno(LIB$_INVARG);
9040 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
9042 if (utimes != NULL) {
9043 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
9044 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
9045 * Since time_t is unsigned long int, and lib$emul takes a signed long int
9046 * as input, we force the sign bit to be clear by shifting unixtime right
9047 * one bit, then multiplying by an extra factor of 2 in lib$emul().
9049 lowbit = (utimes->modtime & 1) ? secscale : 0;
9050 unixtime = (long int) utimes->modtime;
9052 /* If input was UTC; convert to local for sys svc */
9053 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
9055 unixtime >>= 1; secscale <<= 1;
9056 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
9057 if (!(retsts & 1)) {
9059 set_vaxc_errno(retsts);
9062 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
9063 if (!(retsts & 1)) {
9065 set_vaxc_errno(retsts);
9070 /* Just get the current time in VMS format directly */
9071 retsts = sys$gettim(bintime);
9072 if (!(retsts & 1)) {
9074 set_vaxc_errno(retsts);
9079 myfab.fab$l_fna = vmsspec;
9080 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
9081 myfab.fab$l_nam = &mynam;
9082 mynam.nam$l_esa = esa;
9083 mynam.nam$b_ess = (unsigned char) sizeof esa;
9084 mynam.nam$l_rsa = rsa;
9085 mynam.nam$b_rss = (unsigned char) sizeof rsa;
9086 if (decc_efs_case_preserve)
9087 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9089 /* Look for the file to be affected, letting RMS parse the file
9090 * specification for us as well. I have set errno using only
9091 * values documented in the utime() man page for VMS POSIX.
9093 retsts = sys$parse(&myfab,0,0);
9094 if (!(retsts & 1)) {
9095 set_vaxc_errno(retsts);
9096 if (retsts == RMS$_PRV) set_errno(EACCES);
9097 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
9098 else set_errno(EVMSERR);
9101 retsts = sys$search(&myfab,0,0);
9102 if (!(retsts & 1)) {
9103 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9104 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9105 set_vaxc_errno(retsts);
9106 if (retsts == RMS$_PRV) set_errno(EACCES);
9107 else if (retsts == RMS$_FNF) set_errno(ENOENT);
9108 else set_errno(EVMSERR);
9112 devdsc.dsc$w_length = mynam.nam$b_dev;
9113 /* cast ok for read only parameter */
9114 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
9116 retsts = sys$assign(&devdsc,&chan,0,0);
9117 if (!(retsts & 1)) {
9118 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9119 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9120 set_vaxc_errno(retsts);
9121 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
9122 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
9123 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
9124 else set_errno(EVMSERR);
9128 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
9129 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
9131 memset((void *) &myfib, 0, sizeof myfib);
9132 #if defined(__DECC) || defined(__DECCXX)
9133 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
9134 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
9135 /* This prevents the revision time of the file being reset to the current
9136 * time as a result of our IO$_MODIFY $QIO. */
9137 myfib.fib$l_acctl = FIB$M_NORECORD;
9139 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
9140 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
9141 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
9143 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
9144 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9145 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9146 _ckvmssts(sys$dassgn(chan));
9147 if (retsts & 1) retsts = iosb[0];
9148 if (!(retsts & 1)) {
9149 set_vaxc_errno(retsts);
9150 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9151 else set_errno(EVMSERR);
9156 } /* end of my_utime() */
9160 * flex_stat, flex_lstat, flex_fstat
9161 * basic stat, but gets it right when asked to stat
9162 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
9165 #ifndef _USE_STD_STAT
9166 /* encode_dev packs a VMS device name string into an integer to allow
9167 * simple comparisons. This can be used, for example, to check whether two
9168 * files are located on the same device, by comparing their encoded device
9169 * names. Even a string comparison would not do, because stat() reuses the
9170 * device name buffer for each call; so without encode_dev, it would be
9171 * necessary to save the buffer and use strcmp (this would mean a number of
9172 * changes to the standard Perl code, to say nothing of what a Perl script
9175 * The device lock id, if it exists, should be unique (unless perhaps compared
9176 * with lock ids transferred from other nodes). We have a lock id if the disk is
9177 * mounted cluster-wide, which is when we tend to get long (host-qualified)
9178 * device names. Thus we use the lock id in preference, and only if that isn't
9179 * available, do we try to pack the device name into an integer (flagged by
9180 * the sign bit (LOCKID_MASK) being set).
9182 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
9183 * name and its encoded form, but it seems very unlikely that we will find
9184 * two files on different disks that share the same encoded device names,
9185 * and even more remote that they will share the same file id (if the test
9186 * is to check for the same file).
9188 * A better method might be to use sys$device_scan on the first call, and to
9189 * search for the device, returning an index into the cached array.
9190 * The number returned would be more intelligable.
9191 * This is probably not worth it, and anyway would take quite a bit longer
9192 * on the first call.
9194 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
9195 static mydev_t encode_dev (pTHX_ const char *dev)
9198 unsigned long int f;
9203 if (!dev || !dev[0]) return 0;
9207 struct dsc$descriptor_s dev_desc;
9208 unsigned long int status, lockid, item = DVI$_LOCKID;
9210 /* For cluster-mounted disks, the disk lock identifier is unique, so we
9211 can try that first. */
9212 dev_desc.dsc$w_length = strlen (dev);
9213 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
9214 dev_desc.dsc$b_class = DSC$K_CLASS_S;
9215 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
9216 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
9217 if (lockid) return (lockid & ~LOCKID_MASK);
9221 /* Otherwise we try to encode the device name */
9225 for (q = dev + strlen(dev); q--; q >= dev) {
9228 else if (isalpha (toupper (*q)))
9229 c= toupper (*q) - 'A' + (char)10;
9231 continue; /* Skip '$'s */
9233 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
9235 enc += f * (unsigned long int) c;
9237 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
9239 } /* end of encode_dev() */
9242 static char namecache[NAM$C_MAXRSS+1];
9245 is_null_device(name)
9248 if (decc_bug_devnull != 0) {
9249 if (strcmp("/dev/null", name) == 0) /* temp hack */
9252 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
9253 The underscore prefix, controller letter, and unit number are
9254 independently optional; for our purposes, the colon punctuation
9255 is not. The colon can be trailed by optional directory and/or
9256 filename, but two consecutive colons indicates a nodename rather
9257 than a device. [pr] */
9258 if (*name == '_') ++name;
9259 if (tolower(*name++) != 'n') return 0;
9260 if (tolower(*name++) != 'l') return 0;
9261 if (tolower(*name) == 'a') ++name;
9262 if (*name == '0') ++name;
9263 return (*name++ == ':') && (*name != ':');
9266 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
9267 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
9268 * subset of the applicable information.
9271 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
9273 char fname_phdev[NAM$C_MAXRSS+1];
9274 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9275 /* Namecache not workable with symbolic links, as symbolic links do
9276 * not have extensions and directories do in VMS mode. So in order
9277 * to test this, the did and ino_t must be used.
9279 * Fix-me - Hide the information in the new stat structure
9280 * Get rid of the namecache.
9282 if (decc_posix_compliant_pathnames == 0)
9284 if (statbufp == &PL_statcache)
9285 return cando_by_name(bit,effective,namecache);
9287 char fname[NAM$C_MAXRSS+1];
9288 unsigned long int retsts;
9289 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
9290 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9292 /* If the struct mystat is stale, we're OOL; stat() overwrites the
9293 device name on successive calls */
9294 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
9295 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
9296 namdsc.dsc$a_pointer = fname;
9297 namdsc.dsc$w_length = sizeof fname - 1;
9299 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
9300 &namdsc,&namdsc.dsc$w_length,0,0);
9302 fname[namdsc.dsc$w_length] = '\0';
9304 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
9305 * but if someone has redefined that logical, Perl gets very lost. Since
9306 * we have the physical device name from the stat buffer, just paste it on.
9308 strcpy( fname_phdev, statbufp->st_devnam );
9309 strcat( fname_phdev, strrchr(fname, ':') );
9311 return cando_by_name(bit,effective,fname_phdev);
9313 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
9314 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
9318 return FALSE; /* Should never get to here */
9320 } /* end of cando() */
9324 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
9326 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
9328 static char usrname[L_cuserid];
9329 static struct dsc$descriptor_s usrdsc =
9330 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
9331 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
9332 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
9333 unsigned short int retlen, trnlnm_iter_count;
9334 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9335 union prvdef curprv;
9336 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
9337 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
9338 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
9339 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
9341 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
9343 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9345 if (!fname || !*fname) return FALSE;
9346 /* Make sure we expand logical names, since sys$check_access doesn't */
9347 if (!strpbrk(fname,"/]>:")) {
9348 strcpy(fileified,fname);
9349 trnlnm_iter_count = 0;
9350 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
9351 trnlnm_iter_count++;
9352 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
9356 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
9357 retlen = namdsc.dsc$w_length = strlen(vmsname);
9358 namdsc.dsc$a_pointer = vmsname;
9359 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
9360 vmsname[retlen-1] == ':') {
9361 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
9362 namdsc.dsc$w_length = strlen(fileified);
9363 namdsc.dsc$a_pointer = fileified;
9367 case S_IXUSR: case S_IXGRP: case S_IXOTH:
9368 access = ARM$M_EXECUTE; break;
9369 case S_IRUSR: case S_IRGRP: case S_IROTH:
9370 access = ARM$M_READ; break;
9371 case S_IWUSR: case S_IWGRP: case S_IWOTH:
9372 access = ARM$M_WRITE; break;
9373 case S_IDUSR: case S_IDGRP: case S_IDOTH:
9374 access = ARM$M_DELETE; break;
9379 /* Before we call $check_access, create a user profile with the current
9380 * process privs since otherwise it just uses the default privs from the
9381 * UAF and might give false positives or negatives. This only works on
9382 * VMS versions v6.0 and later since that's when sys$create_user_profile
9386 /* get current process privs and username */
9387 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
9390 #if defined(__VMS_VER) && __VMS_VER >= 60000000
9392 /* find out the space required for the profile */
9393 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
9394 &usrprodsc.dsc$w_length,0));
9396 /* allocate space for the profile and get it filled in */
9397 Newx(usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
9398 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
9399 &usrprodsc.dsc$w_length,0));
9401 /* use the profile to check access to the file; free profile & analyze results */
9402 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
9403 Safefree(usrprodsc.dsc$a_pointer);
9404 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
9408 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
9412 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
9413 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
9414 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
9415 set_vaxc_errno(retsts);
9416 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9417 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
9418 else set_errno(ENOENT);
9421 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
9426 return FALSE; /* Should never get here */
9428 } /* end of cando_by_name() */
9432 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
9434 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
9436 if (!fstat(fd,(stat_t *) statbufp)) {
9437 if (statbufp == (Stat_t *) &PL_statcache) {
9440 /* Save name for cando by name in VMS format */
9441 cptr = getname(fd, namecache, 1);
9443 /* This should not happen, but just in case */
9445 namecache[0] = '\0';
9447 #ifdef _USE_STD_STAT
9448 memcpy(&statbufp->st_ino, &statbufp->crtl_stat.st_ino, 8);
9450 memcpy(&statbufp->st_ino, statbufp->crtl_stat.st_ino, 8);
9452 #ifndef _USE_STD_STAT
9453 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
9454 statbufp->st_devnam[63] = 0;
9455 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
9458 * The device is only encoded so that Perl_cando can use it to
9459 * look up ACLS. So rmsexpand it to the 255 character version
9460 * and store it in ->st_devnam. rmsexpand needs to be fixed
9461 * for long filenames and symbolic links first. This also seems
9462 * to remove the need for a namecache that could be stale.
9466 # ifdef RTL_USES_UTC
9469 statbufp->st_mtime = _toloc(statbufp->st_mtime);
9470 statbufp->st_atime = _toloc(statbufp->st_atime);
9471 statbufp->st_ctime = _toloc(statbufp->st_ctime);
9476 if (!VMSISH_TIME) { /* Return UTC instead of local time */
9480 statbufp->st_mtime = _toutc(statbufp->st_mtime);
9481 statbufp->st_atime = _toutc(statbufp->st_atime);
9482 statbufp->st_ctime = _toutc(statbufp->st_ctime);
9489 } /* end of flex_fstat() */
9492 #if !defined(__VAX) && __CRTL_VER >= 80200000
9500 #define lstat(_x, _y) stat(_x, _y)
9503 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
9506 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
9508 char fileified[NAM$C_MAXRSS+1];
9509 char temp_fspec[NAM$C_MAXRSS+300];
9511 int saved_errno, saved_vaxc_errno;
9513 if (!fspec) return retval;
9514 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
9515 strcpy(temp_fspec, fspec);
9516 if (statbufp == (Stat_t *) &PL_statcache)
9517 do_tovmsspec(temp_fspec,namecache,0);
9518 if (decc_bug_devnull != 0) {
9519 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
9520 memset(statbufp,0,sizeof *statbufp);
9521 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
9522 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
9523 statbufp->st_uid = 0x00010001;
9524 statbufp->st_gid = 0x0001;
9525 time((time_t *)&statbufp->st_mtime);
9526 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
9531 /* Try for a directory name first. If fspec contains a filename without
9532 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
9533 * and sea:[wine.dark]water. exist, we prefer the directory here.
9534 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
9535 * not sea:[wine.dark]., if the latter exists. If the intended target is
9536 * the file with null type, specify this by calling flex_stat() with
9537 * a '.' at the end of fspec.
9539 * If we are in Posix filespec mode, accept the filename as is.
9541 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9542 if (decc_posix_compliant_pathnames == 0) {
9544 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
9545 if (lstat_flag == 0)
9546 retval = stat(fileified,(stat_t *) statbufp);
9548 retval = lstat(fileified,(stat_t *) statbufp);
9549 if (!retval && statbufp == (Stat_t *) &PL_statcache)
9550 strcpy(namecache,fileified);
9553 if (lstat_flag == 0)
9554 retval = stat(temp_fspec,(stat_t *) statbufp);
9556 retval = lstat(temp_fspec,(stat_t *) statbufp);
9558 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9560 if (lstat_flag == 0)
9561 retval = stat(temp_fspec,(stat_t *) statbufp);
9563 retval = lstat(temp_fspec,(stat_t *) statbufp);
9567 #ifdef _USE_STD_STAT
9568 memcpy(&statbufp->st_ino, &statbufp->crtl_stat.st_ino, 8);
9570 memcpy(&statbufp->st_ino, statbufp->crtl_stat.st_ino, 8);
9572 #ifndef _USE_STD_STAT
9573 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
9574 statbufp->st_devnam[63] = 0;
9575 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
9578 * The device is only encoded so that Perl_cando can use it to
9579 * look up ACLS. So rmsexpand it to the 255 character version
9580 * and store it in ->st_devnam. rmsexpand needs to be fixed
9581 * for long filenames and symbolic links first. This also seems
9582 * to remove the need for a namecache that could be stale.
9585 # ifdef RTL_USES_UTC
9588 statbufp->st_mtime = _toloc(statbufp->st_mtime);
9589 statbufp->st_atime = _toloc(statbufp->st_atime);
9590 statbufp->st_ctime = _toloc(statbufp->st_ctime);
9595 if (!VMSISH_TIME) { /* Return UTC instead of local time */
9599 statbufp->st_mtime = _toutc(statbufp->st_mtime);
9600 statbufp->st_atime = _toutc(statbufp->st_atime);
9601 statbufp->st_ctime = _toutc(statbufp->st_ctime);
9605 /* If we were successful, leave errno where we found it */
9606 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
9609 } /* end of flex_stat_int() */
9612 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
9614 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
9616 return flex_stat_int(fspec, statbufp, 0);
9620 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
9622 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
9624 return flex_stat_int(fspec, statbufp, 1);
9629 /*{{{char *my_getlogin()*/
9630 /* VMS cuserid == Unix getlogin, except calling sequence */
9634 static char user[L_cuserid];
9635 return cuserid(user);
9640 /* rmscopy - copy a file using VMS RMS routines
9642 * Copies contents and attributes of spec_in to spec_out, except owner
9643 * and protection information. Name and type of spec_in are used as
9644 * defaults for spec_out. The third parameter specifies whether rmscopy()
9645 * should try to propagate timestamps from the input file to the output file.
9646 * If it is less than 0, no timestamps are preserved. If it is 0, then
9647 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
9648 * propagated to the output file at creation iff the output file specification
9649 * did not contain an explicit name or type, and the revision date is always
9650 * updated at the end of the copy operation. If it is greater than 0, then
9651 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
9652 * other than the revision date should be propagated, and bit 1 indicates
9653 * that the revision date should be propagated.
9655 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
9657 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
9658 * Incorporates, with permission, some code from EZCOPY by Tim Adye
9659 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
9660 * as part of the Perl standard distribution under the terms of the
9661 * GNU General Public License or the Perl Artistic License. Copies
9662 * of each may be found in the Perl standard distribution.
9664 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
9666 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
9668 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
9669 rsa[NAM$C_MAXRSS], ubf[32256];
9670 unsigned long int i, sts, sts2;
9671 struct FAB fab_in, fab_out;
9672 struct RAB rab_in, rab_out;
9674 struct XABDAT xabdat;
9675 struct XABFHC xabfhc;
9676 struct XABRDT xabrdt;
9677 struct XABSUM xabsum;
9679 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
9680 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
9681 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9685 fab_in = cc$rms_fab;
9686 fab_in.fab$l_fna = vmsin;
9687 fab_in.fab$b_fns = strlen(vmsin);
9688 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
9689 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
9690 fab_in.fab$l_fop = FAB$M_SQO;
9691 fab_in.fab$l_nam = &nam;
9692 fab_in.fab$l_xab = (void *) &xabdat;
9695 nam.nam$l_rsa = rsa;
9696 nam.nam$b_rss = sizeof(rsa);
9697 nam.nam$l_esa = esa;
9698 nam.nam$b_ess = sizeof (esa);
9699 nam.nam$b_esl = nam.nam$b_rsl = 0;
9700 #ifdef NAM$M_NO_SHORT_UPCASE
9701 if (decc_efs_case_preserve)
9702 nam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9705 xabdat = cc$rms_xabdat; /* To get creation date */
9706 xabdat.xab$l_nxt = (void *) &xabfhc;
9708 xabfhc = cc$rms_xabfhc; /* To get record length */
9709 xabfhc.xab$l_nxt = (void *) &xabsum;
9711 xabsum = cc$rms_xabsum; /* To get key and area information */
9713 if (!((sts = sys$open(&fab_in)) & 1)) {
9714 set_vaxc_errno(sts);
9716 case RMS$_FNF: case RMS$_DNF:
9717 set_errno(ENOENT); break;
9719 set_errno(ENOTDIR); break;
9721 set_errno(ENODEV); break;
9723 set_errno(EINVAL); break;
9725 set_errno(EACCES); break;
9733 fab_out.fab$w_ifi = 0;
9734 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
9735 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
9736 fab_out.fab$l_fop = FAB$M_SQO;
9737 fab_out.fab$l_fna = vmsout;
9738 fab_out.fab$b_fns = strlen(vmsout);
9739 fab_out.fab$l_dna = nam.nam$l_name;
9740 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
9742 if (preserve_dates == 0) { /* Act like DCL COPY */
9743 nam.nam$b_nop |= NAM$M_SYNCHK;
9744 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
9745 if (!((sts = sys$parse(&fab_out)) & 1)) {
9746 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
9747 set_vaxc_errno(sts);
9750 fab_out.fab$l_xab = (void *) &xabdat;
9751 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
9753 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
9754 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
9755 preserve_dates =0; /* bitmask from this point forward */
9757 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
9758 if (!((sts = sys$create(&fab_out)) & 1)) {
9759 set_vaxc_errno(sts);
9762 set_errno(ENOENT); break;
9764 set_errno(ENOTDIR); break;
9766 set_errno(ENODEV); break;
9768 set_errno(EINVAL); break;
9770 set_errno(EACCES); break;
9776 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
9777 if (preserve_dates & 2) {
9778 /* sys$close() will process xabrdt, not xabdat */
9779 xabrdt = cc$rms_xabrdt;
9781 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
9783 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
9784 * is unsigned long[2], while DECC & VAXC use a struct */
9785 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
9787 fab_out.fab$l_xab = (void *) &xabrdt;
9790 rab_in = cc$rms_rab;
9791 rab_in.rab$l_fab = &fab_in;
9792 rab_in.rab$l_rop = RAB$M_BIO;
9793 rab_in.rab$l_ubf = ubf;
9794 rab_in.rab$w_usz = sizeof ubf;
9795 if (!((sts = sys$connect(&rab_in)) & 1)) {
9796 sys$close(&fab_in); sys$close(&fab_out);
9797 set_errno(EVMSERR); set_vaxc_errno(sts);
9801 rab_out = cc$rms_rab;
9802 rab_out.rab$l_fab = &fab_out;
9803 rab_out.rab$l_rbf = ubf;
9804 if (!((sts = sys$connect(&rab_out)) & 1)) {
9805 sys$close(&fab_in); sys$close(&fab_out);
9806 set_errno(EVMSERR); set_vaxc_errno(sts);
9810 while ((sts = sys$read(&rab_in))) { /* always true */
9811 if (sts == RMS$_EOF) break;
9812 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
9813 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
9814 sys$close(&fab_in); sys$close(&fab_out);
9815 set_errno(EVMSERR); set_vaxc_errno(sts);
9820 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
9821 sys$close(&fab_in); sys$close(&fab_out);
9822 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
9824 set_errno(EVMSERR); set_vaxc_errno(sts);
9830 } /* end of rmscopy() */
9834 /*** The following glue provides 'hooks' to make some of the routines
9835 * from this file available from Perl. These routines are sufficiently
9836 * basic, and are required sufficiently early in the build process,
9837 * that's it's nice to have them available to miniperl as well as the
9838 * full Perl, so they're set up here instead of in an extension. The
9839 * Perl code which handles importation of these names into a given
9840 * package lives in [.VMS]Filespec.pm in @INC.
9844 rmsexpand_fromperl(pTHX_ CV *cv)
9847 char *fspec, *defspec = NULL, *rslt;
9850 if (!items || items > 2)
9851 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
9852 fspec = SvPV(ST(0),n_a);
9853 if (!fspec || !*fspec) XSRETURN_UNDEF;
9854 if (items == 2) defspec = SvPV(ST(1),n_a);
9856 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
9857 ST(0) = sv_newmortal();
9858 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
9863 vmsify_fromperl(pTHX_ CV *cv)
9869 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
9870 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
9871 ST(0) = sv_newmortal();
9872 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
9877 unixify_fromperl(pTHX_ CV *cv)
9883 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
9884 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
9885 ST(0) = sv_newmortal();
9886 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
9891 fileify_fromperl(pTHX_ CV *cv)
9897 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
9898 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
9899 ST(0) = sv_newmortal();
9900 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
9905 pathify_fromperl(pTHX_ CV *cv)
9911 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
9912 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
9913 ST(0) = sv_newmortal();
9914 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
9919 vmspath_fromperl(pTHX_ CV *cv)
9925 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
9926 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
9927 ST(0) = sv_newmortal();
9928 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
9933 unixpath_fromperl(pTHX_ CV *cv)
9939 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
9940 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
9941 ST(0) = sv_newmortal();
9942 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
9947 candelete_fromperl(pTHX_ CV *cv)
9950 char fspec[NAM$C_MAXRSS+1], *fsp;
9955 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
9957 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
9958 if (SvTYPE(mysv) == SVt_PVGV) {
9959 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
9960 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9967 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
9968 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9974 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
9979 rmscopy_fromperl(pTHX_ CV *cv)
9982 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
9984 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
9985 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9986 unsigned long int sts;
9991 if (items < 2 || items > 3)
9992 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
9994 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
9995 if (SvTYPE(mysv) == SVt_PVGV) {
9996 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
9997 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10004 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
10005 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10010 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
10011 if (SvTYPE(mysv) == SVt_PVGV) {
10012 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
10013 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10020 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
10021 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10026 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
10028 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
10034 mod2fname(pTHX_ CV *cv)
10037 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
10038 workbuff[NAM$C_MAXRSS*1 + 1];
10039 int total_namelen = 3, counter, num_entries;
10040 /* ODS-5 ups this, but we want to be consistent, so... */
10041 int max_name_len = 39;
10042 AV *in_array = (AV *)SvRV(ST(0));
10044 num_entries = av_len(in_array);
10046 /* All the names start with PL_. */
10047 strcpy(ultimate_name, "PL_");
10049 /* Clean up our working buffer */
10050 Zero(work_name, sizeof(work_name), char);
10052 /* Run through the entries and build up a working name */
10053 for(counter = 0; counter <= num_entries; counter++) {
10054 /* If it's not the first name then tack on a __ */
10056 strcat(work_name, "__");
10058 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
10062 /* Check to see if we actually have to bother...*/
10063 if (strlen(work_name) + 3 <= max_name_len) {
10064 strcat(ultimate_name, work_name);
10066 /* It's too darned big, so we need to go strip. We use the same */
10067 /* algorithm as xsubpp does. First, strip out doubled __ */
10068 char *source, *dest, last;
10071 for (source = work_name; *source; source++) {
10072 if (last == *source && last == '_') {
10078 /* Go put it back */
10079 strcpy(work_name, workbuff);
10080 /* Is it still too big? */
10081 if (strlen(work_name) + 3 > max_name_len) {
10082 /* Strip duplicate letters */
10085 for (source = work_name; *source; source++) {
10086 if (last == toupper(*source)) {
10090 last = toupper(*source);
10092 strcpy(work_name, workbuff);
10095 /* Is it *still* too big? */
10096 if (strlen(work_name) + 3 > max_name_len) {
10097 /* Too bad, we truncate */
10098 work_name[max_name_len - 2] = 0;
10100 strcat(ultimate_name, work_name);
10103 /* Okay, return it */
10104 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
10109 hushexit_fromperl(pTHX_ CV *cv)
10114 VMSISH_HUSHED = SvTRUE(ST(0));
10116 ST(0) = boolSV(VMSISH_HUSHED);
10122 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec);
10125 vms_realpath_fromperl(pTHX_ CV *cv)
10128 char *fspec, *rslt_spec, *rslt;
10131 if (!items || items != 1)
10132 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
10134 fspec = SvPV(ST(0),n_a);
10135 if (!fspec || !*fspec) XSRETURN_UNDEF;
10137 Newx(rslt_spec, VMS_MAXRSS + 1, char);
10138 rslt = do_vms_realpath(fspec, rslt_spec);
10139 ST(0) = sv_newmortal();
10141 sv_usepvn(ST(0),rslt,strlen(rslt));
10143 Safefree(rslt_spec);
10148 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10149 int do_vms_case_tolerant(void);
10152 vms_case_tolerant_fromperl(pTHX_ CV *cv)
10155 ST(0) = boolSV(do_vms_case_tolerant());
10161 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
10162 struct interp_intern *dst)
10164 memcpy(dst,src,sizeof(struct interp_intern));
10168 Perl_sys_intern_clear(pTHX)
10173 Perl_sys_intern_init(pTHX)
10175 unsigned int ix = RAND_MAX;
10180 /* fix me later to track running under GNV */
10181 /* this allows some limited testing */
10182 MY_POSIX_EXIT = decc_filename_unix_report;
10185 MY_INV_RAND_MAX = 1./x;
10189 init_os_extras(void)
10192 char* file = __FILE__;
10193 char temp_buff[512];
10194 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
10195 no_translate_barewords = TRUE;
10197 no_translate_barewords = FALSE;
10200 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
10201 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
10202 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
10203 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
10204 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
10205 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
10206 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
10207 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
10208 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
10209 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
10210 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
10212 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
10214 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10215 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
10218 store_pipelocs(aTHX); /* will redo any earlier attempts */
10225 #if __CRTL_VER == 80200000
10226 /* This missed getting in to the DECC SDK for 8.2 */
10227 char *realpath(const char *file_name, char * resolved_name, ...);
10230 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
10231 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
10232 * The perl fallback routine to provide realpath() is not as efficient
10236 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10238 return realpath(filespec, outbuf);
10242 /* External entry points */
10243 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10244 { return do_vms_realpath(filespec, outbuf); }
10246 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
10251 #if __CRTL_VER >= 70301000 && !defined(__VAX)
10252 /* case_tolerant */
10254 /*{{{int do_vms_case_tolerant(void)*/
10255 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
10256 * controlled by a process setting.
10258 int do_vms_case_tolerant(void)
10260 return vms_process_case_tolerant;
10263 /* External entry points */
10264 int Perl_vms_case_tolerant(void)
10265 { return do_vms_case_tolerant(); }
10267 int Perl_vms_case_tolerant(void)
10268 { return vms_process_case_tolerant; }
10272 /* Start of DECC RTL Feature handling */
10274 static int sys_trnlnm
10275 (const char * logname,
10279 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
10280 const unsigned long attr = LNM$M_CASE_BLIND;
10281 struct dsc$descriptor_s name_dsc;
10283 unsigned short result;
10284 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
10287 name_dsc.dsc$w_length = strlen(logname);
10288 name_dsc.dsc$a_pointer = (char *)logname;
10289 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10290 name_dsc.dsc$b_class = DSC$K_CLASS_S;
10292 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
10294 if ($VMS_STATUS_SUCCESS(status)) {
10296 /* Null terminate and return the string */
10297 /*--------------------------------------*/
10304 static int sys_crelnm
10305 (const char * logname,
10306 const char * value)
10309 const char * proc_table = "LNM$PROCESS_TABLE";
10310 struct dsc$descriptor_s proc_table_dsc;
10311 struct dsc$descriptor_s logname_dsc;
10312 struct itmlst_3 item_list[2];
10314 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
10315 proc_table_dsc.dsc$w_length = strlen(proc_table);
10316 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10317 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
10319 logname_dsc.dsc$a_pointer = (char *) logname;
10320 logname_dsc.dsc$w_length = strlen(logname);
10321 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
10322 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
10324 item_list[0].buflen = strlen(value);
10325 item_list[0].itmcode = LNM$_STRING;
10326 item_list[0].bufadr = (char *)value;
10327 item_list[0].retlen = NULL;
10329 item_list[1].buflen = 0;
10330 item_list[1].itmcode = 0;
10332 ret_val = sys$crelnm
10334 (const struct dsc$descriptor_s *)&proc_table_dsc,
10335 (const struct dsc$descriptor_s *)&logname_dsc,
10337 (const struct item_list_3 *) item_list);
10343 /* C RTL Feature settings */
10345 static int set_features
10346 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
10347 int (* cli_routine)(void), /* Not documented */
10348 void *image_info) /* Not documented */
10355 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
10356 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
10357 unsigned long case_perm;
10358 unsigned long case_image;
10360 /* hacks to see if known bugs are still present for testing */
10362 /* Readdir is returning filenames in VMS syntax always */
10363 decc_bug_readdir_efs1 = 1;
10364 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
10365 if ($VMS_STATUS_SUCCESS(status)) {
10366 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10367 decc_bug_readdir_efs1 = 1;
10369 decc_bug_readdir_efs1 = 0;
10372 /* PCP mode requires creating /dev/null special device file */
10373 decc_bug_devnull = 0;
10374 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
10375 if ($VMS_STATUS_SUCCESS(status)) {
10376 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10377 decc_bug_devnull = 1;
10380 /* fgetname returning a VMS name in UNIX mode */
10381 decc_bug_fgetname = 1;
10382 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
10383 if ($VMS_STATUS_SUCCESS(status)) {
10384 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10385 decc_bug_fgetname = 1;
10387 decc_bug_fgetname = 0;
10390 /* UNIX directory names with no paths are broken in a lot of places */
10391 decc_dir_barename = 1;
10392 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
10393 if ($VMS_STATUS_SUCCESS(status)) {
10394 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
10395 decc_dir_barename = 1;
10397 decc_dir_barename = 0;
10400 #if __CRTL_VER >= 70300000 && !defined(__VAX)
10401 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
10403 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
10404 if (decc_disable_to_vms_logname_translation < 0)
10405 decc_disable_to_vms_logname_translation = 0;
10408 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
10410 decc_efs_case_preserve = decc$feature_get_value(s, 1);
10411 if (decc_efs_case_preserve < 0)
10412 decc_efs_case_preserve = 0;
10415 s = decc$feature_get_index("DECC$EFS_CHARSET");
10417 decc_efs_charset = decc$feature_get_value(s, 1);
10418 if (decc_efs_charset < 0)
10419 decc_efs_charset = 0;
10422 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
10424 decc_filename_unix_report = decc$feature_get_value(s, 1);
10425 if (decc_filename_unix_report > 0)
10426 decc_filename_unix_report = 1;
10428 decc_filename_unix_report = 0;
10431 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
10433 decc_filename_unix_only = decc$feature_get_value(s, 1);
10434 if (decc_filename_unix_only > 0) {
10435 decc_filename_unix_only = 1;
10438 decc_filename_unix_only = 0;
10442 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
10444 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
10445 if (decc_filename_unix_no_version < 0)
10446 decc_filename_unix_no_version = 0;
10449 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
10451 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
10452 if (decc_readdir_dropdotnotype < 0)
10453 decc_readdir_dropdotnotype = 0;
10456 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
10457 if ($VMS_STATUS_SUCCESS(status)) {
10458 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
10460 dflt = decc$feature_get_value(s, 4);
10462 decc_disable_posix_root = decc$feature_get_value(s, 1);
10463 if (decc_disable_posix_root <= 0) {
10464 decc$feature_set_value(s, 1, 1);
10465 decc_disable_posix_root = 1;
10469 /* Traditionally Perl assumes this is off */
10470 decc_disable_posix_root = 1;
10471 decc$feature_set_value(s, 1, 1);
10476 #if __CRTL_VER >= 80200000
10477 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
10479 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
10480 if (decc_posix_compliant_pathnames < 0)
10481 decc_posix_compliant_pathnames = 0;
10482 if (decc_posix_compliant_pathnames > 4)
10483 decc_posix_compliant_pathnames = 0;
10488 status = sys_trnlnm
10489 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
10490 if ($VMS_STATUS_SUCCESS(status)) {
10491 val_str[0] = _toupper(val_str[0]);
10492 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10493 decc_disable_to_vms_logname_translation = 1;
10498 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
10499 if ($VMS_STATUS_SUCCESS(status)) {
10500 val_str[0] = _toupper(val_str[0]);
10501 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10502 decc_efs_case_preserve = 1;
10507 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
10508 if ($VMS_STATUS_SUCCESS(status)) {
10509 val_str[0] = _toupper(val_str[0]);
10510 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10511 decc_filename_unix_report = 1;
10514 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
10515 if ($VMS_STATUS_SUCCESS(status)) {
10516 val_str[0] = _toupper(val_str[0]);
10517 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10518 decc_filename_unix_only = 1;
10519 decc_filename_unix_report = 1;
10522 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
10523 if ($VMS_STATUS_SUCCESS(status)) {
10524 val_str[0] = _toupper(val_str[0]);
10525 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10526 decc_filename_unix_no_version = 1;
10529 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
10530 if ($VMS_STATUS_SUCCESS(status)) {
10531 val_str[0] = _toupper(val_str[0]);
10532 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
10533 decc_readdir_dropdotnotype = 1;
10540 /* Report true case tolerance */
10541 /*----------------------------*/
10542 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
10543 if (!$VMS_STATUS_SUCCESS(status))
10544 case_perm = PPROP$K_CASE_BLIND;
10545 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
10546 if (!$VMS_STATUS_SUCCESS(status))
10547 case_image = PPROP$K_CASE_BLIND;
10548 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
10549 (case_image == PPROP$K_CASE_SENSITIVE))
10550 vms_process_case_tolerant = 0;
10555 /* CRTL can be initialized past this point, but not before. */
10556 /* DECC$CRTL_INIT(); */
10562 /* DECC dependent attributes */
10563 #if __DECC_VER < 60560002
10565 #define not_executable
10567 #define relative ,rel
10568 #define not_executable ,noexe
10571 #pragma extern_model save
10572 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
10574 const __align (LONGWORD) int spare[8] = {0};
10575 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
10578 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
10579 nowrt,noshr relative not_executable
10581 const long vms_cc_features = (const long)set_features;
10584 ** Force a reference to LIB$INITIALIZE to ensure it
10585 ** exists in the image.
10587 int lib$initialize(void);
10589 #pragma extern_model strict_refdef
10591 int lib_init_ref = (int) lib$initialize;
10594 #pragma extern_model restore