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
19 #include <climsgdef.h>
29 #include <libclidef.h>
31 #include <lib$routines.h>
41 #include <str$routines.h>
46 /* Older versions of ssdef.h don't have these */
47 #ifndef SS$_INVFILFOROP
48 # define SS$_INVFILFOROP 3930
50 #ifndef SS$_NOSUCHOBJECT
51 # define SS$_NOSUCHOBJECT 2696
54 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
55 #define PERLIO_NOT_STDIO 0
57 /* Don't replace system definitions of vfork, getenv, and stat,
58 * code below needs to get to the underlying CRTL routines. */
59 #define DONT_MASK_RTL_CALLS
63 /* Anticipating future expansion in lexical warnings . . . */
65 # define WARN_INTERNAL WARN_MISC
68 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
69 # define RTL_USES_UTC 1
73 /* gcc's header files don't #define direct access macros
74 * corresponding to VAXC's variant structs */
76 # define uic$v_format uic$r_uic_form.uic$v_format
77 # define uic$v_group uic$r_uic_form.uic$v_group
78 # define uic$v_member uic$r_uic_form.uic$v_member
79 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
80 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
81 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
82 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
85 #if defined(NEED_AN_H_ERRNO)
90 unsigned short int buflen;
91 unsigned short int itmcode;
93 unsigned short int *retlen;
96 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
97 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
98 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
99 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
100 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
101 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
102 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
103 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
104 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
106 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
107 #define PERL_LNM_MAX_ALLOWED_INDEX 127
109 #define MAX_DCL_SYMBOL 255 /* well, what *we* can set, at least*/
110 #define MAX_DCL_LINE_LENGTH (4*MAX_DCL_SYMBOL-4)
112 static char *__mystrtolower(char *str)
114 if (str) for (; *str; ++str) *str= tolower(*str);
118 static struct dsc$descriptor_s fildevdsc =
119 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
120 static struct dsc$descriptor_s crtlenvdsc =
121 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
122 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
123 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
124 static struct dsc$descriptor_s **env_tables = defenv;
125 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
127 /* True if we shouldn't treat barewords as logicals during directory */
129 static int no_translate_barewords;
132 static int tz_updated = 1;
135 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
137 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
138 struct dsc$descriptor_s **tabvec, unsigned long int flags)
140 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
141 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
142 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
143 unsigned char acmode;
144 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
145 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
146 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
147 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
149 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
150 #if defined(PERL_IMPLICIT_CONTEXT)
152 # if defined(USE_5005THREADS)
153 /* We jump through these hoops because we can be called at */
154 /* platform-specific initialization time, which is before anything is */
155 /* set up--we can't even do a plain dTHX since that relies on the */
156 /* interpreter structure to be initialized */
158 aTHX = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
164 aTHX = PERL_GET_INTERP;
172 if (!lnm || !eqv || idx > PERL_LNM_MAX_ALLOWED_INDEX) {
173 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
175 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
176 *cp2 = _toupper(*cp1);
177 if (cp1 - lnm > LNM$C_NAMLENGTH) {
178 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
182 lnmdsc.dsc$w_length = cp1 - lnm;
183 lnmdsc.dsc$a_pointer = uplnm;
184 uplnm[lnmdsc.dsc$w_length] = '\0';
185 secure = flags & PERL__TRNENV_SECURE;
186 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
187 if (!tabvec || !*tabvec) tabvec = env_tables;
189 for (curtab = 0; tabvec[curtab]; curtab++) {
190 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
191 if (!ivenv && !secure) {
196 Perl_warn(aTHX_ "Can't read CRTL environ\n");
199 retsts = SS$_NOLOGNAM;
200 for (i = 0; environ[i]; i++) {
201 if ((eq = strchr(environ[i],'=')) &&
202 !strncmp(environ[i],uplnm,eq - environ[i])) {
204 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
205 if (!eqvlen) continue;
210 if (retsts != SS$_NOLOGNAM) break;
213 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
214 !str$case_blind_compare(&tmpdsc,&clisym)) {
215 if (!ivsym && !secure) {
216 unsigned short int deflen = LNM$C_NAMLENGTH;
217 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
218 /* dynamic dsc to accomodate possible long value */
219 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
220 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
223 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
225 /* Special hack--we might be called before the interpreter's */
226 /* fully initialized, in which case either thr or PL_curcop */
227 /* might be bogus. We have to check, since ckWARN needs them */
228 /* both to be valid if running threaded */
229 #if defined(USE_5005THREADS)
230 if (thr && PL_curcop) {
232 if (ckWARN(WARN_MISC)) {
233 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
235 #if defined(USE_5005THREADS)
237 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
242 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
244 _ckvmssts(lib$sfree1_dd(&eqvdsc));
245 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
246 if (retsts == LIB$_NOSUCHSYM) continue;
251 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
252 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
253 if (retsts == SS$_NOLOGNAM) continue;
254 /* PPFs have a prefix */
257 *((int *)uplnm) == *((int *)"SYS$") &&
259 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
260 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
261 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
262 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
263 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
264 memcpy(eqv,eqv+4,eqvlen-4);
270 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
271 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
272 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
273 retsts == SS$_NOLOGNAM) {
274 set_errno(EINVAL); set_vaxc_errno(retsts);
276 else _ckvmssts(retsts);
278 } /* end of vmstrnenv */
281 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
282 /* Define as a function so we can access statics. */
283 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
285 return vmstrnenv(lnm,eqv,idx,fildev,
286 #ifdef SECURE_INTERNAL_GETENV
287 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
296 * Note: Uses Perl temp to store result so char * can be returned to
297 * caller; this pointer will be invalidated at next Perl statement
299 * We define this as a function rather than a macro in terms of my_getenv_len()
300 * so that it'll work when PL_curinterp is undefined (and we therefore can't
303 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
305 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
307 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
308 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
309 unsigned long int idx = 0;
310 int trnsuccess, success, secure, saverr, savvmserr;
313 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
314 /* Set up a temporary buffer for the return value; Perl will
315 * clean it up at the next statement transition */
316 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
317 if (!tmpsv) return NULL;
320 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
321 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
322 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
323 getcwd(eqv,LNM$C_NAMLENGTH);
327 if ((cp2 = strchr(lnm,';')) != NULL) {
329 uplnm[cp2-lnm] = '\0';
330 idx = strtoul(cp2+1,NULL,0);
333 /* Impose security constraints only if tainting */
335 /* Impose security constraints only if tainting */
336 secure = PL_curinterp ? PL_tainting : will_taint;
337 saverr = errno; savvmserr = vaxc$errno;
340 success = vmstrnenv(lnm,eqv,idx,
341 secure ? fildev : NULL,
342 #ifdef SECURE_INTERNAL_GETENV
343 secure ? PERL__TRNENV_SECURE : 0
348 /* Discard NOLOGNAM on internal calls since we're often looking
349 * for an optional name, and this "error" often shows up as the
350 * (bogus) exit status for a die() call later on. */
351 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
352 return success ? eqv : Nullch;
355 } /* end of my_getenv() */
359 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
361 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
363 char *buf, *cp1, *cp2;
364 unsigned long idx = 0;
365 static char __my_getenv_len_eqv[LNM$C_NAMLENGTH+1];
366 int secure, saverr, savvmserr;
369 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
370 /* Set up a temporary buffer for the return value; Perl will
371 * clean it up at the next statement transition */
372 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
373 if (!tmpsv) return NULL;
376 else buf = __my_getenv_len_eqv; /* Assume no interpreter ==> single thread */
377 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
378 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
379 getcwd(buf,LNM$C_NAMLENGTH);
384 if ((cp2 = strchr(lnm,';')) != NULL) {
387 idx = strtoul(cp2+1,NULL,0);
391 /* Impose security constraints only if tainting */
392 secure = PL_curinterp ? PL_tainting : will_taint;
393 saverr = errno; savvmserr = vaxc$errno;
396 *len = vmstrnenv(lnm,buf,idx,
397 secure ? fildev : NULL,
398 #ifdef SECURE_INTERNAL_GETENV
399 secure ? PERL__TRNENV_SECURE : 0
404 /* Discard NOLOGNAM on internal calls since we're often looking
405 * for an optional name, and this "error" often shows up as the
406 * (bogus) exit status for a die() call later on. */
407 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
408 return *len ? buf : Nullch;
411 } /* end of my_getenv_len() */
414 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
416 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
418 /*{{{ void prime_env_iter() */
421 /* Fill the %ENV associative array with all logical names we can
422 * find, in preparation for iterating over it.
425 static int primed = 0;
426 HV *seenhv = NULL, *envhv;
428 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
429 unsigned short int chan;
430 #ifndef CLI$M_TRUSTED
431 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
433 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
434 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
436 bool have_sym = FALSE, have_lnm = FALSE;
437 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
438 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
439 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
440 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
441 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
442 #if defined(PERL_IMPLICIT_CONTEXT)
445 #if defined(USE_5005THREADS) || defined(USE_ITHREADS)
446 static perl_mutex primenv_mutex;
447 MUTEX_INIT(&primenv_mutex);
450 #if defined(PERL_IMPLICIT_CONTEXT)
451 /* We jump through these hoops because we can be called at */
452 /* platform-specific initialization time, which is before anything is */
453 /* set up--we can't even do a plain dTHX since that relies on the */
454 /* interpreter structure to be initialized */
455 #if defined(USE_5005THREADS)
457 aTHX = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
463 aTHX = PERL_GET_INTERP;
470 if (primed || !PL_envgv) return;
471 MUTEX_LOCK(&primenv_mutex);
472 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
473 envhv = GvHVn(PL_envgv);
474 /* Perform a dummy fetch as an lval to insure that the hash table is
475 * set up. Otherwise, the hv_store() will turn into a nullop. */
476 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
478 for (i = 0; env_tables[i]; i++) {
479 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
480 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
481 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
483 if (have_sym || have_lnm) {
484 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
485 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
486 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
487 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
490 for (i--; i >= 0; i--) {
491 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
494 for (j = 0; environ[j]; j++) {
495 if (!(start = strchr(environ[j],'='))) {
496 if (ckWARN(WARN_INTERNAL))
497 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
501 sv = newSVpv(start,0);
503 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
508 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
509 !str$case_blind_compare(&tmpdsc,&clisym)) {
510 strcpy(cmd,"Show Symbol/Global *");
511 cmddsc.dsc$w_length = 20;
512 if (env_tables[i]->dsc$w_length == 12 &&
513 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
514 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
515 flags = defflags | CLI$M_NOLOGNAM;
518 strcpy(cmd,"Show Logical *");
519 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
520 strcat(cmd," /Table=");
521 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
522 cmddsc.dsc$w_length = strlen(cmd);
524 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
525 flags = defflags | CLI$M_NOCLISYM;
528 /* Create a new subprocess to execute each command, to exclude the
529 * remote possibility that someone could subvert a mbx or file used
530 * to write multiple commands to a single subprocess.
533 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
534 0,&riseandshine,0,0,&clidsc,&clitabdsc);
535 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
536 defflags &= ~CLI$M_TRUSTED;
537 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
539 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
540 if (seenhv) SvREFCNT_dec(seenhv);
543 char *cp1, *cp2, *key;
544 unsigned long int sts, iosb[2], retlen, keylen;
547 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
548 if (sts & 1) sts = iosb[0] & 0xffff;
549 if (sts == SS$_ENDOFFILE) {
551 while (substs == 0) { sys$hiber(); wakect++;}
552 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
557 retlen = iosb[0] >> 16;
558 if (!retlen) continue; /* blank line */
560 if (iosb[1] != subpid) {
562 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
566 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
567 Perl_warner(aTHX_ WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
569 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
570 if (*cp1 == '(' || /* Logical name table name */
571 *cp1 == '=' /* Next eqv of searchlist */) continue;
572 if (*cp1 == '"') cp1++;
573 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
574 key = cp1; keylen = cp2 - cp1;
575 if (keylen && hv_exists(seenhv,key,keylen)) continue;
576 while (*cp2 && *cp2 != '=') cp2++;
577 while (*cp2 && *cp2 == '=') cp2++;
578 while (*cp2 && *cp2 == ' ') cp2++;
579 if (*cp2 == '"') { /* String translation; may embed "" */
580 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
581 cp2++; cp1--; /* Skip "" surrounding translation */
583 else { /* Numeric translation */
584 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
585 cp1--; /* stop on last non-space char */
587 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
588 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
591 PERL_HASH(hash,key,keylen);
592 sv = newSVpvn(cp2,cp1 - cp2 + 1);
594 hv_store(envhv,key,keylen,sv,hash);
595 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
597 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
598 /* get the PPFs for this process, not the subprocess */
599 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
600 char eqv[LNM$C_NAMLENGTH+1];
602 for (i = 0; ppfs[i]; i++) {
603 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
604 sv = newSVpv(eqv,trnlen);
606 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
611 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
612 if (buf) Safefree(buf);
613 if (seenhv) SvREFCNT_dec(seenhv);
614 MUTEX_UNLOCK(&primenv_mutex);
617 } /* end of prime_env_iter */
621 /*{{{ int vmssetenv(char *lnm, char *eqv)*/
622 /* Define or delete an element in the same "environment" as
623 * vmstrnenv(). If an element is to be deleted, it's removed from
624 * the first place it's found. If it's to be set, it's set in the
625 * place designated by the first element of the table vector.
626 * Like setenv() returns 0 for success, non-zero on error.
629 Perl_vmssetenv(pTHX_ char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
631 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
632 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
633 unsigned long int retsts, usermode = PSL$C_USER;
634 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
635 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
636 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
637 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
638 $DESCRIPTOR(local,"_LOCAL");
640 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
641 *cp2 = _toupper(*cp1);
642 if (cp1 - lnm > LNM$C_NAMLENGTH) {
643 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
647 lnmdsc.dsc$w_length = cp1 - lnm;
648 if (!tabvec || !*tabvec) tabvec = env_tables;
650 if (!eqv) { /* we're deleting n element */
651 for (curtab = 0; tabvec[curtab]; curtab++) {
652 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
654 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
655 if ((cp1 = strchr(environ[i],'=')) &&
656 !strncmp(environ[i],lnm,cp1 - environ[i])) {
658 return setenv(lnm,"",1) ? vaxc$errno : 0;
661 ivenv = 1; retsts = SS$_NOLOGNAM;
663 if (ckWARN(WARN_INTERNAL))
664 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
665 ivenv = 1; retsts = SS$_NOSUCHPGM;
671 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
672 !str$case_blind_compare(&tmpdsc,&clisym)) {
673 unsigned int symtype;
674 if (tabvec[curtab]->dsc$w_length == 12 &&
675 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
676 !str$case_blind_compare(&tmpdsc,&local))
677 symtype = LIB$K_CLI_LOCAL_SYM;
678 else symtype = LIB$K_CLI_GLOBAL_SYM;
679 retsts = lib$delete_symbol(&lnmdsc,&symtype);
680 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
681 if (retsts == LIB$_NOSUCHSYM) continue;
685 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
686 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
687 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
688 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
689 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
693 else { /* we're defining a value */
694 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
696 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
698 if (ckWARN(WARN_INTERNAL))
699 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
700 retsts = SS$_NOSUCHPGM;
704 eqvdsc.dsc$a_pointer = eqv;
705 eqvdsc.dsc$w_length = strlen(eqv);
706 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
707 !str$case_blind_compare(&tmpdsc,&clisym)) {
708 unsigned int symtype;
709 if (tabvec[0]->dsc$w_length == 12 &&
710 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
711 !str$case_blind_compare(&tmpdsc,&local))
712 symtype = LIB$K_CLI_LOCAL_SYM;
713 else symtype = LIB$K_CLI_GLOBAL_SYM;
714 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
717 if (!*eqv) eqvdsc.dsc$w_length = 1;
718 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
719 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH;
720 if (ckWARN(WARN_MISC)) {
721 Perl_warner(aTHX_ WARN_MISC,"Value of logical \"%s\" too long. Truncating to %i bytes",lnm, LNM$C_NAMLENGTH);
724 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
730 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
731 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
732 set_errno(EVMSERR); break;
733 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
734 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
735 set_errno(EINVAL); break;
742 set_vaxc_errno(retsts);
743 return (int) retsts || 44; /* retsts should never be 0, but just in case */
746 /* We reset error values on success because Perl does an hv_fetch()
747 * before each hv_store(), and if the thing we're setting didn't
748 * previously exist, we've got a leftover error message. (Of course,
749 * this fails in the face of
750 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
751 * in that the error reported in $! isn't spurious,
752 * but it's right more often than not.)
754 set_errno(0); set_vaxc_errno(retsts);
758 } /* end of vmssetenv() */
761 /*{{{ void my_setenv(char *lnm, char *eqv)*/
762 /* This has to be a function since there's a prototype for it in proto.h */
764 Perl_my_setenv(pTHX_ char *lnm,char *eqv)
767 int len = strlen(lnm);
771 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
772 if (!strcmp(uplnm,"DEFAULT")) {
773 if (eqv && *eqv) chdir(eqv);
778 if (len == 6 || len == 2) {
781 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
783 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
784 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
788 (void) vmssetenv(lnm,eqv,NULL);
792 /*{{{static void vmssetuserlnm(char *name, char *eqv);
794 * sets a user-mode logical in the process logical name table
795 * used for redirection of sys$error
798 Perl_vmssetuserlnm(pTHX_ char *name, char *eqv)
800 $DESCRIPTOR(d_tab, "LNM$PROCESS");
801 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
802 unsigned long int iss, attr = LNM$M_CONFINE;
803 unsigned char acmode = PSL$C_USER;
804 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
806 d_name.dsc$a_pointer = name;
807 d_name.dsc$w_length = strlen(name);
809 lnmlst[0].buflen = strlen(eqv);
810 lnmlst[0].bufadr = eqv;
812 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
813 if (!(iss&1)) lib$signal(iss);
818 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
819 /* my_crypt - VMS password hashing
820 * my_crypt() provides an interface compatible with the Unix crypt()
821 * C library function, and uses sys$hash_password() to perform VMS
822 * password hashing. The quadword hashed password value is returned
823 * as a NUL-terminated 8 character string. my_crypt() does not change
824 * the case of its string arguments; in order to match the behavior
825 * of LOGINOUT et al., alphabetic characters in both arguments must
826 * be upcased by the caller.
829 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
831 # ifndef UAI$C_PREFERRED_ALGORITHM
832 # define UAI$C_PREFERRED_ALGORITHM 127
834 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
835 unsigned short int salt = 0;
836 unsigned long int sts;
838 unsigned short int dsc$w_length;
839 unsigned char dsc$b_type;
840 unsigned char dsc$b_class;
841 const char * dsc$a_pointer;
842 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
843 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
844 struct itmlst_3 uailst[3] = {
845 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
846 { sizeof salt, UAI$_SALT, &salt, 0},
847 { 0, 0, NULL, NULL}};
850 usrdsc.dsc$w_length = strlen(usrname);
851 usrdsc.dsc$a_pointer = usrname;
852 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
854 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
858 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
864 if (sts != RMS$_RNF) return NULL;
867 txtdsc.dsc$w_length = strlen(textpasswd);
868 txtdsc.dsc$a_pointer = textpasswd;
869 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
870 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
873 return (char *) hash;
875 } /* end of my_crypt() */
879 static char *mp_do_rmsexpand(pTHX_ char *, char *, int, char *, unsigned);
880 static char *mp_do_fileify_dirspec(pTHX_ char *, char *, int);
881 static char *mp_do_tovmsspec(pTHX_ char *, char *, int);
883 /*{{{int do_rmdir(char *name)*/
885 Perl_do_rmdir(pTHX_ char *name)
887 char dirfile[NAM$C_MAXRSS+1];
891 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
892 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
893 else retval = kill_file(dirfile);
896 } /* end of do_rmdir */
900 * Delete any file to which user has control access, regardless of whether
901 * delete access is explicitly allowed.
902 * Limitations: User must have write access to parent directory.
903 * Does not block signals or ASTs; if interrupted in midstream
904 * may leave file with an altered ACL.
907 /*{{{int kill_file(char *name)*/
909 Perl_kill_file(pTHX_ char *name)
911 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
912 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
913 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
914 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
916 unsigned char myace$b_length;
917 unsigned char myace$b_type;
918 unsigned short int myace$w_flags;
919 unsigned long int myace$l_access;
920 unsigned long int myace$l_ident;
921 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
922 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
923 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
925 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
926 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
927 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
928 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
929 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
930 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
932 /* Expand the input spec using RMS, since the CRTL remove() and
933 * system services won't do this by themselves, so we may miss
934 * a file "hiding" behind a logical name or search list. */
935 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
936 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
937 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
938 /* If not, can changing protections help? */
939 if (vaxc$errno != RMS$_PRV) return -1;
941 /* No, so we get our own UIC to use as a rights identifier,
942 * and the insert an ACE at the head of the ACL which allows us
943 * to delete the file.
945 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
946 fildsc.dsc$w_length = strlen(rspec);
947 fildsc.dsc$a_pointer = rspec;
949 newace.myace$l_ident = oldace.myace$l_ident;
950 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
952 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
953 set_errno(ENOENT); break;
955 set_errno(ENOTDIR); break;
957 set_errno(ENODEV); break;
958 case RMS$_SYN: case SS$_INVFILFOROP:
959 set_errno(EINVAL); break;
961 set_errno(EACCES); break;
965 set_vaxc_errno(aclsts);
968 /* Grab any existing ACEs with this identifier in case we fail */
969 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
970 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
971 || fndsts == SS$_NOMOREACE ) {
972 /* Add the new ACE . . . */
973 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
975 if ((rmsts = remove(name))) {
976 /* We blew it - dir with files in it, no write priv for
977 * parent directory, etc. Put things back the way they were. */
978 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
981 addlst[0].bufadr = &oldace;
982 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
989 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
990 /* We just deleted it, so of course it's not there. Some versions of
991 * VMS seem to return success on the unlock operation anyhow (after all
992 * the unlock is successful), but others don't.
994 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
995 if (aclsts & 1) aclsts = fndsts;
998 set_vaxc_errno(aclsts);
1004 } /* end of kill_file() */
1008 /*{{{int my_mkdir(char *,Mode_t)*/
1010 Perl_my_mkdir(pTHX_ char *dir, Mode_t mode)
1012 STRLEN dirlen = strlen(dir);
1014 /* zero length string sometimes gives ACCVIO */
1015 if (dirlen == 0) return -1;
1017 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1018 * null file name/type. However, it's commonplace under Unix,
1019 * so we'll allow it for a gain in portability.
1021 if (dir[dirlen-1] == '/') {
1022 char *newdir = savepvn(dir,dirlen-1);
1023 int ret = mkdir(newdir,mode);
1027 else return mkdir(dir,mode);
1028 } /* end of my_mkdir */
1031 /*{{{int my_chdir(char *)*/
1033 Perl_my_chdir(pTHX_ char *dir)
1035 STRLEN dirlen = strlen(dir);
1037 /* zero length string sometimes gives ACCVIO */
1038 if (dirlen == 0) return -1;
1040 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1042 * null file name/type. However, it's commonplace under Unix,
1043 * so we'll allow it for a gain in portability.
1045 if (dir[dirlen-1] == '/') {
1046 char *newdir = savepvn(dir,dirlen-1);
1047 int ret = chdir(newdir);
1051 else return chdir(dir);
1052 } /* end of my_chdir */
1056 /*{{{FILE *my_tmpfile()*/
1063 if ((fp = tmpfile())) return fp;
1065 New(1323,cp,L_tmpnam+24,char);
1066 strcpy(cp,"Sys$Scratch:");
1067 tmpnam(cp+strlen(cp));
1068 strcat(cp,".Perltmp");
1069 fp = fopen(cp,"w+","fop=dlt");
1076 #ifndef HOMEGROWN_POSIX_SIGNALS
1078 * The C RTL's sigaction fails to check for invalid signal numbers so we
1079 * help it out a bit. The docs are correct, but the actual routine doesn't
1080 * do what the docs say it will.
1082 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1084 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1085 struct sigaction* oact)
1087 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1088 SETERRNO(EINVAL, SS$_INVARG);
1091 return sigaction(sig, act, oact);
1096 #ifdef KILL_BY_SIGPRC
1097 #include <errnodef.h>
1099 /* okay, this is some BLATENT hackery ...
1100 we use this if the kill() in the CRTL uses sys$forcex, causing the
1101 target process to do a sys$exit, which usually can't be handled
1102 gracefully...certainly not by Perl and the %SIG{} mechanism.
1104 Instead we use the (undocumented) system service sys$sigprc.
1105 It has the same parameters as sys$forcex, but throws an exception
1106 in the target process rather than calling sys$exit.
1108 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1109 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1110 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1111 with condition codes C$_SIG0+nsig*8, catching the exception on the
1112 target process and resignaling with appropriate arguments.
1114 But we don't have that VMS 7.0+ exception handler, so if you
1115 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1117 Also note that SIGTERM is listed in the docs as being "unimplemented",
1118 yet always seems to be signaled with a VMS condition code of 4 (and
1119 correctly handled for that code). So we hardwire it in.
1121 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1122 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1123 than signalling with an unrecognized (and unhandled by CRTL) code.
1126 #define _MY_SIG_MAX 17
1129 Perl_my_kill(int pid, int sig)
1132 int sys$sigprc(unsigned int *pidadr,
1133 struct dsc$descriptor_s *prcname,
1135 static unsigned long sig_code[_MY_SIG_MAX+1] =
1138 SS$_HANGUP, /* 1 SIGHUP */
1139 SS$_CONTROLC, /* 2 SIGINT */
1140 SS$_CONTROLY, /* 3 SIGQUIT */
1141 SS$_RADRMOD, /* 4 SIGILL */
1142 SS$_BREAK, /* 5 SIGTRAP */
1143 SS$_OPCCUS, /* 6 SIGABRT */
1144 SS$_COMPAT, /* 7 SIGEMT */
1146 SS$_FLTOVF, /* 8 SIGFPE VAX */
1148 SS$_HPARITH, /* 8 SIGFPE AXP */
1150 SS$_ABORT, /* 9 SIGKILL */
1151 SS$_ACCVIO, /* 10 SIGBUS */
1152 SS$_ACCVIO, /* 11 SIGSEGV */
1153 SS$_BADPARAM, /* 12 SIGSYS */
1154 SS$_NOMBX, /* 13 SIGPIPE */
1155 SS$_ASTFLT, /* 14 SIGALRM */
1161 #if __VMS_VER >= 60200000
1162 static int initted = 0;
1165 sig_code[16] = C$_SIGUSR1;
1166 sig_code[17] = C$_SIGUSR2;
1170 if (!pid || sig < _SIG_MIN || sig > _SIG_MAX || sig > _MY_SIG_MAX || !sig_code[sig]) {
1174 iss = sys$sigprc((unsigned int *)&pid,0,sig_code[sig]);
1175 if (iss&1) return 0;
1179 set_errno(EPERM); break;
1181 case SS$_NOSUCHNODE:
1182 case SS$_UNREACHABLE:
1183 set_errno(ESRCH); break;
1185 set_errno(ENOMEM); break;
1190 set_vaxc_errno(iss);
1196 /* default piping mailbox size */
1197 #define PERL_BUFSIZ 512
1201 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
1203 unsigned long int mbxbufsiz;
1204 static unsigned long int syssize = 0;
1205 unsigned long int dviitm = DVI$_DEVNAM;
1206 char csize[LNM$C_NAMLENGTH+1];
1209 unsigned long syiitm = SYI$_MAXBUF;
1211 * Get the SYSGEN parameter MAXBUF
1213 * If the logical 'PERL_MBX_SIZE' is defined
1214 * use the value of the logical instead of PERL_BUFSIZ, but
1215 * keep the size between 128 and MAXBUF.
1218 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
1221 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
1222 mbxbufsiz = atoi(csize);
1224 mbxbufsiz = PERL_BUFSIZ;
1226 if (mbxbufsiz < 128) mbxbufsiz = 128;
1227 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
1229 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
1231 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
1232 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
1234 } /* end of create_mbx() */
1237 /*{{{ my_popen and my_pclose*/
1239 typedef struct _iosb IOSB;
1240 typedef struct _iosb* pIOSB;
1241 typedef struct _pipe Pipe;
1242 typedef struct _pipe* pPipe;
1243 typedef struct pipe_details Info;
1244 typedef struct pipe_details* pInfo;
1245 typedef struct _srqp RQE;
1246 typedef struct _srqp* pRQE;
1247 typedef struct _tochildbuf CBuf;
1248 typedef struct _tochildbuf* pCBuf;
1251 unsigned short status;
1252 unsigned short count;
1253 unsigned long dvispec;
1256 #pragma member_alignment save
1257 #pragma nomember_alignment quadword
1258 struct _srqp { /* VMS self-relative queue entry */
1259 unsigned long qptr[2];
1261 #pragma member_alignment restore
1262 static RQE RQE_ZERO = {0,0};
1264 struct _tochildbuf {
1267 unsigned short size;
1275 unsigned short chan_in;
1276 unsigned short chan_out;
1278 unsigned int bufsize;
1290 #if defined(PERL_IMPLICIT_CONTEXT)
1291 void *thx; /* Either a thread or an interpreter */
1292 /* pointer, depending on how we're built */
1300 PerlIO *fp; /* file pointer to pipe mailbox */
1301 int useFILE; /* using stdio, not perlio */
1302 int pid; /* PID of subprocess */
1303 int mode; /* == 'r' if pipe open for reading */
1304 int done; /* subprocess has completed */
1305 int waiting; /* waiting for completion/closure */
1306 int closing; /* my_pclose is closing this pipe */
1307 unsigned long completion; /* termination status of subprocess */
1308 pPipe in; /* pipe in to sub */
1309 pPipe out; /* pipe out of sub */
1310 pPipe err; /* pipe of sub's sys$error */
1311 int in_done; /* true when in pipe finished */
1316 struct exit_control_block
1318 struct exit_control_block *flink;
1319 unsigned long int (*exit_routine)();
1320 unsigned long int arg_count;
1321 unsigned long int *status_address;
1322 unsigned long int exit_status;
1325 #define RETRY_DELAY "0 ::0.20"
1326 #define MAX_RETRY 50
1328 static int pipe_ef = 0; /* first call to safe_popen inits these*/
1329 static unsigned long mypid;
1330 static unsigned long delaytime[2];
1332 static pInfo open_pipes = NULL;
1333 static $DESCRIPTOR(nl_desc, "NL:");
1335 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
1339 static unsigned long int
1340 pipe_exit_routine(pTHX)
1343 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1344 int sts, did_stuff, need_eof, j;
1347 flush any pending i/o
1353 PerlIO_flush(info->fp); /* first, flush data */
1355 fflush((FILE *)info->fp);
1361 next we try sending an EOF...ignore if doesn't work, make sure we
1369 _ckvmssts(sys$setast(0));
1370 if (info->in && !info->in->shut_on_empty) {
1371 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
1376 _ckvmssts(sys$setast(1));
1380 /* wait for EOF to have effect, up to ~ 30 sec [default] */
1382 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
1387 _ckvmssts(sys$setast(0));
1388 if (info->waiting && info->done)
1390 nwait += info->waiting;
1391 _ckvmssts(sys$setast(1));
1401 _ckvmssts(sys$setast(0));
1402 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1403 sts = sys$forcex(&info->pid,0,&abort);
1404 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1407 _ckvmssts(sys$setast(1));
1411 /* again, wait for effect */
1413 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
1418 _ckvmssts(sys$setast(0));
1419 if (info->waiting && info->done)
1421 nwait += info->waiting;
1422 _ckvmssts(sys$setast(1));
1431 _ckvmssts(sys$setast(0));
1432 if (!info->done) { /* We tried to be nice . . . */
1433 sts = sys$delprc(&info->pid,0);
1434 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1436 _ckvmssts(sys$setast(1));
1441 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1442 else if (!(sts & 1)) retsts = sts;
1447 static struct exit_control_block pipe_exitblock =
1448 {(struct exit_control_block *) 0,
1449 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1451 static void pipe_mbxtofd_ast(pPipe p);
1452 static void pipe_tochild1_ast(pPipe p);
1453 static void pipe_tochild2_ast(pPipe p);
1456 popen_completion_ast(pInfo info)
1458 pInfo i = open_pipes;
1462 if (i == info) break;
1465 if (!i) return; /* unlinked, probably freed too */
1467 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
1471 Writing to subprocess ...
1472 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
1474 chan_out may be waiting for "done" flag, or hung waiting
1475 for i/o completion to child...cancel the i/o. This will
1476 put it into "snarf mode" (done but no EOF yet) that discards
1479 Output from subprocess (stdout, stderr) needs to be flushed and
1480 shut down. We try sending an EOF, but if the mbx is full the pipe
1481 routine should still catch the "shut_on_empty" flag, telling it to
1482 use immediate-style reads so that "mbx empty" -> EOF.
1486 if (info->in && !info->in_done) { /* only for mode=w */
1487 if (info->in->shut_on_empty && info->in->need_wake) {
1488 info->in->need_wake = FALSE;
1489 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
1491 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
1495 if (info->out && !info->out_done) { /* were we also piping output? */
1496 info->out->shut_on_empty = TRUE;
1497 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1498 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1499 _ckvmssts_noperl(iss);
1502 if (info->err && !info->err_done) { /* we were piping stderr */
1503 info->err->shut_on_empty = TRUE;
1504 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1505 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1506 _ckvmssts_noperl(iss);
1508 _ckvmssts_noperl(sys$setef(pipe_ef));
1512 static unsigned long int setup_cmddsc(pTHX_ char *cmd, int check_img, int *suggest_quote);
1513 static void vms_execfree(pTHX);
1516 we actually differ from vmstrnenv since we use this to
1517 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
1518 are pointing to the same thing
1521 static unsigned short
1522 popen_translate(pTHX_ char *logical, char *result)
1525 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
1526 $DESCRIPTOR(d_log,"");
1528 unsigned short length;
1529 unsigned short code;
1531 unsigned short *retlenaddr;
1533 unsigned short l, ifi;
1535 d_log.dsc$a_pointer = logical;
1536 d_log.dsc$w_length = strlen(logical);
1538 itmlst[0].code = LNM$_STRING;
1539 itmlst[0].length = 255;
1540 itmlst[0].buffer_addr = result;
1541 itmlst[0].retlenaddr = &l;
1544 itmlst[1].length = 0;
1545 itmlst[1].buffer_addr = 0;
1546 itmlst[1].retlenaddr = 0;
1548 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
1549 if (iss == SS$_NOLOGNAM) {
1553 if (!(iss&1)) lib$signal(iss);
1556 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
1557 strip it off and return the ifi, if any
1560 if (result[0] == 0x1b && result[1] == 0x00) {
1561 memcpy(&ifi,result+2,2);
1562 strcpy(result,result+4);
1564 return ifi; /* this is the RMS internal file id */
1567 static void pipe_infromchild_ast(pPipe p);
1570 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
1571 inside an AST routine without worrying about reentrancy and which Perl
1572 memory allocator is being used.
1574 We read data and queue up the buffers, then spit them out one at a
1575 time to the output mailbox when the output mailbox is ready for one.
1578 #define INITIAL_TOCHILDQUEUE 2
1581 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
1585 char mbx1[64], mbx2[64];
1586 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1587 DSC$K_CLASS_S, mbx1},
1588 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1589 DSC$K_CLASS_S, mbx2};
1590 unsigned int dviitm = DVI$_DEVBUFSIZ;
1593 New(1368, p, 1, Pipe);
1595 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1596 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1597 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1600 p->shut_on_empty = FALSE;
1601 p->need_wake = FALSE;
1604 p->iosb.status = SS$_NORMAL;
1605 p->iosb2.status = SS$_NORMAL;
1611 #ifdef PERL_IMPLICIT_CONTEXT
1615 n = sizeof(CBuf) + p->bufsize;
1617 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
1618 _ckvmssts(lib$get_vm(&n, &b));
1619 b->buf = (char *) b + sizeof(CBuf);
1620 _ckvmssts(lib$insqhi(b, &p->free));
1623 pipe_tochild2_ast(p);
1624 pipe_tochild1_ast(p);
1630 /* reads the MBX Perl is writing, and queues */
1633 pipe_tochild1_ast(pPipe p)
1636 int iss = p->iosb.status;
1637 int eof = (iss == SS$_ENDOFFILE);
1638 #ifdef PERL_IMPLICIT_CONTEXT
1644 p->shut_on_empty = TRUE;
1646 _ckvmssts(sys$dassgn(p->chan_in));
1652 b->size = p->iosb.count;
1653 _ckvmssts(lib$insqhi(b, &p->wait));
1655 p->need_wake = FALSE;
1656 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
1659 p->retry = 1; /* initial call */
1662 if (eof) { /* flush the free queue, return when done */
1663 int n = sizeof(CBuf) + p->bufsize;
1665 iss = lib$remqti(&p->free, &b);
1666 if (iss == LIB$_QUEWASEMP) return;
1668 _ckvmssts(lib$free_vm(&n, &b));
1672 iss = lib$remqti(&p->free, &b);
1673 if (iss == LIB$_QUEWASEMP) {
1674 int n = sizeof(CBuf) + p->bufsize;
1675 _ckvmssts(lib$get_vm(&n, &b));
1676 b->buf = (char *) b + sizeof(CBuf);
1682 iss = sys$qio(0,p->chan_in,
1683 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
1685 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
1686 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
1691 /* writes queued buffers to output, waits for each to complete before
1695 pipe_tochild2_ast(pPipe p)
1698 int iss = p->iosb2.status;
1699 int n = sizeof(CBuf) + p->bufsize;
1700 int done = (p->info && p->info->done) ||
1701 iss == SS$_CANCEL || iss == SS$_ABORT;
1702 #if defined(PERL_IMPLICIT_CONTEXT)
1707 if (p->type) { /* type=1 has old buffer, dispose */
1708 if (p->shut_on_empty) {
1709 _ckvmssts(lib$free_vm(&n, &b));
1711 _ckvmssts(lib$insqhi(b, &p->free));
1716 iss = lib$remqti(&p->wait, &b);
1717 if (iss == LIB$_QUEWASEMP) {
1718 if (p->shut_on_empty) {
1720 _ckvmssts(sys$dassgn(p->chan_out));
1721 *p->pipe_done = TRUE;
1722 _ckvmssts(sys$setef(pipe_ef));
1724 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1725 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1729 p->need_wake = TRUE;
1739 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1740 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1742 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
1743 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
1752 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
1755 char mbx1[64], mbx2[64];
1756 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1757 DSC$K_CLASS_S, mbx1},
1758 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1759 DSC$K_CLASS_S, mbx2};
1760 unsigned int dviitm = DVI$_DEVBUFSIZ;
1762 New(1367, p, 1, Pipe);
1763 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1764 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1766 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1767 New(1367, p->buf, p->bufsize, char);
1768 p->shut_on_empty = FALSE;
1771 p->iosb.status = SS$_NORMAL;
1772 #if defined(PERL_IMPLICIT_CONTEXT)
1775 pipe_infromchild_ast(p);
1783 pipe_infromchild_ast(pPipe p)
1785 int iss = p->iosb.status;
1786 int eof = (iss == SS$_ENDOFFILE);
1787 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
1788 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
1789 #if defined(PERL_IMPLICIT_CONTEXT)
1793 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
1794 _ckvmssts(sys$dassgn(p->chan_out));
1799 input shutdown if EOF from self (done or shut_on_empty)
1800 output shutdown if closing flag set (my_pclose)
1801 send data/eof from child or eof from self
1802 otherwise, re-read (snarf of data from child)
1807 if (myeof && p->chan_in) { /* input shutdown */
1808 _ckvmssts(sys$dassgn(p->chan_in));
1813 if (myeof || kideof) { /* pass EOF to parent */
1814 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
1815 pipe_infromchild_ast, p,
1818 } else if (eof) { /* eat EOF --- fall through to read*/
1820 } else { /* transmit data */
1821 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
1822 pipe_infromchild_ast,p,
1823 p->buf, p->iosb.count, 0, 0, 0, 0));
1829 /* everything shut? flag as done */
1831 if (!p->chan_in && !p->chan_out) {
1832 *p->pipe_done = TRUE;
1833 _ckvmssts(sys$setef(pipe_ef));
1837 /* write completed (or read, if snarfing from child)
1838 if still have input active,
1839 queue read...immediate mode if shut_on_empty so we get EOF if empty
1841 check if Perl reading, generate EOFs as needed
1847 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
1848 pipe_infromchild_ast,p,
1849 p->buf, p->bufsize, 0, 0, 0, 0);
1850 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
1852 } else { /* send EOFs for extra reads */
1853 p->iosb.status = SS$_ENDOFFILE;
1854 p->iosb.dvispec = 0;
1855 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
1857 pipe_infromchild_ast, p, 0, 0, 0, 0));
1863 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
1867 unsigned long dviitm = DVI$_DEVBUFSIZ;
1869 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
1870 DSC$K_CLASS_S, mbx};
1872 /* things like terminals and mbx's don't need this filter */
1873 if (fd && fstat(fd,&s) == 0) {
1874 unsigned long dviitm = DVI$_DEVCHAR, devchar;
1875 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
1876 DSC$K_CLASS_S, s.st_dev};
1878 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
1879 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
1880 strcpy(out, s.st_dev);
1885 New(1366, p, 1, Pipe);
1886 p->fd_out = dup(fd);
1887 create_mbx(aTHX_ &p->chan_in, &d_mbx);
1888 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1889 New(1366, p->buf, p->bufsize+1, char);
1890 p->shut_on_empty = FALSE;
1895 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
1896 pipe_mbxtofd_ast, p,
1897 p->buf, p->bufsize, 0, 0, 0, 0));
1903 pipe_mbxtofd_ast(pPipe p)
1905 int iss = p->iosb.status;
1906 int done = p->info->done;
1908 int eof = (iss == SS$_ENDOFFILE);
1909 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
1910 int err = !(iss&1) && !eof;
1911 #if defined(PERL_IMPLICIT_CONTEXT)
1915 if (done && myeof) { /* end piping */
1917 sys$dassgn(p->chan_in);
1918 *p->pipe_done = TRUE;
1919 _ckvmssts(sys$setef(pipe_ef));
1923 if (!err && !eof) { /* good data to send to file */
1924 p->buf[p->iosb.count] = '\n';
1925 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
1928 if (p->retry < MAX_RETRY) {
1929 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
1939 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
1940 pipe_mbxtofd_ast, p,
1941 p->buf, p->bufsize, 0, 0, 0, 0);
1942 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
1947 typedef struct _pipeloc PLOC;
1948 typedef struct _pipeloc* pPLOC;
1952 char dir[NAM$C_MAXRSS+1];
1954 static pPLOC head_PLOC = 0;
1957 free_pipelocs(pTHX_ void *head)
1960 pPLOC *pHead = (pPLOC *)head;
1972 store_pipelocs(pTHX)
1981 char temp[NAM$C_MAXRSS+1];
1985 free_pipelocs(&head_PLOC);
1987 /* the . directory from @INC comes last */
1990 p->next = head_PLOC;
1992 strcpy(p->dir,"./");
1994 /* get the directory from $^X */
1996 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
1997 strcpy(temp, PL_origargv[0]);
1998 x = strrchr(temp,']');
2001 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2003 p->next = head_PLOC;
2005 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2006 p->dir[NAM$C_MAXRSS] = '\0';
2010 /* reverse order of @INC entries, skip "." since entered above */
2012 if (PL_incgv) av = GvAVn(PL_incgv);
2014 for (i = 0; av && i <= AvFILL(av); i++) {
2015 dirsv = *av_fetch(av,i,TRUE);
2017 if (SvROK(dirsv)) continue;
2018 dir = SvPVx(dirsv,n_a);
2019 if (strcmp(dir,".") == 0) continue;
2020 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2024 p->next = head_PLOC;
2026 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2027 p->dir[NAM$C_MAXRSS] = '\0';
2030 /* most likely spot (ARCHLIB) put first in the list */
2033 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
2035 p->next = head_PLOC;
2037 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2038 p->dir[NAM$C_MAXRSS] = '\0';
2041 Perl_call_atexit(aTHX_ &free_pipelocs, &head_PLOC);
2048 static int vmspipe_file_status = 0;
2049 static char vmspipe_file[NAM$C_MAXRSS+1];
2051 /* already found? Check and use ... need read+execute permission */
2053 if (vmspipe_file_status == 1) {
2054 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
2055 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
2056 return vmspipe_file;
2058 vmspipe_file_status = 0;
2061 /* scan through stored @INC, $^X */
2063 if (vmspipe_file_status == 0) {
2064 char file[NAM$C_MAXRSS+1];
2065 pPLOC p = head_PLOC;
2068 strcpy(file, p->dir);
2069 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
2070 file[NAM$C_MAXRSS] = '\0';
2073 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
2075 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
2076 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
2077 vmspipe_file_status = 1;
2078 return vmspipe_file;
2081 vmspipe_file_status = -1; /* failed, use tempfiles */
2088 vmspipe_tempfile(pTHX)
2090 char file[NAM$C_MAXRSS+1];
2092 static int index = 0;
2095 /* create a tempfile */
2097 /* we can't go from W, shr=get to R, shr=get without
2098 an intermediate vulnerable state, so don't bother trying...
2100 and lib$spawn doesn't shr=put, so have to close the write
2102 So... match up the creation date/time and the FID to
2103 make sure we're dealing with the same file
2108 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
2109 fp = fopen(file,"w");
2111 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
2112 fp = fopen(file,"w");
2114 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
2115 fp = fopen(file,"w");
2118 if (!fp) return 0; /* we're hosed */
2120 fprintf(fp,"$! 'f$verify(0)\n");
2121 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
2122 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
2123 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
2124 fprintf(fp,"$ perl_on = \"set noon\"\n");
2125 fprintf(fp,"$ perl_exit = \"exit\"\n");
2126 fprintf(fp,"$ perl_del = \"delete\"\n");
2127 fprintf(fp,"$ pif = \"if\"\n");
2128 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
2129 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
2130 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
2131 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
2132 fprintf(fp,"$! --- build command line to get max possible length\n");
2133 fprintf(fp,"$c=perl_popen_cmd0\n");
2134 fprintf(fp,"$c=c+perl_popen_cmd1\n");
2135 fprintf(fp,"$c=c+perl_popen_cmd2\n");
2136 fprintf(fp,"$x=perl_popen_cmd3\n");
2137 fprintf(fp,"$c=c+x\n");
2138 fprintf(fp,"$! --- get rid of global symbols\n");
2139 fprintf(fp,"$ perl_del/symbol/global perl_popen_in\n");
2140 fprintf(fp,"$ perl_del/symbol/global perl_popen_err\n");
2141 fprintf(fp,"$ perl_del/symbol/global perl_popen_out\n");
2142 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd0\n");
2143 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd1\n");
2144 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd2\n");
2145 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd3\n");
2146 fprintf(fp,"$ perl_on\n");
2147 fprintf(fp,"$ 'c\n");
2148 fprintf(fp,"$ perl_status = $STATUS\n");
2149 fprintf(fp,"$ perl_del 'perl_cfile'\n");
2150 fprintf(fp,"$ perl_exit 'perl_status'\n");
2153 fgetname(fp, file, 1);
2154 fstat(fileno(fp), &s0);
2157 fp = fopen(file,"r","shr=get");
2159 fstat(fileno(fp), &s1);
2161 if (s0.st_ino[0] != s1.st_ino[0] ||
2162 s0.st_ino[1] != s1.st_ino[1] ||
2163 s0.st_ino[2] != s1.st_ino[2] ||
2164 s0.st_ctime != s1.st_ctime ) {
2175 safe_popen(pTHX_ char *cmd, char *in_mode, int *psts)
2177 static int handler_set_up = FALSE;
2178 unsigned long int sts, flags=1; /* nowait - gnu c doesn't allow &1 */
2179 unsigned int table = LIB$K_CLI_GLOBAL_SYM;
2181 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
2182 char in[512], out[512], err[512], mbx[512];
2184 char tfilebuf[NAM$C_MAXRSS+1];
2186 char cmd_sym_name[20];
2187 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
2188 DSC$K_CLASS_S, symbol};
2189 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
2191 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
2192 DSC$K_CLASS_S, cmd_sym_name};
2193 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
2194 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
2195 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
2197 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
2199 /* once-per-program initialization...
2200 note that the SETAST calls and the dual test of pipe_ef
2201 makes sure that only the FIRST thread through here does
2202 the initialization...all other threads wait until it's
2205 Yeah, uglier than a pthread call, it's got all the stuff inline
2206 rather than in a separate routine.
2210 _ckvmssts(sys$setast(0));
2212 unsigned long int pidcode = JPI$_PID;
2213 $DESCRIPTOR(d_delay, RETRY_DELAY);
2214 _ckvmssts(lib$get_ef(&pipe_ef));
2215 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2216 _ckvmssts(sys$bintim(&d_delay, delaytime));
2218 if (!handler_set_up) {
2219 _ckvmssts(sys$dclexh(&pipe_exitblock));
2220 handler_set_up = TRUE;
2222 _ckvmssts(sys$setast(1));
2225 /* see if we can find a VMSPIPE.COM */
2228 vmspipe = find_vmspipe(aTHX);
2230 strcpy(tfilebuf+1,vmspipe);
2231 } else { /* uh, oh...we're in tempfile hell */
2232 tpipe = vmspipe_tempfile(aTHX);
2233 if (!tpipe) { /* a fish popular in Boston */
2234 if (ckWARN(WARN_PIPE)) {
2235 Perl_warner(aTHX_ WARN_PIPE,"unable to find VMSPIPE.COM for i/o piping");
2239 fgetname(tpipe,tfilebuf+1,1);
2241 vmspipedsc.dsc$a_pointer = tfilebuf;
2242 vmspipedsc.dsc$w_length = strlen(tfilebuf);
2244 sts = setup_cmddsc(aTHX_ cmd,0,0);
2247 case RMS$_FNF: case RMS$_DNF:
2248 set_errno(ENOENT); break;
2250 set_errno(ENOTDIR); break;
2252 set_errno(ENODEV); break;
2254 set_errno(EACCES); break;
2256 set_errno(EINVAL); break;
2257 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
2258 set_errno(E2BIG); break;
2259 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
2260 _ckvmssts(sts); /* fall through */
2261 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
2264 set_vaxc_errno(sts);
2265 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
2266 Perl_warner(aTHX_ WARN_PIPE,"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
2271 New(1301,info,1,Info);
2273 strcpy(mode,in_mode);
2276 info->completion = 0;
2277 info->closing = FALSE;
2284 info->in_done = TRUE;
2285 info->out_done = TRUE;
2286 info->err_done = TRUE;
2287 in[0] = out[0] = err[0] = '\0';
2289 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
2293 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
2298 if (*mode == 'r') { /* piping from subroutine */
2300 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
2302 info->out->pipe_done = &info->out_done;
2303 info->out_done = FALSE;
2304 info->out->info = info;
2306 if (!info->useFILE) {
2307 info->fp = PerlIO_open(mbx, mode);
2309 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
2310 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
2313 if (!info->fp && info->out) {
2314 sys$cancel(info->out->chan_out);
2316 while (!info->out_done) {
2318 _ckvmssts(sys$setast(0));
2319 done = info->out_done;
2320 if (!done) _ckvmssts(sys$clref(pipe_ef));
2321 _ckvmssts(sys$setast(1));
2322 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2325 if (info->out->buf) Safefree(info->out->buf);
2326 Safefree(info->out);
2332 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2334 info->err->pipe_done = &info->err_done;
2335 info->err_done = FALSE;
2336 info->err->info = info;
2339 } else if (*mode == 'w') { /* piping to subroutine */
2341 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2343 info->out->pipe_done = &info->out_done;
2344 info->out_done = FALSE;
2345 info->out->info = info;
2348 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2350 info->err->pipe_done = &info->err_done;
2351 info->err_done = FALSE;
2352 info->err->info = info;
2355 info->in = pipe_tochild_setup(aTHX_ in,mbx);
2356 if (!info->useFILE) {
2357 info->fp = PerlIO_open(mbx, mode);
2359 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
2360 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
2364 info->in->pipe_done = &info->in_done;
2365 info->in_done = FALSE;
2366 info->in->info = info;
2370 if (!info->fp && info->in) {
2372 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
2373 0, 0, 0, 0, 0, 0, 0, 0));
2375 while (!info->in_done) {
2377 _ckvmssts(sys$setast(0));
2378 done = info->in_done;
2379 if (!done) _ckvmssts(sys$clref(pipe_ef));
2380 _ckvmssts(sys$setast(1));
2381 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2384 if (info->in->buf) Safefree(info->in->buf);
2392 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
2393 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2395 info->out->pipe_done = &info->out_done;
2396 info->out_done = FALSE;
2397 info->out->info = info;
2400 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2402 info->err->pipe_done = &info->err_done;
2403 info->err_done = FALSE;
2404 info->err->info = info;
2408 symbol[MAX_DCL_SYMBOL] = '\0';
2410 strncpy(symbol, in, MAX_DCL_SYMBOL);
2411 d_symbol.dsc$w_length = strlen(symbol);
2412 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
2414 strncpy(symbol, err, MAX_DCL_SYMBOL);
2415 d_symbol.dsc$w_length = strlen(symbol);
2416 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
2418 strncpy(symbol, out, MAX_DCL_SYMBOL);
2419 d_symbol.dsc$w_length = strlen(symbol);
2420 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
2422 p = VMSCMD.dsc$a_pointer;
2423 while (*p && *p != '\n') p++;
2424 *p = '\0'; /* truncate on \n */
2425 p = VMSCMD.dsc$a_pointer;
2426 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
2427 if (*p == '$') p++; /* remove leading $ */
2428 while (*p == ' ' || *p == '\t') p++;
2430 for (j = 0; j < 4; j++) {
2431 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
2432 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
2434 strncpy(symbol, p, MAX_DCL_SYMBOL);
2435 d_symbol.dsc$w_length = strlen(symbol);
2436 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
2438 if (strlen(p) > MAX_DCL_SYMBOL) {
2439 p += MAX_DCL_SYMBOL;
2444 _ckvmssts(sys$setast(0));
2445 info->next=open_pipes; /* prepend to list */
2447 _ckvmssts(sys$setast(1));
2448 _ckvmssts(lib$spawn(&vmspipedsc, &nl_desc, &nl_desc, &flags,
2449 0, &info->pid, &info->completion,
2450 0, popen_completion_ast,info,0,0,0));
2452 /* if we were using a tempfile, close it now */
2454 if (tpipe) fclose(tpipe);
2456 /* once the subprocess is spawned, it has copied the symbols and
2457 we can get rid of ours */
2459 for (j = 0; j < 4; j++) {
2460 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
2461 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
2462 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
2464 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
2465 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
2466 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
2469 PL_forkprocess = info->pid;
2473 _ckvmssts(sys$setast(0));
2475 if (!done) _ckvmssts(sys$clref(pipe_ef));
2476 _ckvmssts(sys$setast(1));
2477 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2479 *psts = info->completion;
2480 my_pclose(info->fp);
2485 } /* end of safe_popen */
2488 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
2490 Perl_my_popen(pTHX_ char *cmd, char *mode)
2494 TAINT_PROPER("popen");
2495 PERL_FLUSHALL_FOR_CHILD;
2496 return safe_popen(aTHX_ cmd,mode,&sts);
2501 /*{{{ I32 my_pclose(PerlIO *fp)*/
2502 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
2504 pInfo info, last = NULL;
2505 unsigned long int retsts;
2508 for (info = open_pipes; info != NULL; last = info, info = info->next)
2509 if (info->fp == fp) break;
2511 if (info == NULL) { /* no such pipe open */
2512 set_errno(ECHILD); /* quoth POSIX */
2513 set_vaxc_errno(SS$_NONEXPR);
2517 /* If we were writing to a subprocess, insure that someone reading from
2518 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
2519 * produce an EOF record in the mailbox.
2521 * well, at least sometimes it *does*, so we have to watch out for
2522 * the first EOF closing the pipe (and DASSGN'ing the channel)...
2526 PerlIO_flush(info->fp); /* first, flush data */
2528 fflush((FILE *)info->fp);
2531 _ckvmssts(sys$setast(0));
2532 info->closing = TRUE;
2533 done = info->done && info->in_done && info->out_done && info->err_done;
2534 /* hanging on write to Perl's input? cancel it */
2535 if (info->mode == 'r' && info->out && !info->out_done) {
2536 if (info->out->chan_out) {
2537 _ckvmssts(sys$cancel(info->out->chan_out));
2538 if (!info->out->chan_in) { /* EOF generation, need AST */
2539 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
2543 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
2544 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2546 _ckvmssts(sys$setast(1));
2549 PerlIO_close(info->fp);
2551 fclose((FILE *)info->fp);
2554 we have to wait until subprocess completes, but ALSO wait until all
2555 the i/o completes...otherwise we'll be freeing the "info" structure
2556 that the i/o ASTs could still be using...
2560 _ckvmssts(sys$setast(0));
2561 done = info->done && info->in_done && info->out_done && info->err_done;
2562 if (!done) _ckvmssts(sys$clref(pipe_ef));
2563 _ckvmssts(sys$setast(1));
2564 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2566 retsts = info->completion;
2568 /* remove from list of open pipes */
2569 _ckvmssts(sys$setast(0));
2570 if (last) last->next = info->next;
2571 else open_pipes = info->next;
2572 _ckvmssts(sys$setast(1));
2574 /* free buffers and structures */
2577 if (info->in->buf) Safefree(info->in->buf);
2581 if (info->out->buf) Safefree(info->out->buf);
2582 Safefree(info->out);
2585 if (info->err->buf) Safefree(info->err->buf);
2586 Safefree(info->err);
2592 } /* end of my_pclose() */
2594 #if defined(__CRTL_VER) && __CRTL_VER >= 70100322
2595 /* Roll our own prototype because we want this regardless of whether
2596 * _VMS_WAIT is defined.
2598 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
2600 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
2601 created with popen(); otherwise partially emulate waitpid() unless
2602 we have a suitable one from the CRTL that came with VMS 7.2 and later.
2603 Also check processes not considered by the CRTL waitpid().
2605 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
2607 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
2613 if (statusp) *statusp = 0;
2615 for (info = open_pipes; info != NULL; info = info->next)
2616 if (info->pid == pid) break;
2618 if (info != NULL) { /* we know about this child */
2619 while (!info->done) {
2620 _ckvmssts(sys$setast(0));
2622 if (!done) _ckvmssts(sys$clref(pipe_ef));
2623 _ckvmssts(sys$setast(1));
2624 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2627 if (statusp) *statusp = info->completion;
2631 else { /* this child is not one of our own pipe children */
2633 #if defined(__CRTL_VER) && __CRTL_VER >= 70100322
2635 /* waitpid() became available in the CRTL as of VMS 7.0, but only
2636 * in 7.2 did we get a version that fills in the VMS completion
2637 * status as Perl has always tried to do.
2640 sts = __vms_waitpid( pid, statusp, flags );
2642 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
2645 /* If the real waitpid tells us the child does not exist, we
2646 * fall through here to implement waiting for a child that
2647 * was created by some means other than exec() (say, spawned
2648 * from DCL) or to wait for a process that is not a subprocess
2649 * of the current process.
2652 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70100322 */
2654 $DESCRIPTOR(intdsc,"0 00:00:01");
2655 unsigned long int ownercode = JPI$_OWNER, ownerpid;
2656 unsigned long int pidcode = JPI$_PID, mypid;
2657 unsigned long int interval[2];
2658 int termination_mbu = 0;
2659 unsigned short qio_iosb[4];
2660 unsigned int jpi_iosb[2];
2661 struct itmlst_3 jpilist[3] = {
2662 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
2663 {sizeof(termination_mbu), JPI$_TMBU, &termination_mbu, 0},
2666 char trmmbx[NAM$C_DVI+1];
2667 $DESCRIPTOR(trmmbxdsc,trmmbx);
2668 struct accdef trmmsg;
2669 unsigned short int mbxchan;
2672 /* Sorry folks, we don't presently implement rooting around for
2673 the first child we can find, and we definitely don't want to
2674 pass a pid of -1 to $getjpi, where it is a wildcard operation.
2680 /* Get the owner of the child so I can warn if it's not mine, plus
2681 * get the termination mailbox. If the process doesn't exist or I
2682 * don't have the privs to look at it, I can go home early.
2684 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
2685 if (sts & 1) sts = jpi_iosb[0];
2697 set_vaxc_errno(sts);
2701 if (ckWARN(WARN_EXEC)) {
2702 /* remind folks they are asking for non-standard waitpid behavior */
2703 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2704 if (ownerpid != mypid)
2705 Perl_warner(aTHX_ WARN_EXEC,
2706 "waitpid: process %x is not a child of process %x",
2710 /* It's possible to have a mailbox unit number but no actual mailbox; we
2711 * check for this by assigning a channel to it, which we need anyway.
2713 if (termination_mbu != 0) {
2714 sprintf(trmmbx, "MBA%d:", termination_mbu);
2715 trmmbxdsc.dsc$w_length = strlen(trmmbx);
2716 sts = sys$assign(&trmmbxdsc, &mbxchan, 0, 0);
2717 if (sts == SS$_NOSUCHDEV) {
2718 termination_mbu = 0; /* set up to take "no mailbox" case */
2723 /* If the process doesn't have a termination mailbox, then simply check
2724 * on it once a second until it's not there anymore.
2726 if (termination_mbu == 0) {
2727 _ckvmssts(sys$bintim(&intdsc,interval));
2728 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
2729 _ckvmssts(sys$schdwk(0,0,interval,0));
2730 _ckvmssts(sys$hiber());
2732 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
2735 /* If we do have a termination mailbox, post reads to it until we get a
2736 * termination message, discarding messages of the wrong type or for other
2737 * processes. If there is a place to put the final status, then do so.
2741 memset((void *) &trmmsg, 0, sizeof(trmmsg));
2742 sts = sys$qiow(0,mbxchan,IO$_READVBLK,&qio_iosb,0,0,
2743 &trmmsg,ACC$K_TERMLEN,0,0,0,0);
2744 if (sts & 1) sts = qio_iosb[0];
2747 && trmmsg.acc$w_msgtyp == MSG$_DELPROC
2748 && trmmsg.acc$l_pid == pid ) {
2750 if (statusp) *statusp = trmmsg.acc$l_finalsts;
2751 sts = sys$dassgn(mbxchan);
2755 } /* termination_mbu ? */
2760 } /* else one of our own pipe children */
2762 } /* end of waitpid() */
2767 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
2769 my_gconvert(double val, int ndig, int trail, char *buf)
2771 static char __gcvtbuf[DBL_DIG+1];
2774 loc = buf ? buf : __gcvtbuf;
2776 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
2778 sprintf(loc,"%.*g",ndig,val);
2784 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
2785 return gcvt(val,ndig,loc);
2788 loc[0] = '0'; loc[1] = '\0';
2796 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
2797 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
2798 * to expand file specification. Allows for a single default file
2799 * specification and a simple mask of options. If outbuf is non-NULL,
2800 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
2801 * the resultant file specification is placed. If outbuf is NULL, the
2802 * resultant file specification is placed into a static buffer.
2803 * The third argument, if non-NULL, is taken to be a default file
2804 * specification string. The fourth argument is unused at present.
2805 * rmesexpand() returns the address of the resultant string if
2806 * successful, and NULL on error.
2808 static char *mp_do_tounixspec(pTHX_ char *, char *, int);
2811 mp_do_rmsexpand(pTHX_ char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
2813 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
2814 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
2815 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
2816 struct FAB myfab = cc$rms_fab;
2817 struct NAM mynam = cc$rms_nam;
2819 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
2821 if (!filespec || !*filespec) {
2822 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
2826 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
2827 else outbuf = __rmsexpand_retbuf;
2829 if ((isunix = (strchr(filespec,'/') != NULL))) {
2830 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
2831 filespec = vmsfspec;
2834 myfab.fab$l_fna = filespec;
2835 myfab.fab$b_fns = strlen(filespec);
2836 myfab.fab$l_nam = &mynam;
2838 if (defspec && *defspec) {
2839 if (strchr(defspec,'/') != NULL) {
2840 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
2843 myfab.fab$l_dna = defspec;
2844 myfab.fab$b_dns = strlen(defspec);
2847 mynam.nam$l_esa = esa;
2848 mynam.nam$b_ess = sizeof esa;
2849 mynam.nam$l_rsa = outbuf;
2850 mynam.nam$b_rss = NAM$C_MAXRSS;
2852 retsts = sys$parse(&myfab,0,0);
2853 if (!(retsts & 1)) {
2854 mynam.nam$b_nop |= NAM$M_SYNCHK;
2855 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
2856 retsts = sys$parse(&myfab,0,0);
2857 if (retsts & 1) goto expanded;
2859 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
2860 (void) sys$parse(&myfab,0,0); /* Free search context */
2861 if (out) Safefree(out);
2862 set_vaxc_errno(retsts);
2863 if (retsts == RMS$_PRV) set_errno(EACCES);
2864 else if (retsts == RMS$_DEV) set_errno(ENODEV);
2865 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
2866 else set_errno(EVMSERR);
2869 retsts = sys$search(&myfab,0,0);
2870 if (!(retsts & 1) && retsts != RMS$_FNF) {
2871 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2872 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2873 if (out) Safefree(out);
2874 set_vaxc_errno(retsts);
2875 if (retsts == RMS$_PRV) set_errno(EACCES);
2876 else set_errno(EVMSERR);
2880 /* If the input filespec contained any lowercase characters,
2881 * downcase the result for compatibility with Unix-minded code. */
2883 for (out = myfab.fab$l_fna; *out; out++)
2884 if (islower(*out)) { haslower = 1; break; }
2885 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
2886 else { out = esa; speclen = mynam.nam$b_esl; }
2887 /* Trim off null fields added by $PARSE
2888 * If type > 1 char, must have been specified in original or default spec
2889 * (not true for version; $SEARCH may have added version of existing file).
2891 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
2892 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
2893 (mynam.nam$l_ver - mynam.nam$l_type == 1);
2894 if (trimver || trimtype) {
2895 if (defspec && *defspec) {
2896 char defesa[NAM$C_MAXRSS];
2897 struct FAB deffab = cc$rms_fab;
2898 struct NAM defnam = cc$rms_nam;
2900 deffab.fab$l_nam = &defnam;
2901 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
2902 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
2903 defnam.nam$b_nop = NAM$M_SYNCHK;
2904 if (sys$parse(&deffab,0,0) & 1) {
2905 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
2906 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
2909 if (trimver) speclen = mynam.nam$l_ver - out;
2911 /* If we didn't already trim version, copy down */
2912 if (speclen > mynam.nam$l_ver - out)
2913 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
2914 speclen - (mynam.nam$l_ver - out));
2915 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
2918 /* If we just had a directory spec on input, $PARSE "helpfully"
2919 * adds an empty name and type for us */
2920 if (mynam.nam$l_name == mynam.nam$l_type &&
2921 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
2922 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
2923 speclen = mynam.nam$l_name - out;
2924 out[speclen] = '\0';
2925 if (haslower) __mystrtolower(out);
2927 /* Have we been working with an expanded, but not resultant, spec? */
2928 /* Also, convert back to Unix syntax if necessary. */
2929 if (!mynam.nam$b_rsl) {
2931 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
2933 else strcpy(outbuf,esa);
2936 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
2937 strcpy(outbuf,tmpfspec);
2939 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2940 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
2941 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2945 /* External entry points */
2946 char *Perl_rmsexpand(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2947 { return do_rmsexpand(spec,buf,0,def,opt); }
2948 char *Perl_rmsexpand_ts(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2949 { return do_rmsexpand(spec,buf,1,def,opt); }
2953 ** The following routines are provided to make life easier when
2954 ** converting among VMS-style and Unix-style directory specifications.
2955 ** All will take input specifications in either VMS or Unix syntax. On
2956 ** failure, all return NULL. If successful, the routines listed below
2957 ** return a pointer to a buffer containing the appropriately
2958 ** reformatted spec (and, therefore, subsequent calls to that routine
2959 ** will clobber the result), while the routines of the same names with
2960 ** a _ts suffix appended will return a pointer to a mallocd string
2961 ** containing the appropriately reformatted spec.
2962 ** In all cases, only explicit syntax is altered; no check is made that
2963 ** the resulting string is valid or that the directory in question
2966 ** fileify_dirspec() - convert a directory spec into the name of the
2967 ** directory file (i.e. what you can stat() to see if it's a dir).
2968 ** The style (VMS or Unix) of the result is the same as the style
2969 ** of the parameter passed in.
2970 ** pathify_dirspec() - convert a directory spec into a path (i.e.
2971 ** what you prepend to a filename to indicate what directory it's in).
2972 ** The style (VMS or Unix) of the result is the same as the style
2973 ** of the parameter passed in.
2974 ** tounixpath() - convert a directory spec into a Unix-style path.
2975 ** tovmspath() - convert a directory spec into a VMS-style path.
2976 ** tounixspec() - convert any file spec into a Unix-style file spec.
2977 ** tovmsspec() - convert any file spec into a VMS-style spec.
2979 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
2980 ** Permission is given to distribute this code as part of the Perl
2981 ** standard distribution under the terms of the GNU General Public
2982 ** License or the Perl Artistic License. Copies of each may be
2983 ** found in the Perl standard distribution.
2986 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
2987 static char *mp_do_fileify_dirspec(pTHX_ char *dir,char *buf,int ts)
2989 static char __fileify_retbuf[NAM$C_MAXRSS+1];
2990 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
2991 char *retspec, *cp1, *cp2, *lastdir;
2992 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
2994 if (!dir || !*dir) {
2995 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
2997 dirlen = strlen(dir);
2998 while (dirlen && dir[dirlen-1] == '/') --dirlen;
2999 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
3000 strcpy(trndir,"/sys$disk/000000");
3004 if (dirlen > NAM$C_MAXRSS) {
3005 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
3007 if (!strpbrk(dir+1,"/]>:")) {
3008 strcpy(trndir,*dir == '/' ? dir + 1: dir);
3009 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
3011 dirlen = strlen(dir);
3014 strncpy(trndir,dir,dirlen);
3015 trndir[dirlen] = '\0';
3018 /* If we were handed a rooted logical name or spec, treat it like a
3019 * simple directory, so that
3020 * $ Define myroot dev:[dir.]
3021 * ... do_fileify_dirspec("myroot",buf,1) ...
3022 * does something useful.
3024 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".]")) {
3025 dir[--dirlen] = '\0';
3026 dir[dirlen-1] = ']';
3028 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".>")) {
3029 dir[--dirlen] = '\0';
3030 dir[dirlen-1] = '>';
3033 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
3034 /* If we've got an explicit filename, we can just shuffle the string. */
3035 if (*(cp1+1)) hasfilename = 1;
3036 /* Similarly, we can just back up a level if we've got multiple levels
3037 of explicit directories in a VMS spec which ends with directories. */
3039 for (cp2 = cp1; cp2 > dir; cp2--) {
3041 *cp2 = *cp1; *cp1 = '\0';
3045 if (*cp2 == '[' || *cp2 == '<') break;
3050 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
3051 if (dir[0] == '.') {
3052 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
3053 return do_fileify_dirspec("[]",buf,ts);
3054 else if (dir[1] == '.' &&
3055 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
3056 return do_fileify_dirspec("[-]",buf,ts);
3058 if (dirlen && dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
3059 dirlen -= 1; /* to last element */
3060 lastdir = strrchr(dir,'/');
3062 else if ((cp1 = strstr(dir,"/.")) != NULL) {
3063 /* If we have "/." or "/..", VMSify it and let the VMS code
3064 * below expand it, rather than repeating the code to handle
3065 * relative components of a filespec here */
3067 if (*(cp1+2) == '.') cp1++;
3068 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
3069 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
3070 if (strchr(vmsdir,'/') != NULL) {
3071 /* If do_tovmsspec() returned it, it must have VMS syntax
3072 * delimiters in it, so it's a mixed VMS/Unix spec. We take
3073 * the time to check this here only so we avoid a recursion
3074 * loop; otherwise, gigo.
3076 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
3078 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
3079 return do_tounixspec(trndir,buf,ts);
3082 } while ((cp1 = strstr(cp1,"/.")) != NULL);
3083 lastdir = strrchr(dir,'/');
3085 else if (dirlen >= 7 && !strcmp(&dir[dirlen-7],"/000000")) {
3086 /* Ditto for specs that end in an MFD -- let the VMS code
3087 * figure out whether it's a real device or a rooted logical. */
3088 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
3089 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
3090 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
3091 return do_tounixspec(trndir,buf,ts);
3094 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
3095 !(lastdir = cp1 = strrchr(dir,']')) &&
3096 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
3097 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
3099 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3100 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3101 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3102 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3103 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3104 (ver || *cp3)))))) {
3106 set_vaxc_errno(RMS$_DIR);
3112 /* If we lead off with a device or rooted logical, add the MFD
3113 if we're specifying a top-level directory. */
3114 if (lastdir && *dir == '/') {
3116 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
3123 retlen = dirlen + (addmfd ? 13 : 6);
3124 if (buf) retspec = buf;
3125 else if (ts) New(1309,retspec,retlen+1,char);
3126 else retspec = __fileify_retbuf;
3128 dirlen = lastdir - dir;
3129 memcpy(retspec,dir,dirlen);
3130 strcpy(&retspec[dirlen],"/000000");
3131 strcpy(&retspec[dirlen+7],lastdir);
3134 memcpy(retspec,dir,dirlen);
3135 retspec[dirlen] = '\0';
3137 /* We've picked up everything up to the directory file name.
3138 Now just add the type and version, and we're set. */
3139 strcat(retspec,".dir;1");
3142 else { /* VMS-style directory spec */
3143 char esa[NAM$C_MAXRSS+1], term, *cp;
3144 unsigned long int sts, cmplen, haslower = 0;
3145 struct FAB dirfab = cc$rms_fab;
3146 struct NAM savnam, dirnam = cc$rms_nam;
3148 dirfab.fab$b_fns = strlen(dir);
3149 dirfab.fab$l_fna = dir;
3150 dirfab.fab$l_nam = &dirnam;
3151 dirfab.fab$l_dna = ".DIR;1";
3152 dirfab.fab$b_dns = 6;
3153 dirnam.nam$b_ess = NAM$C_MAXRSS;
3154 dirnam.nam$l_esa = esa;
3156 for (cp = dir; *cp; cp++)
3157 if (islower(*cp)) { haslower = 1; break; }
3158 if (!((sts = sys$parse(&dirfab))&1)) {
3159 if (dirfab.fab$l_sts == RMS$_DIR) {
3160 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3161 sts = sys$parse(&dirfab) & 1;
3165 set_vaxc_errno(dirfab.fab$l_sts);
3171 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
3172 /* Yes; fake the fnb bits so we'll check type below */
3173 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
3175 else { /* No; just work with potential name */
3176 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
3178 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
3179 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3180 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3185 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
3186 cp1 = strchr(esa,']');
3187 if (!cp1) cp1 = strchr(esa,'>');
3188 if (cp1) { /* Should always be true */
3189 dirnam.nam$b_esl -= cp1 - esa - 1;
3190 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
3193 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3194 /* Yep; check version while we're at it, if it's there. */
3195 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3196 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3197 /* Something other than .DIR[;1]. Bzzt. */
3198 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3199 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3201 set_vaxc_errno(RMS$_DIR);
3205 esa[dirnam.nam$b_esl] = '\0';
3206 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
3207 /* They provided at least the name; we added the type, if necessary, */
3208 if (buf) retspec = buf; /* in sys$parse() */
3209 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
3210 else retspec = __fileify_retbuf;
3211 strcpy(retspec,esa);
3212 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3213 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3216 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
3217 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
3219 dirnam.nam$b_esl -= 9;
3221 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
3222 if (cp1 == NULL) { /* should never happen */
3223 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3224 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3229 retlen = strlen(esa);
3230 if ((cp1 = strrchr(esa,'.')) != NULL) {
3231 /* There's more than one directory in the path. Just roll back. */
3233 if (buf) retspec = buf;
3234 else if (ts) New(1311,retspec,retlen+7,char);
3235 else retspec = __fileify_retbuf;
3236 strcpy(retspec,esa);
3239 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
3240 /* Go back and expand rooted logical name */
3241 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
3242 if (!(sys$parse(&dirfab) & 1)) {
3243 dirnam.nam$l_rlf = NULL;
3244 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3246 set_vaxc_errno(dirfab.fab$l_sts);
3249 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
3250 if (buf) retspec = buf;
3251 else if (ts) New(1312,retspec,retlen+16,char);
3252 else retspec = __fileify_retbuf;
3253 cp1 = strstr(esa,"][");
3254 if (!cp1) cp1 = strstr(esa,"]<");
3256 memcpy(retspec,esa,dirlen);
3257 if (!strncmp(cp1+2,"000000]",7)) {
3258 retspec[dirlen-1] = '\0';
3259 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3260 if (*cp1 == '.') *cp1 = ']';
3262 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3263 memcpy(cp1+1,"000000]",7);
3267 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
3268 retspec[retlen] = '\0';
3269 /* Convert last '.' to ']' */
3270 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3271 if (*cp1 == '.') *cp1 = ']';
3273 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3274 memcpy(cp1+1,"000000]",7);
3278 else { /* This is a top-level dir. Add the MFD to the path. */
3279 if (buf) retspec = buf;
3280 else if (ts) New(1312,retspec,retlen+16,char);
3281 else retspec = __fileify_retbuf;
3284 while (*cp1 != ':') *(cp2++) = *(cp1++);
3285 strcpy(cp2,":[000000]");
3290 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3291 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3292 /* We've set up the string up through the filename. Add the
3293 type and version, and we're done. */
3294 strcat(retspec,".DIR;1");
3296 /* $PARSE may have upcased filespec, so convert output to lower
3297 * case if input contained any lowercase characters. */
3298 if (haslower) __mystrtolower(retspec);
3301 } /* end of do_fileify_dirspec() */
3303 /* External entry points */
3304 char *Perl_fileify_dirspec(pTHX_ char *dir, char *buf)
3305 { return do_fileify_dirspec(dir,buf,0); }
3306 char *Perl_fileify_dirspec_ts(pTHX_ char *dir, char *buf)
3307 { return do_fileify_dirspec(dir,buf,1); }
3309 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
3310 static char *mp_do_pathify_dirspec(pTHX_ char *dir,char *buf, int ts)
3312 static char __pathify_retbuf[NAM$C_MAXRSS+1];
3313 unsigned long int retlen;
3314 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
3316 if (!dir || !*dir) {
3317 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
3320 if (*dir) strcpy(trndir,dir);
3321 else getcwd(trndir,sizeof trndir - 1);
3323 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
3324 && my_trnlnm(trndir,trndir,0)) {
3325 STRLEN trnlen = strlen(trndir);
3327 /* Trap simple rooted lnms, and return lnm:[000000] */
3328 if (!strcmp(trndir+trnlen-2,".]")) {
3329 if (buf) retpath = buf;
3330 else if (ts) New(1318,retpath,strlen(dir)+10,char);
3331 else retpath = __pathify_retbuf;
3332 strcpy(retpath,dir);
3333 strcat(retpath,":[000000]");
3339 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
3340 if (*dir == '.' && (*(dir+1) == '\0' ||
3341 (*(dir+1) == '.' && *(dir+2) == '\0')))
3342 retlen = 2 + (*(dir+1) != '\0');
3344 if ( !(cp1 = strrchr(dir,'/')) &&
3345 !(cp1 = strrchr(dir,']')) &&
3346 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
3347 if ((cp2 = strchr(cp1,'.')) != NULL &&
3348 (*(cp2-1) != '/' || /* Trailing '.', '..', */
3349 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
3350 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
3351 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
3353 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3354 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3355 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3356 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3357 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3358 (ver || *cp3)))))) {
3360 set_vaxc_errno(RMS$_DIR);
3363 retlen = cp2 - dir + 1;
3365 else { /* No file type present. Treat the filename as a directory. */
3366 retlen = strlen(dir) + 1;
3369 if (buf) retpath = buf;
3370 else if (ts) New(1313,retpath,retlen+1,char);
3371 else retpath = __pathify_retbuf;
3372 strncpy(retpath,dir,retlen-1);
3373 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
3374 retpath[retlen-1] = '/'; /* with '/', add it. */
3375 retpath[retlen] = '\0';
3377 else retpath[retlen-1] = '\0';
3379 else { /* VMS-style directory spec */
3380 char esa[NAM$C_MAXRSS+1], *cp;
3381 unsigned long int sts, cmplen, haslower;
3382 struct FAB dirfab = cc$rms_fab;
3383 struct NAM savnam, dirnam = cc$rms_nam;
3385 /* If we've got an explicit filename, we can just shuffle the string. */
3386 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
3387 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
3388 if ((cp2 = strchr(cp1,'.')) != NULL) {
3390 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3391 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3392 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3393 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3394 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3395 (ver || *cp3)))))) {
3397 set_vaxc_errno(RMS$_DIR);
3401 else { /* No file type, so just draw name into directory part */
3402 for (cp2 = cp1; *cp2; cp2++) ;
3405 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
3407 /* We've now got a VMS 'path'; fall through */
3409 dirfab.fab$b_fns = strlen(dir);
3410 dirfab.fab$l_fna = dir;
3411 if (dir[dirfab.fab$b_fns-1] == ']' ||
3412 dir[dirfab.fab$b_fns-1] == '>' ||
3413 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
3414 if (buf) retpath = buf;
3415 else if (ts) New(1314,retpath,strlen(dir)+1,char);
3416 else retpath = __pathify_retbuf;
3417 strcpy(retpath,dir);
3420 dirfab.fab$l_dna = ".DIR;1";
3421 dirfab.fab$b_dns = 6;
3422 dirfab.fab$l_nam = &dirnam;
3423 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
3424 dirnam.nam$l_esa = esa;
3426 for (cp = dir; *cp; cp++)
3427 if (islower(*cp)) { haslower = 1; break; }
3429 if (!(sts = (sys$parse(&dirfab)&1))) {
3430 if (dirfab.fab$l_sts == RMS$_DIR) {
3431 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3432 sts = sys$parse(&dirfab) & 1;
3436 set_vaxc_errno(dirfab.fab$l_sts);
3442 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
3443 if (dirfab.fab$l_sts != RMS$_FNF) {
3444 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3445 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3447 set_vaxc_errno(dirfab.fab$l_sts);
3450 dirnam = savnam; /* No; just work with potential name */
3453 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3454 /* Yep; check version while we're at it, if it's there. */
3455 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3456 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3457 /* Something other than .DIR[;1]. Bzzt. */
3458 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3459 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3461 set_vaxc_errno(RMS$_DIR);
3465 /* OK, the type was fine. Now pull any file name into the
3467 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
3469 cp1 = strrchr(esa,'>');
3470 *dirnam.nam$l_type = '>';
3473 *(dirnam.nam$l_type + 1) = '\0';
3474 retlen = dirnam.nam$l_type - esa + 2;
3475 if (buf) retpath = buf;
3476 else if (ts) New(1314,retpath,retlen,char);
3477 else retpath = __pathify_retbuf;
3478 strcpy(retpath,esa);
3479 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3480 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3481 /* $PARSE may have upcased filespec, so convert output to lower
3482 * case if input contained any lowercase characters. */
3483 if (haslower) __mystrtolower(retpath);
3487 } /* end of do_pathify_dirspec() */
3489 /* External entry points */
3490 char *Perl_pathify_dirspec(pTHX_ char *dir, char *buf)
3491 { return do_pathify_dirspec(dir,buf,0); }
3492 char *Perl_pathify_dirspec_ts(pTHX_ char *dir, char *buf)
3493 { return do_pathify_dirspec(dir,buf,1); }
3495 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
3496 static char *mp_do_tounixspec(pTHX_ char *spec, char *buf, int ts)
3498 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
3499 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
3500 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
3502 if (spec == NULL) return NULL;
3503 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
3504 if (buf) rslt = buf;
3506 retlen = strlen(spec);
3507 cp1 = strchr(spec,'[');
3508 if (!cp1) cp1 = strchr(spec,'<');
3510 for (cp1++; *cp1; cp1++) {
3511 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
3512 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
3513 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
3516 New(1315,rslt,retlen+2+2*expand,char);
3518 else rslt = __tounixspec_retbuf;
3519 if (strchr(spec,'/') != NULL) {
3526 dirend = strrchr(spec,']');
3527 if (dirend == NULL) dirend = strrchr(spec,'>');
3528 if (dirend == NULL) dirend = strchr(spec,':');
3529 if (dirend == NULL) {
3533 if (*cp2 != '[' && *cp2 != '<') {
3536 else { /* the VMS spec begins with directories */
3538 if (*cp2 == ']' || *cp2 == '>') {
3539 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
3542 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
3543 if (getcwd(tmp,sizeof tmp,1) == NULL) {
3544 if (ts) Safefree(rslt);
3549 while (*cp3 != ':' && *cp3) cp3++;
3551 if (strchr(cp3,']') != NULL) break;
3552 } while (vmstrnenv(tmp,tmp,0,fildev,0));
3554 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
3555 retlen = devlen + dirlen;
3556 Renew(rslt,retlen+1+2*expand,char);
3562 *(cp1++) = *(cp3++);
3563 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
3567 else if ( *cp2 == '.') {
3568 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
3569 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3575 for (; cp2 <= dirend; cp2++) {
3578 if (*(cp2+1) == '[') cp2++;
3580 else if (*cp2 == ']' || *cp2 == '>') {
3581 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
3583 else if (*cp2 == '.') {
3585 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
3586 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
3587 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
3588 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
3589 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
3591 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
3592 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
3596 else if (*cp2 == '-') {
3597 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
3598 while (*cp2 == '-') {
3600 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3602 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
3603 if (ts) Safefree(rslt); /* filespecs like */
3604 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
3608 else *(cp1++) = *cp2;
3610 else *(cp1++) = *cp2;
3612 while (*cp2) *(cp1++) = *(cp2++);
3617 } /* end of do_tounixspec() */
3619 /* External entry points */
3620 char *Perl_tounixspec(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
3621 char *Perl_tounixspec_ts(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
3623 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
3624 static char *mp_do_tovmsspec(pTHX_ char *path, char *buf, int ts) {
3625 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
3626 char *rslt, *dirend;
3627 register char *cp1, *cp2;
3628 unsigned long int infront = 0, hasdir = 1;
3630 if (path == NULL) return NULL;
3631 if (buf) rslt = buf;
3632 else if (ts) New(1316,rslt,strlen(path)+9,char);
3633 else rslt = __tovmsspec_retbuf;
3634 if (strpbrk(path,"]:>") ||
3635 (dirend = strrchr(path,'/')) == NULL) {
3636 if (path[0] == '.') {
3637 if (path[1] == '\0') strcpy(rslt,"[]");
3638 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
3639 else strcpy(rslt,path); /* probably garbage */
3641 else strcpy(rslt,path);
3644 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
3645 if (!*(dirend+2)) dirend +=2;
3646 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
3647 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
3652 char trndev[NAM$C_MAXRSS+1];
3656 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
3658 if (!buf & ts) Renew(rslt,18,char);
3659 strcpy(rslt,"sys$disk:[000000]");
3662 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
3664 islnm = my_trnlnm(rslt,trndev,0);
3665 trnend = islnm ? strlen(trndev) - 1 : 0;
3666 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
3667 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
3668 /* If the first element of the path is a logical name, determine
3669 * whether it has to be translated so we can add more directories. */
3670 if (!islnm || rooted) {
3673 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
3677 if (cp2 != dirend) {
3678 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
3679 strcpy(rslt,trndev);
3680 cp1 = rslt + trnend;
3693 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
3694 cp2 += 2; /* skip over "./" - it's redundant */
3695 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
3697 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3698 *(cp1++) = '-'; /* "../" --> "-" */
3701 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
3702 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
3703 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3704 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
3707 if (cp2 > dirend) cp2 = dirend;
3709 else *(cp1++) = '.';
3711 for (; cp2 < dirend; cp2++) {
3713 if (*(cp2-1) == '/') continue;
3714 if (*(cp1-1) != '.') *(cp1++) = '.';
3717 else if (!infront && *cp2 == '.') {
3718 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
3719 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
3720 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3721 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
3722 else if (*(cp1-2) == '[') *(cp1-1) = '-';
3723 else { /* back up over previous directory name */
3725 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
3726 if (*(cp1-1) == '[') {
3727 memcpy(cp1,"000000.",7);
3732 if (cp2 == dirend) break;
3734 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
3735 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
3736 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
3737 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3739 *(cp1++) = '.'; /* Simulate trailing '/' */
3740 cp2 += 2; /* for loop will incr this to == dirend */
3742 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
3744 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
3747 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
3748 if (*cp2 == '.') *(cp1++) = '_';
3749 else *(cp1++) = *cp2;
3753 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
3754 if (hasdir) *(cp1++) = ']';
3755 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
3756 while (*cp2) *(cp1++) = *(cp2++);
3761 } /* end of do_tovmsspec() */
3763 /* External entry points */
3764 char *Perl_tovmsspec(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,0); }
3765 char *Perl_tovmsspec_ts(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,1); }
3767 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
3768 static char *mp_do_tovmspath(pTHX_ char *path, char *buf, int ts) {
3769 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
3771 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
3773 if (path == NULL) return NULL;
3774 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3775 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
3776 if (buf) return buf;
3778 vmslen = strlen(vmsified);
3779 New(1317,cp,vmslen+1,char);
3780 memcpy(cp,vmsified,vmslen);
3785 strcpy(__tovmspath_retbuf,vmsified);
3786 return __tovmspath_retbuf;
3789 } /* end of do_tovmspath() */
3791 /* External entry points */
3792 char *Perl_tovmspath(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,0); }
3793 char *Perl_tovmspath_ts(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,1); }
3796 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
3797 static char *mp_do_tounixpath(pTHX_ char *path, char *buf, int ts) {
3798 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
3800 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
3802 if (path == NULL) return NULL;
3803 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3804 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
3805 if (buf) return buf;
3807 unixlen = strlen(unixified);
3808 New(1317,cp,unixlen+1,char);
3809 memcpy(cp,unixified,unixlen);
3814 strcpy(__tounixpath_retbuf,unixified);
3815 return __tounixpath_retbuf;
3818 } /* end of do_tounixpath() */
3820 /* External entry points */
3821 char *Perl_tounixpath(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,0); }
3822 char *Perl_tounixpath_ts(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,1); }
3825 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
3827 *****************************************************************************
3829 * Copyright (C) 1989-1994 by *
3830 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
3832 * Permission is hereby granted for the reproduction of this software, *
3833 * on condition that this copyright notice is included in the reproduction, *
3834 * and that such reproduction is not for purposes of profit or material *
3837 * 27-Aug-1994 Modified for inclusion in perl5 *
3838 * by Charles Bailey bailey@newman.upenn.edu *
3839 *****************************************************************************
3843 * getredirection() is intended to aid in porting C programs
3844 * to VMS (Vax-11 C). The native VMS environment does not support
3845 * '>' and '<' I/O redirection, or command line wild card expansion,
3846 * or a command line pipe mechanism using the '|' AND background
3847 * command execution '&'. All of these capabilities are provided to any
3848 * C program which calls this procedure as the first thing in the
3850 * The piping mechanism will probably work with almost any 'filter' type
3851 * of program. With suitable modification, it may useful for other
3852 * portability problems as well.
3854 * Author: Mark Pizzolato mark@infocomm.com
3858 struct list_item *next;
3862 static void add_item(struct list_item **head,
3863 struct list_item **tail,
3867 static void mp_expand_wild_cards(pTHX_ char *item,
3868 struct list_item **head,
3869 struct list_item **tail,
3872 static int background_process(int argc, char **argv);
3874 static void pipe_and_fork(pTHX_ char **cmargv);
3876 /*{{{ void getredirection(int *ac, char ***av)*/
3878 mp_getredirection(pTHX_ int *ac, char ***av)
3880 * Process vms redirection arg's. Exit if any error is seen.
3881 * If getredirection() processes an argument, it is erased
3882 * from the vector. getredirection() returns a new argc and argv value.
3883 * In the event that a background command is requested (by a trailing "&"),
3884 * this routine creates a background subprocess, and simply exits the program.
3886 * Warning: do not try to simplify the code for vms. The code
3887 * presupposes that getredirection() is called before any data is
3888 * read from stdin or written to stdout.
3890 * Normal usage is as follows:
3896 * getredirection(&argc, &argv);
3900 int argc = *ac; /* Argument Count */
3901 char **argv = *av; /* Argument Vector */
3902 char *ap; /* Argument pointer */
3903 int j; /* argv[] index */
3904 int item_count = 0; /* Count of Items in List */
3905 struct list_item *list_head = 0; /* First Item in List */
3906 struct list_item *list_tail; /* Last Item in List */
3907 char *in = NULL; /* Input File Name */
3908 char *out = NULL; /* Output File Name */
3909 char *outmode = "w"; /* Mode to Open Output File */
3910 char *err = NULL; /* Error File Name */
3911 char *errmode = "w"; /* Mode to Open Error File */
3912 int cmargc = 0; /* Piped Command Arg Count */
3913 char **cmargv = NULL;/* Piped Command Arg Vector */
3916 * First handle the case where the last thing on the line ends with
3917 * a '&'. This indicates the desire for the command to be run in a
3918 * subprocess, so we satisfy that desire.
3921 if (0 == strcmp("&", ap))
3922 exit(background_process(--argc, argv));
3923 if (*ap && '&' == ap[strlen(ap)-1])
3925 ap[strlen(ap)-1] = '\0';
3926 exit(background_process(argc, argv));
3929 * Now we handle the general redirection cases that involve '>', '>>',
3930 * '<', and pipes '|'.
3932 for (j = 0; j < argc; ++j)
3934 if (0 == strcmp("<", argv[j]))
3938 fprintf(stderr,"No input file after < on command line");
3939 exit(LIB$_WRONUMARG);
3944 if ('<' == *(ap = argv[j]))
3949 if (0 == strcmp(">", ap))
3953 fprintf(stderr,"No output file after > on command line");
3954 exit(LIB$_WRONUMARG);
3973 fprintf(stderr,"No output file after > or >> on command line");
3974 exit(LIB$_WRONUMARG);
3978 if (('2' == *ap) && ('>' == ap[1]))
3995 fprintf(stderr,"No output file after 2> or 2>> on command line");
3996 exit(LIB$_WRONUMARG);
4000 if (0 == strcmp("|", argv[j]))
4004 fprintf(stderr,"No command into which to pipe on command line");
4005 exit(LIB$_WRONUMARG);
4007 cmargc = argc-(j+1);
4008 cmargv = &argv[j+1];
4012 if ('|' == *(ap = argv[j]))
4020 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
4023 * Allocate and fill in the new argument vector, Some Unix's terminate
4024 * the list with an extra null pointer.
4026 New(1302, argv, item_count+1, char *);
4028 for (j = 0; j < item_count; ++j, list_head = list_head->next)
4029 argv[j] = list_head->value;
4035 fprintf(stderr,"'|' and '>' may not both be specified on command line");
4036 exit(LIB$_INVARGORD);
4038 pipe_and_fork(aTHX_ cmargv);
4041 /* Check for input from a pipe (mailbox) */
4043 if (in == NULL && 1 == isapipe(0))
4045 char mbxname[L_tmpnam];
4047 long int dvi_item = DVI$_DEVBUFSIZ;
4048 $DESCRIPTOR(mbxnam, "");
4049 $DESCRIPTOR(mbxdevnam, "");
4051 /* Input from a pipe, reopen it in binary mode to disable */
4052 /* carriage control processing. */
4054 fgetname(stdin, mbxname);
4055 mbxnam.dsc$a_pointer = mbxname;
4056 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
4057 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
4058 mbxdevnam.dsc$a_pointer = mbxname;
4059 mbxdevnam.dsc$w_length = sizeof(mbxname);
4060 dvi_item = DVI$_DEVNAM;
4061 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
4062 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
4065 freopen(mbxname, "rb", stdin);
4068 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
4072 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
4074 fprintf(stderr,"Can't open input file %s as stdin",in);
4077 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
4079 fprintf(stderr,"Can't open output file %s as stdout",out);
4082 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
4085 if (strcmp(err,"&1") == 0) {
4086 dup2(fileno(stdout), fileno(stderr));
4087 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
4090 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
4092 fprintf(stderr,"Can't open error file %s as stderr",err);
4096 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
4100 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
4103 #ifdef ARGPROC_DEBUG
4104 PerlIO_printf(Perl_debug_log, "Arglist:\n");
4105 for (j = 0; j < *ac; ++j)
4106 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
4108 /* Clear errors we may have hit expanding wildcards, so they don't
4109 show up in Perl's $! later */
4110 set_errno(0); set_vaxc_errno(1);
4111 } /* end of getredirection() */
4114 static void add_item(struct list_item **head,
4115 struct list_item **tail,
4121 New(1303,*head,1,struct list_item);
4125 New(1304,(*tail)->next,1,struct list_item);
4126 *tail = (*tail)->next;
4128 (*tail)->value = value;
4132 static void mp_expand_wild_cards(pTHX_ char *item,
4133 struct list_item **head,
4134 struct list_item **tail,
4138 unsigned long int context = 0;
4144 char vmsspec[NAM$C_MAXRSS+1];
4145 $DESCRIPTOR(filespec, "");
4146 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
4147 $DESCRIPTOR(resultspec, "");
4148 unsigned long int zero = 0, sts;
4150 for (cp = item; *cp; cp++) {
4151 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
4152 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
4154 if (!*cp || isspace(*cp))
4156 add_item(head, tail, item, count);
4159 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
4160 resultspec.dsc$b_class = DSC$K_CLASS_D;
4161 resultspec.dsc$a_pointer = NULL;
4162 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
4163 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
4164 if (!isunix || !filespec.dsc$a_pointer)
4165 filespec.dsc$a_pointer = item;
4166 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
4168 * Only return version specs, if the caller specified a version
4170 had_version = strchr(item, ';');
4172 * Only return device and directory specs, if the caller specifed either.
4174 had_device = strchr(item, ':');
4175 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
4177 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
4178 &defaultspec, 0, 0, &zero))))
4183 New(1305,string,resultspec.dsc$w_length+1,char);
4184 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
4185 string[resultspec.dsc$w_length] = '\0';
4186 if (NULL == had_version)
4187 *((char *)strrchr(string, ';')) = '\0';
4188 if ((!had_directory) && (had_device == NULL))
4190 if (NULL == (devdir = strrchr(string, ']')))
4191 devdir = strrchr(string, '>');
4192 strcpy(string, devdir + 1);
4195 * Be consistent with what the C RTL has already done to the rest of
4196 * the argv items and lowercase all of these names.
4198 for (c = string; *c; ++c)
4201 if (isunix) trim_unixpath(string,item,1);
4202 add_item(head, tail, string, count);
4205 if (sts != RMS$_NMF)
4207 set_vaxc_errno(sts);
4210 case RMS$_FNF: case RMS$_DNF:
4211 set_errno(ENOENT); break;
4213 set_errno(ENOTDIR); break;
4215 set_errno(ENODEV); break;
4216 case RMS$_FNM: case RMS$_SYN:
4217 set_errno(EINVAL); break;
4219 set_errno(EACCES); break;
4221 _ckvmssts_noperl(sts);
4225 add_item(head, tail, item, count);
4226 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
4227 _ckvmssts_noperl(lib$find_file_end(&context));
4230 static int child_st[2];/* Event Flag set when child process completes */
4232 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
4234 static unsigned long int exit_handler(int *status)
4238 if (0 == child_st[0])
4240 #ifdef ARGPROC_DEBUG
4241 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
4243 fflush(stdout); /* Have to flush pipe for binary data to */
4244 /* terminate properly -- <tp@mccall.com> */
4245 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
4246 sys$dassgn(child_chan);
4248 sys$synch(0, child_st);
4253 static void sig_child(int chan)
4255 #ifdef ARGPROC_DEBUG
4256 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
4258 if (child_st[0] == 0)
4262 static struct exit_control_block exit_block =
4267 &exit_block.exit_status,
4272 pipe_and_fork(pTHX_ char **cmargv)
4275 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
4276 int sts, j, l, ismcr, quote, tquote = 0;
4278 sts = setup_cmddsc(cmargv[0],0,"e);
4283 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
4284 && toupper(*(q+2)) == 'R' && !*(q+3);
4286 while (q && l < MAX_DCL_LINE_LENGTH) {
4288 if (j > 0 && quote) {
4294 if (ismcr && j > 1) quote = 1;
4295 tquote = (strchr(q,' ')) != NULL || *q == '\0';
4298 if (quote || tquote) {
4304 if ((quote||tquote) && *q == '"') {
4314 fp = safe_popen(subcmd,"wbF",&sts);
4316 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
4320 static int background_process(int argc, char **argv)
4322 char command[2048] = "$";
4323 $DESCRIPTOR(value, "");
4324 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
4325 static $DESCRIPTOR(null, "NLA0:");
4326 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
4328 $DESCRIPTOR(pidstr, "");
4330 unsigned long int flags = 17, one = 1, retsts;
4332 strcat(command, argv[0]);
4335 strcat(command, " \"");
4336 strcat(command, *(++argv));
4337 strcat(command, "\"");
4339 value.dsc$a_pointer = command;
4340 value.dsc$w_length = strlen(value.dsc$a_pointer);
4341 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
4342 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
4343 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
4344 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
4347 _ckvmssts_noperl(retsts);
4349 #ifdef ARGPROC_DEBUG
4350 PerlIO_printf(Perl_debug_log, "%s\n", command);
4352 sprintf(pidstring, "%08X", pid);
4353 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
4354 pidstr.dsc$a_pointer = pidstring;
4355 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
4356 lib$set_symbol(&pidsymbol, &pidstr);
4360 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
4363 /* OS-specific initialization at image activation (not thread startup) */
4364 /* Older VAXC header files lack these constants */
4365 #ifndef JPI$_RIGHTS_SIZE
4366 # define JPI$_RIGHTS_SIZE 817
4368 #ifndef KGB$M_SUBSYSTEM
4369 # define KGB$M_SUBSYSTEM 0x8
4372 /*{{{void vms_image_init(int *, char ***)*/
4374 vms_image_init(int *argcp, char ***argvp)
4376 char eqv[LNM$C_NAMLENGTH+1] = "";
4377 unsigned int len, tabct = 8, tabidx = 0;
4378 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
4379 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
4380 unsigned short int dummy, rlen;
4381 struct dsc$descriptor_s **tabvec;
4382 #if defined(PERL_IMPLICIT_CONTEXT)
4385 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
4386 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
4387 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
4390 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
4391 _ckvmssts_noperl(iosb[0]);
4392 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
4393 if (iprv[i]) { /* Running image installed with privs? */
4394 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
4399 /* Rights identifiers might trigger tainting as well. */
4400 if (!will_taint && (rlen || rsz)) {
4401 while (rlen < rsz) {
4402 /* We didn't get all the identifiers on the first pass. Allocate a
4403 * buffer much larger than $GETJPI wants (rsz is size in bytes that
4404 * were needed to hold all identifiers at time of last call; we'll
4405 * allocate that many unsigned long ints), and go back and get 'em.
4406 * If it gave us less than it wanted to despite ample buffer space,
4407 * something's broken. Is your system missing a system identifier?
4409 if (rsz <= jpilist[1].buflen) {
4410 /* Perl_croak accvios when used this early in startup. */
4411 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
4412 rsz, (unsigned long) jpilist[1].buflen,
4413 "Check your rights database for corruption.\n");
4416 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
4417 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
4418 jpilist[1].buflen = rsz * sizeof(unsigned long int);
4419 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
4420 _ckvmssts_noperl(iosb[0]);
4422 mask = jpilist[1].bufadr;
4423 /* Check attribute flags for each identifier (2nd longword); protected
4424 * subsystem identifiers trigger tainting.
4426 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
4427 if (mask[i] & KGB$M_SUBSYSTEM) {
4432 if (mask != rlst) Safefree(mask);
4434 /* We need to use this hack to tell Perl it should run with tainting,
4435 * since its tainting flag may be part of the PL_curinterp struct, which
4436 * hasn't been allocated when vms_image_init() is called.
4440 New(1320,newap,*argcp+2,char **);
4441 newap[0] = argvp[0];
4443 Copy(argvp[1],newap[2],*argcp-1,char **);
4444 /* We orphan the old argv, since we don't know where it's come from,
4445 * so we don't know how to free it.
4447 *argcp++; argvp = newap;
4449 else { /* Did user explicitly request tainting? */
4451 char *cp, **av = *argvp;
4452 for (i = 1; i < *argcp; i++) {
4453 if (*av[i] != '-') break;
4454 for (cp = av[i]+1; *cp; cp++) {
4455 if (*cp == 'T') { will_taint = 1; break; }
4456 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
4457 strchr("DFIiMmx",*cp)) break;
4459 if (will_taint) break;
4464 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
4466 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
4467 else if (tabidx >= tabct) {
4469 Renew(tabvec,tabct,struct dsc$descriptor_s *);
4471 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
4472 tabvec[tabidx]->dsc$w_length = 0;
4473 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
4474 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
4475 tabvec[tabidx]->dsc$a_pointer = NULL;
4476 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
4478 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
4480 getredirection(argcp,argvp);
4481 #if defined(USE_5005THREADS) && ( defined(__DECC) || defined(__DECCXX) )
4483 # include <reentrancy.h>
4484 (void) decc$set_reentrancy(C$C_MULTITHREAD);
4493 * Trim Unix-style prefix off filespec, so it looks like what a shell
4494 * glob expansion would return (i.e. from specified prefix on, not
4495 * full path). Note that returned filespec is Unix-style, regardless
4496 * of whether input filespec was VMS-style or Unix-style.
4498 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
4499 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
4500 * vector of options; at present, only bit 0 is used, and if set tells
4501 * trim unixpath to try the current default directory as a prefix when
4502 * presented with a possibly ambiguous ... wildcard.
4504 * Returns !=0 on success, with trimmed filespec replacing contents of
4505 * fspec, and 0 on failure, with contents of fpsec unchanged.
4507 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
4509 Perl_trim_unixpath(pTHX_ char *fspec, char *wildspec, int opts)
4511 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
4512 *template, *base, *end, *cp1, *cp2;
4513 register int tmplen, reslen = 0, dirs = 0;
4515 if (!wildspec || !fspec) return 0;
4516 if (strpbrk(wildspec,"]>:") != NULL) {
4517 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
4518 else template = unixwild;
4520 else template = wildspec;
4521 if (strpbrk(fspec,"]>:") != NULL) {
4522 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
4523 else base = unixified;
4524 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
4525 * check to see that final result fits into (isn't longer than) fspec */
4526 reslen = strlen(fspec);
4530 /* No prefix or absolute path on wildcard, so nothing to remove */
4531 if (!*template || *template == '/') {
4532 if (base == fspec) return 1;
4533 tmplen = strlen(unixified);
4534 if (tmplen > reslen) return 0; /* not enough space */
4535 /* Copy unixified resultant, including trailing NUL */
4536 memmove(fspec,unixified,tmplen+1);
4540 for (end = base; *end; end++) ; /* Find end of resultant filespec */
4541 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
4542 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
4543 for (cp1 = end ;cp1 >= base; cp1--)
4544 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
4546 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
4550 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
4551 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
4552 int ells = 1, totells, segdirs, match;
4553 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
4554 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4556 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
4558 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
4559 if (ellipsis == template && opts & 1) {
4560 /* Template begins with an ellipsis. Since we can't tell how many
4561 * directory names at the front of the resultant to keep for an
4562 * arbitrary starting point, we arbitrarily choose the current
4563 * default directory as a starting point. If it's there as a prefix,
4564 * clip it off. If not, fall through and act as if the leading
4565 * ellipsis weren't there (i.e. return shortest possible path that
4566 * could match template).
4568 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
4569 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4570 if (_tolower(*cp1) != _tolower(*cp2)) break;
4571 segdirs = dirs - totells; /* Min # of dirs we must have left */
4572 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
4573 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
4574 memcpy(fspec,cp2+1,end - cp2);
4578 /* First off, back up over constant elements at end of path */
4580 for (front = end ; front >= base; front--)
4581 if (*front == '/' && !dirs--) { front++; break; }
4583 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
4584 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
4585 if (cp1 != '\0') return 0; /* Path too long. */
4587 *cp2 = '\0'; /* Pick up with memcpy later */
4588 lcfront = lcres + (front - base);
4589 /* Now skip over each ellipsis and try to match the path in front of it. */
4591 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
4592 if (*(cp1) == '.' && *(cp1+1) == '.' &&
4593 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
4594 if (cp1 < template) break; /* template started with an ellipsis */
4595 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
4596 ellipsis = cp1; continue;
4598 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
4600 for (segdirs = 0, cp2 = tpl;
4601 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
4603 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
4604 else *cp2 = _tolower(*cp1); /* else lowercase for match */
4605 if (*cp2 == '/') segdirs++;
4607 if (cp1 != ellipsis - 1) return 0; /* Path too long */
4608 /* Back up at least as many dirs as in template before matching */
4609 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
4610 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
4611 for (match = 0; cp1 > lcres;) {
4612 resdsc.dsc$a_pointer = cp1;
4613 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
4615 if (match == 1) lcfront = cp1;
4617 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
4619 if (!match) return 0; /* Can't find prefix ??? */
4620 if (match > 1 && opts & 1) {
4621 /* This ... wildcard could cover more than one set of dirs (i.e.
4622 * a set of similar dir names is repeated). If the template
4623 * contains more than 1 ..., upstream elements could resolve the
4624 * ambiguity, but it's not worth a full backtracking setup here.
4625 * As a quick heuristic, clip off the current default directory
4626 * if it's present to find the trimmed spec, else use the
4627 * shortest string that this ... could cover.
4629 char def[NAM$C_MAXRSS+1], *st;
4631 if (getcwd(def, sizeof def,0) == NULL) return 0;
4632 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4633 if (_tolower(*cp1) != _tolower(*cp2)) break;
4634 segdirs = dirs - totells; /* Min # of dirs we must have left */
4635 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
4636 if (*cp1 == '\0' && *cp2 == '/') {
4637 memcpy(fspec,cp2+1,end - cp2);
4640 /* Nope -- stick with lcfront from above and keep going. */
4643 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
4648 } /* end of trim_unixpath() */
4653 * VMS readdir() routines.
4654 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
4656 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
4657 * Minor modifications to original routines.
4660 /* Number of elements in vms_versions array */
4661 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
4664 * Open a directory, return a handle for later use.
4666 /*{{{ DIR *opendir(char*name) */
4668 Perl_opendir(pTHX_ char *name)
4671 char dir[NAM$C_MAXRSS+1];
4674 if (do_tovmspath(name,dir,0) == NULL) {
4677 if (flex_stat(dir,&sb) == -1) return NULL;
4678 if (!S_ISDIR(sb.st_mode)) {
4679 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
4682 if (!cando_by_name(S_IRUSR,0,dir)) {
4683 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
4686 /* Get memory for the handle, and the pattern. */
4688 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
4690 /* Fill in the fields; mainly playing with the descriptor. */
4691 (void)sprintf(dd->pattern, "%s*.*",dir);
4694 dd->vms_wantversions = 0;
4695 dd->pat.dsc$a_pointer = dd->pattern;
4696 dd->pat.dsc$w_length = strlen(dd->pattern);
4697 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
4698 dd->pat.dsc$b_class = DSC$K_CLASS_S;
4701 } /* end of opendir() */
4705 * Set the flag to indicate we want versions or not.
4707 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
4709 vmsreaddirversions(DIR *dd, int flag)
4711 dd->vms_wantversions = flag;
4716 * Free up an opened directory.
4718 /*{{{ void closedir(DIR *dd)*/
4722 (void)lib$find_file_end(&dd->context);
4723 Safefree(dd->pattern);
4724 Safefree((char *)dd);
4729 * Collect all the version numbers for the current file.
4732 collectversions(pTHX_ DIR *dd)
4734 struct dsc$descriptor_s pat;
4735 struct dsc$descriptor_s res;
4737 char *p, *text, buff[sizeof dd->entry.d_name];
4739 unsigned long context, tmpsts;
4741 /* Convenient shorthand. */
4744 /* Add the version wildcard, ignoring the "*.*" put on before */
4745 i = strlen(dd->pattern);
4746 New(1308,text,i + e->d_namlen + 3,char);
4747 (void)strcpy(text, dd->pattern);
4748 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
4750 /* Set up the pattern descriptor. */
4751 pat.dsc$a_pointer = text;
4752 pat.dsc$w_length = i + e->d_namlen - 1;
4753 pat.dsc$b_dtype = DSC$K_DTYPE_T;
4754 pat.dsc$b_class = DSC$K_CLASS_S;
4756 /* Set up result descriptor. */
4757 res.dsc$a_pointer = buff;
4758 res.dsc$w_length = sizeof buff - 2;
4759 res.dsc$b_dtype = DSC$K_DTYPE_T;
4760 res.dsc$b_class = DSC$K_CLASS_S;
4762 /* Read files, collecting versions. */
4763 for (context = 0, e->vms_verscount = 0;
4764 e->vms_verscount < VERSIZE(e);
4765 e->vms_verscount++) {
4766 tmpsts = lib$find_file(&pat, &res, &context);
4767 if (tmpsts == RMS$_NMF || context == 0) break;
4769 buff[sizeof buff - 1] = '\0';
4770 if ((p = strchr(buff, ';')))
4771 e->vms_versions[e->vms_verscount] = atoi(p + 1);
4773 e->vms_versions[e->vms_verscount] = -1;
4776 _ckvmssts(lib$find_file_end(&context));
4779 } /* end of collectversions() */
4782 * Read the next entry from the directory.
4784 /*{{{ struct dirent *readdir(DIR *dd)*/
4786 Perl_readdir(pTHX_ DIR *dd)
4788 struct dsc$descriptor_s res;
4789 char *p, buff[sizeof dd->entry.d_name];
4790 unsigned long int tmpsts;
4792 /* Set up result descriptor, and get next file. */
4793 res.dsc$a_pointer = buff;
4794 res.dsc$w_length = sizeof buff - 2;
4795 res.dsc$b_dtype = DSC$K_DTYPE_T;
4796 res.dsc$b_class = DSC$K_CLASS_S;
4797 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4798 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
4799 if (!(tmpsts & 1)) {
4800 set_vaxc_errno(tmpsts);
4803 set_errno(EACCES); break;
4805 set_errno(ENODEV); break;
4807 set_errno(ENOTDIR); break;
4808 case RMS$_FNF: case RMS$_DNF:
4809 set_errno(ENOENT); break;
4816 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
4817 buff[sizeof buff - 1] = '\0';
4818 for (p = buff; *p; p++) *p = _tolower(*p);
4819 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
4822 /* Skip any directory component and just copy the name. */
4823 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
4824 else (void)strcpy(dd->entry.d_name, buff);
4826 /* Clobber the version. */
4827 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
4829 dd->entry.d_namlen = strlen(dd->entry.d_name);
4830 dd->entry.vms_verscount = 0;
4831 if (dd->vms_wantversions) collectversions(aTHX_ dd);
4834 } /* end of readdir() */
4838 * Return something that can be used in a seekdir later.
4840 /*{{{ long telldir(DIR *dd)*/
4849 * Return to a spot where we used to be. Brute force.
4851 /*{{{ void seekdir(DIR *dd,long count)*/
4853 Perl_seekdir(pTHX_ DIR *dd, long count)
4855 int vms_wantversions;
4857 /* If we haven't done anything yet... */
4861 /* Remember some state, and clear it. */
4862 vms_wantversions = dd->vms_wantversions;
4863 dd->vms_wantversions = 0;
4864 _ckvmssts(lib$find_file_end(&dd->context));
4867 /* The increment is in readdir(). */
4868 for (dd->count = 0; dd->count < count; )
4871 dd->vms_wantversions = vms_wantversions;
4873 } /* end of seekdir() */
4876 /* VMS subprocess management
4878 * my_vfork() - just a vfork(), after setting a flag to record that
4879 * the current script is trying a Unix-style fork/exec.
4881 * vms_do_aexec() and vms_do_exec() are called in response to the
4882 * perl 'exec' function. If this follows a vfork call, then they
4883 * call out the the regular perl routines in doio.c which do an
4884 * execvp (for those who really want to try this under VMS).
4885 * Otherwise, they do exactly what the perl docs say exec should
4886 * do - terminate the current script and invoke a new command
4887 * (See below for notes on command syntax.)
4889 * do_aspawn() and do_spawn() implement the VMS side of the perl
4890 * 'system' function.
4892 * Note on command arguments to perl 'exec' and 'system': When handled
4893 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
4894 * are concatenated to form a DCL command string. If the first arg
4895 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
4896 * the the command string is handed off to DCL directly. Otherwise,
4897 * the first token of the command is taken as the filespec of an image
4898 * to run. The filespec is expanded using a default type of '.EXE' and
4899 * the process defaults for device, directory, etc., and if found, the resultant
4900 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
4901 * the command string as parameters. This is perhaps a bit complicated,
4902 * but I hope it will form a happy medium between what VMS folks expect
4903 * from lib$spawn and what Unix folks expect from exec.
4906 static int vfork_called;
4908 /*{{{int my_vfork()*/
4919 vms_execfree(pTHX) {
4921 if (PL_Cmd != VMSCMD.dsc$a_pointer) Safefree(PL_Cmd);
4924 if (VMSCMD.dsc$a_pointer) {
4925 Safefree(VMSCMD.dsc$a_pointer);
4926 VMSCMD.dsc$w_length = 0;
4927 VMSCMD.dsc$a_pointer = Nullch;
4932 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
4934 char *junk, *tmps = Nullch;
4935 register size_t cmdlen = 0;
4942 tmps = SvPV(really,rlen);
4949 for (idx++; idx <= sp; idx++) {
4951 junk = SvPVx(*idx,rlen);
4952 cmdlen += rlen ? rlen + 1 : 0;
4955 New(401,PL_Cmd,cmdlen+1,char);
4957 if (tmps && *tmps) {
4958 strcpy(PL_Cmd,tmps);
4961 else *PL_Cmd = '\0';
4962 while (++mark <= sp) {
4964 char *s = SvPVx(*mark,n_a);
4966 if (*PL_Cmd) strcat(PL_Cmd," ");
4972 } /* end of setup_argstr() */
4975 static unsigned long int
4976 setup_cmddsc(pTHX_ char *cmd, int check_img, int *suggest_quote)
4978 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
4979 $DESCRIPTOR(defdsc,".EXE");
4980 $DESCRIPTOR(defdsc2,".");
4981 $DESCRIPTOR(resdsc,resspec);
4982 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4983 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
4984 register char *s, *rest, *cp, *wordbreak;
4987 if (suggest_quote) *suggest_quote = 0;
4989 if (strlen(cmd) > MAX_DCL_LINE_LENGTH)
4990 return CLI$_BUFOVF; /* continuation lines currently unsupported */
4992 while (*s && isspace(*s)) s++;
4994 if (*s == '@' || *s == '$') {
4995 vmsspec[0] = *s; rest = s + 1;
4996 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
4998 else { cp = vmsspec; rest = s; }
4999 if (*rest == '.' || *rest == '/') {
5002 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
5003 rest++, cp2++) *cp2 = *rest;
5005 if (do_tovmsspec(resspec,cp,0)) {
5008 for (cp2 = vmsspec + strlen(vmsspec);
5009 *rest && cp2 - vmsspec < sizeof vmsspec;
5010 rest++, cp2++) *cp2 = *rest;
5015 /* Intuit whether verb (first word of cmd) is a DCL command:
5016 * - if first nonspace char is '@', it's a DCL indirection
5018 * - if verb contains a filespec separator, it's not a DCL command
5019 * - if it doesn't, caller tells us whether to default to a DCL
5020 * command, or to a local image unless told it's DCL (by leading '$')
5024 if (suggest_quote) *suggest_quote = 1;
5026 register char *filespec = strpbrk(s,":<[.;");
5027 rest = wordbreak = strpbrk(s," \"\t/");
5028 if (!wordbreak) wordbreak = s + strlen(s);
5029 if (*s == '$') check_img = 0;
5030 if (filespec && (filespec < wordbreak)) isdcl = 0;
5031 else isdcl = !check_img;
5035 imgdsc.dsc$a_pointer = s;
5036 imgdsc.dsc$w_length = wordbreak - s;
5037 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
5039 _ckvmssts(lib$find_file_end(&cxt));
5040 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
5041 if (!(retsts & 1) && *s == '$') {
5042 _ckvmssts(lib$find_file_end(&cxt));
5043 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
5044 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
5046 _ckvmssts(lib$find_file_end(&cxt));
5047 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
5051 _ckvmssts(lib$find_file_end(&cxt));
5056 while (*s && !isspace(*s)) s++;
5059 /* check that it's really not DCL with no file extension */
5060 fp = fopen(resspec,"r","ctx=bin,shr=get");
5062 char b[4] = {0,0,0,0};
5063 read(fileno(fp),b,4);
5064 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
5067 if (check_img && isdcl) return RMS$_FNF;
5069 if (cando_by_name(S_IXUSR,0,resspec)) {
5070 New(402,VMSCMD.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
5072 strcpy(VMSCMD.dsc$a_pointer,"$ MCR ");
5073 if (suggest_quote) *suggest_quote = 1;
5075 strcpy(VMSCMD.dsc$a_pointer,"@");
5076 if (suggest_quote) *suggest_quote = 1;
5078 strcat(VMSCMD.dsc$a_pointer,resspec);
5079 if (rest) strcat(VMSCMD.dsc$a_pointer,rest);
5080 VMSCMD.dsc$w_length = strlen(VMSCMD.dsc$a_pointer);
5081 return (VMSCMD.dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
5083 else retsts = RMS$_PRV;
5086 /* It's either a DCL command or we couldn't find a suitable image */
5087 VMSCMD.dsc$w_length = strlen(cmd);
5088 if (cmd == PL_Cmd) {
5089 VMSCMD.dsc$a_pointer = PL_Cmd;
5090 if (suggest_quote) *suggest_quote = 1;
5092 else VMSCMD.dsc$a_pointer = savepvn(cmd,VMSCMD.dsc$w_length);
5094 /* check if it's a symbol (for quoting purposes) */
5095 if (suggest_quote && !*suggest_quote) {
5097 char equiv[LNM$C_NAMLENGTH];
5098 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5099 eqvdsc.dsc$a_pointer = equiv;
5101 iss = lib$get_symbol(&VMSCMD,&eqvdsc);
5102 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
5104 if (!(retsts & 1)) {
5105 /* just hand off status values likely to be due to user error */
5106 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
5107 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
5108 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
5109 else { _ckvmssts(retsts); }
5112 return (VMSCMD.dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
5114 } /* end of setup_cmddsc() */
5117 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
5119 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
5122 if (vfork_called) { /* this follows a vfork - act Unixish */
5124 if (vfork_called < 0) {
5125 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
5128 else return do_aexec(really,mark,sp);
5130 /* no vfork - act VMSish */
5131 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
5136 } /* end of vms_do_aexec() */
5139 /* {{{bool vms_do_exec(char *cmd) */
5141 Perl_vms_do_exec(pTHX_ char *cmd)
5144 if (vfork_called) { /* this follows a vfork - act Unixish */
5146 if (vfork_called < 0) {
5147 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
5150 else return do_exec(cmd);
5153 { /* no vfork - act VMSish */
5154 unsigned long int retsts;
5157 TAINT_PROPER("exec");
5158 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0)) & 1)
5159 retsts = lib$do_command(&VMSCMD);
5162 case RMS$_FNF: case RMS$_DNF:
5163 set_errno(ENOENT); break;
5165 set_errno(ENOTDIR); break;
5167 set_errno(ENODEV); break;
5169 set_errno(EACCES); break;
5171 set_errno(EINVAL); break;
5172 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
5173 set_errno(E2BIG); break;
5174 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
5175 _ckvmssts(retsts); /* fall through */
5176 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
5179 set_vaxc_errno(retsts);
5180 if (ckWARN(WARN_EXEC)) {
5181 Perl_warner(aTHX_ WARN_EXEC,"Can't exec \"%*s\": %s",
5182 VMSCMD.dsc$w_length, VMSCMD.dsc$a_pointer, Strerror(errno));
5189 } /* end of vms_do_exec() */
5192 unsigned long int Perl_do_spawn(pTHX_ char *);
5194 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
5196 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
5198 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
5201 } /* end of do_aspawn() */
5204 /* {{{unsigned long int do_spawn(char *cmd) */
5206 Perl_do_spawn(pTHX_ char *cmd)
5208 unsigned long int sts, substs;
5211 TAINT_PROPER("spawn");
5212 if (!cmd || !*cmd) {
5213 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
5216 case RMS$_FNF: case RMS$_DNF:
5217 set_errno(ENOENT); break;
5219 set_errno(ENOTDIR); break;
5221 set_errno(ENODEV); break;
5223 set_errno(EACCES); break;
5225 set_errno(EINVAL); break;
5226 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
5227 set_errno(E2BIG); break;
5228 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
5229 _ckvmssts(sts); /* fall through */
5230 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
5233 set_vaxc_errno(sts);
5234 if (ckWARN(WARN_EXEC)) {
5235 Perl_warner(aTHX_ WARN_EXEC,"Can't spawn: %s",
5242 (void) safe_popen(cmd, "nW", (int *)&sts);
5245 } /* end of do_spawn() */
5249 static unsigned int *sockflags, sockflagsize;
5252 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
5253 * routines found in some versions of the CRTL can't deal with sockets.
5254 * We don't shim the other file open routines since a socket isn't
5255 * likely to be opened by a name.
5257 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
5258 FILE *my_fdopen(int fd, const char *mode)
5260 FILE *fp = fdopen(fd, (char *) mode);
5263 unsigned int fdoff = fd / sizeof(unsigned int);
5264 struct stat sbuf; /* native stat; we don't need flex_stat */
5265 if (!sockflagsize || fdoff > sockflagsize) {
5266 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
5267 else New (1324,sockflags,fdoff+2,unsigned int);
5268 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
5269 sockflagsize = fdoff + 2;
5271 if (fstat(fd,&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
5272 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
5281 * Clear the corresponding bit when the (possibly) socket stream is closed.
5282 * There still a small hole: we miss an implicit close which might occur
5283 * via freopen(). >> Todo
5285 /*{{{ int my_fclose(FILE *fp)*/
5286 int my_fclose(FILE *fp) {
5288 unsigned int fd = fileno(fp);
5289 unsigned int fdoff = fd / sizeof(unsigned int);
5291 if (sockflagsize && fdoff <= sockflagsize)
5292 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
5300 * A simple fwrite replacement which outputs itmsz*nitm chars without
5301 * introducing record boundaries every itmsz chars.
5302 * We are using fputs, which depends on a terminating null. We may
5303 * well be writing binary data, so we need to accommodate not only
5304 * data with nulls sprinkled in the middle but also data with no null
5307 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
5309 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
5311 register char *cp, *end, *cpd, *data;
5312 register unsigned int fd = fileno(dest);
5313 register unsigned int fdoff = fd / sizeof(unsigned int);
5315 int bufsize = itmsz * nitm + 1;
5317 if (fdoff < sockflagsize &&
5318 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
5319 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
5323 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
5324 memcpy( data, src, itmsz*nitm );
5325 data[itmsz*nitm] = '\0';
5327 end = data + itmsz * nitm;
5328 retval = (int) nitm; /* on success return # items written */
5331 while (cpd <= end) {
5332 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
5333 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
5335 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
5339 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
5342 } /* end of my_fwrite() */
5345 /*{{{ int my_flush(FILE *fp)*/
5347 Perl_my_flush(pTHX_ FILE *fp)
5350 if ((res = fflush(fp)) == 0 && fp) {
5351 #ifdef VMS_DO_SOCKETS
5353 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
5355 res = fsync(fileno(fp));
5358 * If the flush succeeded but set end-of-file, we need to clear
5359 * the error because our caller may check ferror(). BTW, this
5360 * probably means we just flushed an empty file.
5362 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
5369 * Here are replacements for the following Unix routines in the VMS environment:
5370 * getpwuid Get information for a particular UIC or UID
5371 * getpwnam Get information for a named user
5372 * getpwent Get information for each user in the rights database
5373 * setpwent Reset search to the start of the rights database
5374 * endpwent Finish searching for users in the rights database
5376 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
5377 * (defined in pwd.h), which contains the following fields:-
5379 * char *pw_name; Username (in lower case)
5380 * char *pw_passwd; Hashed password
5381 * unsigned int pw_uid; UIC
5382 * unsigned int pw_gid; UIC group number
5383 * char *pw_unixdir; Default device/directory (VMS-style)
5384 * char *pw_gecos; Owner name
5385 * char *pw_dir; Default device/directory (Unix-style)
5386 * char *pw_shell; Default CLI name (eg. DCL)
5388 * If the specified user does not exist, getpwuid and getpwnam return NULL.
5390 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
5391 * not the UIC member number (eg. what's returned by getuid()),
5392 * getpwuid() can accept either as input (if uid is specified, the caller's
5393 * UIC group is used), though it won't recognise gid=0.
5395 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
5396 * information about other users in your group or in other groups, respectively.
5397 * If the required privilege is not available, then these routines fill only
5398 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
5401 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
5404 /* sizes of various UAF record fields */
5405 #define UAI$S_USERNAME 12
5406 #define UAI$S_IDENT 31
5407 #define UAI$S_OWNER 31
5408 #define UAI$S_DEFDEV 31
5409 #define UAI$S_DEFDIR 63
5410 #define UAI$S_DEFCLI 31
5413 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
5414 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
5415 (uic).uic$v_group != UIC$K_WILD_GROUP)
5417 static char __empty[]= "";
5418 static struct passwd __passwd_empty=
5419 {(char *) __empty, (char *) __empty, 0, 0,
5420 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
5421 static int contxt= 0;
5422 static struct passwd __pwdcache;
5423 static char __pw_namecache[UAI$S_IDENT+1];
5426 * This routine does most of the work extracting the user information.
5428 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
5431 unsigned char length;
5432 char pw_gecos[UAI$S_OWNER+1];
5434 static union uicdef uic;
5436 unsigned char length;
5437 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
5440 unsigned char length;
5441 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
5444 unsigned char length;
5445 char pw_shell[UAI$S_DEFCLI+1];
5447 static char pw_passwd[UAI$S_PWD+1];
5449 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
5450 struct dsc$descriptor_s name_desc;
5451 unsigned long int sts;
5453 static struct itmlst_3 itmlst[]= {
5454 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
5455 {sizeof(uic), UAI$_UIC, &uic, &luic},
5456 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
5457 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
5458 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
5459 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
5460 {0, 0, NULL, NULL}};
5462 name_desc.dsc$w_length= strlen(name);
5463 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5464 name_desc.dsc$b_class= DSC$K_CLASS_S;
5465 name_desc.dsc$a_pointer= (char *) name;
5467 /* Note that sys$getuai returns many fields as counted strings. */
5468 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
5469 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
5470 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
5472 else { _ckvmssts(sts); }
5473 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
5475 if ((int) owner.length < lowner) lowner= (int) owner.length;
5476 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
5477 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
5478 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
5479 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
5480 owner.pw_gecos[lowner]= '\0';
5481 defdev.pw_dir[ldefdev+ldefdir]= '\0';
5482 defcli.pw_shell[ldefcli]= '\0';
5483 if (valid_uic(uic)) {
5484 pwd->pw_uid= uic.uic$l_uic;
5485 pwd->pw_gid= uic.uic$v_group;
5488 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
5489 pwd->pw_passwd= pw_passwd;
5490 pwd->pw_gecos= owner.pw_gecos;
5491 pwd->pw_dir= defdev.pw_dir;
5492 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
5493 pwd->pw_shell= defcli.pw_shell;
5494 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
5496 ldir= strlen(pwd->pw_unixdir) - 1;
5497 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
5500 strcpy(pwd->pw_unixdir, pwd->pw_dir);
5501 __mystrtolower(pwd->pw_unixdir);
5506 * Get information for a named user.
5508 /*{{{struct passwd *getpwnam(char *name)*/
5509 struct passwd *Perl_my_getpwnam(pTHX_ char *name)
5511 struct dsc$descriptor_s name_desc;
5513 unsigned long int status, sts;
5515 __pwdcache = __passwd_empty;
5516 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
5517 /* We still may be able to determine pw_uid and pw_gid */
5518 name_desc.dsc$w_length= strlen(name);
5519 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5520 name_desc.dsc$b_class= DSC$K_CLASS_S;
5521 name_desc.dsc$a_pointer= (char *) name;
5522 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
5523 __pwdcache.pw_uid= uic.uic$l_uic;
5524 __pwdcache.pw_gid= uic.uic$v_group;
5527 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
5528 set_vaxc_errno(sts);
5529 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
5532 else { _ckvmssts(sts); }
5535 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
5536 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
5537 __pwdcache.pw_name= __pw_namecache;
5539 } /* end of my_getpwnam() */
5543 * Get information for a particular UIC or UID.
5544 * Called by my_getpwent with uid=-1 to list all users.
5546 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
5547 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
5549 const $DESCRIPTOR(name_desc,__pw_namecache);
5550 unsigned short lname;
5552 unsigned long int status;
5554 if (uid == (unsigned int) -1) {
5556 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
5557 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
5558 set_vaxc_errno(status);
5559 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5563 else { _ckvmssts(status); }
5564 } while (!valid_uic (uic));
5568 if (!uic.uic$v_group)
5569 uic.uic$v_group= PerlProc_getgid();
5571 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
5572 else status = SS$_IVIDENT;
5573 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
5574 status == RMS$_PRV) {
5575 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5578 else { _ckvmssts(status); }
5580 __pw_namecache[lname]= '\0';
5581 __mystrtolower(__pw_namecache);
5583 __pwdcache = __passwd_empty;
5584 __pwdcache.pw_name = __pw_namecache;
5586 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
5587 The identifier's value is usually the UIC, but it doesn't have to be,
5588 so if we can, we let fillpasswd update this. */
5589 __pwdcache.pw_uid = uic.uic$l_uic;
5590 __pwdcache.pw_gid = uic.uic$v_group;
5592 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
5595 } /* end of my_getpwuid() */
5599 * Get information for next user.
5601 /*{{{struct passwd *my_getpwent()*/
5602 struct passwd *Perl_my_getpwent(pTHX)
5604 return (my_getpwuid((unsigned int) -1));
5609 * Finish searching rights database for users.
5611 /*{{{void my_endpwent()*/
5612 void Perl_my_endpwent(pTHX)
5615 _ckvmssts(sys$finish_rdb(&contxt));
5621 #ifdef HOMEGROWN_POSIX_SIGNALS
5622 /* Signal handling routines, pulled into the core from POSIX.xs.
5624 * We need these for threads, so they've been rolled into the core,
5625 * rather than left in POSIX.xs.
5627 * (DRS, Oct 23, 1997)
5630 /* sigset_t is atomic under VMS, so these routines are easy */
5631 /*{{{int my_sigemptyset(sigset_t *) */
5632 int my_sigemptyset(sigset_t *set) {
5633 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5639 /*{{{int my_sigfillset(sigset_t *)*/
5640 int my_sigfillset(sigset_t *set) {
5642 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5643 for (i = 0; i < NSIG; i++) *set |= (1 << i);
5649 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
5650 int my_sigaddset(sigset_t *set, int sig) {
5651 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5652 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5653 *set |= (1 << (sig - 1));
5659 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
5660 int my_sigdelset(sigset_t *set, int sig) {
5661 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5662 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5663 *set &= ~(1 << (sig - 1));
5669 /*{{{int my_sigismember(sigset_t *set, int sig)*/
5670 int my_sigismember(sigset_t *set, int sig) {
5671 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5672 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5673 return *set & (1 << (sig - 1));
5678 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
5679 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
5682 /* If set and oset are both null, then things are badly wrong. Bail out. */
5683 if ((oset == NULL) && (set == NULL)) {
5684 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5688 /* If set's null, then we're just handling a fetch. */
5690 tempmask = sigblock(0);
5695 tempmask = sigsetmask(*set);
5698 tempmask = sigblock(*set);
5701 tempmask = sigblock(0);
5702 sigsetmask(*oset & ~tempmask);
5705 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5710 /* Did they pass us an oset? If so, stick our holding mask into it */
5717 #endif /* HOMEGROWN_POSIX_SIGNALS */
5720 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
5721 * my_utime(), and flex_stat(), all of which operate on UTC unless
5722 * VMSISH_TIMES is true.
5724 /* method used to handle UTC conversions:
5725 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
5727 static int gmtime_emulation_type;
5728 /* number of secs to add to UTC POSIX-style time to get local time */
5729 static long int utc_offset_secs;
5731 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
5732 * in vmsish.h. #undef them here so we can call the CRTL routines
5741 * DEC C previous to 6.0 corrupts the behavior of the /prefix
5742 * qualifier with the extern prefix pragma. This provisional
5743 * hack circumvents this prefix pragma problem in previous
5746 #if defined(__VMS_VER) && __VMS_VER >= 70000000
5747 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
5748 # pragma __extern_prefix save
5749 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
5750 # define gmtime decc$__utctz_gmtime
5751 # define localtime decc$__utctz_localtime
5752 # define time decc$__utc_time
5753 # pragma __extern_prefix restore
5755 struct tm *gmtime(), *localtime();
5761 static time_t toutc_dst(time_t loc) {
5764 if ((rsltmp = localtime(&loc)) == NULL) return -1;
5765 loc -= utc_offset_secs;
5766 if (rsltmp->tm_isdst) loc -= 3600;
5769 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5770 ((gmtime_emulation_type || my_time(NULL)), \
5771 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
5772 ((secs) - utc_offset_secs))))
5774 static time_t toloc_dst(time_t utc) {
5777 utc += utc_offset_secs;
5778 if ((rsltmp = localtime(&utc)) == NULL) return -1;
5779 if (rsltmp->tm_isdst) utc += 3600;
5782 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5783 ((gmtime_emulation_type || my_time(NULL)), \
5784 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
5785 ((secs) + utc_offset_secs))))
5787 #ifndef RTL_USES_UTC
5790 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
5791 DST starts on 1st sun of april at 02:00 std time
5792 ends on last sun of october at 02:00 dst time
5793 see the UCX management command reference, SET CONFIG TIMEZONE
5794 for formatting info.
5796 No, it's not as general as it should be, but then again, NOTHING
5797 will handle UK times in a sensible way.
5802 parse the DST start/end info:
5803 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
5807 tz_parse_startend(char *s, struct tm *w, int *past)
5809 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
5810 int ly, dozjd, d, m, n, hour, min, sec, j, k;
5815 if (!past) return 0;
5818 if (w->tm_year % 4 == 0) ly = 1;
5819 if (w->tm_year % 100 == 0) ly = 0;
5820 if (w->tm_year+1900 % 400 == 0) ly = 1;
5823 dozjd = isdigit(*s);
5824 if (*s == 'J' || *s == 'j' || dozjd) {
5825 if (!dozjd && !isdigit(*++s)) return 0;
5828 d = d*10 + *s++ - '0';
5830 d = d*10 + *s++ - '0';
5833 if (d == 0) return 0;
5834 if (d > 366) return 0;
5836 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
5839 } else if (*s == 'M' || *s == 'm') {
5840 if (!isdigit(*++s)) return 0;
5842 if (isdigit(*s)) m = 10*m + *s++ - '0';
5843 if (*s != '.') return 0;
5844 if (!isdigit(*++s)) return 0;
5846 if (n < 1 || n > 5) return 0;
5847 if (*s != '.') return 0;
5848 if (!isdigit(*++s)) return 0;
5850 if (d > 6) return 0;
5854 if (!isdigit(*++s)) return 0;
5856 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
5858 if (!isdigit(*++s)) return 0;
5860 if (isdigit(*s)) min = 10*min + *s++ - '0';
5862 if (!isdigit(*++s)) return 0;
5864 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
5874 if (w->tm_yday < d) goto before;
5875 if (w->tm_yday > d) goto after;
5877 if (w->tm_mon+1 < m) goto before;
5878 if (w->tm_mon+1 > m) goto after;
5880 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
5881 k = d - j; /* mday of first d */
5883 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
5884 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
5885 if (w->tm_mday < k) goto before;
5886 if (w->tm_mday > k) goto after;
5889 if (w->tm_hour < hour) goto before;
5890 if (w->tm_hour > hour) goto after;
5891 if (w->tm_min < min) goto before;
5892 if (w->tm_min > min) goto after;
5893 if (w->tm_sec < sec) goto before;
5907 /* parse the offset: (+|-)hh[:mm[:ss]] */
5910 tz_parse_offset(char *s, int *offset)
5912 int hour = 0, min = 0, sec = 0;
5915 if (!offset) return 0;
5917 if (*s == '-') {neg++; s++;}
5919 if (!isdigit(*s)) return 0;
5921 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
5922 if (hour > 24) return 0;
5924 if (!isdigit(*++s)) return 0;
5926 if (isdigit(*s)) min = min*10 + (*s++ - '0');
5927 if (min > 59) return 0;
5929 if (!isdigit(*++s)) return 0;
5931 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
5932 if (sec > 59) return 0;
5936 *offset = (hour*60+min)*60 + sec;
5937 if (neg) *offset = -*offset;
5942 input time is w, whatever type of time the CRTL localtime() uses.
5943 sets dst, the zone, and the gmtoff (seconds)
5945 caches the value of TZ and UCX$TZ env variables; note that
5946 my_setenv looks for these and sets a flag if they're changed
5949 We have to watch out for the "australian" case (dst starts in
5950 october, ends in april)...flagged by "reverse" and checked by
5951 scanning through the months of the previous year.
5956 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
5961 char *dstzone, *tz, *s_start, *s_end;
5962 int std_off, dst_off, isdst;
5963 int y, dststart, dstend;
5964 static char envtz[1025]; /* longer than any logical, symbol, ... */
5965 static char ucxtz[1025];
5966 static char reversed = 0;
5972 reversed = -1; /* flag need to check */
5973 envtz[0] = ucxtz[0] = '\0';
5974 tz = my_getenv("TZ",0);
5975 if (tz) strcpy(envtz, tz);
5976 tz = my_getenv("UCX$TZ",0);
5977 if (tz) strcpy(ucxtz, tz);
5978 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
5981 if (!*tz) tz = ucxtz;
5984 while (isalpha(*s)) s++;
5985 s = tz_parse_offset(s, &std_off);
5987 if (!*s) { /* no DST, hurray we're done! */
5993 while (isalpha(*s)) s++;
5994 s2 = tz_parse_offset(s, &dst_off);
5998 dst_off = std_off - 3600;
6001 if (!*s) { /* default dst start/end?? */
6002 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
6003 s = strchr(ucxtz,',');
6005 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
6007 if (*s != ',') return 0;
6010 when = _toutc(when); /* convert to utc */
6011 when = when - std_off; /* convert to pseudolocal time*/
6013 w2 = localtime(&when);
6016 s = tz_parse_startend(s_start,w2,&dststart);
6018 if (*s != ',') return 0;
6021 when = _toutc(when); /* convert to utc */
6022 when = when - dst_off; /* convert to pseudolocal time*/
6023 w2 = localtime(&when);
6024 if (w2->tm_year != y) { /* spans a year, just check one time */
6025 when += dst_off - std_off;
6026 w2 = localtime(&when);
6029 s = tz_parse_startend(s_end,w2,&dstend);
6032 if (reversed == -1) { /* need to check if start later than end */
6036 if (when < 2*365*86400) {
6037 when += 2*365*86400;
6041 w2 =localtime(&when);
6042 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
6044 for (j = 0; j < 12; j++) {
6045 w2 =localtime(&when);
6046 (void) tz_parse_startend(s_start,w2,&ds);
6047 (void) tz_parse_startend(s_end,w2,&de);
6048 if (ds != de) break;
6052 if (de && !ds) reversed = 1;
6055 isdst = dststart && !dstend;
6056 if (reversed) isdst = dststart || !dstend;
6059 if (dst) *dst = isdst;
6060 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
6061 if (isdst) tz = dstzone;
6063 while(isalpha(*tz)) *zone++ = *tz++;
6069 #endif /* !RTL_USES_UTC */
6071 /* my_time(), my_localtime(), my_gmtime()
6072 * By default traffic in UTC time values, using CRTL gmtime() or
6073 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
6074 * Note: We need to use these functions even when the CRTL has working
6075 * UTC support, since they also handle C<use vmsish qw(times);>
6077 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
6078 * Modified by Charles Bailey <bailey@newman.upenn.edu>
6081 /*{{{time_t my_time(time_t *timep)*/
6082 time_t Perl_my_time(pTHX_ time_t *timep)
6087 if (gmtime_emulation_type == 0) {
6089 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
6090 /* results of calls to gmtime() and localtime() */
6091 /* for same &base */
6093 gmtime_emulation_type++;
6094 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
6095 char off[LNM$C_NAMLENGTH+1];;
6097 gmtime_emulation_type++;
6098 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
6099 gmtime_emulation_type++;
6100 utc_offset_secs = 0;
6101 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
6103 else { utc_offset_secs = atol(off); }
6105 else { /* We've got a working gmtime() */
6106 struct tm gmt, local;
6109 tm_p = localtime(&base);
6111 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
6112 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
6113 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
6114 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
6120 # ifdef RTL_USES_UTC
6121 if (VMSISH_TIME) when = _toloc(when);
6123 if (!VMSISH_TIME) when = _toutc(when);
6126 if (timep != NULL) *timep = when;
6129 } /* end of my_time() */
6133 /*{{{struct tm *my_gmtime(const time_t *timep)*/
6135 Perl_my_gmtime(pTHX_ const time_t *timep)
6141 if (timep == NULL) {
6142 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6145 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
6149 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
6151 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
6152 return gmtime(&when);
6154 /* CRTL localtime() wants local time as input, so does no tz correction */
6155 rsltmp = localtime(&when);
6156 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
6159 } /* end of my_gmtime() */
6163 /*{{{struct tm *my_localtime(const time_t *timep)*/
6165 Perl_my_localtime(pTHX_ const time_t *timep)
6167 time_t when, whenutc;
6171 if (timep == NULL) {
6172 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6175 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
6176 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
6179 # ifdef RTL_USES_UTC
6181 if (VMSISH_TIME) when = _toutc(when);
6183 /* CRTL localtime() wants UTC as input, does tz correction itself */
6184 return localtime(&when);
6186 # else /* !RTL_USES_UTC */
6189 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
6190 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
6193 #ifndef RTL_USES_UTC
6194 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
6195 when = whenutc - offset; /* pseudolocal time*/
6198 /* CRTL localtime() wants local time as input, so does no tz correction */
6199 rsltmp = localtime(&when);
6200 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
6204 } /* end of my_localtime() */
6207 /* Reset definitions for later calls */
6208 #define gmtime(t) my_gmtime(t)
6209 #define localtime(t) my_localtime(t)
6210 #define time(t) my_time(t)
6213 /* my_utime - update modification time of a file
6214 * calling sequence is identical to POSIX utime(), but under
6215 * VMS only the modification time is changed; ODS-2 does not
6216 * maintain access times. Restrictions differ from the POSIX
6217 * definition in that the time can be changed as long as the
6218 * caller has permission to execute the necessary IO$_MODIFY $QIO;
6219 * no separate checks are made to insure that the caller is the
6220 * owner of the file or has special privs enabled.
6221 * Code here is based on Joe Meadows' FILE utility.
6224 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
6225 * to VMS epoch (01-JAN-1858 00:00:00.00)
6226 * in 100 ns intervals.
6228 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
6230 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
6231 int Perl_my_utime(pTHX_ char *file, struct utimbuf *utimes)
6234 long int bintime[2], len = 2, lowbit, unixtime,
6235 secscale = 10000000; /* seconds --> 100 ns intervals */
6236 unsigned long int chan, iosb[2], retsts;
6237 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
6238 struct FAB myfab = cc$rms_fab;
6239 struct NAM mynam = cc$rms_nam;
6240 #if defined (__DECC) && defined (__VAX)
6241 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
6242 * at least through VMS V6.1, which causes a type-conversion warning.
6244 # pragma message save
6245 # pragma message disable cvtdiftypes
6247 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
6248 struct fibdef myfib;
6249 #if defined (__DECC) && defined (__VAX)
6250 /* This should be right after the declaration of myatr, but due
6251 * to a bug in VAX DEC C, this takes effect a statement early.
6253 # pragma message restore
6255 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
6256 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
6257 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
6259 if (file == NULL || *file == '\0') {
6261 set_vaxc_errno(LIB$_INVARG);
6264 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
6266 if (utimes != NULL) {
6267 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
6268 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
6269 * Since time_t is unsigned long int, and lib$emul takes a signed long int
6270 * as input, we force the sign bit to be clear by shifting unixtime right
6271 * one bit, then multiplying by an extra factor of 2 in lib$emul().
6273 lowbit = (utimes->modtime & 1) ? secscale : 0;
6274 unixtime = (long int) utimes->modtime;
6276 /* If input was UTC; convert to local for sys svc */
6277 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
6279 unixtime >>= 1; secscale <<= 1;
6280 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
6281 if (!(retsts & 1)) {
6283 set_vaxc_errno(retsts);
6286 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
6287 if (!(retsts & 1)) {
6289 set_vaxc_errno(retsts);
6294 /* Just get the current time in VMS format directly */
6295 retsts = sys$gettim(bintime);
6296 if (!(retsts & 1)) {
6298 set_vaxc_errno(retsts);
6303 myfab.fab$l_fna = vmsspec;
6304 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
6305 myfab.fab$l_nam = &mynam;
6306 mynam.nam$l_esa = esa;
6307 mynam.nam$b_ess = (unsigned char) sizeof esa;
6308 mynam.nam$l_rsa = rsa;
6309 mynam.nam$b_rss = (unsigned char) sizeof rsa;
6311 /* Look for the file to be affected, letting RMS parse the file
6312 * specification for us as well. I have set errno using only
6313 * values documented in the utime() man page for VMS POSIX.
6315 retsts = sys$parse(&myfab,0,0);
6316 if (!(retsts & 1)) {
6317 set_vaxc_errno(retsts);
6318 if (retsts == RMS$_PRV) set_errno(EACCES);
6319 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
6320 else set_errno(EVMSERR);
6323 retsts = sys$search(&myfab,0,0);
6324 if (!(retsts & 1)) {
6325 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6326 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6327 set_vaxc_errno(retsts);
6328 if (retsts == RMS$_PRV) set_errno(EACCES);
6329 else if (retsts == RMS$_FNF) set_errno(ENOENT);
6330 else set_errno(EVMSERR);
6334 devdsc.dsc$w_length = mynam.nam$b_dev;
6335 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
6337 retsts = sys$assign(&devdsc,&chan,0,0);
6338 if (!(retsts & 1)) {
6339 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6340 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6341 set_vaxc_errno(retsts);
6342 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
6343 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
6344 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
6345 else set_errno(EVMSERR);
6349 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
6350 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
6352 memset((void *) &myfib, 0, sizeof myfib);
6353 #if defined(__DECC) || defined(__DECCXX)
6354 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
6355 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
6356 /* This prevents the revision time of the file being reset to the current
6357 * time as a result of our IO$_MODIFY $QIO. */
6358 myfib.fib$l_acctl = FIB$M_NORECORD;
6360 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
6361 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
6362 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
6364 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
6365 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6366 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6367 _ckvmssts(sys$dassgn(chan));
6368 if (retsts & 1) retsts = iosb[0];
6369 if (!(retsts & 1)) {
6370 set_vaxc_errno(retsts);
6371 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6372 else set_errno(EVMSERR);
6377 } /* end of my_utime() */
6381 * flex_stat, flex_fstat
6382 * basic stat, but gets it right when asked to stat
6383 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
6386 /* encode_dev packs a VMS device name string into an integer to allow
6387 * simple comparisons. This can be used, for example, to check whether two
6388 * files are located on the same device, by comparing their encoded device
6389 * names. Even a string comparison would not do, because stat() reuses the
6390 * device name buffer for each call; so without encode_dev, it would be
6391 * necessary to save the buffer and use strcmp (this would mean a number of
6392 * changes to the standard Perl code, to say nothing of what a Perl script
6395 * The device lock id, if it exists, should be unique (unless perhaps compared
6396 * with lock ids transferred from other nodes). We have a lock id if the disk is
6397 * mounted cluster-wide, which is when we tend to get long (host-qualified)
6398 * device names. Thus we use the lock id in preference, and only if that isn't
6399 * available, do we try to pack the device name into an integer (flagged by
6400 * the sign bit (LOCKID_MASK) being set).
6402 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
6403 * name and its encoded form, but it seems very unlikely that we will find
6404 * two files on different disks that share the same encoded device names,
6405 * and even more remote that they will share the same file id (if the test
6406 * is to check for the same file).
6408 * A better method might be to use sys$device_scan on the first call, and to
6409 * search for the device, returning an index into the cached array.
6410 * The number returned would be more intelligable.
6411 * This is probably not worth it, and anyway would take quite a bit longer
6412 * on the first call.
6414 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
6415 static mydev_t encode_dev (pTHX_ const char *dev)
6418 unsigned long int f;
6423 if (!dev || !dev[0]) return 0;
6427 struct dsc$descriptor_s dev_desc;
6428 unsigned long int status, lockid, item = DVI$_LOCKID;
6430 /* For cluster-mounted disks, the disk lock identifier is unique, so we
6431 can try that first. */
6432 dev_desc.dsc$w_length = strlen (dev);
6433 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
6434 dev_desc.dsc$b_class = DSC$K_CLASS_S;
6435 dev_desc.dsc$a_pointer = (char *) dev;
6436 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
6437 if (lockid) return (lockid & ~LOCKID_MASK);
6441 /* Otherwise we try to encode the device name */
6445 for (q = dev + strlen(dev); q--; q >= dev) {
6448 else if (isalpha (toupper (*q)))
6449 c= toupper (*q) - 'A' + (char)10;
6451 continue; /* Skip '$'s */
6453 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
6455 enc += f * (unsigned long int) c;
6457 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
6459 } /* end of encode_dev() */
6461 static char namecache[NAM$C_MAXRSS+1];
6464 is_null_device(name)
6467 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
6468 The underscore prefix, controller letter, and unit number are
6469 independently optional; for our purposes, the colon punctuation
6470 is not. The colon can be trailed by optional directory and/or
6471 filename, but two consecutive colons indicates a nodename rather
6472 than a device. [pr] */
6473 if (*name == '_') ++name;
6474 if (tolower(*name++) != 'n') return 0;
6475 if (tolower(*name++) != 'l') return 0;
6476 if (tolower(*name) == 'a') ++name;
6477 if (*name == '0') ++name;
6478 return (*name++ == ':') && (*name != ':');
6481 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
6482 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
6483 * subset of the applicable information.
6486 Perl_cando(pTHX_ Mode_t bit, Uid_t effective, Stat_t *statbufp)
6488 char fname_phdev[NAM$C_MAXRSS+1];
6489 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
6491 char fname[NAM$C_MAXRSS+1];
6492 unsigned long int retsts;
6493 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6494 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6496 /* If the struct mystat is stale, we're OOL; stat() overwrites the
6497 device name on successive calls */
6498 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
6499 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
6500 namdsc.dsc$a_pointer = fname;
6501 namdsc.dsc$w_length = sizeof fname - 1;
6503 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
6504 &namdsc,&namdsc.dsc$w_length,0,0);
6506 fname[namdsc.dsc$w_length] = '\0';
6508 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
6509 * but if someone has redefined that logical, Perl gets very lost. Since
6510 * we have the physical device name from the stat buffer, just paste it on.
6512 strcpy( fname_phdev, statbufp->st_devnam );
6513 strcat( fname_phdev, strrchr(fname, ':') );
6515 return cando_by_name(bit,effective,fname_phdev);
6517 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
6518 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
6522 return FALSE; /* Should never get to here */
6524 } /* end of cando() */
6528 /*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
6530 Perl_cando_by_name(pTHX_ I32 bit, Uid_t effective, char *fname)
6532 static char usrname[L_cuserid];
6533 static struct dsc$descriptor_s usrdsc =
6534 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
6535 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
6536 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
6537 unsigned short int retlen;
6538 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6539 union prvdef curprv;
6540 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
6541 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
6542 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
6545 if (!fname || !*fname) return FALSE;
6546 /* Make sure we expand logical names, since sys$check_access doesn't */
6547 if (!strpbrk(fname,"/]>:")) {
6548 strcpy(fileified,fname);
6549 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
6552 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
6553 retlen = namdsc.dsc$w_length = strlen(vmsname);
6554 namdsc.dsc$a_pointer = vmsname;
6555 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
6556 vmsname[retlen-1] == ':') {
6557 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
6558 namdsc.dsc$w_length = strlen(fileified);
6559 namdsc.dsc$a_pointer = fileified;
6562 if (!usrdsc.dsc$w_length) {
6564 usrdsc.dsc$w_length = strlen(usrname);
6568 case S_IXUSR: case S_IXGRP: case S_IXOTH:
6569 access = ARM$M_EXECUTE; break;
6570 case S_IRUSR: case S_IRGRP: case S_IROTH:
6571 access = ARM$M_READ; break;
6572 case S_IWUSR: case S_IWGRP: case S_IWOTH:
6573 access = ARM$M_WRITE; break;
6574 case S_IDUSR: case S_IDGRP: case S_IDOTH:
6575 access = ARM$M_DELETE; break;
6580 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
6581 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
6582 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
6583 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
6584 set_vaxc_errno(retsts);
6585 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6586 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
6587 else set_errno(ENOENT);
6590 if (retsts == SS$_NORMAL) {
6591 if (!privused) return TRUE;
6592 /* We can get access, but only by using privs. Do we have the
6593 necessary privs currently enabled? */
6594 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
6595 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
6596 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
6597 !curprv.prv$v_bypass) return FALSE;
6598 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
6599 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
6600 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
6603 if (retsts == SS$_ACCONFLICT) {
6608 return FALSE; /* Should never get here */
6610 } /* end of cando_by_name() */
6614 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
6616 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
6618 if (!fstat(fd,(stat_t *) statbufp)) {
6619 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
6620 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
6621 # ifdef RTL_USES_UTC
6624 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6625 statbufp->st_atime = _toloc(statbufp->st_atime);
6626 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6631 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6635 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6636 statbufp->st_atime = _toutc(statbufp->st_atime);
6637 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6644 } /* end of flex_fstat() */
6647 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
6649 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
6651 char fileified[NAM$C_MAXRSS+1];
6652 char temp_fspec[NAM$C_MAXRSS+300];
6655 if (!fspec) return retval;
6656 strcpy(temp_fspec, fspec);
6657 if (statbufp == (Stat_t *) &PL_statcache)
6658 do_tovmsspec(temp_fspec,namecache,0);
6659 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
6660 memset(statbufp,0,sizeof *statbufp);
6661 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
6662 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
6663 statbufp->st_uid = 0x00010001;
6664 statbufp->st_gid = 0x0001;
6665 time((time_t *)&statbufp->st_mtime);
6666 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
6670 /* Try for a directory name first. If fspec contains a filename without
6671 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
6672 * and sea:[wine.dark]water. exist, we prefer the directory here.
6673 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
6674 * not sea:[wine.dark]., if the latter exists. If the intended target is
6675 * the file with null type, specify this by calling flex_stat() with
6676 * a '.' at the end of fspec.
6678 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
6679 retval = stat(fileified,(stat_t *) statbufp);
6680 if (!retval && statbufp == (Stat_t *) &PL_statcache)
6681 strcpy(namecache,fileified);
6683 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
6685 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
6686 # ifdef RTL_USES_UTC
6689 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6690 statbufp->st_atime = _toloc(statbufp->st_atime);
6691 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6696 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6700 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6701 statbufp->st_atime = _toutc(statbufp->st_atime);
6702 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6708 } /* end of flex_stat() */
6712 /*{{{char *my_getlogin()*/
6713 /* VMS cuserid == Unix getlogin, except calling sequence */
6717 static char user[L_cuserid];
6718 return cuserid(user);
6723 /* rmscopy - copy a file using VMS RMS routines
6725 * Copies contents and attributes of spec_in to spec_out, except owner
6726 * and protection information. Name and type of spec_in are used as
6727 * defaults for spec_out. The third parameter specifies whether rmscopy()
6728 * should try to propagate timestamps from the input file to the output file.
6729 * If it is less than 0, no timestamps are preserved. If it is 0, then
6730 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
6731 * propagated to the output file at creation iff the output file specification
6732 * did not contain an explicit name or type, and the revision date is always
6733 * updated at the end of the copy operation. If it is greater than 0, then
6734 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
6735 * other than the revision date should be propagated, and bit 1 indicates
6736 * that the revision date should be propagated.
6738 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
6740 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
6741 * Incorporates, with permission, some code from EZCOPY by Tim Adye
6742 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
6743 * as part of the Perl standard distribution under the terms of the
6744 * GNU General Public License or the Perl Artistic License. Copies
6745 * of each may be found in the Perl standard distribution.
6747 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
6749 Perl_rmscopy(pTHX_ char *spec_in, char *spec_out, int preserve_dates)
6751 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
6752 rsa[NAM$C_MAXRSS], ubf[32256];
6753 unsigned long int i, sts, sts2;
6754 struct FAB fab_in, fab_out;
6755 struct RAB rab_in, rab_out;
6757 struct XABDAT xabdat;
6758 struct XABFHC xabfhc;
6759 struct XABRDT xabrdt;
6760 struct XABSUM xabsum;
6762 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
6763 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
6764 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6768 fab_in = cc$rms_fab;
6769 fab_in.fab$l_fna = vmsin;
6770 fab_in.fab$b_fns = strlen(vmsin);
6771 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
6772 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
6773 fab_in.fab$l_fop = FAB$M_SQO;
6774 fab_in.fab$l_nam = &nam;
6775 fab_in.fab$l_xab = (void *) &xabdat;
6778 nam.nam$l_rsa = rsa;
6779 nam.nam$b_rss = sizeof(rsa);
6780 nam.nam$l_esa = esa;
6781 nam.nam$b_ess = sizeof (esa);
6782 nam.nam$b_esl = nam.nam$b_rsl = 0;
6784 xabdat = cc$rms_xabdat; /* To get creation date */
6785 xabdat.xab$l_nxt = (void *) &xabfhc;
6787 xabfhc = cc$rms_xabfhc; /* To get record length */
6788 xabfhc.xab$l_nxt = (void *) &xabsum;
6790 xabsum = cc$rms_xabsum; /* To get key and area information */
6792 if (!((sts = sys$open(&fab_in)) & 1)) {
6793 set_vaxc_errno(sts);
6795 case RMS$_FNF: case RMS$_DNF:
6796 set_errno(ENOENT); break;
6798 set_errno(ENOTDIR); break;
6800 set_errno(ENODEV); break;
6802 set_errno(EINVAL); break;
6804 set_errno(EACCES); break;
6812 fab_out.fab$w_ifi = 0;
6813 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
6814 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
6815 fab_out.fab$l_fop = FAB$M_SQO;
6816 fab_out.fab$l_fna = vmsout;
6817 fab_out.fab$b_fns = strlen(vmsout);
6818 fab_out.fab$l_dna = nam.nam$l_name;
6819 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
6821 if (preserve_dates == 0) { /* Act like DCL COPY */
6822 nam.nam$b_nop = NAM$M_SYNCHK;
6823 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
6824 if (!((sts = sys$parse(&fab_out)) & 1)) {
6825 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
6826 set_vaxc_errno(sts);
6829 fab_out.fab$l_xab = (void *) &xabdat;
6830 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
6832 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
6833 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
6834 preserve_dates =0; /* bitmask from this point forward */
6836 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
6837 if (!((sts = sys$create(&fab_out)) & 1)) {
6838 set_vaxc_errno(sts);
6841 set_errno(ENOENT); break;
6843 set_errno(ENOTDIR); break;
6845 set_errno(ENODEV); break;
6847 set_errno(EINVAL); break;
6849 set_errno(EACCES); break;
6855 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
6856 if (preserve_dates & 2) {
6857 /* sys$close() will process xabrdt, not xabdat */
6858 xabrdt = cc$rms_xabrdt;
6860 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
6862 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
6863 * is unsigned long[2], while DECC & VAXC use a struct */
6864 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
6866 fab_out.fab$l_xab = (void *) &xabrdt;
6869 rab_in = cc$rms_rab;
6870 rab_in.rab$l_fab = &fab_in;
6871 rab_in.rab$l_rop = RAB$M_BIO;
6872 rab_in.rab$l_ubf = ubf;
6873 rab_in.rab$w_usz = sizeof ubf;
6874 if (!((sts = sys$connect(&rab_in)) & 1)) {
6875 sys$close(&fab_in); sys$close(&fab_out);
6876 set_errno(EVMSERR); set_vaxc_errno(sts);
6880 rab_out = cc$rms_rab;
6881 rab_out.rab$l_fab = &fab_out;
6882 rab_out.rab$l_rbf = ubf;
6883 if (!((sts = sys$connect(&rab_out)) & 1)) {
6884 sys$close(&fab_in); sys$close(&fab_out);
6885 set_errno(EVMSERR); set_vaxc_errno(sts);
6889 while ((sts = sys$read(&rab_in))) { /* always true */
6890 if (sts == RMS$_EOF) break;
6891 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
6892 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
6893 sys$close(&fab_in); sys$close(&fab_out);
6894 set_errno(EVMSERR); set_vaxc_errno(sts);
6899 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
6900 sys$close(&fab_in); sys$close(&fab_out);
6901 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
6903 set_errno(EVMSERR); set_vaxc_errno(sts);
6909 } /* end of rmscopy() */
6913 /*** The following glue provides 'hooks' to make some of the routines
6914 * from this file available from Perl. These routines are sufficiently
6915 * basic, and are required sufficiently early in the build process,
6916 * that's it's nice to have them available to miniperl as well as the
6917 * full Perl, so they're set up here instead of in an extension. The
6918 * Perl code which handles importation of these names into a given
6919 * package lives in [.VMS]Filespec.pm in @INC.
6923 rmsexpand_fromperl(pTHX_ CV *cv)
6926 char *fspec, *defspec = NULL, *rslt;
6929 if (!items || items > 2)
6930 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
6931 fspec = SvPV(ST(0),n_a);
6932 if (!fspec || !*fspec) XSRETURN_UNDEF;
6933 if (items == 2) defspec = SvPV(ST(1),n_a);
6935 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
6936 ST(0) = sv_newmortal();
6937 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
6942 vmsify_fromperl(pTHX_ CV *cv)
6948 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
6949 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
6950 ST(0) = sv_newmortal();
6951 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
6956 unixify_fromperl(pTHX_ CV *cv)
6962 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
6963 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
6964 ST(0) = sv_newmortal();
6965 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
6970 fileify_fromperl(pTHX_ CV *cv)
6976 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
6977 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
6978 ST(0) = sv_newmortal();
6979 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
6984 pathify_fromperl(pTHX_ CV *cv)
6990 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
6991 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
6992 ST(0) = sv_newmortal();
6993 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
6998 vmspath_fromperl(pTHX_ CV *cv)
7004 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
7005 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
7006 ST(0) = sv_newmortal();
7007 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
7012 unixpath_fromperl(pTHX_ CV *cv)
7018 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
7019 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
7020 ST(0) = sv_newmortal();
7021 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
7026 candelete_fromperl(pTHX_ CV *cv)
7029 char fspec[NAM$C_MAXRSS+1], *fsp;
7034 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
7036 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
7037 if (SvTYPE(mysv) == SVt_PVGV) {
7038 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
7039 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7046 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
7047 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7053 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
7058 rmscopy_fromperl(pTHX_ CV *cv)
7061 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
7063 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
7064 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7065 unsigned long int sts;
7070 if (items < 2 || items > 3)
7071 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
7073 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
7074 if (SvTYPE(mysv) == SVt_PVGV) {
7075 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
7076 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7083 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
7084 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7089 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
7090 if (SvTYPE(mysv) == SVt_PVGV) {
7091 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
7092 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7099 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
7100 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7105 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
7107 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
7113 mod2fname(pTHX_ CV *cv)
7116 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
7117 workbuff[NAM$C_MAXRSS*1 + 1];
7118 int total_namelen = 3, counter, num_entries;
7119 /* ODS-5 ups this, but we want to be consistent, so... */
7120 int max_name_len = 39;
7121 AV *in_array = (AV *)SvRV(ST(0));
7123 num_entries = av_len(in_array);
7125 /* All the names start with PL_. */
7126 strcpy(ultimate_name, "PL_");
7128 /* Clean up our working buffer */
7129 Zero(work_name, sizeof(work_name), char);
7131 /* Run through the entries and build up a working name */
7132 for(counter = 0; counter <= num_entries; counter++) {
7133 /* If it's not the first name then tack on a __ */
7135 strcat(work_name, "__");
7137 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
7141 /* Check to see if we actually have to bother...*/
7142 if (strlen(work_name) + 3 <= max_name_len) {
7143 strcat(ultimate_name, work_name);
7145 /* It's too darned big, so we need to go strip. We use the same */
7146 /* algorithm as xsubpp does. First, strip out doubled __ */
7147 char *source, *dest, last;
7150 for (source = work_name; *source; source++) {
7151 if (last == *source && last == '_') {
7157 /* Go put it back */
7158 strcpy(work_name, workbuff);
7159 /* Is it still too big? */
7160 if (strlen(work_name) + 3 > max_name_len) {
7161 /* Strip duplicate letters */
7164 for (source = work_name; *source; source++) {
7165 if (last == toupper(*source)) {
7169 last = toupper(*source);
7171 strcpy(work_name, workbuff);
7174 /* Is it *still* too big? */
7175 if (strlen(work_name) + 3 > max_name_len) {
7176 /* Too bad, we truncate */
7177 work_name[max_name_len - 2] = 0;
7179 strcat(ultimate_name, work_name);
7182 /* Okay, return it */
7183 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
7188 hushexit_fromperl(pTHX_ CV *cv)
7193 VMSISH_HUSHED = SvTRUE(ST(0));
7195 ST(0) = boolSV(VMSISH_HUSHED);
7200 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
7201 struct interp_intern *dst)
7203 memcpy(dst,src,sizeof(struct interp_intern));
7207 Perl_sys_intern_clear(pTHX)
7212 Perl_sys_intern_init(pTHX)
7220 MY_INV_RAND_MAX = 1./x;
7222 VMSCMD.dsc$a_pointer = NULL;
7223 VMSCMD.dsc$w_length = 0;
7224 VMSCMD.dsc$b_dtype = DSC$K_DTYPE_T;
7225 VMSCMD.dsc$b_class = DSC$K_CLASS_S;
7232 char* file = __FILE__;
7233 char temp_buff[512];
7234 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
7235 no_translate_barewords = TRUE;
7237 no_translate_barewords = FALSE;
7240 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
7241 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
7242 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
7243 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
7244 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
7245 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
7246 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
7247 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
7248 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
7249 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
7250 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
7252 store_pipelocs(aTHX); /* will redo any earlier attempts */