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 /* gcc's header files don't #define direct access macros
64 * corresponding to VAXC's variant structs */
66 # define uic$v_format uic$r_uic_form.uic$v_format
67 # define uic$v_group uic$r_uic_form.uic$v_group
68 # define uic$v_member uic$r_uic_form.uic$v_member
69 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
70 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
71 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
72 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
75 #if defined(NEED_AN_H_ERRNO)
80 unsigned short int buflen;
81 unsigned short int itmcode;
83 unsigned short int *retlen;
86 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
87 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
88 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
89 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
90 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
91 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
92 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
93 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
94 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
96 static char *__mystrtolower(char *str)
98 if (str) for (; *str; ++str) *str= tolower(*str);
102 static struct dsc$descriptor_s fildevdsc =
103 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
104 static struct dsc$descriptor_s crtlenvdsc =
105 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
106 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
107 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
108 static struct dsc$descriptor_s **env_tables = defenv;
109 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
111 /* True if we shouldn't treat barewords as logicals during directory */
113 static int no_translate_barewords;
115 /* Temp for subprocess commands */
116 static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
118 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
120 Perl_vmstrnenv(pTHX_ const char *lnm, char *eqv, unsigned long int idx,
121 struct dsc$descriptor_s **tabvec, unsigned long int flags)
123 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
124 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
125 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
126 unsigned char acmode;
127 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
128 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
129 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
130 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
132 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
133 #if defined(USE_THREADS)
134 /* We jump through these hoops because we can be called at */
135 /* platform-specific initialization time, which is before anything is */
136 /* set up--we can't even do a plain dTHX since that relies on the */
137 /* interpreter structure to be initialized */
138 struct perl_thread *thr;
140 thr = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
146 if (!lnm || !eqv || idx > LNM$_MAX_INDEX) {
147 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
149 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
150 *cp2 = _toupper(*cp1);
151 if (cp1 - lnm > LNM$C_NAMLENGTH) {
152 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
156 lnmdsc.dsc$w_length = cp1 - lnm;
157 lnmdsc.dsc$a_pointer = uplnm;
158 uplnm[lnmdsc.dsc$w_length] = '\0';
159 secure = flags & PERL__TRNENV_SECURE;
160 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
161 if (!tabvec || !*tabvec) tabvec = env_tables;
163 for (curtab = 0; tabvec[curtab]; curtab++) {
164 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
165 if (!ivenv && !secure) {
170 Perl_warn(aTHX_ "Can't read CRTL environ\n");
173 retsts = SS$_NOLOGNAM;
174 for (i = 0; environ[i]; i++) {
175 if ((eq = strchr(environ[i],'=')) &&
176 !strncmp(environ[i],uplnm,eq - environ[i])) {
178 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
179 if (!eqvlen) continue;
184 if (retsts != SS$_NOLOGNAM) break;
187 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
188 !str$case_blind_compare(&tmpdsc,&clisym)) {
189 if (!ivsym && !secure) {
190 unsigned short int deflen = LNM$C_NAMLENGTH;
191 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
192 /* dynamic dsc to accomodate possible long value */
193 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
194 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
197 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
199 /* Special hack--we might be called before the interpreter's */
200 /* fully initialized, in which case either thr or PL_curcop */
201 /* might be bogus. We have to check, since ckWARN needs them */
202 /* both to be valid if running threaded */
203 #if defined(USE_THREADS)
204 if (thr && PL_curcop) {
206 if (ckWARN(WARN_MISC)) {
207 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
209 #if defined(USE_THREADS)
211 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
216 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
218 _ckvmssts(lib$sfree1_dd(&eqvdsc));
219 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
220 if (retsts == LIB$_NOSUCHSYM) continue;
225 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
226 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
227 if (retsts == SS$_NOLOGNAM) continue;
228 /* PPFs have a prefix */
231 *((int *)uplnm) == *((int *)"SYS$") &&
233 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
234 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
235 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
236 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
237 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
238 memcpy(eqv,eqv+4,eqvlen-4);
244 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
245 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
246 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
247 retsts == SS$_NOLOGNAM) {
248 set_errno(EINVAL); set_vaxc_errno(retsts);
250 else _ckvmssts(retsts);
252 } /* end of vmstrnenv */
255 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
256 /* Define as a function so we can access statics. */
257 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
259 return vmstrnenv(lnm,eqv,idx,fildev,
260 #ifdef SECURE_INTERNAL_GETENV
261 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
270 * Note: Uses Perl temp to store result so char * can be returned to
271 * caller; this pointer will be invalidated at next Perl statement
273 * We define this as a function rather than a macro in terms of my_getenv_len()
274 * so that it'll work when PL_curinterp is undefined (and we therefore can't
277 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
279 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
281 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
282 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
283 unsigned long int idx = 0;
287 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
288 /* Set up a temporary buffer for the return value; Perl will
289 * clean it up at the next statement transition */
290 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
291 if (!tmpsv) return NULL;
294 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
295 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
296 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
297 getcwd(eqv,LNM$C_NAMLENGTH);
301 if ((cp2 = strchr(lnm,';')) != NULL) {
303 uplnm[cp2-lnm] = '\0';
304 idx = strtoul(cp2+1,NULL,0);
307 /* Impose security constraints only if tainting */
308 if (sys) sys = PL_curinterp ? PL_tainting : will_taint;
309 if (vmstrnenv(lnm,eqv,idx,
311 #ifdef SECURE_INTERNAL_GETENV
312 sys ? PERL__TRNENV_SECURE : 0
320 } /* end of my_getenv() */
324 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
326 my_getenv_len(const char *lnm, unsigned long *len, bool sys)
329 char *buf, *cp1, *cp2;
330 unsigned long idx = 0;
331 static char __my_getenv_len_eqv[LNM$C_NAMLENGTH+1];
334 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
335 /* Set up a temporary buffer for the return value; Perl will
336 * clean it up at the next statement transition */
337 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
338 if (!tmpsv) return NULL;
341 else buf = __my_getenv_len_eqv; /* Assume no interpreter ==> single thread */
342 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
343 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
344 getcwd(buf,LNM$C_NAMLENGTH);
349 if ((cp2 = strchr(lnm,';')) != NULL) {
352 idx = strtoul(cp2+1,NULL,0);
355 /* Impose security constraints only if tainting */
356 if (sys) sys = PL_curinterp ? PL_tainting : will_taint;
357 if ((*len = vmstrnenv(lnm,buf,idx,
359 #ifdef SECURE_INTERNAL_GETENV
360 sys ? PERL__TRNENV_SECURE : 0
370 } /* end of my_getenv_len() */
373 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
375 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
377 /*{{{ void prime_env_iter() */
380 /* Fill the %ENV associative array with all logical names we can
381 * find, in preparation for iterating over it.
385 static int primed = 0;
386 HV *seenhv = NULL, *envhv;
387 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
388 unsigned short int chan;
389 #ifndef CLI$M_TRUSTED
390 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
392 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
393 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
395 bool have_sym = FALSE, have_lnm = FALSE;
396 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
397 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
398 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
399 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
400 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
401 #if defined(USE_THREADS) || defined(USE_ITHREADS)
402 static perl_mutex primenv_mutex;
403 MUTEX_INIT(&primenv_mutex);
406 if (primed || !PL_envgv) return;
407 MUTEX_LOCK(&primenv_mutex);
408 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
409 envhv = GvHVn(PL_envgv);
410 /* Perform a dummy fetch as an lval to insure that the hash table is
411 * set up. Otherwise, the hv_store() will turn into a nullop. */
412 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
414 for (i = 0; env_tables[i]; i++) {
415 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
416 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
417 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
419 if (have_sym || have_lnm) {
420 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
421 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
422 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
423 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
426 for (i--; i >= 0; i--) {
427 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
430 for (j = 0; environ[j]; j++) {
431 if (!(start = strchr(environ[j],'='))) {
432 if (ckWARN(WARN_INTERNAL))
433 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
437 (void) hv_store(envhv,environ[j],start - environ[j] - 1,
443 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
444 !str$case_blind_compare(&tmpdsc,&clisym)) {
445 strcpy(cmd,"Show Symbol/Global *");
446 cmddsc.dsc$w_length = 20;
447 if (env_tables[i]->dsc$w_length == 12 &&
448 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
449 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
450 flags = defflags | CLI$M_NOLOGNAM;
453 strcpy(cmd,"Show Logical *");
454 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
455 strcat(cmd," /Table=");
456 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
457 cmddsc.dsc$w_length = strlen(cmd);
459 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
460 flags = defflags | CLI$M_NOCLISYM;
463 /* Create a new subprocess to execute each command, to exclude the
464 * remote possibility that someone could subvert a mbx or file used
465 * to write multiple commands to a single subprocess.
468 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
469 0,&riseandshine,0,0,&clidsc,&clitabdsc);
470 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
471 defflags &= ~CLI$M_TRUSTED;
472 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
474 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
475 if (seenhv) SvREFCNT_dec(seenhv);
478 char *cp1, *cp2, *key;
479 unsigned long int sts, iosb[2], retlen, keylen;
482 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
483 if (sts & 1) sts = iosb[0] & 0xffff;
484 if (sts == SS$_ENDOFFILE) {
486 while (substs == 0) { sys$hiber(); wakect++;}
487 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
492 retlen = iosb[0] >> 16;
493 if (!retlen) continue; /* blank line */
495 if (iosb[1] != subpid) {
497 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
501 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
502 Perl_warner(aTHX_ WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
504 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
505 if (*cp1 == '(' || /* Logical name table name */
506 *cp1 == '=' /* Next eqv of searchlist */) continue;
507 if (*cp1 == '"') cp1++;
508 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
509 key = cp1; keylen = cp2 - cp1;
510 if (keylen && hv_exists(seenhv,key,keylen)) continue;
511 while (*cp2 && *cp2 != '=') cp2++;
512 while (*cp2 && *cp2 == '=') cp2++;
513 while (*cp2 && *cp2 == ' ') cp2++;
514 if (*cp2 == '"') { /* String translation; may embed "" */
515 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
516 cp2++; cp1--; /* Skip "" surrounding translation */
518 else { /* Numeric translation */
519 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
520 cp1--; /* stop on last non-space char */
522 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
523 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
526 PERL_HASH(hash,key,keylen);
527 hv_store(envhv,key,keylen,newSVpvn(cp2,cp1 - cp2 + 1),hash);
528 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
530 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
531 /* get the PPFs for this process, not the subprocess */
532 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
533 char eqv[LNM$C_NAMLENGTH+1];
535 for (i = 0; ppfs[i]; i++) {
536 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
537 hv_store(envhv,ppfs[i],strlen(ppfs[i]),newSVpv(eqv,trnlen),0);
542 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
543 if (buf) Safefree(buf);
544 if (seenhv) SvREFCNT_dec(seenhv);
545 MUTEX_UNLOCK(&primenv_mutex);
548 } /* end of prime_env_iter */
552 /*{{{ int vmssetenv(char *lnm, char *eqv)*/
553 /* Define or delete an element in the same "environment" as
554 * vmstrnenv(). If an element is to be deleted, it's removed from
555 * the first place it's found. If it's to be set, it's set in the
556 * place designated by the first element of the table vector.
557 * Like setenv() returns 0 for success, non-zero on error.
560 vmssetenv(char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
562 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
563 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
564 unsigned long int retsts, usermode = PSL$C_USER;
565 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
566 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
567 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
568 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
569 $DESCRIPTOR(local,"_LOCAL");
572 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
573 *cp2 = _toupper(*cp1);
574 if (cp1 - lnm > LNM$C_NAMLENGTH) {
575 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
579 lnmdsc.dsc$w_length = cp1 - lnm;
580 if (!tabvec || !*tabvec) tabvec = env_tables;
582 if (!eqv) { /* we're deleting n element */
583 for (curtab = 0; tabvec[curtab]; curtab++) {
584 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
586 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
587 if ((cp1 = strchr(environ[i],'=')) &&
588 !strncmp(environ[i],lnm,cp1 - environ[i])) {
590 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
593 ivenv = 1; retsts = SS$_NOLOGNAM;
595 if (ckWARN(WARN_INTERNAL))
596 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
597 ivenv = 1; retsts = SS$_NOSUCHPGM;
603 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
604 !str$case_blind_compare(&tmpdsc,&clisym)) {
605 unsigned int symtype;
606 if (tabvec[curtab]->dsc$w_length == 12 &&
607 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
608 !str$case_blind_compare(&tmpdsc,&local))
609 symtype = LIB$K_CLI_LOCAL_SYM;
610 else symtype = LIB$K_CLI_GLOBAL_SYM;
611 retsts = lib$delete_symbol(&lnmdsc,&symtype);
612 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
613 if (retsts == LIB$_NOSUCHSYM) continue;
617 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
618 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
619 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
620 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
621 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
625 else { /* we're defining a value */
626 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
628 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
630 if (ckWARN(WARN_INTERNAL))
631 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
632 retsts = SS$_NOSUCHPGM;
636 eqvdsc.dsc$a_pointer = eqv;
637 eqvdsc.dsc$w_length = strlen(eqv);
638 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
639 !str$case_blind_compare(&tmpdsc,&clisym)) {
640 unsigned int symtype;
641 if (tabvec[0]->dsc$w_length == 12 &&
642 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
643 !str$case_blind_compare(&tmpdsc,&local))
644 symtype = LIB$K_CLI_LOCAL_SYM;
645 else symtype = LIB$K_CLI_GLOBAL_SYM;
646 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
649 if (!*eqv) eqvdsc.dsc$w_length = 1;
650 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
651 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH;
652 if (ckWARN(WARN_MISC)) {
653 Perl_warner(aTHX_ WARN_MISC,"Value of logical \"%s\" too long. Truncating to %i bytes",lnm, LNM$C_NAMLENGTH);
656 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
662 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
663 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
664 set_errno(EVMSERR); break;
665 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
666 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
667 set_errno(EINVAL); break;
674 set_vaxc_errno(retsts);
675 return (int) retsts || 44; /* retsts should never be 0, but just in case */
678 /* We reset error values on success because Perl does an hv_fetch()
679 * before each hv_store(), and if the thing we're setting didn't
680 * previously exist, we've got a leftover error message. (Of course,
681 * this fails in the face of
682 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
683 * in that the error reported in $! isn't spurious,
684 * but it's right more often than not.)
686 set_errno(0); set_vaxc_errno(retsts);
690 } /* end of vmssetenv() */
693 /*{{{ void my_setenv(char *lnm, char *eqv)*/
694 /* This has to be a function since there's a prototype for it in proto.h */
696 Perl_my_setenv(pTHX_ char *lnm,char *eqv)
698 if (lnm && *lnm && strlen(lnm) == 7) {
701 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
702 if (!strcmp(uplnm,"DEFAULT")) {
703 if (eqv && *eqv) chdir(eqv);
707 (void) vmssetenv(lnm,eqv,NULL);
713 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
714 /* my_crypt - VMS password hashing
715 * my_crypt() provides an interface compatible with the Unix crypt()
716 * C library function, and uses sys$hash_password() to perform VMS
717 * password hashing. The quadword hashed password value is returned
718 * as a NUL-terminated 8 character string. my_crypt() does not change
719 * the case of its string arguments; in order to match the behavior
720 * of LOGINOUT et al., alphabetic characters in both arguments must
721 * be upcased by the caller.
724 my_crypt(const char *textpasswd, const char *usrname)
726 # ifndef UAI$C_PREFERRED_ALGORITHM
727 # define UAI$C_PREFERRED_ALGORITHM 127
729 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
730 unsigned short int salt = 0;
731 unsigned long int sts;
733 unsigned short int dsc$w_length;
734 unsigned char dsc$b_type;
735 unsigned char dsc$b_class;
736 const char * dsc$a_pointer;
737 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
738 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
739 struct itmlst_3 uailst[3] = {
740 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
741 { sizeof salt, UAI$_SALT, &salt, 0},
742 { 0, 0, NULL, NULL}};
745 usrdsc.dsc$w_length = strlen(usrname);
746 usrdsc.dsc$a_pointer = usrname;
747 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
749 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
753 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
759 if (sts != RMS$_RNF) return NULL;
762 txtdsc.dsc$w_length = strlen(textpasswd);
763 txtdsc.dsc$a_pointer = textpasswd;
764 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
765 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
768 return (char *) hash;
770 } /* end of my_crypt() */
774 static char *mp_do_rmsexpand(pTHX_ char *, char *, int, char *, unsigned);
775 static char *mp_do_fileify_dirspec(pTHX_ char *, char *, int);
776 static char *mp_do_tovmsspec(pTHX_ char *, char *, int);
778 /*{{{int do_rmdir(char *name)*/
780 Perl_do_rmdir(pTHX_ char *name)
782 char dirfile[NAM$C_MAXRSS+1];
786 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
787 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
788 else retval = kill_file(dirfile);
791 } /* end of do_rmdir */
795 * Delete any file to which user has control access, regardless of whether
796 * delete access is explicitly allowed.
797 * Limitations: User must have write access to parent directory.
798 * Does not block signals or ASTs; if interrupted in midstream
799 * may leave file with an altered ACL.
802 /*{{{int kill_file(char *name)*/
804 kill_file(char *name)
806 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
807 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
808 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
810 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
812 unsigned char myace$b_length;
813 unsigned char myace$b_type;
814 unsigned short int myace$w_flags;
815 unsigned long int myace$l_access;
816 unsigned long int myace$l_ident;
817 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
818 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
819 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
821 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
822 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
823 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
824 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
825 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
826 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
828 /* Expand the input spec using RMS, since the CRTL remove() and
829 * system services won't do this by themselves, so we may miss
830 * a file "hiding" behind a logical name or search list. */
831 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
832 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
833 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
834 /* If not, can changing protections help? */
835 if (vaxc$errno != RMS$_PRV) return -1;
837 /* No, so we get our own UIC to use as a rights identifier,
838 * and the insert an ACE at the head of the ACL which allows us
839 * to delete the file.
841 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
842 fildsc.dsc$w_length = strlen(rspec);
843 fildsc.dsc$a_pointer = rspec;
845 newace.myace$l_ident = oldace.myace$l_ident;
846 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
848 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
849 set_errno(ENOENT); break;
851 set_errno(ENOTDIR); break;
853 set_errno(ENODEV); break;
854 case RMS$_SYN: case SS$_INVFILFOROP:
855 set_errno(EINVAL); break;
857 set_errno(EACCES); break;
861 set_vaxc_errno(aclsts);
864 /* Grab any existing ACEs with this identifier in case we fail */
865 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
866 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
867 || fndsts == SS$_NOMOREACE ) {
868 /* Add the new ACE . . . */
869 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
871 if ((rmsts = remove(name))) {
872 /* We blew it - dir with files in it, no write priv for
873 * parent directory, etc. Put things back the way they were. */
874 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
877 addlst[0].bufadr = &oldace;
878 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
885 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
886 /* We just deleted it, so of course it's not there. Some versions of
887 * VMS seem to return success on the unlock operation anyhow (after all
888 * the unlock is successful), but others don't.
890 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
891 if (aclsts & 1) aclsts = fndsts;
894 set_vaxc_errno(aclsts);
900 } /* end of kill_file() */
904 /*{{{int my_mkdir(char *,Mode_t)*/
906 my_mkdir(char *dir, Mode_t mode)
908 STRLEN dirlen = strlen(dir);
911 /* zero length string sometimes gives ACCVIO */
912 if (dirlen == 0) return -1;
914 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
915 * null file name/type. However, it's commonplace under Unix,
916 * so we'll allow it for a gain in portability.
918 if (dir[dirlen-1] == '/') {
919 char *newdir = savepvn(dir,dirlen-1);
920 int ret = mkdir(newdir,mode);
924 else return mkdir(dir,mode);
925 } /* end of my_mkdir */
928 /*{{{int my_chdir(char *)*/
932 STRLEN dirlen = strlen(dir);
935 /* zero length string sometimes gives ACCVIO */
936 if (dirlen == 0) return -1;
938 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
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 = chdir(newdir);
949 else return chdir(dir);
950 } /* end of my_chdir */
954 /*{{{FILE *my_tmpfile()*/
962 if ((fp = tmpfile())) return fp;
964 New(1323,cp,L_tmpnam+24,char);
965 strcpy(cp,"Sys$Scratch:");
966 tmpnam(cp+strlen(cp));
967 strcat(cp,".Perltmp");
968 fp = fopen(cp,"w+","fop=dlt");
974 /* default piping mailbox size */
975 #define PERL_BUFSIZ 512
979 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
981 unsigned long int mbxbufsiz;
982 static unsigned long int syssize = 0;
983 unsigned long int dviitm = DVI$_DEVNAM;
985 char csize[LNM$C_NAMLENGTH+1];
988 unsigned long syiitm = SYI$_MAXBUF;
990 * Get the SYSGEN parameter MAXBUF
992 * If the logical 'PERL_MBX_SIZE' is defined
993 * use the value of the logical instead of PERL_BUFSIZ, but
994 * keep the size between 128 and MAXBUF.
997 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
1000 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
1001 mbxbufsiz = atoi(csize);
1003 mbxbufsiz = PERL_BUFSIZ;
1005 if (mbxbufsiz < 128) mbxbufsiz = 128;
1006 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
1008 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
1010 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
1011 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
1013 } /* end of create_mbx() */
1016 /*{{{ my_popen and my_pclose*/
1018 typedef struct _iosb IOSB;
1019 typedef struct _iosb* pIOSB;
1020 typedef struct _pipe Pipe;
1021 typedef struct _pipe* pPipe;
1022 typedef struct pipe_details Info;
1023 typedef struct pipe_details* pInfo;
1024 typedef struct _srqp RQE;
1025 typedef struct _srqp* pRQE;
1026 typedef struct _tochildbuf CBuf;
1027 typedef struct _tochildbuf* pCBuf;
1030 unsigned short status;
1031 unsigned short count;
1032 unsigned long dvispec;
1035 #pragma member_alignment save
1036 #pragma nomember_alignment quadword
1037 struct _srqp { /* VMS self-relative queue entry */
1038 unsigned long qptr[2];
1040 #pragma member_alignment restore
1041 static RQE RQE_ZERO = {0,0};
1043 struct _tochildbuf {
1046 unsigned short size;
1054 unsigned short chan_in;
1055 unsigned short chan_out;
1057 unsigned int bufsize;
1075 PerlIO *fp; /* stdio file pointer to pipe mailbox */
1076 int pid; /* PID of subprocess */
1077 int mode; /* == 'r' if pipe open for reading */
1078 int done; /* subprocess has completed */
1079 int closing; /* my_pclose is closing this pipe */
1080 unsigned long completion; /* termination status of subprocess */
1081 pPipe in; /* pipe in to sub */
1082 pPipe out; /* pipe out of sub */
1083 pPipe err; /* pipe of sub's sys$error */
1084 int in_done; /* true when in pipe finished */
1089 struct exit_control_block
1091 struct exit_control_block *flink;
1092 unsigned long int (*exit_routine)();
1093 unsigned long int arg_count;
1094 unsigned long int *status_address;
1095 unsigned long int exit_status;
1098 #define RETRY_DELAY "0 ::0.20"
1099 #define MAX_RETRY 50
1101 static int pipe_ef = 0; /* first call to safe_popen inits these*/
1102 static unsigned long mypid;
1103 static unsigned long delaytime[2];
1105 static pInfo open_pipes = NULL;
1106 static $DESCRIPTOR(nl_desc, "NL:");
1109 static unsigned long int
1113 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1114 int sts, did_stuff, need_eof;
1118 first we try sending an EOF...ignore if doesn't work, make sure we
1126 _ckvmssts(sys$setast(0));
1127 if (info->in && !info->in->shut_on_empty) {
1128 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
1132 _ckvmssts(sys$setast(1));
1135 if (did_stuff) sleep(1); /* wait for EOF to have an effect */
1140 _ckvmssts(sys$setast(0));
1141 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1142 sts = sys$forcex(&info->pid,0,&abort);
1143 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1146 _ckvmssts(sys$setast(1));
1149 if (did_stuff) sleep(1); /* wait for them to respond */
1153 _ckvmssts(sys$setast(0));
1154 if (!info->done) { /* We tried to be nice . . . */
1155 sts = sys$delprc(&info->pid,0);
1156 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1158 _ckvmssts(sys$setast(1));
1163 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1164 else if (!(sts & 1)) retsts = sts;
1169 static struct exit_control_block pipe_exitblock =
1170 {(struct exit_control_block *) 0,
1171 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1173 static void pipe_mbxtofd_ast(pPipe p);
1174 static void pipe_tochild1_ast(pPipe p);
1175 static void pipe_tochild2_ast(pPipe p);
1178 popen_completion_ast(pInfo info)
1181 pInfo i = open_pipes;
1185 if (i == info) break;
1188 if (!i) return; /* unlinked, probably freed too */
1190 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
1194 Writing to subprocess ...
1195 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
1197 chan_out may be waiting for "done" flag, or hung waiting
1198 for i/o completion to child...cancel the i/o. This will
1199 put it into "snarf mode" (done but no EOF yet) that discards
1202 Output from subprocess (stdout, stderr) needs to be flushed and
1203 shut down. We try sending an EOF, but if the mbx is full the pipe
1204 routine should still catch the "shut_on_empty" flag, telling it to
1205 use immediate-style reads so that "mbx empty" -> EOF.
1209 if (info->in && !info->in_done) { /* only for mode=w */
1210 if (info->in->shut_on_empty && info->in->need_wake) {
1211 info->in->need_wake = FALSE;
1212 _ckvmssts(sys$dclast(pipe_tochild2_ast,info->in,0));
1214 _ckvmssts(sys$cancel(info->in->chan_out));
1218 if (info->out && !info->out_done) { /* were we also piping output? */
1219 info->out->shut_on_empty = TRUE;
1220 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1221 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1225 if (info->err && !info->err_done) { /* we were piping stderr */
1226 info->err->shut_on_empty = TRUE;
1227 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1228 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1231 _ckvmssts(sys$setef(pipe_ef));
1235 static unsigned long int setup_cmddsc(char *cmd, int check_img);
1236 static void vms_execfree(pTHX);
1239 we actually differ from vmstrnenv since we use this to
1240 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
1241 are pointing to the same thing
1244 static unsigned short
1245 popen_translate(char *logical, char *result)
1248 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
1249 $DESCRIPTOR(d_log,"");
1251 unsigned short length;
1252 unsigned short code;
1254 unsigned short *retlenaddr;
1256 unsigned short l, ifi;
1258 d_log.dsc$a_pointer = logical;
1259 d_log.dsc$w_length = strlen(logical);
1261 itmlst[0].code = LNM$_STRING;
1262 itmlst[0].length = 255;
1263 itmlst[0].buffer_addr = result;
1264 itmlst[0].retlenaddr = &l;
1267 itmlst[1].length = 0;
1268 itmlst[1].buffer_addr = 0;
1269 itmlst[1].retlenaddr = 0;
1271 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
1272 if (iss == SS$_NOLOGNAM) {
1276 if (!(iss&1)) lib$signal(iss);
1279 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
1280 strip it off and return the ifi, if any
1283 if (result[0] == 0x1b && result[1] == 0x00) {
1284 memcpy(&ifi,result+2,2);
1285 strcpy(result,result+4);
1287 return ifi; /* this is the RMS internal file id */
1290 #define MAX_DCL_SYMBOL 255
1291 static void pipe_infromchild_ast(pPipe p);
1294 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
1295 inside an AST routine without worrying about reentrancy and which Perl
1296 memory allocator is being used.
1298 We read data and queue up the buffers, then spit them out one at a
1299 time to the output mailbox when the output mailbox is ready for one.
1302 #define INITIAL_TOCHILDQUEUE 2
1305 pipe_tochild_setup(char *rmbx, char *wmbx)
1310 char mbx1[64], mbx2[64];
1311 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1312 DSC$K_CLASS_S, mbx1},
1313 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1314 DSC$K_CLASS_S, mbx2};
1315 unsigned int dviitm = DVI$_DEVBUFSIZ;
1318 New(1368, p, 1, Pipe);
1320 create_mbx(&p->chan_in , &d_mbx1);
1321 create_mbx(&p->chan_out, &d_mbx2);
1322 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1325 p->shut_on_empty = FALSE;
1326 p->need_wake = FALSE;
1329 p->iosb.status = SS$_NORMAL;
1330 p->iosb2.status = SS$_NORMAL;
1337 n = sizeof(CBuf) + p->bufsize;
1339 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
1340 _ckvmssts(lib$get_vm(&n, &b));
1341 b->buf = (char *) b + sizeof(CBuf);
1342 _ckvmssts(lib$insqhi(b, &p->free));
1345 pipe_tochild2_ast(p);
1346 pipe_tochild1_ast(p);
1352 /* reads the MBX Perl is writing, and queues */
1355 pipe_tochild1_ast(pPipe p)
1359 int iss = p->iosb.status;
1360 int eof = (iss == SS$_ENDOFFILE);
1364 p->shut_on_empty = TRUE;
1366 _ckvmssts(sys$dassgn(p->chan_in));
1372 b->size = p->iosb.count;
1373 _ckvmssts(lib$insqhi(b, &p->wait));
1375 p->need_wake = FALSE;
1376 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
1379 p->retry = 1; /* initial call */
1382 if (eof) { /* flush the free queue, return when done */
1383 int n = sizeof(CBuf) + p->bufsize;
1385 iss = lib$remqti(&p->free, &b);
1386 if (iss == LIB$_QUEWASEMP) return;
1388 _ckvmssts(lib$free_vm(&n, &b));
1392 iss = lib$remqti(&p->free, &b);
1393 if (iss == LIB$_QUEWASEMP) {
1394 int n = sizeof(CBuf) + p->bufsize;
1395 _ckvmssts(lib$get_vm(&n, &b));
1396 b->buf = (char *) b + sizeof(CBuf);
1402 iss = sys$qio(0,p->chan_in,
1403 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
1405 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
1406 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
1411 /* writes queued buffers to output, waits for each to complete before
1415 pipe_tochild2_ast(pPipe p)
1419 int iss = p->iosb2.status;
1420 int n = sizeof(CBuf) + p->bufsize;
1421 int done = (p->info && p->info->done) ||
1422 iss == SS$_CANCEL || iss == SS$_ABORT;
1425 if (p->type) { /* type=1 has old buffer, dispose */
1426 if (p->shut_on_empty) {
1427 _ckvmssts(lib$free_vm(&n, &b));
1429 _ckvmssts(lib$insqhi(b, &p->free));
1434 iss = lib$remqti(&p->wait, &b);
1435 if (iss == LIB$_QUEWASEMP) {
1436 if (p->shut_on_empty) {
1438 _ckvmssts(sys$dassgn(p->chan_out));
1439 *p->pipe_done = TRUE;
1440 _ckvmssts(sys$setef(pipe_ef));
1442 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1443 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1447 p->need_wake = TRUE;
1457 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1458 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1460 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
1461 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
1470 pipe_infromchild_setup(char *rmbx, char *wmbx)
1474 char mbx1[64], mbx2[64];
1475 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1476 DSC$K_CLASS_S, mbx1},
1477 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1478 DSC$K_CLASS_S, mbx2};
1479 unsigned int dviitm = DVI$_DEVBUFSIZ;
1481 New(1367, p, 1, Pipe);
1482 create_mbx(&p->chan_in , &d_mbx1);
1483 create_mbx(&p->chan_out, &d_mbx2);
1485 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1486 New(1367, p->buf, p->bufsize, char);
1487 p->shut_on_empty = FALSE;
1490 p->iosb.status = SS$_NORMAL;
1491 pipe_infromchild_ast(p);
1499 pipe_infromchild_ast(pPipe p)
1502 int iss = p->iosb.status;
1503 int eof = (iss == SS$_ENDOFFILE);
1504 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
1505 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
1507 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
1508 _ckvmssts(sys$dassgn(p->chan_out));
1513 input shutdown if EOF from self (done or shut_on_empty)
1514 output shutdown if closing flag set (my_pclose)
1515 send data/eof from child or eof from self
1516 otherwise, re-read (snarf of data from child)
1521 if (myeof && p->chan_in) { /* input shutdown */
1522 _ckvmssts(sys$dassgn(p->chan_in));
1527 if (myeof || kideof) { /* pass EOF to parent */
1528 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
1529 pipe_infromchild_ast, p,
1532 } else if (eof) { /* eat EOF --- fall through to read*/
1534 } else { /* transmit data */
1535 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
1536 pipe_infromchild_ast,p,
1537 p->buf, p->iosb.count, 0, 0, 0, 0));
1543 /* everything shut? flag as done */
1545 if (!p->chan_in && !p->chan_out) {
1546 *p->pipe_done = TRUE;
1547 _ckvmssts(sys$setef(pipe_ef));
1551 /* write completed (or read, if snarfing from child)
1552 if still have input active,
1553 queue read...immediate mode if shut_on_empty so we get EOF if empty
1555 check if Perl reading, generate EOFs as needed
1561 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
1562 pipe_infromchild_ast,p,
1563 p->buf, p->bufsize, 0, 0, 0, 0);
1564 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
1566 } else { /* send EOFs for extra reads */
1567 p->iosb.status = SS$_ENDOFFILE;
1568 p->iosb.dvispec = 0;
1569 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
1571 pipe_infromchild_ast, p, 0, 0, 0, 0));
1577 pipe_mbxtofd_setup(int fd, char *out)
1582 unsigned long dviitm = DVI$_DEVBUFSIZ;
1584 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
1585 DSC$K_CLASS_S, mbx};
1587 /* things like terminals and mbx's don't need this filter */
1588 if (fd && fstat(fd,&s) == 0) {
1589 unsigned long dviitm = DVI$_DEVCHAR, devchar;
1590 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
1591 DSC$K_CLASS_S, s.st_dev};
1593 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
1594 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
1595 strcpy(out, s.st_dev);
1600 New(1366, p, 1, Pipe);
1601 p->fd_out = dup(fd);
1602 create_mbx(&p->chan_in, &d_mbx);
1603 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1604 New(1366, p->buf, p->bufsize+1, char);
1605 p->shut_on_empty = FALSE;
1610 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
1611 pipe_mbxtofd_ast, p,
1612 p->buf, p->bufsize, 0, 0, 0, 0));
1618 pipe_mbxtofd_ast(pPipe p)
1621 int iss = p->iosb.status;
1622 int done = p->info->done;
1624 int eof = (iss == SS$_ENDOFFILE);
1625 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
1626 int err = !(iss&1) && !eof;
1629 if (done && myeof) { /* end piping */
1631 sys$dassgn(p->chan_in);
1632 *p->pipe_done = TRUE;
1633 _ckvmssts(sys$setef(pipe_ef));
1637 if (!err && !eof) { /* good data to send to file */
1638 p->buf[p->iosb.count] = '\n';
1639 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
1642 if (p->retry < MAX_RETRY) {
1643 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
1653 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
1654 pipe_mbxtofd_ast, p,
1655 p->buf, p->bufsize, 0, 0, 0, 0);
1656 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
1661 typedef struct _pipeloc PLOC;
1662 typedef struct _pipeloc* pPLOC;
1666 char dir[NAM$C_MAXRSS+1];
1668 static pPLOC head_PLOC = 0;
1676 AV *av = GvAVn(PL_incgv);
1681 char temp[NAM$C_MAXRSS+1];
1684 /* the . directory from @INC comes last */
1687 p->next = head_PLOC;
1689 strcpy(p->dir,"./");
1691 /* get the directory from $^X */
1693 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
1694 strcpy(temp, PL_origargv[0]);
1695 x = strrchr(temp,']');
1698 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
1700 p->next = head_PLOC;
1702 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1703 p->dir[NAM$C_MAXRSS] = '\0';
1707 /* reverse order of @INC entries, skip "." since entered above */
1709 for (i = 0; i <= AvFILL(av); i++) {
1710 dirsv = *av_fetch(av,i,TRUE);
1712 if (SvROK(dirsv)) continue;
1713 dir = SvPVx(dirsv,n_a);
1714 if (strcmp(dir,".") == 0) continue;
1715 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
1719 p->next = head_PLOC;
1721 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1722 p->dir[NAM$C_MAXRSS] = '\0';
1725 /* most likely spot (ARCHLIB) put first in the list */
1728 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
1730 p->next = head_PLOC;
1732 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1733 p->dir[NAM$C_MAXRSS] = '\0';
1743 static int vmspipe_file_status = 0;
1744 static char vmspipe_file[NAM$C_MAXRSS+1];
1746 /* already found? Check and use ... need read+execute permission */
1748 if (vmspipe_file_status == 1) {
1749 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
1750 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
1751 return vmspipe_file;
1753 vmspipe_file_status = 0;
1756 /* scan through stored @INC, $^X */
1758 if (vmspipe_file_status == 0) {
1759 char file[NAM$C_MAXRSS+1];
1760 pPLOC p = head_PLOC;
1763 strcpy(file, p->dir);
1764 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
1765 file[NAM$C_MAXRSS] = '\0';
1768 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
1770 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
1771 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
1772 vmspipe_file_status = 1;
1773 return vmspipe_file;
1776 vmspipe_file_status = -1; /* failed, use tempfiles */
1783 vmspipe_tempfile(void)
1785 char file[NAM$C_MAXRSS+1];
1787 static int index = 0;
1790 /* create a tempfile */
1792 /* we can't go from W, shr=get to R, shr=get without
1793 an intermediate vulnerable state, so don't bother trying...
1795 and lib$spawn doesn't shr=put, so have to close the write
1797 So... match up the creation date/time and the FID to
1798 make sure we're dealing with the same file
1803 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
1804 fp = fopen(file,"w");
1806 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
1807 fp = fopen(file,"w");
1809 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
1810 fp = fopen(file,"w");
1813 if (!fp) return 0; /* we're hosed */
1815 fprintf(fp,"$! 'f$verify(0)\n");
1816 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
1817 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
1818 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
1819 fprintf(fp,"$ perl_on = \"set noon\"\n");
1820 fprintf(fp,"$ perl_exit = \"exit\"\n");
1821 fprintf(fp,"$ perl_del = \"delete\"\n");
1822 fprintf(fp,"$ pif = \"if\"\n");
1823 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
1824 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define sys$input 'perl_popen_in'\n");
1825 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define sys$error 'perl_popen_err'\n");
1826 fprintf(fp,"$ cmd = perl_popen_cmd\n");
1827 fprintf(fp,"$! --- get rid of global symbols\n");
1828 fprintf(fp,"$ perl_del/symbol/global perl_popen_in\n");
1829 fprintf(fp,"$ perl_del/symbol/global perl_popen_err\n");
1830 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd\n");
1831 fprintf(fp,"$ perl_on\n");
1832 fprintf(fp,"$ 'cmd\n");
1833 fprintf(fp,"$ perl_status = $STATUS\n");
1834 fprintf(fp,"$ perl_del 'perl_cfile'\n");
1835 fprintf(fp,"$ perl_exit 'perl_status'\n");
1838 fgetname(fp, file, 1);
1839 fstat(fileno(fp), &s0);
1842 fp = fopen(file,"r","shr=get");
1844 fstat(fileno(fp), &s1);
1846 if (s0.st_ino[0] != s1.st_ino[0] ||
1847 s0.st_ino[1] != s1.st_ino[1] ||
1848 s0.st_ino[2] != s1.st_ino[2] ||
1849 s0.st_ctime != s1.st_ctime ) {
1860 safe_popen(char *cmd, char *mode)
1863 static int handler_set_up = FALSE;
1864 unsigned long int sts, flags=1; /* nowait - gnu c doesn't allow &1 */
1865 unsigned int table = LIB$K_CLI_GLOBAL_SYM;
1866 char *p, symbol[MAX_DCL_SYMBOL+1], *vmspipe;
1867 char in[512], out[512], err[512], mbx[512];
1869 char tfilebuf[NAM$C_MAXRSS+1];
1871 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
1872 DSC$K_CLASS_S, symbol};
1873 struct dsc$descriptor_s d_out = {0, DSC$K_DTYPE_T,
1874 DSC$K_CLASS_S, out};
1875 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
1877 $DESCRIPTOR(d_sym_cmd,"PERL_POPEN_CMD");
1878 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
1879 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
1881 /* once-per-program initialization...
1882 note that the SETAST calls and the dual test of pipe_ef
1883 makes sure that only the FIRST thread through here does
1884 the initialization...all other threads wait until it's
1887 Yeah, uglier than a pthread call, it's got all the stuff inline
1888 rather than in a separate routine.
1892 _ckvmssts(sys$setast(0));
1894 unsigned long int pidcode = JPI$_PID;
1895 $DESCRIPTOR(d_delay, RETRY_DELAY);
1896 _ckvmssts(lib$get_ef(&pipe_ef));
1897 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
1898 _ckvmssts(sys$bintim(&d_delay, delaytime));
1900 if (!handler_set_up) {
1901 _ckvmssts(sys$dclexh(&pipe_exitblock));
1902 handler_set_up = TRUE;
1904 _ckvmssts(sys$setast(1));
1907 /* see if we can find a VMSPIPE.COM */
1910 vmspipe = find_vmspipe();
1912 strcpy(tfilebuf+1,vmspipe);
1913 } else { /* uh, oh...we're in tempfile hell */
1914 tpipe = vmspipe_tempfile();
1915 if (!tpipe) { /* a fish popular in Boston */
1916 if (ckWARN(WARN_PIPE)) {
1917 Perl_warner(aTHX_ WARN_PIPE,"unable to find VMSPIPE.COM for i/o piping");
1921 fgetname(tpipe,tfilebuf+1,1);
1923 vmspipedsc.dsc$a_pointer = tfilebuf;
1924 vmspipedsc.dsc$w_length = strlen(tfilebuf);
1926 if (!(setup_cmddsc(cmd,0) & 1)) { set_errno(EINVAL); return Nullfp; }
1927 New(1301,info,1,Info);
1931 info->completion = 0;
1932 info->closing = FALSE;
1936 info->in_done = TRUE;
1937 info->out_done = TRUE;
1938 info->err_done = TRUE;
1940 if (*mode == 'r') { /* piping from subroutine */
1943 info->out = pipe_infromchild_setup(mbx,out);
1945 info->out->pipe_done = &info->out_done;
1946 info->out_done = FALSE;
1947 info->out->info = info;
1949 info->fp = PerlIO_open(mbx, mode);
1950 if (!info->fp && info->out) {
1951 sys$cancel(info->out->chan_out);
1953 while (!info->out_done) {
1955 _ckvmssts(sys$setast(0));
1956 done = info->out_done;
1957 if (!done) _ckvmssts(sys$clref(pipe_ef));
1958 _ckvmssts(sys$setast(1));
1959 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
1962 if (info->out->buf) Safefree(info->out->buf);
1963 Safefree(info->out);
1968 info->err = pipe_mbxtofd_setup(fileno(stderr), err);
1970 info->err->pipe_done = &info->err_done;
1971 info->err_done = FALSE;
1972 info->err->info = info;
1975 } else { /* piping to subroutine , mode=w*/
1978 info->in = pipe_tochild_setup(in,mbx);
1979 info->fp = PerlIO_open(mbx, mode);
1981 info->in->pipe_done = &info->in_done;
1982 info->in_done = FALSE;
1983 info->in->info = info;
1987 if (!info->fp && info->in) {
1989 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
1990 0, 0, 0, 0, 0, 0, 0, 0));
1992 while (!info->in_done) {
1994 _ckvmssts(sys$setast(0));
1995 done = info->in_done;
1996 if (!done) _ckvmssts(sys$clref(pipe_ef));
1997 _ckvmssts(sys$setast(1));
1998 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2001 if (info->in->buf) Safefree(info->in->buf);
2007 /* if SYS$ERROR == SYS$OUTPUT, use only one mbx */
2010 fgetname(stderr, err);
2011 if (strncmp(err,"SYS$ERROR:",10) == 0) {
2012 fgetname(stdout, out);
2013 if (strncmp(out,"SYS$OUTPUT:",11) == 0) {
2014 if (popen_translate("SYS$OUTPUT",out) == popen_translate("SYS$ERROR",err)) {
2020 info->out = pipe_mbxtofd_setup(fileno(stdout), out);
2022 info->out->pipe_done = &info->out_done;
2023 info->out_done = FALSE;
2024 info->out->info = info;
2027 info->err = pipe_mbxtofd_setup(fileno(stderr), err);
2029 info->err->pipe_done = &info->err_done;
2030 info->err_done = FALSE;
2031 info->err->info = info;
2037 d_out.dsc$w_length = strlen(out); /* lib$spawn sets SYS$OUTPUT so can meld*/
2039 symbol[MAX_DCL_SYMBOL] = '\0';
2041 strncpy(symbol, in, MAX_DCL_SYMBOL);
2042 d_symbol.dsc$w_length = strlen(symbol);
2043 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
2045 strncpy(symbol, err, MAX_DCL_SYMBOL);
2046 d_symbol.dsc$w_length = strlen(symbol);
2047 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
2050 p = VMScmd.dsc$a_pointer;
2051 while (*p && *p != '\n') p++;
2052 *p = '\0'; /* truncate on \n */
2053 p = VMScmd.dsc$a_pointer;
2054 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
2055 if (*p == '$') p++; /* remove leading $ */
2056 while (*p == ' ' || *p == '\t') p++;
2057 strncpy(symbol, p, MAX_DCL_SYMBOL);
2058 d_symbol.dsc$w_length = strlen(symbol);
2059 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
2061 _ckvmssts(sys$setast(0));
2062 info->next=open_pipes; /* prepend to list */
2064 _ckvmssts(sys$setast(1));
2065 _ckvmssts(lib$spawn(&vmspipedsc, &nl_desc, &d_out, &flags,
2066 0, &info->pid, &info->completion,
2067 0, popen_completion_ast,info,0,0,0));
2069 /* if we were using a tempfile, close it now */
2071 if (tpipe) fclose(tpipe);
2073 /* once the subprocess is spawned, its copied the symbols and
2074 we can get rid of ours */
2076 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
2077 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
2078 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
2082 PL_forkprocess = info->pid;
2084 } /* end of safe_popen */
2087 /*{{{ FILE *my_popen(char *cmd, char *mode)*/
2089 Perl_my_popen(pTHX_ char *cmd, char *mode)
2092 TAINT_PROPER("popen");
2093 PERL_FLUSHALL_FOR_CHILD;
2094 return safe_popen(cmd,mode);
2099 /*{{{ I32 my_pclose(FILE *fp)*/
2100 I32 Perl_my_pclose(pTHX_ FILE *fp)
2103 pInfo info, last = NULL;
2104 unsigned long int retsts;
2107 for (info = open_pipes; info != NULL; last = info, info = info->next)
2108 if (info->fp == fp) break;
2110 if (info == NULL) { /* no such pipe open */
2111 set_errno(ECHILD); /* quoth POSIX */
2112 set_vaxc_errno(SS$_NONEXPR);
2116 /* If we were writing to a subprocess, insure that someone reading from
2117 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
2118 * produce an EOF record in the mailbox.
2120 * well, at least sometimes it *does*, so we have to watch out for
2121 * the first EOF closing the pipe (and DASSGN'ing the channel)...
2124 fsync(fileno(info->fp)); /* first, flush data */
2126 _ckvmssts(sys$setast(0));
2127 info->closing = TRUE;
2128 done = info->done && info->in_done && info->out_done && info->err_done;
2129 /* hanging on write to Perl's input? cancel it */
2130 if (info->mode == 'r' && info->out && !info->out_done) {
2131 if (info->out->chan_out) {
2132 _ckvmssts(sys$cancel(info->out->chan_out));
2133 if (!info->out->chan_in) { /* EOF generation, need AST */
2134 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
2138 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
2139 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2141 _ckvmssts(sys$setast(1));
2142 PerlIO_close(info->fp);
2145 we have to wait until subprocess completes, but ALSO wait until all
2146 the i/o completes...otherwise we'll be freeing the "info" structure
2147 that the i/o ASTs could still be using...
2151 _ckvmssts(sys$setast(0));
2152 done = info->done && info->in_done && info->out_done && info->err_done;
2153 if (!done) _ckvmssts(sys$clref(pipe_ef));
2154 _ckvmssts(sys$setast(1));
2155 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2157 retsts = info->completion;
2159 /* remove from list of open pipes */
2160 _ckvmssts(sys$setast(0));
2161 if (last) last->next = info->next;
2162 else open_pipes = info->next;
2163 _ckvmssts(sys$setast(1));
2165 /* free buffers and structures */
2168 if (info->in->buf) Safefree(info->in->buf);
2172 if (info->out->buf) Safefree(info->out->buf);
2173 Safefree(info->out);
2176 if (info->err->buf) Safefree(info->err->buf);
2177 Safefree(info->err);
2183 } /* end of my_pclose() */
2185 /* sort-of waitpid; use only with popen() */
2186 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
2188 my_waitpid(Pid_t pid, int *statusp, int flags)
2194 for (info = open_pipes; info != NULL; info = info->next)
2195 if (info->pid == pid) break;
2197 if (info != NULL) { /* we know about this child */
2198 while (!info->done) {
2199 _ckvmssts(sys$setast(0));
2201 if (!done) _ckvmssts(sys$clref(pipe_ef));
2202 _ckvmssts(sys$setast(1));
2203 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2206 *statusp = info->completion;
2209 else { /* we haven't heard of this child */
2210 $DESCRIPTOR(intdsc,"0 00:00:01");
2211 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
2212 unsigned long int interval[2],sts;
2214 if (ckWARN(WARN_EXEC)) {
2215 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
2216 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
2217 if (ownerpid != mypid)
2218 Perl_warner(aTHX_ WARN_EXEC,"pid %x not a child",pid);
2221 _ckvmssts(sys$bintim(&intdsc,interval));
2222 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
2223 _ckvmssts(sys$schdwk(0,0,interval,0));
2224 _ckvmssts(sys$hiber());
2226 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
2229 /* There's no easy way to find the termination status a child we're
2230 * not aware of beforehand. If we're really interested in the future,
2231 * we can go looking for a termination mailbox, or chase after the
2232 * accounting record for the process.
2238 } /* end of waitpid() */
2243 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
2245 my_gconvert(double val, int ndig, int trail, char *buf)
2247 static char __gcvtbuf[DBL_DIG+1];
2250 loc = buf ? buf : __gcvtbuf;
2252 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
2254 sprintf(loc,"%.*g",ndig,val);
2260 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
2261 return gcvt(val,ndig,loc);
2264 loc[0] = '0'; loc[1] = '\0';
2272 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
2273 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
2274 * to expand file specification. Allows for a single default file
2275 * specification and a simple mask of options. If outbuf is non-NULL,
2276 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
2277 * the resultant file specification is placed. If outbuf is NULL, the
2278 * resultant file specification is placed into a static buffer.
2279 * The third argument, if non-NULL, is taken to be a default file
2280 * specification string. The fourth argument is unused at present.
2281 * rmesexpand() returns the address of the resultant string if
2282 * successful, and NULL on error.
2284 static char *mp_do_tounixspec(pTHX_ char *, char *, int);
2287 mp_do_rmsexpand(pTHX_ char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
2289 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
2290 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
2291 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
2292 struct FAB myfab = cc$rms_fab;
2293 struct NAM mynam = cc$rms_nam;
2295 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
2297 if (!filespec || !*filespec) {
2298 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
2302 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
2303 else outbuf = __rmsexpand_retbuf;
2305 if ((isunix = (strchr(filespec,'/') != NULL))) {
2306 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
2307 filespec = vmsfspec;
2310 myfab.fab$l_fna = filespec;
2311 myfab.fab$b_fns = strlen(filespec);
2312 myfab.fab$l_nam = &mynam;
2314 if (defspec && *defspec) {
2315 if (strchr(defspec,'/') != NULL) {
2316 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
2319 myfab.fab$l_dna = defspec;
2320 myfab.fab$b_dns = strlen(defspec);
2323 mynam.nam$l_esa = esa;
2324 mynam.nam$b_ess = sizeof esa;
2325 mynam.nam$l_rsa = outbuf;
2326 mynam.nam$b_rss = NAM$C_MAXRSS;
2328 retsts = sys$parse(&myfab,0,0);
2329 if (!(retsts & 1)) {
2330 mynam.nam$b_nop |= NAM$M_SYNCHK;
2331 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
2332 retsts = sys$parse(&myfab,0,0);
2333 if (retsts & 1) goto expanded;
2335 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
2336 (void) sys$parse(&myfab,0,0); /* Free search context */
2337 if (out) Safefree(out);
2338 set_vaxc_errno(retsts);
2339 if (retsts == RMS$_PRV) set_errno(EACCES);
2340 else if (retsts == RMS$_DEV) set_errno(ENODEV);
2341 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
2342 else set_errno(EVMSERR);
2345 retsts = sys$search(&myfab,0,0);
2346 if (!(retsts & 1) && retsts != RMS$_FNF) {
2347 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2348 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2349 if (out) Safefree(out);
2350 set_vaxc_errno(retsts);
2351 if (retsts == RMS$_PRV) set_errno(EACCES);
2352 else set_errno(EVMSERR);
2356 /* If the input filespec contained any lowercase characters,
2357 * downcase the result for compatibility with Unix-minded code. */
2359 for (out = myfab.fab$l_fna; *out; out++)
2360 if (islower(*out)) { haslower = 1; break; }
2361 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
2362 else { out = esa; speclen = mynam.nam$b_esl; }
2363 /* Trim off null fields added by $PARSE
2364 * If type > 1 char, must have been specified in original or default spec
2365 * (not true for version; $SEARCH may have added version of existing file).
2367 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
2368 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
2369 (mynam.nam$l_ver - mynam.nam$l_type == 1);
2370 if (trimver || trimtype) {
2371 if (defspec && *defspec) {
2372 char defesa[NAM$C_MAXRSS];
2373 struct FAB deffab = cc$rms_fab;
2374 struct NAM defnam = cc$rms_nam;
2376 deffab.fab$l_nam = &defnam;
2377 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
2378 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
2379 defnam.nam$b_nop = NAM$M_SYNCHK;
2380 if (sys$parse(&deffab,0,0) & 1) {
2381 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
2382 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
2385 if (trimver) speclen = mynam.nam$l_ver - out;
2387 /* If we didn't already trim version, copy down */
2388 if (speclen > mynam.nam$l_ver - out)
2389 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
2390 speclen - (mynam.nam$l_ver - out));
2391 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
2394 /* If we just had a directory spec on input, $PARSE "helpfully"
2395 * adds an empty name and type for us */
2396 if (mynam.nam$l_name == mynam.nam$l_type &&
2397 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
2398 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
2399 speclen = mynam.nam$l_name - out;
2400 out[speclen] = '\0';
2401 if (haslower) __mystrtolower(out);
2403 /* Have we been working with an expanded, but not resultant, spec? */
2404 /* Also, convert back to Unix syntax if necessary. */
2405 if (!mynam.nam$b_rsl) {
2407 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
2409 else strcpy(outbuf,esa);
2412 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
2413 strcpy(outbuf,tmpfspec);
2415 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2416 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
2417 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2421 /* External entry points */
2422 char *Perl_rmsexpand(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2423 { return do_rmsexpand(spec,buf,0,def,opt); }
2424 char *Perl_rmsexpand_ts(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2425 { return do_rmsexpand(spec,buf,1,def,opt); }
2429 ** The following routines are provided to make life easier when
2430 ** converting among VMS-style and Unix-style directory specifications.
2431 ** All will take input specifications in either VMS or Unix syntax. On
2432 ** failure, all return NULL. If successful, the routines listed below
2433 ** return a pointer to a buffer containing the appropriately
2434 ** reformatted spec (and, therefore, subsequent calls to that routine
2435 ** will clobber the result), while the routines of the same names with
2436 ** a _ts suffix appended will return a pointer to a mallocd string
2437 ** containing the appropriately reformatted spec.
2438 ** In all cases, only explicit syntax is altered; no check is made that
2439 ** the resulting string is valid or that the directory in question
2442 ** fileify_dirspec() - convert a directory spec into the name of the
2443 ** directory file (i.e. what you can stat() to see if it's a dir).
2444 ** The style (VMS or Unix) of the result is the same as the style
2445 ** of the parameter passed in.
2446 ** pathify_dirspec() - convert a directory spec into a path (i.e.
2447 ** what you prepend to a filename to indicate what directory it's in).
2448 ** The style (VMS or Unix) of the result is the same as the style
2449 ** of the parameter passed in.
2450 ** tounixpath() - convert a directory spec into a Unix-style path.
2451 ** tovmspath() - convert a directory spec into a VMS-style path.
2452 ** tounixspec() - convert any file spec into a Unix-style file spec.
2453 ** tovmsspec() - convert any file spec into a VMS-style spec.
2455 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
2456 ** Permission is given to distribute this code as part of the Perl
2457 ** standard distribution under the terms of the GNU General Public
2458 ** License or the Perl Artistic License. Copies of each may be
2459 ** found in the Perl standard distribution.
2462 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
2463 static char *mp_do_fileify_dirspec(pTHX_ char *dir,char *buf,int ts)
2465 static char __fileify_retbuf[NAM$C_MAXRSS+1];
2466 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
2467 char *retspec, *cp1, *cp2, *lastdir;
2468 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
2470 if (!dir || !*dir) {
2471 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
2473 dirlen = strlen(dir);
2474 while (dirlen && dir[dirlen-1] == '/') --dirlen;
2475 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
2476 strcpy(trndir,"/sys$disk/000000");
2480 if (dirlen > NAM$C_MAXRSS) {
2481 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
2483 if (!strpbrk(dir+1,"/]>:")) {
2484 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2485 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
2487 dirlen = strlen(dir);
2490 strncpy(trndir,dir,dirlen);
2491 trndir[dirlen] = '\0';
2494 /* If we were handed a rooted logical name or spec, treat it like a
2495 * simple directory, so that
2496 * $ Define myroot dev:[dir.]
2497 * ... do_fileify_dirspec("myroot",buf,1) ...
2498 * does something useful.
2500 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".]")) {
2501 dir[--dirlen] = '\0';
2502 dir[dirlen-1] = ']';
2505 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
2506 /* If we've got an explicit filename, we can just shuffle the string. */
2507 if (*(cp1+1)) hasfilename = 1;
2508 /* Similarly, we can just back up a level if we've got multiple levels
2509 of explicit directories in a VMS spec which ends with directories. */
2511 for (cp2 = cp1; cp2 > dir; cp2--) {
2513 *cp2 = *cp1; *cp1 = '\0';
2517 if (*cp2 == '[' || *cp2 == '<') break;
2522 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
2523 if (dir[0] == '.') {
2524 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
2525 return do_fileify_dirspec("[]",buf,ts);
2526 else if (dir[1] == '.' &&
2527 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
2528 return do_fileify_dirspec("[-]",buf,ts);
2530 if (dirlen && dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
2531 dirlen -= 1; /* to last element */
2532 lastdir = strrchr(dir,'/');
2534 else if ((cp1 = strstr(dir,"/.")) != NULL) {
2535 /* If we have "/." or "/..", VMSify it and let the VMS code
2536 * below expand it, rather than repeating the code to handle
2537 * relative components of a filespec here */
2539 if (*(cp1+2) == '.') cp1++;
2540 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
2541 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
2542 if (strchr(vmsdir,'/') != NULL) {
2543 /* If do_tovmsspec() returned it, it must have VMS syntax
2544 * delimiters in it, so it's a mixed VMS/Unix spec. We take
2545 * the time to check this here only so we avoid a recursion
2546 * loop; otherwise, gigo.
2548 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
2550 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
2551 return do_tounixspec(trndir,buf,ts);
2554 } while ((cp1 = strstr(cp1,"/.")) != NULL);
2555 lastdir = strrchr(dir,'/');
2557 else if (dirlen >= 7 && !strcmp(&dir[dirlen-7],"/000000")) {
2558 /* Ditto for specs that end in an MFD -- let the VMS code
2559 * figure out whether it's a real device or a rooted logical. */
2560 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
2561 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
2562 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
2563 return do_tounixspec(trndir,buf,ts);
2566 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
2567 !(lastdir = cp1 = strrchr(dir,']')) &&
2568 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
2569 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
2571 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2572 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2573 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2574 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2575 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2576 (ver || *cp3)))))) {
2578 set_vaxc_errno(RMS$_DIR);
2584 /* If we lead off with a device or rooted logical, add the MFD
2585 if we're specifying a top-level directory. */
2586 if (lastdir && *dir == '/') {
2588 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
2595 retlen = dirlen + (addmfd ? 13 : 6);
2596 if (buf) retspec = buf;
2597 else if (ts) New(1309,retspec,retlen+1,char);
2598 else retspec = __fileify_retbuf;
2600 dirlen = lastdir - dir;
2601 memcpy(retspec,dir,dirlen);
2602 strcpy(&retspec[dirlen],"/000000");
2603 strcpy(&retspec[dirlen+7],lastdir);
2606 memcpy(retspec,dir,dirlen);
2607 retspec[dirlen] = '\0';
2609 /* We've picked up everything up to the directory file name.
2610 Now just add the type and version, and we're set. */
2611 strcat(retspec,".dir;1");
2614 else { /* VMS-style directory spec */
2615 char esa[NAM$C_MAXRSS+1], term, *cp;
2616 unsigned long int sts, cmplen, haslower = 0;
2617 struct FAB dirfab = cc$rms_fab;
2618 struct NAM savnam, dirnam = cc$rms_nam;
2620 dirfab.fab$b_fns = strlen(dir);
2621 dirfab.fab$l_fna = dir;
2622 dirfab.fab$l_nam = &dirnam;
2623 dirfab.fab$l_dna = ".DIR;1";
2624 dirfab.fab$b_dns = 6;
2625 dirnam.nam$b_ess = NAM$C_MAXRSS;
2626 dirnam.nam$l_esa = esa;
2628 for (cp = dir; *cp; cp++)
2629 if (islower(*cp)) { haslower = 1; break; }
2630 if (!((sts = sys$parse(&dirfab))&1)) {
2631 if (dirfab.fab$l_sts == RMS$_DIR) {
2632 dirnam.nam$b_nop |= NAM$M_SYNCHK;
2633 sts = sys$parse(&dirfab) & 1;
2637 set_vaxc_errno(dirfab.fab$l_sts);
2643 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
2644 /* Yes; fake the fnb bits so we'll check type below */
2645 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
2647 else { /* No; just work with potential name */
2648 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
2650 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
2651 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2652 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2657 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
2658 cp1 = strchr(esa,']');
2659 if (!cp1) cp1 = strchr(esa,'>');
2660 if (cp1) { /* Should always be true */
2661 dirnam.nam$b_esl -= cp1 - esa - 1;
2662 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
2665 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
2666 /* Yep; check version while we're at it, if it's there. */
2667 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
2668 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
2669 /* Something other than .DIR[;1]. Bzzt. */
2670 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2671 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2673 set_vaxc_errno(RMS$_DIR);
2677 esa[dirnam.nam$b_esl] = '\0';
2678 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
2679 /* They provided at least the name; we added the type, if necessary, */
2680 if (buf) retspec = buf; /* in sys$parse() */
2681 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
2682 else retspec = __fileify_retbuf;
2683 strcpy(retspec,esa);
2684 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2685 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2688 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
2689 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
2691 dirnam.nam$b_esl -= 9;
2693 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
2694 if (cp1 == NULL) { /* should never happen */
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);
2701 retlen = strlen(esa);
2702 if ((cp1 = strrchr(esa,'.')) != NULL) {
2703 /* There's more than one directory in the path. Just roll back. */
2705 if (buf) retspec = buf;
2706 else if (ts) New(1311,retspec,retlen+7,char);
2707 else retspec = __fileify_retbuf;
2708 strcpy(retspec,esa);
2711 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
2712 /* Go back and expand rooted logical name */
2713 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
2714 if (!(sys$parse(&dirfab) & 1)) {
2715 dirnam.nam$l_rlf = NULL;
2716 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2718 set_vaxc_errno(dirfab.fab$l_sts);
2721 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
2722 if (buf) retspec = buf;
2723 else if (ts) New(1312,retspec,retlen+16,char);
2724 else retspec = __fileify_retbuf;
2725 cp1 = strstr(esa,"][");
2727 memcpy(retspec,esa,dirlen);
2728 if (!strncmp(cp1+2,"000000]",7)) {
2729 retspec[dirlen-1] = '\0';
2730 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
2731 if (*cp1 == '.') *cp1 = ']';
2733 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
2734 memcpy(cp1+1,"000000]",7);
2738 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
2739 retspec[retlen] = '\0';
2740 /* Convert last '.' to ']' */
2741 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
2742 if (*cp1 == '.') *cp1 = ']';
2744 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
2745 memcpy(cp1+1,"000000]",7);
2749 else { /* This is a top-level dir. Add the MFD to the path. */
2750 if (buf) retspec = buf;
2751 else if (ts) New(1312,retspec,retlen+16,char);
2752 else retspec = __fileify_retbuf;
2755 while (*cp1 != ':') *(cp2++) = *(cp1++);
2756 strcpy(cp2,":[000000]");
2761 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2762 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2763 /* We've set up the string up through the filename. Add the
2764 type and version, and we're done. */
2765 strcat(retspec,".DIR;1");
2767 /* $PARSE may have upcased filespec, so convert output to lower
2768 * case if input contained any lowercase characters. */
2769 if (haslower) __mystrtolower(retspec);
2772 } /* end of do_fileify_dirspec() */
2774 /* External entry points */
2775 char *Perl_fileify_dirspec(pTHX_ char *dir, char *buf)
2776 { return do_fileify_dirspec(dir,buf,0); }
2777 char *Perl_fileify_dirspec_ts(pTHX_ char *dir, char *buf)
2778 { return do_fileify_dirspec(dir,buf,1); }
2780 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
2781 static char *mp_do_pathify_dirspec(pTHX_ char *dir,char *buf, int ts)
2783 static char __pathify_retbuf[NAM$C_MAXRSS+1];
2784 unsigned long int retlen;
2785 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
2787 if (!dir || !*dir) {
2788 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
2791 if (*dir) strcpy(trndir,dir);
2792 else getcwd(trndir,sizeof trndir - 1);
2794 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
2795 && my_trnlnm(trndir,trndir,0)) {
2796 STRLEN trnlen = strlen(trndir);
2798 /* Trap simple rooted lnms, and return lnm:[000000] */
2799 if (!strcmp(trndir+trnlen-2,".]")) {
2800 if (buf) retpath = buf;
2801 else if (ts) New(1318,retpath,strlen(dir)+10,char);
2802 else retpath = __pathify_retbuf;
2803 strcpy(retpath,dir);
2804 strcat(retpath,":[000000]");
2810 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
2811 if (*dir == '.' && (*(dir+1) == '\0' ||
2812 (*(dir+1) == '.' && *(dir+2) == '\0')))
2813 retlen = 2 + (*(dir+1) != '\0');
2815 if ( !(cp1 = strrchr(dir,'/')) &&
2816 !(cp1 = strrchr(dir,']')) &&
2817 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
2818 if ((cp2 = strchr(cp1,'.')) != NULL &&
2819 (*(cp2-1) != '/' || /* Trailing '.', '..', */
2820 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
2821 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
2822 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
2824 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2825 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2826 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2827 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2828 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2829 (ver || *cp3)))))) {
2831 set_vaxc_errno(RMS$_DIR);
2834 retlen = cp2 - dir + 1;
2836 else { /* No file type present. Treat the filename as a directory. */
2837 retlen = strlen(dir) + 1;
2840 if (buf) retpath = buf;
2841 else if (ts) New(1313,retpath,retlen+1,char);
2842 else retpath = __pathify_retbuf;
2843 strncpy(retpath,dir,retlen-1);
2844 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
2845 retpath[retlen-1] = '/'; /* with '/', add it. */
2846 retpath[retlen] = '\0';
2848 else retpath[retlen-1] = '\0';
2850 else { /* VMS-style directory spec */
2851 char esa[NAM$C_MAXRSS+1], *cp;
2852 unsigned long int sts, cmplen, haslower;
2853 struct FAB dirfab = cc$rms_fab;
2854 struct NAM savnam, dirnam = cc$rms_nam;
2856 /* If we've got an explicit filename, we can just shuffle the string. */
2857 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
2858 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
2859 if ((cp2 = strchr(cp1,'.')) != NULL) {
2861 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2862 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2863 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2864 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2865 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2866 (ver || *cp3)))))) {
2868 set_vaxc_errno(RMS$_DIR);
2872 else { /* No file type, so just draw name into directory part */
2873 for (cp2 = cp1; *cp2; cp2++) ;
2876 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
2878 /* We've now got a VMS 'path'; fall through */
2880 dirfab.fab$b_fns = strlen(dir);
2881 dirfab.fab$l_fna = dir;
2882 if (dir[dirfab.fab$b_fns-1] == ']' ||
2883 dir[dirfab.fab$b_fns-1] == '>' ||
2884 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
2885 if (buf) retpath = buf;
2886 else if (ts) New(1314,retpath,strlen(dir)+1,char);
2887 else retpath = __pathify_retbuf;
2888 strcpy(retpath,dir);
2891 dirfab.fab$l_dna = ".DIR;1";
2892 dirfab.fab$b_dns = 6;
2893 dirfab.fab$l_nam = &dirnam;
2894 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
2895 dirnam.nam$l_esa = esa;
2897 for (cp = dir; *cp; cp++)
2898 if (islower(*cp)) { haslower = 1; break; }
2900 if (!(sts = (sys$parse(&dirfab)&1))) {
2901 if (dirfab.fab$l_sts == RMS$_DIR) {
2902 dirnam.nam$b_nop |= NAM$M_SYNCHK;
2903 sts = sys$parse(&dirfab) & 1;
2907 set_vaxc_errno(dirfab.fab$l_sts);
2913 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
2914 if (dirfab.fab$l_sts != RMS$_FNF) {
2915 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2916 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2918 set_vaxc_errno(dirfab.fab$l_sts);
2921 dirnam = savnam; /* No; just work with potential name */
2924 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
2925 /* Yep; check version while we're at it, if it's there. */
2926 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
2927 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
2928 /* Something other than .DIR[;1]. Bzzt. */
2929 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2930 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2932 set_vaxc_errno(RMS$_DIR);
2936 /* OK, the type was fine. Now pull any file name into the
2938 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
2940 cp1 = strrchr(esa,'>');
2941 *dirnam.nam$l_type = '>';
2944 *(dirnam.nam$l_type + 1) = '\0';
2945 retlen = dirnam.nam$l_type - esa + 2;
2946 if (buf) retpath = buf;
2947 else if (ts) New(1314,retpath,retlen,char);
2948 else retpath = __pathify_retbuf;
2949 strcpy(retpath,esa);
2950 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2951 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2952 /* $PARSE may have upcased filespec, so convert output to lower
2953 * case if input contained any lowercase characters. */
2954 if (haslower) __mystrtolower(retpath);
2958 } /* end of do_pathify_dirspec() */
2960 /* External entry points */
2961 char *Perl_pathify_dirspec(pTHX_ char *dir, char *buf)
2962 { return do_pathify_dirspec(dir,buf,0); }
2963 char *Perl_pathify_dirspec_ts(pTHX_ char *dir, char *buf)
2964 { return do_pathify_dirspec(dir,buf,1); }
2966 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
2967 static char *mp_do_tounixspec(pTHX_ char *spec, char *buf, int ts)
2969 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
2970 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
2971 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
2973 if (spec == NULL) return NULL;
2974 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
2975 if (buf) rslt = buf;
2977 retlen = strlen(spec);
2978 cp1 = strchr(spec,'[');
2979 if (!cp1) cp1 = strchr(spec,'<');
2981 for (cp1++; *cp1; cp1++) {
2982 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
2983 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
2984 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
2987 New(1315,rslt,retlen+2+2*expand,char);
2989 else rslt = __tounixspec_retbuf;
2990 if (strchr(spec,'/') != NULL) {
2997 dirend = strrchr(spec,']');
2998 if (dirend == NULL) dirend = strrchr(spec,'>');
2999 if (dirend == NULL) dirend = strchr(spec,':');
3000 if (dirend == NULL) {
3004 if (*cp2 != '[' && *cp2 != '<') {
3007 else { /* the VMS spec begins with directories */
3009 if (*cp2 == ']' || *cp2 == '>') {
3010 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
3013 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
3014 if (getcwd(tmp,sizeof tmp,1) == NULL) {
3015 if (ts) Safefree(rslt);
3020 while (*cp3 != ':' && *cp3) cp3++;
3022 if (strchr(cp3,']') != NULL) break;
3023 } while (vmstrnenv(tmp,tmp,0,fildev,0));
3025 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
3026 retlen = devlen + dirlen;
3027 Renew(rslt,retlen+1+2*expand,char);
3033 *(cp1++) = *(cp3++);
3034 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
3038 else if ( *cp2 == '.') {
3039 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
3040 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3046 for (; cp2 <= dirend; cp2++) {
3049 if (*(cp2+1) == '[') cp2++;
3051 else if (*cp2 == ']' || *cp2 == '>') {
3052 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
3054 else if (*cp2 == '.') {
3056 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
3057 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
3058 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
3059 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
3060 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
3062 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
3063 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
3067 else if (*cp2 == '-') {
3068 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
3069 while (*cp2 == '-') {
3071 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3073 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
3074 if (ts) Safefree(rslt); /* filespecs like */
3075 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
3079 else *(cp1++) = *cp2;
3081 else *(cp1++) = *cp2;
3083 while (*cp2) *(cp1++) = *(cp2++);
3088 } /* end of do_tounixspec() */
3090 /* External entry points */
3091 char *Perl_tounixspec(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
3092 char *Perl_tounixspec_ts(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
3094 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
3095 static char *mp_do_tovmsspec(pTHX_ char *path, char *buf, int ts) {
3096 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
3097 char *rslt, *dirend;
3098 register char *cp1, *cp2;
3099 unsigned long int infront = 0, hasdir = 1;
3101 if (path == NULL) return NULL;
3102 if (buf) rslt = buf;
3103 else if (ts) New(1316,rslt,strlen(path)+9,char);
3104 else rslt = __tovmsspec_retbuf;
3105 if (strpbrk(path,"]:>") ||
3106 (dirend = strrchr(path,'/')) == NULL) {
3107 if (path[0] == '.') {
3108 if (path[1] == '\0') strcpy(rslt,"[]");
3109 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
3110 else strcpy(rslt,path); /* probably garbage */
3112 else strcpy(rslt,path);
3115 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
3116 if (!*(dirend+2)) dirend +=2;
3117 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
3118 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
3123 char trndev[NAM$C_MAXRSS+1];
3127 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
3129 if (!buf & ts) Renew(rslt,18,char);
3130 strcpy(rslt,"sys$disk:[000000]");
3133 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
3135 islnm = my_trnlnm(rslt,trndev,0);
3136 trnend = islnm ? strlen(trndev) - 1 : 0;
3137 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
3138 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
3139 /* If the first element of the path is a logical name, determine
3140 * whether it has to be translated so we can add more directories. */
3141 if (!islnm || rooted) {
3144 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
3148 if (cp2 != dirend) {
3149 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
3150 strcpy(rslt,trndev);
3151 cp1 = rslt + trnend;
3164 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
3165 cp2 += 2; /* skip over "./" - it's redundant */
3166 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
3168 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3169 *(cp1++) = '-'; /* "../" --> "-" */
3172 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
3173 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
3174 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3175 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
3178 if (cp2 > dirend) cp2 = dirend;
3180 else *(cp1++) = '.';
3182 for (; cp2 < dirend; cp2++) {
3184 if (*(cp2-1) == '/') continue;
3185 if (*(cp1-1) != '.') *(cp1++) = '.';
3188 else if (!infront && *cp2 == '.') {
3189 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
3190 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
3191 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3192 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
3193 else if (*(cp1-2) == '[') *(cp1-1) = '-';
3194 else { /* back up over previous directory name */
3196 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
3197 if (*(cp1-1) == '[') {
3198 memcpy(cp1,"000000.",7);
3203 if (cp2 == dirend) break;
3205 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
3206 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
3207 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
3208 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3210 *(cp1++) = '.'; /* Simulate trailing '/' */
3211 cp2 += 2; /* for loop will incr this to == dirend */
3213 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
3215 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
3218 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
3219 if (*cp2 == '.') *(cp1++) = '_';
3220 else *(cp1++) = *cp2;
3224 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
3225 if (hasdir) *(cp1++) = ']';
3226 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
3227 while (*cp2) *(cp1++) = *(cp2++);
3232 } /* end of do_tovmsspec() */
3234 /* External entry points */
3235 char *Perl_tovmsspec(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,0); }
3236 char *Perl_tovmsspec_ts(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,1); }
3238 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
3239 static char *mp_do_tovmspath(pTHX_ char *path, char *buf, int ts) {
3240 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
3242 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
3244 if (path == NULL) return NULL;
3245 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3246 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
3247 if (buf) return buf;
3249 vmslen = strlen(vmsified);
3250 New(1317,cp,vmslen+1,char);
3251 memcpy(cp,vmsified,vmslen);
3256 strcpy(__tovmspath_retbuf,vmsified);
3257 return __tovmspath_retbuf;
3260 } /* end of do_tovmspath() */
3262 /* External entry points */
3263 char *Perl_tovmspath(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,0); }
3264 char *Perl_tovmspath_ts(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,1); }
3267 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
3268 static char *mp_do_tounixpath(pTHX_ char *path, char *buf, int ts) {
3269 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
3271 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
3273 if (path == NULL) return NULL;
3274 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3275 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
3276 if (buf) return buf;
3278 unixlen = strlen(unixified);
3279 New(1317,cp,unixlen+1,char);
3280 memcpy(cp,unixified,unixlen);
3285 strcpy(__tounixpath_retbuf,unixified);
3286 return __tounixpath_retbuf;
3289 } /* end of do_tounixpath() */
3291 /* External entry points */
3292 char *Perl_tounixpath(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,0); }
3293 char *Perl_tounixpath_ts(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,1); }
3296 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
3298 *****************************************************************************
3300 * Copyright (C) 1989-1994 by *
3301 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
3303 * Permission is hereby granted for the reproduction of this software, *
3304 * on condition that this copyright notice is included in the reproduction, *
3305 * and that such reproduction is not for purposes of profit or material *
3308 * 27-Aug-1994 Modified for inclusion in perl5 *
3309 * by Charles Bailey bailey@newman.upenn.edu *
3310 *****************************************************************************
3314 * getredirection() is intended to aid in porting C programs
3315 * to VMS (Vax-11 C). The native VMS environment does not support
3316 * '>' and '<' I/O redirection, or command line wild card expansion,
3317 * or a command line pipe mechanism using the '|' AND background
3318 * command execution '&'. All of these capabilities are provided to any
3319 * C program which calls this procedure as the first thing in the
3321 * The piping mechanism will probably work with almost any 'filter' type
3322 * of program. With suitable modification, it may useful for other
3323 * portability problems as well.
3325 * Author: Mark Pizzolato mark@infocomm.com
3329 struct list_item *next;
3333 static void add_item(struct list_item **head,
3334 struct list_item **tail,
3338 static void mp_expand_wild_cards(pTHX_ char *item,
3339 struct list_item **head,
3340 struct list_item **tail,
3343 static int background_process(int argc, char **argv);
3345 static void pipe_and_fork(char **cmargv);
3347 /*{{{ void getredirection(int *ac, char ***av)*/
3349 mp_getredirection(pTHX_ int *ac, char ***av)
3351 * Process vms redirection arg's. Exit if any error is seen.
3352 * If getredirection() processes an argument, it is erased
3353 * from the vector. getredirection() returns a new argc and argv value.
3354 * In the event that a background command is requested (by a trailing "&"),
3355 * this routine creates a background subprocess, and simply exits the program.
3357 * Warning: do not try to simplify the code for vms. The code
3358 * presupposes that getredirection() is called before any data is
3359 * read from stdin or written to stdout.
3361 * Normal usage is as follows:
3367 * getredirection(&argc, &argv);
3371 int argc = *ac; /* Argument Count */
3372 char **argv = *av; /* Argument Vector */
3373 char *ap; /* Argument pointer */
3374 int j; /* argv[] index */
3375 int item_count = 0; /* Count of Items in List */
3376 struct list_item *list_head = 0; /* First Item in List */
3377 struct list_item *list_tail; /* Last Item in List */
3378 char *in = NULL; /* Input File Name */
3379 char *out = NULL; /* Output File Name */
3380 char *outmode = "w"; /* Mode to Open Output File */
3381 char *err = NULL; /* Error File Name */
3382 char *errmode = "w"; /* Mode to Open Error File */
3383 int cmargc = 0; /* Piped Command Arg Count */
3384 char **cmargv = NULL;/* Piped Command Arg Vector */
3387 * First handle the case where the last thing on the line ends with
3388 * a '&'. This indicates the desire for the command to be run in a
3389 * subprocess, so we satisfy that desire.
3392 if (0 == strcmp("&", ap))
3393 exit(background_process(--argc, argv));
3394 if (*ap && '&' == ap[strlen(ap)-1])
3396 ap[strlen(ap)-1] = '\0';
3397 exit(background_process(argc, argv));
3400 * Now we handle the general redirection cases that involve '>', '>>',
3401 * '<', and pipes '|'.
3403 for (j = 0; j < argc; ++j)
3405 if (0 == strcmp("<", argv[j]))
3409 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
3410 exit(LIB$_WRONUMARG);
3415 if ('<' == *(ap = argv[j]))
3420 if (0 == strcmp(">", ap))
3424 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
3425 exit(LIB$_WRONUMARG);
3444 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
3445 exit(LIB$_WRONUMARG);
3449 if (('2' == *ap) && ('>' == ap[1]))
3466 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
3467 exit(LIB$_WRONUMARG);
3471 if (0 == strcmp("|", argv[j]))
3475 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
3476 exit(LIB$_WRONUMARG);
3478 cmargc = argc-(j+1);
3479 cmargv = &argv[j+1];
3483 if ('|' == *(ap = argv[j]))
3491 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
3494 * Allocate and fill in the new argument vector, Some Unix's terminate
3495 * the list with an extra null pointer.
3497 New(1302, argv, item_count+1, char *);
3499 for (j = 0; j < item_count; ++j, list_head = list_head->next)
3500 argv[j] = list_head->value;
3506 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
3507 exit(LIB$_INVARGORD);
3509 pipe_and_fork(cmargv);
3512 /* Check for input from a pipe (mailbox) */
3514 if (in == NULL && 1 == isapipe(0))
3516 char mbxname[L_tmpnam];
3518 long int dvi_item = DVI$_DEVBUFSIZ;
3519 $DESCRIPTOR(mbxnam, "");
3520 $DESCRIPTOR(mbxdevnam, "");
3522 /* Input from a pipe, reopen it in binary mode to disable */
3523 /* carriage control processing. */
3525 PerlIO_getname(stdin, mbxname);
3526 mbxnam.dsc$a_pointer = mbxname;
3527 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
3528 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
3529 mbxdevnam.dsc$a_pointer = mbxname;
3530 mbxdevnam.dsc$w_length = sizeof(mbxname);
3531 dvi_item = DVI$_DEVNAM;
3532 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
3533 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
3536 freopen(mbxname, "rb", stdin);
3539 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
3543 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
3545 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
3548 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
3550 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
3554 if (strcmp(err,"&1") == 0) {
3555 dup2(fileno(stdout), fileno(Perl_debug_log));
3558 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
3560 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
3564 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
3570 #ifdef ARGPROC_DEBUG
3571 PerlIO_printf(Perl_debug_log, "Arglist:\n");
3572 for (j = 0; j < *ac; ++j)
3573 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
3575 /* Clear errors we may have hit expanding wildcards, so they don't
3576 show up in Perl's $! later */
3577 set_errno(0); set_vaxc_errno(1);
3578 } /* end of getredirection() */
3581 static void add_item(struct list_item **head,
3582 struct list_item **tail,
3588 New(1303,*head,1,struct list_item);
3592 New(1304,(*tail)->next,1,struct list_item);
3593 *tail = (*tail)->next;
3595 (*tail)->value = value;
3599 static void mp_expand_wild_cards(pTHX_ char *item,
3600 struct list_item **head,
3601 struct list_item **tail,
3605 unsigned long int context = 0;
3611 char vmsspec[NAM$C_MAXRSS+1];
3612 $DESCRIPTOR(filespec, "");
3613 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
3614 $DESCRIPTOR(resultspec, "");
3615 unsigned long int zero = 0, sts;
3617 for (cp = item; *cp; cp++) {
3618 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
3619 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
3621 if (!*cp || isspace(*cp))
3623 add_item(head, tail, item, count);
3626 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
3627 resultspec.dsc$b_class = DSC$K_CLASS_D;
3628 resultspec.dsc$a_pointer = NULL;
3629 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
3630 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
3631 if (!isunix || !filespec.dsc$a_pointer)
3632 filespec.dsc$a_pointer = item;
3633 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
3635 * Only return version specs, if the caller specified a version
3637 had_version = strchr(item, ';');
3639 * Only return device and directory specs, if the caller specifed either.
3641 had_device = strchr(item, ':');
3642 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
3644 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
3645 &defaultspec, 0, 0, &zero))))
3650 New(1305,string,resultspec.dsc$w_length+1,char);
3651 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
3652 string[resultspec.dsc$w_length] = '\0';
3653 if (NULL == had_version)
3654 *((char *)strrchr(string, ';')) = '\0';
3655 if ((!had_directory) && (had_device == NULL))
3657 if (NULL == (devdir = strrchr(string, ']')))
3658 devdir = strrchr(string, '>');
3659 strcpy(string, devdir + 1);
3662 * Be consistent with what the C RTL has already done to the rest of
3663 * the argv items and lowercase all of these names.
3665 for (c = string; *c; ++c)
3668 if (isunix) trim_unixpath(string,item,1);
3669 add_item(head, tail, string, count);
3672 if (sts != RMS$_NMF)
3674 set_vaxc_errno(sts);
3677 case RMS$_FNF: case RMS$_DNF:
3678 set_errno(ENOENT); break;
3680 set_errno(ENOTDIR); break;
3682 set_errno(ENODEV); break;
3683 case RMS$_FNM: case RMS$_SYN:
3684 set_errno(EINVAL); break;
3686 set_errno(EACCES); break;
3688 _ckvmssts_noperl(sts);
3692 add_item(head, tail, item, count);
3693 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
3694 _ckvmssts_noperl(lib$find_file_end(&context));
3697 static int child_st[2];/* Event Flag set when child process completes */
3699 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
3701 static unsigned long int exit_handler(int *status)
3705 if (0 == child_st[0])
3707 #ifdef ARGPROC_DEBUG
3708 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
3710 fflush(stdout); /* Have to flush pipe for binary data to */
3711 /* terminate properly -- <tp@mccall.com> */
3712 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
3713 sys$dassgn(child_chan);
3715 sys$synch(0, child_st);
3720 static void sig_child(int chan)
3722 #ifdef ARGPROC_DEBUG
3723 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
3725 if (child_st[0] == 0)
3729 static struct exit_control_block exit_block =
3734 &exit_block.exit_status,
3738 static void pipe_and_fork(char **cmargv)
3741 $DESCRIPTOR(cmddsc, "");
3742 static char mbxname[64];
3743 $DESCRIPTOR(mbxdsc, mbxname);
3745 unsigned long int zero = 0, one = 1;
3747 strcpy(subcmd, cmargv[0]);
3748 for (j = 1; NULL != cmargv[j]; ++j)
3750 strcat(subcmd, " \"");
3751 strcat(subcmd, cmargv[j]);
3752 strcat(subcmd, "\"");
3754 cmddsc.dsc$a_pointer = subcmd;
3755 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
3757 create_mbx(&child_chan,&mbxdsc);
3758 #ifdef ARGPROC_DEBUG
3759 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
3760 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
3762 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
3763 0, &pid, child_st, &zero, sig_child,
3765 #ifdef ARGPROC_DEBUG
3766 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
3768 sys$dclexh(&exit_block);
3769 if (NULL == freopen(mbxname, "wb", stdout))
3771 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
3775 static int background_process(int argc, char **argv)
3777 char command[2048] = "$";
3778 $DESCRIPTOR(value, "");
3779 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
3780 static $DESCRIPTOR(null, "NLA0:");
3781 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
3783 $DESCRIPTOR(pidstr, "");
3785 unsigned long int flags = 17, one = 1, retsts;
3787 strcat(command, argv[0]);
3790 strcat(command, " \"");
3791 strcat(command, *(++argv));
3792 strcat(command, "\"");
3794 value.dsc$a_pointer = command;
3795 value.dsc$w_length = strlen(value.dsc$a_pointer);
3796 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
3797 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
3798 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
3799 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
3802 _ckvmssts_noperl(retsts);
3804 #ifdef ARGPROC_DEBUG
3805 PerlIO_printf(Perl_debug_log, "%s\n", command);
3807 sprintf(pidstring, "%08X", pid);
3808 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
3809 pidstr.dsc$a_pointer = pidstring;
3810 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
3811 lib$set_symbol(&pidsymbol, &pidstr);
3815 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
3818 /* OS-specific initialization at image activation (not thread startup) */
3819 /* Older VAXC header files lack these constants */
3820 #ifndef JPI$_RIGHTS_SIZE
3821 # define JPI$_RIGHTS_SIZE 817
3823 #ifndef KGB$M_SUBSYSTEM
3824 # define KGB$M_SUBSYSTEM 0x8
3827 /*{{{void vms_image_init(int *, char ***)*/
3829 vms_image_init(int *argcp, char ***argvp)
3831 char eqv[LNM$C_NAMLENGTH+1] = "";
3832 unsigned int len, tabct = 8, tabidx = 0;
3833 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
3834 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
3835 unsigned short int dummy, rlen;
3836 struct dsc$descriptor_s **tabvec;
3838 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
3839 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
3840 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
3843 _ckvmssts(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
3845 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
3846 if (iprv[i]) { /* Running image installed with privs? */
3847 _ckvmssts(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
3852 /* Rights identifiers might trigger tainting as well. */
3853 if (!will_taint && (rlen || rsz)) {
3854 while (rlen < rsz) {
3855 /* We didn't get all the identifiers on the first pass. Allocate a
3856 * buffer much larger than $GETJPI wants (rsz is size in bytes that
3857 * were needed to hold all identifiers at time of last call; we'll
3858 * allocate that many unsigned long ints), and go back and get 'em.
3859 * If it gave us less than it wanted to despite ample buffer space,
3860 * something's broken. Is your system missing a system identifier?
3862 if (rsz <= jpilist[1].buflen) {
3863 /* Perl_croak accvios when used this early in startup. */
3864 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
3865 rsz, (unsigned long) jpilist[1].buflen,
3866 "Check your rights database for corruption.\n");
3869 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
3870 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
3871 jpilist[1].buflen = rsz * sizeof(unsigned long int);
3872 _ckvmssts(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
3875 mask = jpilist[1].bufadr;
3876 /* Check attribute flags for each identifier (2nd longword); protected
3877 * subsystem identifiers trigger tainting.
3879 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
3880 if (mask[i] & KGB$M_SUBSYSTEM) {
3885 if (mask != rlst) Safefree(mask);
3887 /* We need to use this hack to tell Perl it should run with tainting,
3888 * since its tainting flag may be part of the PL_curinterp struct, which
3889 * hasn't been allocated when vms_image_init() is called.
3893 New(1320,newap,*argcp+2,char **);
3894 newap[0] = argvp[0];
3896 Copy(argvp[1],newap[2],*argcp-1,char **);
3897 /* We orphan the old argv, since we don't know where it's come from,
3898 * so we don't know how to free it.
3900 *argcp++; argvp = newap;
3902 else { /* Did user explicitly request tainting? */
3904 char *cp, **av = *argvp;
3905 for (i = 1; i < *argcp; i++) {
3906 if (*av[i] != '-') break;
3907 for (cp = av[i]+1; *cp; cp++) {
3908 if (*cp == 'T') { will_taint = 1; break; }
3909 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
3910 strchr("DFIiMmx",*cp)) break;
3912 if (will_taint) break;
3917 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
3919 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
3920 else if (tabidx >= tabct) {
3922 Renew(tabvec,tabct,struct dsc$descriptor_s *);
3924 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
3925 tabvec[tabidx]->dsc$w_length = 0;
3926 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
3927 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
3928 tabvec[tabidx]->dsc$a_pointer = NULL;
3929 _ckvmssts(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
3931 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
3933 getredirection(argcp,argvp);
3934 #if defined(USE_THREADS) && defined(__DECC)
3936 # include <reentrancy.h>
3937 (void) decc$set_reentrancy(C$C_MULTITHREAD);
3946 * Trim Unix-style prefix off filespec, so it looks like what a shell
3947 * glob expansion would return (i.e. from specified prefix on, not
3948 * full path). Note that returned filespec is Unix-style, regardless
3949 * of whether input filespec was VMS-style or Unix-style.
3951 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
3952 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
3953 * vector of options; at present, only bit 0 is used, and if set tells
3954 * trim unixpath to try the current default directory as a prefix when
3955 * presented with a possibly ambiguous ... wildcard.
3957 * Returns !=0 on success, with trimmed filespec replacing contents of
3958 * fspec, and 0 on failure, with contents of fpsec unchanged.
3960 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
3962 Perl_trim_unixpath(pTHX_ char *fspec, char *wildspec, int opts)
3964 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
3965 *template, *base, *end, *cp1, *cp2;
3966 register int tmplen, reslen = 0, dirs = 0;
3968 if (!wildspec || !fspec) return 0;
3969 if (strpbrk(wildspec,"]>:") != NULL) {
3970 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
3971 else template = unixwild;
3973 else template = wildspec;
3974 if (strpbrk(fspec,"]>:") != NULL) {
3975 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
3976 else base = unixified;
3977 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
3978 * check to see that final result fits into (isn't longer than) fspec */
3979 reslen = strlen(fspec);
3983 /* No prefix or absolute path on wildcard, so nothing to remove */
3984 if (!*template || *template == '/') {
3985 if (base == fspec) return 1;
3986 tmplen = strlen(unixified);
3987 if (tmplen > reslen) return 0; /* not enough space */
3988 /* Copy unixified resultant, including trailing NUL */
3989 memmove(fspec,unixified,tmplen+1);
3993 for (end = base; *end; end++) ; /* Find end of resultant filespec */
3994 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
3995 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
3996 for (cp1 = end ;cp1 >= base; cp1--)
3997 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
3999 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
4003 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
4004 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
4005 int ells = 1, totells, segdirs, match;
4006 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
4007 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4009 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
4011 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
4012 if (ellipsis == template && opts & 1) {
4013 /* Template begins with an ellipsis. Since we can't tell how many
4014 * directory names at the front of the resultant to keep for an
4015 * arbitrary starting point, we arbitrarily choose the current
4016 * default directory as a starting point. If it's there as a prefix,
4017 * clip it off. If not, fall through and act as if the leading
4018 * ellipsis weren't there (i.e. return shortest possible path that
4019 * could match template).
4021 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
4022 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4023 if (_tolower(*cp1) != _tolower(*cp2)) break;
4024 segdirs = dirs - totells; /* Min # of dirs we must have left */
4025 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
4026 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
4027 memcpy(fspec,cp2+1,end - cp2);
4031 /* First off, back up over constant elements at end of path */
4033 for (front = end ; front >= base; front--)
4034 if (*front == '/' && !dirs--) { front++; break; }
4036 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
4037 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
4038 if (cp1 != '\0') return 0; /* Path too long. */
4040 *cp2 = '\0'; /* Pick up with memcpy later */
4041 lcfront = lcres + (front - base);
4042 /* Now skip over each ellipsis and try to match the path in front of it. */
4044 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
4045 if (*(cp1) == '.' && *(cp1+1) == '.' &&
4046 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
4047 if (cp1 < template) break; /* template started with an ellipsis */
4048 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
4049 ellipsis = cp1; continue;
4051 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
4053 for (segdirs = 0, cp2 = tpl;
4054 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
4056 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
4057 else *cp2 = _tolower(*cp1); /* else lowercase for match */
4058 if (*cp2 == '/') segdirs++;
4060 if (cp1 != ellipsis - 1) return 0; /* Path too long */
4061 /* Back up at least as many dirs as in template before matching */
4062 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
4063 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
4064 for (match = 0; cp1 > lcres;) {
4065 resdsc.dsc$a_pointer = cp1;
4066 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
4068 if (match == 1) lcfront = cp1;
4070 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
4072 if (!match) return 0; /* Can't find prefix ??? */
4073 if (match > 1 && opts & 1) {
4074 /* This ... wildcard could cover more than one set of dirs (i.e.
4075 * a set of similar dir names is repeated). If the template
4076 * contains more than 1 ..., upstream elements could resolve the
4077 * ambiguity, but it's not worth a full backtracking setup here.
4078 * As a quick heuristic, clip off the current default directory
4079 * if it's present to find the trimmed spec, else use the
4080 * shortest string that this ... could cover.
4082 char def[NAM$C_MAXRSS+1], *st;
4084 if (getcwd(def, sizeof def,0) == NULL) return 0;
4085 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4086 if (_tolower(*cp1) != _tolower(*cp2)) break;
4087 segdirs = dirs - totells; /* Min # of dirs we must have left */
4088 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
4089 if (*cp1 == '\0' && *cp2 == '/') {
4090 memcpy(fspec,cp2+1,end - cp2);
4093 /* Nope -- stick with lcfront from above and keep going. */
4096 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
4101 } /* end of trim_unixpath() */
4106 * VMS readdir() routines.
4107 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
4109 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
4110 * Minor modifications to original routines.
4113 /* Number of elements in vms_versions array */
4114 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
4117 * Open a directory, return a handle for later use.
4119 /*{{{ DIR *opendir(char*name) */
4121 Perl_opendir(pTHX_ char *name)
4124 char dir[NAM$C_MAXRSS+1];
4127 if (do_tovmspath(name,dir,0) == NULL) {
4130 if (flex_stat(dir,&sb) == -1) return NULL;
4131 if (!S_ISDIR(sb.st_mode)) {
4132 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
4135 if (!cando_by_name(S_IRUSR,0,dir)) {
4136 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
4139 /* Get memory for the handle, and the pattern. */
4141 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
4143 /* Fill in the fields; mainly playing with the descriptor. */
4144 (void)sprintf(dd->pattern, "%s*.*",dir);
4147 dd->vms_wantversions = 0;
4148 dd->pat.dsc$a_pointer = dd->pattern;
4149 dd->pat.dsc$w_length = strlen(dd->pattern);
4150 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
4151 dd->pat.dsc$b_class = DSC$K_CLASS_S;
4154 } /* end of opendir() */
4158 * Set the flag to indicate we want versions or not.
4160 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
4162 vmsreaddirversions(DIR *dd, int flag)
4164 dd->vms_wantversions = flag;
4169 * Free up an opened directory.
4171 /*{{{ void closedir(DIR *dd)*/
4175 (void)lib$find_file_end(&dd->context);
4176 Safefree(dd->pattern);
4177 Safefree((char *)dd);
4182 * Collect all the version numbers for the current file.
4188 struct dsc$descriptor_s pat;
4189 struct dsc$descriptor_s res;
4191 char *p, *text, buff[sizeof dd->entry.d_name];
4193 unsigned long context, tmpsts;
4196 /* Convenient shorthand. */
4199 /* Add the version wildcard, ignoring the "*.*" put on before */
4200 i = strlen(dd->pattern);
4201 New(1308,text,i + e->d_namlen + 3,char);
4202 (void)strcpy(text, dd->pattern);
4203 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
4205 /* Set up the pattern descriptor. */
4206 pat.dsc$a_pointer = text;
4207 pat.dsc$w_length = i + e->d_namlen - 1;
4208 pat.dsc$b_dtype = DSC$K_DTYPE_T;
4209 pat.dsc$b_class = DSC$K_CLASS_S;
4211 /* Set up result descriptor. */
4212 res.dsc$a_pointer = buff;
4213 res.dsc$w_length = sizeof buff - 2;
4214 res.dsc$b_dtype = DSC$K_DTYPE_T;
4215 res.dsc$b_class = DSC$K_CLASS_S;
4217 /* Read files, collecting versions. */
4218 for (context = 0, e->vms_verscount = 0;
4219 e->vms_verscount < VERSIZE(e);
4220 e->vms_verscount++) {
4221 tmpsts = lib$find_file(&pat, &res, &context);
4222 if (tmpsts == RMS$_NMF || context == 0) break;
4224 buff[sizeof buff - 1] = '\0';
4225 if ((p = strchr(buff, ';')))
4226 e->vms_versions[e->vms_verscount] = atoi(p + 1);
4228 e->vms_versions[e->vms_verscount] = -1;
4231 _ckvmssts(lib$find_file_end(&context));
4234 } /* end of collectversions() */
4237 * Read the next entry from the directory.
4239 /*{{{ struct dirent *readdir(DIR *dd)*/
4243 struct dsc$descriptor_s res;
4244 char *p, buff[sizeof dd->entry.d_name];
4245 unsigned long int tmpsts;
4247 /* Set up result descriptor, and get next file. */
4248 res.dsc$a_pointer = buff;
4249 res.dsc$w_length = sizeof buff - 2;
4250 res.dsc$b_dtype = DSC$K_DTYPE_T;
4251 res.dsc$b_class = DSC$K_CLASS_S;
4252 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4253 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
4254 if (!(tmpsts & 1)) {
4255 set_vaxc_errno(tmpsts);
4258 set_errno(EACCES); break;
4260 set_errno(ENODEV); break;
4262 set_errno(ENOTDIR); break;
4263 case RMS$_FNF: case RMS$_DNF:
4264 set_errno(ENOENT); break;
4271 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
4272 buff[sizeof buff - 1] = '\0';
4273 for (p = buff; *p; p++) *p = _tolower(*p);
4274 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
4277 /* Skip any directory component and just copy the name. */
4278 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
4279 else (void)strcpy(dd->entry.d_name, buff);
4281 /* Clobber the version. */
4282 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
4284 dd->entry.d_namlen = strlen(dd->entry.d_name);
4285 dd->entry.vms_verscount = 0;
4286 if (dd->vms_wantversions) collectversions(dd);
4289 } /* end of readdir() */
4293 * Return something that can be used in a seekdir later.
4295 /*{{{ long telldir(DIR *dd)*/
4304 * Return to a spot where we used to be. Brute force.
4306 /*{{{ void seekdir(DIR *dd,long count)*/
4308 seekdir(DIR *dd, long count)
4310 int vms_wantversions;
4313 /* If we haven't done anything yet... */
4317 /* Remember some state, and clear it. */
4318 vms_wantversions = dd->vms_wantversions;
4319 dd->vms_wantversions = 0;
4320 _ckvmssts(lib$find_file_end(&dd->context));
4323 /* The increment is in readdir(). */
4324 for (dd->count = 0; dd->count < count; )
4327 dd->vms_wantversions = vms_wantversions;
4329 } /* end of seekdir() */
4332 /* VMS subprocess management
4334 * my_vfork() - just a vfork(), after setting a flag to record that
4335 * the current script is trying a Unix-style fork/exec.
4337 * vms_do_aexec() and vms_do_exec() are called in response to the
4338 * perl 'exec' function. If this follows a vfork call, then they
4339 * call out the the regular perl routines in doio.c which do an
4340 * execvp (for those who really want to try this under VMS).
4341 * Otherwise, they do exactly what the perl docs say exec should
4342 * do - terminate the current script and invoke a new command
4343 * (See below for notes on command syntax.)
4345 * do_aspawn() and do_spawn() implement the VMS side of the perl
4346 * 'system' function.
4348 * Note on command arguments to perl 'exec' and 'system': When handled
4349 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
4350 * are concatenated to form a DCL command string. If the first arg
4351 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
4352 * the the command string is handed off to DCL directly. Otherwise,
4353 * the first token of the command is taken as the filespec of an image
4354 * to run. The filespec is expanded using a default type of '.EXE' and
4355 * the process defaults for device, directory, etc., and if found, the resultant
4356 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
4357 * the command string as parameters. This is perhaps a bit complicated,
4358 * but I hope it will form a happy medium between what VMS folks expect
4359 * from lib$spawn and what Unix folks expect from exec.
4362 static int vfork_called;
4364 /*{{{int my_vfork()*/
4375 vms_execfree(pTHX) {
4377 if (PL_Cmd != VMScmd.dsc$a_pointer) Safefree(PL_Cmd);
4380 if (VMScmd.dsc$a_pointer) {
4381 Safefree(VMScmd.dsc$a_pointer);
4382 VMScmd.dsc$w_length = 0;
4383 VMScmd.dsc$a_pointer = Nullch;
4388 setup_argstr(SV *really, SV **mark, SV **sp)
4391 char *junk, *tmps = Nullch;
4392 register size_t cmdlen = 0;
4399 tmps = SvPV(really,rlen);
4406 for (idx++; idx <= sp; idx++) {
4408 junk = SvPVx(*idx,rlen);
4409 cmdlen += rlen ? rlen + 1 : 0;
4412 New(401,PL_Cmd,cmdlen+1,char);
4414 if (tmps && *tmps) {
4415 strcpy(PL_Cmd,tmps);
4418 else *PL_Cmd = '\0';
4419 while (++mark <= sp) {
4421 char *s = SvPVx(*mark,n_a);
4423 if (*PL_Cmd) strcat(PL_Cmd," ");
4429 } /* end of setup_argstr() */
4432 static unsigned long int
4433 setup_cmddsc(char *cmd, int check_img)
4435 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
4436 $DESCRIPTOR(defdsc,".EXE");
4437 $DESCRIPTOR(defdsc2,".");
4438 $DESCRIPTOR(resdsc,resspec);
4439 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4440 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
4441 register char *s, *rest, *cp, *wordbreak;
4446 (sizeof(vmsspec) > sizeof(resspec) ? sizeof(resspec) : sizeof(vmsspec)))
4449 while (*s && isspace(*s)) s++;
4451 if (*s == '@' || *s == '$') {
4452 vmsspec[0] = *s; rest = s + 1;
4453 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
4455 else { cp = vmsspec; rest = s; }
4456 if (*rest == '.' || *rest == '/') {
4459 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
4460 rest++, cp2++) *cp2 = *rest;
4462 if (do_tovmsspec(resspec,cp,0)) {
4465 for (cp2 = vmsspec + strlen(vmsspec);
4466 *rest && cp2 - vmsspec < sizeof vmsspec;
4467 rest++, cp2++) *cp2 = *rest;
4472 /* Intuit whether verb (first word of cmd) is a DCL command:
4473 * - if first nonspace char is '@', it's a DCL indirection
4475 * - if verb contains a filespec separator, it's not a DCL command
4476 * - if it doesn't, caller tells us whether to default to a DCL
4477 * command, or to a local image unless told it's DCL (by leading '$')
4479 if (*s == '@') isdcl = 1;
4481 register char *filespec = strpbrk(s,":<[.;");
4482 rest = wordbreak = strpbrk(s," \"\t/");
4483 if (!wordbreak) wordbreak = s + strlen(s);
4484 if (*s == '$') check_img = 0;
4485 if (filespec && (filespec < wordbreak)) isdcl = 0;
4486 else isdcl = !check_img;
4490 imgdsc.dsc$a_pointer = s;
4491 imgdsc.dsc$w_length = wordbreak - s;
4492 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4494 _ckvmssts(lib$find_file_end(&cxt));
4495 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
4496 if (!(retsts & 1) && *s == '$') {
4497 _ckvmssts(lib$find_file_end(&cxt));
4498 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
4499 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4501 _ckvmssts(lib$find_file_end(&cxt));
4502 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
4506 _ckvmssts(lib$find_file_end(&cxt));
4511 while (*s && !isspace(*s)) s++;
4514 /* check that it's really not DCL with no file extension */
4515 fp = fopen(resspec,"r","ctx=bin,shr=get");
4517 char b[4] = {0,0,0,0};
4518 read(fileno(fp),b,4);
4519 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
4522 if (check_img && isdcl) return RMS$_FNF;
4524 if (cando_by_name(S_IXUSR,0,resspec)) {
4525 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
4527 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
4529 strcpy(VMScmd.dsc$a_pointer,"@");
4531 strcat(VMScmd.dsc$a_pointer,resspec);
4532 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
4533 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
4536 else retsts = RMS$_PRV;
4539 /* It's either a DCL command or we couldn't find a suitable image */
4540 VMScmd.dsc$w_length = strlen(cmd);
4541 if (cmd == PL_Cmd) VMScmd.dsc$a_pointer = PL_Cmd;
4542 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
4543 if (!(retsts & 1)) {
4544 /* just hand off status values likely to be due to user error */
4545 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
4546 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
4547 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
4548 else { _ckvmssts(retsts); }
4551 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : retsts);
4553 } /* end of setup_cmddsc() */
4556 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
4558 vms_do_aexec(SV *really,SV **mark,SV **sp)
4562 if (vfork_called) { /* this follows a vfork - act Unixish */
4564 if (vfork_called < 0) {
4565 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
4568 else return do_aexec(really,mark,sp);
4570 /* no vfork - act VMSish */
4571 return vms_do_exec(setup_argstr(really,mark,sp));
4576 } /* end of vms_do_aexec() */
4579 /* {{{bool vms_do_exec(char *cmd) */
4581 vms_do_exec(char *cmd)
4585 if (vfork_called) { /* this follows a vfork - act Unixish */
4587 if (vfork_called < 0) {
4588 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
4591 else return do_exec(cmd);
4594 { /* no vfork - act VMSish */
4595 unsigned long int retsts;
4598 TAINT_PROPER("exec");
4599 if ((retsts = setup_cmddsc(cmd,1)) & 1)
4600 retsts = lib$do_command(&VMScmd);
4603 case RMS$_FNF: case RMS$_DNF:
4604 set_errno(ENOENT); break;
4606 set_errno(ENOTDIR); break;
4608 set_errno(ENODEV); break;
4610 set_errno(EACCES); break;
4612 set_errno(EINVAL); break;
4614 set_errno(E2BIG); break;
4615 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4616 _ckvmssts(retsts); /* fall through */
4617 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4620 set_vaxc_errno(retsts);
4621 if (ckWARN(WARN_EXEC)) {
4622 Perl_warner(aTHX_ WARN_EXEC,"Can't exec \"%*s\": %s",
4623 VMScmd.dsc$w_length, VMScmd.dsc$a_pointer, Strerror(errno));
4630 } /* end of vms_do_exec() */
4633 unsigned long int do_spawn(char *);
4635 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
4637 do_aspawn(void *really,void **mark,void **sp)
4640 if (sp > mark) return do_spawn(setup_argstr((SV *)really,(SV **)mark,(SV **)sp));
4643 } /* end of do_aspawn() */
4646 /* {{{unsigned long int do_spawn(char *cmd) */
4650 unsigned long int sts, substs, hadcmd = 1;
4654 TAINT_PROPER("spawn");
4655 if (!cmd || !*cmd) {
4657 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
4659 else if ((sts = setup_cmddsc(cmd,0)) & 1) {
4660 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
4665 case RMS$_FNF: case RMS$_DNF:
4666 set_errno(ENOENT); break;
4668 set_errno(ENOTDIR); break;
4670 set_errno(ENODEV); break;
4672 set_errno(EACCES); break;
4674 set_errno(EINVAL); break;
4676 set_errno(E2BIG); break;
4677 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4678 _ckvmssts(sts); /* fall through */
4679 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4682 set_vaxc_errno(sts);
4683 if (ckWARN(WARN_EXEC)) {
4684 Perl_warner(aTHX_ WARN_EXEC,"Can't spawn \"%*s\": %s",
4685 hadcmd ? VMScmd.dsc$w_length : 0,
4686 hadcmd ? VMScmd.dsc$a_pointer : "",
4693 } /* end of do_spawn() */
4697 * A simple fwrite replacement which outputs itmsz*nitm chars without
4698 * introducing record boundaries every itmsz chars.
4699 * We are using fputs, which depends on a terminating null. We may
4700 * well be writing binary data, so we need to accommodate not only
4701 * data with nulls sprinkled in the middle but also data with no null
4704 /*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
4706 my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
4708 register char *cp, *end, *cpd, *data;
4710 int bufsize = itmsz*nitm+1;
4712 _ckvmssts_noperl(lib$get_vm( &bufsize, &data ));
4713 memcpy( data, src, itmsz*nitm );
4714 data[itmsz*nitm] = '\0';
4716 end = data + itmsz * nitm;
4717 retval = (int) nitm; /* on success return # items written */
4720 while (cpd <= end) {
4721 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
4722 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
4724 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
4728 if (data) _ckvmssts_noperl(lib$free_vm( &bufsize, &data ));
4731 } /* end of my_fwrite() */
4734 /*{{{ int my_flush(FILE *fp)*/
4739 if ((res = fflush(fp)) == 0 && fp) {
4740 #ifdef VMS_DO_SOCKETS
4742 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
4744 res = fsync(fileno(fp));
4747 * If the flush succeeded but set end-of-file, we need to clear
4748 * the error because our caller may check ferror(). BTW, this
4749 * probably means we just flushed an empty file.
4751 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
4758 * Here are replacements for the following Unix routines in the VMS environment:
4759 * getpwuid Get information for a particular UIC or UID
4760 * getpwnam Get information for a named user
4761 * getpwent Get information for each user in the rights database
4762 * setpwent Reset search to the start of the rights database
4763 * endpwent Finish searching for users in the rights database
4765 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
4766 * (defined in pwd.h), which contains the following fields:-
4768 * char *pw_name; Username (in lower case)
4769 * char *pw_passwd; Hashed password
4770 * unsigned int pw_uid; UIC
4771 * unsigned int pw_gid; UIC group number
4772 * char *pw_unixdir; Default device/directory (VMS-style)
4773 * char *pw_gecos; Owner name
4774 * char *pw_dir; Default device/directory (Unix-style)
4775 * char *pw_shell; Default CLI name (eg. DCL)
4777 * If the specified user does not exist, getpwuid and getpwnam return NULL.
4779 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
4780 * not the UIC member number (eg. what's returned by getuid()),
4781 * getpwuid() can accept either as input (if uid is specified, the caller's
4782 * UIC group is used), though it won't recognise gid=0.
4784 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
4785 * information about other users in your group or in other groups, respectively.
4786 * If the required privilege is not available, then these routines fill only
4787 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
4790 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
4793 /* sizes of various UAF record fields */
4794 #define UAI$S_USERNAME 12
4795 #define UAI$S_IDENT 31
4796 #define UAI$S_OWNER 31
4797 #define UAI$S_DEFDEV 31
4798 #define UAI$S_DEFDIR 63
4799 #define UAI$S_DEFCLI 31
4802 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
4803 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
4804 (uic).uic$v_group != UIC$K_WILD_GROUP)
4806 static char __empty[]= "";
4807 static struct passwd __passwd_empty=
4808 {(char *) __empty, (char *) __empty, 0, 0,
4809 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
4810 static int contxt= 0;
4811 static struct passwd __pwdcache;
4812 static char __pw_namecache[UAI$S_IDENT+1];
4815 * This routine does most of the work extracting the user information.
4817 static int fillpasswd (const char *name, struct passwd *pwd)
4821 unsigned char length;
4822 char pw_gecos[UAI$S_OWNER+1];
4824 static union uicdef uic;
4826 unsigned char length;
4827 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
4830 unsigned char length;
4831 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
4834 unsigned char length;
4835 char pw_shell[UAI$S_DEFCLI+1];
4837 static char pw_passwd[UAI$S_PWD+1];
4839 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
4840 struct dsc$descriptor_s name_desc;
4841 unsigned long int sts;
4843 static struct itmlst_3 itmlst[]= {
4844 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
4845 {sizeof(uic), UAI$_UIC, &uic, &luic},
4846 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
4847 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
4848 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
4849 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
4850 {0, 0, NULL, NULL}};
4852 name_desc.dsc$w_length= strlen(name);
4853 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
4854 name_desc.dsc$b_class= DSC$K_CLASS_S;
4855 name_desc.dsc$a_pointer= (char *) name;
4857 /* Note that sys$getuai returns many fields as counted strings. */
4858 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
4859 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
4860 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
4862 else { _ckvmssts(sts); }
4863 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
4865 if ((int) owner.length < lowner) lowner= (int) owner.length;
4866 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
4867 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
4868 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
4869 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
4870 owner.pw_gecos[lowner]= '\0';
4871 defdev.pw_dir[ldefdev+ldefdir]= '\0';
4872 defcli.pw_shell[ldefcli]= '\0';
4873 if (valid_uic(uic)) {
4874 pwd->pw_uid= uic.uic$l_uic;
4875 pwd->pw_gid= uic.uic$v_group;
4878 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
4879 pwd->pw_passwd= pw_passwd;
4880 pwd->pw_gecos= owner.pw_gecos;
4881 pwd->pw_dir= defdev.pw_dir;
4882 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
4883 pwd->pw_shell= defcli.pw_shell;
4884 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
4886 ldir= strlen(pwd->pw_unixdir) - 1;
4887 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
4890 strcpy(pwd->pw_unixdir, pwd->pw_dir);
4891 __mystrtolower(pwd->pw_unixdir);
4896 * Get information for a named user.
4898 /*{{{struct passwd *getpwnam(char *name)*/
4899 struct passwd *my_getpwnam(char *name)
4901 struct dsc$descriptor_s name_desc;
4903 unsigned long int status, sts;
4906 __pwdcache = __passwd_empty;
4907 if (!fillpasswd(name, &__pwdcache)) {
4908 /* We still may be able to determine pw_uid and pw_gid */
4909 name_desc.dsc$w_length= strlen(name);
4910 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
4911 name_desc.dsc$b_class= DSC$K_CLASS_S;
4912 name_desc.dsc$a_pointer= (char *) name;
4913 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
4914 __pwdcache.pw_uid= uic.uic$l_uic;
4915 __pwdcache.pw_gid= uic.uic$v_group;
4918 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
4919 set_vaxc_errno(sts);
4920 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
4923 else { _ckvmssts(sts); }
4926 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
4927 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
4928 __pwdcache.pw_name= __pw_namecache;
4930 } /* end of my_getpwnam() */
4934 * Get information for a particular UIC or UID.
4935 * Called by my_getpwent with uid=-1 to list all users.
4937 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
4938 struct passwd *my_getpwuid(Uid_t uid)
4940 const $DESCRIPTOR(name_desc,__pw_namecache);
4941 unsigned short lname;
4943 unsigned long int status;
4946 if (uid == (unsigned int) -1) {
4948 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
4949 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
4950 set_vaxc_errno(status);
4951 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
4955 else { _ckvmssts(status); }
4956 } while (!valid_uic (uic));
4960 if (!uic.uic$v_group)
4961 uic.uic$v_group= PerlProc_getgid();
4963 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
4964 else status = SS$_IVIDENT;
4965 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
4966 status == RMS$_PRV) {
4967 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
4970 else { _ckvmssts(status); }
4972 __pw_namecache[lname]= '\0';
4973 __mystrtolower(__pw_namecache);
4975 __pwdcache = __passwd_empty;
4976 __pwdcache.pw_name = __pw_namecache;
4978 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
4979 The identifier's value is usually the UIC, but it doesn't have to be,
4980 so if we can, we let fillpasswd update this. */
4981 __pwdcache.pw_uid = uic.uic$l_uic;
4982 __pwdcache.pw_gid = uic.uic$v_group;
4984 fillpasswd(__pw_namecache, &__pwdcache);
4987 } /* end of my_getpwuid() */
4991 * Get information for next user.
4993 /*{{{struct passwd *my_getpwent()*/
4994 struct passwd *my_getpwent()
4996 return (my_getpwuid((unsigned int) -1));
5001 * Finish searching rights database for users.
5003 /*{{{void my_endpwent()*/
5008 _ckvmssts(sys$finish_rdb(&contxt));
5014 #ifdef HOMEGROWN_POSIX_SIGNALS
5015 /* Signal handling routines, pulled into the core from POSIX.xs.
5017 * We need these for threads, so they've been rolled into the core,
5018 * rather than left in POSIX.xs.
5020 * (DRS, Oct 23, 1997)
5023 /* sigset_t is atomic under VMS, so these routines are easy */
5024 /*{{{int my_sigemptyset(sigset_t *) */
5025 int my_sigemptyset(sigset_t *set) {
5026 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5032 /*{{{int my_sigfillset(sigset_t *)*/
5033 int my_sigfillset(sigset_t *set) {
5035 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5036 for (i = 0; i < NSIG; i++) *set |= (1 << i);
5042 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
5043 int my_sigaddset(sigset_t *set, int sig) {
5044 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5045 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5046 *set |= (1 << (sig - 1));
5052 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
5053 int my_sigdelset(sigset_t *set, int sig) {
5054 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5055 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5056 *set &= ~(1 << (sig - 1));
5062 /*{{{int my_sigismember(sigset_t *set, int sig)*/
5063 int my_sigismember(sigset_t *set, int sig) {
5064 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5065 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5066 *set & (1 << (sig - 1));
5071 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
5072 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
5075 /* If set and oset are both null, then things are badly wrong. Bail out. */
5076 if ((oset == NULL) && (set == NULL)) {
5077 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5081 /* If set's null, then we're just handling a fetch. */
5083 tempmask = sigblock(0);
5088 tempmask = sigsetmask(*set);
5091 tempmask = sigblock(*set);
5094 tempmask = sigblock(0);
5095 sigsetmask(*oset & ~tempmask);
5098 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5103 /* Did they pass us an oset? If so, stick our holding mask into it */
5110 #endif /* HOMEGROWN_POSIX_SIGNALS */
5113 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
5114 * my_utime(), and flex_stat(), all of which operate on UTC unless
5115 * VMSISH_TIMES is true.
5117 /* method used to handle UTC conversions:
5118 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
5120 static int gmtime_emulation_type;
5121 /* number of secs to add to UTC POSIX-style time to get local time */
5122 static long int utc_offset_secs;
5124 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
5125 * in vmsish.h. #undef them here so we can call the CRTL routines
5132 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
5133 # define RTL_USES_UTC 1
5137 * DEC C previous to 6.0 corrupts the behavior of the /prefix
5138 * qualifier with the extern prefix pragma. This provisional
5139 * hack circumvents this prefix pragma problem in previous
5142 #if defined(__VMS_VER) && __VMS_VER >= 70000000
5143 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
5144 # pragma __extern_prefix save
5145 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
5146 # define gmtime decc$__utctz_gmtime
5147 # define localtime decc$__utctz_localtime
5148 # define time decc$__utc_time
5149 # pragma __extern_prefix restore
5151 struct tm *gmtime(), *localtime();
5157 static time_t toutc_dst(time_t loc) {
5160 if ((rsltmp = localtime(&loc)) == NULL) return -1;
5161 loc -= utc_offset_secs;
5162 if (rsltmp->tm_isdst) loc -= 3600;
5165 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5166 ((gmtime_emulation_type || my_time(NULL)), \
5167 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
5168 ((secs) - utc_offset_secs))))
5170 static time_t toloc_dst(time_t utc) {
5173 utc += utc_offset_secs;
5174 if ((rsltmp = localtime(&utc)) == NULL) return -1;
5175 if (rsltmp->tm_isdst) utc += 3600;
5178 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5179 ((gmtime_emulation_type || my_time(NULL)), \
5180 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
5181 ((secs) + utc_offset_secs))))
5184 /* my_time(), my_localtime(), my_gmtime()
5185 * By default traffic in UTC time values, using CRTL gmtime() or
5186 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
5187 * Note: We need to use these functions even when the CRTL has working
5188 * UTC support, since they also handle C<use vmsish qw(times);>
5190 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
5191 * Modified by Charles Bailey <bailey@newman.upenn.edu>
5194 /*{{{time_t my_time(time_t *timep)*/
5195 time_t my_time(time_t *timep)
5201 if (gmtime_emulation_type == 0) {
5203 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
5204 /* results of calls to gmtime() and localtime() */
5205 /* for same &base */
5207 gmtime_emulation_type++;
5208 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
5209 char off[LNM$C_NAMLENGTH+1];;
5211 gmtime_emulation_type++;
5212 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
5213 gmtime_emulation_type++;
5214 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
5216 else { utc_offset_secs = atol(off); }
5218 else { /* We've got a working gmtime() */
5219 struct tm gmt, local;
5222 tm_p = localtime(&base);
5224 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
5225 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
5226 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
5227 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
5233 # ifdef RTL_USES_UTC
5234 if (VMSISH_TIME) when = _toloc(when);
5236 if (!VMSISH_TIME) when = _toutc(when);
5239 if (timep != NULL) *timep = when;
5242 } /* end of my_time() */
5246 /*{{{struct tm *my_gmtime(const time_t *timep)*/
5248 my_gmtime(const time_t *timep)
5255 if (timep == NULL) {
5256 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5259 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
5263 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
5265 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
5266 return gmtime(&when);
5268 /* CRTL localtime() wants local time as input, so does no tz correction */
5269 rsltmp = localtime(&when);
5270 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
5273 } /* end of my_gmtime() */
5277 /*{{{struct tm *my_localtime(const time_t *timep)*/
5279 my_localtime(const time_t *timep)
5285 if (timep == NULL) {
5286 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5289 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
5290 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
5293 # ifdef RTL_USES_UTC
5295 if (VMSISH_TIME) when = _toutc(when);
5297 /* CRTL localtime() wants UTC as input, does tz correction itself */
5298 return localtime(&when);
5301 if (!VMSISH_TIME) when = _toloc(when); /* Input was UTC */
5304 /* CRTL localtime() wants local time as input, so does no tz correction */
5305 rsltmp = localtime(&when);
5306 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = -1;
5309 } /* end of my_localtime() */
5312 /* Reset definitions for later calls */
5313 #define gmtime(t) my_gmtime(t)
5314 #define localtime(t) my_localtime(t)
5315 #define time(t) my_time(t)
5318 /* my_utime - update modification time of a file
5319 * calling sequence is identical to POSIX utime(), but under
5320 * VMS only the modification time is changed; ODS-2 does not
5321 * maintain access times. Restrictions differ from the POSIX
5322 * definition in that the time can be changed as long as the
5323 * caller has permission to execute the necessary IO$_MODIFY $QIO;
5324 * no separate checks are made to insure that the caller is the
5325 * owner of the file or has special privs enabled.
5326 * Code here is based on Joe Meadows' FILE utility.
5329 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
5330 * to VMS epoch (01-JAN-1858 00:00:00.00)
5331 * in 100 ns intervals.
5333 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
5335 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
5336 int my_utime(char *file, struct utimbuf *utimes)
5340 long int bintime[2], len = 2, lowbit, unixtime,
5341 secscale = 10000000; /* seconds --> 100 ns intervals */
5342 unsigned long int chan, iosb[2], retsts;
5343 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
5344 struct FAB myfab = cc$rms_fab;
5345 struct NAM mynam = cc$rms_nam;
5346 #if defined (__DECC) && defined (__VAX)
5347 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
5348 * at least through VMS V6.1, which causes a type-conversion warning.
5350 # pragma message save
5351 # pragma message disable cvtdiftypes
5353 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
5354 struct fibdef myfib;
5355 #if defined (__DECC) && defined (__VAX)
5356 /* This should be right after the declaration of myatr, but due
5357 * to a bug in VAX DEC C, this takes effect a statement early.
5359 # pragma message restore
5361 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
5362 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
5363 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
5365 if (file == NULL || *file == '\0') {
5367 set_vaxc_errno(LIB$_INVARG);
5370 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
5372 if (utimes != NULL) {
5373 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
5374 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
5375 * Since time_t is unsigned long int, and lib$emul takes a signed long int
5376 * as input, we force the sign bit to be clear by shifting unixtime right
5377 * one bit, then multiplying by an extra factor of 2 in lib$emul().
5379 lowbit = (utimes->modtime & 1) ? secscale : 0;
5380 unixtime = (long int) utimes->modtime;
5382 /* If input was UTC; convert to local for sys svc */
5383 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
5385 unixtime >>= 1; secscale <<= 1;
5386 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
5387 if (!(retsts & 1)) {
5389 set_vaxc_errno(retsts);
5392 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
5393 if (!(retsts & 1)) {
5395 set_vaxc_errno(retsts);
5400 /* Just get the current time in VMS format directly */
5401 retsts = sys$gettim(bintime);
5402 if (!(retsts & 1)) {
5404 set_vaxc_errno(retsts);
5409 myfab.fab$l_fna = vmsspec;
5410 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
5411 myfab.fab$l_nam = &mynam;
5412 mynam.nam$l_esa = esa;
5413 mynam.nam$b_ess = (unsigned char) sizeof esa;
5414 mynam.nam$l_rsa = rsa;
5415 mynam.nam$b_rss = (unsigned char) sizeof rsa;
5417 /* Look for the file to be affected, letting RMS parse the file
5418 * specification for us as well. I have set errno using only
5419 * values documented in the utime() man page for VMS POSIX.
5421 retsts = sys$parse(&myfab,0,0);
5422 if (!(retsts & 1)) {
5423 set_vaxc_errno(retsts);
5424 if (retsts == RMS$_PRV) set_errno(EACCES);
5425 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5426 else set_errno(EVMSERR);
5429 retsts = sys$search(&myfab,0,0);
5430 if (!(retsts & 1)) {
5431 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
5432 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
5433 set_vaxc_errno(retsts);
5434 if (retsts == RMS$_PRV) set_errno(EACCES);
5435 else if (retsts == RMS$_FNF) set_errno(ENOENT);
5436 else set_errno(EVMSERR);
5440 devdsc.dsc$w_length = mynam.nam$b_dev;
5441 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
5443 retsts = sys$assign(&devdsc,&chan,0,0);
5444 if (!(retsts & 1)) {
5445 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
5446 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
5447 set_vaxc_errno(retsts);
5448 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
5449 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
5450 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
5451 else set_errno(EVMSERR);
5455 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
5456 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
5458 memset((void *) &myfib, 0, sizeof myfib);
5460 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
5461 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
5462 /* This prevents the revision time of the file being reset to the current
5463 * time as a result of our IO$_MODIFY $QIO. */
5464 myfib.fib$l_acctl = FIB$M_NORECORD;
5466 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
5467 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
5468 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
5470 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
5471 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
5472 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
5473 _ckvmssts(sys$dassgn(chan));
5474 if (retsts & 1) retsts = iosb[0];
5475 if (!(retsts & 1)) {
5476 set_vaxc_errno(retsts);
5477 if (retsts == SS$_NOPRIV) set_errno(EACCES);
5478 else set_errno(EVMSERR);
5483 } /* end of my_utime() */
5487 * flex_stat, flex_fstat
5488 * basic stat, but gets it right when asked to stat
5489 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
5492 /* encode_dev packs a VMS device name string into an integer to allow
5493 * simple comparisons. This can be used, for example, to check whether two
5494 * files are located on the same device, by comparing their encoded device
5495 * names. Even a string comparison would not do, because stat() reuses the
5496 * device name buffer for each call; so without encode_dev, it would be
5497 * necessary to save the buffer and use strcmp (this would mean a number of
5498 * changes to the standard Perl code, to say nothing of what a Perl script
5501 * The device lock id, if it exists, should be unique (unless perhaps compared
5502 * with lock ids transferred from other nodes). We have a lock id if the disk is
5503 * mounted cluster-wide, which is when we tend to get long (host-qualified)
5504 * device names. Thus we use the lock id in preference, and only if that isn't
5505 * available, do we try to pack the device name into an integer (flagged by
5506 * the sign bit (LOCKID_MASK) being set).
5508 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
5509 * name and its encoded form, but it seems very unlikely that we will find
5510 * two files on different disks that share the same encoded device names,
5511 * and even more remote that they will share the same file id (if the test
5512 * is to check for the same file).
5514 * A better method might be to use sys$device_scan on the first call, and to
5515 * search for the device, returning an index into the cached array.
5516 * The number returned would be more intelligable.
5517 * This is probably not worth it, and anyway would take quite a bit longer
5518 * on the first call.
5520 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
5521 static mydev_t encode_dev (const char *dev)
5524 unsigned long int f;
5530 if (!dev || !dev[0]) return 0;
5534 struct dsc$descriptor_s dev_desc;
5535 unsigned long int status, lockid, item = DVI$_LOCKID;
5537 /* For cluster-mounted disks, the disk lock identifier is unique, so we
5538 can try that first. */
5539 dev_desc.dsc$w_length = strlen (dev);
5540 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
5541 dev_desc.dsc$b_class = DSC$K_CLASS_S;
5542 dev_desc.dsc$a_pointer = (char *) dev;
5543 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
5544 if (lockid) return (lockid & ~LOCKID_MASK);
5548 /* Otherwise we try to encode the device name */
5552 for (q = dev + strlen(dev); q--; q >= dev) {
5555 else if (isalpha (toupper (*q)))
5556 c= toupper (*q) - 'A' + (char)10;
5558 continue; /* Skip '$'s */
5560 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
5562 enc += f * (unsigned long int) c;
5564 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
5566 } /* end of encode_dev() */
5568 static char namecache[NAM$C_MAXRSS+1];
5571 is_null_device(name)
5575 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
5576 The underscore prefix, controller letter, and unit number are
5577 independently optional; for our purposes, the colon punctuation
5578 is not. The colon can be trailed by optional directory and/or
5579 filename, but two consecutive colons indicates a nodename rather
5580 than a device. [pr] */
5581 if (*name == '_') ++name;
5582 if (tolower(*name++) != 'n') return 0;
5583 if (tolower(*name++) != 'l') return 0;
5584 if (tolower(*name) == 'a') ++name;
5585 if (*name == '0') ++name;
5586 return (*name++ == ':') && (*name != ':');
5589 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
5590 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
5591 * subset of the applicable information.
5594 Perl_cando(pTHX_ Mode_t bit, Uid_t effective, Stat_t *statbufp)
5596 char fname_phdev[NAM$C_MAXRSS+1];
5597 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
5599 char fname[NAM$C_MAXRSS+1];
5600 unsigned long int retsts;
5601 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
5602 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5604 /* If the struct mystat is stale, we're OOL; stat() overwrites the
5605 device name on successive calls */
5606 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
5607 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
5608 namdsc.dsc$a_pointer = fname;
5609 namdsc.dsc$w_length = sizeof fname - 1;
5611 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
5612 &namdsc,&namdsc.dsc$w_length,0,0);
5614 fname[namdsc.dsc$w_length] = '\0';
5616 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
5617 * but if someone has redefined that logical, Perl gets very lost. Since
5618 * we have the physical device name from the stat buffer, just paste it on.
5620 strcpy( fname_phdev, statbufp->st_devnam );
5621 strcat( fname_phdev, strrchr(fname, ':') );
5623 return cando_by_name(bit,effective,fname_phdev);
5625 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
5626 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
5630 return FALSE; /* Should never get to here */
5632 } /* end of cando() */
5636 /*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
5638 cando_by_name(I32 bit, Uid_t effective, char *fname)
5640 static char usrname[L_cuserid];
5641 static struct dsc$descriptor_s usrdsc =
5642 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
5643 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
5644 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
5645 unsigned short int retlen;
5647 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5648 union prvdef curprv;
5649 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
5650 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
5651 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
5654 if (!fname || !*fname) return FALSE;
5655 /* Make sure we expand logical names, since sys$check_access doesn't */
5656 if (!strpbrk(fname,"/]>:")) {
5657 strcpy(fileified,fname);
5658 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
5661 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
5662 retlen = namdsc.dsc$w_length = strlen(vmsname);
5663 namdsc.dsc$a_pointer = vmsname;
5664 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
5665 vmsname[retlen-1] == ':') {
5666 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
5667 namdsc.dsc$w_length = strlen(fileified);
5668 namdsc.dsc$a_pointer = fileified;
5671 if (!usrdsc.dsc$w_length) {
5673 usrdsc.dsc$w_length = strlen(usrname);
5677 case S_IXUSR: case S_IXGRP: case S_IXOTH:
5678 access = ARM$M_EXECUTE; break;
5679 case S_IRUSR: case S_IRGRP: case S_IROTH:
5680 access = ARM$M_READ; break;
5681 case S_IWUSR: case S_IWGRP: case S_IWOTH:
5682 access = ARM$M_WRITE; break;
5683 case S_IDUSR: case S_IDGRP: case S_IDOTH:
5684 access = ARM$M_DELETE; break;
5689 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
5690 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
5691 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
5692 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
5693 set_vaxc_errno(retsts);
5694 if (retsts == SS$_NOPRIV) set_errno(EACCES);
5695 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
5696 else set_errno(ENOENT);
5699 if (retsts == SS$_NORMAL) {
5700 if (!privused) return TRUE;
5701 /* We can get access, but only by using privs. Do we have the
5702 necessary privs currently enabled? */
5703 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
5704 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
5705 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
5706 !curprv.prv$v_bypass) return FALSE;
5707 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
5708 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
5709 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
5712 if (retsts == SS$_ACCONFLICT) {
5717 return FALSE; /* Should never get here */
5719 } /* end of cando_by_name() */
5723 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
5725 flex_fstat(int fd, Stat_t *statbufp)
5728 if (!fstat(fd,(stat_t *) statbufp)) {
5729 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
5730 statbufp->st_dev = encode_dev(statbufp->st_devnam);
5731 # ifdef RTL_USES_UTC
5734 statbufp->st_mtime = _toloc(statbufp->st_mtime);
5735 statbufp->st_atime = _toloc(statbufp->st_atime);
5736 statbufp->st_ctime = _toloc(statbufp->st_ctime);
5741 if (!VMSISH_TIME) { /* Return UTC instead of local time */
5745 statbufp->st_mtime = _toutc(statbufp->st_mtime);
5746 statbufp->st_atime = _toutc(statbufp->st_atime);
5747 statbufp->st_ctime = _toutc(statbufp->st_ctime);
5754 } /* end of flex_fstat() */
5757 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
5759 flex_stat(const char *fspec, Stat_t *statbufp)
5762 char fileified[NAM$C_MAXRSS+1];
5763 char temp_fspec[NAM$C_MAXRSS+300];
5766 strcpy(temp_fspec, fspec);
5767 if (statbufp == (Stat_t *) &PL_statcache)
5768 do_tovmsspec(temp_fspec,namecache,0);
5769 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
5770 memset(statbufp,0,sizeof *statbufp);
5771 statbufp->st_dev = encode_dev("_NLA0:");
5772 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
5773 statbufp->st_uid = 0x00010001;
5774 statbufp->st_gid = 0x0001;
5775 time((time_t *)&statbufp->st_mtime);
5776 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
5780 /* Try for a directory name first. If fspec contains a filename without
5781 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
5782 * and sea:[wine.dark]water. exist, we prefer the directory here.
5783 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
5784 * not sea:[wine.dark]., if the latter exists. If the intended target is
5785 * the file with null type, specify this by calling flex_stat() with
5786 * a '.' at the end of fspec.
5788 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
5789 retval = stat(fileified,(stat_t *) statbufp);
5790 if (!retval && statbufp == (Stat_t *) &PL_statcache)
5791 strcpy(namecache,fileified);
5793 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
5795 statbufp->st_dev = encode_dev(statbufp->st_devnam);
5796 # ifdef RTL_USES_UTC
5799 statbufp->st_mtime = _toloc(statbufp->st_mtime);
5800 statbufp->st_atime = _toloc(statbufp->st_atime);
5801 statbufp->st_ctime = _toloc(statbufp->st_ctime);
5806 if (!VMSISH_TIME) { /* Return UTC instead of local time */
5810 statbufp->st_mtime = _toutc(statbufp->st_mtime);
5811 statbufp->st_atime = _toutc(statbufp->st_atime);
5812 statbufp->st_ctime = _toutc(statbufp->st_ctime);
5818 } /* end of flex_stat() */
5822 /*{{{char *my_getlogin()*/
5823 /* VMS cuserid == Unix getlogin, except calling sequence */
5827 static char user[L_cuserid];
5828 return cuserid(user);
5833 /* rmscopy - copy a file using VMS RMS routines
5835 * Copies contents and attributes of spec_in to spec_out, except owner
5836 * and protection information. Name and type of spec_in are used as
5837 * defaults for spec_out. The third parameter specifies whether rmscopy()
5838 * should try to propagate timestamps from the input file to the output file.
5839 * If it is less than 0, no timestamps are preserved. If it is 0, then
5840 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
5841 * propagated to the output file at creation iff the output file specification
5842 * did not contain an explicit name or type, and the revision date is always
5843 * updated at the end of the copy operation. If it is greater than 0, then
5844 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
5845 * other than the revision date should be propagated, and bit 1 indicates
5846 * that the revision date should be propagated.
5848 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
5850 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
5851 * Incorporates, with permission, some code from EZCOPY by Tim Adye
5852 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
5853 * as part of the Perl standard distribution under the terms of the
5854 * GNU General Public License or the Perl Artistic License. Copies
5855 * of each may be found in the Perl standard distribution.
5857 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
5859 Perl_rmscopy(pTHX_ char *spec_in, char *spec_out, int preserve_dates)
5861 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
5862 rsa[NAM$C_MAXRSS], ubf[32256];
5863 unsigned long int i, sts, sts2;
5864 struct FAB fab_in, fab_out;
5865 struct RAB rab_in, rab_out;
5867 struct XABDAT xabdat;
5868 struct XABFHC xabfhc;
5869 struct XABRDT xabrdt;
5870 struct XABSUM xabsum;
5872 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
5873 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
5874 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5878 fab_in = cc$rms_fab;
5879 fab_in.fab$l_fna = vmsin;
5880 fab_in.fab$b_fns = strlen(vmsin);
5881 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
5882 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
5883 fab_in.fab$l_fop = FAB$M_SQO;
5884 fab_in.fab$l_nam = &nam;
5885 fab_in.fab$l_xab = (void *) &xabdat;
5888 nam.nam$l_rsa = rsa;
5889 nam.nam$b_rss = sizeof(rsa);
5890 nam.nam$l_esa = esa;
5891 nam.nam$b_ess = sizeof (esa);
5892 nam.nam$b_esl = nam.nam$b_rsl = 0;
5894 xabdat = cc$rms_xabdat; /* To get creation date */
5895 xabdat.xab$l_nxt = (void *) &xabfhc;
5897 xabfhc = cc$rms_xabfhc; /* To get record length */
5898 xabfhc.xab$l_nxt = (void *) &xabsum;
5900 xabsum = cc$rms_xabsum; /* To get key and area information */
5902 if (!((sts = sys$open(&fab_in)) & 1)) {
5903 set_vaxc_errno(sts);
5905 case RMS$_FNF: case RMS$_DNF:
5906 set_errno(ENOENT); break;
5908 set_errno(ENOTDIR); break;
5910 set_errno(ENODEV); break;
5912 set_errno(EINVAL); break;
5914 set_errno(EACCES); break;
5922 fab_out.fab$w_ifi = 0;
5923 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
5924 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
5925 fab_out.fab$l_fop = FAB$M_SQO;
5926 fab_out.fab$l_fna = vmsout;
5927 fab_out.fab$b_fns = strlen(vmsout);
5928 fab_out.fab$l_dna = nam.nam$l_name;
5929 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
5931 if (preserve_dates == 0) { /* Act like DCL COPY */
5932 nam.nam$b_nop = NAM$M_SYNCHK;
5933 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
5934 if (!((sts = sys$parse(&fab_out)) & 1)) {
5935 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
5936 set_vaxc_errno(sts);
5939 fab_out.fab$l_xab = (void *) &xabdat;
5940 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
5942 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
5943 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
5944 preserve_dates =0; /* bitmask from this point forward */
5946 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
5947 if (!((sts = sys$create(&fab_out)) & 1)) {
5948 set_vaxc_errno(sts);
5951 set_errno(ENOENT); break;
5953 set_errno(ENOTDIR); break;
5955 set_errno(ENODEV); break;
5957 set_errno(EINVAL); break;
5959 set_errno(EACCES); break;
5965 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
5966 if (preserve_dates & 2) {
5967 /* sys$close() will process xabrdt, not xabdat */
5968 xabrdt = cc$rms_xabrdt;
5970 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
5972 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
5973 * is unsigned long[2], while DECC & VAXC use a struct */
5974 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
5976 fab_out.fab$l_xab = (void *) &xabrdt;
5979 rab_in = cc$rms_rab;
5980 rab_in.rab$l_fab = &fab_in;
5981 rab_in.rab$l_rop = RAB$M_BIO;
5982 rab_in.rab$l_ubf = ubf;
5983 rab_in.rab$w_usz = sizeof ubf;
5984 if (!((sts = sys$connect(&rab_in)) & 1)) {
5985 sys$close(&fab_in); sys$close(&fab_out);
5986 set_errno(EVMSERR); set_vaxc_errno(sts);
5990 rab_out = cc$rms_rab;
5991 rab_out.rab$l_fab = &fab_out;
5992 rab_out.rab$l_rbf = ubf;
5993 if (!((sts = sys$connect(&rab_out)) & 1)) {
5994 sys$close(&fab_in); sys$close(&fab_out);
5995 set_errno(EVMSERR); set_vaxc_errno(sts);
5999 while ((sts = sys$read(&rab_in))) { /* always true */
6000 if (sts == RMS$_EOF) break;
6001 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
6002 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
6003 sys$close(&fab_in); sys$close(&fab_out);
6004 set_errno(EVMSERR); set_vaxc_errno(sts);
6009 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
6010 sys$close(&fab_in); sys$close(&fab_out);
6011 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
6013 set_errno(EVMSERR); set_vaxc_errno(sts);
6019 } /* end of rmscopy() */
6023 /*** The following glue provides 'hooks' to make some of the routines
6024 * from this file available from Perl. These routines are sufficiently
6025 * basic, and are required sufficiently early in the build process,
6026 * that's it's nice to have them available to miniperl as well as the
6027 * full Perl, so they're set up here instead of in an extension. The
6028 * Perl code which handles importation of these names into a given
6029 * package lives in [.VMS]Filespec.pm in @INC.
6033 rmsexpand_fromperl(pTHX_ CV *cv)
6036 char *fspec, *defspec = NULL, *rslt;
6039 if (!items || items > 2)
6040 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
6041 fspec = SvPV(ST(0),n_a);
6042 if (!fspec || !*fspec) XSRETURN_UNDEF;
6043 if (items == 2) defspec = SvPV(ST(1),n_a);
6045 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
6046 ST(0) = sv_newmortal();
6047 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
6052 vmsify_fromperl(pTHX_ CV *cv)
6058 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
6059 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
6060 ST(0) = sv_newmortal();
6061 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
6066 unixify_fromperl(pTHX_ CV *cv)
6072 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
6073 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
6074 ST(0) = sv_newmortal();
6075 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
6080 fileify_fromperl(pTHX_ CV *cv)
6086 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
6087 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
6088 ST(0) = sv_newmortal();
6089 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
6094 pathify_fromperl(pTHX_ CV *cv)
6100 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
6101 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
6102 ST(0) = sv_newmortal();
6103 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
6108 vmspath_fromperl(pTHX_ CV *cv)
6114 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
6115 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
6116 ST(0) = sv_newmortal();
6117 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
6122 unixpath_fromperl(pTHX_ CV *cv)
6128 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
6129 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
6130 ST(0) = sv_newmortal();
6131 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
6136 candelete_fromperl(pTHX_ CV *cv)
6139 char fspec[NAM$C_MAXRSS+1], *fsp;
6144 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
6146 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
6147 if (SvTYPE(mysv) == SVt_PVGV) {
6148 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec,1)) {
6149 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6156 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
6157 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6163 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
6168 rmscopy_fromperl(pTHX_ CV *cv)
6171 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
6173 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6174 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6175 unsigned long int sts;
6180 if (items < 2 || items > 3)
6181 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
6183 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
6184 if (SvTYPE(mysv) == SVt_PVGV) {
6185 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec,1)) {
6186 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6193 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
6194 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6199 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
6200 if (SvTYPE(mysv) == SVt_PVGV) {
6201 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec,1)) {
6202 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6209 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
6210 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6215 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
6217 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
6226 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
6227 workbuff[NAM$C_MAXRSS*1 + 1];
6228 int total_namelen = 3, counter, num_entries;
6229 /* ODS-5 ups this, but we want to be consistent, so... */
6230 int max_name_len = 39;
6231 AV *in_array = (AV *)SvRV(ST(0));
6233 num_entries = av_len(in_array);
6235 /* All the names start with PL_. */
6236 strcpy(ultimate_name, "PL_");
6238 /* Clean up our working buffer */
6239 Zero(work_name, sizeof(work_name), char);
6241 /* Run through the entries and build up a working name */
6242 for(counter = 0; counter <= num_entries; counter++) {
6243 /* If it's not the first name then tack on a __ */
6245 strcat(work_name, "__");
6247 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
6251 /* Check to see if we actually have to bother...*/
6252 if (strlen(work_name) + 3 <= max_name_len) {
6253 strcat(ultimate_name, work_name);
6255 /* It's too darned big, so we need to go strip. We use the same */
6256 /* algorithm as xsubpp does. First, strip out doubled __ */
6257 char *source, *dest, last;
6260 for (source = work_name; *source; source++) {
6261 if (last == *source && last == '_') {
6267 /* Go put it back */
6268 strcpy(work_name, workbuff);
6269 /* Is it still too big? */
6270 if (strlen(work_name) + 3 > max_name_len) {
6271 /* Strip duplicate letters */
6274 for (source = work_name; *source; source++) {
6275 if (last == toupper(*source)) {
6279 last = toupper(*source);
6281 strcpy(work_name, workbuff);
6284 /* Is it *still* too big? */
6285 if (strlen(work_name) + 3 > max_name_len) {
6286 /* Too bad, we truncate */
6287 work_name[max_name_len - 2] = 0;
6289 strcat(ultimate_name, work_name);
6292 /* Okay, return it */
6293 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
6300 char* file = __FILE__;
6302 char temp_buff[512];
6303 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
6304 no_translate_barewords = TRUE;
6306 no_translate_barewords = FALSE;
6309 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
6310 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
6311 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
6312 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
6313 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
6314 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
6315 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
6316 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
6317 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
6318 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);