3 * VMS-specific routines for perl5
6 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
7 * and Perl_cando by Craig Berry
8 * 29-Aug-2000 Charles Lane's piping improvements rolled in
9 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
18 #include <climsgdef.h>
28 #include <libclidef.h>
30 #include <lib$routines.h>
40 #include <str$routines.h>
45 /* Older versions of ssdef.h don't have these */
46 #ifndef SS$_INVFILFOROP
47 # define SS$_INVFILFOROP 3930
49 #ifndef SS$_NOSUCHOBJECT
50 # define SS$_NOSUCHOBJECT 2696
53 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
54 #define PERLIO_NOT_STDIO 0
56 /* Don't replace system definitions of vfork, getenv, and stat,
57 * code below needs to get to the underlying CRTL routines. */
58 #define DONT_MASK_RTL_CALLS
62 /* Anticipating future expansion in lexical warnings . . . */
64 # define WARN_INTERNAL WARN_MISC
67 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
68 # define RTL_USES_UTC 1
72 /* gcc's header files don't #define direct access macros
73 * corresponding to VAXC's variant structs */
75 # define uic$v_format uic$r_uic_form.uic$v_format
76 # define uic$v_group uic$r_uic_form.uic$v_group
77 # define uic$v_member uic$r_uic_form.uic$v_member
78 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
79 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
80 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
81 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
84 #if defined(NEED_AN_H_ERRNO)
89 unsigned short int buflen;
90 unsigned short int itmcode;
92 unsigned short int *retlen;
95 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
96 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
97 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
98 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
99 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
100 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
101 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
102 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
103 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
105 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
106 #define PERL_LNM_MAX_ALLOWED_INDEX 127
108 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
109 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
112 #define PERL_LNM_MAX_ITER 10
114 #define MAX_DCL_SYMBOL 255 /* well, what *we* can set, at least*/
115 #define MAX_DCL_LINE_LENGTH (4*MAX_DCL_SYMBOL-4)
117 static char *__mystrtolower(char *str)
119 if (str) for (; *str; ++str) *str= tolower(*str);
123 static struct dsc$descriptor_s fildevdsc =
124 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
125 static struct dsc$descriptor_s crtlenvdsc =
126 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
127 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
128 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
129 static struct dsc$descriptor_s **env_tables = defenv;
130 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
132 /* True if we shouldn't treat barewords as logicals during directory */
134 static int no_translate_barewords;
137 static int tz_updated = 1;
141 * Routine to retrieve the maximum equivalence index for an input
142 * logical name. Some calls to this routine have no knowledge if
143 * the variable is a logical or not. So on error we return a max
146 /*{{{int my_maxidx(char *lnm) */
152 int attr = LNM$M_CASE_BLIND;
153 struct dsc$descriptor lnmdsc;
154 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
157 lnmdsc.dsc$w_length = strlen(lnm);
158 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
159 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
160 lnmdsc.dsc$a_pointer = lnm;
162 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
163 if ((status & 1) == 0)
170 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
172 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
173 struct dsc$descriptor_s **tabvec, unsigned long int flags)
175 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
176 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
177 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
179 unsigned char acmode;
180 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
181 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
182 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
183 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
185 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
186 #if defined(PERL_IMPLICIT_CONTEXT)
189 aTHX = PERL_GET_INTERP;
195 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
196 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
198 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
199 *cp2 = _toupper(*cp1);
200 if (cp1 - lnm > LNM$C_NAMLENGTH) {
201 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
205 lnmdsc.dsc$w_length = cp1 - lnm;
206 lnmdsc.dsc$a_pointer = uplnm;
207 uplnm[lnmdsc.dsc$w_length] = '\0';
208 secure = flags & PERL__TRNENV_SECURE;
209 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
210 if (!tabvec || !*tabvec) tabvec = env_tables;
212 for (curtab = 0; tabvec[curtab]; curtab++) {
213 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
214 if (!ivenv && !secure) {
219 Perl_warn(aTHX_ "Can't read CRTL environ\n");
222 retsts = SS$_NOLOGNAM;
223 for (i = 0; environ[i]; i++) {
224 if ((eq = strchr(environ[i],'=')) &&
225 !strncmp(environ[i],uplnm,eq - environ[i])) {
227 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
228 if (!eqvlen) continue;
233 if (retsts != SS$_NOLOGNAM) break;
236 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
237 !str$case_blind_compare(&tmpdsc,&clisym)) {
238 if (!ivsym && !secure) {
239 unsigned short int deflen = LNM$C_NAMLENGTH;
240 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
241 /* dynamic dsc to accomodate possible long value */
242 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
243 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
246 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
248 /* Special hack--we might be called before the interpreter's */
249 /* fully initialized, in which case either thr or PL_curcop */
250 /* might be bogus. We have to check, since ckWARN needs them */
251 /* both to be valid if running threaded */
252 if (ckWARN(WARN_MISC)) {
253 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
256 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
258 _ckvmssts(lib$sfree1_dd(&eqvdsc));
259 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
260 if (retsts == LIB$_NOSUCHSYM) continue;
265 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
266 midx = my_maxidx((char *) lnm);
267 for (idx = 0, cp1 = eqv; idx <= midx; idx++) {
268 lnmlst[1].bufadr = cp1;
270 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
271 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
272 if (retsts == SS$_NOLOGNAM) break;
273 /* PPFs have a prefix */
276 *((int *)uplnm) == *((int *)"SYS$") &&
278 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
279 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
280 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
281 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
282 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
283 memcpy(eqv,eqv+4,eqvlen-4);
289 if ((retsts == SS$_IVLOGNAM) ||
290 (retsts == SS$_NOLOGNAM)) { continue; }
293 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
294 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
295 if (retsts == SS$_NOLOGNAM) continue;
298 eqvlen = strlen(eqv);
302 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
303 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
304 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
305 retsts == SS$_NOLOGNAM) {
306 set_errno(EINVAL); set_vaxc_errno(retsts);
308 else _ckvmssts(retsts);
310 } /* end of vmstrnenv */
313 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
314 /* Define as a function so we can access statics. */
315 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
317 return vmstrnenv(lnm,eqv,idx,fildev,
318 #ifdef SECURE_INTERNAL_GETENV
319 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
328 * Note: Uses Perl temp to store result so char * can be returned to
329 * caller; this pointer will be invalidated at next Perl statement
331 * We define this as a function rather than a macro in terms of my_getenv_len()
332 * so that it'll work when PL_curinterp is undefined (and we therefore can't
335 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
337 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
339 static char *__my_getenv_eqv = NULL;
340 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
341 unsigned long int idx = 0;
342 int trnsuccess, success, secure, saverr, savvmserr;
346 midx = my_maxidx((char *) lnm) + 1;
348 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
349 /* Set up a temporary buffer for the return value; Perl will
350 * clean it up at the next statement transition */
351 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
352 if (!tmpsv) return NULL;
356 /* Assume no interpreter ==> single thread */
357 if (__my_getenv_eqv != NULL) {
358 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
361 New(1380,__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
363 eqv = __my_getenv_eqv;
366 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
367 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
368 getcwd(eqv,LNM$C_NAMLENGTH);
372 /* Impose security constraints only if tainting */
374 /* Impose security constraints only if tainting */
375 secure = PL_curinterp ? PL_tainting : will_taint;
376 saverr = errno; savvmserr = vaxc$errno;
383 #ifdef SECURE_INTERNAL_GETENV
384 secure ? PERL__TRNENV_SECURE : 0
390 /* For the getenv interface we combine all the equivalence names
391 * of a search list logical into one value to acquire a maximum
392 * value length of 255*128 (assuming %ENV is using logicals).
394 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
396 /* If the name contains a semicolon-delimited index, parse it
397 * off and make sure we only retrieve the equivalence name for
399 if ((cp2 = strchr(lnm,';')) != NULL) {
401 uplnm[cp2-lnm] = '\0';
402 idx = strtoul(cp2+1,NULL,0);
404 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
407 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
409 /* Discard NOLOGNAM on internal calls since we're often looking
410 * for an optional name, and this "error" often shows up as the
411 * (bogus) exit status for a die() call later on. */
412 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
413 return success ? eqv : Nullch;
416 } /* end of my_getenv() */
420 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
422 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
424 char *buf, *cp1, *cp2;
425 unsigned long idx = 0;
427 static char *__my_getenv_len_eqv = NULL;
428 int secure, saverr, savvmserr;
431 midx = my_maxidx((char *) lnm) + 1;
433 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
434 /* Set up a temporary buffer for the return value; Perl will
435 * clean it up at the next statement transition */
436 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
437 if (!tmpsv) return NULL;
441 /* Assume no interpreter ==> single thread */
442 if (__my_getenv_len_eqv != NULL) {
443 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
446 New(1381,__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
448 buf = __my_getenv_len_eqv;
451 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
452 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
453 getcwd(buf,LNM$C_NAMLENGTH);
459 /* Impose security constraints only if tainting */
460 secure = PL_curinterp ? PL_tainting : will_taint;
461 saverr = errno; savvmserr = vaxc$errno;
468 #ifdef SECURE_INTERNAL_GETENV
469 secure ? PERL__TRNENV_SECURE : 0
475 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
477 if ((cp2 = strchr(lnm,';')) != NULL) {
480 idx = strtoul(cp2+1,NULL,0);
482 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
485 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
487 /* Discard NOLOGNAM on internal calls since we're often looking
488 * for an optional name, and this "error" often shows up as the
489 * (bogus) exit status for a die() call later on. */
490 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
491 return *len ? buf : Nullch;
494 } /* end of my_getenv_len() */
497 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
499 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
501 /*{{{ void prime_env_iter() */
504 /* Fill the %ENV associative array with all logical names we can
505 * find, in preparation for iterating over it.
508 static int primed = 0;
509 HV *seenhv = NULL, *envhv;
511 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
512 unsigned short int chan;
513 #ifndef CLI$M_TRUSTED
514 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
516 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
517 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
519 bool have_sym = FALSE, have_lnm = FALSE;
520 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
521 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
522 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
523 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
524 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
525 #if defined(PERL_IMPLICIT_CONTEXT)
528 #if defined(USE_ITHREADS)
529 static perl_mutex primenv_mutex;
530 MUTEX_INIT(&primenv_mutex);
533 #if defined(PERL_IMPLICIT_CONTEXT)
534 /* We jump through these hoops because we can be called at */
535 /* platform-specific initialization time, which is before anything is */
536 /* set up--we can't even do a plain dTHX since that relies on the */
537 /* interpreter structure to be initialized */
539 aTHX = PERL_GET_INTERP;
545 if (primed || !PL_envgv) return;
546 MUTEX_LOCK(&primenv_mutex);
547 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
548 envhv = GvHVn(PL_envgv);
549 /* Perform a dummy fetch as an lval to insure that the hash table is
550 * set up. Otherwise, the hv_store() will turn into a nullop. */
551 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
553 for (i = 0; env_tables[i]; i++) {
554 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
555 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
556 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
558 if (have_sym || have_lnm) {
559 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
560 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
561 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
562 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
565 for (i--; i >= 0; i--) {
566 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
569 for (j = 0; environ[j]; j++) {
570 if (!(start = strchr(environ[j],'='))) {
571 if (ckWARN(WARN_INTERNAL))
572 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
576 sv = newSVpv(start,0);
578 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
583 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
584 !str$case_blind_compare(&tmpdsc,&clisym)) {
585 strcpy(cmd,"Show Symbol/Global *");
586 cmddsc.dsc$w_length = 20;
587 if (env_tables[i]->dsc$w_length == 12 &&
588 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
589 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
590 flags = defflags | CLI$M_NOLOGNAM;
593 strcpy(cmd,"Show Logical *");
594 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
595 strcat(cmd," /Table=");
596 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
597 cmddsc.dsc$w_length = strlen(cmd);
599 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
600 flags = defflags | CLI$M_NOCLISYM;
603 /* Create a new subprocess to execute each command, to exclude the
604 * remote possibility that someone could subvert a mbx or file used
605 * to write multiple commands to a single subprocess.
608 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
609 0,&riseandshine,0,0,&clidsc,&clitabdsc);
610 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
611 defflags &= ~CLI$M_TRUSTED;
612 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
614 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
615 if (seenhv) SvREFCNT_dec(seenhv);
618 char *cp1, *cp2, *key;
619 unsigned long int sts, iosb[2], retlen, keylen;
622 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
623 if (sts & 1) sts = iosb[0] & 0xffff;
624 if (sts == SS$_ENDOFFILE) {
626 while (substs == 0) { sys$hiber(); wakect++;}
627 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
632 retlen = iosb[0] >> 16;
633 if (!retlen) continue; /* blank line */
635 if (iosb[1] != subpid) {
637 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
641 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
642 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
644 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
645 if (*cp1 == '(' || /* Logical name table name */
646 *cp1 == '=' /* Next eqv of searchlist */) continue;
647 if (*cp1 == '"') cp1++;
648 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
649 key = cp1; keylen = cp2 - cp1;
650 if (keylen && hv_exists(seenhv,key,keylen)) continue;
651 while (*cp2 && *cp2 != '=') cp2++;
652 while (*cp2 && *cp2 == '=') cp2++;
653 while (*cp2 && *cp2 == ' ') cp2++;
654 if (*cp2 == '"') { /* String translation; may embed "" */
655 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
656 cp2++; cp1--; /* Skip "" surrounding translation */
658 else { /* Numeric translation */
659 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
660 cp1--; /* stop on last non-space char */
662 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
663 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
666 PERL_HASH(hash,key,keylen);
667 sv = newSVpvn(cp2,cp1 - cp2 + 1);
669 hv_store(envhv,key,keylen,sv,hash);
670 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
672 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
673 /* get the PPFs for this process, not the subprocess */
674 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
675 char eqv[LNM$C_NAMLENGTH+1];
677 for (i = 0; ppfs[i]; i++) {
678 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
679 sv = newSVpv(eqv,trnlen);
681 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
686 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
687 if (buf) Safefree(buf);
688 if (seenhv) SvREFCNT_dec(seenhv);
689 MUTEX_UNLOCK(&primenv_mutex);
692 } /* end of prime_env_iter */
696 /*{{{ int vmssetenv(char *lnm, char *eqv)*/
697 /* Define or delete an element in the same "environment" as
698 * vmstrnenv(). If an element is to be deleted, it's removed from
699 * the first place it's found. If it's to be set, it's set in the
700 * place designated by the first element of the table vector.
701 * Like setenv() returns 0 for success, non-zero on error.
704 Perl_vmssetenv(pTHX_ char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
706 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2, *c;
707 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
709 unsigned long int retsts, usermode = PSL$C_USER;
710 struct itmlst_3 *ile, *ilist;
711 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
712 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
713 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
714 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
715 $DESCRIPTOR(local,"_LOCAL");
718 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
722 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
723 *cp2 = _toupper(*cp1);
724 if (cp1 - lnm > LNM$C_NAMLENGTH) {
725 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
729 lnmdsc.dsc$w_length = cp1 - lnm;
730 if (!tabvec || !*tabvec) tabvec = env_tables;
732 if (!eqv) { /* we're deleting n element */
733 for (curtab = 0; tabvec[curtab]; curtab++) {
734 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
736 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
737 if ((cp1 = strchr(environ[i],'=')) &&
738 !strncmp(environ[i],lnm,cp1 - environ[i])) {
740 return setenv(lnm,"",1) ? vaxc$errno : 0;
743 ivenv = 1; retsts = SS$_NOLOGNAM;
745 if (ckWARN(WARN_INTERNAL))
746 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
747 ivenv = 1; retsts = SS$_NOSUCHPGM;
753 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
754 !str$case_blind_compare(&tmpdsc,&clisym)) {
755 unsigned int symtype;
756 if (tabvec[curtab]->dsc$w_length == 12 &&
757 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
758 !str$case_blind_compare(&tmpdsc,&local))
759 symtype = LIB$K_CLI_LOCAL_SYM;
760 else symtype = LIB$K_CLI_GLOBAL_SYM;
761 retsts = lib$delete_symbol(&lnmdsc,&symtype);
762 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
763 if (retsts == LIB$_NOSUCHSYM) continue;
767 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
768 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
769 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
770 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
771 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
775 else { /* we're defining a value */
776 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
778 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
780 if (ckWARN(WARN_INTERNAL))
781 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
782 retsts = SS$_NOSUCHPGM;
786 eqvdsc.dsc$a_pointer = eqv;
787 eqvdsc.dsc$w_length = strlen(eqv);
788 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
789 !str$case_blind_compare(&tmpdsc,&clisym)) {
790 unsigned int symtype;
791 if (tabvec[0]->dsc$w_length == 12 &&
792 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
793 !str$case_blind_compare(&tmpdsc,&local))
794 symtype = LIB$K_CLI_LOCAL_SYM;
795 else symtype = LIB$K_CLI_GLOBAL_SYM;
796 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
799 if (!*eqv) eqvdsc.dsc$w_length = 1;
800 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
802 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
803 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
804 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
805 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
806 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
807 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
810 New(1382,ilist,nseg+1,struct itmlst_3);
813 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
816 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
818 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
819 ile->itmcode = LNM$_STRING;
822 ile->buflen = strlen(c);
823 /* in case we are truncating one that's too long */
824 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
827 ile->buflen = LNM$C_NAMLENGTH;
831 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
835 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
842 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
843 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
844 set_errno(EVMSERR); break;
845 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
846 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
847 set_errno(EINVAL); break;
854 set_vaxc_errno(retsts);
855 return (int) retsts || 44; /* retsts should never be 0, but just in case */
858 /* We reset error values on success because Perl does an hv_fetch()
859 * before each hv_store(), and if the thing we're setting didn't
860 * previously exist, we've got a leftover error message. (Of course,
861 * this fails in the face of
862 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
863 * in that the error reported in $! isn't spurious,
864 * but it's right more often than not.)
866 set_errno(0); set_vaxc_errno(retsts);
870 } /* end of vmssetenv() */
873 /*{{{ void my_setenv(char *lnm, char *eqv)*/
874 /* This has to be a function since there's a prototype for it in proto.h */
876 Perl_my_setenv(pTHX_ char *lnm,char *eqv)
879 int len = strlen(lnm);
883 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
884 if (!strcmp(uplnm,"DEFAULT")) {
885 if (eqv && *eqv) chdir(eqv);
890 if (len == 6 || len == 2) {
893 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
895 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
896 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
900 (void) vmssetenv(lnm,eqv,NULL);
904 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
906 * sets a user-mode logical in the process logical name table
907 * used for redirection of sys$error
910 Perl_vmssetuserlnm(pTHX_ char *name, char *eqv)
912 $DESCRIPTOR(d_tab, "LNM$PROCESS");
913 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
914 unsigned long int iss, attr = LNM$M_CONFINE;
915 unsigned char acmode = PSL$C_USER;
916 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
918 d_name.dsc$a_pointer = name;
919 d_name.dsc$w_length = strlen(name);
921 lnmlst[0].buflen = strlen(eqv);
922 lnmlst[0].bufadr = eqv;
924 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
925 if (!(iss&1)) lib$signal(iss);
930 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
931 /* my_crypt - VMS password hashing
932 * my_crypt() provides an interface compatible with the Unix crypt()
933 * C library function, and uses sys$hash_password() to perform VMS
934 * password hashing. The quadword hashed password value is returned
935 * as a NUL-terminated 8 character string. my_crypt() does not change
936 * the case of its string arguments; in order to match the behavior
937 * of LOGINOUT et al., alphabetic characters in both arguments must
938 * be upcased by the caller.
941 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
943 # ifndef UAI$C_PREFERRED_ALGORITHM
944 # define UAI$C_PREFERRED_ALGORITHM 127
946 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
947 unsigned short int salt = 0;
948 unsigned long int sts;
950 unsigned short int dsc$w_length;
951 unsigned char dsc$b_type;
952 unsigned char dsc$b_class;
953 const char * dsc$a_pointer;
954 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
955 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
956 struct itmlst_3 uailst[3] = {
957 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
958 { sizeof salt, UAI$_SALT, &salt, 0},
959 { 0, 0, NULL, NULL}};
962 usrdsc.dsc$w_length = strlen(usrname);
963 usrdsc.dsc$a_pointer = usrname;
964 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
966 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
970 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
976 if (sts != RMS$_RNF) return NULL;
979 txtdsc.dsc$w_length = strlen(textpasswd);
980 txtdsc.dsc$a_pointer = textpasswd;
981 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
982 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
985 return (char *) hash;
987 } /* end of my_crypt() */
991 static char *mp_do_rmsexpand(pTHX_ char *, char *, int, char *, unsigned);
992 static char *mp_do_fileify_dirspec(pTHX_ char *, char *, int);
993 static char *mp_do_tovmsspec(pTHX_ char *, char *, int);
995 /*{{{int do_rmdir(char *name)*/
997 Perl_do_rmdir(pTHX_ char *name)
999 char dirfile[NAM$C_MAXRSS+1];
1003 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1004 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1005 else retval = kill_file(dirfile);
1008 } /* end of do_rmdir */
1012 * Delete any file to which user has control access, regardless of whether
1013 * delete access is explicitly allowed.
1014 * Limitations: User must have write access to parent directory.
1015 * Does not block signals or ASTs; if interrupted in midstream
1016 * may leave file with an altered ACL.
1019 /*{{{int kill_file(char *name)*/
1021 Perl_kill_file(pTHX_ char *name)
1023 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
1024 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1025 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1026 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1028 unsigned char myace$b_length;
1029 unsigned char myace$b_type;
1030 unsigned short int myace$w_flags;
1031 unsigned long int myace$l_access;
1032 unsigned long int myace$l_ident;
1033 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1034 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1035 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1037 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1038 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1039 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1040 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1041 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1042 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1044 /* Expand the input spec using RMS, since the CRTL remove() and
1045 * system services won't do this by themselves, so we may miss
1046 * a file "hiding" behind a logical name or search list. */
1047 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
1048 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
1049 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1050 /* If not, can changing protections help? */
1051 if (vaxc$errno != RMS$_PRV) return -1;
1053 /* No, so we get our own UIC to use as a rights identifier,
1054 * and the insert an ACE at the head of the ACL which allows us
1055 * to delete the file.
1057 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1058 fildsc.dsc$w_length = strlen(rspec);
1059 fildsc.dsc$a_pointer = rspec;
1061 newace.myace$l_ident = oldace.myace$l_ident;
1062 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1064 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1065 set_errno(ENOENT); break;
1067 set_errno(ENOTDIR); break;
1069 set_errno(ENODEV); break;
1070 case RMS$_SYN: case SS$_INVFILFOROP:
1071 set_errno(EINVAL); break;
1073 set_errno(EACCES); break;
1077 set_vaxc_errno(aclsts);
1080 /* Grab any existing ACEs with this identifier in case we fail */
1081 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1082 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1083 || fndsts == SS$_NOMOREACE ) {
1084 /* Add the new ACE . . . */
1085 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1087 if ((rmsts = remove(name))) {
1088 /* We blew it - dir with files in it, no write priv for
1089 * parent directory, etc. Put things back the way they were. */
1090 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1093 addlst[0].bufadr = &oldace;
1094 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1101 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1102 /* We just deleted it, so of course it's not there. Some versions of
1103 * VMS seem to return success on the unlock operation anyhow (after all
1104 * the unlock is successful), but others don't.
1106 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1107 if (aclsts & 1) aclsts = fndsts;
1108 if (!(aclsts & 1)) {
1110 set_vaxc_errno(aclsts);
1116 } /* end of kill_file() */
1120 /*{{{int my_mkdir(char *,Mode_t)*/
1122 Perl_my_mkdir(pTHX_ char *dir, Mode_t mode)
1124 STRLEN dirlen = strlen(dir);
1126 /* zero length string sometimes gives ACCVIO */
1127 if (dirlen == 0) return -1;
1129 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1130 * null file name/type. However, it's commonplace under Unix,
1131 * so we'll allow it for a gain in portability.
1133 if (dir[dirlen-1] == '/') {
1134 char *newdir = savepvn(dir,dirlen-1);
1135 int ret = mkdir(newdir,mode);
1139 else return mkdir(dir,mode);
1140 } /* end of my_mkdir */
1143 /*{{{int my_chdir(char *)*/
1145 Perl_my_chdir(pTHX_ char *dir)
1147 STRLEN dirlen = strlen(dir);
1149 /* zero length string sometimes gives ACCVIO */
1150 if (dirlen == 0) return -1;
1152 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1154 * null file name/type. However, it's commonplace under Unix,
1155 * so we'll allow it for a gain in portability.
1157 if (dir[dirlen-1] == '/') {
1158 char *newdir = savepvn(dir,dirlen-1);
1159 int ret = chdir(newdir);
1163 else return chdir(dir);
1164 } /* end of my_chdir */
1168 /*{{{FILE *my_tmpfile()*/
1175 if ((fp = tmpfile())) return fp;
1177 New(1323,cp,L_tmpnam+24,char);
1178 strcpy(cp,"Sys$Scratch:");
1179 tmpnam(cp+strlen(cp));
1180 strcat(cp,".Perltmp");
1181 fp = fopen(cp,"w+","fop=dlt");
1188 #ifndef HOMEGROWN_POSIX_SIGNALS
1190 * The C RTL's sigaction fails to check for invalid signal numbers so we
1191 * help it out a bit. The docs are correct, but the actual routine doesn't
1192 * do what the docs say it will.
1194 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1196 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1197 struct sigaction* oact)
1199 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1200 SETERRNO(EINVAL, SS$_INVARG);
1203 return sigaction(sig, act, oact);
1208 #ifdef KILL_BY_SIGPRC
1209 #include <errnodef.h>
1211 /* We implement our own kill() using the undocumented system service
1212 sys$sigprc for one of two reasons:
1214 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1215 target process to do a sys$exit, which usually can't be handled
1216 gracefully...certainly not by Perl and the %SIG{} mechanism.
1218 2.) If the kill() in the CRTL can't be called from a signal
1219 handler without disappearing into the ether, i.e., the signal
1220 it purportedly sends is never trapped. Still true as of VMS 7.3.
1222 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1223 in the target process rather than calling sys$exit.
1225 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1226 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1227 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1228 with condition codes C$_SIG0+nsig*8, catching the exception on the
1229 target process and resignaling with appropriate arguments.
1231 But we don't have that VMS 7.0+ exception handler, so if you
1232 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1234 Also note that SIGTERM is listed in the docs as being "unimplemented",
1235 yet always seems to be signaled with a VMS condition code of 4 (and
1236 correctly handled for that code). So we hardwire it in.
1238 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1239 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1240 than signalling with an unrecognized (and unhandled by CRTL) code.
1243 #define _MY_SIG_MAX 17
1246 Perl_sig_to_vmscondition(int sig)
1248 static unsigned int sig_code[_MY_SIG_MAX+1] =
1251 SS$_HANGUP, /* 1 SIGHUP */
1252 SS$_CONTROLC, /* 2 SIGINT */
1253 SS$_CONTROLY, /* 3 SIGQUIT */
1254 SS$_RADRMOD, /* 4 SIGILL */
1255 SS$_BREAK, /* 5 SIGTRAP */
1256 SS$_OPCCUS, /* 6 SIGABRT */
1257 SS$_COMPAT, /* 7 SIGEMT */
1259 SS$_FLTOVF, /* 8 SIGFPE VAX */
1261 SS$_HPARITH, /* 8 SIGFPE AXP */
1263 SS$_ABORT, /* 9 SIGKILL */
1264 SS$_ACCVIO, /* 10 SIGBUS */
1265 SS$_ACCVIO, /* 11 SIGSEGV */
1266 SS$_BADPARAM, /* 12 SIGSYS */
1267 SS$_NOMBX, /* 13 SIGPIPE */
1268 SS$_ASTFLT, /* 14 SIGALRM */
1274 #if __VMS_VER >= 60200000
1275 static int initted = 0;
1278 sig_code[16] = C$_SIGUSR1;
1279 sig_code[17] = C$_SIGUSR2;
1283 if (sig < _SIG_MIN) return 0;
1284 if (sig > _MY_SIG_MAX) return 0;
1285 return sig_code[sig];
1290 Perl_my_kill(int pid, int sig)
1295 int sys$sigprc(unsigned int *pidadr,
1296 struct dsc$descriptor_s *prcname,
1299 code = Perl_sig_to_vmscondition(sig);
1301 if (!pid || !code) {
1305 iss = sys$sigprc((unsigned int *)&pid,0,code);
1306 if (iss&1) return 0;
1310 set_errno(EPERM); break;
1312 case SS$_NOSUCHNODE:
1313 case SS$_UNREACHABLE:
1314 set_errno(ESRCH); break;
1316 set_errno(ENOMEM); break;
1321 set_vaxc_errno(iss);
1327 /* default piping mailbox size */
1328 #define PERL_BUFSIZ 512
1332 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
1334 unsigned long int mbxbufsiz;
1335 static unsigned long int syssize = 0;
1336 unsigned long int dviitm = DVI$_DEVNAM;
1337 char csize[LNM$C_NAMLENGTH+1];
1340 unsigned long syiitm = SYI$_MAXBUF;
1342 * Get the SYSGEN parameter MAXBUF
1344 * If the logical 'PERL_MBX_SIZE' is defined
1345 * use the value of the logical instead of PERL_BUFSIZ, but
1346 * keep the size between 128 and MAXBUF.
1349 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
1352 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
1353 mbxbufsiz = atoi(csize);
1355 mbxbufsiz = PERL_BUFSIZ;
1357 if (mbxbufsiz < 128) mbxbufsiz = 128;
1358 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
1360 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
1362 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
1363 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
1365 } /* end of create_mbx() */
1368 /*{{{ my_popen and my_pclose*/
1370 typedef struct _iosb IOSB;
1371 typedef struct _iosb* pIOSB;
1372 typedef struct _pipe Pipe;
1373 typedef struct _pipe* pPipe;
1374 typedef struct pipe_details Info;
1375 typedef struct pipe_details* pInfo;
1376 typedef struct _srqp RQE;
1377 typedef struct _srqp* pRQE;
1378 typedef struct _tochildbuf CBuf;
1379 typedef struct _tochildbuf* pCBuf;
1382 unsigned short status;
1383 unsigned short count;
1384 unsigned long dvispec;
1387 #pragma member_alignment save
1388 #pragma nomember_alignment quadword
1389 struct _srqp { /* VMS self-relative queue entry */
1390 unsigned long qptr[2];
1392 #pragma member_alignment restore
1393 static RQE RQE_ZERO = {0,0};
1395 struct _tochildbuf {
1398 unsigned short size;
1406 unsigned short chan_in;
1407 unsigned short chan_out;
1409 unsigned int bufsize;
1421 #if defined(PERL_IMPLICIT_CONTEXT)
1422 void *thx; /* Either a thread or an interpreter */
1423 /* pointer, depending on how we're built */
1431 PerlIO *fp; /* file pointer to pipe mailbox */
1432 int useFILE; /* using stdio, not perlio */
1433 int pid; /* PID of subprocess */
1434 int mode; /* == 'r' if pipe open for reading */
1435 int done; /* subprocess has completed */
1436 int waiting; /* waiting for completion/closure */
1437 int closing; /* my_pclose is closing this pipe */
1438 unsigned long completion; /* termination status of subprocess */
1439 pPipe in; /* pipe in to sub */
1440 pPipe out; /* pipe out of sub */
1441 pPipe err; /* pipe of sub's sys$error */
1442 int in_done; /* true when in pipe finished */
1447 struct exit_control_block
1449 struct exit_control_block *flink;
1450 unsigned long int (*exit_routine)();
1451 unsigned long int arg_count;
1452 unsigned long int *status_address;
1453 unsigned long int exit_status;
1456 typedef struct _closed_pipes Xpipe;
1457 typedef struct _closed_pipes* pXpipe;
1459 struct _closed_pipes {
1460 int pid; /* PID of subprocess */
1461 unsigned long completion; /* termination status of subprocess */
1463 #define NKEEPCLOSED 50
1464 static Xpipe closed_list[NKEEPCLOSED];
1465 static int closed_index = 0;
1466 static int closed_num = 0;
1468 #define RETRY_DELAY "0 ::0.20"
1469 #define MAX_RETRY 50
1471 static int pipe_ef = 0; /* first call to safe_popen inits these*/
1472 static unsigned long mypid;
1473 static unsigned long delaytime[2];
1475 static pInfo open_pipes = NULL;
1476 static $DESCRIPTOR(nl_desc, "NL:");
1478 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
1482 static unsigned long int
1483 pipe_exit_routine(pTHX)
1486 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1487 int sts, did_stuff, need_eof, j;
1490 flush any pending i/o
1496 PerlIO_flush(info->fp); /* first, flush data */
1498 fflush((FILE *)info->fp);
1504 next we try sending an EOF...ignore if doesn't work, make sure we
1512 _ckvmssts(sys$setast(0));
1513 if (info->in && !info->in->shut_on_empty) {
1514 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
1519 _ckvmssts(sys$setast(1));
1523 /* wait for EOF to have effect, up to ~ 30 sec [default] */
1525 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
1530 _ckvmssts(sys$setast(0));
1531 if (info->waiting && info->done)
1533 nwait += info->waiting;
1534 _ckvmssts(sys$setast(1));
1544 _ckvmssts(sys$setast(0));
1545 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1546 sts = sys$forcex(&info->pid,0,&abort);
1547 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1550 _ckvmssts(sys$setast(1));
1554 /* again, wait for effect */
1556 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
1561 _ckvmssts(sys$setast(0));
1562 if (info->waiting && info->done)
1564 nwait += info->waiting;
1565 _ckvmssts(sys$setast(1));
1574 _ckvmssts(sys$setast(0));
1575 if (!info->done) { /* We tried to be nice . . . */
1576 sts = sys$delprc(&info->pid,0);
1577 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1579 _ckvmssts(sys$setast(1));
1584 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1585 else if (!(sts & 1)) retsts = sts;
1590 static struct exit_control_block pipe_exitblock =
1591 {(struct exit_control_block *) 0,
1592 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1594 static void pipe_mbxtofd_ast(pPipe p);
1595 static void pipe_tochild1_ast(pPipe p);
1596 static void pipe_tochild2_ast(pPipe p);
1599 popen_completion_ast(pInfo info)
1601 pInfo i = open_pipes;
1605 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
1606 closed_list[closed_index].pid = info->pid;
1607 closed_list[closed_index].completion = info->completion;
1609 if (closed_index == NKEEPCLOSED)
1614 if (i == info) break;
1617 if (!i) return; /* unlinked, probably freed too */
1622 Writing to subprocess ...
1623 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
1625 chan_out may be waiting for "done" flag, or hung waiting
1626 for i/o completion to child...cancel the i/o. This will
1627 put it into "snarf mode" (done but no EOF yet) that discards
1630 Output from subprocess (stdout, stderr) needs to be flushed and
1631 shut down. We try sending an EOF, but if the mbx is full the pipe
1632 routine should still catch the "shut_on_empty" flag, telling it to
1633 use immediate-style reads so that "mbx empty" -> EOF.
1637 if (info->in && !info->in_done) { /* only for mode=w */
1638 if (info->in->shut_on_empty && info->in->need_wake) {
1639 info->in->need_wake = FALSE;
1640 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
1642 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
1646 if (info->out && !info->out_done) { /* were we also piping output? */
1647 info->out->shut_on_empty = TRUE;
1648 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1649 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1650 _ckvmssts_noperl(iss);
1653 if (info->err && !info->err_done) { /* we were piping stderr */
1654 info->err->shut_on_empty = TRUE;
1655 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1656 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1657 _ckvmssts_noperl(iss);
1659 _ckvmssts_noperl(sys$setef(pipe_ef));
1663 static unsigned long int setup_cmddsc(pTHX_ char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
1664 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
1667 we actually differ from vmstrnenv since we use this to
1668 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
1669 are pointing to the same thing
1672 static unsigned short
1673 popen_translate(pTHX_ char *logical, char *result)
1676 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
1677 $DESCRIPTOR(d_log,"");
1679 unsigned short length;
1680 unsigned short code;
1682 unsigned short *retlenaddr;
1684 unsigned short l, ifi;
1686 d_log.dsc$a_pointer = logical;
1687 d_log.dsc$w_length = strlen(logical);
1689 itmlst[0].code = LNM$_STRING;
1690 itmlst[0].length = 255;
1691 itmlst[0].buffer_addr = result;
1692 itmlst[0].retlenaddr = &l;
1695 itmlst[1].length = 0;
1696 itmlst[1].buffer_addr = 0;
1697 itmlst[1].retlenaddr = 0;
1699 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
1700 if (iss == SS$_NOLOGNAM) {
1704 if (!(iss&1)) lib$signal(iss);
1707 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
1708 strip it off and return the ifi, if any
1711 if (result[0] == 0x1b && result[1] == 0x00) {
1712 memcpy(&ifi,result+2,2);
1713 strcpy(result,result+4);
1715 return ifi; /* this is the RMS internal file id */
1718 static void pipe_infromchild_ast(pPipe p);
1721 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
1722 inside an AST routine without worrying about reentrancy and which Perl
1723 memory allocator is being used.
1725 We read data and queue up the buffers, then spit them out one at a
1726 time to the output mailbox when the output mailbox is ready for one.
1729 #define INITIAL_TOCHILDQUEUE 2
1732 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
1736 char mbx1[64], mbx2[64];
1737 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1738 DSC$K_CLASS_S, mbx1},
1739 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1740 DSC$K_CLASS_S, mbx2};
1741 unsigned int dviitm = DVI$_DEVBUFSIZ;
1744 New(1368, p, 1, Pipe);
1746 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1747 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1748 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1751 p->shut_on_empty = FALSE;
1752 p->need_wake = FALSE;
1755 p->iosb.status = SS$_NORMAL;
1756 p->iosb2.status = SS$_NORMAL;
1762 #ifdef PERL_IMPLICIT_CONTEXT
1766 n = sizeof(CBuf) + p->bufsize;
1768 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
1769 _ckvmssts(lib$get_vm(&n, &b));
1770 b->buf = (char *) b + sizeof(CBuf);
1771 _ckvmssts(lib$insqhi(b, &p->free));
1774 pipe_tochild2_ast(p);
1775 pipe_tochild1_ast(p);
1781 /* reads the MBX Perl is writing, and queues */
1784 pipe_tochild1_ast(pPipe p)
1787 int iss = p->iosb.status;
1788 int eof = (iss == SS$_ENDOFFILE);
1789 #ifdef PERL_IMPLICIT_CONTEXT
1795 p->shut_on_empty = TRUE;
1797 _ckvmssts(sys$dassgn(p->chan_in));
1803 b->size = p->iosb.count;
1804 _ckvmssts(lib$insqhi(b, &p->wait));
1806 p->need_wake = FALSE;
1807 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
1810 p->retry = 1; /* initial call */
1813 if (eof) { /* flush the free queue, return when done */
1814 int n = sizeof(CBuf) + p->bufsize;
1816 iss = lib$remqti(&p->free, &b);
1817 if (iss == LIB$_QUEWASEMP) return;
1819 _ckvmssts(lib$free_vm(&n, &b));
1823 iss = lib$remqti(&p->free, &b);
1824 if (iss == LIB$_QUEWASEMP) {
1825 int n = sizeof(CBuf) + p->bufsize;
1826 _ckvmssts(lib$get_vm(&n, &b));
1827 b->buf = (char *) b + sizeof(CBuf);
1833 iss = sys$qio(0,p->chan_in,
1834 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
1836 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
1837 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
1842 /* writes queued buffers to output, waits for each to complete before
1846 pipe_tochild2_ast(pPipe p)
1849 int iss = p->iosb2.status;
1850 int n = sizeof(CBuf) + p->bufsize;
1851 int done = (p->info && p->info->done) ||
1852 iss == SS$_CANCEL || iss == SS$_ABORT;
1853 #if defined(PERL_IMPLICIT_CONTEXT)
1858 if (p->type) { /* type=1 has old buffer, dispose */
1859 if (p->shut_on_empty) {
1860 _ckvmssts(lib$free_vm(&n, &b));
1862 _ckvmssts(lib$insqhi(b, &p->free));
1867 iss = lib$remqti(&p->wait, &b);
1868 if (iss == LIB$_QUEWASEMP) {
1869 if (p->shut_on_empty) {
1871 _ckvmssts(sys$dassgn(p->chan_out));
1872 *p->pipe_done = TRUE;
1873 _ckvmssts(sys$setef(pipe_ef));
1875 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1876 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1880 p->need_wake = TRUE;
1890 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1891 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1893 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
1894 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
1903 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
1906 char mbx1[64], mbx2[64];
1907 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1908 DSC$K_CLASS_S, mbx1},
1909 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1910 DSC$K_CLASS_S, mbx2};
1911 unsigned int dviitm = DVI$_DEVBUFSIZ;
1913 New(1367, p, 1, Pipe);
1914 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1915 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1917 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1918 New(1367, p->buf, p->bufsize, char);
1919 p->shut_on_empty = FALSE;
1922 p->iosb.status = SS$_NORMAL;
1923 #if defined(PERL_IMPLICIT_CONTEXT)
1926 pipe_infromchild_ast(p);
1934 pipe_infromchild_ast(pPipe p)
1936 int iss = p->iosb.status;
1937 int eof = (iss == SS$_ENDOFFILE);
1938 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
1939 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
1940 #if defined(PERL_IMPLICIT_CONTEXT)
1944 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
1945 _ckvmssts(sys$dassgn(p->chan_out));
1950 input shutdown if EOF from self (done or shut_on_empty)
1951 output shutdown if closing flag set (my_pclose)
1952 send data/eof from child or eof from self
1953 otherwise, re-read (snarf of data from child)
1958 if (myeof && p->chan_in) { /* input shutdown */
1959 _ckvmssts(sys$dassgn(p->chan_in));
1964 if (myeof || kideof) { /* pass EOF to parent */
1965 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
1966 pipe_infromchild_ast, p,
1969 } else if (eof) { /* eat EOF --- fall through to read*/
1971 } else { /* transmit data */
1972 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
1973 pipe_infromchild_ast,p,
1974 p->buf, p->iosb.count, 0, 0, 0, 0));
1980 /* everything shut? flag as done */
1982 if (!p->chan_in && !p->chan_out) {
1983 *p->pipe_done = TRUE;
1984 _ckvmssts(sys$setef(pipe_ef));
1988 /* write completed (or read, if snarfing from child)
1989 if still have input active,
1990 queue read...immediate mode if shut_on_empty so we get EOF if empty
1992 check if Perl reading, generate EOFs as needed
1998 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
1999 pipe_infromchild_ast,p,
2000 p->buf, p->bufsize, 0, 0, 0, 0);
2001 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
2003 } else { /* send EOFs for extra reads */
2004 p->iosb.status = SS$_ENDOFFILE;
2005 p->iosb.dvispec = 0;
2006 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
2008 pipe_infromchild_ast, p, 0, 0, 0, 0));
2014 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
2018 unsigned long dviitm = DVI$_DEVBUFSIZ;
2020 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
2021 DSC$K_CLASS_S, mbx};
2023 /* things like terminals and mbx's don't need this filter */
2024 if (fd && fstat(fd,&s) == 0) {
2025 unsigned long dviitm = DVI$_DEVCHAR, devchar;
2026 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
2027 DSC$K_CLASS_S, s.st_dev};
2029 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
2030 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
2031 strcpy(out, s.st_dev);
2036 New(1366, p, 1, Pipe);
2037 p->fd_out = dup(fd);
2038 create_mbx(aTHX_ &p->chan_in, &d_mbx);
2039 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2040 New(1366, p->buf, p->bufsize+1, char);
2041 p->shut_on_empty = FALSE;
2046 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
2047 pipe_mbxtofd_ast, p,
2048 p->buf, p->bufsize, 0, 0, 0, 0));
2054 pipe_mbxtofd_ast(pPipe p)
2056 int iss = p->iosb.status;
2057 int done = p->info->done;
2059 int eof = (iss == SS$_ENDOFFILE);
2060 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
2061 int err = !(iss&1) && !eof;
2062 #if defined(PERL_IMPLICIT_CONTEXT)
2066 if (done && myeof) { /* end piping */
2068 sys$dassgn(p->chan_in);
2069 *p->pipe_done = TRUE;
2070 _ckvmssts(sys$setef(pipe_ef));
2074 if (!err && !eof) { /* good data to send to file */
2075 p->buf[p->iosb.count] = '\n';
2076 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
2079 if (p->retry < MAX_RETRY) {
2080 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
2090 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
2091 pipe_mbxtofd_ast, p,
2092 p->buf, p->bufsize, 0, 0, 0, 0);
2093 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
2098 typedef struct _pipeloc PLOC;
2099 typedef struct _pipeloc* pPLOC;
2103 char dir[NAM$C_MAXRSS+1];
2105 static pPLOC head_PLOC = 0;
2108 free_pipelocs(pTHX_ void *head)
2111 pPLOC *pHead = (pPLOC *)head;
2123 store_pipelocs(pTHX)
2132 char temp[NAM$C_MAXRSS+1];
2136 free_pipelocs(aTHX_ &head_PLOC);
2138 /* the . directory from @INC comes last */
2141 p->next = head_PLOC;
2143 strcpy(p->dir,"./");
2145 /* get the directory from $^X */
2147 #ifdef PERL_IMPLICIT_CONTEXT
2148 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2150 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2152 strcpy(temp, PL_origargv[0]);
2153 x = strrchr(temp,']');
2156 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2158 p->next = head_PLOC;
2160 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2161 p->dir[NAM$C_MAXRSS] = '\0';
2165 /* reverse order of @INC entries, skip "." since entered above */
2167 #ifdef PERL_IMPLICIT_CONTEXT
2170 if (PL_incgv) av = GvAVn(PL_incgv);
2172 for (i = 0; av && i <= AvFILL(av); i++) {
2173 dirsv = *av_fetch(av,i,TRUE);
2175 if (SvROK(dirsv)) continue;
2176 dir = SvPVx(dirsv,n_a);
2177 if (strcmp(dir,".") == 0) continue;
2178 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2182 p->next = head_PLOC;
2184 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2185 p->dir[NAM$C_MAXRSS] = '\0';
2188 /* most likely spot (ARCHLIB) put first in the list */
2191 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
2193 p->next = head_PLOC;
2195 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2196 p->dir[NAM$C_MAXRSS] = '\0';
2205 static int vmspipe_file_status = 0;
2206 static char vmspipe_file[NAM$C_MAXRSS+1];
2208 /* already found? Check and use ... need read+execute permission */
2210 if (vmspipe_file_status == 1) {
2211 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
2212 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
2213 return vmspipe_file;
2215 vmspipe_file_status = 0;
2218 /* scan through stored @INC, $^X */
2220 if (vmspipe_file_status == 0) {
2221 char file[NAM$C_MAXRSS+1];
2222 pPLOC p = head_PLOC;
2225 strcpy(file, p->dir);
2226 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
2227 file[NAM$C_MAXRSS] = '\0';
2230 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
2232 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
2233 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
2234 vmspipe_file_status = 1;
2235 return vmspipe_file;
2238 vmspipe_file_status = -1; /* failed, use tempfiles */
2245 vmspipe_tempfile(pTHX)
2247 char file[NAM$C_MAXRSS+1];
2249 static int index = 0;
2252 /* create a tempfile */
2254 /* we can't go from W, shr=get to R, shr=get without
2255 an intermediate vulnerable state, so don't bother trying...
2257 and lib$spawn doesn't shr=put, so have to close the write
2259 So... match up the creation date/time and the FID to
2260 make sure we're dealing with the same file
2265 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
2266 fp = fopen(file,"w");
2268 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
2269 fp = fopen(file,"w");
2271 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
2272 fp = fopen(file,"w");
2275 if (!fp) return 0; /* we're hosed */
2277 fprintf(fp,"$! 'f$verify(0)'\n");
2278 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
2279 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
2280 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
2281 fprintf(fp,"$ perl_on = \"set noon\"\n");
2282 fprintf(fp,"$ perl_exit = \"exit\"\n");
2283 fprintf(fp,"$ perl_del = \"delete\"\n");
2284 fprintf(fp,"$ pif = \"if\"\n");
2285 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
2286 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
2287 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
2288 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
2289 fprintf(fp,"$! --- build command line to get max possible length\n");
2290 fprintf(fp,"$c=perl_popen_cmd0\n");
2291 fprintf(fp,"$c=c+perl_popen_cmd1\n");
2292 fprintf(fp,"$c=c+perl_popen_cmd2\n");
2293 fprintf(fp,"$x=perl_popen_cmd3\n");
2294 fprintf(fp,"$c=c+x\n");
2295 fprintf(fp,"$ perl_on\n");
2296 fprintf(fp,"$ 'c'\n");
2297 fprintf(fp,"$ perl_status = $STATUS\n");
2298 fprintf(fp,"$ perl_del 'perl_cfile'\n");
2299 fprintf(fp,"$ perl_exit 'perl_status'\n");
2302 fgetname(fp, file, 1);
2303 fstat(fileno(fp), &s0);
2306 fp = fopen(file,"r","shr=get");
2308 fstat(fileno(fp), &s1);
2310 if (s0.st_ino[0] != s1.st_ino[0] ||
2311 s0.st_ino[1] != s1.st_ino[1] ||
2312 s0.st_ino[2] != s1.st_ino[2] ||
2313 s0.st_ctime != s1.st_ctime ) {
2324 safe_popen(pTHX_ char *cmd, char *in_mode, int *psts)
2326 static int handler_set_up = FALSE;
2327 unsigned long int sts, flags = CLI$M_NOWAIT;
2328 /* The use of a GLOBAL table (as was done previously) rendered
2329 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
2330 * environment. Hence we've switched to LOCAL symbol table.
2332 unsigned int table = LIB$K_CLI_LOCAL_SYM;
2334 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
2335 char in[512], out[512], err[512], mbx[512];
2337 char tfilebuf[NAM$C_MAXRSS+1];
2339 char cmd_sym_name[20];
2340 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
2341 DSC$K_CLASS_S, symbol};
2342 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
2344 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
2345 DSC$K_CLASS_S, cmd_sym_name};
2346 struct dsc$descriptor_s *vmscmd;
2347 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
2348 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
2349 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
2351 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
2353 /* once-per-program initialization...
2354 note that the SETAST calls and the dual test of pipe_ef
2355 makes sure that only the FIRST thread through here does
2356 the initialization...all other threads wait until it's
2359 Yeah, uglier than a pthread call, it's got all the stuff inline
2360 rather than in a separate routine.
2364 _ckvmssts(sys$setast(0));
2366 unsigned long int pidcode = JPI$_PID;
2367 $DESCRIPTOR(d_delay, RETRY_DELAY);
2368 _ckvmssts(lib$get_ef(&pipe_ef));
2369 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2370 _ckvmssts(sys$bintim(&d_delay, delaytime));
2372 if (!handler_set_up) {
2373 _ckvmssts(sys$dclexh(&pipe_exitblock));
2374 handler_set_up = TRUE;
2376 _ckvmssts(sys$setast(1));
2379 /* see if we can find a VMSPIPE.COM */
2382 vmspipe = find_vmspipe(aTHX);
2384 strcpy(tfilebuf+1,vmspipe);
2385 } else { /* uh, oh...we're in tempfile hell */
2386 tpipe = vmspipe_tempfile(aTHX);
2387 if (!tpipe) { /* a fish popular in Boston */
2388 if (ckWARN(WARN_PIPE)) {
2389 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
2393 fgetname(tpipe,tfilebuf+1,1);
2395 vmspipedsc.dsc$a_pointer = tfilebuf;
2396 vmspipedsc.dsc$w_length = strlen(tfilebuf);
2398 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
2401 case RMS$_FNF: case RMS$_DNF:
2402 set_errno(ENOENT); break;
2404 set_errno(ENOTDIR); break;
2406 set_errno(ENODEV); break;
2408 set_errno(EACCES); break;
2410 set_errno(EINVAL); break;
2411 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
2412 set_errno(E2BIG); break;
2413 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
2414 _ckvmssts(sts); /* fall through */
2415 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
2418 set_vaxc_errno(sts);
2419 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
2420 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
2425 New(1301,info,1,Info);
2427 strcpy(mode,in_mode);
2430 info->completion = 0;
2431 info->closing = FALSE;
2438 info->in_done = TRUE;
2439 info->out_done = TRUE;
2440 info->err_done = TRUE;
2441 in[0] = out[0] = err[0] = '\0';
2443 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
2447 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
2452 if (*mode == 'r') { /* piping from subroutine */
2454 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
2456 info->out->pipe_done = &info->out_done;
2457 info->out_done = FALSE;
2458 info->out->info = info;
2460 if (!info->useFILE) {
2461 info->fp = PerlIO_open(mbx, mode);
2463 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
2464 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
2467 if (!info->fp && info->out) {
2468 sys$cancel(info->out->chan_out);
2470 while (!info->out_done) {
2472 _ckvmssts(sys$setast(0));
2473 done = info->out_done;
2474 if (!done) _ckvmssts(sys$clref(pipe_ef));
2475 _ckvmssts(sys$setast(1));
2476 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2479 if (info->out->buf) Safefree(info->out->buf);
2480 Safefree(info->out);
2486 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2488 info->err->pipe_done = &info->err_done;
2489 info->err_done = FALSE;
2490 info->err->info = info;
2493 } else if (*mode == 'w') { /* piping to subroutine */
2495 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2497 info->out->pipe_done = &info->out_done;
2498 info->out_done = FALSE;
2499 info->out->info = info;
2502 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2504 info->err->pipe_done = &info->err_done;
2505 info->err_done = FALSE;
2506 info->err->info = info;
2509 info->in = pipe_tochild_setup(aTHX_ in,mbx);
2510 if (!info->useFILE) {
2511 info->fp = PerlIO_open(mbx, mode);
2513 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
2514 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
2518 info->in->pipe_done = &info->in_done;
2519 info->in_done = FALSE;
2520 info->in->info = info;
2524 if (!info->fp && info->in) {
2526 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
2527 0, 0, 0, 0, 0, 0, 0, 0));
2529 while (!info->in_done) {
2531 _ckvmssts(sys$setast(0));
2532 done = info->in_done;
2533 if (!done) _ckvmssts(sys$clref(pipe_ef));
2534 _ckvmssts(sys$setast(1));
2535 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2538 if (info->in->buf) Safefree(info->in->buf);
2546 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
2547 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2549 info->out->pipe_done = &info->out_done;
2550 info->out_done = FALSE;
2551 info->out->info = info;
2554 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2556 info->err->pipe_done = &info->err_done;
2557 info->err_done = FALSE;
2558 info->err->info = info;
2562 symbol[MAX_DCL_SYMBOL] = '\0';
2564 strncpy(symbol, in, MAX_DCL_SYMBOL);
2565 d_symbol.dsc$w_length = strlen(symbol);
2566 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
2568 strncpy(symbol, err, MAX_DCL_SYMBOL);
2569 d_symbol.dsc$w_length = strlen(symbol);
2570 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
2572 strncpy(symbol, out, MAX_DCL_SYMBOL);
2573 d_symbol.dsc$w_length = strlen(symbol);
2574 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
2576 p = vmscmd->dsc$a_pointer;
2577 while (*p && *p != '\n') p++;
2578 *p = '\0'; /* truncate on \n */
2579 p = vmscmd->dsc$a_pointer;
2580 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
2581 if (*p == '$') p++; /* remove leading $ */
2582 while (*p == ' ' || *p == '\t') p++;
2584 for (j = 0; j < 4; j++) {
2585 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
2586 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
2588 strncpy(symbol, p, MAX_DCL_SYMBOL);
2589 d_symbol.dsc$w_length = strlen(symbol);
2590 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
2592 if (strlen(p) > MAX_DCL_SYMBOL) {
2593 p += MAX_DCL_SYMBOL;
2598 _ckvmssts(sys$setast(0));
2599 info->next=open_pipes; /* prepend to list */
2601 _ckvmssts(sys$setast(1));
2602 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
2603 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
2604 * have SYS$COMMAND if we need it.
2606 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
2607 0, &info->pid, &info->completion,
2608 0, popen_completion_ast,info,0,0,0));
2610 /* if we were using a tempfile, close it now */
2612 if (tpipe) fclose(tpipe);
2614 /* once the subprocess is spawned, it has copied the symbols and
2615 we can get rid of ours */
2617 for (j = 0; j < 4; j++) {
2618 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
2619 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
2620 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
2622 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
2623 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
2624 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
2625 vms_execfree(vmscmd);
2627 #ifdef PERL_IMPLICIT_CONTEXT
2630 PL_forkprocess = info->pid;
2635 _ckvmssts(sys$setast(0));
2637 if (!done) _ckvmssts(sys$clref(pipe_ef));
2638 _ckvmssts(sys$setast(1));
2639 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2641 *psts = info->completion;
2642 my_pclose(info->fp);
2647 } /* end of safe_popen */
2650 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
2652 Perl_my_popen(pTHX_ char *cmd, char *mode)
2656 TAINT_PROPER("popen");
2657 PERL_FLUSHALL_FOR_CHILD;
2658 return safe_popen(aTHX_ cmd,mode,&sts);
2663 /*{{{ I32 my_pclose(PerlIO *fp)*/
2664 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
2666 pInfo info, last = NULL;
2667 unsigned long int retsts;
2670 for (info = open_pipes; info != NULL; last = info, info = info->next)
2671 if (info->fp == fp) break;
2673 if (info == NULL) { /* no such pipe open */
2674 set_errno(ECHILD); /* quoth POSIX */
2675 set_vaxc_errno(SS$_NONEXPR);
2679 /* If we were writing to a subprocess, insure that someone reading from
2680 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
2681 * produce an EOF record in the mailbox.
2683 * well, at least sometimes it *does*, so we have to watch out for
2684 * the first EOF closing the pipe (and DASSGN'ing the channel)...
2688 PerlIO_flush(info->fp); /* first, flush data */
2690 fflush((FILE *)info->fp);
2693 _ckvmssts(sys$setast(0));
2694 info->closing = TRUE;
2695 done = info->done && info->in_done && info->out_done && info->err_done;
2696 /* hanging on write to Perl's input? cancel it */
2697 if (info->mode == 'r' && info->out && !info->out_done) {
2698 if (info->out->chan_out) {
2699 _ckvmssts(sys$cancel(info->out->chan_out));
2700 if (!info->out->chan_in) { /* EOF generation, need AST */
2701 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
2705 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
2706 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2708 _ckvmssts(sys$setast(1));
2711 PerlIO_close(info->fp);
2713 fclose((FILE *)info->fp);
2716 we have to wait until subprocess completes, but ALSO wait until all
2717 the i/o completes...otherwise we'll be freeing the "info" structure
2718 that the i/o ASTs could still be using...
2722 _ckvmssts(sys$setast(0));
2723 done = info->done && info->in_done && info->out_done && info->err_done;
2724 if (!done) _ckvmssts(sys$clref(pipe_ef));
2725 _ckvmssts(sys$setast(1));
2726 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2728 retsts = info->completion;
2730 /* remove from list of open pipes */
2731 _ckvmssts(sys$setast(0));
2732 if (last) last->next = info->next;
2733 else open_pipes = info->next;
2734 _ckvmssts(sys$setast(1));
2736 /* free buffers and structures */
2739 if (info->in->buf) Safefree(info->in->buf);
2743 if (info->out->buf) Safefree(info->out->buf);
2744 Safefree(info->out);
2747 if (info->err->buf) Safefree(info->err->buf);
2748 Safefree(info->err);
2754 } /* end of my_pclose() */
2756 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
2757 /* Roll our own prototype because we want this regardless of whether
2758 * _VMS_WAIT is defined.
2760 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
2762 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
2763 created with popen(); otherwise partially emulate waitpid() unless
2764 we have a suitable one from the CRTL that came with VMS 7.2 and later.
2765 Also check processes not considered by the CRTL waitpid().
2767 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
2769 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
2776 if (statusp) *statusp = 0;
2778 for (info = open_pipes; info != NULL; info = info->next)
2779 if (info->pid == pid) break;
2781 if (info != NULL) { /* we know about this child */
2782 while (!info->done) {
2783 _ckvmssts(sys$setast(0));
2785 if (!done) _ckvmssts(sys$clref(pipe_ef));
2786 _ckvmssts(sys$setast(1));
2787 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2790 if (statusp) *statusp = info->completion;
2794 /* child that already terminated? */
2796 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
2797 if (closed_list[j].pid == pid) {
2798 if (statusp) *statusp = closed_list[j].completion;
2803 /* fall through if this child is not one of our own pipe children */
2805 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
2807 /* waitpid() became available in the CRTL as of VMS 7.0, but only
2808 * in 7.2 did we get a version that fills in the VMS completion
2809 * status as Perl has always tried to do.
2812 sts = __vms_waitpid( pid, statusp, flags );
2814 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
2817 /* If the real waitpid tells us the child does not exist, we
2818 * fall through here to implement waiting for a child that
2819 * was created by some means other than exec() (say, spawned
2820 * from DCL) or to wait for a process that is not a subprocess
2821 * of the current process.
2824 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
2827 $DESCRIPTOR(intdsc,"0 00:00:01");
2828 unsigned long int ownercode = JPI$_OWNER, ownerpid;
2829 unsigned long int pidcode = JPI$_PID, mypid;
2830 unsigned long int interval[2];
2831 unsigned int jpi_iosb[2];
2832 struct itmlst_3 jpilist[2] = {
2833 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
2838 /* Sorry folks, we don't presently implement rooting around for
2839 the first child we can find, and we definitely don't want to
2840 pass a pid of -1 to $getjpi, where it is a wildcard operation.
2846 /* Get the owner of the child so I can warn if it's not mine. If the
2847 * process doesn't exist or I don't have the privs to look at it,
2848 * I can go home early.
2850 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
2851 if (sts & 1) sts = jpi_iosb[0];
2863 set_vaxc_errno(sts);
2867 if (ckWARN(WARN_EXEC)) {
2868 /* remind folks they are asking for non-standard waitpid behavior */
2869 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2870 if (ownerpid != mypid)
2871 Perl_warner(aTHX_ packWARN(WARN_EXEC),
2872 "waitpid: process %x is not a child of process %x",
2876 /* simply check on it once a second until it's not there anymore. */
2878 _ckvmssts(sys$bintim(&intdsc,interval));
2879 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
2880 _ckvmssts(sys$schdwk(0,0,interval,0));
2881 _ckvmssts(sys$hiber());
2883 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
2888 } /* end of waitpid() */
2893 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
2895 my_gconvert(double val, int ndig, int trail, char *buf)
2897 static char __gcvtbuf[DBL_DIG+1];
2900 loc = buf ? buf : __gcvtbuf;
2902 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
2904 sprintf(loc,"%.*g",ndig,val);
2910 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
2911 return gcvt(val,ndig,loc);
2914 loc[0] = '0'; loc[1] = '\0';
2922 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
2923 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
2924 * to expand file specification. Allows for a single default file
2925 * specification and a simple mask of options. If outbuf is non-NULL,
2926 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
2927 * the resultant file specification is placed. If outbuf is NULL, the
2928 * resultant file specification is placed into a static buffer.
2929 * The third argument, if non-NULL, is taken to be a default file
2930 * specification string. The fourth argument is unused at present.
2931 * rmesexpand() returns the address of the resultant string if
2932 * successful, and NULL on error.
2934 static char *mp_do_tounixspec(pTHX_ char *, char *, int);
2937 mp_do_rmsexpand(pTHX_ char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
2939 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
2940 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
2941 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
2942 struct FAB myfab = cc$rms_fab;
2943 struct NAM mynam = cc$rms_nam;
2945 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
2947 if (!filespec || !*filespec) {
2948 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
2952 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
2953 else outbuf = __rmsexpand_retbuf;
2955 if ((isunix = (strchr(filespec,'/') != NULL))) {
2956 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
2957 filespec = vmsfspec;
2960 myfab.fab$l_fna = filespec;
2961 myfab.fab$b_fns = strlen(filespec);
2962 myfab.fab$l_nam = &mynam;
2964 if (defspec && *defspec) {
2965 if (strchr(defspec,'/') != NULL) {
2966 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
2969 myfab.fab$l_dna = defspec;
2970 myfab.fab$b_dns = strlen(defspec);
2973 mynam.nam$l_esa = esa;
2974 mynam.nam$b_ess = sizeof esa;
2975 mynam.nam$l_rsa = outbuf;
2976 mynam.nam$b_rss = NAM$C_MAXRSS;
2978 retsts = sys$parse(&myfab,0,0);
2979 if (!(retsts & 1)) {
2980 mynam.nam$b_nop |= NAM$M_SYNCHK;
2981 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
2982 retsts = sys$parse(&myfab,0,0);
2983 if (retsts & 1) goto expanded;
2985 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
2986 (void) sys$parse(&myfab,0,0); /* Free search context */
2987 if (out) Safefree(out);
2988 set_vaxc_errno(retsts);
2989 if (retsts == RMS$_PRV) set_errno(EACCES);
2990 else if (retsts == RMS$_DEV) set_errno(ENODEV);
2991 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
2992 else set_errno(EVMSERR);
2995 retsts = sys$search(&myfab,0,0);
2996 if (!(retsts & 1) && retsts != RMS$_FNF) {
2997 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2998 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2999 if (out) Safefree(out);
3000 set_vaxc_errno(retsts);
3001 if (retsts == RMS$_PRV) set_errno(EACCES);
3002 else set_errno(EVMSERR);
3006 /* If the input filespec contained any lowercase characters,
3007 * downcase the result for compatibility with Unix-minded code. */
3009 for (out = myfab.fab$l_fna; *out; out++)
3010 if (islower(*out)) { haslower = 1; break; }
3011 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
3012 else { out = esa; speclen = mynam.nam$b_esl; }
3013 /* Trim off null fields added by $PARSE
3014 * If type > 1 char, must have been specified in original or default spec
3015 * (not true for version; $SEARCH may have added version of existing file).
3017 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
3018 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
3019 (mynam.nam$l_ver - mynam.nam$l_type == 1);
3020 if (trimver || trimtype) {
3021 if (defspec && *defspec) {
3022 char defesa[NAM$C_MAXRSS];
3023 struct FAB deffab = cc$rms_fab;
3024 struct NAM defnam = cc$rms_nam;
3026 deffab.fab$l_nam = &defnam;
3027 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
3028 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
3029 defnam.nam$b_nop = NAM$M_SYNCHK;
3030 if (sys$parse(&deffab,0,0) & 1) {
3031 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
3032 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
3035 if (trimver) speclen = mynam.nam$l_ver - out;
3037 /* If we didn't already trim version, copy down */
3038 if (speclen > mynam.nam$l_ver - out)
3039 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
3040 speclen - (mynam.nam$l_ver - out));
3041 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
3044 /* If we just had a directory spec on input, $PARSE "helpfully"
3045 * adds an empty name and type for us */
3046 if (mynam.nam$l_name == mynam.nam$l_type &&
3047 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
3048 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
3049 speclen = mynam.nam$l_name - out;
3050 out[speclen] = '\0';
3051 if (haslower) __mystrtolower(out);
3053 /* Have we been working with an expanded, but not resultant, spec? */
3054 /* Also, convert back to Unix syntax if necessary. */
3055 if (!mynam.nam$b_rsl) {
3057 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
3059 else strcpy(outbuf,esa);
3062 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
3063 strcpy(outbuf,tmpfspec);
3065 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3066 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
3067 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
3071 /* External entry points */
3072 char *Perl_rmsexpand(pTHX_ char *spec, char *buf, char *def, unsigned opt)
3073 { return do_rmsexpand(spec,buf,0,def,opt); }
3074 char *Perl_rmsexpand_ts(pTHX_ char *spec, char *buf, char *def, unsigned opt)
3075 { return do_rmsexpand(spec,buf,1,def,opt); }
3079 ** The following routines are provided to make life easier when
3080 ** converting among VMS-style and Unix-style directory specifications.
3081 ** All will take input specifications in either VMS or Unix syntax. On
3082 ** failure, all return NULL. If successful, the routines listed below
3083 ** return a pointer to a buffer containing the appropriately
3084 ** reformatted spec (and, therefore, subsequent calls to that routine
3085 ** will clobber the result), while the routines of the same names with
3086 ** a _ts suffix appended will return a pointer to a mallocd string
3087 ** containing the appropriately reformatted spec.
3088 ** In all cases, only explicit syntax is altered; no check is made that
3089 ** the resulting string is valid or that the directory in question
3092 ** fileify_dirspec() - convert a directory spec into the name of the
3093 ** directory file (i.e. what you can stat() to see if it's a dir).
3094 ** The style (VMS or Unix) of the result is the same as the style
3095 ** of the parameter passed in.
3096 ** pathify_dirspec() - convert a directory spec into a path (i.e.
3097 ** what you prepend to a filename to indicate what directory it's in).
3098 ** The style (VMS or Unix) of the result is the same as the style
3099 ** of the parameter passed in.
3100 ** tounixpath() - convert a directory spec into a Unix-style path.
3101 ** tovmspath() - convert a directory spec into a VMS-style path.
3102 ** tounixspec() - convert any file spec into a Unix-style file spec.
3103 ** tovmsspec() - convert any file spec into a VMS-style spec.
3105 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
3106 ** Permission is given to distribute this code as part of the Perl
3107 ** standard distribution under the terms of the GNU General Public
3108 ** License or the Perl Artistic License. Copies of each may be
3109 ** found in the Perl standard distribution.
3112 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
3113 static char *mp_do_fileify_dirspec(pTHX_ char *dir,char *buf,int ts)
3115 static char __fileify_retbuf[NAM$C_MAXRSS+1];
3116 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
3117 char *retspec, *cp1, *cp2, *lastdir;
3118 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
3119 unsigned short int trnlnm_iter_count;
3121 if (!dir || !*dir) {
3122 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
3124 dirlen = strlen(dir);
3125 while (dirlen && dir[dirlen-1] == '/') --dirlen;
3126 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
3127 strcpy(trndir,"/sys$disk/000000");
3131 if (dirlen > NAM$C_MAXRSS) {
3132 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
3134 if (!strpbrk(dir+1,"/]>:")) {
3135 strcpy(trndir,*dir == '/' ? dir + 1: dir);
3136 trnlnm_iter_count = 0;
3137 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
3138 trnlnm_iter_count++;
3139 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
3142 dirlen = strlen(dir);
3145 strncpy(trndir,dir,dirlen);
3146 trndir[dirlen] = '\0';
3149 /* If we were handed a rooted logical name or spec, treat it like a
3150 * simple directory, so that
3151 * $ Define myroot dev:[dir.]
3152 * ... do_fileify_dirspec("myroot",buf,1) ...
3153 * does something useful.
3155 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".]")) {
3156 dir[--dirlen] = '\0';
3157 dir[dirlen-1] = ']';
3159 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".>")) {
3160 dir[--dirlen] = '\0';
3161 dir[dirlen-1] = '>';
3164 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
3165 /* If we've got an explicit filename, we can just shuffle the string. */
3166 if (*(cp1+1)) hasfilename = 1;
3167 /* Similarly, we can just back up a level if we've got multiple levels
3168 of explicit directories in a VMS spec which ends with directories. */
3170 for (cp2 = cp1; cp2 > dir; cp2--) {
3172 *cp2 = *cp1; *cp1 = '\0';
3176 if (*cp2 == '[' || *cp2 == '<') break;
3181 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
3182 if (dir[0] == '.') {
3183 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
3184 return do_fileify_dirspec("[]",buf,ts);
3185 else if (dir[1] == '.' &&
3186 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
3187 return do_fileify_dirspec("[-]",buf,ts);
3189 if (dirlen && dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
3190 dirlen -= 1; /* to last element */
3191 lastdir = strrchr(dir,'/');
3193 else if ((cp1 = strstr(dir,"/.")) != NULL) {
3194 /* If we have "/." or "/..", VMSify it and let the VMS code
3195 * below expand it, rather than repeating the code to handle
3196 * relative components of a filespec here */
3198 if (*(cp1+2) == '.') cp1++;
3199 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
3200 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
3201 if (strchr(vmsdir,'/') != NULL) {
3202 /* If do_tovmsspec() returned it, it must have VMS syntax
3203 * delimiters in it, so it's a mixed VMS/Unix spec. We take
3204 * the time to check this here only so we avoid a recursion
3205 * loop; otherwise, gigo.
3207 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
3209 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
3210 return do_tounixspec(trndir,buf,ts);
3213 } while ((cp1 = strstr(cp1,"/.")) != NULL);
3214 lastdir = strrchr(dir,'/');
3216 else if (dirlen >= 7 && !strcmp(&dir[dirlen-7],"/000000")) {
3217 /* Ditto for specs that end in an MFD -- let the VMS code
3218 * figure out whether it's a real device or a rooted logical. */
3219 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
3220 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
3221 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
3222 return do_tounixspec(trndir,buf,ts);
3225 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
3226 !(lastdir = cp1 = strrchr(dir,']')) &&
3227 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
3228 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
3230 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3231 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3232 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3233 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3234 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3235 (ver || *cp3)))))) {
3237 set_vaxc_errno(RMS$_DIR);
3243 /* If we lead off with a device or rooted logical, add the MFD
3244 if we're specifying a top-level directory. */
3245 if (lastdir && *dir == '/') {
3247 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
3254 retlen = dirlen + (addmfd ? 13 : 6);
3255 if (buf) retspec = buf;
3256 else if (ts) New(1309,retspec,retlen+1,char);
3257 else retspec = __fileify_retbuf;
3259 dirlen = lastdir - dir;
3260 memcpy(retspec,dir,dirlen);
3261 strcpy(&retspec[dirlen],"/000000");
3262 strcpy(&retspec[dirlen+7],lastdir);
3265 memcpy(retspec,dir,dirlen);
3266 retspec[dirlen] = '\0';
3268 /* We've picked up everything up to the directory file name.
3269 Now just add the type and version, and we're set. */
3270 strcat(retspec,".dir;1");
3273 else { /* VMS-style directory spec */
3274 char esa[NAM$C_MAXRSS+1], term, *cp;
3275 unsigned long int sts, cmplen, haslower = 0;
3276 struct FAB dirfab = cc$rms_fab;
3277 struct NAM savnam, dirnam = cc$rms_nam;
3279 dirfab.fab$b_fns = strlen(dir);
3280 dirfab.fab$l_fna = dir;
3281 dirfab.fab$l_nam = &dirnam;
3282 dirfab.fab$l_dna = ".DIR;1";
3283 dirfab.fab$b_dns = 6;
3284 dirnam.nam$b_ess = NAM$C_MAXRSS;
3285 dirnam.nam$l_esa = esa;
3287 for (cp = dir; *cp; cp++)
3288 if (islower(*cp)) { haslower = 1; break; }
3289 if (!((sts = sys$parse(&dirfab))&1)) {
3290 if (dirfab.fab$l_sts == RMS$_DIR) {
3291 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3292 sts = sys$parse(&dirfab) & 1;
3296 set_vaxc_errno(dirfab.fab$l_sts);
3302 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
3303 /* Yes; fake the fnb bits so we'll check type below */
3304 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
3306 else { /* No; just work with potential name */
3307 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
3309 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
3310 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3311 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3316 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
3317 cp1 = strchr(esa,']');
3318 if (!cp1) cp1 = strchr(esa,'>');
3319 if (cp1) { /* Should always be true */
3320 dirnam.nam$b_esl -= cp1 - esa - 1;
3321 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
3324 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3325 /* Yep; check version while we're at it, if it's there. */
3326 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3327 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3328 /* Something other than .DIR[;1]. Bzzt. */
3329 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3330 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3332 set_vaxc_errno(RMS$_DIR);
3336 esa[dirnam.nam$b_esl] = '\0';
3337 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
3338 /* They provided at least the name; we added the type, if necessary, */
3339 if (buf) retspec = buf; /* in sys$parse() */
3340 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
3341 else retspec = __fileify_retbuf;
3342 strcpy(retspec,esa);
3343 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3344 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3347 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
3348 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
3350 dirnam.nam$b_esl -= 9;
3352 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
3353 if (cp1 == NULL) { /* should never happen */
3354 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3355 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3360 retlen = strlen(esa);
3361 if ((cp1 = strrchr(esa,'.')) != NULL) {
3362 /* There's more than one directory in the path. Just roll back. */
3364 if (buf) retspec = buf;
3365 else if (ts) New(1311,retspec,retlen+7,char);
3366 else retspec = __fileify_retbuf;
3367 strcpy(retspec,esa);
3370 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
3371 /* Go back and expand rooted logical name */
3372 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
3373 if (!(sys$parse(&dirfab) & 1)) {
3374 dirnam.nam$l_rlf = NULL;
3375 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3377 set_vaxc_errno(dirfab.fab$l_sts);
3380 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
3381 if (buf) retspec = buf;
3382 else if (ts) New(1312,retspec,retlen+16,char);
3383 else retspec = __fileify_retbuf;
3384 cp1 = strstr(esa,"][");
3385 if (!cp1) cp1 = strstr(esa,"]<");
3387 memcpy(retspec,esa,dirlen);
3388 if (!strncmp(cp1+2,"000000]",7)) {
3389 retspec[dirlen-1] = '\0';
3390 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3391 if (*cp1 == '.') *cp1 = ']';
3393 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3394 memcpy(cp1+1,"000000]",7);
3398 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
3399 retspec[retlen] = '\0';
3400 /* Convert last '.' to ']' */
3401 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3402 if (*cp1 == '.') *cp1 = ']';
3404 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3405 memcpy(cp1+1,"000000]",7);
3409 else { /* This is a top-level dir. Add the MFD to the path. */
3410 if (buf) retspec = buf;
3411 else if (ts) New(1312,retspec,retlen+16,char);
3412 else retspec = __fileify_retbuf;
3415 while (*cp1 != ':') *(cp2++) = *(cp1++);
3416 strcpy(cp2,":[000000]");
3421 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3422 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3423 /* We've set up the string up through the filename. Add the
3424 type and version, and we're done. */
3425 strcat(retspec,".DIR;1");
3427 /* $PARSE may have upcased filespec, so convert output to lower
3428 * case if input contained any lowercase characters. */
3429 if (haslower) __mystrtolower(retspec);
3432 } /* end of do_fileify_dirspec() */
3434 /* External entry points */
3435 char *Perl_fileify_dirspec(pTHX_ char *dir, char *buf)
3436 { return do_fileify_dirspec(dir,buf,0); }
3437 char *Perl_fileify_dirspec_ts(pTHX_ char *dir, char *buf)
3438 { return do_fileify_dirspec(dir,buf,1); }
3440 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
3441 static char *mp_do_pathify_dirspec(pTHX_ char *dir,char *buf, int ts)
3443 static char __pathify_retbuf[NAM$C_MAXRSS+1];
3444 unsigned long int retlen;
3445 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
3446 unsigned short int trnlnm_iter_count;
3449 if (!dir || !*dir) {
3450 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
3453 if (*dir) strcpy(trndir,dir);
3454 else getcwd(trndir,sizeof trndir - 1);
3456 trnlnm_iter_count = 0;
3457 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
3458 && my_trnlnm(trndir,trndir,0)) {
3459 trnlnm_iter_count++;
3460 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
3461 trnlen = strlen(trndir);
3463 /* Trap simple rooted lnms, and return lnm:[000000] */
3464 if (!strcmp(trndir+trnlen-2,".]")) {
3465 if (buf) retpath = buf;
3466 else if (ts) New(1318,retpath,strlen(dir)+10,char);
3467 else retpath = __pathify_retbuf;
3468 strcpy(retpath,dir);
3469 strcat(retpath,":[000000]");
3475 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
3476 if (*dir == '.' && (*(dir+1) == '\0' ||
3477 (*(dir+1) == '.' && *(dir+2) == '\0')))
3478 retlen = 2 + (*(dir+1) != '\0');
3480 if ( !(cp1 = strrchr(dir,'/')) &&
3481 !(cp1 = strrchr(dir,']')) &&
3482 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
3483 if ((cp2 = strchr(cp1,'.')) != NULL &&
3484 (*(cp2-1) != '/' || /* Trailing '.', '..', */
3485 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
3486 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
3487 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
3489 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3490 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3491 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3492 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3493 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3494 (ver || *cp3)))))) {
3496 set_vaxc_errno(RMS$_DIR);
3499 retlen = cp2 - dir + 1;
3501 else { /* No file type present. Treat the filename as a directory. */
3502 retlen = strlen(dir) + 1;
3505 if (buf) retpath = buf;
3506 else if (ts) New(1313,retpath,retlen+1,char);
3507 else retpath = __pathify_retbuf;
3508 strncpy(retpath,dir,retlen-1);
3509 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
3510 retpath[retlen-1] = '/'; /* with '/', add it. */
3511 retpath[retlen] = '\0';
3513 else retpath[retlen-1] = '\0';
3515 else { /* VMS-style directory spec */
3516 char esa[NAM$C_MAXRSS+1], *cp;
3517 unsigned long int sts, cmplen, haslower;
3518 struct FAB dirfab = cc$rms_fab;
3519 struct NAM savnam, dirnam = cc$rms_nam;
3521 /* If we've got an explicit filename, we can just shuffle the string. */
3522 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
3523 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
3524 if ((cp2 = strchr(cp1,'.')) != NULL) {
3526 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3527 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3528 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3529 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3530 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3531 (ver || *cp3)))))) {
3533 set_vaxc_errno(RMS$_DIR);
3537 else { /* No file type, so just draw name into directory part */
3538 for (cp2 = cp1; *cp2; cp2++) ;
3541 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
3543 /* We've now got a VMS 'path'; fall through */
3545 dirfab.fab$b_fns = strlen(dir);
3546 dirfab.fab$l_fna = dir;
3547 if (dir[dirfab.fab$b_fns-1] == ']' ||
3548 dir[dirfab.fab$b_fns-1] == '>' ||
3549 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
3550 if (buf) retpath = buf;
3551 else if (ts) New(1314,retpath,strlen(dir)+1,char);
3552 else retpath = __pathify_retbuf;
3553 strcpy(retpath,dir);
3556 dirfab.fab$l_dna = ".DIR;1";
3557 dirfab.fab$b_dns = 6;
3558 dirfab.fab$l_nam = &dirnam;
3559 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
3560 dirnam.nam$l_esa = esa;
3562 for (cp = dir; *cp; cp++)
3563 if (islower(*cp)) { haslower = 1; break; }
3565 if (!(sts = (sys$parse(&dirfab)&1))) {
3566 if (dirfab.fab$l_sts == RMS$_DIR) {
3567 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3568 sts = sys$parse(&dirfab) & 1;
3572 set_vaxc_errno(dirfab.fab$l_sts);
3578 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
3579 if (dirfab.fab$l_sts != RMS$_FNF) {
3580 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3581 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3583 set_vaxc_errno(dirfab.fab$l_sts);
3586 dirnam = savnam; /* No; just work with potential name */
3589 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3590 /* Yep; check version while we're at it, if it's there. */
3591 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3592 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3593 /* Something other than .DIR[;1]. Bzzt. */
3594 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3595 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3597 set_vaxc_errno(RMS$_DIR);
3601 /* OK, the type was fine. Now pull any file name into the
3603 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
3605 cp1 = strrchr(esa,'>');
3606 *dirnam.nam$l_type = '>';
3609 *(dirnam.nam$l_type + 1) = '\0';
3610 retlen = dirnam.nam$l_type - esa + 2;
3611 if (buf) retpath = buf;
3612 else if (ts) New(1314,retpath,retlen,char);
3613 else retpath = __pathify_retbuf;
3614 strcpy(retpath,esa);
3615 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3616 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3617 /* $PARSE may have upcased filespec, so convert output to lower
3618 * case if input contained any lowercase characters. */
3619 if (haslower) __mystrtolower(retpath);
3623 } /* end of do_pathify_dirspec() */
3625 /* External entry points */
3626 char *Perl_pathify_dirspec(pTHX_ char *dir, char *buf)
3627 { return do_pathify_dirspec(dir,buf,0); }
3628 char *Perl_pathify_dirspec_ts(pTHX_ char *dir, char *buf)
3629 { return do_pathify_dirspec(dir,buf,1); }
3631 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
3632 static char *mp_do_tounixspec(pTHX_ char *spec, char *buf, int ts)
3634 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
3635 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
3636 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
3637 unsigned short int trnlnm_iter_count;
3639 if (spec == NULL) return NULL;
3640 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
3641 if (buf) rslt = buf;
3643 retlen = strlen(spec);
3644 cp1 = strchr(spec,'[');
3645 if (!cp1) cp1 = strchr(spec,'<');
3647 for (cp1++; *cp1; cp1++) {
3648 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
3649 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
3650 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
3653 New(1315,rslt,retlen+2+2*expand,char);
3655 else rslt = __tounixspec_retbuf;
3656 if (strchr(spec,'/') != NULL) {
3663 dirend = strrchr(spec,']');
3664 if (dirend == NULL) dirend = strrchr(spec,'>');
3665 if (dirend == NULL) dirend = strchr(spec,':');
3666 if (dirend == NULL) {
3670 if (*cp2 != '[' && *cp2 != '<') {
3673 else { /* the VMS spec begins with directories */
3675 if (*cp2 == ']' || *cp2 == '>') {
3676 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
3679 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
3680 if (getcwd(tmp,sizeof tmp,1) == NULL) {
3681 if (ts) Safefree(rslt);
3684 trnlnm_iter_count = 0;
3687 while (*cp3 != ':' && *cp3) cp3++;
3689 if (strchr(cp3,']') != NULL) break;
3690 trnlnm_iter_count++;
3691 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
3692 } while (vmstrnenv(tmp,tmp,0,fildev,0));
3694 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
3695 retlen = devlen + dirlen;
3696 Renew(rslt,retlen+1+2*expand,char);
3702 *(cp1++) = *(cp3++);
3703 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
3707 else if ( *cp2 == '.') {
3708 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
3709 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3715 for (; cp2 <= dirend; cp2++) {
3718 if (*(cp2+1) == '[') cp2++;
3720 else if (*cp2 == ']' || *cp2 == '>') {
3721 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
3723 else if (*cp2 == '.') {
3725 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
3726 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
3727 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
3728 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
3729 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
3731 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
3732 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
3736 else if (*cp2 == '-') {
3737 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
3738 while (*cp2 == '-') {
3740 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3742 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
3743 if (ts) Safefree(rslt); /* filespecs like */
3744 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
3748 else *(cp1++) = *cp2;
3750 else *(cp1++) = *cp2;
3752 while (*cp2) *(cp1++) = *(cp2++);
3757 } /* end of do_tounixspec() */
3759 /* External entry points */
3760 char *Perl_tounixspec(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
3761 char *Perl_tounixspec_ts(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
3763 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
3764 static char *mp_do_tovmsspec(pTHX_ char *path, char *buf, int ts) {
3765 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
3766 char *rslt, *dirend;
3767 register char *cp1, *cp2;
3768 unsigned long int infront = 0, hasdir = 1;
3770 if (path == NULL) return NULL;
3771 if (buf) rslt = buf;
3772 else if (ts) New(1316,rslt,strlen(path)+9,char);
3773 else rslt = __tovmsspec_retbuf;
3774 if (strpbrk(path,"]:>") ||
3775 (dirend = strrchr(path,'/')) == NULL) {
3776 if (path[0] == '.') {
3777 if (path[1] == '\0') strcpy(rslt,"[]");
3778 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
3779 else strcpy(rslt,path); /* probably garbage */
3781 else strcpy(rslt,path);
3784 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
3785 if (!*(dirend+2)) dirend +=2;
3786 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
3787 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
3792 char trndev[NAM$C_MAXRSS+1];
3796 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
3798 if (!buf & ts) Renew(rslt,18,char);
3799 strcpy(rslt,"sys$disk:[000000]");
3802 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
3804 islnm = my_trnlnm(rslt,trndev,0);
3805 trnend = islnm ? strlen(trndev) - 1 : 0;
3806 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
3807 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
3808 /* If the first element of the path is a logical name, determine
3809 * whether it has to be translated so we can add more directories. */
3810 if (!islnm || rooted) {
3813 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
3817 if (cp2 != dirend) {
3818 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
3819 strcpy(rslt,trndev);
3820 cp1 = rslt + trnend;
3833 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
3834 cp2 += 2; /* skip over "./" - it's redundant */
3835 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
3837 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3838 *(cp1++) = '-'; /* "../" --> "-" */
3841 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
3842 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
3843 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3844 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
3847 if (cp2 > dirend) cp2 = dirend;
3849 else *(cp1++) = '.';
3851 for (; cp2 < dirend; cp2++) {
3853 if (*(cp2-1) == '/') continue;
3854 if (*(cp1-1) != '.') *(cp1++) = '.';
3857 else if (!infront && *cp2 == '.') {
3858 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
3859 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
3860 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3861 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
3862 else if (*(cp1-2) == '[') *(cp1-1) = '-';
3863 else { /* back up over previous directory name */
3865 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
3866 if (*(cp1-1) == '[') {
3867 memcpy(cp1,"000000.",7);
3872 if (cp2 == dirend) break;
3874 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
3875 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
3876 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
3877 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3879 *(cp1++) = '.'; /* Simulate trailing '/' */
3880 cp2 += 2; /* for loop will incr this to == dirend */
3882 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
3884 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
3887 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
3888 if (*cp2 == '.') *(cp1++) = '_';
3889 else *(cp1++) = *cp2;
3893 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
3894 if (hasdir) *(cp1++) = ']';
3895 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
3896 while (*cp2) *(cp1++) = *(cp2++);
3901 } /* end of do_tovmsspec() */
3903 /* External entry points */
3904 char *Perl_tovmsspec(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,0); }
3905 char *Perl_tovmsspec_ts(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,1); }
3907 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
3908 static char *mp_do_tovmspath(pTHX_ char *path, char *buf, int ts) {
3909 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
3911 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
3913 if (path == NULL) return NULL;
3914 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3915 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
3916 if (buf) return buf;
3918 vmslen = strlen(vmsified);
3919 New(1317,cp,vmslen+1,char);
3920 memcpy(cp,vmsified,vmslen);
3925 strcpy(__tovmspath_retbuf,vmsified);
3926 return __tovmspath_retbuf;
3929 } /* end of do_tovmspath() */
3931 /* External entry points */
3932 char *Perl_tovmspath(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,0); }
3933 char *Perl_tovmspath_ts(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,1); }
3936 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
3937 static char *mp_do_tounixpath(pTHX_ char *path, char *buf, int ts) {
3938 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
3940 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
3942 if (path == NULL) return NULL;
3943 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3944 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
3945 if (buf) return buf;
3947 unixlen = strlen(unixified);
3948 New(1317,cp,unixlen+1,char);
3949 memcpy(cp,unixified,unixlen);
3954 strcpy(__tounixpath_retbuf,unixified);
3955 return __tounixpath_retbuf;
3958 } /* end of do_tounixpath() */
3960 /* External entry points */
3961 char *Perl_tounixpath(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,0); }
3962 char *Perl_tounixpath_ts(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,1); }
3965 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
3967 *****************************************************************************
3969 * Copyright (C) 1989-1994 by *
3970 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
3972 * Permission is hereby granted for the reproduction of this software, *
3973 * on condition that this copyright notice is included in the reproduction, *
3974 * and that such reproduction is not for purposes of profit or material *
3977 * 27-Aug-1994 Modified for inclusion in perl5 *
3978 * by Charles Bailey bailey@newman.upenn.edu *
3979 *****************************************************************************
3983 * getredirection() is intended to aid in porting C programs
3984 * to VMS (Vax-11 C). The native VMS environment does not support
3985 * '>' and '<' I/O redirection, or command line wild card expansion,
3986 * or a command line pipe mechanism using the '|' AND background
3987 * command execution '&'. All of these capabilities are provided to any
3988 * C program which calls this procedure as the first thing in the
3990 * The piping mechanism will probably work with almost any 'filter' type
3991 * of program. With suitable modification, it may useful for other
3992 * portability problems as well.
3994 * Author: Mark Pizzolato mark@infocomm.com
3998 struct list_item *next;
4002 static void add_item(struct list_item **head,
4003 struct list_item **tail,
4007 static void mp_expand_wild_cards(pTHX_ char *item,
4008 struct list_item **head,
4009 struct list_item **tail,
4012 static int background_process(pTHX_ int argc, char **argv);
4014 static void pipe_and_fork(pTHX_ char **cmargv);
4016 /*{{{ void getredirection(int *ac, char ***av)*/
4018 mp_getredirection(pTHX_ int *ac, char ***av)
4020 * Process vms redirection arg's. Exit if any error is seen.
4021 * If getredirection() processes an argument, it is erased
4022 * from the vector. getredirection() returns a new argc and argv value.
4023 * In the event that a background command is requested (by a trailing "&"),
4024 * this routine creates a background subprocess, and simply exits the program.
4026 * Warning: do not try to simplify the code for vms. The code
4027 * presupposes that getredirection() is called before any data is
4028 * read from stdin or written to stdout.
4030 * Normal usage is as follows:
4036 * getredirection(&argc, &argv);
4040 int argc = *ac; /* Argument Count */
4041 char **argv = *av; /* Argument Vector */
4042 char *ap; /* Argument pointer */
4043 int j; /* argv[] index */
4044 int item_count = 0; /* Count of Items in List */
4045 struct list_item *list_head = 0; /* First Item in List */
4046 struct list_item *list_tail; /* Last Item in List */
4047 char *in = NULL; /* Input File Name */
4048 char *out = NULL; /* Output File Name */
4049 char *outmode = "w"; /* Mode to Open Output File */
4050 char *err = NULL; /* Error File Name */
4051 char *errmode = "w"; /* Mode to Open Error File */
4052 int cmargc = 0; /* Piped Command Arg Count */
4053 char **cmargv = NULL;/* Piped Command Arg Vector */
4056 * First handle the case where the last thing on the line ends with
4057 * a '&'. This indicates the desire for the command to be run in a
4058 * subprocess, so we satisfy that desire.
4061 if (0 == strcmp("&", ap))
4062 exit(background_process(aTHX_ --argc, argv));
4063 if (*ap && '&' == ap[strlen(ap)-1])
4065 ap[strlen(ap)-1] = '\0';
4066 exit(background_process(aTHX_ argc, argv));
4069 * Now we handle the general redirection cases that involve '>', '>>',
4070 * '<', and pipes '|'.
4072 for (j = 0; j < argc; ++j)
4074 if (0 == strcmp("<", argv[j]))
4078 fprintf(stderr,"No input file after < on command line");
4079 exit(LIB$_WRONUMARG);
4084 if ('<' == *(ap = argv[j]))
4089 if (0 == strcmp(">", ap))
4093 fprintf(stderr,"No output file after > on command line");
4094 exit(LIB$_WRONUMARG);
4113 fprintf(stderr,"No output file after > or >> on command line");
4114 exit(LIB$_WRONUMARG);
4118 if (('2' == *ap) && ('>' == ap[1]))
4135 fprintf(stderr,"No output file after 2> or 2>> on command line");
4136 exit(LIB$_WRONUMARG);
4140 if (0 == strcmp("|", argv[j]))
4144 fprintf(stderr,"No command into which to pipe on command line");
4145 exit(LIB$_WRONUMARG);
4147 cmargc = argc-(j+1);
4148 cmargv = &argv[j+1];
4152 if ('|' == *(ap = argv[j]))
4160 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
4163 * Allocate and fill in the new argument vector, Some Unix's terminate
4164 * the list with an extra null pointer.
4166 New(1302, argv, item_count+1, char *);
4168 for (j = 0; j < item_count; ++j, list_head = list_head->next)
4169 argv[j] = list_head->value;
4175 fprintf(stderr,"'|' and '>' may not both be specified on command line");
4176 exit(LIB$_INVARGORD);
4178 pipe_and_fork(aTHX_ cmargv);
4181 /* Check for input from a pipe (mailbox) */
4183 if (in == NULL && 1 == isapipe(0))
4185 char mbxname[L_tmpnam];
4187 long int dvi_item = DVI$_DEVBUFSIZ;
4188 $DESCRIPTOR(mbxnam, "");
4189 $DESCRIPTOR(mbxdevnam, "");
4191 /* Input from a pipe, reopen it in binary mode to disable */
4192 /* carriage control processing. */
4194 fgetname(stdin, mbxname);
4195 mbxnam.dsc$a_pointer = mbxname;
4196 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
4197 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
4198 mbxdevnam.dsc$a_pointer = mbxname;
4199 mbxdevnam.dsc$w_length = sizeof(mbxname);
4200 dvi_item = DVI$_DEVNAM;
4201 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
4202 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
4205 freopen(mbxname, "rb", stdin);
4208 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
4212 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
4214 fprintf(stderr,"Can't open input file %s as stdin",in);
4217 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
4219 fprintf(stderr,"Can't open output file %s as stdout",out);
4222 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
4225 if (strcmp(err,"&1") == 0) {
4226 dup2(fileno(stdout), fileno(stderr));
4227 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
4230 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
4232 fprintf(stderr,"Can't open error file %s as stderr",err);
4236 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
4240 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
4243 #ifdef ARGPROC_DEBUG
4244 PerlIO_printf(Perl_debug_log, "Arglist:\n");
4245 for (j = 0; j < *ac; ++j)
4246 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
4248 /* Clear errors we may have hit expanding wildcards, so they don't
4249 show up in Perl's $! later */
4250 set_errno(0); set_vaxc_errno(1);
4251 } /* end of getredirection() */
4254 static void add_item(struct list_item **head,
4255 struct list_item **tail,
4261 New(1303,*head,1,struct list_item);
4265 New(1304,(*tail)->next,1,struct list_item);
4266 *tail = (*tail)->next;
4268 (*tail)->value = value;
4272 static void mp_expand_wild_cards(pTHX_ char *item,
4273 struct list_item **head,
4274 struct list_item **tail,
4278 unsigned long int context = 0;
4285 char vmsspec[NAM$C_MAXRSS+1];
4286 $DESCRIPTOR(filespec, "");
4287 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
4288 $DESCRIPTOR(resultspec, "");
4289 unsigned long int zero = 0, sts;
4291 for (cp = item; *cp; cp++) {
4292 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
4293 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
4295 if (!*cp || isspace(*cp))
4297 add_item(head, tail, item, count);
4302 /* "double quoted" wild card expressions pass as is */
4303 /* From DCL that means using e.g.: */
4304 /* perl program """perl.*""" */
4305 item_len = strlen(item);
4306 if ( '"' == *item && '"' == item[item_len-1] )
4309 item[item_len-2] = '\0';
4310 add_item(head, tail, item, count);
4314 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
4315 resultspec.dsc$b_class = DSC$K_CLASS_D;
4316 resultspec.dsc$a_pointer = NULL;
4317 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
4318 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
4319 if (!isunix || !filespec.dsc$a_pointer)
4320 filespec.dsc$a_pointer = item;
4321 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
4323 * Only return version specs, if the caller specified a version
4325 had_version = strchr(item, ';');
4327 * Only return device and directory specs, if the caller specifed either.
4329 had_device = strchr(item, ':');
4330 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
4332 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
4333 &defaultspec, 0, 0, &zero))))
4338 New(1305,string,resultspec.dsc$w_length+1,char);
4339 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
4340 string[resultspec.dsc$w_length] = '\0';
4341 if (NULL == had_version)
4342 *((char *)strrchr(string, ';')) = '\0';
4343 if ((!had_directory) && (had_device == NULL))
4345 if (NULL == (devdir = strrchr(string, ']')))
4346 devdir = strrchr(string, '>');
4347 strcpy(string, devdir + 1);
4350 * Be consistent with what the C RTL has already done to the rest of
4351 * the argv items and lowercase all of these names.
4353 for (c = string; *c; ++c)
4356 if (isunix) trim_unixpath(string,item,1);
4357 add_item(head, tail, string, count);
4360 if (sts != RMS$_NMF)
4362 set_vaxc_errno(sts);
4365 case RMS$_FNF: case RMS$_DNF:
4366 set_errno(ENOENT); break;
4368 set_errno(ENOTDIR); break;
4370 set_errno(ENODEV); break;
4371 case RMS$_FNM: case RMS$_SYN:
4372 set_errno(EINVAL); break;
4374 set_errno(EACCES); break;
4376 _ckvmssts_noperl(sts);
4380 add_item(head, tail, item, count);
4381 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
4382 _ckvmssts_noperl(lib$find_file_end(&context));
4385 static int child_st[2];/* Event Flag set when child process completes */
4387 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
4389 static unsigned long int exit_handler(int *status)
4393 if (0 == child_st[0])
4395 #ifdef ARGPROC_DEBUG
4396 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
4398 fflush(stdout); /* Have to flush pipe for binary data to */
4399 /* terminate properly -- <tp@mccall.com> */
4400 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
4401 sys$dassgn(child_chan);
4403 sys$synch(0, child_st);
4408 static void sig_child(int chan)
4410 #ifdef ARGPROC_DEBUG
4411 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
4413 if (child_st[0] == 0)
4417 static struct exit_control_block exit_block =
4422 &exit_block.exit_status,
4427 pipe_and_fork(pTHX_ char **cmargv)
4430 struct dsc$descriptor_s *vmscmd;
4431 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
4432 int sts, j, l, ismcr, quote, tquote = 0;
4434 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
4435 vms_execfree(vmscmd);
4440 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
4441 && toupper(*(q+2)) == 'R' && !*(q+3);
4443 while (q && l < MAX_DCL_LINE_LENGTH) {
4445 if (j > 0 && quote) {
4451 if (ismcr && j > 1) quote = 1;
4452 tquote = (strchr(q,' ')) != NULL || *q == '\0';
4455 if (quote || tquote) {
4461 if ((quote||tquote) && *q == '"') {
4471 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
4473 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
4477 static int background_process(pTHX_ int argc, char **argv)
4479 char command[2048] = "$";
4480 $DESCRIPTOR(value, "");
4481 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
4482 static $DESCRIPTOR(null, "NLA0:");
4483 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
4485 $DESCRIPTOR(pidstr, "");
4487 unsigned long int flags = 17, one = 1, retsts;
4489 strcat(command, argv[0]);
4492 strcat(command, " \"");
4493 strcat(command, *(++argv));
4494 strcat(command, "\"");
4496 value.dsc$a_pointer = command;
4497 value.dsc$w_length = strlen(value.dsc$a_pointer);
4498 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
4499 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
4500 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
4501 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
4504 _ckvmssts_noperl(retsts);
4506 #ifdef ARGPROC_DEBUG
4507 PerlIO_printf(Perl_debug_log, "%s\n", command);
4509 sprintf(pidstring, "%08X", pid);
4510 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
4511 pidstr.dsc$a_pointer = pidstring;
4512 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
4513 lib$set_symbol(&pidsymbol, &pidstr);
4517 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
4520 /* OS-specific initialization at image activation (not thread startup) */
4521 /* Older VAXC header files lack these constants */
4522 #ifndef JPI$_RIGHTS_SIZE
4523 # define JPI$_RIGHTS_SIZE 817
4525 #ifndef KGB$M_SUBSYSTEM
4526 # define KGB$M_SUBSYSTEM 0x8
4529 /*{{{void vms_image_init(int *, char ***)*/
4531 vms_image_init(int *argcp, char ***argvp)
4533 char eqv[LNM$C_NAMLENGTH+1] = "";
4534 unsigned int len, tabct = 8, tabidx = 0;
4535 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
4536 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
4537 unsigned short int dummy, rlen;
4538 struct dsc$descriptor_s **tabvec;
4539 #if defined(PERL_IMPLICIT_CONTEXT)
4542 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
4543 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
4544 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
4547 #ifdef KILL_BY_SIGPRC
4548 (void) Perl_csighandler_init();
4551 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
4552 _ckvmssts_noperl(iosb[0]);
4553 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
4554 if (iprv[i]) { /* Running image installed with privs? */
4555 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
4560 /* Rights identifiers might trigger tainting as well. */
4561 if (!will_taint && (rlen || rsz)) {
4562 while (rlen < rsz) {
4563 /* We didn't get all the identifiers on the first pass. Allocate a
4564 * buffer much larger than $GETJPI wants (rsz is size in bytes that
4565 * were needed to hold all identifiers at time of last call; we'll
4566 * allocate that many unsigned long ints), and go back and get 'em.
4567 * If it gave us less than it wanted to despite ample buffer space,
4568 * something's broken. Is your system missing a system identifier?
4570 if (rsz <= jpilist[1].buflen) {
4571 /* Perl_croak accvios when used this early in startup. */
4572 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
4573 rsz, (unsigned long) jpilist[1].buflen,
4574 "Check your rights database for corruption.\n");
4577 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
4578 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
4579 jpilist[1].buflen = rsz * sizeof(unsigned long int);
4580 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
4581 _ckvmssts_noperl(iosb[0]);
4583 mask = jpilist[1].bufadr;
4584 /* Check attribute flags for each identifier (2nd longword); protected
4585 * subsystem identifiers trigger tainting.
4587 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
4588 if (mask[i] & KGB$M_SUBSYSTEM) {
4593 if (mask != rlst) Safefree(mask);
4595 /* We need to use this hack to tell Perl it should run with tainting,
4596 * since its tainting flag may be part of the PL_curinterp struct, which
4597 * hasn't been allocated when vms_image_init() is called.
4600 char **newargv, **oldargv;
4602 New(1320,newargv,(*argcp)+2,char *);
4603 newargv[0] = oldargv[0];
4604 New(1320,newargv[1],3,char);
4605 strcpy(newargv[1], "-T");
4606 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
4608 newargv[*argcp] = NULL;
4609 /* We orphan the old argv, since we don't know where it's come from,
4610 * so we don't know how to free it.
4614 else { /* Did user explicitly request tainting? */
4616 char *cp, **av = *argvp;
4617 for (i = 1; i < *argcp; i++) {
4618 if (*av[i] != '-') break;
4619 for (cp = av[i]+1; *cp; cp++) {
4620 if (*cp == 'T') { will_taint = 1; break; }
4621 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
4622 strchr("DFIiMmx",*cp)) break;
4624 if (will_taint) break;
4629 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
4631 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
4632 else if (tabidx >= tabct) {
4634 Renew(tabvec,tabct,struct dsc$descriptor_s *);
4636 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
4637 tabvec[tabidx]->dsc$w_length = 0;
4638 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
4639 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
4640 tabvec[tabidx]->dsc$a_pointer = NULL;
4641 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
4643 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
4645 getredirection(argcp,argvp);
4646 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
4648 # include <reentrancy.h>
4649 (void) decc$set_reentrancy(C$C_MULTITHREAD);
4658 * Trim Unix-style prefix off filespec, so it looks like what a shell
4659 * glob expansion would return (i.e. from specified prefix on, not
4660 * full path). Note that returned filespec is Unix-style, regardless
4661 * of whether input filespec was VMS-style or Unix-style.
4663 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
4664 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
4665 * vector of options; at present, only bit 0 is used, and if set tells
4666 * trim unixpath to try the current default directory as a prefix when
4667 * presented with a possibly ambiguous ... wildcard.
4669 * Returns !=0 on success, with trimmed filespec replacing contents of
4670 * fspec, and 0 on failure, with contents of fpsec unchanged.
4672 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
4674 Perl_trim_unixpath(pTHX_ char *fspec, char *wildspec, int opts)
4676 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
4677 *template, *base, *end, *cp1, *cp2;
4678 register int tmplen, reslen = 0, dirs = 0;
4680 if (!wildspec || !fspec) return 0;
4681 if (strpbrk(wildspec,"]>:") != NULL) {
4682 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
4683 else template = unixwild;
4685 else template = wildspec;
4686 if (strpbrk(fspec,"]>:") != NULL) {
4687 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
4688 else base = unixified;
4689 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
4690 * check to see that final result fits into (isn't longer than) fspec */
4691 reslen = strlen(fspec);
4695 /* No prefix or absolute path on wildcard, so nothing to remove */
4696 if (!*template || *template == '/') {
4697 if (base == fspec) return 1;
4698 tmplen = strlen(unixified);
4699 if (tmplen > reslen) return 0; /* not enough space */
4700 /* Copy unixified resultant, including trailing NUL */
4701 memmove(fspec,unixified,tmplen+1);
4705 for (end = base; *end; end++) ; /* Find end of resultant filespec */
4706 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
4707 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
4708 for (cp1 = end ;cp1 >= base; cp1--)
4709 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
4711 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
4715 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
4716 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
4717 int ells = 1, totells, segdirs, match;
4718 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
4719 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4721 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
4723 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
4724 if (ellipsis == template && opts & 1) {
4725 /* Template begins with an ellipsis. Since we can't tell how many
4726 * directory names at the front of the resultant to keep for an
4727 * arbitrary starting point, we arbitrarily choose the current
4728 * default directory as a starting point. If it's there as a prefix,
4729 * clip it off. If not, fall through and act as if the leading
4730 * ellipsis weren't there (i.e. return shortest possible path that
4731 * could match template).
4733 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
4734 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4735 if (_tolower(*cp1) != _tolower(*cp2)) break;
4736 segdirs = dirs - totells; /* Min # of dirs we must have left */
4737 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
4738 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
4739 memcpy(fspec,cp2+1,end - cp2);
4743 /* First off, back up over constant elements at end of path */
4745 for (front = end ; front >= base; front--)
4746 if (*front == '/' && !dirs--) { front++; break; }
4748 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
4749 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
4750 if (cp1 != '\0') return 0; /* Path too long. */
4752 *cp2 = '\0'; /* Pick up with memcpy later */
4753 lcfront = lcres + (front - base);
4754 /* Now skip over each ellipsis and try to match the path in front of it. */
4756 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
4757 if (*(cp1) == '.' && *(cp1+1) == '.' &&
4758 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
4759 if (cp1 < template) break; /* template started with an ellipsis */
4760 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
4761 ellipsis = cp1; continue;
4763 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
4765 for (segdirs = 0, cp2 = tpl;
4766 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
4768 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
4769 else *cp2 = _tolower(*cp1); /* else lowercase for match */
4770 if (*cp2 == '/') segdirs++;
4772 if (cp1 != ellipsis - 1) return 0; /* Path too long */
4773 /* Back up at least as many dirs as in template before matching */
4774 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
4775 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
4776 for (match = 0; cp1 > lcres;) {
4777 resdsc.dsc$a_pointer = cp1;
4778 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
4780 if (match == 1) lcfront = cp1;
4782 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
4784 if (!match) return 0; /* Can't find prefix ??? */
4785 if (match > 1 && opts & 1) {
4786 /* This ... wildcard could cover more than one set of dirs (i.e.
4787 * a set of similar dir names is repeated). If the template
4788 * contains more than 1 ..., upstream elements could resolve the
4789 * ambiguity, but it's not worth a full backtracking setup here.
4790 * As a quick heuristic, clip off the current default directory
4791 * if it's present to find the trimmed spec, else use the
4792 * shortest string that this ... could cover.
4794 char def[NAM$C_MAXRSS+1], *st;
4796 if (getcwd(def, sizeof def,0) == NULL) return 0;
4797 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4798 if (_tolower(*cp1) != _tolower(*cp2)) break;
4799 segdirs = dirs - totells; /* Min # of dirs we must have left */
4800 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
4801 if (*cp1 == '\0' && *cp2 == '/') {
4802 memcpy(fspec,cp2+1,end - cp2);
4805 /* Nope -- stick with lcfront from above and keep going. */
4808 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
4813 } /* end of trim_unixpath() */
4818 * VMS readdir() routines.
4819 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
4821 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
4822 * Minor modifications to original routines.
4825 /* readdir may have been redefined by reentr.h, so make sure we get
4826 * the local version for what we do here.
4831 #if !defined(PERL_IMPLICIT_CONTEXT)
4832 # define readdir Perl_readdir
4834 # define readdir(a) Perl_readdir(aTHX_ a)
4837 /* Number of elements in vms_versions array */
4838 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
4841 * Open a directory, return a handle for later use.
4843 /*{{{ DIR *opendir(char*name) */
4845 Perl_opendir(pTHX_ char *name)
4848 char dir[NAM$C_MAXRSS+1];
4851 if (do_tovmspath(name,dir,0) == NULL) {
4854 /* Check access before stat; otherwise stat does not
4855 * accurately report whether it's a directory.
4857 if (!cando_by_name(S_IRUSR,0,dir)) {
4858 /* cando_by_name has already set errno */
4861 if (flex_stat(dir,&sb) == -1) return NULL;
4862 if (!S_ISDIR(sb.st_mode)) {
4863 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
4866 /* Get memory for the handle, and the pattern. */
4868 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
4870 /* Fill in the fields; mainly playing with the descriptor. */
4871 (void)sprintf(dd->pattern, "%s*.*",dir);
4874 dd->vms_wantversions = 0;
4875 dd->pat.dsc$a_pointer = dd->pattern;
4876 dd->pat.dsc$w_length = strlen(dd->pattern);
4877 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
4878 dd->pat.dsc$b_class = DSC$K_CLASS_S;
4879 #if defined(USE_ITHREADS)
4880 New(1308,dd->mutex,1,perl_mutex);
4881 MUTEX_INIT( (perl_mutex *) dd->mutex );
4887 } /* end of opendir() */
4891 * Set the flag to indicate we want versions or not.
4893 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
4895 vmsreaddirversions(DIR *dd, int flag)
4897 dd->vms_wantversions = flag;
4902 * Free up an opened directory.
4904 /*{{{ void closedir(DIR *dd)*/
4908 (void)lib$find_file_end(&dd->context);
4909 Safefree(dd->pattern);
4910 #if defined(USE_ITHREADS)
4911 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
4912 Safefree(dd->mutex);
4914 Safefree((char *)dd);
4919 * Collect all the version numbers for the current file.
4922 collectversions(pTHX_ DIR *dd)
4924 struct dsc$descriptor_s pat;
4925 struct dsc$descriptor_s res;
4927 char *p, *text, buff[sizeof dd->entry.d_name];
4929 unsigned long context, tmpsts;
4931 /* Convenient shorthand. */
4934 /* Add the version wildcard, ignoring the "*.*" put on before */
4935 i = strlen(dd->pattern);
4936 New(1308,text,i + e->d_namlen + 3,char);
4937 (void)strcpy(text, dd->pattern);
4938 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
4940 /* Set up the pattern descriptor. */
4941 pat.dsc$a_pointer = text;
4942 pat.dsc$w_length = i + e->d_namlen - 1;
4943 pat.dsc$b_dtype = DSC$K_DTYPE_T;
4944 pat.dsc$b_class = DSC$K_CLASS_S;
4946 /* Set up result descriptor. */
4947 res.dsc$a_pointer = buff;
4948 res.dsc$w_length = sizeof buff - 2;
4949 res.dsc$b_dtype = DSC$K_DTYPE_T;
4950 res.dsc$b_class = DSC$K_CLASS_S;
4952 /* Read files, collecting versions. */
4953 for (context = 0, e->vms_verscount = 0;
4954 e->vms_verscount < VERSIZE(e);
4955 e->vms_verscount++) {
4956 tmpsts = lib$find_file(&pat, &res, &context);
4957 if (tmpsts == RMS$_NMF || context == 0) break;
4959 buff[sizeof buff - 1] = '\0';
4960 if ((p = strchr(buff, ';')))
4961 e->vms_versions[e->vms_verscount] = atoi(p + 1);
4963 e->vms_versions[e->vms_verscount] = -1;
4966 _ckvmssts(lib$find_file_end(&context));
4969 } /* end of collectversions() */
4972 * Read the next entry from the directory.
4974 /*{{{ struct dirent *readdir(DIR *dd)*/
4976 Perl_readdir(pTHX_ DIR *dd)
4978 struct dsc$descriptor_s res;
4979 char *p, buff[sizeof dd->entry.d_name];
4980 unsigned long int tmpsts;
4982 /* Set up result descriptor, and get next file. */
4983 res.dsc$a_pointer = buff;
4984 res.dsc$w_length = sizeof buff - 2;
4985 res.dsc$b_dtype = DSC$K_DTYPE_T;
4986 res.dsc$b_class = DSC$K_CLASS_S;
4987 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4988 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
4989 if (!(tmpsts & 1)) {
4990 set_vaxc_errno(tmpsts);
4993 set_errno(EACCES); break;
4995 set_errno(ENODEV); break;
4997 set_errno(ENOTDIR); break;
4998 case RMS$_FNF: case RMS$_DNF:
4999 set_errno(ENOENT); break;
5006 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
5007 buff[sizeof buff - 1] = '\0';
5008 for (p = buff; *p; p++) *p = _tolower(*p);
5009 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
5012 /* Skip any directory component and just copy the name. */
5013 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
5014 else (void)strcpy(dd->entry.d_name, buff);
5016 /* Clobber the version. */
5017 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
5019 dd->entry.d_namlen = strlen(dd->entry.d_name);
5020 dd->entry.vms_verscount = 0;
5021 if (dd->vms_wantversions) collectversions(aTHX_ dd);
5024 } /* end of readdir() */
5028 * Read the next entry from the directory -- thread-safe version.
5030 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
5032 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
5036 MUTEX_LOCK( (perl_mutex *) dd->mutex );
5038 entry = readdir(dd);
5040 retval = ( *result == NULL ? errno : 0 );
5042 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
5046 } /* end of readdir_r() */
5050 * Return something that can be used in a seekdir later.
5052 /*{{{ long telldir(DIR *dd)*/
5061 * Return to a spot where we used to be. Brute force.
5063 /*{{{ void seekdir(DIR *dd,long count)*/
5065 Perl_seekdir(pTHX_ DIR *dd, long count)
5067 int vms_wantversions;
5069 /* If we haven't done anything yet... */
5073 /* Remember some state, and clear it. */
5074 vms_wantversions = dd->vms_wantversions;
5075 dd->vms_wantversions = 0;
5076 _ckvmssts(lib$find_file_end(&dd->context));
5079 /* The increment is in readdir(). */
5080 for (dd->count = 0; dd->count < count; )
5083 dd->vms_wantversions = vms_wantversions;
5085 } /* end of seekdir() */
5088 /* VMS subprocess management
5090 * my_vfork() - just a vfork(), after setting a flag to record that
5091 * the current script is trying a Unix-style fork/exec.
5093 * vms_do_aexec() and vms_do_exec() are called in response to the
5094 * perl 'exec' function. If this follows a vfork call, then they
5095 * call out the regular perl routines in doio.c which do an
5096 * execvp (for those who really want to try this under VMS).
5097 * Otherwise, they do exactly what the perl docs say exec should
5098 * do - terminate the current script and invoke a new command
5099 * (See below for notes on command syntax.)
5101 * do_aspawn() and do_spawn() implement the VMS side of the perl
5102 * 'system' function.
5104 * Note on command arguments to perl 'exec' and 'system': When handled
5105 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
5106 * are concatenated to form a DCL command string. If the first arg
5107 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
5108 * the command string is handed off to DCL directly. Otherwise,
5109 * the first token of the command is taken as the filespec of an image
5110 * to run. The filespec is expanded using a default type of '.EXE' and
5111 * the process defaults for device, directory, etc., and if found, the resultant
5112 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
5113 * the command string as parameters. This is perhaps a bit complicated,
5114 * but I hope it will form a happy medium between what VMS folks expect
5115 * from lib$spawn and what Unix folks expect from exec.
5118 static int vfork_called;
5120 /*{{{int my_vfork()*/
5131 vms_execfree(struct dsc$descriptor_s *vmscmd)
5134 if (vmscmd->dsc$a_pointer) {
5135 Safefree(vmscmd->dsc$a_pointer);
5142 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
5144 char *junk, *tmps = Nullch;
5145 register size_t cmdlen = 0;
5152 tmps = SvPV(really,rlen);
5159 for (idx++; idx <= sp; idx++) {
5161 junk = SvPVx(*idx,rlen);
5162 cmdlen += rlen ? rlen + 1 : 0;
5165 New(401,PL_Cmd,cmdlen+1,char);
5167 if (tmps && *tmps) {
5168 strcpy(PL_Cmd,tmps);
5171 else *PL_Cmd = '\0';
5172 while (++mark <= sp) {
5174 char *s = SvPVx(*mark,n_a);
5176 if (*PL_Cmd) strcat(PL_Cmd," ");
5182 } /* end of setup_argstr() */
5185 static unsigned long int
5186 setup_cmddsc(pTHX_ char *cmd, int check_img, int *suggest_quote,
5187 struct dsc$descriptor_s **pvmscmd)
5189 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
5190 $DESCRIPTOR(defdsc,".EXE");
5191 $DESCRIPTOR(defdsc2,".");
5192 $DESCRIPTOR(resdsc,resspec);
5193 struct dsc$descriptor_s *vmscmd;
5194 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5195 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
5196 register char *s, *rest, *cp, *wordbreak;
5199 New(402,vmscmd,sizeof(struct dsc$descriptor_s),struct dsc$descriptor_s);
5200 vmscmd->dsc$a_pointer = NULL;
5201 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
5202 vmscmd->dsc$b_class = DSC$K_CLASS_S;
5203 vmscmd->dsc$w_length = 0;
5204 if (pvmscmd) *pvmscmd = vmscmd;
5206 if (suggest_quote) *suggest_quote = 0;
5208 if (strlen(cmd) > MAX_DCL_LINE_LENGTH)
5209 return CLI$_BUFOVF; /* continuation lines currently unsupported */
5211 while (*s && isspace(*s)) s++;
5213 if (*s == '@' || *s == '$') {
5214 vmsspec[0] = *s; rest = s + 1;
5215 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
5217 else { cp = vmsspec; rest = s; }
5218 if (*rest == '.' || *rest == '/') {
5221 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
5222 rest++, cp2++) *cp2 = *rest;
5224 if (do_tovmsspec(resspec,cp,0)) {
5227 for (cp2 = vmsspec + strlen(vmsspec);
5228 *rest && cp2 - vmsspec < sizeof vmsspec;
5229 rest++, cp2++) *cp2 = *rest;
5234 /* Intuit whether verb (first word of cmd) is a DCL command:
5235 * - if first nonspace char is '@', it's a DCL indirection
5237 * - if verb contains a filespec separator, it's not a DCL command
5238 * - if it doesn't, caller tells us whether to default to a DCL
5239 * command, or to a local image unless told it's DCL (by leading '$')
5243 if (suggest_quote) *suggest_quote = 1;
5245 register char *filespec = strpbrk(s,":<[.;");
5246 rest = wordbreak = strpbrk(s," \"\t/");
5247 if (!wordbreak) wordbreak = s + strlen(s);
5248 if (*s == '$') check_img = 0;
5249 if (filespec && (filespec < wordbreak)) isdcl = 0;
5250 else isdcl = !check_img;
5254 imgdsc.dsc$a_pointer = s;
5255 imgdsc.dsc$w_length = wordbreak - s;
5256 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
5258 _ckvmssts(lib$find_file_end(&cxt));
5259 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
5260 if (!(retsts & 1) && *s == '$') {
5261 _ckvmssts(lib$find_file_end(&cxt));
5262 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
5263 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
5265 _ckvmssts(lib$find_file_end(&cxt));
5266 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
5270 _ckvmssts(lib$find_file_end(&cxt));
5275 while (*s && !isspace(*s)) s++;
5278 /* check that it's really not DCL with no file extension */
5279 fp = fopen(resspec,"r","ctx=bin,shr=get");
5281 char b[4] = {0,0,0,0};
5282 read(fileno(fp),b,4);
5283 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
5286 if (check_img && isdcl) return RMS$_FNF;
5288 if (cando_by_name(S_IXUSR,0,resspec)) {
5289 New(402,vmscmd->dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
5291 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
5292 if (suggest_quote) *suggest_quote = 1;
5294 strcpy(vmscmd->dsc$a_pointer,"@");
5295 if (suggest_quote) *suggest_quote = 1;
5297 strcat(vmscmd->dsc$a_pointer,resspec);
5298 if (rest) strcat(vmscmd->dsc$a_pointer,rest);
5299 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
5300 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
5302 else retsts = RMS$_PRV;
5305 /* It's either a DCL command or we couldn't find a suitable image */
5306 vmscmd->dsc$w_length = strlen(cmd);
5307 /* if (cmd == PL_Cmd) {
5308 vmscmd->dsc$a_pointer = PL_Cmd;
5309 if (suggest_quote) *suggest_quote = 1;
5312 vmscmd->dsc$a_pointer = savepvn(cmd,vmscmd->dsc$w_length);
5314 /* check if it's a symbol (for quoting purposes) */
5315 if (suggest_quote && !*suggest_quote) {
5317 char equiv[LNM$C_NAMLENGTH];
5318 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5319 eqvdsc.dsc$a_pointer = equiv;
5321 iss = lib$get_symbol(vmscmd,&eqvdsc);
5322 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
5324 if (!(retsts & 1)) {
5325 /* just hand off status values likely to be due to user error */
5326 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
5327 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
5328 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
5329 else { _ckvmssts(retsts); }
5332 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
5334 } /* end of setup_cmddsc() */
5337 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
5339 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
5342 if (vfork_called) { /* this follows a vfork - act Unixish */
5344 if (vfork_called < 0) {
5345 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
5348 else return do_aexec(really,mark,sp);
5350 /* no vfork - act VMSish */
5351 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
5356 } /* end of vms_do_aexec() */
5359 /* {{{bool vms_do_exec(char *cmd) */
5361 Perl_vms_do_exec(pTHX_ char *cmd)
5363 struct dsc$descriptor_s *vmscmd;
5365 if (vfork_called) { /* this follows a vfork - act Unixish */
5367 if (vfork_called < 0) {
5368 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
5371 else return do_exec(cmd);
5374 { /* no vfork - act VMSish */
5375 unsigned long int retsts;
5378 TAINT_PROPER("exec");
5379 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
5380 retsts = lib$do_command(vmscmd);
5383 case RMS$_FNF: case RMS$_DNF:
5384 set_errno(ENOENT); break;
5386 set_errno(ENOTDIR); break;
5388 set_errno(ENODEV); break;
5390 set_errno(EACCES); break;
5392 set_errno(EINVAL); break;
5393 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
5394 set_errno(E2BIG); break;
5395 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
5396 _ckvmssts(retsts); /* fall through */
5397 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
5400 set_vaxc_errno(retsts);
5401 if (ckWARN(WARN_EXEC)) {
5402 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
5403 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
5405 vms_execfree(vmscmd);
5410 } /* end of vms_do_exec() */
5413 unsigned long int Perl_do_spawn(pTHX_ char *);
5415 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
5417 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
5419 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
5422 } /* end of do_aspawn() */
5425 /* {{{unsigned long int do_spawn(char *cmd) */
5427 Perl_do_spawn(pTHX_ char *cmd)
5429 unsigned long int sts, substs;
5432 TAINT_PROPER("spawn");
5433 if (!cmd || !*cmd) {
5434 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
5437 case RMS$_FNF: case RMS$_DNF:
5438 set_errno(ENOENT); break;
5440 set_errno(ENOTDIR); break;
5442 set_errno(ENODEV); break;
5444 set_errno(EACCES); break;
5446 set_errno(EINVAL); break;
5447 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
5448 set_errno(E2BIG); break;
5449 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
5450 _ckvmssts(sts); /* fall through */
5451 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
5454 set_vaxc_errno(sts);
5455 if (ckWARN(WARN_EXEC)) {
5456 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
5463 (void) safe_popen(aTHX_ cmd, "nW", (int *)&sts);
5466 } /* end of do_spawn() */
5470 static unsigned int *sockflags, sockflagsize;
5473 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
5474 * routines found in some versions of the CRTL can't deal with sockets.
5475 * We don't shim the other file open routines since a socket isn't
5476 * likely to be opened by a name.
5478 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
5479 FILE *my_fdopen(int fd, const char *mode)
5481 FILE *fp = fdopen(fd, (char *) mode);
5484 unsigned int fdoff = fd / sizeof(unsigned int);
5485 struct stat sbuf; /* native stat; we don't need flex_stat */
5486 if (!sockflagsize || fdoff > sockflagsize) {
5487 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
5488 else New (1324,sockflags,fdoff+2,unsigned int);
5489 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
5490 sockflagsize = fdoff + 2;
5492 if (fstat(fd,&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
5493 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
5502 * Clear the corresponding bit when the (possibly) socket stream is closed.
5503 * There still a small hole: we miss an implicit close which might occur
5504 * via freopen(). >> Todo
5506 /*{{{ int my_fclose(FILE *fp)*/
5507 int my_fclose(FILE *fp) {
5509 unsigned int fd = fileno(fp);
5510 unsigned int fdoff = fd / sizeof(unsigned int);
5512 if (sockflagsize && fdoff <= sockflagsize)
5513 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
5521 * A simple fwrite replacement which outputs itmsz*nitm chars without
5522 * introducing record boundaries every itmsz chars.
5523 * We are using fputs, which depends on a terminating null. We may
5524 * well be writing binary data, so we need to accommodate not only
5525 * data with nulls sprinkled in the middle but also data with no null
5528 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
5530 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
5532 register char *cp, *end, *cpd, *data;
5533 register unsigned int fd = fileno(dest);
5534 register unsigned int fdoff = fd / sizeof(unsigned int);
5536 int bufsize = itmsz * nitm + 1;
5538 if (fdoff < sockflagsize &&
5539 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
5540 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
5544 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
5545 memcpy( data, src, itmsz*nitm );
5546 data[itmsz*nitm] = '\0';
5548 end = data + itmsz * nitm;
5549 retval = (int) nitm; /* on success return # items written */
5552 while (cpd <= end) {
5553 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
5554 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
5556 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
5560 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
5563 } /* end of my_fwrite() */
5566 /*{{{ int my_flush(FILE *fp)*/
5568 Perl_my_flush(pTHX_ FILE *fp)
5571 if ((res = fflush(fp)) == 0 && fp) {
5572 #ifdef VMS_DO_SOCKETS
5574 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
5576 res = fsync(fileno(fp));
5579 * If the flush succeeded but set end-of-file, we need to clear
5580 * the error because our caller may check ferror(). BTW, this
5581 * probably means we just flushed an empty file.
5583 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
5590 * Here are replacements for the following Unix routines in the VMS environment:
5591 * getpwuid Get information for a particular UIC or UID
5592 * getpwnam Get information for a named user
5593 * getpwent Get information for each user in the rights database
5594 * setpwent Reset search to the start of the rights database
5595 * endpwent Finish searching for users in the rights database
5597 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
5598 * (defined in pwd.h), which contains the following fields:-
5600 * char *pw_name; Username (in lower case)
5601 * char *pw_passwd; Hashed password
5602 * unsigned int pw_uid; UIC
5603 * unsigned int pw_gid; UIC group number
5604 * char *pw_unixdir; Default device/directory (VMS-style)
5605 * char *pw_gecos; Owner name
5606 * char *pw_dir; Default device/directory (Unix-style)
5607 * char *pw_shell; Default CLI name (eg. DCL)
5609 * If the specified user does not exist, getpwuid and getpwnam return NULL.
5611 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
5612 * not the UIC member number (eg. what's returned by getuid()),
5613 * getpwuid() can accept either as input (if uid is specified, the caller's
5614 * UIC group is used), though it won't recognise gid=0.
5616 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
5617 * information about other users in your group or in other groups, respectively.
5618 * If the required privilege is not available, then these routines fill only
5619 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
5622 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
5625 /* sizes of various UAF record fields */
5626 #define UAI$S_USERNAME 12
5627 #define UAI$S_IDENT 31
5628 #define UAI$S_OWNER 31
5629 #define UAI$S_DEFDEV 31
5630 #define UAI$S_DEFDIR 63
5631 #define UAI$S_DEFCLI 31
5634 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
5635 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
5636 (uic).uic$v_group != UIC$K_WILD_GROUP)
5638 static char __empty[]= "";
5639 static struct passwd __passwd_empty=
5640 {(char *) __empty, (char *) __empty, 0, 0,
5641 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
5642 static int contxt= 0;
5643 static struct passwd __pwdcache;
5644 static char __pw_namecache[UAI$S_IDENT+1];
5647 * This routine does most of the work extracting the user information.
5649 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
5652 unsigned char length;
5653 char pw_gecos[UAI$S_OWNER+1];
5655 static union uicdef uic;
5657 unsigned char length;
5658 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
5661 unsigned char length;
5662 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
5665 unsigned char length;
5666 char pw_shell[UAI$S_DEFCLI+1];
5668 static char pw_passwd[UAI$S_PWD+1];
5670 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
5671 struct dsc$descriptor_s name_desc;
5672 unsigned long int sts;
5674 static struct itmlst_3 itmlst[]= {
5675 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
5676 {sizeof(uic), UAI$_UIC, &uic, &luic},
5677 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
5678 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
5679 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
5680 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
5681 {0, 0, NULL, NULL}};
5683 name_desc.dsc$w_length= strlen(name);
5684 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5685 name_desc.dsc$b_class= DSC$K_CLASS_S;
5686 name_desc.dsc$a_pointer= (char *) name;
5688 /* Note that sys$getuai returns many fields as counted strings. */
5689 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
5690 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
5691 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
5693 else { _ckvmssts(sts); }
5694 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
5696 if ((int) owner.length < lowner) lowner= (int) owner.length;
5697 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
5698 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
5699 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
5700 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
5701 owner.pw_gecos[lowner]= '\0';
5702 defdev.pw_dir[ldefdev+ldefdir]= '\0';
5703 defcli.pw_shell[ldefcli]= '\0';
5704 if (valid_uic(uic)) {
5705 pwd->pw_uid= uic.uic$l_uic;
5706 pwd->pw_gid= uic.uic$v_group;
5709 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
5710 pwd->pw_passwd= pw_passwd;
5711 pwd->pw_gecos= owner.pw_gecos;
5712 pwd->pw_dir= defdev.pw_dir;
5713 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
5714 pwd->pw_shell= defcli.pw_shell;
5715 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
5717 ldir= strlen(pwd->pw_unixdir) - 1;
5718 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
5721 strcpy(pwd->pw_unixdir, pwd->pw_dir);
5722 __mystrtolower(pwd->pw_unixdir);
5727 * Get information for a named user.
5729 /*{{{struct passwd *getpwnam(char *name)*/
5730 struct passwd *Perl_my_getpwnam(pTHX_ char *name)
5732 struct dsc$descriptor_s name_desc;
5734 unsigned long int status, sts;
5736 __pwdcache = __passwd_empty;
5737 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
5738 /* We still may be able to determine pw_uid and pw_gid */
5739 name_desc.dsc$w_length= strlen(name);
5740 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5741 name_desc.dsc$b_class= DSC$K_CLASS_S;
5742 name_desc.dsc$a_pointer= (char *) name;
5743 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
5744 __pwdcache.pw_uid= uic.uic$l_uic;
5745 __pwdcache.pw_gid= uic.uic$v_group;
5748 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
5749 set_vaxc_errno(sts);
5750 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
5753 else { _ckvmssts(sts); }
5756 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
5757 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
5758 __pwdcache.pw_name= __pw_namecache;
5760 } /* end of my_getpwnam() */
5764 * Get information for a particular UIC or UID.
5765 * Called by my_getpwent with uid=-1 to list all users.
5767 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
5768 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
5770 const $DESCRIPTOR(name_desc,__pw_namecache);
5771 unsigned short lname;
5773 unsigned long int status;
5775 if (uid == (unsigned int) -1) {
5777 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
5778 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
5779 set_vaxc_errno(status);
5780 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5784 else { _ckvmssts(status); }
5785 } while (!valid_uic (uic));
5789 if (!uic.uic$v_group)
5790 uic.uic$v_group= PerlProc_getgid();
5792 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
5793 else status = SS$_IVIDENT;
5794 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
5795 status == RMS$_PRV) {
5796 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5799 else { _ckvmssts(status); }
5801 __pw_namecache[lname]= '\0';
5802 __mystrtolower(__pw_namecache);
5804 __pwdcache = __passwd_empty;
5805 __pwdcache.pw_name = __pw_namecache;
5807 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
5808 The identifier's value is usually the UIC, but it doesn't have to be,
5809 so if we can, we let fillpasswd update this. */
5810 __pwdcache.pw_uid = uic.uic$l_uic;
5811 __pwdcache.pw_gid = uic.uic$v_group;
5813 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
5816 } /* end of my_getpwuid() */
5820 * Get information for next user.
5822 /*{{{struct passwd *my_getpwent()*/
5823 struct passwd *Perl_my_getpwent(pTHX)
5825 return (my_getpwuid((unsigned int) -1));
5830 * Finish searching rights database for users.
5832 /*{{{void my_endpwent()*/
5833 void Perl_my_endpwent(pTHX)
5836 _ckvmssts(sys$finish_rdb(&contxt));
5842 #ifdef HOMEGROWN_POSIX_SIGNALS
5843 /* Signal handling routines, pulled into the core from POSIX.xs.
5845 * We need these for threads, so they've been rolled into the core,
5846 * rather than left in POSIX.xs.
5848 * (DRS, Oct 23, 1997)
5851 /* sigset_t is atomic under VMS, so these routines are easy */
5852 /*{{{int my_sigemptyset(sigset_t *) */
5853 int my_sigemptyset(sigset_t *set) {
5854 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5860 /*{{{int my_sigfillset(sigset_t *)*/
5861 int my_sigfillset(sigset_t *set) {
5863 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5864 for (i = 0; i < NSIG; i++) *set |= (1 << i);
5870 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
5871 int my_sigaddset(sigset_t *set, int sig) {
5872 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5873 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5874 *set |= (1 << (sig - 1));
5880 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
5881 int my_sigdelset(sigset_t *set, int sig) {
5882 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5883 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5884 *set &= ~(1 << (sig - 1));
5890 /*{{{int my_sigismember(sigset_t *set, int sig)*/
5891 int my_sigismember(sigset_t *set, int sig) {
5892 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5893 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5894 return *set & (1 << (sig - 1));
5899 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
5900 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
5903 /* If set and oset are both null, then things are badly wrong. Bail out. */
5904 if ((oset == NULL) && (set == NULL)) {
5905 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5909 /* If set's null, then we're just handling a fetch. */
5911 tempmask = sigblock(0);
5916 tempmask = sigsetmask(*set);
5919 tempmask = sigblock(*set);
5922 tempmask = sigblock(0);
5923 sigsetmask(*oset & ~tempmask);
5926 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5931 /* Did they pass us an oset? If so, stick our holding mask into it */
5938 #endif /* HOMEGROWN_POSIX_SIGNALS */
5941 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
5942 * my_utime(), and flex_stat(), all of which operate on UTC unless
5943 * VMSISH_TIMES is true.
5945 /* method used to handle UTC conversions:
5946 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
5948 static int gmtime_emulation_type;
5949 /* number of secs to add to UTC POSIX-style time to get local time */
5950 static long int utc_offset_secs;
5952 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
5953 * in vmsish.h. #undef them here so we can call the CRTL routines
5962 * DEC C previous to 6.0 corrupts the behavior of the /prefix
5963 * qualifier with the extern prefix pragma. This provisional
5964 * hack circumvents this prefix pragma problem in previous
5967 #if defined(__VMS_VER) && __VMS_VER >= 70000000
5968 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
5969 # pragma __extern_prefix save
5970 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
5971 # define gmtime decc$__utctz_gmtime
5972 # define localtime decc$__utctz_localtime
5973 # define time decc$__utc_time
5974 # pragma __extern_prefix restore
5976 struct tm *gmtime(), *localtime();
5982 static time_t toutc_dst(time_t loc) {
5985 if ((rsltmp = localtime(&loc)) == NULL) return -1;
5986 loc -= utc_offset_secs;
5987 if (rsltmp->tm_isdst) loc -= 3600;
5990 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5991 ((gmtime_emulation_type || my_time(NULL)), \
5992 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
5993 ((secs) - utc_offset_secs))))
5995 static time_t toloc_dst(time_t utc) {
5998 utc += utc_offset_secs;
5999 if ((rsltmp = localtime(&utc)) == NULL) return -1;
6000 if (rsltmp->tm_isdst) utc += 3600;
6003 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
6004 ((gmtime_emulation_type || my_time(NULL)), \
6005 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
6006 ((secs) + utc_offset_secs))))
6008 #ifndef RTL_USES_UTC
6011 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
6012 DST starts on 1st sun of april at 02:00 std time
6013 ends on last sun of october at 02:00 dst time
6014 see the UCX management command reference, SET CONFIG TIMEZONE
6015 for formatting info.
6017 No, it's not as general as it should be, but then again, NOTHING
6018 will handle UK times in a sensible way.
6023 parse the DST start/end info:
6024 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
6028 tz_parse_startend(char *s, struct tm *w, int *past)
6030 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
6031 int ly, dozjd, d, m, n, hour, min, sec, j, k;
6036 if (!past) return 0;
6039 if (w->tm_year % 4 == 0) ly = 1;
6040 if (w->tm_year % 100 == 0) ly = 0;
6041 if (w->tm_year+1900 % 400 == 0) ly = 1;
6044 dozjd = isdigit(*s);
6045 if (*s == 'J' || *s == 'j' || dozjd) {
6046 if (!dozjd && !isdigit(*++s)) return 0;
6049 d = d*10 + *s++ - '0';
6051 d = d*10 + *s++ - '0';
6054 if (d == 0) return 0;
6055 if (d > 366) return 0;
6057 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
6060 } else if (*s == 'M' || *s == 'm') {
6061 if (!isdigit(*++s)) return 0;
6063 if (isdigit(*s)) m = 10*m + *s++ - '0';
6064 if (*s != '.') return 0;
6065 if (!isdigit(*++s)) return 0;
6067 if (n < 1 || n > 5) return 0;
6068 if (*s != '.') return 0;
6069 if (!isdigit(*++s)) return 0;
6071 if (d > 6) return 0;
6075 if (!isdigit(*++s)) return 0;
6077 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
6079 if (!isdigit(*++s)) return 0;
6081 if (isdigit(*s)) min = 10*min + *s++ - '0';
6083 if (!isdigit(*++s)) return 0;
6085 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
6095 if (w->tm_yday < d) goto before;
6096 if (w->tm_yday > d) goto after;
6098 if (w->tm_mon+1 < m) goto before;
6099 if (w->tm_mon+1 > m) goto after;
6101 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
6102 k = d - j; /* mday of first d */
6104 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
6105 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
6106 if (w->tm_mday < k) goto before;
6107 if (w->tm_mday > k) goto after;
6110 if (w->tm_hour < hour) goto before;
6111 if (w->tm_hour > hour) goto after;
6112 if (w->tm_min < min) goto before;
6113 if (w->tm_min > min) goto after;
6114 if (w->tm_sec < sec) goto before;
6128 /* parse the offset: (+|-)hh[:mm[:ss]] */
6131 tz_parse_offset(char *s, int *offset)
6133 int hour = 0, min = 0, sec = 0;
6136 if (!offset) return 0;
6138 if (*s == '-') {neg++; s++;}
6140 if (!isdigit(*s)) return 0;
6142 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
6143 if (hour > 24) return 0;
6145 if (!isdigit(*++s)) return 0;
6147 if (isdigit(*s)) min = min*10 + (*s++ - '0');
6148 if (min > 59) return 0;
6150 if (!isdigit(*++s)) return 0;
6152 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
6153 if (sec > 59) return 0;
6157 *offset = (hour*60+min)*60 + sec;
6158 if (neg) *offset = -*offset;
6163 input time is w, whatever type of time the CRTL localtime() uses.
6164 sets dst, the zone, and the gmtoff (seconds)
6166 caches the value of TZ and UCX$TZ env variables; note that
6167 my_setenv looks for these and sets a flag if they're changed
6170 We have to watch out for the "australian" case (dst starts in
6171 october, ends in april)...flagged by "reverse" and checked by
6172 scanning through the months of the previous year.
6177 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
6182 char *dstzone, *tz, *s_start, *s_end;
6183 int std_off, dst_off, isdst;
6184 int y, dststart, dstend;
6185 static char envtz[1025]; /* longer than any logical, symbol, ... */
6186 static char ucxtz[1025];
6187 static char reversed = 0;
6193 reversed = -1; /* flag need to check */
6194 envtz[0] = ucxtz[0] = '\0';
6195 tz = my_getenv("TZ",0);
6196 if (tz) strcpy(envtz, tz);
6197 tz = my_getenv("UCX$TZ",0);
6198 if (tz) strcpy(ucxtz, tz);
6199 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
6202 if (!*tz) tz = ucxtz;
6205 while (isalpha(*s)) s++;
6206 s = tz_parse_offset(s, &std_off);
6208 if (!*s) { /* no DST, hurray we're done! */
6214 while (isalpha(*s)) s++;
6215 s2 = tz_parse_offset(s, &dst_off);
6219 dst_off = std_off - 3600;
6222 if (!*s) { /* default dst start/end?? */
6223 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
6224 s = strchr(ucxtz,',');
6226 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
6228 if (*s != ',') return 0;
6231 when = _toutc(when); /* convert to utc */
6232 when = when - std_off; /* convert to pseudolocal time*/
6234 w2 = localtime(&when);
6237 s = tz_parse_startend(s_start,w2,&dststart);
6239 if (*s != ',') return 0;
6242 when = _toutc(when); /* convert to utc */
6243 when = when - dst_off; /* convert to pseudolocal time*/
6244 w2 = localtime(&when);
6245 if (w2->tm_year != y) { /* spans a year, just check one time */
6246 when += dst_off - std_off;
6247 w2 = localtime(&when);
6250 s = tz_parse_startend(s_end,w2,&dstend);
6253 if (reversed == -1) { /* need to check if start later than end */
6257 if (when < 2*365*86400) {
6258 when += 2*365*86400;
6262 w2 =localtime(&when);
6263 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
6265 for (j = 0; j < 12; j++) {
6266 w2 =localtime(&when);
6267 (void) tz_parse_startend(s_start,w2,&ds);
6268 (void) tz_parse_startend(s_end,w2,&de);
6269 if (ds != de) break;
6273 if (de && !ds) reversed = 1;
6276 isdst = dststart && !dstend;
6277 if (reversed) isdst = dststart || !dstend;
6280 if (dst) *dst = isdst;
6281 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
6282 if (isdst) tz = dstzone;
6284 while(isalpha(*tz)) *zone++ = *tz++;
6290 #endif /* !RTL_USES_UTC */
6292 /* my_time(), my_localtime(), my_gmtime()
6293 * By default traffic in UTC time values, using CRTL gmtime() or
6294 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
6295 * Note: We need to use these functions even when the CRTL has working
6296 * UTC support, since they also handle C<use vmsish qw(times);>
6298 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
6299 * Modified by Charles Bailey <bailey@newman.upenn.edu>
6302 /*{{{time_t my_time(time_t *timep)*/
6303 time_t Perl_my_time(pTHX_ time_t *timep)
6308 if (gmtime_emulation_type == 0) {
6310 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
6311 /* results of calls to gmtime() and localtime() */
6312 /* for same &base */
6314 gmtime_emulation_type++;
6315 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
6316 char off[LNM$C_NAMLENGTH+1];;
6318 gmtime_emulation_type++;
6319 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
6320 gmtime_emulation_type++;
6321 utc_offset_secs = 0;
6322 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
6324 else { utc_offset_secs = atol(off); }
6326 else { /* We've got a working gmtime() */
6327 struct tm gmt, local;
6330 tm_p = localtime(&base);
6332 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
6333 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
6334 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
6335 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
6341 # ifdef RTL_USES_UTC
6342 if (VMSISH_TIME) when = _toloc(when);
6344 if (!VMSISH_TIME) when = _toutc(when);
6347 if (timep != NULL) *timep = when;
6350 } /* end of my_time() */
6354 /*{{{struct tm *my_gmtime(const time_t *timep)*/
6356 Perl_my_gmtime(pTHX_ const time_t *timep)
6362 if (timep == NULL) {
6363 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6366 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
6370 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
6372 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
6373 return gmtime(&when);
6375 /* CRTL localtime() wants local time as input, so does no tz correction */
6376 rsltmp = localtime(&when);
6377 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
6380 } /* end of my_gmtime() */
6384 /*{{{struct tm *my_localtime(const time_t *timep)*/
6386 Perl_my_localtime(pTHX_ const time_t *timep)
6388 time_t when, whenutc;
6392 if (timep == NULL) {
6393 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6396 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
6397 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
6400 # ifdef RTL_USES_UTC
6402 if (VMSISH_TIME) when = _toutc(when);
6404 /* CRTL localtime() wants UTC as input, does tz correction itself */
6405 return localtime(&when);
6407 # else /* !RTL_USES_UTC */
6410 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
6411 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
6414 #ifndef RTL_USES_UTC
6415 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
6416 when = whenutc - offset; /* pseudolocal time*/
6419 /* CRTL localtime() wants local time as input, so does no tz correction */
6420 rsltmp = localtime(&when);
6421 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
6425 } /* end of my_localtime() */
6428 /* Reset definitions for later calls */
6429 #define gmtime(t) my_gmtime(t)
6430 #define localtime(t) my_localtime(t)
6431 #define time(t) my_time(t)
6434 /* my_utime - update modification time of a file
6435 * calling sequence is identical to POSIX utime(), but under
6436 * VMS only the modification time is changed; ODS-2 does not
6437 * maintain access times. Restrictions differ from the POSIX
6438 * definition in that the time can be changed as long as the
6439 * caller has permission to execute the necessary IO$_MODIFY $QIO;
6440 * no separate checks are made to insure that the caller is the
6441 * owner of the file or has special privs enabled.
6442 * Code here is based on Joe Meadows' FILE utility.
6445 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
6446 * to VMS epoch (01-JAN-1858 00:00:00.00)
6447 * in 100 ns intervals.
6449 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
6451 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
6452 int Perl_my_utime(pTHX_ char *file, struct utimbuf *utimes)
6455 long int bintime[2], len = 2, lowbit, unixtime,
6456 secscale = 10000000; /* seconds --> 100 ns intervals */
6457 unsigned long int chan, iosb[2], retsts;
6458 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
6459 struct FAB myfab = cc$rms_fab;
6460 struct NAM mynam = cc$rms_nam;
6461 #if defined (__DECC) && defined (__VAX)
6462 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
6463 * at least through VMS V6.1, which causes a type-conversion warning.
6465 # pragma message save
6466 # pragma message disable cvtdiftypes
6468 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
6469 struct fibdef myfib;
6470 #if defined (__DECC) && defined (__VAX)
6471 /* This should be right after the declaration of myatr, but due
6472 * to a bug in VAX DEC C, this takes effect a statement early.
6474 # pragma message restore
6476 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
6477 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
6478 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
6480 if (file == NULL || *file == '\0') {
6482 set_vaxc_errno(LIB$_INVARG);
6485 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
6487 if (utimes != NULL) {
6488 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
6489 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
6490 * Since time_t is unsigned long int, and lib$emul takes a signed long int
6491 * as input, we force the sign bit to be clear by shifting unixtime right
6492 * one bit, then multiplying by an extra factor of 2 in lib$emul().
6494 lowbit = (utimes->modtime & 1) ? secscale : 0;
6495 unixtime = (long int) utimes->modtime;
6497 /* If input was UTC; convert to local for sys svc */
6498 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
6500 unixtime >>= 1; secscale <<= 1;
6501 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
6502 if (!(retsts & 1)) {
6504 set_vaxc_errno(retsts);
6507 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
6508 if (!(retsts & 1)) {
6510 set_vaxc_errno(retsts);
6515 /* Just get the current time in VMS format directly */
6516 retsts = sys$gettim(bintime);
6517 if (!(retsts & 1)) {
6519 set_vaxc_errno(retsts);
6524 myfab.fab$l_fna = vmsspec;
6525 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
6526 myfab.fab$l_nam = &mynam;
6527 mynam.nam$l_esa = esa;
6528 mynam.nam$b_ess = (unsigned char) sizeof esa;
6529 mynam.nam$l_rsa = rsa;
6530 mynam.nam$b_rss = (unsigned char) sizeof rsa;
6532 /* Look for the file to be affected, letting RMS parse the file
6533 * specification for us as well. I have set errno using only
6534 * values documented in the utime() man page for VMS POSIX.
6536 retsts = sys$parse(&myfab,0,0);
6537 if (!(retsts & 1)) {
6538 set_vaxc_errno(retsts);
6539 if (retsts == RMS$_PRV) set_errno(EACCES);
6540 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
6541 else set_errno(EVMSERR);
6544 retsts = sys$search(&myfab,0,0);
6545 if (!(retsts & 1)) {
6546 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6547 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6548 set_vaxc_errno(retsts);
6549 if (retsts == RMS$_PRV) set_errno(EACCES);
6550 else if (retsts == RMS$_FNF) set_errno(ENOENT);
6551 else set_errno(EVMSERR);
6555 devdsc.dsc$w_length = mynam.nam$b_dev;
6556 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
6558 retsts = sys$assign(&devdsc,&chan,0,0);
6559 if (!(retsts & 1)) {
6560 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6561 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6562 set_vaxc_errno(retsts);
6563 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
6564 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
6565 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
6566 else set_errno(EVMSERR);
6570 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
6571 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
6573 memset((void *) &myfib, 0, sizeof myfib);
6574 #if defined(__DECC) || defined(__DECCXX)
6575 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
6576 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
6577 /* This prevents the revision time of the file being reset to the current
6578 * time as a result of our IO$_MODIFY $QIO. */
6579 myfib.fib$l_acctl = FIB$M_NORECORD;
6581 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
6582 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
6583 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
6585 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
6586 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6587 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6588 _ckvmssts(sys$dassgn(chan));
6589 if (retsts & 1) retsts = iosb[0];
6590 if (!(retsts & 1)) {
6591 set_vaxc_errno(retsts);
6592 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6593 else set_errno(EVMSERR);
6598 } /* end of my_utime() */
6602 * flex_stat, flex_fstat
6603 * basic stat, but gets it right when asked to stat
6604 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
6607 /* encode_dev packs a VMS device name string into an integer to allow
6608 * simple comparisons. This can be used, for example, to check whether two
6609 * files are located on the same device, by comparing their encoded device
6610 * names. Even a string comparison would not do, because stat() reuses the
6611 * device name buffer for each call; so without encode_dev, it would be
6612 * necessary to save the buffer and use strcmp (this would mean a number of
6613 * changes to the standard Perl code, to say nothing of what a Perl script
6616 * The device lock id, if it exists, should be unique (unless perhaps compared
6617 * with lock ids transferred from other nodes). We have a lock id if the disk is
6618 * mounted cluster-wide, which is when we tend to get long (host-qualified)
6619 * device names. Thus we use the lock id in preference, and only if that isn't
6620 * available, do we try to pack the device name into an integer (flagged by
6621 * the sign bit (LOCKID_MASK) being set).
6623 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
6624 * name and its encoded form, but it seems very unlikely that we will find
6625 * two files on different disks that share the same encoded device names,
6626 * and even more remote that they will share the same file id (if the test
6627 * is to check for the same file).
6629 * A better method might be to use sys$device_scan on the first call, and to
6630 * search for the device, returning an index into the cached array.
6631 * The number returned would be more intelligable.
6632 * This is probably not worth it, and anyway would take quite a bit longer
6633 * on the first call.
6635 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
6636 static mydev_t encode_dev (pTHX_ const char *dev)
6639 unsigned long int f;
6644 if (!dev || !dev[0]) return 0;
6648 struct dsc$descriptor_s dev_desc;
6649 unsigned long int status, lockid, item = DVI$_LOCKID;
6651 /* For cluster-mounted disks, the disk lock identifier is unique, so we
6652 can try that first. */
6653 dev_desc.dsc$w_length = strlen (dev);
6654 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
6655 dev_desc.dsc$b_class = DSC$K_CLASS_S;
6656 dev_desc.dsc$a_pointer = (char *) dev;
6657 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
6658 if (lockid) return (lockid & ~LOCKID_MASK);
6662 /* Otherwise we try to encode the device name */
6666 for (q = dev + strlen(dev); q--; q >= dev) {
6669 else if (isalpha (toupper (*q)))
6670 c= toupper (*q) - 'A' + (char)10;
6672 continue; /* Skip '$'s */
6674 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
6676 enc += f * (unsigned long int) c;
6678 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
6680 } /* end of encode_dev() */
6682 static char namecache[NAM$C_MAXRSS+1];
6685 is_null_device(name)
6688 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
6689 The underscore prefix, controller letter, and unit number are
6690 independently optional; for our purposes, the colon punctuation
6691 is not. The colon can be trailed by optional directory and/or
6692 filename, but two consecutive colons indicates a nodename rather
6693 than a device. [pr] */
6694 if (*name == '_') ++name;
6695 if (tolower(*name++) != 'n') return 0;
6696 if (tolower(*name++) != 'l') return 0;
6697 if (tolower(*name) == 'a') ++name;
6698 if (*name == '0') ++name;
6699 return (*name++ == ':') && (*name != ':');
6702 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
6703 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
6704 * subset of the applicable information.
6707 Perl_cando(pTHX_ Mode_t bit, Uid_t effective, Stat_t *statbufp)
6709 char fname_phdev[NAM$C_MAXRSS+1];
6710 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
6712 char fname[NAM$C_MAXRSS+1];
6713 unsigned long int retsts;
6714 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6715 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6717 /* If the struct mystat is stale, we're OOL; stat() overwrites the
6718 device name on successive calls */
6719 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
6720 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
6721 namdsc.dsc$a_pointer = fname;
6722 namdsc.dsc$w_length = sizeof fname - 1;
6724 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
6725 &namdsc,&namdsc.dsc$w_length,0,0);
6727 fname[namdsc.dsc$w_length] = '\0';
6729 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
6730 * but if someone has redefined that logical, Perl gets very lost. Since
6731 * we have the physical device name from the stat buffer, just paste it on.
6733 strcpy( fname_phdev, statbufp->st_devnam );
6734 strcat( fname_phdev, strrchr(fname, ':') );
6736 return cando_by_name(bit,effective,fname_phdev);
6738 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
6739 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
6743 return FALSE; /* Should never get to here */
6745 } /* end of cando() */
6749 /*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
6751 Perl_cando_by_name(pTHX_ I32 bit, Uid_t effective, char *fname)
6753 static char usrname[L_cuserid];
6754 static struct dsc$descriptor_s usrdsc =
6755 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
6756 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
6757 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
6758 unsigned short int retlen, trnlnm_iter_count;
6759 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6760 union prvdef curprv;
6761 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
6762 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
6763 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
6764 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
6766 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
6768 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6770 if (!fname || !*fname) return FALSE;
6771 /* Make sure we expand logical names, since sys$check_access doesn't */
6772 if (!strpbrk(fname,"/]>:")) {
6773 strcpy(fileified,fname);
6774 trnlnm_iter_count = 0;
6775 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
6776 trnlnm_iter_count++;
6777 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6781 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
6782 retlen = namdsc.dsc$w_length = strlen(vmsname);
6783 namdsc.dsc$a_pointer = vmsname;
6784 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
6785 vmsname[retlen-1] == ':') {
6786 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
6787 namdsc.dsc$w_length = strlen(fileified);
6788 namdsc.dsc$a_pointer = fileified;
6792 case S_IXUSR: case S_IXGRP: case S_IXOTH:
6793 access = ARM$M_EXECUTE; break;
6794 case S_IRUSR: case S_IRGRP: case S_IROTH:
6795 access = ARM$M_READ; break;
6796 case S_IWUSR: case S_IWGRP: case S_IWOTH:
6797 access = ARM$M_WRITE; break;
6798 case S_IDUSR: case S_IDGRP: case S_IDOTH:
6799 access = ARM$M_DELETE; break;
6804 /* Before we call $check_access, create a user profile with the current
6805 * process privs since otherwise it just uses the default privs from the
6806 * UAF and might give false positives or negatives. This only works on
6807 * VMS versions v6.0 and later since that's when sys$create_user_profile
6811 /* get current process privs and username */
6812 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
6815 #if defined(__VMS_VER) && __VMS_VER >= 60000000
6817 /* find out the space required for the profile */
6818 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
6819 &usrprodsc.dsc$w_length,0));
6821 /* allocate space for the profile and get it filled in */
6822 New(1330,usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
6823 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
6824 &usrprodsc.dsc$w_length,0));
6826 /* use the profile to check access to the file; free profile & analyze results */
6827 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
6828 Safefree(usrprodsc.dsc$a_pointer);
6829 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
6833 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
6837 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
6838 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
6839 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
6840 set_vaxc_errno(retsts);
6841 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6842 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
6843 else set_errno(ENOENT);
6846 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
6851 return FALSE; /* Should never get here */
6853 } /* end of cando_by_name() */
6857 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
6859 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
6861 if (!fstat(fd,(stat_t *) statbufp)) {
6862 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
6863 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
6864 # ifdef RTL_USES_UTC
6867 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6868 statbufp->st_atime = _toloc(statbufp->st_atime);
6869 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6874 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6878 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6879 statbufp->st_atime = _toutc(statbufp->st_atime);
6880 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6887 } /* end of flex_fstat() */
6890 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
6892 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
6894 char fileified[NAM$C_MAXRSS+1];
6895 char temp_fspec[NAM$C_MAXRSS+300];
6897 int saved_errno, saved_vaxc_errno;
6899 if (!fspec) return retval;
6900 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
6901 strcpy(temp_fspec, fspec);
6902 if (statbufp == (Stat_t *) &PL_statcache)
6903 do_tovmsspec(temp_fspec,namecache,0);
6904 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
6905 memset(statbufp,0,sizeof *statbufp);
6906 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
6907 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
6908 statbufp->st_uid = 0x00010001;
6909 statbufp->st_gid = 0x0001;
6910 time((time_t *)&statbufp->st_mtime);
6911 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
6915 /* Try for a directory name first. If fspec contains a filename without
6916 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
6917 * and sea:[wine.dark]water. exist, we prefer the directory here.
6918 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
6919 * not sea:[wine.dark]., if the latter exists. If the intended target is
6920 * the file with null type, specify this by calling flex_stat() with
6921 * a '.' at the end of fspec.
6923 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
6924 retval = stat(fileified,(stat_t *) statbufp);
6925 if (!retval && statbufp == (Stat_t *) &PL_statcache)
6926 strcpy(namecache,fileified);
6928 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
6930 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
6931 # ifdef RTL_USES_UTC
6934 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6935 statbufp->st_atime = _toloc(statbufp->st_atime);
6936 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6941 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6945 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6946 statbufp->st_atime = _toutc(statbufp->st_atime);
6947 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6951 /* If we were successful, leave errno where we found it */
6952 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
6955 } /* end of flex_stat() */
6959 /*{{{char *my_getlogin()*/
6960 /* VMS cuserid == Unix getlogin, except calling sequence */
6964 static char user[L_cuserid];
6965 return cuserid(user);
6970 /* rmscopy - copy a file using VMS RMS routines
6972 * Copies contents and attributes of spec_in to spec_out, except owner
6973 * and protection information. Name and type of spec_in are used as
6974 * defaults for spec_out. The third parameter specifies whether rmscopy()
6975 * should try to propagate timestamps from the input file to the output file.
6976 * If it is less than 0, no timestamps are preserved. If it is 0, then
6977 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
6978 * propagated to the output file at creation iff the output file specification
6979 * did not contain an explicit name or type, and the revision date is always
6980 * updated at the end of the copy operation. If it is greater than 0, then
6981 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
6982 * other than the revision date should be propagated, and bit 1 indicates
6983 * that the revision date should be propagated.
6985 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
6987 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
6988 * Incorporates, with permission, some code from EZCOPY by Tim Adye
6989 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
6990 * as part of the Perl standard distribution under the terms of the
6991 * GNU General Public License or the Perl Artistic License. Copies
6992 * of each may be found in the Perl standard distribution.
6994 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
6996 Perl_rmscopy(pTHX_ char *spec_in, char *spec_out, int preserve_dates)
6998 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
6999 rsa[NAM$C_MAXRSS], ubf[32256];
7000 unsigned long int i, sts, sts2;
7001 struct FAB fab_in, fab_out;
7002 struct RAB rab_in, rab_out;
7004 struct XABDAT xabdat;
7005 struct XABFHC xabfhc;
7006 struct XABRDT xabrdt;
7007 struct XABSUM xabsum;
7009 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
7010 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
7011 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7015 fab_in = cc$rms_fab;
7016 fab_in.fab$l_fna = vmsin;
7017 fab_in.fab$b_fns = strlen(vmsin);
7018 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
7019 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
7020 fab_in.fab$l_fop = FAB$M_SQO;
7021 fab_in.fab$l_nam = &nam;
7022 fab_in.fab$l_xab = (void *) &xabdat;
7025 nam.nam$l_rsa = rsa;
7026 nam.nam$b_rss = sizeof(rsa);
7027 nam.nam$l_esa = esa;
7028 nam.nam$b_ess = sizeof (esa);
7029 nam.nam$b_esl = nam.nam$b_rsl = 0;
7031 xabdat = cc$rms_xabdat; /* To get creation date */
7032 xabdat.xab$l_nxt = (void *) &xabfhc;
7034 xabfhc = cc$rms_xabfhc; /* To get record length */
7035 xabfhc.xab$l_nxt = (void *) &xabsum;
7037 xabsum = cc$rms_xabsum; /* To get key and area information */
7039 if (!((sts = sys$open(&fab_in)) & 1)) {
7040 set_vaxc_errno(sts);
7042 case RMS$_FNF: case RMS$_DNF:
7043 set_errno(ENOENT); break;
7045 set_errno(ENOTDIR); break;
7047 set_errno(ENODEV); break;
7049 set_errno(EINVAL); break;
7051 set_errno(EACCES); break;
7059 fab_out.fab$w_ifi = 0;
7060 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
7061 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
7062 fab_out.fab$l_fop = FAB$M_SQO;
7063 fab_out.fab$l_fna = vmsout;
7064 fab_out.fab$b_fns = strlen(vmsout);
7065 fab_out.fab$l_dna = nam.nam$l_name;
7066 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
7068 if (preserve_dates == 0) { /* Act like DCL COPY */
7069 nam.nam$b_nop = NAM$M_SYNCHK;
7070 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
7071 if (!((sts = sys$parse(&fab_out)) & 1)) {
7072 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
7073 set_vaxc_errno(sts);
7076 fab_out.fab$l_xab = (void *) &xabdat;
7077 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
7079 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
7080 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
7081 preserve_dates =0; /* bitmask from this point forward */
7083 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
7084 if (!((sts = sys$create(&fab_out)) & 1)) {
7085 set_vaxc_errno(sts);
7088 set_errno(ENOENT); break;
7090 set_errno(ENOTDIR); break;
7092 set_errno(ENODEV); break;
7094 set_errno(EINVAL); break;
7096 set_errno(EACCES); break;
7102 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
7103 if (preserve_dates & 2) {
7104 /* sys$close() will process xabrdt, not xabdat */
7105 xabrdt = cc$rms_xabrdt;
7107 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
7109 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
7110 * is unsigned long[2], while DECC & VAXC use a struct */
7111 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
7113 fab_out.fab$l_xab = (void *) &xabrdt;
7116 rab_in = cc$rms_rab;
7117 rab_in.rab$l_fab = &fab_in;
7118 rab_in.rab$l_rop = RAB$M_BIO;
7119 rab_in.rab$l_ubf = ubf;
7120 rab_in.rab$w_usz = sizeof ubf;
7121 if (!((sts = sys$connect(&rab_in)) & 1)) {
7122 sys$close(&fab_in); sys$close(&fab_out);
7123 set_errno(EVMSERR); set_vaxc_errno(sts);
7127 rab_out = cc$rms_rab;
7128 rab_out.rab$l_fab = &fab_out;
7129 rab_out.rab$l_rbf = ubf;
7130 if (!((sts = sys$connect(&rab_out)) & 1)) {
7131 sys$close(&fab_in); sys$close(&fab_out);
7132 set_errno(EVMSERR); set_vaxc_errno(sts);
7136 while ((sts = sys$read(&rab_in))) { /* always true */
7137 if (sts == RMS$_EOF) break;
7138 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
7139 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
7140 sys$close(&fab_in); sys$close(&fab_out);
7141 set_errno(EVMSERR); set_vaxc_errno(sts);
7146 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
7147 sys$close(&fab_in); sys$close(&fab_out);
7148 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
7150 set_errno(EVMSERR); set_vaxc_errno(sts);
7156 } /* end of rmscopy() */
7160 /*** The following glue provides 'hooks' to make some of the routines
7161 * from this file available from Perl. These routines are sufficiently
7162 * basic, and are required sufficiently early in the build process,
7163 * that's it's nice to have them available to miniperl as well as the
7164 * full Perl, so they're set up here instead of in an extension. The
7165 * Perl code which handles importation of these names into a given
7166 * package lives in [.VMS]Filespec.pm in @INC.
7170 rmsexpand_fromperl(pTHX_ CV *cv)
7173 char *fspec, *defspec = NULL, *rslt;
7176 if (!items || items > 2)
7177 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
7178 fspec = SvPV(ST(0),n_a);
7179 if (!fspec || !*fspec) XSRETURN_UNDEF;
7180 if (items == 2) defspec = SvPV(ST(1),n_a);
7182 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
7183 ST(0) = sv_newmortal();
7184 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
7189 vmsify_fromperl(pTHX_ CV *cv)
7195 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
7196 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
7197 ST(0) = sv_newmortal();
7198 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
7203 unixify_fromperl(pTHX_ CV *cv)
7209 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
7210 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
7211 ST(0) = sv_newmortal();
7212 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
7217 fileify_fromperl(pTHX_ CV *cv)
7223 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
7224 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
7225 ST(0) = sv_newmortal();
7226 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
7231 pathify_fromperl(pTHX_ CV *cv)
7237 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
7238 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
7239 ST(0) = sv_newmortal();
7240 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
7245 vmspath_fromperl(pTHX_ CV *cv)
7251 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
7252 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
7253 ST(0) = sv_newmortal();
7254 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
7259 unixpath_fromperl(pTHX_ CV *cv)
7265 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
7266 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
7267 ST(0) = sv_newmortal();
7268 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
7273 candelete_fromperl(pTHX_ CV *cv)
7276 char fspec[NAM$C_MAXRSS+1], *fsp;
7281 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
7283 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
7284 if (SvTYPE(mysv) == SVt_PVGV) {
7285 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
7286 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7293 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
7294 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7300 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
7305 rmscopy_fromperl(pTHX_ CV *cv)
7308 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
7310 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
7311 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7312 unsigned long int sts;
7317 if (items < 2 || items > 3)
7318 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
7320 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
7321 if (SvTYPE(mysv) == SVt_PVGV) {
7322 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
7323 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7330 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
7331 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7336 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
7337 if (SvTYPE(mysv) == SVt_PVGV) {
7338 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
7339 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7346 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
7347 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7352 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
7354 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
7360 mod2fname(pTHX_ CV *cv)
7363 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
7364 workbuff[NAM$C_MAXRSS*1 + 1];
7365 int total_namelen = 3, counter, num_entries;
7366 /* ODS-5 ups this, but we want to be consistent, so... */
7367 int max_name_len = 39;
7368 AV *in_array = (AV *)SvRV(ST(0));
7370 num_entries = av_len(in_array);
7372 /* All the names start with PL_. */
7373 strcpy(ultimate_name, "PL_");
7375 /* Clean up our working buffer */
7376 Zero(work_name, sizeof(work_name), char);
7378 /* Run through the entries and build up a working name */
7379 for(counter = 0; counter <= num_entries; counter++) {
7380 /* If it's not the first name then tack on a __ */
7382 strcat(work_name, "__");
7384 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
7388 /* Check to see if we actually have to bother...*/
7389 if (strlen(work_name) + 3 <= max_name_len) {
7390 strcat(ultimate_name, work_name);
7392 /* It's too darned big, so we need to go strip. We use the same */
7393 /* algorithm as xsubpp does. First, strip out doubled __ */
7394 char *source, *dest, last;
7397 for (source = work_name; *source; source++) {
7398 if (last == *source && last == '_') {
7404 /* Go put it back */
7405 strcpy(work_name, workbuff);
7406 /* Is it still too big? */
7407 if (strlen(work_name) + 3 > max_name_len) {
7408 /* Strip duplicate letters */
7411 for (source = work_name; *source; source++) {
7412 if (last == toupper(*source)) {
7416 last = toupper(*source);
7418 strcpy(work_name, workbuff);
7421 /* Is it *still* too big? */
7422 if (strlen(work_name) + 3 > max_name_len) {
7423 /* Too bad, we truncate */
7424 work_name[max_name_len - 2] = 0;
7426 strcat(ultimate_name, work_name);
7429 /* Okay, return it */
7430 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
7435 hushexit_fromperl(pTHX_ CV *cv)
7440 VMSISH_HUSHED = SvTRUE(ST(0));
7442 ST(0) = boolSV(VMSISH_HUSHED);
7447 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
7448 struct interp_intern *dst)
7450 memcpy(dst,src,sizeof(struct interp_intern));
7454 Perl_sys_intern_clear(pTHX)
7459 Perl_sys_intern_init(pTHX)
7461 unsigned int ix = RAND_MAX;
7467 MY_INV_RAND_MAX = 1./x;
7474 char* file = __FILE__;
7475 char temp_buff[512];
7476 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
7477 no_translate_barewords = TRUE;
7479 no_translate_barewords = FALSE;
7482 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
7483 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
7484 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
7485 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
7486 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
7487 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
7488 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
7489 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
7490 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
7491 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
7492 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
7494 store_pipelocs(aTHX); /* will redo any earlier attempts */