3 * VMS-specific routines for perl5
6 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
7 * and Perl_cando by Craig Berry
8 * 29-Aug-2000 Charles Lane's piping improvements rolled in
9 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
18 #include <climsgdef.h>
28 #include <libclidef.h>
30 #include <lib$routines.h>
39 #include <str$routines.h>
44 /* Older versions of ssdef.h don't have these */
45 #ifndef SS$_INVFILFOROP
46 # define SS$_INVFILFOROP 3930
48 #ifndef SS$_NOSUCHOBJECT
49 # define SS$_NOSUCHOBJECT 2696
52 /* Don't replace system definitions of vfork, getenv, and stat,
53 * code below needs to get to the underlying CRTL routines. */
54 #define DONT_MASK_RTL_CALLS
58 /* Anticipating future expansion in lexical warnings . . . */
60 # define WARN_INTERNAL WARN_MISC
63 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
64 # define RTL_USES_UTC 1
68 /* gcc's header files don't #define direct access macros
69 * corresponding to VAXC's variant structs */
71 # define uic$v_format uic$r_uic_form.uic$v_format
72 # define uic$v_group uic$r_uic_form.uic$v_group
73 # define uic$v_member uic$r_uic_form.uic$v_member
74 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
75 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
76 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
77 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
80 #if defined(NEED_AN_H_ERRNO)
85 unsigned short int buflen;
86 unsigned short int itmcode;
88 unsigned short int *retlen;
91 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
92 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
93 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
94 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
95 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
96 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
97 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
98 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
99 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
101 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
102 #define PERL_LNM_MAX_ALLOWED_INDEX 127
104 static char *__mystrtolower(char *str)
106 if (str) for (; *str; ++str) *str= tolower(*str);
110 static struct dsc$descriptor_s fildevdsc =
111 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
112 static struct dsc$descriptor_s crtlenvdsc =
113 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
114 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
115 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
116 static struct dsc$descriptor_s **env_tables = defenv;
117 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
119 /* True if we shouldn't treat barewords as logicals during directory */
121 static int no_translate_barewords;
123 /* Temp for subprocess commands */
124 static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
127 static int tz_updated = 1;
130 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
132 Perl_vmstrnenv(pTHX_ const char *lnm, char *eqv, unsigned long int idx,
133 struct dsc$descriptor_s **tabvec, unsigned long int flags)
135 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
136 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
137 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
138 unsigned char acmode;
139 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
140 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
141 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
142 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
144 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
145 #if defined(USE_THREADS)
146 /* We jump through these hoops because we can be called at */
147 /* platform-specific initialization time, which is before anything is */
148 /* set up--we can't even do a plain dTHX since that relies on the */
149 /* interpreter structure to be initialized */
150 struct perl_thread *thr;
152 thr = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
158 if (!lnm || !eqv || idx > PERL_LNM_MAX_ALLOWED_INDEX) {
159 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
161 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
162 *cp2 = _toupper(*cp1);
163 if (cp1 - lnm > LNM$C_NAMLENGTH) {
164 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
168 lnmdsc.dsc$w_length = cp1 - lnm;
169 lnmdsc.dsc$a_pointer = uplnm;
170 uplnm[lnmdsc.dsc$w_length] = '\0';
171 secure = flags & PERL__TRNENV_SECURE;
172 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
173 if (!tabvec || !*tabvec) tabvec = env_tables;
175 for (curtab = 0; tabvec[curtab]; curtab++) {
176 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
177 if (!ivenv && !secure) {
182 Perl_warn(aTHX_ "Can't read CRTL environ\n");
185 retsts = SS$_NOLOGNAM;
186 for (i = 0; environ[i]; i++) {
187 if ((eq = strchr(environ[i],'=')) &&
188 !strncmp(environ[i],uplnm,eq - environ[i])) {
190 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
191 if (!eqvlen) continue;
196 if (retsts != SS$_NOLOGNAM) break;
199 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
200 !str$case_blind_compare(&tmpdsc,&clisym)) {
201 if (!ivsym && !secure) {
202 unsigned short int deflen = LNM$C_NAMLENGTH;
203 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
204 /* dynamic dsc to accomodate possible long value */
205 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
206 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
209 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
211 /* Special hack--we might be called before the interpreter's */
212 /* fully initialized, in which case either thr or PL_curcop */
213 /* might be bogus. We have to check, since ckWARN needs them */
214 /* both to be valid if running threaded */
215 #if defined(USE_THREADS)
216 if (thr && PL_curcop) {
218 if (ckWARN(WARN_MISC)) {
219 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
221 #if defined(USE_THREADS)
223 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
228 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
230 _ckvmssts(lib$sfree1_dd(&eqvdsc));
231 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
232 if (retsts == LIB$_NOSUCHSYM) continue;
237 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
238 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
239 if (retsts == SS$_NOLOGNAM) continue;
240 /* PPFs have a prefix */
243 *((int *)uplnm) == *((int *)"SYS$") &&
245 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
246 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
247 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
248 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
249 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
250 memcpy(eqv,eqv+4,eqvlen-4);
256 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
257 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
258 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
259 retsts == SS$_NOLOGNAM) {
260 set_errno(EINVAL); set_vaxc_errno(retsts);
262 else _ckvmssts(retsts);
264 } /* end of vmstrnenv */
267 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
268 /* Define as a function so we can access statics. */
269 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
271 return vmstrnenv(lnm,eqv,idx,fildev,
272 #ifdef SECURE_INTERNAL_GETENV
273 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
282 * Note: Uses Perl temp to store result so char * can be returned to
283 * caller; this pointer will be invalidated at next Perl statement
285 * We define this as a function rather than a macro in terms of my_getenv_len()
286 * so that it'll work when PL_curinterp is undefined (and we therefore can't
289 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
291 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
293 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
294 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
295 unsigned long int idx = 0;
299 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
300 /* Set up a temporary buffer for the return value; Perl will
301 * clean it up at the next statement transition */
302 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
303 if (!tmpsv) return NULL;
306 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
307 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
308 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
309 getcwd(eqv,LNM$C_NAMLENGTH);
313 if ((cp2 = strchr(lnm,';')) != NULL) {
315 uplnm[cp2-lnm] = '\0';
316 idx = strtoul(cp2+1,NULL,0);
319 /* Impose security constraints only if tainting */
320 if (sys) sys = PL_curinterp ? PL_tainting : will_taint;
321 if (vmstrnenv(lnm,eqv,idx,
323 #ifdef SECURE_INTERNAL_GETENV
324 sys ? PERL__TRNENV_SECURE : 0
332 } /* end of my_getenv() */
336 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
338 my_getenv_len(const char *lnm, unsigned long *len, bool sys)
341 char *buf, *cp1, *cp2;
342 unsigned long idx = 0;
343 static char __my_getenv_len_eqv[LNM$C_NAMLENGTH+1];
346 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
347 /* Set up a temporary buffer for the return value; Perl will
348 * clean it up at the next statement transition */
349 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
350 if (!tmpsv) return NULL;
353 else buf = __my_getenv_len_eqv; /* Assume no interpreter ==> single thread */
354 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
355 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
356 getcwd(buf,LNM$C_NAMLENGTH);
361 if ((cp2 = strchr(lnm,';')) != NULL) {
364 idx = strtoul(cp2+1,NULL,0);
367 /* Impose security constraints only if tainting */
368 if (sys) sys = PL_curinterp ? PL_tainting : will_taint;
369 if ((*len = vmstrnenv(lnm,buf,idx,
371 #ifdef SECURE_INTERNAL_GETENV
372 sys ? PERL__TRNENV_SECURE : 0
382 } /* end of my_getenv_len() */
385 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
387 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
389 /*{{{ void prime_env_iter() */
392 /* Fill the %ENV associative array with all logical names we can
393 * find, in preparation for iterating over it.
397 static int primed = 0;
398 HV *seenhv = NULL, *envhv;
399 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
400 unsigned short int chan;
401 #ifndef CLI$M_TRUSTED
402 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
404 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
405 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
407 bool have_sym = FALSE, have_lnm = FALSE;
408 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
409 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
410 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
411 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
412 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
413 #if defined(USE_THREADS) || defined(USE_ITHREADS)
414 static perl_mutex primenv_mutex;
415 MUTEX_INIT(&primenv_mutex);
418 if (primed || !PL_envgv) return;
419 MUTEX_LOCK(&primenv_mutex);
420 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
421 envhv = GvHVn(PL_envgv);
422 /* Perform a dummy fetch as an lval to insure that the hash table is
423 * set up. Otherwise, the hv_store() will turn into a nullop. */
424 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
426 for (i = 0; env_tables[i]; i++) {
427 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
428 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
429 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
431 if (have_sym || have_lnm) {
432 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
433 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
434 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
435 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
438 for (i--; i >= 0; i--) {
439 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
442 for (j = 0; environ[j]; j++) {
443 if (!(start = strchr(environ[j],'='))) {
444 if (ckWARN(WARN_INTERNAL))
445 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
449 (void) hv_store(envhv,environ[j],start - environ[j] - 1,
455 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
456 !str$case_blind_compare(&tmpdsc,&clisym)) {
457 strcpy(cmd,"Show Symbol/Global *");
458 cmddsc.dsc$w_length = 20;
459 if (env_tables[i]->dsc$w_length == 12 &&
460 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
461 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
462 flags = defflags | CLI$M_NOLOGNAM;
465 strcpy(cmd,"Show Logical *");
466 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
467 strcat(cmd," /Table=");
468 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
469 cmddsc.dsc$w_length = strlen(cmd);
471 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
472 flags = defflags | CLI$M_NOCLISYM;
475 /* Create a new subprocess to execute each command, to exclude the
476 * remote possibility that someone could subvert a mbx or file used
477 * to write multiple commands to a single subprocess.
480 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
481 0,&riseandshine,0,0,&clidsc,&clitabdsc);
482 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
483 defflags &= ~CLI$M_TRUSTED;
484 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
486 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
487 if (seenhv) SvREFCNT_dec(seenhv);
490 char *cp1, *cp2, *key;
491 unsigned long int sts, iosb[2], retlen, keylen;
494 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
495 if (sts & 1) sts = iosb[0] & 0xffff;
496 if (sts == SS$_ENDOFFILE) {
498 while (substs == 0) { sys$hiber(); wakect++;}
499 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
504 retlen = iosb[0] >> 16;
505 if (!retlen) continue; /* blank line */
507 if (iosb[1] != subpid) {
509 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
513 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
514 Perl_warner(aTHX_ WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
516 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
517 if (*cp1 == '(' || /* Logical name table name */
518 *cp1 == '=' /* Next eqv of searchlist */) continue;
519 if (*cp1 == '"') cp1++;
520 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
521 key = cp1; keylen = cp2 - cp1;
522 if (keylen && hv_exists(seenhv,key,keylen)) continue;
523 while (*cp2 && *cp2 != '=') cp2++;
524 while (*cp2 && *cp2 == '=') cp2++;
525 while (*cp2 && *cp2 == ' ') cp2++;
526 if (*cp2 == '"') { /* String translation; may embed "" */
527 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
528 cp2++; cp1--; /* Skip "" surrounding translation */
530 else { /* Numeric translation */
531 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
532 cp1--; /* stop on last non-space char */
534 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
535 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
538 PERL_HASH(hash,key,keylen);
539 hv_store(envhv,key,keylen,newSVpvn(cp2,cp1 - cp2 + 1),hash);
540 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
542 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
543 /* get the PPFs for this process, not the subprocess */
544 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
545 char eqv[LNM$C_NAMLENGTH+1];
547 for (i = 0; ppfs[i]; i++) {
548 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
549 hv_store(envhv,ppfs[i],strlen(ppfs[i]),newSVpv(eqv,trnlen),0);
554 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
555 if (buf) Safefree(buf);
556 if (seenhv) SvREFCNT_dec(seenhv);
557 MUTEX_UNLOCK(&primenv_mutex);
560 } /* end of prime_env_iter */
564 /*{{{ int vmssetenv(char *lnm, char *eqv)*/
565 /* Define or delete an element in the same "environment" as
566 * vmstrnenv(). If an element is to be deleted, it's removed from
567 * the first place it's found. If it's to be set, it's set in the
568 * place designated by the first element of the table vector.
569 * Like setenv() returns 0 for success, non-zero on error.
572 vmssetenv(char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
574 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
575 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
576 unsigned long int retsts, usermode = PSL$C_USER;
577 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
578 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
579 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
580 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
581 $DESCRIPTOR(local,"_LOCAL");
584 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
585 *cp2 = _toupper(*cp1);
586 if (cp1 - lnm > LNM$C_NAMLENGTH) {
587 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
591 lnmdsc.dsc$w_length = cp1 - lnm;
592 if (!tabvec || !*tabvec) tabvec = env_tables;
594 if (!eqv) { /* we're deleting n element */
595 for (curtab = 0; tabvec[curtab]; curtab++) {
596 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
598 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
599 if ((cp1 = strchr(environ[i],'=')) &&
600 !strncmp(environ[i],lnm,cp1 - environ[i])) {
602 return setenv(lnm,"",1) ? vaxc$errno : 0;
605 ivenv = 1; retsts = SS$_NOLOGNAM;
607 if (ckWARN(WARN_INTERNAL))
608 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
609 ivenv = 1; retsts = SS$_NOSUCHPGM;
615 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
616 !str$case_blind_compare(&tmpdsc,&clisym)) {
617 unsigned int symtype;
618 if (tabvec[curtab]->dsc$w_length == 12 &&
619 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
620 !str$case_blind_compare(&tmpdsc,&local))
621 symtype = LIB$K_CLI_LOCAL_SYM;
622 else symtype = LIB$K_CLI_GLOBAL_SYM;
623 retsts = lib$delete_symbol(&lnmdsc,&symtype);
624 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
625 if (retsts == LIB$_NOSUCHSYM) continue;
629 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
630 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
631 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
632 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
633 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
637 else { /* we're defining a value */
638 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
640 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
642 if (ckWARN(WARN_INTERNAL))
643 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
644 retsts = SS$_NOSUCHPGM;
648 eqvdsc.dsc$a_pointer = eqv;
649 eqvdsc.dsc$w_length = strlen(eqv);
650 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
651 !str$case_blind_compare(&tmpdsc,&clisym)) {
652 unsigned int symtype;
653 if (tabvec[0]->dsc$w_length == 12 &&
654 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
655 !str$case_blind_compare(&tmpdsc,&local))
656 symtype = LIB$K_CLI_LOCAL_SYM;
657 else symtype = LIB$K_CLI_GLOBAL_SYM;
658 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
661 if (!*eqv) eqvdsc.dsc$w_length = 1;
662 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
663 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH;
664 if (ckWARN(WARN_MISC)) {
665 Perl_warner(aTHX_ WARN_MISC,"Value of logical \"%s\" too long. Truncating to %i bytes",lnm, LNM$C_NAMLENGTH);
668 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
674 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
675 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
676 set_errno(EVMSERR); break;
677 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
678 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
679 set_errno(EINVAL); break;
686 set_vaxc_errno(retsts);
687 return (int) retsts || 44; /* retsts should never be 0, but just in case */
690 /* We reset error values on success because Perl does an hv_fetch()
691 * before each hv_store(), and if the thing we're setting didn't
692 * previously exist, we've got a leftover error message. (Of course,
693 * this fails in the face of
694 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
695 * in that the error reported in $! isn't spurious,
696 * but it's right more often than not.)
698 set_errno(0); set_vaxc_errno(retsts);
702 } /* end of vmssetenv() */
705 /*{{{ void my_setenv(char *lnm, char *eqv)*/
706 /* This has to be a function since there's a prototype for it in proto.h */
708 Perl_my_setenv(pTHX_ char *lnm,char *eqv)
711 int len = strlen(lnm);
715 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
716 if (!strcmp(uplnm,"DEFAULT")) {
717 if (eqv && *eqv) chdir(eqv);
722 if (len == 6 || len == 2) {
725 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
727 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
728 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
732 (void) vmssetenv(lnm,eqv,NULL);
738 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
739 /* my_crypt - VMS password hashing
740 * my_crypt() provides an interface compatible with the Unix crypt()
741 * C library function, and uses sys$hash_password() to perform VMS
742 * password hashing. The quadword hashed password value is returned
743 * as a NUL-terminated 8 character string. my_crypt() does not change
744 * the case of its string arguments; in order to match the behavior
745 * of LOGINOUT et al., alphabetic characters in both arguments must
746 * be upcased by the caller.
749 my_crypt(const char *textpasswd, const char *usrname)
751 # ifndef UAI$C_PREFERRED_ALGORITHM
752 # define UAI$C_PREFERRED_ALGORITHM 127
754 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
755 unsigned short int salt = 0;
756 unsigned long int sts;
758 unsigned short int dsc$w_length;
759 unsigned char dsc$b_type;
760 unsigned char dsc$b_class;
761 const char * dsc$a_pointer;
762 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
763 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
764 struct itmlst_3 uailst[3] = {
765 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
766 { sizeof salt, UAI$_SALT, &salt, 0},
767 { 0, 0, NULL, NULL}};
770 usrdsc.dsc$w_length = strlen(usrname);
771 usrdsc.dsc$a_pointer = usrname;
772 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
774 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
778 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
784 if (sts != RMS$_RNF) return NULL;
787 txtdsc.dsc$w_length = strlen(textpasswd);
788 txtdsc.dsc$a_pointer = textpasswd;
789 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
790 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
793 return (char *) hash;
795 } /* end of my_crypt() */
799 static char *mp_do_rmsexpand(pTHX_ char *, char *, int, char *, unsigned);
800 static char *mp_do_fileify_dirspec(pTHX_ char *, char *, int);
801 static char *mp_do_tovmsspec(pTHX_ char *, char *, int);
803 /*{{{int do_rmdir(char *name)*/
805 Perl_do_rmdir(pTHX_ char *name)
807 char dirfile[NAM$C_MAXRSS+1];
811 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
812 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
813 else retval = kill_file(dirfile);
816 } /* end of do_rmdir */
820 * Delete any file to which user has control access, regardless of whether
821 * delete access is explicitly allowed.
822 * Limitations: User must have write access to parent directory.
823 * Does not block signals or ASTs; if interrupted in midstream
824 * may leave file with an altered ACL.
827 /*{{{int kill_file(char *name)*/
829 kill_file(char *name)
831 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
832 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
833 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
835 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
837 unsigned char myace$b_length;
838 unsigned char myace$b_type;
839 unsigned short int myace$w_flags;
840 unsigned long int myace$l_access;
841 unsigned long int myace$l_ident;
842 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
843 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
844 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
846 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
847 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
848 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
849 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
850 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
851 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
853 /* Expand the input spec using RMS, since the CRTL remove() and
854 * system services won't do this by themselves, so we may miss
855 * a file "hiding" behind a logical name or search list. */
856 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
857 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
858 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
859 /* If not, can changing protections help? */
860 if (vaxc$errno != RMS$_PRV) return -1;
862 /* No, so we get our own UIC to use as a rights identifier,
863 * and the insert an ACE at the head of the ACL which allows us
864 * to delete the file.
866 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
867 fildsc.dsc$w_length = strlen(rspec);
868 fildsc.dsc$a_pointer = rspec;
870 newace.myace$l_ident = oldace.myace$l_ident;
871 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
873 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
874 set_errno(ENOENT); break;
876 set_errno(ENOTDIR); break;
878 set_errno(ENODEV); break;
879 case RMS$_SYN: case SS$_INVFILFOROP:
880 set_errno(EINVAL); break;
882 set_errno(EACCES); break;
886 set_vaxc_errno(aclsts);
889 /* Grab any existing ACEs with this identifier in case we fail */
890 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
891 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
892 || fndsts == SS$_NOMOREACE ) {
893 /* Add the new ACE . . . */
894 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
896 if ((rmsts = remove(name))) {
897 /* We blew it - dir with files in it, no write priv for
898 * parent directory, etc. Put things back the way they were. */
899 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
902 addlst[0].bufadr = &oldace;
903 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
910 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
911 /* We just deleted it, so of course it's not there. Some versions of
912 * VMS seem to return success on the unlock operation anyhow (after all
913 * the unlock is successful), but others don't.
915 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
916 if (aclsts & 1) aclsts = fndsts;
919 set_vaxc_errno(aclsts);
925 } /* end of kill_file() */
929 /*{{{int my_mkdir(char *,Mode_t)*/
931 my_mkdir(char *dir, Mode_t mode)
933 STRLEN dirlen = strlen(dir);
936 /* zero length string sometimes gives ACCVIO */
937 if (dirlen == 0) return -1;
939 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
940 * null file name/type. However, it's commonplace under Unix,
941 * so we'll allow it for a gain in portability.
943 if (dir[dirlen-1] == '/') {
944 char *newdir = savepvn(dir,dirlen-1);
945 int ret = mkdir(newdir,mode);
949 else return mkdir(dir,mode);
950 } /* end of my_mkdir */
953 /*{{{int my_chdir(char *)*/
957 STRLEN dirlen = strlen(dir);
960 /* zero length string sometimes gives ACCVIO */
961 if (dirlen == 0) return -1;
963 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
965 * null file name/type. However, it's commonplace under Unix,
966 * so we'll allow it for a gain in portability.
968 if (dir[dirlen-1] == '/') {
969 char *newdir = savepvn(dir,dirlen-1);
970 int ret = chdir(newdir);
974 else return chdir(dir);
975 } /* end of my_chdir */
979 /*{{{FILE *my_tmpfile()*/
987 if ((fp = tmpfile())) return fp;
989 New(1323,cp,L_tmpnam+24,char);
990 strcpy(cp,"Sys$Scratch:");
991 tmpnam(cp+strlen(cp));
992 strcat(cp,".Perltmp");
993 fp = fopen(cp,"w+","fop=dlt");
999 /* default piping mailbox size */
1000 #define PERL_BUFSIZ 512
1004 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
1006 unsigned long int mbxbufsiz;
1007 static unsigned long int syssize = 0;
1008 unsigned long int dviitm = DVI$_DEVNAM;
1010 char csize[LNM$C_NAMLENGTH+1];
1013 unsigned long syiitm = SYI$_MAXBUF;
1015 * Get the SYSGEN parameter MAXBUF
1017 * If the logical 'PERL_MBX_SIZE' is defined
1018 * use the value of the logical instead of PERL_BUFSIZ, but
1019 * keep the size between 128 and MAXBUF.
1022 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
1025 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
1026 mbxbufsiz = atoi(csize);
1028 mbxbufsiz = PERL_BUFSIZ;
1030 if (mbxbufsiz < 128) mbxbufsiz = 128;
1031 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
1033 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
1035 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
1036 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
1038 } /* end of create_mbx() */
1041 /*{{{ my_popen and my_pclose*/
1043 typedef struct _iosb IOSB;
1044 typedef struct _iosb* pIOSB;
1045 typedef struct _pipe Pipe;
1046 typedef struct _pipe* pPipe;
1047 typedef struct pipe_details Info;
1048 typedef struct pipe_details* pInfo;
1049 typedef struct _srqp RQE;
1050 typedef struct _srqp* pRQE;
1051 typedef struct _tochildbuf CBuf;
1052 typedef struct _tochildbuf* pCBuf;
1055 unsigned short status;
1056 unsigned short count;
1057 unsigned long dvispec;
1060 #pragma member_alignment save
1061 #pragma nomember_alignment quadword
1062 struct _srqp { /* VMS self-relative queue entry */
1063 unsigned long qptr[2];
1065 #pragma member_alignment restore
1066 static RQE RQE_ZERO = {0,0};
1068 struct _tochildbuf {
1071 unsigned short size;
1079 unsigned short chan_in;
1080 unsigned short chan_out;
1082 unsigned int bufsize;
1100 PerlIO *fp; /* stdio file pointer to pipe mailbox */
1101 int pid; /* PID of subprocess */
1102 int mode; /* == 'r' if pipe open for reading */
1103 int done; /* subprocess has completed */
1104 int closing; /* my_pclose is closing this pipe */
1105 unsigned long completion; /* termination status of subprocess */
1106 pPipe in; /* pipe in to sub */
1107 pPipe out; /* pipe out of sub */
1108 pPipe err; /* pipe of sub's sys$error */
1109 int in_done; /* true when in pipe finished */
1114 struct exit_control_block
1116 struct exit_control_block *flink;
1117 unsigned long int (*exit_routine)();
1118 unsigned long int arg_count;
1119 unsigned long int *status_address;
1120 unsigned long int exit_status;
1123 #define RETRY_DELAY "0 ::0.20"
1124 #define MAX_RETRY 50
1126 static int pipe_ef = 0; /* first call to safe_popen inits these*/
1127 static unsigned long mypid;
1128 static unsigned long delaytime[2];
1130 static pInfo open_pipes = NULL;
1131 static $DESCRIPTOR(nl_desc, "NL:");
1134 static unsigned long int
1138 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1139 int sts, did_stuff, need_eof;
1143 first we try sending an EOF...ignore if doesn't work, make sure we
1151 _ckvmssts(sys$setast(0));
1152 if (info->in && !info->in->shut_on_empty) {
1153 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
1157 _ckvmssts(sys$setast(1));
1160 if (did_stuff) sleep(1); /* wait for EOF to have an effect */
1165 _ckvmssts(sys$setast(0));
1166 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1167 sts = sys$forcex(&info->pid,0,&abort);
1168 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1171 _ckvmssts(sys$setast(1));
1174 if (did_stuff) sleep(1); /* wait for them to respond */
1178 _ckvmssts(sys$setast(0));
1179 if (!info->done) { /* We tried to be nice . . . */
1180 sts = sys$delprc(&info->pid,0);
1181 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1183 _ckvmssts(sys$setast(1));
1188 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1189 else if (!(sts & 1)) retsts = sts;
1194 static struct exit_control_block pipe_exitblock =
1195 {(struct exit_control_block *) 0,
1196 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1198 static void pipe_mbxtofd_ast(pPipe p);
1199 static void pipe_tochild1_ast(pPipe p);
1200 static void pipe_tochild2_ast(pPipe p);
1203 popen_completion_ast(pInfo info)
1206 pInfo i = open_pipes;
1210 if (i == info) break;
1213 if (!i) return; /* unlinked, probably freed too */
1215 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
1219 Writing to subprocess ...
1220 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
1222 chan_out may be waiting for "done" flag, or hung waiting
1223 for i/o completion to child...cancel the i/o. This will
1224 put it into "snarf mode" (done but no EOF yet) that discards
1227 Output from subprocess (stdout, stderr) needs to be flushed and
1228 shut down. We try sending an EOF, but if the mbx is full the pipe
1229 routine should still catch the "shut_on_empty" flag, telling it to
1230 use immediate-style reads so that "mbx empty" -> EOF.
1234 if (info->in && !info->in_done) { /* only for mode=w */
1235 if (info->in->shut_on_empty && info->in->need_wake) {
1236 info->in->need_wake = FALSE;
1237 _ckvmssts(sys$dclast(pipe_tochild2_ast,info->in,0));
1239 _ckvmssts(sys$cancel(info->in->chan_out));
1243 if (info->out && !info->out_done) { /* were we also piping output? */
1244 info->out->shut_on_empty = TRUE;
1245 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1246 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1250 if (info->err && !info->err_done) { /* we were piping stderr */
1251 info->err->shut_on_empty = TRUE;
1252 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1253 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1256 _ckvmssts(sys$setef(pipe_ef));
1260 static unsigned long int setup_cmddsc(char *cmd, int check_img);
1261 static void vms_execfree(pTHX);
1264 we actually differ from vmstrnenv since we use this to
1265 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
1266 are pointing to the same thing
1269 static unsigned short
1270 popen_translate(char *logical, char *result)
1273 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
1274 $DESCRIPTOR(d_log,"");
1276 unsigned short length;
1277 unsigned short code;
1279 unsigned short *retlenaddr;
1281 unsigned short l, ifi;
1283 d_log.dsc$a_pointer = logical;
1284 d_log.dsc$w_length = strlen(logical);
1286 itmlst[0].code = LNM$_STRING;
1287 itmlst[0].length = 255;
1288 itmlst[0].buffer_addr = result;
1289 itmlst[0].retlenaddr = &l;
1292 itmlst[1].length = 0;
1293 itmlst[1].buffer_addr = 0;
1294 itmlst[1].retlenaddr = 0;
1296 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
1297 if (iss == SS$_NOLOGNAM) {
1301 if (!(iss&1)) lib$signal(iss);
1304 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
1305 strip it off and return the ifi, if any
1308 if (result[0] == 0x1b && result[1] == 0x00) {
1309 memcpy(&ifi,result+2,2);
1310 strcpy(result,result+4);
1312 return ifi; /* this is the RMS internal file id */
1315 #define MAX_DCL_SYMBOL 255
1316 static void pipe_infromchild_ast(pPipe p);
1319 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
1320 inside an AST routine without worrying about reentrancy and which Perl
1321 memory allocator is being used.
1323 We read data and queue up the buffers, then spit them out one at a
1324 time to the output mailbox when the output mailbox is ready for one.
1327 #define INITIAL_TOCHILDQUEUE 2
1330 pipe_tochild_setup(char *rmbx, char *wmbx)
1335 char mbx1[64], mbx2[64];
1336 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1337 DSC$K_CLASS_S, mbx1},
1338 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1339 DSC$K_CLASS_S, mbx2};
1340 unsigned int dviitm = DVI$_DEVBUFSIZ;
1343 New(1368, p, 1, Pipe);
1345 create_mbx(&p->chan_in , &d_mbx1);
1346 create_mbx(&p->chan_out, &d_mbx2);
1347 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1350 p->shut_on_empty = FALSE;
1351 p->need_wake = FALSE;
1354 p->iosb.status = SS$_NORMAL;
1355 p->iosb2.status = SS$_NORMAL;
1362 n = sizeof(CBuf) + p->bufsize;
1364 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
1365 _ckvmssts(lib$get_vm(&n, &b));
1366 b->buf = (char *) b + sizeof(CBuf);
1367 _ckvmssts(lib$insqhi(b, &p->free));
1370 pipe_tochild2_ast(p);
1371 pipe_tochild1_ast(p);
1377 /* reads the MBX Perl is writing, and queues */
1380 pipe_tochild1_ast(pPipe p)
1384 int iss = p->iosb.status;
1385 int eof = (iss == SS$_ENDOFFILE);
1389 p->shut_on_empty = TRUE;
1391 _ckvmssts(sys$dassgn(p->chan_in));
1397 b->size = p->iosb.count;
1398 _ckvmssts(lib$insqhi(b, &p->wait));
1400 p->need_wake = FALSE;
1401 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
1404 p->retry = 1; /* initial call */
1407 if (eof) { /* flush the free queue, return when done */
1408 int n = sizeof(CBuf) + p->bufsize;
1410 iss = lib$remqti(&p->free, &b);
1411 if (iss == LIB$_QUEWASEMP) return;
1413 _ckvmssts(lib$free_vm(&n, &b));
1417 iss = lib$remqti(&p->free, &b);
1418 if (iss == LIB$_QUEWASEMP) {
1419 int n = sizeof(CBuf) + p->bufsize;
1420 _ckvmssts(lib$get_vm(&n, &b));
1421 b->buf = (char *) b + sizeof(CBuf);
1427 iss = sys$qio(0,p->chan_in,
1428 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
1430 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
1431 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
1436 /* writes queued buffers to output, waits for each to complete before
1440 pipe_tochild2_ast(pPipe p)
1444 int iss = p->iosb2.status;
1445 int n = sizeof(CBuf) + p->bufsize;
1446 int done = (p->info && p->info->done) ||
1447 iss == SS$_CANCEL || iss == SS$_ABORT;
1450 if (p->type) { /* type=1 has old buffer, dispose */
1451 if (p->shut_on_empty) {
1452 _ckvmssts(lib$free_vm(&n, &b));
1454 _ckvmssts(lib$insqhi(b, &p->free));
1459 iss = lib$remqti(&p->wait, &b);
1460 if (iss == LIB$_QUEWASEMP) {
1461 if (p->shut_on_empty) {
1463 _ckvmssts(sys$dassgn(p->chan_out));
1464 *p->pipe_done = TRUE;
1465 _ckvmssts(sys$setef(pipe_ef));
1467 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1468 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1472 p->need_wake = TRUE;
1482 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1483 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1485 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
1486 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
1495 pipe_infromchild_setup(char *rmbx, char *wmbx)
1499 char mbx1[64], mbx2[64];
1500 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1501 DSC$K_CLASS_S, mbx1},
1502 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1503 DSC$K_CLASS_S, mbx2};
1504 unsigned int dviitm = DVI$_DEVBUFSIZ;
1506 New(1367, p, 1, Pipe);
1507 create_mbx(&p->chan_in , &d_mbx1);
1508 create_mbx(&p->chan_out, &d_mbx2);
1510 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1511 New(1367, p->buf, p->bufsize, char);
1512 p->shut_on_empty = FALSE;
1515 p->iosb.status = SS$_NORMAL;
1516 pipe_infromchild_ast(p);
1524 pipe_infromchild_ast(pPipe p)
1527 int iss = p->iosb.status;
1528 int eof = (iss == SS$_ENDOFFILE);
1529 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
1530 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
1532 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
1533 _ckvmssts(sys$dassgn(p->chan_out));
1538 input shutdown if EOF from self (done or shut_on_empty)
1539 output shutdown if closing flag set (my_pclose)
1540 send data/eof from child or eof from self
1541 otherwise, re-read (snarf of data from child)
1546 if (myeof && p->chan_in) { /* input shutdown */
1547 _ckvmssts(sys$dassgn(p->chan_in));
1552 if (myeof || kideof) { /* pass EOF to parent */
1553 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
1554 pipe_infromchild_ast, p,
1557 } else if (eof) { /* eat EOF --- fall through to read*/
1559 } else { /* transmit data */
1560 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
1561 pipe_infromchild_ast,p,
1562 p->buf, p->iosb.count, 0, 0, 0, 0));
1568 /* everything shut? flag as done */
1570 if (!p->chan_in && !p->chan_out) {
1571 *p->pipe_done = TRUE;
1572 _ckvmssts(sys$setef(pipe_ef));
1576 /* write completed (or read, if snarfing from child)
1577 if still have input active,
1578 queue read...immediate mode if shut_on_empty so we get EOF if empty
1580 check if Perl reading, generate EOFs as needed
1586 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
1587 pipe_infromchild_ast,p,
1588 p->buf, p->bufsize, 0, 0, 0, 0);
1589 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
1591 } else { /* send EOFs for extra reads */
1592 p->iosb.status = SS$_ENDOFFILE;
1593 p->iosb.dvispec = 0;
1594 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
1596 pipe_infromchild_ast, p, 0, 0, 0, 0));
1602 pipe_mbxtofd_setup(int fd, char *out)
1607 unsigned long dviitm = DVI$_DEVBUFSIZ;
1609 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
1610 DSC$K_CLASS_S, mbx};
1612 /* things like terminals and mbx's don't need this filter */
1613 if (fd && fstat(fd,&s) == 0) {
1614 unsigned long dviitm = DVI$_DEVCHAR, devchar;
1615 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
1616 DSC$K_CLASS_S, s.st_dev};
1618 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
1619 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
1620 strcpy(out, s.st_dev);
1625 New(1366, p, 1, Pipe);
1626 p->fd_out = dup(fd);
1627 create_mbx(&p->chan_in, &d_mbx);
1628 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1629 New(1366, p->buf, p->bufsize+1, char);
1630 p->shut_on_empty = FALSE;
1635 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
1636 pipe_mbxtofd_ast, p,
1637 p->buf, p->bufsize, 0, 0, 0, 0));
1643 pipe_mbxtofd_ast(pPipe p)
1646 int iss = p->iosb.status;
1647 int done = p->info->done;
1649 int eof = (iss == SS$_ENDOFFILE);
1650 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
1651 int err = !(iss&1) && !eof;
1654 if (done && myeof) { /* end piping */
1656 sys$dassgn(p->chan_in);
1657 *p->pipe_done = TRUE;
1658 _ckvmssts(sys$setef(pipe_ef));
1662 if (!err && !eof) { /* good data to send to file */
1663 p->buf[p->iosb.count] = '\n';
1664 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
1667 if (p->retry < MAX_RETRY) {
1668 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
1678 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
1679 pipe_mbxtofd_ast, p,
1680 p->buf, p->bufsize, 0, 0, 0, 0);
1681 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
1686 typedef struct _pipeloc PLOC;
1687 typedef struct _pipeloc* pPLOC;
1691 char dir[NAM$C_MAXRSS+1];
1693 static pPLOC head_PLOC = 0;
1701 AV *av = GvAVn(PL_incgv);
1706 char temp[NAM$C_MAXRSS+1];
1709 /* the . directory from @INC comes last */
1712 p->next = head_PLOC;
1714 strcpy(p->dir,"./");
1716 /* get the directory from $^X */
1718 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
1719 strcpy(temp, PL_origargv[0]);
1720 x = strrchr(temp,']');
1723 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
1725 p->next = head_PLOC;
1727 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1728 p->dir[NAM$C_MAXRSS] = '\0';
1732 /* reverse order of @INC entries, skip "." since entered above */
1734 for (i = 0; i <= AvFILL(av); i++) {
1735 dirsv = *av_fetch(av,i,TRUE);
1737 if (SvROK(dirsv)) continue;
1738 dir = SvPVx(dirsv,n_a);
1739 if (strcmp(dir,".") == 0) continue;
1740 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
1744 p->next = head_PLOC;
1746 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1747 p->dir[NAM$C_MAXRSS] = '\0';
1750 /* most likely spot (ARCHLIB) put first in the list */
1753 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
1755 p->next = head_PLOC;
1757 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1758 p->dir[NAM$C_MAXRSS] = '\0';
1768 static int vmspipe_file_status = 0;
1769 static char vmspipe_file[NAM$C_MAXRSS+1];
1771 /* already found? Check and use ... need read+execute permission */
1773 if (vmspipe_file_status == 1) {
1774 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
1775 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
1776 return vmspipe_file;
1778 vmspipe_file_status = 0;
1781 /* scan through stored @INC, $^X */
1783 if (vmspipe_file_status == 0) {
1784 char file[NAM$C_MAXRSS+1];
1785 pPLOC p = head_PLOC;
1788 strcpy(file, p->dir);
1789 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
1790 file[NAM$C_MAXRSS] = '\0';
1793 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
1795 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
1796 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
1797 vmspipe_file_status = 1;
1798 return vmspipe_file;
1801 vmspipe_file_status = -1; /* failed, use tempfiles */
1808 vmspipe_tempfile(void)
1810 char file[NAM$C_MAXRSS+1];
1812 static int index = 0;
1815 /* create a tempfile */
1817 /* we can't go from W, shr=get to R, shr=get without
1818 an intermediate vulnerable state, so don't bother trying...
1820 and lib$spawn doesn't shr=put, so have to close the write
1822 So... match up the creation date/time and the FID to
1823 make sure we're dealing with the same file
1828 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
1829 fp = fopen(file,"w");
1831 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
1832 fp = fopen(file,"w");
1834 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
1835 fp = fopen(file,"w");
1838 if (!fp) return 0; /* we're hosed */
1840 fprintf(fp,"$! 'f$verify(0)\n");
1841 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
1842 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
1843 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
1844 fprintf(fp,"$ perl_on = \"set noon\"\n");
1845 fprintf(fp,"$ perl_exit = \"exit\"\n");
1846 fprintf(fp,"$ perl_del = \"delete\"\n");
1847 fprintf(fp,"$ pif = \"if\"\n");
1848 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
1849 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define sys$input 'perl_popen_in'\n");
1850 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define sys$error 'perl_popen_err'\n");
1851 fprintf(fp,"$ cmd = perl_popen_cmd\n");
1852 fprintf(fp,"$! --- get rid of global symbols\n");
1853 fprintf(fp,"$ perl_del/symbol/global perl_popen_in\n");
1854 fprintf(fp,"$ perl_del/symbol/global perl_popen_err\n");
1855 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd\n");
1856 fprintf(fp,"$ perl_on\n");
1857 fprintf(fp,"$ 'cmd\n");
1858 fprintf(fp,"$ perl_status = $STATUS\n");
1859 fprintf(fp,"$ perl_del 'perl_cfile'\n");
1860 fprintf(fp,"$ perl_exit 'perl_status'\n");
1863 fgetname(fp, file, 1);
1864 fstat(fileno(fp), &s0);
1867 fp = fopen(file,"r","shr=get");
1869 fstat(fileno(fp), &s1);
1871 if (s0.st_ino[0] != s1.st_ino[0] ||
1872 s0.st_ino[1] != s1.st_ino[1] ||
1873 s0.st_ino[2] != s1.st_ino[2] ||
1874 s0.st_ctime != s1.st_ctime ) {
1885 safe_popen(char *cmd, char *mode)
1888 static int handler_set_up = FALSE;
1889 unsigned long int sts, flags=1; /* nowait - gnu c doesn't allow &1 */
1890 unsigned int table = LIB$K_CLI_GLOBAL_SYM;
1891 char *p, symbol[MAX_DCL_SYMBOL+1], *vmspipe;
1892 char in[512], out[512], err[512], mbx[512];
1894 char tfilebuf[NAM$C_MAXRSS+1];
1896 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
1897 DSC$K_CLASS_S, symbol};
1898 struct dsc$descriptor_s d_out = {0, DSC$K_DTYPE_T,
1899 DSC$K_CLASS_S, out};
1900 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
1902 $DESCRIPTOR(d_sym_cmd,"PERL_POPEN_CMD");
1903 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
1904 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
1906 /* once-per-program initialization...
1907 note that the SETAST calls and the dual test of pipe_ef
1908 makes sure that only the FIRST thread through here does
1909 the initialization...all other threads wait until it's
1912 Yeah, uglier than a pthread call, it's got all the stuff inline
1913 rather than in a separate routine.
1917 _ckvmssts(sys$setast(0));
1919 unsigned long int pidcode = JPI$_PID;
1920 $DESCRIPTOR(d_delay, RETRY_DELAY);
1921 _ckvmssts(lib$get_ef(&pipe_ef));
1922 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
1923 _ckvmssts(sys$bintim(&d_delay, delaytime));
1925 if (!handler_set_up) {
1926 _ckvmssts(sys$dclexh(&pipe_exitblock));
1927 handler_set_up = TRUE;
1929 _ckvmssts(sys$setast(1));
1932 /* see if we can find a VMSPIPE.COM */
1935 vmspipe = find_vmspipe();
1937 strcpy(tfilebuf+1,vmspipe);
1938 } else { /* uh, oh...we're in tempfile hell */
1939 tpipe = vmspipe_tempfile();
1940 if (!tpipe) { /* a fish popular in Boston */
1941 if (ckWARN(WARN_PIPE)) {
1942 Perl_warner(aTHX_ WARN_PIPE,"unable to find VMSPIPE.COM for i/o piping");
1946 fgetname(tpipe,tfilebuf+1,1);
1948 vmspipedsc.dsc$a_pointer = tfilebuf;
1949 vmspipedsc.dsc$w_length = strlen(tfilebuf);
1951 if (!(setup_cmddsc(cmd,0) & 1)) { set_errno(EINVAL); return Nullfp; }
1952 New(1301,info,1,Info);
1956 info->completion = 0;
1957 info->closing = FALSE;
1961 info->in_done = TRUE;
1962 info->out_done = TRUE;
1963 info->err_done = TRUE;
1965 if (*mode == 'r') { /* piping from subroutine */
1968 info->out = pipe_infromchild_setup(mbx,out);
1970 info->out->pipe_done = &info->out_done;
1971 info->out_done = FALSE;
1972 info->out->info = info;
1974 info->fp = PerlIO_open(mbx, mode);
1975 if (!info->fp && info->out) {
1976 sys$cancel(info->out->chan_out);
1978 while (!info->out_done) {
1980 _ckvmssts(sys$setast(0));
1981 done = info->out_done;
1982 if (!done) _ckvmssts(sys$clref(pipe_ef));
1983 _ckvmssts(sys$setast(1));
1984 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
1987 if (info->out->buf) Safefree(info->out->buf);
1988 Safefree(info->out);
1993 info->err = pipe_mbxtofd_setup(fileno(stderr), err);
1995 info->err->pipe_done = &info->err_done;
1996 info->err_done = FALSE;
1997 info->err->info = info;
2000 } else { /* piping to subroutine , mode=w*/
2003 info->in = pipe_tochild_setup(in,mbx);
2004 info->fp = PerlIO_open(mbx, mode);
2006 info->in->pipe_done = &info->in_done;
2007 info->in_done = FALSE;
2008 info->in->info = info;
2012 if (!info->fp && info->in) {
2014 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
2015 0, 0, 0, 0, 0, 0, 0, 0));
2017 while (!info->in_done) {
2019 _ckvmssts(sys$setast(0));
2020 done = info->in_done;
2021 if (!done) _ckvmssts(sys$clref(pipe_ef));
2022 _ckvmssts(sys$setast(1));
2023 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2026 if (info->in->buf) Safefree(info->in->buf);
2032 /* if SYS$ERROR == SYS$OUTPUT, use only one mbx */
2035 fgetname(stderr, err);
2036 if (strncmp(err,"SYS$ERROR:",10) == 0) {
2037 fgetname(stdout, out);
2038 if (strncmp(out,"SYS$OUTPUT:",11) == 0) {
2039 if (popen_translate("SYS$OUTPUT",out) == popen_translate("SYS$ERROR",err)) {
2045 info->out = pipe_mbxtofd_setup(fileno(stdout), out);
2047 info->out->pipe_done = &info->out_done;
2048 info->out_done = FALSE;
2049 info->out->info = info;
2052 info->err = pipe_mbxtofd_setup(fileno(stderr), err);
2054 info->err->pipe_done = &info->err_done;
2055 info->err_done = FALSE;
2056 info->err->info = info;
2062 d_out.dsc$w_length = strlen(out); /* lib$spawn sets SYS$OUTPUT so can meld*/
2064 symbol[MAX_DCL_SYMBOL] = '\0';
2066 strncpy(symbol, in, MAX_DCL_SYMBOL);
2067 d_symbol.dsc$w_length = strlen(symbol);
2068 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
2070 strncpy(symbol, err, MAX_DCL_SYMBOL);
2071 d_symbol.dsc$w_length = strlen(symbol);
2072 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
2075 p = VMScmd.dsc$a_pointer;
2076 while (*p && *p != '\n') p++;
2077 *p = '\0'; /* truncate on \n */
2078 p = VMScmd.dsc$a_pointer;
2079 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
2080 if (*p == '$') p++; /* remove leading $ */
2081 while (*p == ' ' || *p == '\t') p++;
2082 strncpy(symbol, p, MAX_DCL_SYMBOL);
2083 d_symbol.dsc$w_length = strlen(symbol);
2084 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
2086 _ckvmssts(sys$setast(0));
2087 info->next=open_pipes; /* prepend to list */
2089 _ckvmssts(sys$setast(1));
2090 _ckvmssts(lib$spawn(&vmspipedsc, &nl_desc, &d_out, &flags,
2091 0, &info->pid, &info->completion,
2092 0, popen_completion_ast,info,0,0,0));
2094 /* if we were using a tempfile, close it now */
2096 if (tpipe) fclose(tpipe);
2098 /* once the subprocess is spawned, its copied the symbols and
2099 we can get rid of ours */
2101 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
2102 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
2103 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
2107 PL_forkprocess = info->pid;
2109 } /* end of safe_popen */
2112 /*{{{ FILE *my_popen(char *cmd, char *mode)*/
2114 Perl_my_popen(pTHX_ char *cmd, char *mode)
2117 TAINT_PROPER("popen");
2118 PERL_FLUSHALL_FOR_CHILD;
2119 return safe_popen(cmd,mode);
2124 /*{{{ I32 my_pclose(FILE *fp)*/
2125 I32 Perl_my_pclose(pTHX_ FILE *fp)
2128 pInfo info, last = NULL;
2129 unsigned long int retsts;
2132 for (info = open_pipes; info != NULL; last = info, info = info->next)
2133 if (info->fp == fp) break;
2135 if (info == NULL) { /* no such pipe open */
2136 set_errno(ECHILD); /* quoth POSIX */
2137 set_vaxc_errno(SS$_NONEXPR);
2141 /* If we were writing to a subprocess, insure that someone reading from
2142 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
2143 * produce an EOF record in the mailbox.
2145 * well, at least sometimes it *does*, so we have to watch out for
2146 * the first EOF closing the pipe (and DASSGN'ing the channel)...
2149 fsync(fileno(info->fp)); /* first, flush data */
2151 _ckvmssts(sys$setast(0));
2152 info->closing = TRUE;
2153 done = info->done && info->in_done && info->out_done && info->err_done;
2154 /* hanging on write to Perl's input? cancel it */
2155 if (info->mode == 'r' && info->out && !info->out_done) {
2156 if (info->out->chan_out) {
2157 _ckvmssts(sys$cancel(info->out->chan_out));
2158 if (!info->out->chan_in) { /* EOF generation, need AST */
2159 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
2163 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
2164 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2166 _ckvmssts(sys$setast(1));
2167 PerlIO_close(info->fp);
2170 we have to wait until subprocess completes, but ALSO wait until all
2171 the i/o completes...otherwise we'll be freeing the "info" structure
2172 that the i/o ASTs could still be using...
2176 _ckvmssts(sys$setast(0));
2177 done = info->done && info->in_done && info->out_done && info->err_done;
2178 if (!done) _ckvmssts(sys$clref(pipe_ef));
2179 _ckvmssts(sys$setast(1));
2180 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2182 retsts = info->completion;
2184 /* remove from list of open pipes */
2185 _ckvmssts(sys$setast(0));
2186 if (last) last->next = info->next;
2187 else open_pipes = info->next;
2188 _ckvmssts(sys$setast(1));
2190 /* free buffers and structures */
2193 if (info->in->buf) Safefree(info->in->buf);
2197 if (info->out->buf) Safefree(info->out->buf);
2198 Safefree(info->out);
2201 if (info->err->buf) Safefree(info->err->buf);
2202 Safefree(info->err);
2208 } /* end of my_pclose() */
2210 /* sort-of waitpid; use only with popen() */
2211 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
2213 my_waitpid(Pid_t pid, int *statusp, int flags)
2219 for (info = open_pipes; info != NULL; info = info->next)
2220 if (info->pid == pid) break;
2222 if (info != NULL) { /* we know about this child */
2223 while (!info->done) {
2224 _ckvmssts(sys$setast(0));
2226 if (!done) _ckvmssts(sys$clref(pipe_ef));
2227 _ckvmssts(sys$setast(1));
2228 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2231 *statusp = info->completion;
2234 else { /* we haven't heard of this child */
2235 $DESCRIPTOR(intdsc,"0 00:00:01");
2236 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
2237 unsigned long int interval[2],sts;
2239 if (ckWARN(WARN_EXEC)) {
2240 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
2241 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
2242 if (ownerpid != mypid)
2243 Perl_warner(aTHX_ WARN_EXEC,"pid %x not a child",pid);
2246 _ckvmssts(sys$bintim(&intdsc,interval));
2247 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
2248 _ckvmssts(sys$schdwk(0,0,interval,0));
2249 _ckvmssts(sys$hiber());
2251 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
2254 /* There's no easy way to find the termination status a child we're
2255 * not aware of beforehand. If we're really interested in the future,
2256 * we can go looking for a termination mailbox, or chase after the
2257 * accounting record for the process.
2263 } /* end of waitpid() */
2268 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
2270 my_gconvert(double val, int ndig, int trail, char *buf)
2272 static char __gcvtbuf[DBL_DIG+1];
2275 loc = buf ? buf : __gcvtbuf;
2277 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
2279 sprintf(loc,"%.*g",ndig,val);
2285 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
2286 return gcvt(val,ndig,loc);
2289 loc[0] = '0'; loc[1] = '\0';
2297 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
2298 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
2299 * to expand file specification. Allows for a single default file
2300 * specification and a simple mask of options. If outbuf is non-NULL,
2301 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
2302 * the resultant file specification is placed. If outbuf is NULL, the
2303 * resultant file specification is placed into a static buffer.
2304 * The third argument, if non-NULL, is taken to be a default file
2305 * specification string. The fourth argument is unused at present.
2306 * rmesexpand() returns the address of the resultant string if
2307 * successful, and NULL on error.
2309 static char *mp_do_tounixspec(pTHX_ char *, char *, int);
2312 mp_do_rmsexpand(pTHX_ char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
2314 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
2315 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
2316 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
2317 struct FAB myfab = cc$rms_fab;
2318 struct NAM mynam = cc$rms_nam;
2320 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
2322 if (!filespec || !*filespec) {
2323 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
2327 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
2328 else outbuf = __rmsexpand_retbuf;
2330 if ((isunix = (strchr(filespec,'/') != NULL))) {
2331 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
2332 filespec = vmsfspec;
2335 myfab.fab$l_fna = filespec;
2336 myfab.fab$b_fns = strlen(filespec);
2337 myfab.fab$l_nam = &mynam;
2339 if (defspec && *defspec) {
2340 if (strchr(defspec,'/') != NULL) {
2341 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
2344 myfab.fab$l_dna = defspec;
2345 myfab.fab$b_dns = strlen(defspec);
2348 mynam.nam$l_esa = esa;
2349 mynam.nam$b_ess = sizeof esa;
2350 mynam.nam$l_rsa = outbuf;
2351 mynam.nam$b_rss = NAM$C_MAXRSS;
2353 retsts = sys$parse(&myfab,0,0);
2354 if (!(retsts & 1)) {
2355 mynam.nam$b_nop |= NAM$M_SYNCHK;
2356 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
2357 retsts = sys$parse(&myfab,0,0);
2358 if (retsts & 1) goto expanded;
2360 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
2361 (void) sys$parse(&myfab,0,0); /* Free search context */
2362 if (out) Safefree(out);
2363 set_vaxc_errno(retsts);
2364 if (retsts == RMS$_PRV) set_errno(EACCES);
2365 else if (retsts == RMS$_DEV) set_errno(ENODEV);
2366 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
2367 else set_errno(EVMSERR);
2370 retsts = sys$search(&myfab,0,0);
2371 if (!(retsts & 1) && retsts != RMS$_FNF) {
2372 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2373 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2374 if (out) Safefree(out);
2375 set_vaxc_errno(retsts);
2376 if (retsts == RMS$_PRV) set_errno(EACCES);
2377 else set_errno(EVMSERR);
2381 /* If the input filespec contained any lowercase characters,
2382 * downcase the result for compatibility with Unix-minded code. */
2384 for (out = myfab.fab$l_fna; *out; out++)
2385 if (islower(*out)) { haslower = 1; break; }
2386 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
2387 else { out = esa; speclen = mynam.nam$b_esl; }
2388 /* Trim off null fields added by $PARSE
2389 * If type > 1 char, must have been specified in original or default spec
2390 * (not true for version; $SEARCH may have added version of existing file).
2392 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
2393 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
2394 (mynam.nam$l_ver - mynam.nam$l_type == 1);
2395 if (trimver || trimtype) {
2396 if (defspec && *defspec) {
2397 char defesa[NAM$C_MAXRSS];
2398 struct FAB deffab = cc$rms_fab;
2399 struct NAM defnam = cc$rms_nam;
2401 deffab.fab$l_nam = &defnam;
2402 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
2403 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
2404 defnam.nam$b_nop = NAM$M_SYNCHK;
2405 if (sys$parse(&deffab,0,0) & 1) {
2406 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
2407 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
2410 if (trimver) speclen = mynam.nam$l_ver - out;
2412 /* If we didn't already trim version, copy down */
2413 if (speclen > mynam.nam$l_ver - out)
2414 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
2415 speclen - (mynam.nam$l_ver - out));
2416 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
2419 /* If we just had a directory spec on input, $PARSE "helpfully"
2420 * adds an empty name and type for us */
2421 if (mynam.nam$l_name == mynam.nam$l_type &&
2422 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
2423 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
2424 speclen = mynam.nam$l_name - out;
2425 out[speclen] = '\0';
2426 if (haslower) __mystrtolower(out);
2428 /* Have we been working with an expanded, but not resultant, spec? */
2429 /* Also, convert back to Unix syntax if necessary. */
2430 if (!mynam.nam$b_rsl) {
2432 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
2434 else strcpy(outbuf,esa);
2437 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
2438 strcpy(outbuf,tmpfspec);
2440 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2441 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
2442 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2446 /* External entry points */
2447 char *Perl_rmsexpand(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2448 { return do_rmsexpand(spec,buf,0,def,opt); }
2449 char *Perl_rmsexpand_ts(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2450 { return do_rmsexpand(spec,buf,1,def,opt); }
2454 ** The following routines are provided to make life easier when
2455 ** converting among VMS-style and Unix-style directory specifications.
2456 ** All will take input specifications in either VMS or Unix syntax. On
2457 ** failure, all return NULL. If successful, the routines listed below
2458 ** return a pointer to a buffer containing the appropriately
2459 ** reformatted spec (and, therefore, subsequent calls to that routine
2460 ** will clobber the result), while the routines of the same names with
2461 ** a _ts suffix appended will return a pointer to a mallocd string
2462 ** containing the appropriately reformatted spec.
2463 ** In all cases, only explicit syntax is altered; no check is made that
2464 ** the resulting string is valid or that the directory in question
2467 ** fileify_dirspec() - convert a directory spec into the name of the
2468 ** directory file (i.e. what you can stat() to see if it's a dir).
2469 ** The style (VMS or Unix) of the result is the same as the style
2470 ** of the parameter passed in.
2471 ** pathify_dirspec() - convert a directory spec into a path (i.e.
2472 ** what you prepend to a filename to indicate what directory it's in).
2473 ** The style (VMS or Unix) of the result is the same as the style
2474 ** of the parameter passed in.
2475 ** tounixpath() - convert a directory spec into a Unix-style path.
2476 ** tovmspath() - convert a directory spec into a VMS-style path.
2477 ** tounixspec() - convert any file spec into a Unix-style file spec.
2478 ** tovmsspec() - convert any file spec into a VMS-style spec.
2480 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
2481 ** Permission is given to distribute this code as part of the Perl
2482 ** standard distribution under the terms of the GNU General Public
2483 ** License or the Perl Artistic License. Copies of each may be
2484 ** found in the Perl standard distribution.
2487 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
2488 static char *mp_do_fileify_dirspec(pTHX_ char *dir,char *buf,int ts)
2490 static char __fileify_retbuf[NAM$C_MAXRSS+1];
2491 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
2492 char *retspec, *cp1, *cp2, *lastdir;
2493 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
2495 if (!dir || !*dir) {
2496 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
2498 dirlen = strlen(dir);
2499 while (dirlen && dir[dirlen-1] == '/') --dirlen;
2500 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
2501 strcpy(trndir,"/sys$disk/000000");
2505 if (dirlen > NAM$C_MAXRSS) {
2506 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
2508 if (!strpbrk(dir+1,"/]>:")) {
2509 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2510 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
2512 dirlen = strlen(dir);
2515 strncpy(trndir,dir,dirlen);
2516 trndir[dirlen] = '\0';
2519 /* If we were handed a rooted logical name or spec, treat it like a
2520 * simple directory, so that
2521 * $ Define myroot dev:[dir.]
2522 * ... do_fileify_dirspec("myroot",buf,1) ...
2523 * does something useful.
2525 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".]")) {
2526 dir[--dirlen] = '\0';
2527 dir[dirlen-1] = ']';
2530 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
2531 /* If we've got an explicit filename, we can just shuffle the string. */
2532 if (*(cp1+1)) hasfilename = 1;
2533 /* Similarly, we can just back up a level if we've got multiple levels
2534 of explicit directories in a VMS spec which ends with directories. */
2536 for (cp2 = cp1; cp2 > dir; cp2--) {
2538 *cp2 = *cp1; *cp1 = '\0';
2542 if (*cp2 == '[' || *cp2 == '<') break;
2547 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
2548 if (dir[0] == '.') {
2549 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
2550 return do_fileify_dirspec("[]",buf,ts);
2551 else if (dir[1] == '.' &&
2552 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
2553 return do_fileify_dirspec("[-]",buf,ts);
2555 if (dirlen && dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
2556 dirlen -= 1; /* to last element */
2557 lastdir = strrchr(dir,'/');
2559 else if ((cp1 = strstr(dir,"/.")) != NULL) {
2560 /* If we have "/." or "/..", VMSify it and let the VMS code
2561 * below expand it, rather than repeating the code to handle
2562 * relative components of a filespec here */
2564 if (*(cp1+2) == '.') cp1++;
2565 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
2566 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
2567 if (strchr(vmsdir,'/') != NULL) {
2568 /* If do_tovmsspec() returned it, it must have VMS syntax
2569 * delimiters in it, so it's a mixed VMS/Unix spec. We take
2570 * the time to check this here only so we avoid a recursion
2571 * loop; otherwise, gigo.
2573 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
2575 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
2576 return do_tounixspec(trndir,buf,ts);
2579 } while ((cp1 = strstr(cp1,"/.")) != NULL);
2580 lastdir = strrchr(dir,'/');
2582 else if (dirlen >= 7 && !strcmp(&dir[dirlen-7],"/000000")) {
2583 /* Ditto for specs that end in an MFD -- let the VMS code
2584 * figure out whether it's a real device or a rooted logical. */
2585 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
2586 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
2587 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
2588 return do_tounixspec(trndir,buf,ts);
2591 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
2592 !(lastdir = cp1 = strrchr(dir,']')) &&
2593 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
2594 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
2596 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2597 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2598 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2599 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2600 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2601 (ver || *cp3)))))) {
2603 set_vaxc_errno(RMS$_DIR);
2609 /* If we lead off with a device or rooted logical, add the MFD
2610 if we're specifying a top-level directory. */
2611 if (lastdir && *dir == '/') {
2613 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
2620 retlen = dirlen + (addmfd ? 13 : 6);
2621 if (buf) retspec = buf;
2622 else if (ts) New(1309,retspec,retlen+1,char);
2623 else retspec = __fileify_retbuf;
2625 dirlen = lastdir - dir;
2626 memcpy(retspec,dir,dirlen);
2627 strcpy(&retspec[dirlen],"/000000");
2628 strcpy(&retspec[dirlen+7],lastdir);
2631 memcpy(retspec,dir,dirlen);
2632 retspec[dirlen] = '\0';
2634 /* We've picked up everything up to the directory file name.
2635 Now just add the type and version, and we're set. */
2636 strcat(retspec,".dir;1");
2639 else { /* VMS-style directory spec */
2640 char esa[NAM$C_MAXRSS+1], term, *cp;
2641 unsigned long int sts, cmplen, haslower = 0;
2642 struct FAB dirfab = cc$rms_fab;
2643 struct NAM savnam, dirnam = cc$rms_nam;
2645 dirfab.fab$b_fns = strlen(dir);
2646 dirfab.fab$l_fna = dir;
2647 dirfab.fab$l_nam = &dirnam;
2648 dirfab.fab$l_dna = ".DIR;1";
2649 dirfab.fab$b_dns = 6;
2650 dirnam.nam$b_ess = NAM$C_MAXRSS;
2651 dirnam.nam$l_esa = esa;
2653 for (cp = dir; *cp; cp++)
2654 if (islower(*cp)) { haslower = 1; break; }
2655 if (!((sts = sys$parse(&dirfab))&1)) {
2656 if (dirfab.fab$l_sts == RMS$_DIR) {
2657 dirnam.nam$b_nop |= NAM$M_SYNCHK;
2658 sts = sys$parse(&dirfab) & 1;
2662 set_vaxc_errno(dirfab.fab$l_sts);
2668 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
2669 /* Yes; fake the fnb bits so we'll check type below */
2670 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
2672 else { /* No; just work with potential name */
2673 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
2675 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
2676 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2677 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2682 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
2683 cp1 = strchr(esa,']');
2684 if (!cp1) cp1 = strchr(esa,'>');
2685 if (cp1) { /* Should always be true */
2686 dirnam.nam$b_esl -= cp1 - esa - 1;
2687 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
2690 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
2691 /* Yep; check version while we're at it, if it's there. */
2692 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
2693 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
2694 /* Something other than .DIR[;1]. Bzzt. */
2695 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2696 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2698 set_vaxc_errno(RMS$_DIR);
2702 esa[dirnam.nam$b_esl] = '\0';
2703 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
2704 /* They provided at least the name; we added the type, if necessary, */
2705 if (buf) retspec = buf; /* in sys$parse() */
2706 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
2707 else retspec = __fileify_retbuf;
2708 strcpy(retspec,esa);
2709 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2710 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2713 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
2714 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
2716 dirnam.nam$b_esl -= 9;
2718 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
2719 if (cp1 == NULL) { /* should never happen */
2720 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2721 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2726 retlen = strlen(esa);
2727 if ((cp1 = strrchr(esa,'.')) != NULL) {
2728 /* There's more than one directory in the path. Just roll back. */
2730 if (buf) retspec = buf;
2731 else if (ts) New(1311,retspec,retlen+7,char);
2732 else retspec = __fileify_retbuf;
2733 strcpy(retspec,esa);
2736 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
2737 /* Go back and expand rooted logical name */
2738 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
2739 if (!(sys$parse(&dirfab) & 1)) {
2740 dirnam.nam$l_rlf = NULL;
2741 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2743 set_vaxc_errno(dirfab.fab$l_sts);
2746 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
2747 if (buf) retspec = buf;
2748 else if (ts) New(1312,retspec,retlen+16,char);
2749 else retspec = __fileify_retbuf;
2750 cp1 = strstr(esa,"][");
2752 memcpy(retspec,esa,dirlen);
2753 if (!strncmp(cp1+2,"000000]",7)) {
2754 retspec[dirlen-1] = '\0';
2755 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
2756 if (*cp1 == '.') *cp1 = ']';
2758 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
2759 memcpy(cp1+1,"000000]",7);
2763 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
2764 retspec[retlen] = '\0';
2765 /* Convert last '.' to ']' */
2766 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
2767 if (*cp1 == '.') *cp1 = ']';
2769 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
2770 memcpy(cp1+1,"000000]",7);
2774 else { /* This is a top-level dir. Add the MFD to the path. */
2775 if (buf) retspec = buf;
2776 else if (ts) New(1312,retspec,retlen+16,char);
2777 else retspec = __fileify_retbuf;
2780 while (*cp1 != ':') *(cp2++) = *(cp1++);
2781 strcpy(cp2,":[000000]");
2786 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2787 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2788 /* We've set up the string up through the filename. Add the
2789 type and version, and we're done. */
2790 strcat(retspec,".DIR;1");
2792 /* $PARSE may have upcased filespec, so convert output to lower
2793 * case if input contained any lowercase characters. */
2794 if (haslower) __mystrtolower(retspec);
2797 } /* end of do_fileify_dirspec() */
2799 /* External entry points */
2800 char *Perl_fileify_dirspec(pTHX_ char *dir, char *buf)
2801 { return do_fileify_dirspec(dir,buf,0); }
2802 char *Perl_fileify_dirspec_ts(pTHX_ char *dir, char *buf)
2803 { return do_fileify_dirspec(dir,buf,1); }
2805 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
2806 static char *mp_do_pathify_dirspec(pTHX_ char *dir,char *buf, int ts)
2808 static char __pathify_retbuf[NAM$C_MAXRSS+1];
2809 unsigned long int retlen;
2810 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
2812 if (!dir || !*dir) {
2813 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
2816 if (*dir) strcpy(trndir,dir);
2817 else getcwd(trndir,sizeof trndir - 1);
2819 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
2820 && my_trnlnm(trndir,trndir,0)) {
2821 STRLEN trnlen = strlen(trndir);
2823 /* Trap simple rooted lnms, and return lnm:[000000] */
2824 if (!strcmp(trndir+trnlen-2,".]")) {
2825 if (buf) retpath = buf;
2826 else if (ts) New(1318,retpath,strlen(dir)+10,char);
2827 else retpath = __pathify_retbuf;
2828 strcpy(retpath,dir);
2829 strcat(retpath,":[000000]");
2835 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
2836 if (*dir == '.' && (*(dir+1) == '\0' ||
2837 (*(dir+1) == '.' && *(dir+2) == '\0')))
2838 retlen = 2 + (*(dir+1) != '\0');
2840 if ( !(cp1 = strrchr(dir,'/')) &&
2841 !(cp1 = strrchr(dir,']')) &&
2842 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
2843 if ((cp2 = strchr(cp1,'.')) != NULL &&
2844 (*(cp2-1) != '/' || /* Trailing '.', '..', */
2845 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
2846 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
2847 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
2849 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2850 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2851 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2852 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2853 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2854 (ver || *cp3)))))) {
2856 set_vaxc_errno(RMS$_DIR);
2859 retlen = cp2 - dir + 1;
2861 else { /* No file type present. Treat the filename as a directory. */
2862 retlen = strlen(dir) + 1;
2865 if (buf) retpath = buf;
2866 else if (ts) New(1313,retpath,retlen+1,char);
2867 else retpath = __pathify_retbuf;
2868 strncpy(retpath,dir,retlen-1);
2869 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
2870 retpath[retlen-1] = '/'; /* with '/', add it. */
2871 retpath[retlen] = '\0';
2873 else retpath[retlen-1] = '\0';
2875 else { /* VMS-style directory spec */
2876 char esa[NAM$C_MAXRSS+1], *cp;
2877 unsigned long int sts, cmplen, haslower;
2878 struct FAB dirfab = cc$rms_fab;
2879 struct NAM savnam, dirnam = cc$rms_nam;
2881 /* If we've got an explicit filename, we can just shuffle the string. */
2882 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
2883 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
2884 if ((cp2 = strchr(cp1,'.')) != NULL) {
2886 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2887 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2888 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2889 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2890 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2891 (ver || *cp3)))))) {
2893 set_vaxc_errno(RMS$_DIR);
2897 else { /* No file type, so just draw name into directory part */
2898 for (cp2 = cp1; *cp2; cp2++) ;
2901 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
2903 /* We've now got a VMS 'path'; fall through */
2905 dirfab.fab$b_fns = strlen(dir);
2906 dirfab.fab$l_fna = dir;
2907 if (dir[dirfab.fab$b_fns-1] == ']' ||
2908 dir[dirfab.fab$b_fns-1] == '>' ||
2909 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
2910 if (buf) retpath = buf;
2911 else if (ts) New(1314,retpath,strlen(dir)+1,char);
2912 else retpath = __pathify_retbuf;
2913 strcpy(retpath,dir);
2916 dirfab.fab$l_dna = ".DIR;1";
2917 dirfab.fab$b_dns = 6;
2918 dirfab.fab$l_nam = &dirnam;
2919 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
2920 dirnam.nam$l_esa = esa;
2922 for (cp = dir; *cp; cp++)
2923 if (islower(*cp)) { haslower = 1; break; }
2925 if (!(sts = (sys$parse(&dirfab)&1))) {
2926 if (dirfab.fab$l_sts == RMS$_DIR) {
2927 dirnam.nam$b_nop |= NAM$M_SYNCHK;
2928 sts = sys$parse(&dirfab) & 1;
2932 set_vaxc_errno(dirfab.fab$l_sts);
2938 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
2939 if (dirfab.fab$l_sts != RMS$_FNF) {
2940 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2941 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2943 set_vaxc_errno(dirfab.fab$l_sts);
2946 dirnam = savnam; /* No; just work with potential name */
2949 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
2950 /* Yep; check version while we're at it, if it's there. */
2951 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
2952 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
2953 /* Something other than .DIR[;1]. Bzzt. */
2954 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2955 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2957 set_vaxc_errno(RMS$_DIR);
2961 /* OK, the type was fine. Now pull any file name into the
2963 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
2965 cp1 = strrchr(esa,'>');
2966 *dirnam.nam$l_type = '>';
2969 *(dirnam.nam$l_type + 1) = '\0';
2970 retlen = dirnam.nam$l_type - esa + 2;
2971 if (buf) retpath = buf;
2972 else if (ts) New(1314,retpath,retlen,char);
2973 else retpath = __pathify_retbuf;
2974 strcpy(retpath,esa);
2975 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2976 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2977 /* $PARSE may have upcased filespec, so convert output to lower
2978 * case if input contained any lowercase characters. */
2979 if (haslower) __mystrtolower(retpath);
2983 } /* end of do_pathify_dirspec() */
2985 /* External entry points */
2986 char *Perl_pathify_dirspec(pTHX_ char *dir, char *buf)
2987 { return do_pathify_dirspec(dir,buf,0); }
2988 char *Perl_pathify_dirspec_ts(pTHX_ char *dir, char *buf)
2989 { return do_pathify_dirspec(dir,buf,1); }
2991 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
2992 static char *mp_do_tounixspec(pTHX_ char *spec, char *buf, int ts)
2994 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
2995 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
2996 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
2998 if (spec == NULL) return NULL;
2999 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
3000 if (buf) rslt = buf;
3002 retlen = strlen(spec);
3003 cp1 = strchr(spec,'[');
3004 if (!cp1) cp1 = strchr(spec,'<');
3006 for (cp1++; *cp1; cp1++) {
3007 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
3008 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
3009 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
3012 New(1315,rslt,retlen+2+2*expand,char);
3014 else rslt = __tounixspec_retbuf;
3015 if (strchr(spec,'/') != NULL) {
3022 dirend = strrchr(spec,']');
3023 if (dirend == NULL) dirend = strrchr(spec,'>');
3024 if (dirend == NULL) dirend = strchr(spec,':');
3025 if (dirend == NULL) {
3029 if (*cp2 != '[' && *cp2 != '<') {
3032 else { /* the VMS spec begins with directories */
3034 if (*cp2 == ']' || *cp2 == '>') {
3035 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
3038 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
3039 if (getcwd(tmp,sizeof tmp,1) == NULL) {
3040 if (ts) Safefree(rslt);
3045 while (*cp3 != ':' && *cp3) cp3++;
3047 if (strchr(cp3,']') != NULL) break;
3048 } while (vmstrnenv(tmp,tmp,0,fildev,0));
3050 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
3051 retlen = devlen + dirlen;
3052 Renew(rslt,retlen+1+2*expand,char);
3058 *(cp1++) = *(cp3++);
3059 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
3063 else if ( *cp2 == '.') {
3064 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
3065 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3071 for (; cp2 <= dirend; cp2++) {
3074 if (*(cp2+1) == '[') cp2++;
3076 else if (*cp2 == ']' || *cp2 == '>') {
3077 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
3079 else if (*cp2 == '.') {
3081 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
3082 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
3083 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
3084 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
3085 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
3087 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
3088 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
3092 else if (*cp2 == '-') {
3093 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
3094 while (*cp2 == '-') {
3096 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3098 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
3099 if (ts) Safefree(rslt); /* filespecs like */
3100 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
3104 else *(cp1++) = *cp2;
3106 else *(cp1++) = *cp2;
3108 while (*cp2) *(cp1++) = *(cp2++);
3113 } /* end of do_tounixspec() */
3115 /* External entry points */
3116 char *Perl_tounixspec(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
3117 char *Perl_tounixspec_ts(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
3119 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
3120 static char *mp_do_tovmsspec(pTHX_ char *path, char *buf, int ts) {
3121 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
3122 char *rslt, *dirend;
3123 register char *cp1, *cp2;
3124 unsigned long int infront = 0, hasdir = 1;
3126 if (path == NULL) return NULL;
3127 if (buf) rslt = buf;
3128 else if (ts) New(1316,rslt,strlen(path)+9,char);
3129 else rslt = __tovmsspec_retbuf;
3130 if (strpbrk(path,"]:>") ||
3131 (dirend = strrchr(path,'/')) == NULL) {
3132 if (path[0] == '.') {
3133 if (path[1] == '\0') strcpy(rslt,"[]");
3134 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
3135 else strcpy(rslt,path); /* probably garbage */
3137 else strcpy(rslt,path);
3140 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
3141 if (!*(dirend+2)) dirend +=2;
3142 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
3143 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
3148 char trndev[NAM$C_MAXRSS+1];
3152 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
3154 if (!buf & ts) Renew(rslt,18,char);
3155 strcpy(rslt,"sys$disk:[000000]");
3158 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
3160 islnm = my_trnlnm(rslt,trndev,0);
3161 trnend = islnm ? strlen(trndev) - 1 : 0;
3162 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
3163 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
3164 /* If the first element of the path is a logical name, determine
3165 * whether it has to be translated so we can add more directories. */
3166 if (!islnm || rooted) {
3169 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
3173 if (cp2 != dirend) {
3174 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
3175 strcpy(rslt,trndev);
3176 cp1 = rslt + trnend;
3189 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
3190 cp2 += 2; /* skip over "./" - it's redundant */
3191 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
3193 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3194 *(cp1++) = '-'; /* "../" --> "-" */
3197 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
3198 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
3199 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3200 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
3203 if (cp2 > dirend) cp2 = dirend;
3205 else *(cp1++) = '.';
3207 for (; cp2 < dirend; cp2++) {
3209 if (*(cp2-1) == '/') continue;
3210 if (*(cp1-1) != '.') *(cp1++) = '.';
3213 else if (!infront && *cp2 == '.') {
3214 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
3215 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
3216 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3217 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
3218 else if (*(cp1-2) == '[') *(cp1-1) = '-';
3219 else { /* back up over previous directory name */
3221 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
3222 if (*(cp1-1) == '[') {
3223 memcpy(cp1,"000000.",7);
3228 if (cp2 == dirend) break;
3230 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
3231 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
3232 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
3233 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3235 *(cp1++) = '.'; /* Simulate trailing '/' */
3236 cp2 += 2; /* for loop will incr this to == dirend */
3238 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
3240 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
3243 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
3244 if (*cp2 == '.') *(cp1++) = '_';
3245 else *(cp1++) = *cp2;
3249 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
3250 if (hasdir) *(cp1++) = ']';
3251 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
3252 while (*cp2) *(cp1++) = *(cp2++);
3257 } /* end of do_tovmsspec() */
3259 /* External entry points */
3260 char *Perl_tovmsspec(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,0); }
3261 char *Perl_tovmsspec_ts(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,1); }
3263 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
3264 static char *mp_do_tovmspath(pTHX_ char *path, char *buf, int ts) {
3265 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
3267 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
3269 if (path == NULL) return NULL;
3270 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3271 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
3272 if (buf) return buf;
3274 vmslen = strlen(vmsified);
3275 New(1317,cp,vmslen+1,char);
3276 memcpy(cp,vmsified,vmslen);
3281 strcpy(__tovmspath_retbuf,vmsified);
3282 return __tovmspath_retbuf;
3285 } /* end of do_tovmspath() */
3287 /* External entry points */
3288 char *Perl_tovmspath(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,0); }
3289 char *Perl_tovmspath_ts(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,1); }
3292 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
3293 static char *mp_do_tounixpath(pTHX_ char *path, char *buf, int ts) {
3294 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
3296 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
3298 if (path == NULL) return NULL;
3299 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3300 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
3301 if (buf) return buf;
3303 unixlen = strlen(unixified);
3304 New(1317,cp,unixlen+1,char);
3305 memcpy(cp,unixified,unixlen);
3310 strcpy(__tounixpath_retbuf,unixified);
3311 return __tounixpath_retbuf;
3314 } /* end of do_tounixpath() */
3316 /* External entry points */
3317 char *Perl_tounixpath(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,0); }
3318 char *Perl_tounixpath_ts(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,1); }
3321 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
3323 *****************************************************************************
3325 * Copyright (C) 1989-1994 by *
3326 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
3328 * Permission is hereby granted for the reproduction of this software, *
3329 * on condition that this copyright notice is included in the reproduction, *
3330 * and that such reproduction is not for purposes of profit or material *
3333 * 27-Aug-1994 Modified for inclusion in perl5 *
3334 * by Charles Bailey bailey@newman.upenn.edu *
3335 *****************************************************************************
3339 * getredirection() is intended to aid in porting C programs
3340 * to VMS (Vax-11 C). The native VMS environment does not support
3341 * '>' and '<' I/O redirection, or command line wild card expansion,
3342 * or a command line pipe mechanism using the '|' AND background
3343 * command execution '&'. All of these capabilities are provided to any
3344 * C program which calls this procedure as the first thing in the
3346 * The piping mechanism will probably work with almost any 'filter' type
3347 * of program. With suitable modification, it may useful for other
3348 * portability problems as well.
3350 * Author: Mark Pizzolato mark@infocomm.com
3354 struct list_item *next;
3358 static void add_item(struct list_item **head,
3359 struct list_item **tail,
3363 static void mp_expand_wild_cards(pTHX_ char *item,
3364 struct list_item **head,
3365 struct list_item **tail,
3368 static int background_process(int argc, char **argv);
3370 static void pipe_and_fork(char **cmargv);
3372 /*{{{ void getredirection(int *ac, char ***av)*/
3374 mp_getredirection(pTHX_ int *ac, char ***av)
3376 * Process vms redirection arg's. Exit if any error is seen.
3377 * If getredirection() processes an argument, it is erased
3378 * from the vector. getredirection() returns a new argc and argv value.
3379 * In the event that a background command is requested (by a trailing "&"),
3380 * this routine creates a background subprocess, and simply exits the program.
3382 * Warning: do not try to simplify the code for vms. The code
3383 * presupposes that getredirection() is called before any data is
3384 * read from stdin or written to stdout.
3386 * Normal usage is as follows:
3392 * getredirection(&argc, &argv);
3396 int argc = *ac; /* Argument Count */
3397 char **argv = *av; /* Argument Vector */
3398 char *ap; /* Argument pointer */
3399 int j; /* argv[] index */
3400 int item_count = 0; /* Count of Items in List */
3401 struct list_item *list_head = 0; /* First Item in List */
3402 struct list_item *list_tail; /* Last Item in List */
3403 char *in = NULL; /* Input File Name */
3404 char *out = NULL; /* Output File Name */
3405 char *outmode = "w"; /* Mode to Open Output File */
3406 char *err = NULL; /* Error File Name */
3407 char *errmode = "w"; /* Mode to Open Error File */
3408 int cmargc = 0; /* Piped Command Arg Count */
3409 char **cmargv = NULL;/* Piped Command Arg Vector */
3412 * First handle the case where the last thing on the line ends with
3413 * a '&'. This indicates the desire for the command to be run in a
3414 * subprocess, so we satisfy that desire.
3417 if (0 == strcmp("&", ap))
3418 exit(background_process(--argc, argv));
3419 if (*ap && '&' == ap[strlen(ap)-1])
3421 ap[strlen(ap)-1] = '\0';
3422 exit(background_process(argc, argv));
3425 * Now we handle the general redirection cases that involve '>', '>>',
3426 * '<', and pipes '|'.
3428 for (j = 0; j < argc; ++j)
3430 if (0 == strcmp("<", argv[j]))
3434 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
3435 exit(LIB$_WRONUMARG);
3440 if ('<' == *(ap = argv[j]))
3445 if (0 == strcmp(">", ap))
3449 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
3450 exit(LIB$_WRONUMARG);
3469 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
3470 exit(LIB$_WRONUMARG);
3474 if (('2' == *ap) && ('>' == ap[1]))
3491 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
3492 exit(LIB$_WRONUMARG);
3496 if (0 == strcmp("|", argv[j]))
3500 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
3501 exit(LIB$_WRONUMARG);
3503 cmargc = argc-(j+1);
3504 cmargv = &argv[j+1];
3508 if ('|' == *(ap = argv[j]))
3516 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
3519 * Allocate and fill in the new argument vector, Some Unix's terminate
3520 * the list with an extra null pointer.
3522 New(1302, argv, item_count+1, char *);
3524 for (j = 0; j < item_count; ++j, list_head = list_head->next)
3525 argv[j] = list_head->value;
3531 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
3532 exit(LIB$_INVARGORD);
3534 pipe_and_fork(cmargv);
3537 /* Check for input from a pipe (mailbox) */
3539 if (in == NULL && 1 == isapipe(0))
3541 char mbxname[L_tmpnam];
3543 long int dvi_item = DVI$_DEVBUFSIZ;
3544 $DESCRIPTOR(mbxnam, "");
3545 $DESCRIPTOR(mbxdevnam, "");
3547 /* Input from a pipe, reopen it in binary mode to disable */
3548 /* carriage control processing. */
3550 PerlIO_getname(stdin, mbxname);
3551 mbxnam.dsc$a_pointer = mbxname;
3552 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
3553 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
3554 mbxdevnam.dsc$a_pointer = mbxname;
3555 mbxdevnam.dsc$w_length = sizeof(mbxname);
3556 dvi_item = DVI$_DEVNAM;
3557 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
3558 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
3561 freopen(mbxname, "rb", stdin);
3564 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
3568 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
3570 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
3573 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
3575 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
3579 if (strcmp(err,"&1") == 0) {
3580 dup2(fileno(stdout), fileno(Perl_debug_log));
3583 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
3585 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
3589 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
3595 #ifdef ARGPROC_DEBUG
3596 PerlIO_printf(Perl_debug_log, "Arglist:\n");
3597 for (j = 0; j < *ac; ++j)
3598 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
3600 /* Clear errors we may have hit expanding wildcards, so they don't
3601 show up in Perl's $! later */
3602 set_errno(0); set_vaxc_errno(1);
3603 } /* end of getredirection() */
3606 static void add_item(struct list_item **head,
3607 struct list_item **tail,
3613 New(1303,*head,1,struct list_item);
3617 New(1304,(*tail)->next,1,struct list_item);
3618 *tail = (*tail)->next;
3620 (*tail)->value = value;
3624 static void mp_expand_wild_cards(pTHX_ char *item,
3625 struct list_item **head,
3626 struct list_item **tail,
3630 unsigned long int context = 0;
3636 char vmsspec[NAM$C_MAXRSS+1];
3637 $DESCRIPTOR(filespec, "");
3638 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
3639 $DESCRIPTOR(resultspec, "");
3640 unsigned long int zero = 0, sts;
3642 for (cp = item; *cp; cp++) {
3643 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
3644 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
3646 if (!*cp || isspace(*cp))
3648 add_item(head, tail, item, count);
3651 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
3652 resultspec.dsc$b_class = DSC$K_CLASS_D;
3653 resultspec.dsc$a_pointer = NULL;
3654 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
3655 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
3656 if (!isunix || !filespec.dsc$a_pointer)
3657 filespec.dsc$a_pointer = item;
3658 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
3660 * Only return version specs, if the caller specified a version
3662 had_version = strchr(item, ';');
3664 * Only return device and directory specs, if the caller specifed either.
3666 had_device = strchr(item, ':');
3667 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
3669 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
3670 &defaultspec, 0, 0, &zero))))
3675 New(1305,string,resultspec.dsc$w_length+1,char);
3676 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
3677 string[resultspec.dsc$w_length] = '\0';
3678 if (NULL == had_version)
3679 *((char *)strrchr(string, ';')) = '\0';
3680 if ((!had_directory) && (had_device == NULL))
3682 if (NULL == (devdir = strrchr(string, ']')))
3683 devdir = strrchr(string, '>');
3684 strcpy(string, devdir + 1);
3687 * Be consistent with what the C RTL has already done to the rest of
3688 * the argv items and lowercase all of these names.
3690 for (c = string; *c; ++c)
3693 if (isunix) trim_unixpath(string,item,1);
3694 add_item(head, tail, string, count);
3697 if (sts != RMS$_NMF)
3699 set_vaxc_errno(sts);
3702 case RMS$_FNF: case RMS$_DNF:
3703 set_errno(ENOENT); break;
3705 set_errno(ENOTDIR); break;
3707 set_errno(ENODEV); break;
3708 case RMS$_FNM: case RMS$_SYN:
3709 set_errno(EINVAL); break;
3711 set_errno(EACCES); break;
3713 _ckvmssts_noperl(sts);
3717 add_item(head, tail, item, count);
3718 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
3719 _ckvmssts_noperl(lib$find_file_end(&context));
3722 static int child_st[2];/* Event Flag set when child process completes */
3724 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
3726 static unsigned long int exit_handler(int *status)
3730 if (0 == child_st[0])
3732 #ifdef ARGPROC_DEBUG
3733 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
3735 fflush(stdout); /* Have to flush pipe for binary data to */
3736 /* terminate properly -- <tp@mccall.com> */
3737 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
3738 sys$dassgn(child_chan);
3740 sys$synch(0, child_st);
3745 static void sig_child(int chan)
3747 #ifdef ARGPROC_DEBUG
3748 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
3750 if (child_st[0] == 0)
3754 static struct exit_control_block exit_block =
3759 &exit_block.exit_status,
3763 static void pipe_and_fork(char **cmargv)
3766 $DESCRIPTOR(cmddsc, "");
3767 static char mbxname[64];
3768 $DESCRIPTOR(mbxdsc, mbxname);
3770 unsigned long int zero = 0, one = 1;
3772 strcpy(subcmd, cmargv[0]);
3773 for (j = 1; NULL != cmargv[j]; ++j)
3775 strcat(subcmd, " \"");
3776 strcat(subcmd, cmargv[j]);
3777 strcat(subcmd, "\"");
3779 cmddsc.dsc$a_pointer = subcmd;
3780 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
3782 create_mbx(&child_chan,&mbxdsc);
3783 #ifdef ARGPROC_DEBUG
3784 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
3785 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
3787 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
3788 0, &pid, child_st, &zero, sig_child,
3790 #ifdef ARGPROC_DEBUG
3791 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
3793 sys$dclexh(&exit_block);
3794 if (NULL == freopen(mbxname, "wb", stdout))
3796 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
3800 static int background_process(int argc, char **argv)
3802 char command[2048] = "$";
3803 $DESCRIPTOR(value, "");
3804 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
3805 static $DESCRIPTOR(null, "NLA0:");
3806 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
3808 $DESCRIPTOR(pidstr, "");
3810 unsigned long int flags = 17, one = 1, retsts;
3812 strcat(command, argv[0]);
3815 strcat(command, " \"");
3816 strcat(command, *(++argv));
3817 strcat(command, "\"");
3819 value.dsc$a_pointer = command;
3820 value.dsc$w_length = strlen(value.dsc$a_pointer);
3821 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
3822 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
3823 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
3824 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
3827 _ckvmssts_noperl(retsts);
3829 #ifdef ARGPROC_DEBUG
3830 PerlIO_printf(Perl_debug_log, "%s\n", command);
3832 sprintf(pidstring, "%08X", pid);
3833 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
3834 pidstr.dsc$a_pointer = pidstring;
3835 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
3836 lib$set_symbol(&pidsymbol, &pidstr);
3840 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
3843 /* OS-specific initialization at image activation (not thread startup) */
3844 /* Older VAXC header files lack these constants */
3845 #ifndef JPI$_RIGHTS_SIZE
3846 # define JPI$_RIGHTS_SIZE 817
3848 #ifndef KGB$M_SUBSYSTEM
3849 # define KGB$M_SUBSYSTEM 0x8
3852 /*{{{void vms_image_init(int *, char ***)*/
3854 vms_image_init(int *argcp, char ***argvp)
3856 char eqv[LNM$C_NAMLENGTH+1] = "";
3857 unsigned int len, tabct = 8, tabidx = 0;
3858 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
3859 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
3860 unsigned short int dummy, rlen;
3861 struct dsc$descriptor_s **tabvec;
3863 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
3864 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
3865 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
3868 _ckvmssts(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
3870 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
3871 if (iprv[i]) { /* Running image installed with privs? */
3872 _ckvmssts(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
3877 /* Rights identifiers might trigger tainting as well. */
3878 if (!will_taint && (rlen || rsz)) {
3879 while (rlen < rsz) {
3880 /* We didn't get all the identifiers on the first pass. Allocate a
3881 * buffer much larger than $GETJPI wants (rsz is size in bytes that
3882 * were needed to hold all identifiers at time of last call; we'll
3883 * allocate that many unsigned long ints), and go back and get 'em.
3884 * If it gave us less than it wanted to despite ample buffer space,
3885 * something's broken. Is your system missing a system identifier?
3887 if (rsz <= jpilist[1].buflen) {
3888 /* Perl_croak accvios when used this early in startup. */
3889 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
3890 rsz, (unsigned long) jpilist[1].buflen,
3891 "Check your rights database for corruption.\n");
3894 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
3895 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
3896 jpilist[1].buflen = rsz * sizeof(unsigned long int);
3897 _ckvmssts(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
3900 mask = jpilist[1].bufadr;
3901 /* Check attribute flags for each identifier (2nd longword); protected
3902 * subsystem identifiers trigger tainting.
3904 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
3905 if (mask[i] & KGB$M_SUBSYSTEM) {
3910 if (mask != rlst) Safefree(mask);
3912 /* We need to use this hack to tell Perl it should run with tainting,
3913 * since its tainting flag may be part of the PL_curinterp struct, which
3914 * hasn't been allocated when vms_image_init() is called.
3918 New(1320,newap,*argcp+2,char **);
3919 newap[0] = argvp[0];
3921 Copy(argvp[1],newap[2],*argcp-1,char **);
3922 /* We orphan the old argv, since we don't know where it's come from,
3923 * so we don't know how to free it.
3925 *argcp++; argvp = newap;
3927 else { /* Did user explicitly request tainting? */
3929 char *cp, **av = *argvp;
3930 for (i = 1; i < *argcp; i++) {
3931 if (*av[i] != '-') break;
3932 for (cp = av[i]+1; *cp; cp++) {
3933 if (*cp == 'T') { will_taint = 1; break; }
3934 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
3935 strchr("DFIiMmx",*cp)) break;
3937 if (will_taint) break;
3942 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
3944 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
3945 else if (tabidx >= tabct) {
3947 Renew(tabvec,tabct,struct dsc$descriptor_s *);
3949 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
3950 tabvec[tabidx]->dsc$w_length = 0;
3951 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
3952 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
3953 tabvec[tabidx]->dsc$a_pointer = NULL;
3954 _ckvmssts(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
3956 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
3958 getredirection(argcp,argvp);
3959 #if defined(USE_THREADS) && ( defined(__DECC) || defined(__DECCXX) )
3961 # include <reentrancy.h>
3962 (void) decc$set_reentrancy(C$C_MULTITHREAD);
3971 * Trim Unix-style prefix off filespec, so it looks like what a shell
3972 * glob expansion would return (i.e. from specified prefix on, not
3973 * full path). Note that returned filespec is Unix-style, regardless
3974 * of whether input filespec was VMS-style or Unix-style.
3976 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
3977 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
3978 * vector of options; at present, only bit 0 is used, and if set tells
3979 * trim unixpath to try the current default directory as a prefix when
3980 * presented with a possibly ambiguous ... wildcard.
3982 * Returns !=0 on success, with trimmed filespec replacing contents of
3983 * fspec, and 0 on failure, with contents of fpsec unchanged.
3985 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
3987 Perl_trim_unixpath(pTHX_ char *fspec, char *wildspec, int opts)
3989 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
3990 *template, *base, *end, *cp1, *cp2;
3991 register int tmplen, reslen = 0, dirs = 0;
3993 if (!wildspec || !fspec) return 0;
3994 if (strpbrk(wildspec,"]>:") != NULL) {
3995 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
3996 else template = unixwild;
3998 else template = wildspec;
3999 if (strpbrk(fspec,"]>:") != NULL) {
4000 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
4001 else base = unixified;
4002 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
4003 * check to see that final result fits into (isn't longer than) fspec */
4004 reslen = strlen(fspec);
4008 /* No prefix or absolute path on wildcard, so nothing to remove */
4009 if (!*template || *template == '/') {
4010 if (base == fspec) return 1;
4011 tmplen = strlen(unixified);
4012 if (tmplen > reslen) return 0; /* not enough space */
4013 /* Copy unixified resultant, including trailing NUL */
4014 memmove(fspec,unixified,tmplen+1);
4018 for (end = base; *end; end++) ; /* Find end of resultant filespec */
4019 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
4020 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
4021 for (cp1 = end ;cp1 >= base; cp1--)
4022 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
4024 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
4028 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
4029 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
4030 int ells = 1, totells, segdirs, match;
4031 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
4032 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4034 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
4036 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
4037 if (ellipsis == template && opts & 1) {
4038 /* Template begins with an ellipsis. Since we can't tell how many
4039 * directory names at the front of the resultant to keep for an
4040 * arbitrary starting point, we arbitrarily choose the current
4041 * default directory as a starting point. If it's there as a prefix,
4042 * clip it off. If not, fall through and act as if the leading
4043 * ellipsis weren't there (i.e. return shortest possible path that
4044 * could match template).
4046 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
4047 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4048 if (_tolower(*cp1) != _tolower(*cp2)) break;
4049 segdirs = dirs - totells; /* Min # of dirs we must have left */
4050 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
4051 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
4052 memcpy(fspec,cp2+1,end - cp2);
4056 /* First off, back up over constant elements at end of path */
4058 for (front = end ; front >= base; front--)
4059 if (*front == '/' && !dirs--) { front++; break; }
4061 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
4062 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
4063 if (cp1 != '\0') return 0; /* Path too long. */
4065 *cp2 = '\0'; /* Pick up with memcpy later */
4066 lcfront = lcres + (front - base);
4067 /* Now skip over each ellipsis and try to match the path in front of it. */
4069 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
4070 if (*(cp1) == '.' && *(cp1+1) == '.' &&
4071 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
4072 if (cp1 < template) break; /* template started with an ellipsis */
4073 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
4074 ellipsis = cp1; continue;
4076 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
4078 for (segdirs = 0, cp2 = tpl;
4079 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
4081 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
4082 else *cp2 = _tolower(*cp1); /* else lowercase for match */
4083 if (*cp2 == '/') segdirs++;
4085 if (cp1 != ellipsis - 1) return 0; /* Path too long */
4086 /* Back up at least as many dirs as in template before matching */
4087 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
4088 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
4089 for (match = 0; cp1 > lcres;) {
4090 resdsc.dsc$a_pointer = cp1;
4091 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
4093 if (match == 1) lcfront = cp1;
4095 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
4097 if (!match) return 0; /* Can't find prefix ??? */
4098 if (match > 1 && opts & 1) {
4099 /* This ... wildcard could cover more than one set of dirs (i.e.
4100 * a set of similar dir names is repeated). If the template
4101 * contains more than 1 ..., upstream elements could resolve the
4102 * ambiguity, but it's not worth a full backtracking setup here.
4103 * As a quick heuristic, clip off the current default directory
4104 * if it's present to find the trimmed spec, else use the
4105 * shortest string that this ... could cover.
4107 char def[NAM$C_MAXRSS+1], *st;
4109 if (getcwd(def, sizeof def,0) == NULL) return 0;
4110 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4111 if (_tolower(*cp1) != _tolower(*cp2)) break;
4112 segdirs = dirs - totells; /* Min # of dirs we must have left */
4113 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
4114 if (*cp1 == '\0' && *cp2 == '/') {
4115 memcpy(fspec,cp2+1,end - cp2);
4118 /* Nope -- stick with lcfront from above and keep going. */
4121 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
4126 } /* end of trim_unixpath() */
4131 * VMS readdir() routines.
4132 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
4134 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
4135 * Minor modifications to original routines.
4138 /* Number of elements in vms_versions array */
4139 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
4142 * Open a directory, return a handle for later use.
4144 /*{{{ DIR *opendir(char*name) */
4146 Perl_opendir(pTHX_ char *name)
4149 char dir[NAM$C_MAXRSS+1];
4152 if (do_tovmspath(name,dir,0) == NULL) {
4155 if (flex_stat(dir,&sb) == -1) return NULL;
4156 if (!S_ISDIR(sb.st_mode)) {
4157 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
4160 if (!cando_by_name(S_IRUSR,0,dir)) {
4161 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
4164 /* Get memory for the handle, and the pattern. */
4166 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
4168 /* Fill in the fields; mainly playing with the descriptor. */
4169 (void)sprintf(dd->pattern, "%s*.*",dir);
4172 dd->vms_wantversions = 0;
4173 dd->pat.dsc$a_pointer = dd->pattern;
4174 dd->pat.dsc$w_length = strlen(dd->pattern);
4175 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
4176 dd->pat.dsc$b_class = DSC$K_CLASS_S;
4179 } /* end of opendir() */
4183 * Set the flag to indicate we want versions or not.
4185 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
4187 vmsreaddirversions(DIR *dd, int flag)
4189 dd->vms_wantversions = flag;
4194 * Free up an opened directory.
4196 /*{{{ void closedir(DIR *dd)*/
4200 (void)lib$find_file_end(&dd->context);
4201 Safefree(dd->pattern);
4202 Safefree((char *)dd);
4207 * Collect all the version numbers for the current file.
4213 struct dsc$descriptor_s pat;
4214 struct dsc$descriptor_s res;
4216 char *p, *text, buff[sizeof dd->entry.d_name];
4218 unsigned long context, tmpsts;
4221 /* Convenient shorthand. */
4224 /* Add the version wildcard, ignoring the "*.*" put on before */
4225 i = strlen(dd->pattern);
4226 New(1308,text,i + e->d_namlen + 3,char);
4227 (void)strcpy(text, dd->pattern);
4228 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
4230 /* Set up the pattern descriptor. */
4231 pat.dsc$a_pointer = text;
4232 pat.dsc$w_length = i + e->d_namlen - 1;
4233 pat.dsc$b_dtype = DSC$K_DTYPE_T;
4234 pat.dsc$b_class = DSC$K_CLASS_S;
4236 /* Set up result descriptor. */
4237 res.dsc$a_pointer = buff;
4238 res.dsc$w_length = sizeof buff - 2;
4239 res.dsc$b_dtype = DSC$K_DTYPE_T;
4240 res.dsc$b_class = DSC$K_CLASS_S;
4242 /* Read files, collecting versions. */
4243 for (context = 0, e->vms_verscount = 0;
4244 e->vms_verscount < VERSIZE(e);
4245 e->vms_verscount++) {
4246 tmpsts = lib$find_file(&pat, &res, &context);
4247 if (tmpsts == RMS$_NMF || context == 0) break;
4249 buff[sizeof buff - 1] = '\0';
4250 if ((p = strchr(buff, ';')))
4251 e->vms_versions[e->vms_verscount] = atoi(p + 1);
4253 e->vms_versions[e->vms_verscount] = -1;
4256 _ckvmssts(lib$find_file_end(&context));
4259 } /* end of collectversions() */
4262 * Read the next entry from the directory.
4264 /*{{{ struct dirent *readdir(DIR *dd)*/
4268 struct dsc$descriptor_s res;
4269 char *p, buff[sizeof dd->entry.d_name];
4270 unsigned long int tmpsts;
4272 /* Set up result descriptor, and get next file. */
4273 res.dsc$a_pointer = buff;
4274 res.dsc$w_length = sizeof buff - 2;
4275 res.dsc$b_dtype = DSC$K_DTYPE_T;
4276 res.dsc$b_class = DSC$K_CLASS_S;
4277 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4278 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
4279 if (!(tmpsts & 1)) {
4280 set_vaxc_errno(tmpsts);
4283 set_errno(EACCES); break;
4285 set_errno(ENODEV); break;
4287 set_errno(ENOTDIR); break;
4288 case RMS$_FNF: case RMS$_DNF:
4289 set_errno(ENOENT); break;
4296 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
4297 buff[sizeof buff - 1] = '\0';
4298 for (p = buff; *p; p++) *p = _tolower(*p);
4299 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
4302 /* Skip any directory component and just copy the name. */
4303 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
4304 else (void)strcpy(dd->entry.d_name, buff);
4306 /* Clobber the version. */
4307 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
4309 dd->entry.d_namlen = strlen(dd->entry.d_name);
4310 dd->entry.vms_verscount = 0;
4311 if (dd->vms_wantversions) collectversions(dd);
4314 } /* end of readdir() */
4318 * Return something that can be used in a seekdir later.
4320 /*{{{ long telldir(DIR *dd)*/
4329 * Return to a spot where we used to be. Brute force.
4331 /*{{{ void seekdir(DIR *dd,long count)*/
4333 seekdir(DIR *dd, long count)
4335 int vms_wantversions;
4338 /* If we haven't done anything yet... */
4342 /* Remember some state, and clear it. */
4343 vms_wantversions = dd->vms_wantversions;
4344 dd->vms_wantversions = 0;
4345 _ckvmssts(lib$find_file_end(&dd->context));
4348 /* The increment is in readdir(). */
4349 for (dd->count = 0; dd->count < count; )
4352 dd->vms_wantversions = vms_wantversions;
4354 } /* end of seekdir() */
4357 /* VMS subprocess management
4359 * my_vfork() - just a vfork(), after setting a flag to record that
4360 * the current script is trying a Unix-style fork/exec.
4362 * vms_do_aexec() and vms_do_exec() are called in response to the
4363 * perl 'exec' function. If this follows a vfork call, then they
4364 * call out the the regular perl routines in doio.c which do an
4365 * execvp (for those who really want to try this under VMS).
4366 * Otherwise, they do exactly what the perl docs say exec should
4367 * do - terminate the current script and invoke a new command
4368 * (See below for notes on command syntax.)
4370 * do_aspawn() and do_spawn() implement the VMS side of the perl
4371 * 'system' function.
4373 * Note on command arguments to perl 'exec' and 'system': When handled
4374 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
4375 * are concatenated to form a DCL command string. If the first arg
4376 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
4377 * the the command string is handed off to DCL directly. Otherwise,
4378 * the first token of the command is taken as the filespec of an image
4379 * to run. The filespec is expanded using a default type of '.EXE' and
4380 * the process defaults for device, directory, etc., and if found, the resultant
4381 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
4382 * the command string as parameters. This is perhaps a bit complicated,
4383 * but I hope it will form a happy medium between what VMS folks expect
4384 * from lib$spawn and what Unix folks expect from exec.
4387 static int vfork_called;
4389 /*{{{int my_vfork()*/
4400 vms_execfree(pTHX) {
4402 if (PL_Cmd != VMScmd.dsc$a_pointer) Safefree(PL_Cmd);
4405 if (VMScmd.dsc$a_pointer) {
4406 Safefree(VMScmd.dsc$a_pointer);
4407 VMScmd.dsc$w_length = 0;
4408 VMScmd.dsc$a_pointer = Nullch;
4413 setup_argstr(SV *really, SV **mark, SV **sp)
4416 char *junk, *tmps = Nullch;
4417 register size_t cmdlen = 0;
4424 tmps = SvPV(really,rlen);
4431 for (idx++; idx <= sp; idx++) {
4433 junk = SvPVx(*idx,rlen);
4434 cmdlen += rlen ? rlen + 1 : 0;
4437 New(401,PL_Cmd,cmdlen+1,char);
4439 if (tmps && *tmps) {
4440 strcpy(PL_Cmd,tmps);
4443 else *PL_Cmd = '\0';
4444 while (++mark <= sp) {
4446 char *s = SvPVx(*mark,n_a);
4448 if (*PL_Cmd) strcat(PL_Cmd," ");
4454 } /* end of setup_argstr() */
4457 static unsigned long int
4458 setup_cmddsc(char *cmd, int check_img)
4460 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
4461 $DESCRIPTOR(defdsc,".EXE");
4462 $DESCRIPTOR(defdsc2,".");
4463 $DESCRIPTOR(resdsc,resspec);
4464 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4465 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
4466 register char *s, *rest, *cp, *wordbreak;
4471 (sizeof(vmsspec) > sizeof(resspec) ? sizeof(resspec) : sizeof(vmsspec)))
4474 while (*s && isspace(*s)) s++;
4476 if (*s == '@' || *s == '$') {
4477 vmsspec[0] = *s; rest = s + 1;
4478 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
4480 else { cp = vmsspec; rest = s; }
4481 if (*rest == '.' || *rest == '/') {
4484 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
4485 rest++, cp2++) *cp2 = *rest;
4487 if (do_tovmsspec(resspec,cp,0)) {
4490 for (cp2 = vmsspec + strlen(vmsspec);
4491 *rest && cp2 - vmsspec < sizeof vmsspec;
4492 rest++, cp2++) *cp2 = *rest;
4497 /* Intuit whether verb (first word of cmd) is a DCL command:
4498 * - if first nonspace char is '@', it's a DCL indirection
4500 * - if verb contains a filespec separator, it's not a DCL command
4501 * - if it doesn't, caller tells us whether to default to a DCL
4502 * command, or to a local image unless told it's DCL (by leading '$')
4504 if (*s == '@') isdcl = 1;
4506 register char *filespec = strpbrk(s,":<[.;");
4507 rest = wordbreak = strpbrk(s," \"\t/");
4508 if (!wordbreak) wordbreak = s + strlen(s);
4509 if (*s == '$') check_img = 0;
4510 if (filespec && (filespec < wordbreak)) isdcl = 0;
4511 else isdcl = !check_img;
4515 imgdsc.dsc$a_pointer = s;
4516 imgdsc.dsc$w_length = wordbreak - s;
4517 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4519 _ckvmssts(lib$find_file_end(&cxt));
4520 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
4521 if (!(retsts & 1) && *s == '$') {
4522 _ckvmssts(lib$find_file_end(&cxt));
4523 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
4524 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4526 _ckvmssts(lib$find_file_end(&cxt));
4527 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
4531 _ckvmssts(lib$find_file_end(&cxt));
4536 while (*s && !isspace(*s)) s++;
4539 /* check that it's really not DCL with no file extension */
4540 fp = fopen(resspec,"r","ctx=bin,shr=get");
4542 char b[4] = {0,0,0,0};
4543 read(fileno(fp),b,4);
4544 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
4547 if (check_img && isdcl) return RMS$_FNF;
4549 if (cando_by_name(S_IXUSR,0,resspec)) {
4550 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
4552 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
4554 strcpy(VMScmd.dsc$a_pointer,"@");
4556 strcat(VMScmd.dsc$a_pointer,resspec);
4557 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
4558 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
4561 else retsts = RMS$_PRV;
4564 /* It's either a DCL command or we couldn't find a suitable image */
4565 VMScmd.dsc$w_length = strlen(cmd);
4566 if (cmd == PL_Cmd) VMScmd.dsc$a_pointer = PL_Cmd;
4567 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
4568 if (!(retsts & 1)) {
4569 /* just hand off status values likely to be due to user error */
4570 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
4571 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
4572 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
4573 else { _ckvmssts(retsts); }
4576 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : retsts);
4578 } /* end of setup_cmddsc() */
4581 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
4583 vms_do_aexec(SV *really,SV **mark,SV **sp)
4587 if (vfork_called) { /* this follows a vfork - act Unixish */
4589 if (vfork_called < 0) {
4590 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
4593 else return do_aexec(really,mark,sp);
4595 /* no vfork - act VMSish */
4596 return vms_do_exec(setup_argstr(really,mark,sp));
4601 } /* end of vms_do_aexec() */
4604 /* {{{bool vms_do_exec(char *cmd) */
4606 vms_do_exec(char *cmd)
4610 if (vfork_called) { /* this follows a vfork - act Unixish */
4612 if (vfork_called < 0) {
4613 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
4616 else return do_exec(cmd);
4619 { /* no vfork - act VMSish */
4620 unsigned long int retsts;
4623 TAINT_PROPER("exec");
4624 if ((retsts = setup_cmddsc(cmd,1)) & 1)
4625 retsts = lib$do_command(&VMScmd);
4628 case RMS$_FNF: case RMS$_DNF:
4629 set_errno(ENOENT); break;
4631 set_errno(ENOTDIR); break;
4633 set_errno(ENODEV); break;
4635 set_errno(EACCES); break;
4637 set_errno(EINVAL); break;
4639 set_errno(E2BIG); break;
4640 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4641 _ckvmssts(retsts); /* fall through */
4642 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4645 set_vaxc_errno(retsts);
4646 if (ckWARN(WARN_EXEC)) {
4647 Perl_warner(aTHX_ WARN_EXEC,"Can't exec \"%*s\": %s",
4648 VMScmd.dsc$w_length, VMScmd.dsc$a_pointer, Strerror(errno));
4655 } /* end of vms_do_exec() */
4658 unsigned long int do_spawn(char *);
4660 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
4662 do_aspawn(void *really,void **mark,void **sp)
4665 if (sp > mark) return do_spawn(setup_argstr((SV *)really,(SV **)mark,(SV **)sp));
4668 } /* end of do_aspawn() */
4671 /* {{{unsigned long int do_spawn(char *cmd) */
4675 unsigned long int sts, substs, hadcmd = 1;
4679 TAINT_PROPER("spawn");
4680 if (!cmd || !*cmd) {
4682 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
4684 else if ((sts = setup_cmddsc(cmd,0)) & 1) {
4685 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
4690 case RMS$_FNF: case RMS$_DNF:
4691 set_errno(ENOENT); break;
4693 set_errno(ENOTDIR); break;
4695 set_errno(ENODEV); break;
4697 set_errno(EACCES); break;
4699 set_errno(EINVAL); break;
4701 set_errno(E2BIG); break;
4702 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4703 _ckvmssts(sts); /* fall through */
4704 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4707 set_vaxc_errno(sts);
4708 if (ckWARN(WARN_EXEC)) {
4709 Perl_warner(aTHX_ WARN_EXEC,"Can't spawn \"%*s\": %s",
4710 hadcmd ? VMScmd.dsc$w_length : 0,
4711 hadcmd ? VMScmd.dsc$a_pointer : "",
4718 } /* end of do_spawn() */
4722 * A simple fwrite replacement which outputs itmsz*nitm chars without
4723 * introducing record boundaries every itmsz chars.
4724 * We are using fputs, which depends on a terminating null. We may
4725 * well be writing binary data, so we need to accommodate not only
4726 * data with nulls sprinkled in the middle but also data with no null
4729 /*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
4731 my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
4733 register char *cp, *end, *cpd, *data;
4735 int bufsize = itmsz*nitm+1;
4737 _ckvmssts_noperl(lib$get_vm( &bufsize, &data ));
4738 memcpy( data, src, itmsz*nitm );
4739 data[itmsz*nitm] = '\0';
4741 end = data + itmsz * nitm;
4742 retval = (int) nitm; /* on success return # items written */
4745 while (cpd <= end) {
4746 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
4747 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
4749 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
4753 if (data) _ckvmssts_noperl(lib$free_vm( &bufsize, &data ));
4756 } /* end of my_fwrite() */
4759 /*{{{ int my_flush(FILE *fp)*/
4764 if ((res = fflush(fp)) == 0 && fp) {
4765 #ifdef VMS_DO_SOCKETS
4767 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
4769 res = fsync(fileno(fp));
4772 * If the flush succeeded but set end-of-file, we need to clear
4773 * the error because our caller may check ferror(). BTW, this
4774 * probably means we just flushed an empty file.
4776 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
4783 * Here are replacements for the following Unix routines in the VMS environment:
4784 * getpwuid Get information for a particular UIC or UID
4785 * getpwnam Get information for a named user
4786 * getpwent Get information for each user in the rights database
4787 * setpwent Reset search to the start of the rights database
4788 * endpwent Finish searching for users in the rights database
4790 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
4791 * (defined in pwd.h), which contains the following fields:-
4793 * char *pw_name; Username (in lower case)
4794 * char *pw_passwd; Hashed password
4795 * unsigned int pw_uid; UIC
4796 * unsigned int pw_gid; UIC group number
4797 * char *pw_unixdir; Default device/directory (VMS-style)
4798 * char *pw_gecos; Owner name
4799 * char *pw_dir; Default device/directory (Unix-style)
4800 * char *pw_shell; Default CLI name (eg. DCL)
4802 * If the specified user does not exist, getpwuid and getpwnam return NULL.
4804 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
4805 * not the UIC member number (eg. what's returned by getuid()),
4806 * getpwuid() can accept either as input (if uid is specified, the caller's
4807 * UIC group is used), though it won't recognise gid=0.
4809 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
4810 * information about other users in your group or in other groups, respectively.
4811 * If the required privilege is not available, then these routines fill only
4812 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
4815 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
4818 /* sizes of various UAF record fields */
4819 #define UAI$S_USERNAME 12
4820 #define UAI$S_IDENT 31
4821 #define UAI$S_OWNER 31
4822 #define UAI$S_DEFDEV 31
4823 #define UAI$S_DEFDIR 63
4824 #define UAI$S_DEFCLI 31
4827 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
4828 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
4829 (uic).uic$v_group != UIC$K_WILD_GROUP)
4831 static char __empty[]= "";
4832 static struct passwd __passwd_empty=
4833 {(char *) __empty, (char *) __empty, 0, 0,
4834 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
4835 static int contxt= 0;
4836 static struct passwd __pwdcache;
4837 static char __pw_namecache[UAI$S_IDENT+1];
4840 * This routine does most of the work extracting the user information.
4842 static int fillpasswd (const char *name, struct passwd *pwd)
4846 unsigned char length;
4847 char pw_gecos[UAI$S_OWNER+1];
4849 static union uicdef uic;
4851 unsigned char length;
4852 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
4855 unsigned char length;
4856 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
4859 unsigned char length;
4860 char pw_shell[UAI$S_DEFCLI+1];
4862 static char pw_passwd[UAI$S_PWD+1];
4864 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
4865 struct dsc$descriptor_s name_desc;
4866 unsigned long int sts;
4868 static struct itmlst_3 itmlst[]= {
4869 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
4870 {sizeof(uic), UAI$_UIC, &uic, &luic},
4871 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
4872 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
4873 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
4874 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
4875 {0, 0, NULL, NULL}};
4877 name_desc.dsc$w_length= strlen(name);
4878 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
4879 name_desc.dsc$b_class= DSC$K_CLASS_S;
4880 name_desc.dsc$a_pointer= (char *) name;
4882 /* Note that sys$getuai returns many fields as counted strings. */
4883 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
4884 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
4885 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
4887 else { _ckvmssts(sts); }
4888 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
4890 if ((int) owner.length < lowner) lowner= (int) owner.length;
4891 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
4892 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
4893 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
4894 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
4895 owner.pw_gecos[lowner]= '\0';
4896 defdev.pw_dir[ldefdev+ldefdir]= '\0';
4897 defcli.pw_shell[ldefcli]= '\0';
4898 if (valid_uic(uic)) {
4899 pwd->pw_uid= uic.uic$l_uic;
4900 pwd->pw_gid= uic.uic$v_group;
4903 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
4904 pwd->pw_passwd= pw_passwd;
4905 pwd->pw_gecos= owner.pw_gecos;
4906 pwd->pw_dir= defdev.pw_dir;
4907 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
4908 pwd->pw_shell= defcli.pw_shell;
4909 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
4911 ldir= strlen(pwd->pw_unixdir) - 1;
4912 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
4915 strcpy(pwd->pw_unixdir, pwd->pw_dir);
4916 __mystrtolower(pwd->pw_unixdir);
4921 * Get information for a named user.
4923 /*{{{struct passwd *getpwnam(char *name)*/
4924 struct passwd *my_getpwnam(char *name)
4926 struct dsc$descriptor_s name_desc;
4928 unsigned long int status, sts;
4931 __pwdcache = __passwd_empty;
4932 if (!fillpasswd(name, &__pwdcache)) {
4933 /* We still may be able to determine pw_uid and pw_gid */
4934 name_desc.dsc$w_length= strlen(name);
4935 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
4936 name_desc.dsc$b_class= DSC$K_CLASS_S;
4937 name_desc.dsc$a_pointer= (char *) name;
4938 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
4939 __pwdcache.pw_uid= uic.uic$l_uic;
4940 __pwdcache.pw_gid= uic.uic$v_group;
4943 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
4944 set_vaxc_errno(sts);
4945 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
4948 else { _ckvmssts(sts); }
4951 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
4952 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
4953 __pwdcache.pw_name= __pw_namecache;
4955 } /* end of my_getpwnam() */
4959 * Get information for a particular UIC or UID.
4960 * Called by my_getpwent with uid=-1 to list all users.
4962 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
4963 struct passwd *my_getpwuid(Uid_t uid)
4965 const $DESCRIPTOR(name_desc,__pw_namecache);
4966 unsigned short lname;
4968 unsigned long int status;
4971 if (uid == (unsigned int) -1) {
4973 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
4974 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
4975 set_vaxc_errno(status);
4976 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
4980 else { _ckvmssts(status); }
4981 } while (!valid_uic (uic));
4985 if (!uic.uic$v_group)
4986 uic.uic$v_group= PerlProc_getgid();
4988 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
4989 else status = SS$_IVIDENT;
4990 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
4991 status == RMS$_PRV) {
4992 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
4995 else { _ckvmssts(status); }
4997 __pw_namecache[lname]= '\0';
4998 __mystrtolower(__pw_namecache);
5000 __pwdcache = __passwd_empty;
5001 __pwdcache.pw_name = __pw_namecache;
5003 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
5004 The identifier's value is usually the UIC, but it doesn't have to be,
5005 so if we can, we let fillpasswd update this. */
5006 __pwdcache.pw_uid = uic.uic$l_uic;
5007 __pwdcache.pw_gid = uic.uic$v_group;
5009 fillpasswd(__pw_namecache, &__pwdcache);
5012 } /* end of my_getpwuid() */
5016 * Get information for next user.
5018 /*{{{struct passwd *my_getpwent()*/
5019 struct passwd *my_getpwent()
5021 return (my_getpwuid((unsigned int) -1));
5026 * Finish searching rights database for users.
5028 /*{{{void my_endpwent()*/
5033 _ckvmssts(sys$finish_rdb(&contxt));
5039 #ifdef HOMEGROWN_POSIX_SIGNALS
5040 /* Signal handling routines, pulled into the core from POSIX.xs.
5042 * We need these for threads, so they've been rolled into the core,
5043 * rather than left in POSIX.xs.
5045 * (DRS, Oct 23, 1997)
5048 /* sigset_t is atomic under VMS, so these routines are easy */
5049 /*{{{int my_sigemptyset(sigset_t *) */
5050 int my_sigemptyset(sigset_t *set) {
5051 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5057 /*{{{int my_sigfillset(sigset_t *)*/
5058 int my_sigfillset(sigset_t *set) {
5060 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5061 for (i = 0; i < NSIG; i++) *set |= (1 << i);
5067 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
5068 int my_sigaddset(sigset_t *set, int sig) {
5069 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5070 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5071 *set |= (1 << (sig - 1));
5077 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
5078 int my_sigdelset(sigset_t *set, int sig) {
5079 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5080 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5081 *set &= ~(1 << (sig - 1));
5087 /*{{{int my_sigismember(sigset_t *set, int sig)*/
5088 int my_sigismember(sigset_t *set, int sig) {
5089 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5090 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5091 *set & (1 << (sig - 1));
5096 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
5097 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
5100 /* If set and oset are both null, then things are badly wrong. Bail out. */
5101 if ((oset == NULL) && (set == NULL)) {
5102 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5106 /* If set's null, then we're just handling a fetch. */
5108 tempmask = sigblock(0);
5113 tempmask = sigsetmask(*set);
5116 tempmask = sigblock(*set);
5119 tempmask = sigblock(0);
5120 sigsetmask(*oset & ~tempmask);
5123 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5128 /* Did they pass us an oset? If so, stick our holding mask into it */
5135 #endif /* HOMEGROWN_POSIX_SIGNALS */
5138 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
5139 * my_utime(), and flex_stat(), all of which operate on UTC unless
5140 * VMSISH_TIMES is true.
5142 /* method used to handle UTC conversions:
5143 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
5145 static int gmtime_emulation_type;
5146 /* number of secs to add to UTC POSIX-style time to get local time */
5147 static long int utc_offset_secs;
5149 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
5150 * in vmsish.h. #undef them here so we can call the CRTL routines
5159 * DEC C previous to 6.0 corrupts the behavior of the /prefix
5160 * qualifier with the extern prefix pragma. This provisional
5161 * hack circumvents this prefix pragma problem in previous
5164 #if defined(__VMS_VER) && __VMS_VER >= 70000000
5165 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
5166 # pragma __extern_prefix save
5167 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
5168 # define gmtime decc$__utctz_gmtime
5169 # define localtime decc$__utctz_localtime
5170 # define time decc$__utc_time
5171 # pragma __extern_prefix restore
5173 struct tm *gmtime(), *localtime();
5179 static time_t toutc_dst(time_t loc) {
5182 if ((rsltmp = localtime(&loc)) == NULL) return -1;
5183 loc -= utc_offset_secs;
5184 if (rsltmp->tm_isdst) loc -= 3600;
5187 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5188 ((gmtime_emulation_type || my_time(NULL)), \
5189 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
5190 ((secs) - utc_offset_secs))))
5192 static time_t toloc_dst(time_t utc) {
5195 utc += utc_offset_secs;
5196 if ((rsltmp = localtime(&utc)) == NULL) return -1;
5197 if (rsltmp->tm_isdst) utc += 3600;
5200 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5201 ((gmtime_emulation_type || my_time(NULL)), \
5202 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
5203 ((secs) + utc_offset_secs))))
5205 #ifndef RTL_USES_UTC
5208 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
5209 DST starts on 1st sun of april at 02:00 std time
5210 ends on last sun of october at 02:00 dst time
5211 see the UCX management command reference, SET CONFIG TIMEZONE
5212 for formatting info.
5214 No, it's not as general as it should be, but then again, NOTHING
5215 will handle UK times in a sensible way.
5220 parse the DST start/end info:
5221 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
5225 tz_parse_startend(char *s, struct tm *w, int *past)
5227 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
5228 int ly, dozjd, d, m, n, hour, min, sec, j, k;
5233 if (!past) return 0;
5236 if (w->tm_year % 4 == 0) ly = 1;
5237 if (w->tm_year % 100 == 0) ly = 0;
5238 if (w->tm_year+1900 % 400 == 0) ly = 1;
5241 dozjd = isdigit(*s);
5242 if (*s == 'J' || *s == 'j' || dozjd) {
5243 if (!dozjd && !isdigit(*++s)) return 0;
5246 d = d*10 + *s++ - '0';
5248 d = d*10 + *s++ - '0';
5251 if (d == 0) return 0;
5252 if (d > 366) return 0;
5254 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
5257 } else if (*s == 'M' || *s == 'm') {
5258 if (!isdigit(*++s)) return 0;
5260 if (isdigit(*s)) m = 10*m + *s++ - '0';
5261 if (*s != '.') return 0;
5262 if (!isdigit(*++s)) return 0;
5264 if (n < 1 || n > 5) return 0;
5265 if (*s != '.') return 0;
5266 if (!isdigit(*++s)) return 0;
5268 if (d > 6) return 0;
5272 if (!isdigit(*++s)) return 0;
5274 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
5276 if (!isdigit(*++s)) return 0;
5278 if (isdigit(*s)) min = 10*min + *s++ - '0';
5280 if (!isdigit(*++s)) return 0;
5282 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
5292 if (w->tm_yday < d) goto before;
5293 if (w->tm_yday > d) goto after;
5295 if (w->tm_mon+1 < m) goto before;
5296 if (w->tm_mon+1 > m) goto after;
5298 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
5299 k = d - j; /* mday of first d */
5301 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
5302 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
5303 if (w->tm_mday < k) goto before;
5304 if (w->tm_mday > k) goto after;
5307 if (w->tm_hour < hour) goto before;
5308 if (w->tm_hour > hour) goto after;
5309 if (w->tm_min < min) goto before;
5310 if (w->tm_min > min) goto after;
5311 if (w->tm_sec < sec) goto before;
5325 /* parse the offset: (+|-)hh[:mm[:ss]] */
5328 tz_parse_offset(char *s, int *offset)
5330 int hour = 0, min = 0, sec = 0;
5333 if (!offset) return 0;
5335 if (*s == '-') {neg++; s++;}
5337 if (!isdigit(*s)) return 0;
5339 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
5340 if (hour > 24) return 0;
5342 if (!isdigit(*++s)) return 0;
5344 if (isdigit(*s)) min = min*10 + (*s++ - '0');
5345 if (min > 59) return 0;
5347 if (!isdigit(*++s)) return 0;
5349 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
5350 if (sec > 59) return 0;
5354 *offset = (hour*60+min)*60 + sec;
5355 if (neg) *offset = -*offset;
5360 input time is w, whatever type of time the CRTL localtime() uses.
5361 sets dst, the zone, and the gmtoff (seconds)
5363 caches the value of TZ and UCX$TZ env variables; note that
5364 my_setenv looks for these and sets a flag if they're changed
5367 We have to watch out for the "australian" case (dst starts in
5368 october, ends in april)...flagged by "reverse" and checked by
5369 scanning through the months of the previous year.
5374 tz_parse(time_t *w, int *dst, char *zone, int *gmtoff)
5379 char *dstzone, *tz, *s_start, *s_end;
5380 int std_off, dst_off, isdst;
5381 int y, dststart, dstend;
5382 static char envtz[1025]; /* longer than any logical, symbol, ... */
5383 static char ucxtz[1025];
5384 static char reversed = 0;
5390 reversed = -1; /* flag need to check */
5391 envtz[0] = ucxtz[0] = '\0';
5392 tz = my_getenv("TZ",0);
5393 if (tz) strcpy(envtz, tz);
5394 tz = my_getenv("UCX$TZ",0);
5395 if (tz) strcpy(ucxtz, tz);
5396 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
5399 if (!*tz) tz = ucxtz;
5402 while (isalpha(*s)) s++;
5403 s = tz_parse_offset(s, &std_off);
5405 if (!*s) { /* no DST, hurray we're done! */
5411 while (isalpha(*s)) s++;
5412 s2 = tz_parse_offset(s, &dst_off);
5416 dst_off = std_off - 3600;
5419 if (!*s) { /* default dst start/end?? */
5420 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
5421 s = strchr(ucxtz,',');
5423 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
5425 if (*s != ',') return 0;
5428 when = _toutc(when); /* convert to utc */
5429 when = when - std_off; /* convert to pseudolocal time*/
5431 w2 = localtime(&when);
5434 s = tz_parse_startend(s_start,w2,&dststart);
5436 if (*s != ',') return 0;
5439 when = _toutc(when); /* convert to utc */
5440 when = when - dst_off; /* convert to pseudolocal time*/
5441 w2 = localtime(&when);
5442 if (w2->tm_year != y) { /* spans a year, just check one time */
5443 when += dst_off - std_off;
5444 w2 = localtime(&when);
5447 s = tz_parse_startend(s_end,w2,&dstend);
5450 if (reversed == -1) { /* need to check if start later than end */
5454 if (when < 2*365*86400) {
5455 when += 2*365*86400;
5459 w2 =localtime(&when);
5460 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
5462 for (j = 0; j < 12; j++) {
5463 w2 =localtime(&when);
5464 (void) tz_parse_startend(s_start,w2,&ds);
5465 (void) tz_parse_startend(s_end,w2,&de);
5466 if (ds != de) break;
5470 if (de && !ds) reversed = 1;
5473 isdst = dststart && !dstend;
5474 if (reversed) isdst = dststart || !dstend;
5477 if (dst) *dst = isdst;
5478 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
5479 if (isdst) tz = dstzone;
5481 while(isalpha(*tz)) *zone++ = *tz++;
5487 #endif /* !RTL_USES_UTC */
5489 /* my_time(), my_localtime(), my_gmtime()
5490 * By default traffic in UTC time values, using CRTL gmtime() or
5491 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
5492 * Note: We need to use these functions even when the CRTL has working
5493 * UTC support, since they also handle C<use vmsish qw(times);>
5495 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
5496 * Modified by Charles Bailey <bailey@newman.upenn.edu>
5499 /*{{{time_t my_time(time_t *timep)*/
5500 time_t my_time(time_t *timep)
5506 if (gmtime_emulation_type == 0) {
5508 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
5509 /* results of calls to gmtime() and localtime() */
5510 /* for same &base */
5512 gmtime_emulation_type++;
5513 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
5514 char off[LNM$C_NAMLENGTH+1];;
5516 gmtime_emulation_type++;
5517 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
5518 gmtime_emulation_type++;
5519 utc_offset_secs = 0;
5520 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
5522 else { utc_offset_secs = atol(off); }
5524 else { /* We've got a working gmtime() */
5525 struct tm gmt, local;
5528 tm_p = localtime(&base);
5530 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
5531 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
5532 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
5533 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
5539 # ifdef RTL_USES_UTC
5540 if (VMSISH_TIME) when = _toloc(when);
5542 if (!VMSISH_TIME) when = _toutc(when);
5545 if (timep != NULL) *timep = when;
5548 } /* end of my_time() */
5552 /*{{{struct tm *my_gmtime(const time_t *timep)*/
5554 my_gmtime(const time_t *timep)
5561 if (timep == NULL) {
5562 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5565 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
5569 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
5571 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
5572 return gmtime(&when);
5574 /* CRTL localtime() wants local time as input, so does no tz correction */
5575 rsltmp = localtime(&when);
5576 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
5579 } /* end of my_gmtime() */
5583 /*{{{struct tm *my_localtime(const time_t *timep)*/
5585 my_localtime(const time_t *timep)
5588 time_t when, whenutc;
5592 if (timep == NULL) {
5593 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5596 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
5597 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
5600 # ifdef RTL_USES_UTC
5602 if (VMSISH_TIME) when = _toutc(when);
5604 /* CRTL localtime() wants UTC as input, does tz correction itself */
5605 return localtime(&when);
5607 # else /* !RTL_USES_UTC */
5610 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
5611 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
5614 #ifndef RTL_USES_UTC
5615 if (tz_parse(&when, &dst, 0, &offset)) { /* truelocal determines DST*/
5616 when = whenutc - offset; /* pseudolocal time*/
5619 /* CRTL localtime() wants local time as input, so does no tz correction */
5620 rsltmp = localtime(&when);
5621 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
5625 } /* end of my_localtime() */
5628 /* Reset definitions for later calls */
5629 #define gmtime(t) my_gmtime(t)
5630 #define localtime(t) my_localtime(t)
5631 #define time(t) my_time(t)
5634 /* my_utime - update modification time of a file
5635 * calling sequence is identical to POSIX utime(), but under
5636 * VMS only the modification time is changed; ODS-2 does not
5637 * maintain access times. Restrictions differ from the POSIX
5638 * definition in that the time can be changed as long as the
5639 * caller has permission to execute the necessary IO$_MODIFY $QIO;
5640 * no separate checks are made to insure that the caller is the
5641 * owner of the file or has special privs enabled.
5642 * Code here is based on Joe Meadows' FILE utility.
5645 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
5646 * to VMS epoch (01-JAN-1858 00:00:00.00)
5647 * in 100 ns intervals.
5649 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
5651 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
5652 int my_utime(char *file, struct utimbuf *utimes)
5656 long int bintime[2], len = 2, lowbit, unixtime,
5657 secscale = 10000000; /* seconds --> 100 ns intervals */
5658 unsigned long int chan, iosb[2], retsts;
5659 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
5660 struct FAB myfab = cc$rms_fab;
5661 struct NAM mynam = cc$rms_nam;
5662 #if defined (__DECC) && defined (__VAX)
5663 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
5664 * at least through VMS V6.1, which causes a type-conversion warning.
5666 # pragma message save
5667 # pragma message disable cvtdiftypes
5669 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
5670 struct fibdef myfib;
5671 #if defined (__DECC) && defined (__VAX)
5672 /* This should be right after the declaration of myatr, but due
5673 * to a bug in VAX DEC C, this takes effect a statement early.
5675 # pragma message restore
5677 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
5678 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
5679 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
5681 if (file == NULL || *file == '\0') {
5683 set_vaxc_errno(LIB$_INVARG);
5686 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
5688 if (utimes != NULL) {
5689 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
5690 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
5691 * Since time_t is unsigned long int, and lib$emul takes a signed long int
5692 * as input, we force the sign bit to be clear by shifting unixtime right
5693 * one bit, then multiplying by an extra factor of 2 in lib$emul().
5695 lowbit = (utimes->modtime & 1) ? secscale : 0;
5696 unixtime = (long int) utimes->modtime;
5698 /* If input was UTC; convert to local for sys svc */
5699 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
5701 unixtime >>= 1; secscale <<= 1;
5702 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
5703 if (!(retsts & 1)) {
5705 set_vaxc_errno(retsts);
5708 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
5709 if (!(retsts & 1)) {
5711 set_vaxc_errno(retsts);
5716 /* Just get the current time in VMS format directly */
5717 retsts = sys$gettim(bintime);
5718 if (!(retsts & 1)) {
5720 set_vaxc_errno(retsts);
5725 myfab.fab$l_fna = vmsspec;
5726 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
5727 myfab.fab$l_nam = &mynam;
5728 mynam.nam$l_esa = esa;
5729 mynam.nam$b_ess = (unsigned char) sizeof esa;
5730 mynam.nam$l_rsa = rsa;
5731 mynam.nam$b_rss = (unsigned char) sizeof rsa;
5733 /* Look for the file to be affected, letting RMS parse the file
5734 * specification for us as well. I have set errno using only
5735 * values documented in the utime() man page for VMS POSIX.
5737 retsts = sys$parse(&myfab,0,0);
5738 if (!(retsts & 1)) {
5739 set_vaxc_errno(retsts);
5740 if (retsts == RMS$_PRV) set_errno(EACCES);
5741 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5742 else set_errno(EVMSERR);
5745 retsts = sys$search(&myfab,0,0);
5746 if (!(retsts & 1)) {
5747 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
5748 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
5749 set_vaxc_errno(retsts);
5750 if (retsts == RMS$_PRV) set_errno(EACCES);
5751 else if (retsts == RMS$_FNF) set_errno(ENOENT);
5752 else set_errno(EVMSERR);
5756 devdsc.dsc$w_length = mynam.nam$b_dev;
5757 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
5759 retsts = sys$assign(&devdsc,&chan,0,0);
5760 if (!(retsts & 1)) {
5761 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
5762 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
5763 set_vaxc_errno(retsts);
5764 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
5765 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
5766 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
5767 else set_errno(EVMSERR);
5771 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
5772 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
5774 memset((void *) &myfib, 0, sizeof myfib);
5775 #if defined(__DECC) || defined(__DECCXX)
5776 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
5777 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
5778 /* This prevents the revision time of the file being reset to the current
5779 * time as a result of our IO$_MODIFY $QIO. */
5780 myfib.fib$l_acctl = FIB$M_NORECORD;
5782 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
5783 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
5784 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
5786 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
5787 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
5788 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
5789 _ckvmssts(sys$dassgn(chan));
5790 if (retsts & 1) retsts = iosb[0];
5791 if (!(retsts & 1)) {
5792 set_vaxc_errno(retsts);
5793 if (retsts == SS$_NOPRIV) set_errno(EACCES);
5794 else set_errno(EVMSERR);
5799 } /* end of my_utime() */
5803 * flex_stat, flex_fstat
5804 * basic stat, but gets it right when asked to stat
5805 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
5808 /* encode_dev packs a VMS device name string into an integer to allow
5809 * simple comparisons. This can be used, for example, to check whether two
5810 * files are located on the same device, by comparing their encoded device
5811 * names. Even a string comparison would not do, because stat() reuses the
5812 * device name buffer for each call; so without encode_dev, it would be
5813 * necessary to save the buffer and use strcmp (this would mean a number of
5814 * changes to the standard Perl code, to say nothing of what a Perl script
5817 * The device lock id, if it exists, should be unique (unless perhaps compared
5818 * with lock ids transferred from other nodes). We have a lock id if the disk is
5819 * mounted cluster-wide, which is when we tend to get long (host-qualified)
5820 * device names. Thus we use the lock id in preference, and only if that isn't
5821 * available, do we try to pack the device name into an integer (flagged by
5822 * the sign bit (LOCKID_MASK) being set).
5824 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
5825 * name and its encoded form, but it seems very unlikely that we will find
5826 * two files on different disks that share the same encoded device names,
5827 * and even more remote that they will share the same file id (if the test
5828 * is to check for the same file).
5830 * A better method might be to use sys$device_scan on the first call, and to
5831 * search for the device, returning an index into the cached array.
5832 * The number returned would be more intelligable.
5833 * This is probably not worth it, and anyway would take quite a bit longer
5834 * on the first call.
5836 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
5837 static mydev_t encode_dev (const char *dev)
5840 unsigned long int f;
5846 if (!dev || !dev[0]) return 0;
5850 struct dsc$descriptor_s dev_desc;
5851 unsigned long int status, lockid, item = DVI$_LOCKID;
5853 /* For cluster-mounted disks, the disk lock identifier is unique, so we
5854 can try that first. */
5855 dev_desc.dsc$w_length = strlen (dev);
5856 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
5857 dev_desc.dsc$b_class = DSC$K_CLASS_S;
5858 dev_desc.dsc$a_pointer = (char *) dev;
5859 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
5860 if (lockid) return (lockid & ~LOCKID_MASK);
5864 /* Otherwise we try to encode the device name */
5868 for (q = dev + strlen(dev); q--; q >= dev) {
5871 else if (isalpha (toupper (*q)))
5872 c= toupper (*q) - 'A' + (char)10;
5874 continue; /* Skip '$'s */
5876 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
5878 enc += f * (unsigned long int) c;
5880 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
5882 } /* end of encode_dev() */
5884 static char namecache[NAM$C_MAXRSS+1];
5887 is_null_device(name)
5891 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
5892 The underscore prefix, controller letter, and unit number are
5893 independently optional; for our purposes, the colon punctuation
5894 is not. The colon can be trailed by optional directory and/or
5895 filename, but two consecutive colons indicates a nodename rather
5896 than a device. [pr] */
5897 if (*name == '_') ++name;
5898 if (tolower(*name++) != 'n') return 0;
5899 if (tolower(*name++) != 'l') return 0;
5900 if (tolower(*name) == 'a') ++name;
5901 if (*name == '0') ++name;
5902 return (*name++ == ':') && (*name != ':');
5905 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
5906 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
5907 * subset of the applicable information.
5910 Perl_cando(pTHX_ Mode_t bit, Uid_t effective, Stat_t *statbufp)
5912 char fname_phdev[NAM$C_MAXRSS+1];
5913 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
5915 char fname[NAM$C_MAXRSS+1];
5916 unsigned long int retsts;
5917 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
5918 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5920 /* If the struct mystat is stale, we're OOL; stat() overwrites the
5921 device name on successive calls */
5922 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
5923 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
5924 namdsc.dsc$a_pointer = fname;
5925 namdsc.dsc$w_length = sizeof fname - 1;
5927 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
5928 &namdsc,&namdsc.dsc$w_length,0,0);
5930 fname[namdsc.dsc$w_length] = '\0';
5932 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
5933 * but if someone has redefined that logical, Perl gets very lost. Since
5934 * we have the physical device name from the stat buffer, just paste it on.
5936 strcpy( fname_phdev, statbufp->st_devnam );
5937 strcat( fname_phdev, strrchr(fname, ':') );
5939 return cando_by_name(bit,effective,fname_phdev);
5941 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
5942 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
5946 return FALSE; /* Should never get to here */
5948 } /* end of cando() */
5952 /*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
5954 cando_by_name(I32 bit, Uid_t effective, char *fname)
5956 static char usrname[L_cuserid];
5957 static struct dsc$descriptor_s usrdsc =
5958 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
5959 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
5960 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
5961 unsigned short int retlen;
5963 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5964 union prvdef curprv;
5965 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
5966 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
5967 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
5970 if (!fname || !*fname) return FALSE;
5971 /* Make sure we expand logical names, since sys$check_access doesn't */
5972 if (!strpbrk(fname,"/]>:")) {
5973 strcpy(fileified,fname);
5974 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
5977 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
5978 retlen = namdsc.dsc$w_length = strlen(vmsname);
5979 namdsc.dsc$a_pointer = vmsname;
5980 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
5981 vmsname[retlen-1] == ':') {
5982 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
5983 namdsc.dsc$w_length = strlen(fileified);
5984 namdsc.dsc$a_pointer = fileified;
5987 if (!usrdsc.dsc$w_length) {
5989 usrdsc.dsc$w_length = strlen(usrname);
5993 case S_IXUSR: case S_IXGRP: case S_IXOTH:
5994 access = ARM$M_EXECUTE; break;
5995 case S_IRUSR: case S_IRGRP: case S_IROTH:
5996 access = ARM$M_READ; break;
5997 case S_IWUSR: case S_IWGRP: case S_IWOTH:
5998 access = ARM$M_WRITE; break;
5999 case S_IDUSR: case S_IDGRP: case S_IDOTH:
6000 access = ARM$M_DELETE; break;
6005 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
6006 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
6007 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
6008 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
6009 set_vaxc_errno(retsts);
6010 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6011 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
6012 else set_errno(ENOENT);
6015 if (retsts == SS$_NORMAL) {
6016 if (!privused) return TRUE;
6017 /* We can get access, but only by using privs. Do we have the
6018 necessary privs currently enabled? */
6019 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
6020 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
6021 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
6022 !curprv.prv$v_bypass) return FALSE;
6023 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
6024 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
6025 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
6028 if (retsts == SS$_ACCONFLICT) {
6033 return FALSE; /* Should never get here */
6035 } /* end of cando_by_name() */
6039 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
6041 flex_fstat(int fd, Stat_t *statbufp)
6044 if (!fstat(fd,(stat_t *) statbufp)) {
6045 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
6046 statbufp->st_dev = encode_dev(statbufp->st_devnam);
6047 # ifdef RTL_USES_UTC
6050 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6051 statbufp->st_atime = _toloc(statbufp->st_atime);
6052 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6057 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6061 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6062 statbufp->st_atime = _toutc(statbufp->st_atime);
6063 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6070 } /* end of flex_fstat() */
6073 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
6075 flex_stat(const char *fspec, Stat_t *statbufp)
6078 char fileified[NAM$C_MAXRSS+1];
6079 char temp_fspec[NAM$C_MAXRSS+300];
6082 strcpy(temp_fspec, fspec);
6083 if (statbufp == (Stat_t *) &PL_statcache)
6084 do_tovmsspec(temp_fspec,namecache,0);
6085 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
6086 memset(statbufp,0,sizeof *statbufp);
6087 statbufp->st_dev = encode_dev("_NLA0:");
6088 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
6089 statbufp->st_uid = 0x00010001;
6090 statbufp->st_gid = 0x0001;
6091 time((time_t *)&statbufp->st_mtime);
6092 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
6096 /* Try for a directory name first. If fspec contains a filename without
6097 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
6098 * and sea:[wine.dark]water. exist, we prefer the directory here.
6099 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
6100 * not sea:[wine.dark]., if the latter exists. If the intended target is
6101 * the file with null type, specify this by calling flex_stat() with
6102 * a '.' at the end of fspec.
6104 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
6105 retval = stat(fileified,(stat_t *) statbufp);
6106 if (!retval && statbufp == (Stat_t *) &PL_statcache)
6107 strcpy(namecache,fileified);
6109 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
6111 statbufp->st_dev = encode_dev(statbufp->st_devnam);
6112 # ifdef RTL_USES_UTC
6115 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6116 statbufp->st_atime = _toloc(statbufp->st_atime);
6117 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6122 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6126 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6127 statbufp->st_atime = _toutc(statbufp->st_atime);
6128 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6134 } /* end of flex_stat() */
6138 /*{{{char *my_getlogin()*/
6139 /* VMS cuserid == Unix getlogin, except calling sequence */
6143 static char user[L_cuserid];
6144 return cuserid(user);
6149 /* rmscopy - copy a file using VMS RMS routines
6151 * Copies contents and attributes of spec_in to spec_out, except owner
6152 * and protection information. Name and type of spec_in are used as
6153 * defaults for spec_out. The third parameter specifies whether rmscopy()
6154 * should try to propagate timestamps from the input file to the output file.
6155 * If it is less than 0, no timestamps are preserved. If it is 0, then
6156 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
6157 * propagated to the output file at creation iff the output file specification
6158 * did not contain an explicit name or type, and the revision date is always
6159 * updated at the end of the copy operation. If it is greater than 0, then
6160 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
6161 * other than the revision date should be propagated, and bit 1 indicates
6162 * that the revision date should be propagated.
6164 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
6166 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
6167 * Incorporates, with permission, some code from EZCOPY by Tim Adye
6168 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
6169 * as part of the Perl standard distribution under the terms of the
6170 * GNU General Public License or the Perl Artistic License. Copies
6171 * of each may be found in the Perl standard distribution.
6173 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
6175 Perl_rmscopy(pTHX_ char *spec_in, char *spec_out, int preserve_dates)
6177 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
6178 rsa[NAM$C_MAXRSS], ubf[32256];
6179 unsigned long int i, sts, sts2;
6180 struct FAB fab_in, fab_out;
6181 struct RAB rab_in, rab_out;
6183 struct XABDAT xabdat;
6184 struct XABFHC xabfhc;
6185 struct XABRDT xabrdt;
6186 struct XABSUM xabsum;
6188 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
6189 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
6190 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6194 fab_in = cc$rms_fab;
6195 fab_in.fab$l_fna = vmsin;
6196 fab_in.fab$b_fns = strlen(vmsin);
6197 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
6198 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
6199 fab_in.fab$l_fop = FAB$M_SQO;
6200 fab_in.fab$l_nam = &nam;
6201 fab_in.fab$l_xab = (void *) &xabdat;
6204 nam.nam$l_rsa = rsa;
6205 nam.nam$b_rss = sizeof(rsa);
6206 nam.nam$l_esa = esa;
6207 nam.nam$b_ess = sizeof (esa);
6208 nam.nam$b_esl = nam.nam$b_rsl = 0;
6210 xabdat = cc$rms_xabdat; /* To get creation date */
6211 xabdat.xab$l_nxt = (void *) &xabfhc;
6213 xabfhc = cc$rms_xabfhc; /* To get record length */
6214 xabfhc.xab$l_nxt = (void *) &xabsum;
6216 xabsum = cc$rms_xabsum; /* To get key and area information */
6218 if (!((sts = sys$open(&fab_in)) & 1)) {
6219 set_vaxc_errno(sts);
6221 case RMS$_FNF: case RMS$_DNF:
6222 set_errno(ENOENT); break;
6224 set_errno(ENOTDIR); break;
6226 set_errno(ENODEV); break;
6228 set_errno(EINVAL); break;
6230 set_errno(EACCES); break;
6238 fab_out.fab$w_ifi = 0;
6239 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
6240 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
6241 fab_out.fab$l_fop = FAB$M_SQO;
6242 fab_out.fab$l_fna = vmsout;
6243 fab_out.fab$b_fns = strlen(vmsout);
6244 fab_out.fab$l_dna = nam.nam$l_name;
6245 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
6247 if (preserve_dates == 0) { /* Act like DCL COPY */
6248 nam.nam$b_nop = NAM$M_SYNCHK;
6249 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
6250 if (!((sts = sys$parse(&fab_out)) & 1)) {
6251 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
6252 set_vaxc_errno(sts);
6255 fab_out.fab$l_xab = (void *) &xabdat;
6256 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
6258 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
6259 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
6260 preserve_dates =0; /* bitmask from this point forward */
6262 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
6263 if (!((sts = sys$create(&fab_out)) & 1)) {
6264 set_vaxc_errno(sts);
6267 set_errno(ENOENT); break;
6269 set_errno(ENOTDIR); break;
6271 set_errno(ENODEV); break;
6273 set_errno(EINVAL); break;
6275 set_errno(EACCES); break;
6281 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
6282 if (preserve_dates & 2) {
6283 /* sys$close() will process xabrdt, not xabdat */
6284 xabrdt = cc$rms_xabrdt;
6286 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
6288 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
6289 * is unsigned long[2], while DECC & VAXC use a struct */
6290 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
6292 fab_out.fab$l_xab = (void *) &xabrdt;
6295 rab_in = cc$rms_rab;
6296 rab_in.rab$l_fab = &fab_in;
6297 rab_in.rab$l_rop = RAB$M_BIO;
6298 rab_in.rab$l_ubf = ubf;
6299 rab_in.rab$w_usz = sizeof ubf;
6300 if (!((sts = sys$connect(&rab_in)) & 1)) {
6301 sys$close(&fab_in); sys$close(&fab_out);
6302 set_errno(EVMSERR); set_vaxc_errno(sts);
6306 rab_out = cc$rms_rab;
6307 rab_out.rab$l_fab = &fab_out;
6308 rab_out.rab$l_rbf = ubf;
6309 if (!((sts = sys$connect(&rab_out)) & 1)) {
6310 sys$close(&fab_in); sys$close(&fab_out);
6311 set_errno(EVMSERR); set_vaxc_errno(sts);
6315 while ((sts = sys$read(&rab_in))) { /* always true */
6316 if (sts == RMS$_EOF) break;
6317 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
6318 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
6319 sys$close(&fab_in); sys$close(&fab_out);
6320 set_errno(EVMSERR); set_vaxc_errno(sts);
6325 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
6326 sys$close(&fab_in); sys$close(&fab_out);
6327 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
6329 set_errno(EVMSERR); set_vaxc_errno(sts);
6335 } /* end of rmscopy() */
6339 /*** The following glue provides 'hooks' to make some of the routines
6340 * from this file available from Perl. These routines are sufficiently
6341 * basic, and are required sufficiently early in the build process,
6342 * that's it's nice to have them available to miniperl as well as the
6343 * full Perl, so they're set up here instead of in an extension. The
6344 * Perl code which handles importation of these names into a given
6345 * package lives in [.VMS]Filespec.pm in @INC.
6349 rmsexpand_fromperl(pTHX_ CV *cv)
6352 char *fspec, *defspec = NULL, *rslt;
6355 if (!items || items > 2)
6356 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
6357 fspec = SvPV(ST(0),n_a);
6358 if (!fspec || !*fspec) XSRETURN_UNDEF;
6359 if (items == 2) defspec = SvPV(ST(1),n_a);
6361 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
6362 ST(0) = sv_newmortal();
6363 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
6368 vmsify_fromperl(pTHX_ CV *cv)
6374 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
6375 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
6376 ST(0) = sv_newmortal();
6377 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
6382 unixify_fromperl(pTHX_ CV *cv)
6388 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
6389 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
6390 ST(0) = sv_newmortal();
6391 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
6396 fileify_fromperl(pTHX_ CV *cv)
6402 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
6403 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
6404 ST(0) = sv_newmortal();
6405 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
6410 pathify_fromperl(pTHX_ CV *cv)
6416 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
6417 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
6418 ST(0) = sv_newmortal();
6419 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
6424 vmspath_fromperl(pTHX_ CV *cv)
6430 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
6431 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
6432 ST(0) = sv_newmortal();
6433 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
6438 unixpath_fromperl(pTHX_ CV *cv)
6444 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
6445 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
6446 ST(0) = sv_newmortal();
6447 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
6452 candelete_fromperl(pTHX_ CV *cv)
6455 char fspec[NAM$C_MAXRSS+1], *fsp;
6460 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
6462 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
6463 if (SvTYPE(mysv) == SVt_PVGV) {
6464 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec,1)) {
6465 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6472 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
6473 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6479 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
6484 rmscopy_fromperl(pTHX_ CV *cv)
6487 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
6489 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6490 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6491 unsigned long int sts;
6496 if (items < 2 || items > 3)
6497 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
6499 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
6500 if (SvTYPE(mysv) == SVt_PVGV) {
6501 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec,1)) {
6502 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6509 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
6510 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6515 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
6516 if (SvTYPE(mysv) == SVt_PVGV) {
6517 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec,1)) {
6518 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6525 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
6526 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6531 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
6533 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
6542 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
6543 workbuff[NAM$C_MAXRSS*1 + 1];
6544 int total_namelen = 3, counter, num_entries;
6545 /* ODS-5 ups this, but we want to be consistent, so... */
6546 int max_name_len = 39;
6547 AV *in_array = (AV *)SvRV(ST(0));
6549 num_entries = av_len(in_array);
6551 /* All the names start with PL_. */
6552 strcpy(ultimate_name, "PL_");
6554 /* Clean up our working buffer */
6555 Zero(work_name, sizeof(work_name), char);
6557 /* Run through the entries and build up a working name */
6558 for(counter = 0; counter <= num_entries; counter++) {
6559 /* If it's not the first name then tack on a __ */
6561 strcat(work_name, "__");
6563 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
6567 /* Check to see if we actually have to bother...*/
6568 if (strlen(work_name) + 3 <= max_name_len) {
6569 strcat(ultimate_name, work_name);
6571 /* It's too darned big, so we need to go strip. We use the same */
6572 /* algorithm as xsubpp does. First, strip out doubled __ */
6573 char *source, *dest, last;
6576 for (source = work_name; *source; source++) {
6577 if (last == *source && last == '_') {
6583 /* Go put it back */
6584 strcpy(work_name, workbuff);
6585 /* Is it still too big? */
6586 if (strlen(work_name) + 3 > max_name_len) {
6587 /* Strip duplicate letters */
6590 for (source = work_name; *source; source++) {
6591 if (last == toupper(*source)) {
6595 last = toupper(*source);
6597 strcpy(work_name, workbuff);
6600 /* Is it *still* too big? */
6601 if (strlen(work_name) + 3 > max_name_len) {
6602 /* Too bad, we truncate */
6603 work_name[max_name_len - 2] = 0;
6605 strcat(ultimate_name, work_name);
6608 /* Okay, return it */
6609 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
6616 char* file = __FILE__;
6618 char temp_buff[512];
6619 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
6620 no_translate_barewords = TRUE;
6622 no_translate_barewords = FALSE;
6625 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
6626 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
6627 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
6628 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
6629 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
6630 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
6631 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
6632 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
6633 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
6634 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);