3 * VMS-specific routines for perl5
5 * Last revised: 20-Aug-1999 by Charles Bailey bailey@newman.upenn.edu
15 #include <climsgdef.h>
24 #include <libclidef.h>
26 #include <lib$routines.h>
35 #include <str$routines.h>
40 /* Older versions of ssdef.h don't have these */
41 #ifndef SS$_INVFILFOROP
42 # define SS$_INVFILFOROP 3930
44 #ifndef SS$_NOSUCHOBJECT
45 # define SS$_NOSUCHOBJECT 2696
48 /* Don't replace system definitions of vfork, getenv, and stat,
49 * code below needs to get to the underlying CRTL routines. */
50 #define DONT_MASK_RTL_CALLS
54 /* Anticipating future expansion in lexical warnings . . . */
56 # define WARN_INTERNAL WARN_MISC
59 /* gcc's header files don't #define direct access macros
60 * corresponding to VAXC's variant structs */
62 # define uic$v_format uic$r_uic_form.uic$v_format
63 # define uic$v_group uic$r_uic_form.uic$v_group
64 # define uic$v_member uic$r_uic_form.uic$v_member
65 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
66 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
67 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
68 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
71 #if defined(NEED_AN_H_ERRNO)
76 unsigned short int buflen;
77 unsigned short int itmcode;
79 unsigned short int *retlen;
82 static char *__mystrtolower(char *str)
84 if (str) for (; *str; ++str) *str= tolower(*str);
88 static struct dsc$descriptor_s fildevdsc =
89 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
90 static struct dsc$descriptor_s crtlenvdsc =
91 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
92 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
93 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
94 static struct dsc$descriptor_s **env_tables = defenv;
95 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
97 /* True if we shouldn't treat barewords as logicals during directory */
99 static int no_translate_barewords;
101 /* Temp for subprocess commands */
102 static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
104 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
106 vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
107 struct dsc$descriptor_s **tabvec, unsigned long int flags)
109 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
110 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
111 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
112 unsigned char acmode;
113 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
114 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
115 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
116 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
118 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
119 #if defined(USE_THREADS)
120 /* We jump through these hoops because we can be called at */
121 /* platform-specific initialization time, which is before anything is */
122 /* set up--we can't even do a plain dTHX since that relies on the */
123 /* interpreter structure to be initialized */
124 struct perl_thread *thr;
126 thr = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
132 if (!lnm || !eqv || idx > LNM$_MAX_INDEX) {
133 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
135 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
136 *cp2 = _toupper(*cp1);
137 if (cp1 - lnm > LNM$C_NAMLENGTH) {
138 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
142 lnmdsc.dsc$w_length = cp1 - lnm;
143 lnmdsc.dsc$a_pointer = uplnm;
144 uplnm[lnmdsc.dsc$w_length] = '\0';
145 secure = flags & PERL__TRNENV_SECURE;
146 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
147 if (!tabvec || !*tabvec) tabvec = env_tables;
149 for (curtab = 0; tabvec[curtab]; curtab++) {
150 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
151 if (!ivenv && !secure) {
156 Perl_warn(aTHX_ "Can't read CRTL environ\n");
159 retsts = SS$_NOLOGNAM;
160 for (i = 0; environ[i]; i++) {
161 if ((eq = strchr(environ[i],'=')) &&
162 !strncmp(environ[i],uplnm,eq - environ[i])) {
164 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
165 if (!eqvlen) continue;
170 if (retsts != SS$_NOLOGNAM) break;
173 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
174 !str$case_blind_compare(&tmpdsc,&clisym)) {
175 if (!ivsym && !secure) {
176 unsigned short int deflen = LNM$C_NAMLENGTH;
177 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
178 /* dynamic dsc to accomodate possible long value */
179 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
180 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
183 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
185 /* Special hack--we might be called before the interpreter's */
186 /* fully initialized, in which case either thr or PL_curcop */
187 /* might be bogus. We have to check, since ckWARN needs them */
188 /* both to be valid if running threaded */
189 #if defined(USE_THREADS)
190 if (thr && PL_curcop) {
192 if (ckWARN(WARN_MISC)) {
193 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
195 #if defined(USE_THREADS)
197 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
202 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
204 _ckvmssts(lib$sfree1_dd(&eqvdsc));
205 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
206 if (retsts == LIB$_NOSUCHSYM) continue;
211 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
212 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
213 if (retsts == SS$_NOLOGNAM) continue;
214 /* PPFs have a prefix */
217 *((int *)uplnm) == *((int *)"SYS$") &&
219 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
220 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
221 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
222 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
223 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
224 memcpy(eqv,eqv+4,eqvlen-4);
230 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
231 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
232 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
233 retsts == SS$_NOLOGNAM) {
234 set_errno(EINVAL); set_vaxc_errno(retsts);
236 else _ckvmssts(retsts);
238 } /* end of vmstrnenv */
241 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
242 /* Define as a function so we can access statics. */
243 int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)
245 return vmstrnenv(lnm,eqv,idx,fildev,
246 #ifdef SECURE_INTERNAL_GETENV
247 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
256 * Note: Uses Perl temp to store result so char * can be returned to
257 * caller; this pointer will be invalidated at next Perl statement
259 * We define this as a function rather than a macro in terms of my_getenv_len()
260 * so that it'll work when PL_curinterp is undefined (and we therefore can't
263 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
265 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
267 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
268 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
269 unsigned long int idx = 0;
273 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
274 /* Set up a temporary buffer for the return value; Perl will
275 * clean it up at the next statement transition */
276 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
277 if (!tmpsv) return NULL;
280 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
281 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
282 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
283 getcwd(eqv,LNM$C_NAMLENGTH);
287 if ((cp2 = strchr(lnm,';')) != NULL) {
289 uplnm[cp2-lnm] = '\0';
290 idx = strtoul(cp2+1,NULL,0);
293 /* Impose security constraints only if tainting */
294 if (sys) sys = PL_curinterp ? PL_tainting : will_taint;
295 if (vmstrnenv(lnm,eqv,idx,
297 #ifdef SECURE_INTERNAL_GETENV
298 sys ? PERL__TRNENV_SECURE : 0
306 } /* end of my_getenv() */
310 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
312 my_getenv_len(const char *lnm, unsigned long *len, bool sys)
315 char *buf, *cp1, *cp2;
316 unsigned long idx = 0;
317 static char __my_getenv_len_eqv[LNM$C_NAMLENGTH+1];
320 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
321 /* Set up a temporary buffer for the return value; Perl will
322 * clean it up at the next statement transition */
323 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
324 if (!tmpsv) return NULL;
327 else buf = __my_getenv_len_eqv; /* Assume no interpreter ==> single thread */
328 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
329 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
330 getcwd(buf,LNM$C_NAMLENGTH);
335 if ((cp2 = strchr(lnm,';')) != NULL) {
338 idx = strtoul(cp2+1,NULL,0);
341 /* Impose security constraints only if tainting */
342 if (sys) sys = PL_curinterp ? PL_tainting : will_taint;
343 if ((*len = vmstrnenv(lnm,buf,idx,
345 #ifdef SECURE_INTERNAL_GETENV
346 sys ? PERL__TRNENV_SECURE : 0
356 } /* end of my_getenv_len() */
359 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
361 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
363 /*{{{ void prime_env_iter() */
366 /* Fill the %ENV associative array with all logical names we can
367 * find, in preparation for iterating over it.
371 static int primed = 0;
372 HV *seenhv = NULL, *envhv;
373 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
374 unsigned short int chan;
375 #ifndef CLI$M_TRUSTED
376 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
378 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
379 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
381 bool have_sym = FALSE, have_lnm = FALSE;
382 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
383 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
384 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
385 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
386 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
388 static perl_mutex primenv_mutex;
389 MUTEX_INIT(&primenv_mutex);
392 if (primed || !PL_envgv) return;
393 MUTEX_LOCK(&primenv_mutex);
394 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
395 envhv = GvHVn(PL_envgv);
396 /* Perform a dummy fetch as an lval to insure that the hash table is
397 * set up. Otherwise, the hv_store() will turn into a nullop. */
398 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
400 for (i = 0; env_tables[i]; i++) {
401 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
402 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
403 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
405 if (have_sym || have_lnm) {
406 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
407 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
408 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
409 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
412 for (i--; i >= 0; i--) {
413 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
416 for (j = 0; environ[j]; j++) {
417 if (!(start = strchr(environ[j],'='))) {
418 if (ckWARN(WARN_INTERNAL))
419 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
423 (void) hv_store(envhv,environ[j],start - environ[j] - 1,
429 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
430 !str$case_blind_compare(&tmpdsc,&clisym)) {
431 strcpy(cmd,"Show Symbol/Global *");
432 cmddsc.dsc$w_length = 20;
433 if (env_tables[i]->dsc$w_length == 12 &&
434 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
435 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
436 flags = defflags | CLI$M_NOLOGNAM;
439 strcpy(cmd,"Show Logical *");
440 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
441 strcat(cmd," /Table=");
442 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
443 cmddsc.dsc$w_length = strlen(cmd);
445 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
446 flags = defflags | CLI$M_NOCLISYM;
449 /* Create a new subprocess to execute each command, to exclude the
450 * remote possibility that someone could subvert a mbx or file used
451 * to write multiple commands to a single subprocess.
454 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
455 0,&riseandshine,0,0,&clidsc,&clitabdsc);
456 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
457 defflags &= ~CLI$M_TRUSTED;
458 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
460 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
461 if (seenhv) SvREFCNT_dec(seenhv);
464 char *cp1, *cp2, *key;
465 unsigned long int sts, iosb[2], retlen, keylen;
468 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
469 if (sts & 1) sts = iosb[0] & 0xffff;
470 if (sts == SS$_ENDOFFILE) {
472 while (substs == 0) { sys$hiber(); wakect++;}
473 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
478 retlen = iosb[0] >> 16;
479 if (!retlen) continue; /* blank line */
481 if (iosb[1] != subpid) {
483 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
487 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
488 Perl_warner(aTHX_ WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
490 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
491 if (*cp1 == '(' || /* Logical name table name */
492 *cp1 == '=' /* Next eqv of searchlist */) continue;
493 if (*cp1 == '"') cp1++;
494 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
495 key = cp1; keylen = cp2 - cp1;
496 if (keylen && hv_exists(seenhv,key,keylen)) continue;
497 while (*cp2 && *cp2 != '=') cp2++;
498 while (*cp2 && *cp2 == '=') cp2++;
499 while (*cp2 && *cp2 == ' ') cp2++;
500 if (*cp2 == '"') { /* String translation; may embed "" */
501 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
502 cp2++; cp1--; /* Skip "" surrounding translation */
504 else { /* Numeric translation */
505 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
506 cp1--; /* stop on last non-space char */
508 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
509 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
512 PERL_HASH(hash,key,keylen);
513 hv_store(envhv,key,keylen,newSVpvn(cp2,cp1 - cp2 + 1),hash);
514 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
516 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
517 /* get the PPFs for this process, not the subprocess */
518 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
519 char eqv[LNM$C_NAMLENGTH+1];
521 for (i = 0; ppfs[i]; i++) {
522 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
523 hv_store(envhv,ppfs[i],strlen(ppfs[i]),newSVpv(eqv,trnlen),0);
528 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
529 if (buf) Safefree(buf);
530 if (seenhv) SvREFCNT_dec(seenhv);
531 MUTEX_UNLOCK(&primenv_mutex);
534 } /* end of prime_env_iter */
538 /*{{{ int vmssetenv(char *lnm, char *eqv)*/
539 /* Define or delete an element in the same "environment" as
540 * vmstrnenv(). If an element is to be deleted, it's removed from
541 * the first place it's found. If it's to be set, it's set in the
542 * place designated by the first element of the table vector.
543 * Like setenv() returns 0 for success, non-zero on error.
546 vmssetenv(char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
548 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
549 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
550 unsigned long int retsts, usermode = PSL$C_USER;
551 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
552 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
553 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
554 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
555 $DESCRIPTOR(local,"_LOCAL");
558 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
559 *cp2 = _toupper(*cp1);
560 if (cp1 - lnm > LNM$C_NAMLENGTH) {
561 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
565 lnmdsc.dsc$w_length = cp1 - lnm;
566 if (!tabvec || !*tabvec) tabvec = env_tables;
568 if (!eqv) { /* we're deleting n element */
569 for (curtab = 0; tabvec[curtab]; curtab++) {
570 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
572 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
573 if ((cp1 = strchr(environ[i],'=')) &&
574 !strncmp(environ[i],lnm,cp1 - environ[i])) {
576 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
579 ivenv = 1; retsts = SS$_NOLOGNAM;
581 if (ckWARN(WARN_INTERNAL))
582 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
583 ivenv = 1; retsts = SS$_NOSUCHPGM;
589 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
590 !str$case_blind_compare(&tmpdsc,&clisym)) {
591 unsigned int symtype;
592 if (tabvec[curtab]->dsc$w_length == 12 &&
593 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
594 !str$case_blind_compare(&tmpdsc,&local))
595 symtype = LIB$K_CLI_LOCAL_SYM;
596 else symtype = LIB$K_CLI_GLOBAL_SYM;
597 retsts = lib$delete_symbol(&lnmdsc,&symtype);
598 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
599 if (retsts == LIB$_NOSUCHSYM) continue;
603 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
604 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
605 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
606 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
607 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
611 else { /* we're defining a value */
612 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
614 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
616 if (ckWARN(WARN_INTERNAL))
617 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
618 retsts = SS$_NOSUCHPGM;
622 eqvdsc.dsc$a_pointer = eqv;
623 eqvdsc.dsc$w_length = strlen(eqv);
624 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
625 !str$case_blind_compare(&tmpdsc,&clisym)) {
626 unsigned int symtype;
627 if (tabvec[0]->dsc$w_length == 12 &&
628 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
629 !str$case_blind_compare(&tmpdsc,&local))
630 symtype = LIB$K_CLI_LOCAL_SYM;
631 else symtype = LIB$K_CLI_GLOBAL_SYM;
632 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
635 if (!*eqv) eqvdsc.dsc$w_length = 1;
636 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
637 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH;
638 if (ckWARN(WARN_MISC)) {
639 Perl_warner(aTHX_ WARN_MISC,"Value of logical \"%s\" too long. Truncating to %i bytes",lnm, LNM$C_NAMLENGTH);
642 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
648 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
649 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
650 set_errno(EVMSERR); break;
651 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
652 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
653 set_errno(EINVAL); break;
660 set_vaxc_errno(retsts);
661 return (int) retsts || 44; /* retsts should never be 0, but just in case */
664 /* We reset error values on success because Perl does an hv_fetch()
665 * before each hv_store(), and if the thing we're setting didn't
666 * previously exist, we've got a leftover error message. (Of course,
667 * this fails in the face of
668 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
669 * in that the error reported in $! isn't spurious,
670 * but it's right more often than not.)
672 set_errno(0); set_vaxc_errno(retsts);
676 } /* end of vmssetenv() */
679 /*{{{ void my_setenv(char *lnm, char *eqv)*/
680 /* This has to be a function since there's a prototype for it in proto.h */
682 Perl_my_setenv(pTHX_ char *lnm,char *eqv)
684 if (lnm && *lnm && strlen(lnm) == 7) {
687 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
688 if (!strcmp(uplnm,"DEFAULT")) {
689 if (eqv && *eqv) chdir(eqv);
693 (void) vmssetenv(lnm,eqv,NULL);
699 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
700 /* my_crypt - VMS password hashing
701 * my_crypt() provides an interface compatible with the Unix crypt()
702 * C library function, and uses sys$hash_password() to perform VMS
703 * password hashing. The quadword hashed password value is returned
704 * as a NUL-terminated 8 character string. my_crypt() does not change
705 * the case of its string arguments; in order to match the behavior
706 * of LOGINOUT et al., alphabetic characters in both arguments must
707 * be upcased by the caller.
710 my_crypt(const char *textpasswd, const char *usrname)
712 # ifndef UAI$C_PREFERRED_ALGORITHM
713 # define UAI$C_PREFERRED_ALGORITHM 127
715 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
716 unsigned short int salt = 0;
717 unsigned long int sts;
719 unsigned short int dsc$w_length;
720 unsigned char dsc$b_type;
721 unsigned char dsc$b_class;
722 const char * dsc$a_pointer;
723 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
724 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
725 struct itmlst_3 uailst[3] = {
726 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
727 { sizeof salt, UAI$_SALT, &salt, 0},
728 { 0, 0, NULL, NULL}};
731 usrdsc.dsc$w_length = strlen(usrname);
732 usrdsc.dsc$a_pointer = usrname;
733 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
735 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
739 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
745 if (sts != RMS$_RNF) return NULL;
748 txtdsc.dsc$w_length = strlen(textpasswd);
749 txtdsc.dsc$a_pointer = textpasswd;
750 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
751 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
754 return (char *) hash;
756 } /* end of my_crypt() */
760 static char *do_rmsexpand(char *, char *, int, char *, unsigned);
761 static char *do_fileify_dirspec(char *, char *, int);
762 static char *do_tovmsspec(char *, char *, int);
764 /*{{{int do_rmdir(char *name)*/
768 char dirfile[NAM$C_MAXRSS+1];
772 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
773 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
774 else retval = kill_file(dirfile);
777 } /* end of do_rmdir */
781 * Delete any file to which user has control access, regardless of whether
782 * delete access is explicitly allowed.
783 * Limitations: User must have write access to parent directory.
784 * Does not block signals or ASTs; if interrupted in midstream
785 * may leave file with an altered ACL.
788 /*{{{int kill_file(char *name)*/
790 kill_file(char *name)
792 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
793 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
794 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
796 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
798 unsigned char myace$b_length;
799 unsigned char myace$b_type;
800 unsigned short int myace$w_flags;
801 unsigned long int myace$l_access;
802 unsigned long int myace$l_ident;
803 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
804 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
805 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
807 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
808 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
809 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
810 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
811 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
812 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
814 /* Expand the input spec using RMS, since the CRTL remove() and
815 * system services won't do this by themselves, so we may miss
816 * a file "hiding" behind a logical name or search list. */
817 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
818 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
819 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
820 /* If not, can changing protections help? */
821 if (vaxc$errno != RMS$_PRV) return -1;
823 /* No, so we get our own UIC to use as a rights identifier,
824 * and the insert an ACE at the head of the ACL which allows us
825 * to delete the file.
827 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
828 fildsc.dsc$w_length = strlen(rspec);
829 fildsc.dsc$a_pointer = rspec;
831 newace.myace$l_ident = oldace.myace$l_ident;
832 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
834 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
835 set_errno(ENOENT); break;
837 set_errno(ENOTDIR); break;
839 set_errno(ENODEV); break;
840 case RMS$_SYN: case SS$_INVFILFOROP:
841 set_errno(EINVAL); break;
843 set_errno(EACCES); break;
847 set_vaxc_errno(aclsts);
850 /* Grab any existing ACEs with this identifier in case we fail */
851 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
852 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
853 || fndsts == SS$_NOMOREACE ) {
854 /* Add the new ACE . . . */
855 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
857 if ((rmsts = remove(name))) {
858 /* We blew it - dir with files in it, no write priv for
859 * parent directory, etc. Put things back the way they were. */
860 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
863 addlst[0].bufadr = &oldace;
864 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
871 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
872 /* We just deleted it, so of course it's not there. Some versions of
873 * VMS seem to return success on the unlock operation anyhow (after all
874 * the unlock is successful), but others don't.
876 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
877 if (aclsts & 1) aclsts = fndsts;
880 set_vaxc_errno(aclsts);
886 } /* end of kill_file() */
890 /*{{{int my_mkdir(char *,Mode_t)*/
892 my_mkdir(char *dir, Mode_t mode)
894 STRLEN dirlen = strlen(dir);
897 /* zero length string sometimes gives ACCVIO */
898 if (dirlen == 0) return -1;
900 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
901 * null file name/type. However, it's commonplace under Unix,
902 * so we'll allow it for a gain in portability.
904 if (dir[dirlen-1] == '/') {
905 char *newdir = savepvn(dir,dirlen-1);
906 int ret = mkdir(newdir,mode);
910 else return mkdir(dir,mode);
911 } /* end of my_mkdir */
914 /*{{{int my_chdir(char *)*/
918 STRLEN dirlen = strlen(dir);
921 /* zero length string sometimes gives ACCVIO */
922 if (dirlen == 0) return -1;
924 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
926 * null file name/type. However, it's commonplace under Unix,
927 * so we'll allow it for a gain in portability.
929 if (dir[dirlen-1] == '/') {
930 char *newdir = savepvn(dir,dirlen-1);
931 int ret = chdir(newdir);
935 else return chdir(dir);
936 } /* end of my_chdir */
940 /*{{{FILE *my_tmpfile()*/
948 if ((fp = tmpfile())) return fp;
950 New(1323,cp,L_tmpnam+24,char);
951 strcpy(cp,"Sys$Scratch:");
952 tmpnam(cp+strlen(cp));
953 strcat(cp,".Perltmp");
954 fp = fopen(cp,"w+","fop=dlt");
962 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
964 static unsigned long int mbxbufsiz;
965 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
970 * Get the SYSGEN parameter MAXBUF, and the smaller of it and the
971 * preprocessor consant BUFSIZ from stdio.h as the size of the
974 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
975 if (mbxbufsiz > BUFSIZ) mbxbufsiz = BUFSIZ;
977 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
979 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
980 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
982 } /* end of create_mbx() */
984 /*{{{ my_popen and my_pclose*/
987 struct pipe_details *next;
988 PerlIO *fp; /* stdio file pointer to pipe mailbox */
989 int pid; /* PID of subprocess */
990 int mode; /* == 'r' if pipe open for reading */
991 int done; /* subprocess has completed */
992 unsigned long int completion; /* termination status of subprocess */
995 struct exit_control_block
997 struct exit_control_block *flink;
998 unsigned long int (*exit_routine)();
999 unsigned long int arg_count;
1000 unsigned long int *status_address;
1001 unsigned long int exit_status;
1004 static struct pipe_details *open_pipes = NULL;
1005 static $DESCRIPTOR(nl_desc, "NL:");
1006 static int waitpid_asleep = 0;
1008 /* Send an EOF to a mbx. N.B. We don't check that fp actually points
1009 * to a mbx; that's the caller's responsibility.
1011 static unsigned long int
1012 pipe_eof(FILE *fp, int immediate)
1014 char devnam[NAM$C_MAXRSS+1], *cp;
1015 unsigned long int chan, iosb[2], retsts, retsts2;
1016 struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
1019 if (fgetname(fp,devnam,1)) {
1020 /* It oughta be a mailbox, so fgetname should give just the device
1021 * name, but just in case . . . */
1022 if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
1023 devdsc.dsc$w_length = strlen(devnam);
1024 _ckvmssts(sys$assign(&devdsc,&chan,0,0));
1025 retsts = sys$qiow(0,chan,IO$_WRITEOF|(immediate?IO$M_NOW|IO$M_NORSWAIT:0),
1026 iosb,0,0,0,0,0,0,0,0);
1027 if (retsts & 1) retsts = iosb[0];
1028 retsts2 = sys$dassgn(chan); /* Be sure to deassign the channel */
1029 if (retsts & 1) retsts = retsts2;
1033 else _ckvmssts(vaxc$errno); /* Should never happen */
1034 return (unsigned long int) vaxc$errno;
1037 static unsigned long int
1040 struct pipe_details *info;
1041 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1046 first we try sending an EOF...ignore if doesn't work, make sure we
1054 _ckvmssts(sys$setast(0));
1055 need_eof = info->mode != 'r' && !info->done;
1056 _ckvmssts(sys$setast(1));
1058 if (pipe_eof(info->fp, 1) & 1) did_stuff = 1;
1062 if (did_stuff) sleep(1); /* wait for EOF to have an effect */
1067 _ckvmssts(sys$setast(0));
1068 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1069 sts = sys$forcex(&info->pid,0,&abort);
1070 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1073 _ckvmssts(sys$setast(1));
1076 if (did_stuff) sleep(1); /* wait for them to respond */
1080 _ckvmssts(sys$setast(0));
1081 if (!info->done) { /* We tried to be nice . . . */
1082 sts = sys$delprc(&info->pid,0);
1083 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1084 info->done = 1; /* so my_pclose doesn't try to write EOF */
1086 _ckvmssts(sys$setast(1));
1091 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1092 else if (!(sts & 1)) retsts = sts;
1097 static struct exit_control_block pipe_exitblock =
1098 {(struct exit_control_block *) 0,
1099 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1103 popen_completion_ast(struct pipe_details *thispipe)
1105 thispipe->done = TRUE;
1106 if (waitpid_asleep) {
1112 static unsigned long int setup_cmddsc(char *cmd, int check_img);
1113 static void vms_execfree();
1116 safe_popen(char *cmd, char *mode)
1118 static int handler_set_up = FALSE;
1120 unsigned short int chan;
1121 unsigned long int sts, flags=1; /* nowait - gnu c doesn't allow &1 */
1123 struct pipe_details *info;
1124 struct dsc$descriptor_s namdsc = {sizeof mbxname, DSC$K_DTYPE_T,
1125 DSC$K_CLASS_S, mbxname},
1126 cmddsc = {0, DSC$K_DTYPE_T,
1130 if (!(setup_cmddsc(cmd,0) & 1)) { set_errno(EINVAL); return Nullfp; }
1131 New(1301,info,1,struct pipe_details);
1133 /* create mailbox */
1134 create_mbx(&chan,&namdsc);
1136 /* open a FILE* onto it */
1137 info->fp = PerlIO_open(mbxname, mode);
1139 /* give up other channel onto it */
1140 _ckvmssts(sys$dassgn(chan));
1150 _ckvmssts(lib$spawn(&VMScmd, &nl_desc, &namdsc, &flags,
1151 0 /* name */, &info->pid, &info->completion,
1152 0, popen_completion_ast,info,0,0,0));
1155 _ckvmssts(lib$spawn(&VMScmd, &namdsc, 0 /* sys$output */, &flags,
1156 0 /* name */, &info->pid, &info->completion,
1157 0, popen_completion_ast,info,0,0,0));
1161 if (!handler_set_up) {
1162 _ckvmssts(sys$dclexh(&pipe_exitblock));
1163 handler_set_up = TRUE;
1165 info->next=open_pipes; /* prepend to list */
1168 PL_forkprocess = info->pid;
1170 } /* end of safe_popen */
1173 /*{{{ FILE *my_popen(char *cmd, char *mode)*/
1175 Perl_my_popen(pTHX_ char *cmd, char *mode)
1178 TAINT_PROPER("popen");
1179 PERL_FLUSHALL_FOR_CHILD;
1180 return safe_popen(cmd,mode);
1185 /*{{{ I32 my_pclose(FILE *fp)*/
1186 I32 Perl_my_pclose(pTHX_ FILE *fp)
1188 struct pipe_details *info, *last = NULL;
1189 unsigned long int retsts;
1192 for (info = open_pipes; info != NULL; last = info, info = info->next)
1193 if (info->fp == fp) break;
1195 if (info == NULL) { /* no such pipe open */
1196 set_errno(ECHILD); /* quoth POSIX */
1197 set_vaxc_errno(SS$_NONEXPR);
1201 /* If we were writing to a subprocess, insure that someone reading from
1202 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
1203 * produce an EOF record in the mailbox. */
1204 _ckvmssts(sys$setast(0));
1205 need_eof = info->mode != 'r' && !info->done;
1206 _ckvmssts(sys$setast(1));
1207 if (need_eof) pipe_eof(info->fp,0);
1208 PerlIO_close(info->fp);
1210 if (info->done) retsts = info->completion;
1211 else waitpid(info->pid,(int *) &retsts,0);
1213 /* remove from list of open pipes */
1214 _ckvmssts(sys$setast(0));
1215 if (last) last->next = info->next;
1216 else open_pipes = info->next;
1217 _ckvmssts(sys$setast(1));
1222 } /* end of my_pclose() */
1224 /* sort-of waitpid; use only with popen() */
1225 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
1227 my_waitpid(Pid_t pid, int *statusp, int flags)
1229 struct pipe_details *info;
1232 for (info = open_pipes; info != NULL; info = info->next)
1233 if (info->pid == pid) break;
1235 if (info != NULL) { /* we know about this child */
1236 while (!info->done) {
1241 *statusp = info->completion;
1244 else { /* we haven't heard of this child */
1245 $DESCRIPTOR(intdsc,"0 00:00:01");
1246 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
1247 unsigned long int interval[2],sts;
1249 if (ckWARN(WARN_EXEC)) {
1250 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
1251 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
1252 if (ownerpid != mypid)
1253 Perl_warner(aTHX_ WARN_EXEC,"pid %x not a child",pid);
1256 _ckvmssts(sys$bintim(&intdsc,interval));
1257 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
1258 _ckvmssts(sys$schdwk(0,0,interval,0));
1259 _ckvmssts(sys$hiber());
1263 /* There's no easy way to find the termination status a child we're
1264 * not aware of beforehand. If we're really interested in the future,
1265 * we can go looking for a termination mailbox, or chase after the
1266 * accounting record for the process.
1272 } /* end of waitpid() */
1277 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
1279 my_gconvert(double val, int ndig, int trail, char *buf)
1281 static char __gcvtbuf[DBL_DIG+1];
1284 loc = buf ? buf : __gcvtbuf;
1286 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
1288 sprintf(loc,"%.*g",ndig,val);
1294 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
1295 return gcvt(val,ndig,loc);
1298 loc[0] = '0'; loc[1] = '\0';
1306 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
1307 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
1308 * to expand file specification. Allows for a single default file
1309 * specification and a simple mask of options. If outbuf is non-NULL,
1310 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
1311 * the resultant file specification is placed. If outbuf is NULL, the
1312 * resultant file specification is placed into a static buffer.
1313 * The third argument, if non-NULL, is taken to be a default file
1314 * specification string. The fourth argument is unused at present.
1315 * rmesexpand() returns the address of the resultant string if
1316 * successful, and NULL on error.
1318 static char *do_tounixspec(char *, char *, int);
1321 do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
1323 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
1324 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
1325 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
1326 struct FAB myfab = cc$rms_fab;
1327 struct NAM mynam = cc$rms_nam;
1329 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
1331 if (!filespec || !*filespec) {
1332 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
1336 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
1337 else outbuf = __rmsexpand_retbuf;
1339 if ((isunix = (strchr(filespec,'/') != NULL))) {
1340 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
1341 filespec = vmsfspec;
1344 myfab.fab$l_fna = filespec;
1345 myfab.fab$b_fns = strlen(filespec);
1346 myfab.fab$l_nam = &mynam;
1348 if (defspec && *defspec) {
1349 if (strchr(defspec,'/') != NULL) {
1350 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
1353 myfab.fab$l_dna = defspec;
1354 myfab.fab$b_dns = strlen(defspec);
1357 mynam.nam$l_esa = esa;
1358 mynam.nam$b_ess = sizeof esa;
1359 mynam.nam$l_rsa = outbuf;
1360 mynam.nam$b_rss = NAM$C_MAXRSS;
1362 retsts = sys$parse(&myfab,0,0);
1363 if (!(retsts & 1)) {
1364 mynam.nam$b_nop |= NAM$M_SYNCHK;
1365 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
1366 retsts = sys$parse(&myfab,0,0);
1367 if (retsts & 1) goto expanded;
1369 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
1370 (void) sys$parse(&myfab,0,0); /* Free search context */
1371 if (out) Safefree(out);
1372 set_vaxc_errno(retsts);
1373 if (retsts == RMS$_PRV) set_errno(EACCES);
1374 else if (retsts == RMS$_DEV) set_errno(ENODEV);
1375 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
1376 else set_errno(EVMSERR);
1379 retsts = sys$search(&myfab,0,0);
1380 if (!(retsts & 1) && retsts != RMS$_FNF) {
1381 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
1382 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
1383 if (out) Safefree(out);
1384 set_vaxc_errno(retsts);
1385 if (retsts == RMS$_PRV) set_errno(EACCES);
1386 else set_errno(EVMSERR);
1390 /* If the input filespec contained any lowercase characters,
1391 * downcase the result for compatibility with Unix-minded code. */
1393 for (out = myfab.fab$l_fna; *out; out++)
1394 if (islower(*out)) { haslower = 1; break; }
1395 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
1396 else { out = esa; speclen = mynam.nam$b_esl; }
1397 /* Trim off null fields added by $PARSE
1398 * If type > 1 char, must have been specified in original or default spec
1399 * (not true for version; $SEARCH may have added version of existing file).
1401 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
1402 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
1403 (mynam.nam$l_ver - mynam.nam$l_type == 1);
1404 if (trimver || trimtype) {
1405 if (defspec && *defspec) {
1406 char defesa[NAM$C_MAXRSS];
1407 struct FAB deffab = cc$rms_fab;
1408 struct NAM defnam = cc$rms_nam;
1410 deffab.fab$l_nam = &defnam;
1411 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
1412 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
1413 defnam.nam$b_nop = NAM$M_SYNCHK;
1414 if (sys$parse(&deffab,0,0) & 1) {
1415 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
1416 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
1419 if (trimver) speclen = mynam.nam$l_ver - out;
1421 /* If we didn't already trim version, copy down */
1422 if (speclen > mynam.nam$l_ver - out)
1423 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
1424 speclen - (mynam.nam$l_ver - out));
1425 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
1428 /* If we just had a directory spec on input, $PARSE "helpfully"
1429 * adds an empty name and type for us */
1430 if (mynam.nam$l_name == mynam.nam$l_type &&
1431 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
1432 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
1433 speclen = mynam.nam$l_name - out;
1434 out[speclen] = '\0';
1435 if (haslower) __mystrtolower(out);
1437 /* Have we been working with an expanded, but not resultant, spec? */
1438 /* Also, convert back to Unix syntax if necessary. */
1439 if (!mynam.nam$b_rsl) {
1441 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
1443 else strcpy(outbuf,esa);
1446 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
1447 strcpy(outbuf,tmpfspec);
1449 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
1450 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
1451 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
1455 /* External entry points */
1456 char *rmsexpand(char *spec, char *buf, char *def, unsigned opt)
1457 { return do_rmsexpand(spec,buf,0,def,opt); }
1458 char *rmsexpand_ts(char *spec, char *buf, char *def, unsigned opt)
1459 { return do_rmsexpand(spec,buf,1,def,opt); }
1463 ** The following routines are provided to make life easier when
1464 ** converting among VMS-style and Unix-style directory specifications.
1465 ** All will take input specifications in either VMS or Unix syntax. On
1466 ** failure, all return NULL. If successful, the routines listed below
1467 ** return a pointer to a buffer containing the appropriately
1468 ** reformatted spec (and, therefore, subsequent calls to that routine
1469 ** will clobber the result), while the routines of the same names with
1470 ** a _ts suffix appended will return a pointer to a mallocd string
1471 ** containing the appropriately reformatted spec.
1472 ** In all cases, only explicit syntax is altered; no check is made that
1473 ** the resulting string is valid or that the directory in question
1476 ** fileify_dirspec() - convert a directory spec into the name of the
1477 ** directory file (i.e. what you can stat() to see if it's a dir).
1478 ** The style (VMS or Unix) of the result is the same as the style
1479 ** of the parameter passed in.
1480 ** pathify_dirspec() - convert a directory spec into a path (i.e.
1481 ** what you prepend to a filename to indicate what directory it's in).
1482 ** The style (VMS or Unix) of the result is the same as the style
1483 ** of the parameter passed in.
1484 ** tounixpath() - convert a directory spec into a Unix-style path.
1485 ** tovmspath() - convert a directory spec into a VMS-style path.
1486 ** tounixspec() - convert any file spec into a Unix-style file spec.
1487 ** tovmsspec() - convert any file spec into a VMS-style spec.
1489 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
1490 ** Permission is given to distribute this code as part of the Perl
1491 ** standard distribution under the terms of the GNU General Public
1492 ** License or the Perl Artistic License. Copies of each may be
1493 ** found in the Perl standard distribution.
1496 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
1497 static char *do_fileify_dirspec(char *dir,char *buf,int ts)
1499 static char __fileify_retbuf[NAM$C_MAXRSS+1];
1500 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
1501 char *retspec, *cp1, *cp2, *lastdir;
1502 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
1504 if (!dir || !*dir) {
1505 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1507 dirlen = strlen(dir);
1508 while (dirlen && dir[dirlen-1] == '/') --dirlen;
1509 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
1510 strcpy(trndir,"/sys$disk/000000");
1514 if (dirlen > NAM$C_MAXRSS) {
1515 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
1517 if (!strpbrk(dir+1,"/]>:")) {
1518 strcpy(trndir,*dir == '/' ? dir + 1: dir);
1519 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
1521 dirlen = strlen(dir);
1524 strncpy(trndir,dir,dirlen);
1525 trndir[dirlen] = '\0';
1528 /* If we were handed a rooted logical name or spec, treat it like a
1529 * simple directory, so that
1530 * $ Define myroot dev:[dir.]
1531 * ... do_fileify_dirspec("myroot",buf,1) ...
1532 * does something useful.
1534 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".]")) {
1535 dir[--dirlen] = '\0';
1536 dir[dirlen-1] = ']';
1539 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
1540 /* If we've got an explicit filename, we can just shuffle the string. */
1541 if (*(cp1+1)) hasfilename = 1;
1542 /* Similarly, we can just back up a level if we've got multiple levels
1543 of explicit directories in a VMS spec which ends with directories. */
1545 for (cp2 = cp1; cp2 > dir; cp2--) {
1547 *cp2 = *cp1; *cp1 = '\0';
1551 if (*cp2 == '[' || *cp2 == '<') break;
1556 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
1557 if (dir[0] == '.') {
1558 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
1559 return do_fileify_dirspec("[]",buf,ts);
1560 else if (dir[1] == '.' &&
1561 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
1562 return do_fileify_dirspec("[-]",buf,ts);
1564 if (dirlen && dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
1565 dirlen -= 1; /* to last element */
1566 lastdir = strrchr(dir,'/');
1568 else if ((cp1 = strstr(dir,"/.")) != NULL) {
1569 /* If we have "/." or "/..", VMSify it and let the VMS code
1570 * below expand it, rather than repeating the code to handle
1571 * relative components of a filespec here */
1573 if (*(cp1+2) == '.') cp1++;
1574 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
1575 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1576 if (strchr(vmsdir,'/') != NULL) {
1577 /* If do_tovmsspec() returned it, it must have VMS syntax
1578 * delimiters in it, so it's a mixed VMS/Unix spec. We take
1579 * the time to check this here only so we avoid a recursion
1580 * loop; otherwise, gigo.
1582 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
1584 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1585 return do_tounixspec(trndir,buf,ts);
1588 } while ((cp1 = strstr(cp1,"/.")) != NULL);
1589 lastdir = strrchr(dir,'/');
1591 else if (dirlen >= 7 && !strcmp(&dir[dirlen-7],"/000000")) {
1592 /* Ditto for specs that end in an MFD -- let the VMS code
1593 * figure out whether it's a real device or a rooted logical. */
1594 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
1595 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1596 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1597 return do_tounixspec(trndir,buf,ts);
1600 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
1601 !(lastdir = cp1 = strrchr(dir,']')) &&
1602 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
1603 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
1605 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1606 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1607 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1608 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1609 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1610 (ver || *cp3)))))) {
1612 set_vaxc_errno(RMS$_DIR);
1618 /* If we lead off with a device or rooted logical, add the MFD
1619 if we're specifying a top-level directory. */
1620 if (lastdir && *dir == '/') {
1622 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
1629 retlen = dirlen + (addmfd ? 13 : 6);
1630 if (buf) retspec = buf;
1631 else if (ts) New(1309,retspec,retlen+1,char);
1632 else retspec = __fileify_retbuf;
1634 dirlen = lastdir - dir;
1635 memcpy(retspec,dir,dirlen);
1636 strcpy(&retspec[dirlen],"/000000");
1637 strcpy(&retspec[dirlen+7],lastdir);
1640 memcpy(retspec,dir,dirlen);
1641 retspec[dirlen] = '\0';
1643 /* We've picked up everything up to the directory file name.
1644 Now just add the type and version, and we're set. */
1645 strcat(retspec,".dir;1");
1648 else { /* VMS-style directory spec */
1649 char esa[NAM$C_MAXRSS+1], term, *cp;
1650 unsigned long int sts, cmplen, haslower = 0;
1651 struct FAB dirfab = cc$rms_fab;
1652 struct NAM savnam, dirnam = cc$rms_nam;
1654 dirfab.fab$b_fns = strlen(dir);
1655 dirfab.fab$l_fna = dir;
1656 dirfab.fab$l_nam = &dirnam;
1657 dirfab.fab$l_dna = ".DIR;1";
1658 dirfab.fab$b_dns = 6;
1659 dirnam.nam$b_ess = NAM$C_MAXRSS;
1660 dirnam.nam$l_esa = esa;
1662 for (cp = dir; *cp; cp++)
1663 if (islower(*cp)) { haslower = 1; break; }
1664 if (!((sts = sys$parse(&dirfab))&1)) {
1665 if (dirfab.fab$l_sts == RMS$_DIR) {
1666 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1667 sts = sys$parse(&dirfab) & 1;
1671 set_vaxc_errno(dirfab.fab$l_sts);
1677 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
1678 /* Yes; fake the fnb bits so we'll check type below */
1679 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
1681 else { /* No; just work with potential name */
1682 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
1684 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
1685 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1686 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1691 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
1692 cp1 = strchr(esa,']');
1693 if (!cp1) cp1 = strchr(esa,'>');
1694 if (cp1) { /* Should always be true */
1695 dirnam.nam$b_esl -= cp1 - esa - 1;
1696 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
1699 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1700 /* Yep; check version while we're at it, if it's there. */
1701 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1702 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1703 /* Something other than .DIR[;1]. Bzzt. */
1704 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1705 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1707 set_vaxc_errno(RMS$_DIR);
1711 esa[dirnam.nam$b_esl] = '\0';
1712 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
1713 /* They provided at least the name; we added the type, if necessary, */
1714 if (buf) retspec = buf; /* in sys$parse() */
1715 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
1716 else retspec = __fileify_retbuf;
1717 strcpy(retspec,esa);
1718 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1719 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1722 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
1723 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
1725 dirnam.nam$b_esl -= 9;
1727 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
1728 if (cp1 == NULL) { /* should never happen */
1729 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1730 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1735 retlen = strlen(esa);
1736 if ((cp1 = strrchr(esa,'.')) != NULL) {
1737 /* There's more than one directory in the path. Just roll back. */
1739 if (buf) retspec = buf;
1740 else if (ts) New(1311,retspec,retlen+7,char);
1741 else retspec = __fileify_retbuf;
1742 strcpy(retspec,esa);
1745 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
1746 /* Go back and expand rooted logical name */
1747 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
1748 if (!(sys$parse(&dirfab) & 1)) {
1749 dirnam.nam$l_rlf = NULL;
1750 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1752 set_vaxc_errno(dirfab.fab$l_sts);
1755 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
1756 if (buf) retspec = buf;
1757 else if (ts) New(1312,retspec,retlen+16,char);
1758 else retspec = __fileify_retbuf;
1759 cp1 = strstr(esa,"][");
1761 memcpy(retspec,esa,dirlen);
1762 if (!strncmp(cp1+2,"000000]",7)) {
1763 retspec[dirlen-1] = '\0';
1764 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1765 if (*cp1 == '.') *cp1 = ']';
1767 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1768 memcpy(cp1+1,"000000]",7);
1772 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
1773 retspec[retlen] = '\0';
1774 /* Convert last '.' to ']' */
1775 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1776 if (*cp1 == '.') *cp1 = ']';
1778 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1779 memcpy(cp1+1,"000000]",7);
1783 else { /* This is a top-level dir. Add the MFD to the path. */
1784 if (buf) retspec = buf;
1785 else if (ts) New(1312,retspec,retlen+16,char);
1786 else retspec = __fileify_retbuf;
1789 while (*cp1 != ':') *(cp2++) = *(cp1++);
1790 strcpy(cp2,":[000000]");
1795 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1796 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1797 /* We've set up the string up through the filename. Add the
1798 type and version, and we're done. */
1799 strcat(retspec,".DIR;1");
1801 /* $PARSE may have upcased filespec, so convert output to lower
1802 * case if input contained any lowercase characters. */
1803 if (haslower) __mystrtolower(retspec);
1806 } /* end of do_fileify_dirspec() */
1808 /* External entry points */
1809 char *fileify_dirspec(char *dir, char *buf)
1810 { return do_fileify_dirspec(dir,buf,0); }
1811 char *fileify_dirspec_ts(char *dir, char *buf)
1812 { return do_fileify_dirspec(dir,buf,1); }
1814 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
1815 static char *do_pathify_dirspec(char *dir,char *buf, int ts)
1817 static char __pathify_retbuf[NAM$C_MAXRSS+1];
1818 unsigned long int retlen;
1819 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
1821 if (!dir || !*dir) {
1822 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1825 if (*dir) strcpy(trndir,dir);
1826 else getcwd(trndir,sizeof trndir - 1);
1828 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
1829 && my_trnlnm(trndir,trndir,0)) {
1830 STRLEN trnlen = strlen(trndir);
1832 /* Trap simple rooted lnms, and return lnm:[000000] */
1833 if (!strcmp(trndir+trnlen-2,".]")) {
1834 if (buf) retpath = buf;
1835 else if (ts) New(1318,retpath,strlen(dir)+10,char);
1836 else retpath = __pathify_retbuf;
1837 strcpy(retpath,dir);
1838 strcat(retpath,":[000000]");
1844 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
1845 if (*dir == '.' && (*(dir+1) == '\0' ||
1846 (*(dir+1) == '.' && *(dir+2) == '\0')))
1847 retlen = 2 + (*(dir+1) != '\0');
1849 if ( !(cp1 = strrchr(dir,'/')) &&
1850 !(cp1 = strrchr(dir,']')) &&
1851 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
1852 if ((cp2 = strchr(cp1,'.')) != NULL &&
1853 (*(cp2-1) != '/' || /* Trailing '.', '..', */
1854 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
1855 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
1856 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
1858 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1859 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1860 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1861 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1862 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1863 (ver || *cp3)))))) {
1865 set_vaxc_errno(RMS$_DIR);
1868 retlen = cp2 - dir + 1;
1870 else { /* No file type present. Treat the filename as a directory. */
1871 retlen = strlen(dir) + 1;
1874 if (buf) retpath = buf;
1875 else if (ts) New(1313,retpath,retlen+1,char);
1876 else retpath = __pathify_retbuf;
1877 strncpy(retpath,dir,retlen-1);
1878 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
1879 retpath[retlen-1] = '/'; /* with '/', add it. */
1880 retpath[retlen] = '\0';
1882 else retpath[retlen-1] = '\0';
1884 else { /* VMS-style directory spec */
1885 char esa[NAM$C_MAXRSS+1], *cp;
1886 unsigned long int sts, cmplen, haslower;
1887 struct FAB dirfab = cc$rms_fab;
1888 struct NAM savnam, dirnam = cc$rms_nam;
1890 /* If we've got an explicit filename, we can just shuffle the string. */
1891 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
1892 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
1893 if ((cp2 = strchr(cp1,'.')) != NULL) {
1895 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1896 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1897 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1898 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1899 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1900 (ver || *cp3)))))) {
1902 set_vaxc_errno(RMS$_DIR);
1906 else { /* No file type, so just draw name into directory part */
1907 for (cp2 = cp1; *cp2; cp2++) ;
1910 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
1912 /* We've now got a VMS 'path'; fall through */
1914 dirfab.fab$b_fns = strlen(dir);
1915 dirfab.fab$l_fna = dir;
1916 if (dir[dirfab.fab$b_fns-1] == ']' ||
1917 dir[dirfab.fab$b_fns-1] == '>' ||
1918 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
1919 if (buf) retpath = buf;
1920 else if (ts) New(1314,retpath,strlen(dir)+1,char);
1921 else retpath = __pathify_retbuf;
1922 strcpy(retpath,dir);
1925 dirfab.fab$l_dna = ".DIR;1";
1926 dirfab.fab$b_dns = 6;
1927 dirfab.fab$l_nam = &dirnam;
1928 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
1929 dirnam.nam$l_esa = esa;
1931 for (cp = dir; *cp; cp++)
1932 if (islower(*cp)) { haslower = 1; break; }
1934 if (!(sts = (sys$parse(&dirfab)&1))) {
1935 if (dirfab.fab$l_sts == RMS$_DIR) {
1936 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1937 sts = sys$parse(&dirfab) & 1;
1941 set_vaxc_errno(dirfab.fab$l_sts);
1947 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
1948 if (dirfab.fab$l_sts != RMS$_FNF) {
1949 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1950 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1952 set_vaxc_errno(dirfab.fab$l_sts);
1955 dirnam = savnam; /* No; just work with potential name */
1958 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1959 /* Yep; check version while we're at it, if it's there. */
1960 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1961 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1962 /* Something other than .DIR[;1]. Bzzt. */
1963 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1964 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1966 set_vaxc_errno(RMS$_DIR);
1970 /* OK, the type was fine. Now pull any file name into the
1972 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
1974 cp1 = strrchr(esa,'>');
1975 *dirnam.nam$l_type = '>';
1978 *(dirnam.nam$l_type + 1) = '\0';
1979 retlen = dirnam.nam$l_type - esa + 2;
1980 if (buf) retpath = buf;
1981 else if (ts) New(1314,retpath,retlen,char);
1982 else retpath = __pathify_retbuf;
1983 strcpy(retpath,esa);
1984 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
1985 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
1986 /* $PARSE may have upcased filespec, so convert output to lower
1987 * case if input contained any lowercase characters. */
1988 if (haslower) __mystrtolower(retpath);
1992 } /* end of do_pathify_dirspec() */
1994 /* External entry points */
1995 char *pathify_dirspec(char *dir, char *buf)
1996 { return do_pathify_dirspec(dir,buf,0); }
1997 char *pathify_dirspec_ts(char *dir, char *buf)
1998 { return do_pathify_dirspec(dir,buf,1); }
2000 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
2001 static char *do_tounixspec(char *spec, char *buf, int ts)
2003 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
2004 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
2005 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
2007 if (spec == NULL) return NULL;
2008 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
2009 if (buf) rslt = buf;
2011 retlen = strlen(spec);
2012 cp1 = strchr(spec,'[');
2013 if (!cp1) cp1 = strchr(spec,'<');
2015 for (cp1++; *cp1; cp1++) {
2016 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
2017 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
2018 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
2021 New(1315,rslt,retlen+2+2*expand,char);
2023 else rslt = __tounixspec_retbuf;
2024 if (strchr(spec,'/') != NULL) {
2031 dirend = strrchr(spec,']');
2032 if (dirend == NULL) dirend = strrchr(spec,'>');
2033 if (dirend == NULL) dirend = strchr(spec,':');
2034 if (dirend == NULL) {
2038 if (*cp2 != '[' && *cp2 != '<') {
2041 else { /* the VMS spec begins with directories */
2043 if (*cp2 == ']' || *cp2 == '>') {
2044 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
2047 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
2048 if (getcwd(tmp,sizeof tmp,1) == NULL) {
2049 if (ts) Safefree(rslt);
2054 while (*cp3 != ':' && *cp3) cp3++;
2056 if (strchr(cp3,']') != NULL) break;
2057 } while (vmstrnenv(tmp,tmp,0,fildev,0));
2059 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
2060 retlen = devlen + dirlen;
2061 Renew(rslt,retlen+1+2*expand,char);
2067 *(cp1++) = *(cp3++);
2068 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
2072 else if ( *cp2 == '.') {
2073 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
2074 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
2080 for (; cp2 <= dirend; cp2++) {
2083 if (*(cp2+1) == '[') cp2++;
2085 else if (*cp2 == ']' || *cp2 == '>') {
2086 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
2088 else if (*cp2 == '.') {
2090 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
2091 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
2092 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
2093 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
2094 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
2096 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
2097 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
2101 else if (*cp2 == '-') {
2102 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
2103 while (*cp2 == '-') {
2105 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
2107 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
2108 if (ts) Safefree(rslt); /* filespecs like */
2109 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
2113 else *(cp1++) = *cp2;
2115 else *(cp1++) = *cp2;
2117 while (*cp2) *(cp1++) = *(cp2++);
2122 } /* end of do_tounixspec() */
2124 /* External entry points */
2125 char *tounixspec(char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
2126 char *tounixspec_ts(char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
2128 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
2129 static char *do_tovmsspec(char *path, char *buf, int ts) {
2130 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
2131 char *rslt, *dirend;
2132 register char *cp1, *cp2;
2133 unsigned long int infront = 0, hasdir = 1;
2135 if (path == NULL) return NULL;
2136 if (buf) rslt = buf;
2137 else if (ts) New(1316,rslt,strlen(path)+9,char);
2138 else rslt = __tovmsspec_retbuf;
2139 if (strpbrk(path,"]:>") ||
2140 (dirend = strrchr(path,'/')) == NULL) {
2141 if (path[0] == '.') {
2142 if (path[1] == '\0') strcpy(rslt,"[]");
2143 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
2144 else strcpy(rslt,path); /* probably garbage */
2146 else strcpy(rslt,path);
2149 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
2150 if (!*(dirend+2)) dirend +=2;
2151 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
2152 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
2157 char trndev[NAM$C_MAXRSS+1];
2161 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
2163 if (!buf & ts) Renew(rslt,18,char);
2164 strcpy(rslt,"sys$disk:[000000]");
2167 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
2169 islnm = my_trnlnm(rslt,trndev,0);
2170 trnend = islnm ? strlen(trndev) - 1 : 0;
2171 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
2172 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
2173 /* If the first element of the path is a logical name, determine
2174 * whether it has to be translated so we can add more directories. */
2175 if (!islnm || rooted) {
2178 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
2182 if (cp2 != dirend) {
2183 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
2184 strcpy(rslt,trndev);
2185 cp1 = rslt + trnend;
2198 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
2199 cp2 += 2; /* skip over "./" - it's redundant */
2200 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
2202 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
2203 *(cp1++) = '-'; /* "../" --> "-" */
2206 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
2207 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
2208 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
2209 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
2212 if (cp2 > dirend) cp2 = dirend;
2214 else *(cp1++) = '.';
2216 for (; cp2 < dirend; cp2++) {
2218 if (*(cp2-1) == '/') continue;
2219 if (*(cp1-1) != '.') *(cp1++) = '.';
2222 else if (!infront && *cp2 == '.') {
2223 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
2224 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
2225 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
2226 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
2227 else if (*(cp1-2) == '[') *(cp1-1) = '-';
2228 else { /* back up over previous directory name */
2230 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
2231 if (*(cp1-1) == '[') {
2232 memcpy(cp1,"000000.",7);
2237 if (cp2 == dirend) break;
2239 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
2240 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
2241 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
2242 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
2244 *(cp1++) = '.'; /* Simulate trailing '/' */
2245 cp2 += 2; /* for loop will incr this to == dirend */
2247 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
2249 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
2252 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
2253 if (*cp2 == '.') *(cp1++) = '_';
2254 else *(cp1++) = *cp2;
2258 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
2259 if (hasdir) *(cp1++) = ']';
2260 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
2261 while (*cp2) *(cp1++) = *(cp2++);
2266 } /* end of do_tovmsspec() */
2268 /* External entry points */
2269 char *tovmsspec(char *path, char *buf) { return do_tovmsspec(path,buf,0); }
2270 char *tovmsspec_ts(char *path, char *buf) { return do_tovmsspec(path,buf,1); }
2272 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
2273 static char *do_tovmspath(char *path, char *buf, int ts) {
2274 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
2276 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
2278 if (path == NULL) return NULL;
2279 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
2280 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
2281 if (buf) return buf;
2283 vmslen = strlen(vmsified);
2284 New(1317,cp,vmslen+1,char);
2285 memcpy(cp,vmsified,vmslen);
2290 strcpy(__tovmspath_retbuf,vmsified);
2291 return __tovmspath_retbuf;
2294 } /* end of do_tovmspath() */
2296 /* External entry points */
2297 char *tovmspath(char *path, char *buf) { return do_tovmspath(path,buf,0); }
2298 char *tovmspath_ts(char *path, char *buf) { return do_tovmspath(path,buf,1); }
2301 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
2302 static char *do_tounixpath(char *path, char *buf, int ts) {
2303 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
2305 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
2307 if (path == NULL) return NULL;
2308 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
2309 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
2310 if (buf) return buf;
2312 unixlen = strlen(unixified);
2313 New(1317,cp,unixlen+1,char);
2314 memcpy(cp,unixified,unixlen);
2319 strcpy(__tounixpath_retbuf,unixified);
2320 return __tounixpath_retbuf;
2323 } /* end of do_tounixpath() */
2325 /* External entry points */
2326 char *tounixpath(char *path, char *buf) { return do_tounixpath(path,buf,0); }
2327 char *tounixpath_ts(char *path, char *buf) { return do_tounixpath(path,buf,1); }
2330 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
2332 *****************************************************************************
2334 * Copyright (C) 1989-1994 by *
2335 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
2337 * Permission is hereby granted for the reproduction of this software, *
2338 * on condition that this copyright notice is included in the reproduction, *
2339 * and that such reproduction is not for purposes of profit or material *
2342 * 27-Aug-1994 Modified for inclusion in perl5 *
2343 * by Charles Bailey bailey@newman.upenn.edu *
2344 *****************************************************************************
2348 * getredirection() is intended to aid in porting C programs
2349 * to VMS (Vax-11 C). The native VMS environment does not support
2350 * '>' and '<' I/O redirection, or command line wild card expansion,
2351 * or a command line pipe mechanism using the '|' AND background
2352 * command execution '&'. All of these capabilities are provided to any
2353 * C program which calls this procedure as the first thing in the
2355 * The piping mechanism will probably work with almost any 'filter' type
2356 * of program. With suitable modification, it may useful for other
2357 * portability problems as well.
2359 * Author: Mark Pizzolato mark@infocomm.com
2363 struct list_item *next;
2367 static void add_item(struct list_item **head,
2368 struct list_item **tail,
2372 static void expand_wild_cards(char *item,
2373 struct list_item **head,
2374 struct list_item **tail,
2377 static int background_process(int argc, char **argv);
2379 static void pipe_and_fork(char **cmargv);
2381 /*{{{ void getredirection(int *ac, char ***av)*/
2383 getredirection(int *ac, char ***av)
2385 * Process vms redirection arg's. Exit if any error is seen.
2386 * If getredirection() processes an argument, it is erased
2387 * from the vector. getredirection() returns a new argc and argv value.
2388 * In the event that a background command is requested (by a trailing "&"),
2389 * this routine creates a background subprocess, and simply exits the program.
2391 * Warning: do not try to simplify the code for vms. The code
2392 * presupposes that getredirection() is called before any data is
2393 * read from stdin or written to stdout.
2395 * Normal usage is as follows:
2401 * getredirection(&argc, &argv);
2405 int argc = *ac; /* Argument Count */
2406 char **argv = *av; /* Argument Vector */
2407 char *ap; /* Argument pointer */
2408 int j; /* argv[] index */
2409 int item_count = 0; /* Count of Items in List */
2410 struct list_item *list_head = 0; /* First Item in List */
2411 struct list_item *list_tail; /* Last Item in List */
2412 char *in = NULL; /* Input File Name */
2413 char *out = NULL; /* Output File Name */
2414 char *outmode = "w"; /* Mode to Open Output File */
2415 char *err = NULL; /* Error File Name */
2416 char *errmode = "w"; /* Mode to Open Error File */
2417 int cmargc = 0; /* Piped Command Arg Count */
2418 char **cmargv = NULL;/* Piped Command Arg Vector */
2421 * First handle the case where the last thing on the line ends with
2422 * a '&'. This indicates the desire for the command to be run in a
2423 * subprocess, so we satisfy that desire.
2426 if (0 == strcmp("&", ap))
2427 exit(background_process(--argc, argv));
2428 if (*ap && '&' == ap[strlen(ap)-1])
2430 ap[strlen(ap)-1] = '\0';
2431 exit(background_process(argc, argv));
2434 * Now we handle the general redirection cases that involve '>', '>>',
2435 * '<', and pipes '|'.
2437 for (j = 0; j < argc; ++j)
2439 if (0 == strcmp("<", argv[j]))
2443 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
2444 exit(LIB$_WRONUMARG);
2449 if ('<' == *(ap = argv[j]))
2454 if (0 == strcmp(">", ap))
2458 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
2459 exit(LIB$_WRONUMARG);
2478 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
2479 exit(LIB$_WRONUMARG);
2483 if (('2' == *ap) && ('>' == ap[1]))
2500 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
2501 exit(LIB$_WRONUMARG);
2505 if (0 == strcmp("|", argv[j]))
2509 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
2510 exit(LIB$_WRONUMARG);
2512 cmargc = argc-(j+1);
2513 cmargv = &argv[j+1];
2517 if ('|' == *(ap = argv[j]))
2525 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
2528 * Allocate and fill in the new argument vector, Some Unix's terminate
2529 * the list with an extra null pointer.
2531 New(1302, argv, item_count+1, char *);
2533 for (j = 0; j < item_count; ++j, list_head = list_head->next)
2534 argv[j] = list_head->value;
2540 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
2541 exit(LIB$_INVARGORD);
2543 pipe_and_fork(cmargv);
2546 /* Check for input from a pipe (mailbox) */
2548 if (in == NULL && 1 == isapipe(0))
2550 char mbxname[L_tmpnam];
2552 long int dvi_item = DVI$_DEVBUFSIZ;
2553 $DESCRIPTOR(mbxnam, "");
2554 $DESCRIPTOR(mbxdevnam, "");
2556 /* Input from a pipe, reopen it in binary mode to disable */
2557 /* carriage control processing. */
2559 PerlIO_getname(stdin, mbxname);
2560 mbxnam.dsc$a_pointer = mbxname;
2561 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
2562 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
2563 mbxdevnam.dsc$a_pointer = mbxname;
2564 mbxdevnam.dsc$w_length = sizeof(mbxname);
2565 dvi_item = DVI$_DEVNAM;
2566 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
2567 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
2570 freopen(mbxname, "rb", stdin);
2573 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
2577 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
2579 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
2582 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
2584 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
2588 if (strcmp(err,"&1") == 0) {
2589 dup2(fileno(stdout), fileno(Perl_debug_log));
2592 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
2594 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
2598 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
2604 #ifdef ARGPROC_DEBUG
2605 PerlIO_printf(Perl_debug_log, "Arglist:\n");
2606 for (j = 0; j < *ac; ++j)
2607 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
2609 /* Clear errors we may have hit expanding wildcards, so they don't
2610 show up in Perl's $! later */
2611 set_errno(0); set_vaxc_errno(1);
2612 } /* end of getredirection() */
2615 static void add_item(struct list_item **head,
2616 struct list_item **tail,
2622 New(1303,*head,1,struct list_item);
2626 New(1304,(*tail)->next,1,struct list_item);
2627 *tail = (*tail)->next;
2629 (*tail)->value = value;
2633 static void expand_wild_cards(char *item,
2634 struct list_item **head,
2635 struct list_item **tail,
2639 unsigned long int context = 0;
2645 char vmsspec[NAM$C_MAXRSS+1];
2646 $DESCRIPTOR(filespec, "");
2647 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
2648 $DESCRIPTOR(resultspec, "");
2649 unsigned long int zero = 0, sts;
2651 for (cp = item; *cp; cp++) {
2652 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
2653 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
2655 if (!*cp || isspace(*cp))
2657 add_item(head, tail, item, count);
2660 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
2661 resultspec.dsc$b_class = DSC$K_CLASS_D;
2662 resultspec.dsc$a_pointer = NULL;
2663 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
2664 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
2665 if (!isunix || !filespec.dsc$a_pointer)
2666 filespec.dsc$a_pointer = item;
2667 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
2669 * Only return version specs, if the caller specified a version
2671 had_version = strchr(item, ';');
2673 * Only return device and directory specs, if the caller specifed either.
2675 had_device = strchr(item, ':');
2676 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
2678 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
2679 &defaultspec, 0, 0, &zero))))
2684 New(1305,string,resultspec.dsc$w_length+1,char);
2685 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
2686 string[resultspec.dsc$w_length] = '\0';
2687 if (NULL == had_version)
2688 *((char *)strrchr(string, ';')) = '\0';
2689 if ((!had_directory) && (had_device == NULL))
2691 if (NULL == (devdir = strrchr(string, ']')))
2692 devdir = strrchr(string, '>');
2693 strcpy(string, devdir + 1);
2696 * Be consistent with what the C RTL has already done to the rest of
2697 * the argv items and lowercase all of these names.
2699 for (c = string; *c; ++c)
2702 if (isunix) trim_unixpath(string,item,1);
2703 add_item(head, tail, string, count);
2706 if (sts != RMS$_NMF)
2708 set_vaxc_errno(sts);
2711 case RMS$_FNF: case RMS$_DNF:
2712 set_errno(ENOENT); break;
2714 set_errno(ENOTDIR); break;
2716 set_errno(ENODEV); break;
2717 case RMS$_FNM: case RMS$_SYN:
2718 set_errno(EINVAL); break;
2720 set_errno(EACCES); break;
2722 _ckvmssts_noperl(sts);
2726 add_item(head, tail, item, count);
2727 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
2728 _ckvmssts_noperl(lib$find_file_end(&context));
2731 static int child_st[2];/* Event Flag set when child process completes */
2733 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
2735 static unsigned long int exit_handler(int *status)
2739 if (0 == child_st[0])
2741 #ifdef ARGPROC_DEBUG
2742 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
2744 fflush(stdout); /* Have to flush pipe for binary data to */
2745 /* terminate properly -- <tp@mccall.com> */
2746 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
2747 sys$dassgn(child_chan);
2749 sys$synch(0, child_st);
2754 static void sig_child(int chan)
2756 #ifdef ARGPROC_DEBUG
2757 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
2759 if (child_st[0] == 0)
2763 static struct exit_control_block exit_block =
2768 &exit_block.exit_status,
2772 static void pipe_and_fork(char **cmargv)
2775 $DESCRIPTOR(cmddsc, "");
2776 static char mbxname[64];
2777 $DESCRIPTOR(mbxdsc, mbxname);
2779 unsigned long int zero = 0, one = 1;
2781 strcpy(subcmd, cmargv[0]);
2782 for (j = 1; NULL != cmargv[j]; ++j)
2784 strcat(subcmd, " \"");
2785 strcat(subcmd, cmargv[j]);
2786 strcat(subcmd, "\"");
2788 cmddsc.dsc$a_pointer = subcmd;
2789 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
2791 create_mbx(&child_chan,&mbxdsc);
2792 #ifdef ARGPROC_DEBUG
2793 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
2794 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
2796 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
2797 0, &pid, child_st, &zero, sig_child,
2799 #ifdef ARGPROC_DEBUG
2800 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
2802 sys$dclexh(&exit_block);
2803 if (NULL == freopen(mbxname, "wb", stdout))
2805 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
2809 static int background_process(int argc, char **argv)
2811 char command[2048] = "$";
2812 $DESCRIPTOR(value, "");
2813 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
2814 static $DESCRIPTOR(null, "NLA0:");
2815 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
2817 $DESCRIPTOR(pidstr, "");
2819 unsigned long int flags = 17, one = 1, retsts;
2821 strcat(command, argv[0]);
2824 strcat(command, " \"");
2825 strcat(command, *(++argv));
2826 strcat(command, "\"");
2828 value.dsc$a_pointer = command;
2829 value.dsc$w_length = strlen(value.dsc$a_pointer);
2830 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
2831 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
2832 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
2833 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
2836 _ckvmssts_noperl(retsts);
2838 #ifdef ARGPROC_DEBUG
2839 PerlIO_printf(Perl_debug_log, "%s\n", command);
2841 sprintf(pidstring, "%08X", pid);
2842 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
2843 pidstr.dsc$a_pointer = pidstring;
2844 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
2845 lib$set_symbol(&pidsymbol, &pidstr);
2849 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
2852 /* OS-specific initialization at image activation (not thread startup) */
2853 /* Older VAXC header files lack these constants */
2854 #ifndef JPI$_RIGHTS_SIZE
2855 # define JPI$_RIGHTS_SIZE 817
2857 #ifndef KGB$M_SUBSYSTEM
2858 # define KGB$M_SUBSYSTEM 0x8
2861 /*{{{void vms_image_init(int *, char ***)*/
2863 vms_image_init(int *argcp, char ***argvp)
2865 char eqv[LNM$C_NAMLENGTH+1] = "";
2866 unsigned int len, tabct = 8, tabidx = 0;
2867 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
2868 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
2869 unsigned short int dummy, rlen;
2870 struct dsc$descriptor_s **tabvec;
2872 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
2873 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
2874 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
2877 _ckvmssts(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
2879 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
2880 if (iprv[i]) { /* Running image installed with privs? */
2881 _ckvmssts(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
2886 /* Rights identifiers might trigger tainting as well. */
2887 if (!will_taint && (rlen || rsz)) {
2888 while (rlen < rsz) {
2889 /* We didn't get all the identifiers on the first pass. Allocate a
2890 * buffer much larger than $GETJPI wants (rsz is size in bytes that
2891 * were needed to hold all identifiers at time of last call; we'll
2892 * allocate that many unsigned long ints), and go back and get 'em.
2894 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
2895 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
2896 jpilist[1].buflen = rsz * sizeof(unsigned long int);
2897 _ckvmssts(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
2900 mask = jpilist[1].bufadr;
2901 /* Check attribute flags for each identifier (2nd longword); protected
2902 * subsystem identifiers trigger tainting.
2904 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
2905 if (mask[i] & KGB$M_SUBSYSTEM) {
2910 if (mask != rlst) Safefree(mask);
2912 /* We need to use this hack to tell Perl it should run with tainting,
2913 * since its tainting flag may be part of the PL_curinterp struct, which
2914 * hasn't been allocated when vms_image_init() is called.
2918 New(1320,newap,*argcp+2,char **);
2919 newap[0] = argvp[0];
2921 Copy(argvp[1],newap[2],*argcp-1,char **);
2922 /* We orphan the old argv, since we don't know where it's come from,
2923 * so we don't know how to free it.
2925 *argcp++; argvp = newap;
2927 else { /* Did user explicitly request tainting? */
2929 char *cp, **av = *argvp;
2930 for (i = 1; i < *argcp; i++) {
2931 if (*av[i] != '-') break;
2932 for (cp = av[i]+1; *cp; cp++) {
2933 if (*cp == 'T') { will_taint = 1; break; }
2934 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
2935 strchr("DFIiMmx",*cp)) break;
2937 if (will_taint) break;
2942 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
2944 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
2945 else if (tabidx >= tabct) {
2947 Renew(tabvec,tabct,struct dsc$descriptor_s *);
2949 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
2950 tabvec[tabidx]->dsc$w_length = 0;
2951 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
2952 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
2953 tabvec[tabidx]->dsc$a_pointer = NULL;
2954 _ckvmssts(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
2956 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
2958 getredirection(argcp,argvp);
2959 #if defined(USE_THREADS) && defined(__DECC)
2961 # include <reentrancy.h>
2962 (void) decc$set_reentrancy(C$C_MULTITHREAD);
2971 * Trim Unix-style prefix off filespec, so it looks like what a shell
2972 * glob expansion would return (i.e. from specified prefix on, not
2973 * full path). Note that returned filespec is Unix-style, regardless
2974 * of whether input filespec was VMS-style or Unix-style.
2976 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
2977 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
2978 * vector of options; at present, only bit 0 is used, and if set tells
2979 * trim unixpath to try the current default directory as a prefix when
2980 * presented with a possibly ambiguous ... wildcard.
2982 * Returns !=0 on success, with trimmed filespec replacing contents of
2983 * fspec, and 0 on failure, with contents of fpsec unchanged.
2985 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
2987 trim_unixpath(char *fspec, char *wildspec, int opts)
2989 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
2990 *template, *base, *end, *cp1, *cp2;
2991 register int tmplen, reslen = 0, dirs = 0;
2993 if (!wildspec || !fspec) return 0;
2994 if (strpbrk(wildspec,"]>:") != NULL) {
2995 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
2996 else template = unixwild;
2998 else template = wildspec;
2999 if (strpbrk(fspec,"]>:") != NULL) {
3000 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
3001 else base = unixified;
3002 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
3003 * check to see that final result fits into (isn't longer than) fspec */
3004 reslen = strlen(fspec);
3008 /* No prefix or absolute path on wildcard, so nothing to remove */
3009 if (!*template || *template == '/') {
3010 if (base == fspec) return 1;
3011 tmplen = strlen(unixified);
3012 if (tmplen > reslen) return 0; /* not enough space */
3013 /* Copy unixified resultant, including trailing NUL */
3014 memmove(fspec,unixified,tmplen+1);
3018 for (end = base; *end; end++) ; /* Find end of resultant filespec */
3019 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
3020 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
3021 for (cp1 = end ;cp1 >= base; cp1--)
3022 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
3024 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
3028 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
3029 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
3030 int ells = 1, totells, segdirs, match;
3031 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
3032 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3034 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
3036 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
3037 if (ellipsis == template && opts & 1) {
3038 /* Template begins with an ellipsis. Since we can't tell how many
3039 * directory names at the front of the resultant to keep for an
3040 * arbitrary starting point, we arbitrarily choose the current
3041 * default directory as a starting point. If it's there as a prefix,
3042 * clip it off. If not, fall through and act as if the leading
3043 * ellipsis weren't there (i.e. return shortest possible path that
3044 * could match template).
3046 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
3047 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
3048 if (_tolower(*cp1) != _tolower(*cp2)) break;
3049 segdirs = dirs - totells; /* Min # of dirs we must have left */
3050 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
3051 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
3052 memcpy(fspec,cp2+1,end - cp2);
3056 /* First off, back up over constant elements at end of path */
3058 for (front = end ; front >= base; front--)
3059 if (*front == '/' && !dirs--) { front++; break; }
3061 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
3062 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
3063 if (cp1 != '\0') return 0; /* Path too long. */
3065 *cp2 = '\0'; /* Pick up with memcpy later */
3066 lcfront = lcres + (front - base);
3067 /* Now skip over each ellipsis and try to match the path in front of it. */
3069 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
3070 if (*(cp1) == '.' && *(cp1+1) == '.' &&
3071 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
3072 if (cp1 < template) break; /* template started with an ellipsis */
3073 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
3074 ellipsis = cp1; continue;
3076 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
3078 for (segdirs = 0, cp2 = tpl;
3079 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
3081 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
3082 else *cp2 = _tolower(*cp1); /* else lowercase for match */
3083 if (*cp2 == '/') segdirs++;
3085 if (cp1 != ellipsis - 1) return 0; /* Path too long */
3086 /* Back up at least as many dirs as in template before matching */
3087 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
3088 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
3089 for (match = 0; cp1 > lcres;) {
3090 resdsc.dsc$a_pointer = cp1;
3091 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
3093 if (match == 1) lcfront = cp1;
3095 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
3097 if (!match) return 0; /* Can't find prefix ??? */
3098 if (match > 1 && opts & 1) {
3099 /* This ... wildcard could cover more than one set of dirs (i.e.
3100 * a set of similar dir names is repeated). If the template
3101 * contains more than 1 ..., upstream elements could resolve the
3102 * ambiguity, but it's not worth a full backtracking setup here.
3103 * As a quick heuristic, clip off the current default directory
3104 * if it's present to find the trimmed spec, else use the
3105 * shortest string that this ... could cover.
3107 char def[NAM$C_MAXRSS+1], *st;
3109 if (getcwd(def, sizeof def,0) == NULL) return 0;
3110 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
3111 if (_tolower(*cp1) != _tolower(*cp2)) break;
3112 segdirs = dirs - totells; /* Min # of dirs we must have left */
3113 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
3114 if (*cp1 == '\0' && *cp2 == '/') {
3115 memcpy(fspec,cp2+1,end - cp2);
3118 /* Nope -- stick with lcfront from above and keep going. */
3121 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
3126 } /* end of trim_unixpath() */
3131 * VMS readdir() routines.
3132 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
3134 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
3135 * Minor modifications to original routines.
3138 /* Number of elements in vms_versions array */
3139 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
3142 * Open a directory, return a handle for later use.
3144 /*{{{ DIR *opendir(char*name) */
3149 char dir[NAM$C_MAXRSS+1];
3152 if (do_tovmspath(name,dir,0) == NULL) {
3155 if (flex_stat(dir,&sb) == -1) return NULL;
3156 if (!S_ISDIR(sb.st_mode)) {
3157 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
3160 if (!cando_by_name(S_IRUSR,0,dir)) {
3161 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
3164 /* Get memory for the handle, and the pattern. */
3166 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
3168 /* Fill in the fields; mainly playing with the descriptor. */
3169 (void)sprintf(dd->pattern, "%s*.*",dir);
3172 dd->vms_wantversions = 0;
3173 dd->pat.dsc$a_pointer = dd->pattern;
3174 dd->pat.dsc$w_length = strlen(dd->pattern);
3175 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
3176 dd->pat.dsc$b_class = DSC$K_CLASS_S;
3179 } /* end of opendir() */
3183 * Set the flag to indicate we want versions or not.
3185 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
3187 vmsreaddirversions(DIR *dd, int flag)
3189 dd->vms_wantversions = flag;
3194 * Free up an opened directory.
3196 /*{{{ void closedir(DIR *dd)*/
3200 (void)lib$find_file_end(&dd->context);
3201 Safefree(dd->pattern);
3202 Safefree((char *)dd);
3207 * Collect all the version numbers for the current file.
3213 struct dsc$descriptor_s pat;
3214 struct dsc$descriptor_s res;
3216 char *p, *text, buff[sizeof dd->entry.d_name];
3218 unsigned long context, tmpsts;
3221 /* Convenient shorthand. */
3224 /* Add the version wildcard, ignoring the "*.*" put on before */
3225 i = strlen(dd->pattern);
3226 New(1308,text,i + e->d_namlen + 3,char);
3227 (void)strcpy(text, dd->pattern);
3228 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
3230 /* Set up the pattern descriptor. */
3231 pat.dsc$a_pointer = text;
3232 pat.dsc$w_length = i + e->d_namlen - 1;
3233 pat.dsc$b_dtype = DSC$K_DTYPE_T;
3234 pat.dsc$b_class = DSC$K_CLASS_S;
3236 /* Set up result descriptor. */
3237 res.dsc$a_pointer = buff;
3238 res.dsc$w_length = sizeof buff - 2;
3239 res.dsc$b_dtype = DSC$K_DTYPE_T;
3240 res.dsc$b_class = DSC$K_CLASS_S;
3242 /* Read files, collecting versions. */
3243 for (context = 0, e->vms_verscount = 0;
3244 e->vms_verscount < VERSIZE(e);
3245 e->vms_verscount++) {
3246 tmpsts = lib$find_file(&pat, &res, &context);
3247 if (tmpsts == RMS$_NMF || context == 0) break;
3249 buff[sizeof buff - 1] = '\0';
3250 if ((p = strchr(buff, ';')))
3251 e->vms_versions[e->vms_verscount] = atoi(p + 1);
3253 e->vms_versions[e->vms_verscount] = -1;
3256 _ckvmssts(lib$find_file_end(&context));
3259 } /* end of collectversions() */
3262 * Read the next entry from the directory.
3264 /*{{{ struct dirent *readdir(DIR *dd)*/
3268 struct dsc$descriptor_s res;
3269 char *p, buff[sizeof dd->entry.d_name];
3270 unsigned long int tmpsts;
3272 /* Set up result descriptor, and get next file. */
3273 res.dsc$a_pointer = buff;
3274 res.dsc$w_length = sizeof buff - 2;
3275 res.dsc$b_dtype = DSC$K_DTYPE_T;
3276 res.dsc$b_class = DSC$K_CLASS_S;
3277 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
3278 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
3279 if (!(tmpsts & 1)) {
3280 set_vaxc_errno(tmpsts);
3283 set_errno(EACCES); break;
3285 set_errno(ENODEV); break;
3287 set_errno(ENOTDIR); break;
3288 case RMS$_FNF: case RMS$_DNF:
3289 set_errno(ENOENT); break;
3296 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
3297 buff[sizeof buff - 1] = '\0';
3298 for (p = buff; *p; p++) *p = _tolower(*p);
3299 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
3302 /* Skip any directory component and just copy the name. */
3303 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
3304 else (void)strcpy(dd->entry.d_name, buff);
3306 /* Clobber the version. */
3307 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
3309 dd->entry.d_namlen = strlen(dd->entry.d_name);
3310 dd->entry.vms_verscount = 0;
3311 if (dd->vms_wantversions) collectversions(dd);
3314 } /* end of readdir() */
3318 * Return something that can be used in a seekdir later.
3320 /*{{{ long telldir(DIR *dd)*/
3329 * Return to a spot where we used to be. Brute force.
3331 /*{{{ void seekdir(DIR *dd,long count)*/
3333 seekdir(DIR *dd, long count)
3335 int vms_wantversions;
3338 /* If we haven't done anything yet... */
3342 /* Remember some state, and clear it. */
3343 vms_wantversions = dd->vms_wantversions;
3344 dd->vms_wantversions = 0;
3345 _ckvmssts(lib$find_file_end(&dd->context));
3348 /* The increment is in readdir(). */
3349 for (dd->count = 0; dd->count < count; )
3352 dd->vms_wantversions = vms_wantversions;
3354 } /* end of seekdir() */
3357 /* VMS subprocess management
3359 * my_vfork() - just a vfork(), after setting a flag to record that
3360 * the current script is trying a Unix-style fork/exec.
3362 * vms_do_aexec() and vms_do_exec() are called in response to the
3363 * perl 'exec' function. If this follows a vfork call, then they
3364 * call out the the regular perl routines in doio.c which do an
3365 * execvp (for those who really want to try this under VMS).
3366 * Otherwise, they do exactly what the perl docs say exec should
3367 * do - terminate the current script and invoke a new command
3368 * (See below for notes on command syntax.)
3370 * do_aspawn() and do_spawn() implement the VMS side of the perl
3371 * 'system' function.
3373 * Note on command arguments to perl 'exec' and 'system': When handled
3374 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
3375 * are concatenated to form a DCL command string. If the first arg
3376 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
3377 * the the command string is handed off to DCL directly. Otherwise,
3378 * the first token of the command is taken as the filespec of an image
3379 * to run. The filespec is expanded using a default type of '.EXE' and
3380 * the process defaults for device, directory, etc., and if found, the resultant
3381 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
3382 * the command string as parameters. This is perhaps a bit complicated,
3383 * but I hope it will form a happy medium between what VMS folks expect
3384 * from lib$spawn and what Unix folks expect from exec.
3387 static int vfork_called;
3389 /*{{{int my_vfork()*/
3402 if (PL_Cmd != VMScmd.dsc$a_pointer) Safefree(PL_Cmd);
3405 if (VMScmd.dsc$a_pointer) {
3406 Safefree(VMScmd.dsc$a_pointer);
3407 VMScmd.dsc$w_length = 0;
3408 VMScmd.dsc$a_pointer = Nullch;
3413 setup_argstr(SV *really, SV **mark, SV **sp)
3416 char *junk, *tmps = Nullch;
3417 register size_t cmdlen = 0;
3424 tmps = SvPV(really,rlen);
3431 for (idx++; idx <= sp; idx++) {
3433 junk = SvPVx(*idx,rlen);
3434 cmdlen += rlen ? rlen + 1 : 0;
3437 New(401,PL_Cmd,cmdlen+1,char);
3439 if (tmps && *tmps) {
3440 strcpy(PL_Cmd,tmps);
3443 else *PL_Cmd = '\0';
3444 while (++mark <= sp) {
3446 char *s = SvPVx(*mark,n_a);
3448 if (*PL_Cmd) strcat(PL_Cmd," ");
3454 } /* end of setup_argstr() */
3457 static unsigned long int
3458 setup_cmddsc(char *cmd, int check_img)
3460 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
3461 $DESCRIPTOR(defdsc,".EXE");
3462 $DESCRIPTOR(defdsc2,".");
3463 $DESCRIPTOR(resdsc,resspec);
3464 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3465 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
3466 register char *s, *rest, *cp, *wordbreak;
3471 (sizeof(vmsspec) > sizeof(resspec) ? sizeof(resspec) : sizeof(vmsspec)))
3474 while (*s && isspace(*s)) s++;
3476 if (*s == '@' || *s == '$') {
3477 vmsspec[0] = *s; rest = s + 1;
3478 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
3480 else { cp = vmsspec; rest = s; }
3481 if (*rest == '.' || *rest == '/') {
3484 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
3485 rest++, cp2++) *cp2 = *rest;
3487 if (do_tovmsspec(resspec,cp,0)) {
3490 for (cp2 = vmsspec + strlen(vmsspec);
3491 *rest && cp2 - vmsspec < sizeof vmsspec;
3492 rest++, cp2++) *cp2 = *rest;
3497 /* Intuit whether verb (first word of cmd) is a DCL command:
3498 * - if first nonspace char is '@', it's a DCL indirection
3500 * - if verb contains a filespec separator, it's not a DCL command
3501 * - if it doesn't, caller tells us whether to default to a DCL
3502 * command, or to a local image unless told it's DCL (by leading '$')
3504 if (*s == '@') isdcl = 1;
3506 register char *filespec = strpbrk(s,":<[.;");
3507 rest = wordbreak = strpbrk(s," \"\t/");
3508 if (!wordbreak) wordbreak = s + strlen(s);
3509 if (*s == '$') check_img = 0;
3510 if (filespec && (filespec < wordbreak)) isdcl = 0;
3511 else isdcl = !check_img;
3515 imgdsc.dsc$a_pointer = s;
3516 imgdsc.dsc$w_length = wordbreak - s;
3517 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
3519 _ckvmssts(lib$find_file_end(&cxt));
3520 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
3521 if (!(retsts & 1) && *s == '$') {
3522 _ckvmssts(lib$find_file_end(&cxt));
3523 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
3524 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
3526 _ckvmssts(lib$find_file_end(&cxt));
3527 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
3531 _ckvmssts(lib$find_file_end(&cxt));
3536 while (*s && !isspace(*s)) s++;
3539 /* check that it's really not DCL with no file extension */
3540 fp = fopen(resspec,"r","ctx=bin,shr=get");
3542 char b[4] = {0,0,0,0};
3543 read(fileno(fp),b,4);
3544 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
3547 if (check_img && isdcl) return RMS$_FNF;
3549 if (cando_by_name(S_IXUSR,0,resspec)) {
3550 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
3552 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
3554 strcpy(VMScmd.dsc$a_pointer,"@");
3556 strcat(VMScmd.dsc$a_pointer,resspec);
3557 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
3558 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
3561 else retsts = RMS$_PRV;
3564 /* It's either a DCL command or we couldn't find a suitable image */
3565 VMScmd.dsc$w_length = strlen(cmd);
3566 if (cmd == PL_Cmd) VMScmd.dsc$a_pointer = PL_Cmd;
3567 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
3568 if (!(retsts & 1)) {
3569 /* just hand off status values likely to be due to user error */
3570 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
3571 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
3572 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
3573 else { _ckvmssts(retsts); }
3576 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : retsts);
3578 } /* end of setup_cmddsc() */
3581 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
3583 vms_do_aexec(SV *really,SV **mark,SV **sp)
3587 if (vfork_called) { /* this follows a vfork - act Unixish */
3589 if (vfork_called < 0) {
3590 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
3593 else return do_aexec(really,mark,sp);
3595 /* no vfork - act VMSish */
3596 return vms_do_exec(setup_argstr(really,mark,sp));
3601 } /* end of vms_do_aexec() */
3604 /* {{{bool vms_do_exec(char *cmd) */
3606 vms_do_exec(char *cmd)
3610 if (vfork_called) { /* this follows a vfork - act Unixish */
3612 if (vfork_called < 0) {
3613 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
3616 else return do_exec(cmd);
3619 { /* no vfork - act VMSish */
3620 unsigned long int retsts;
3623 TAINT_PROPER("exec");
3624 if ((retsts = setup_cmddsc(cmd,1)) & 1)
3625 retsts = lib$do_command(&VMScmd);
3628 case RMS$_FNF: case RMS$_DNF:
3629 set_errno(ENOENT); break;
3631 set_errno(ENOTDIR); break;
3633 set_errno(ENODEV); break;
3635 set_errno(EACCES); break;
3637 set_errno(EINVAL); break;
3639 set_errno(E2BIG); break;
3640 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3641 _ckvmssts(retsts); /* fall through */
3642 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3645 set_vaxc_errno(retsts);
3646 if (ckWARN(WARN_EXEC)) {
3647 Perl_warner(aTHX_ WARN_EXEC,"Can't exec \"%*s\": %s",
3648 VMScmd.dsc$w_length, VMScmd.dsc$a_pointer, Strerror(errno));
3655 } /* end of vms_do_exec() */
3658 unsigned long int do_spawn(char *);
3660 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
3662 do_aspawn(void *really,void **mark,void **sp)
3665 if (sp > mark) return do_spawn(setup_argstr((SV *)really,(SV **)mark,(SV **)sp));
3668 } /* end of do_aspawn() */
3671 /* {{{unsigned long int do_spawn(char *cmd) */
3675 unsigned long int sts, substs, hadcmd = 1;
3679 TAINT_PROPER("spawn");
3680 if (!cmd || !*cmd) {
3682 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
3684 else if ((sts = setup_cmddsc(cmd,0)) & 1) {
3685 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
3690 case RMS$_FNF: case RMS$_DNF:
3691 set_errno(ENOENT); break;
3693 set_errno(ENOTDIR); break;
3695 set_errno(ENODEV); break;
3697 set_errno(EACCES); break;
3699 set_errno(EINVAL); break;
3701 set_errno(E2BIG); break;
3702 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3703 _ckvmssts(sts); /* fall through */
3704 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3707 set_vaxc_errno(sts);
3708 if (ckWARN(WARN_EXEC)) {
3709 Perl_warner(aTHX_ WARN_EXEC,"Can't spawn \"%*s\": %s",
3710 hadcmd ? VMScmd.dsc$w_length : 0,
3711 hadcmd ? VMScmd.dsc$a_pointer : "",
3718 } /* end of do_spawn() */
3722 * A simple fwrite replacement which outputs itmsz*nitm chars without
3723 * introducing record boundaries every itmsz chars.
3725 /*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
3727 my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
3729 register char *cp, *end;
3731 end = (char *)src + itmsz * nitm;
3733 while ((char *)src <= end) {
3734 for (cp = src; cp <= end; cp++) if (!*cp) break;
3735 if (fputs(src,dest) == EOF) return EOF;
3737 if (fputc('\0',dest) == EOF) return EOF;
3743 } /* end of my_fwrite() */
3746 /*{{{ int my_flush(FILE *fp)*/
3751 if ((res = fflush(fp)) == 0 && fp) {
3752 #ifdef VMS_DO_SOCKETS
3754 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
3756 res = fsync(fileno(fp));
3763 * Here are replacements for the following Unix routines in the VMS environment:
3764 * getpwuid Get information for a particular UIC or UID
3765 * getpwnam Get information for a named user
3766 * getpwent Get information for each user in the rights database
3767 * setpwent Reset search to the start of the rights database
3768 * endpwent Finish searching for users in the rights database
3770 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
3771 * (defined in pwd.h), which contains the following fields:-
3773 * char *pw_name; Username (in lower case)
3774 * char *pw_passwd; Hashed password
3775 * unsigned int pw_uid; UIC
3776 * unsigned int pw_gid; UIC group number
3777 * char *pw_unixdir; Default device/directory (VMS-style)
3778 * char *pw_gecos; Owner name
3779 * char *pw_dir; Default device/directory (Unix-style)
3780 * char *pw_shell; Default CLI name (eg. DCL)
3782 * If the specified user does not exist, getpwuid and getpwnam return NULL.
3784 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
3785 * not the UIC member number (eg. what's returned by getuid()),
3786 * getpwuid() can accept either as input (if uid is specified, the caller's
3787 * UIC group is used), though it won't recognise gid=0.
3789 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
3790 * information about other users in your group or in other groups, respectively.
3791 * If the required privilege is not available, then these routines fill only
3792 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
3795 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
3798 /* sizes of various UAF record fields */
3799 #define UAI$S_USERNAME 12
3800 #define UAI$S_IDENT 31
3801 #define UAI$S_OWNER 31
3802 #define UAI$S_DEFDEV 31
3803 #define UAI$S_DEFDIR 63
3804 #define UAI$S_DEFCLI 31
3807 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
3808 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
3809 (uic).uic$v_group != UIC$K_WILD_GROUP)
3811 static char __empty[]= "";
3812 static struct passwd __passwd_empty=
3813 {(char *) __empty, (char *) __empty, 0, 0,
3814 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
3815 static int contxt= 0;
3816 static struct passwd __pwdcache;
3817 static char __pw_namecache[UAI$S_IDENT+1];
3820 * This routine does most of the work extracting the user information.
3822 static int fillpasswd (const char *name, struct passwd *pwd)
3826 unsigned char length;
3827 char pw_gecos[UAI$S_OWNER+1];
3829 static union uicdef uic;
3831 unsigned char length;
3832 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
3835 unsigned char length;
3836 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
3839 unsigned char length;
3840 char pw_shell[UAI$S_DEFCLI+1];
3842 static char pw_passwd[UAI$S_PWD+1];
3844 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
3845 struct dsc$descriptor_s name_desc;
3846 unsigned long int sts;
3848 static struct itmlst_3 itmlst[]= {
3849 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
3850 {sizeof(uic), UAI$_UIC, &uic, &luic},
3851 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
3852 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
3853 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
3854 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
3855 {0, 0, NULL, NULL}};
3857 name_desc.dsc$w_length= strlen(name);
3858 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3859 name_desc.dsc$b_class= DSC$K_CLASS_S;
3860 name_desc.dsc$a_pointer= (char *) name;
3862 /* Note that sys$getuai returns many fields as counted strings. */
3863 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
3864 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
3865 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
3867 else { _ckvmssts(sts); }
3868 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
3870 if ((int) owner.length < lowner) lowner= (int) owner.length;
3871 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
3872 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
3873 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
3874 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
3875 owner.pw_gecos[lowner]= '\0';
3876 defdev.pw_dir[ldefdev+ldefdir]= '\0';
3877 defcli.pw_shell[ldefcli]= '\0';
3878 if (valid_uic(uic)) {
3879 pwd->pw_uid= uic.uic$l_uic;
3880 pwd->pw_gid= uic.uic$v_group;
3883 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
3884 pwd->pw_passwd= pw_passwd;
3885 pwd->pw_gecos= owner.pw_gecos;
3886 pwd->pw_dir= defdev.pw_dir;
3887 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
3888 pwd->pw_shell= defcli.pw_shell;
3889 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
3891 ldir= strlen(pwd->pw_unixdir) - 1;
3892 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
3895 strcpy(pwd->pw_unixdir, pwd->pw_dir);
3896 __mystrtolower(pwd->pw_unixdir);
3901 * Get information for a named user.
3903 /*{{{struct passwd *getpwnam(char *name)*/
3904 struct passwd *my_getpwnam(char *name)
3906 struct dsc$descriptor_s name_desc;
3908 unsigned long int status, sts;
3911 __pwdcache = __passwd_empty;
3912 if (!fillpasswd(name, &__pwdcache)) {
3913 /* We still may be able to determine pw_uid and pw_gid */
3914 name_desc.dsc$w_length= strlen(name);
3915 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3916 name_desc.dsc$b_class= DSC$K_CLASS_S;
3917 name_desc.dsc$a_pointer= (char *) name;
3918 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
3919 __pwdcache.pw_uid= uic.uic$l_uic;
3920 __pwdcache.pw_gid= uic.uic$v_group;
3923 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
3924 set_vaxc_errno(sts);
3925 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
3928 else { _ckvmssts(sts); }
3931 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
3932 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
3933 __pwdcache.pw_name= __pw_namecache;
3935 } /* end of my_getpwnam() */
3939 * Get information for a particular UIC or UID.
3940 * Called by my_getpwent with uid=-1 to list all users.
3942 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
3943 struct passwd *my_getpwuid(Uid_t uid)
3945 const $DESCRIPTOR(name_desc,__pw_namecache);
3946 unsigned short lname;
3948 unsigned long int status;
3951 if (uid == (unsigned int) -1) {
3953 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
3954 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
3955 set_vaxc_errno(status);
3956 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3960 else { _ckvmssts(status); }
3961 } while (!valid_uic (uic));
3965 if (!uic.uic$v_group)
3966 uic.uic$v_group= PerlProc_getgid();
3968 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
3969 else status = SS$_IVIDENT;
3970 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
3971 status == RMS$_PRV) {
3972 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3975 else { _ckvmssts(status); }
3977 __pw_namecache[lname]= '\0';
3978 __mystrtolower(__pw_namecache);
3980 __pwdcache = __passwd_empty;
3981 __pwdcache.pw_name = __pw_namecache;
3983 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
3984 The identifier's value is usually the UIC, but it doesn't have to be,
3985 so if we can, we let fillpasswd update this. */
3986 __pwdcache.pw_uid = uic.uic$l_uic;
3987 __pwdcache.pw_gid = uic.uic$v_group;
3989 fillpasswd(__pw_namecache, &__pwdcache);
3992 } /* end of my_getpwuid() */
3996 * Get information for next user.
3998 /*{{{struct passwd *my_getpwent()*/
3999 struct passwd *my_getpwent()
4001 return (my_getpwuid((unsigned int) -1));
4006 * Finish searching rights database for users.
4008 /*{{{void my_endpwent()*/
4013 _ckvmssts(sys$finish_rdb(&contxt));
4019 #ifdef HOMEGROWN_POSIX_SIGNALS
4020 /* Signal handling routines, pulled into the core from POSIX.xs.
4022 * We need these for threads, so they've been rolled into the core,
4023 * rather than left in POSIX.xs.
4025 * (DRS, Oct 23, 1997)
4028 /* sigset_t is atomic under VMS, so these routines are easy */
4029 /*{{{int my_sigemptyset(sigset_t *) */
4030 int my_sigemptyset(sigset_t *set) {
4031 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
4037 /*{{{int my_sigfillset(sigset_t *)*/
4038 int my_sigfillset(sigset_t *set) {
4040 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
4041 for (i = 0; i < NSIG; i++) *set |= (1 << i);
4047 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
4048 int my_sigaddset(sigset_t *set, int sig) {
4049 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
4050 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
4051 *set |= (1 << (sig - 1));
4057 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
4058 int my_sigdelset(sigset_t *set, int sig) {
4059 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
4060 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
4061 *set &= ~(1 << (sig - 1));
4067 /*{{{int my_sigismember(sigset_t *set, int sig)*/
4068 int my_sigismember(sigset_t *set, int sig) {
4069 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
4070 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
4071 *set & (1 << (sig - 1));
4076 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
4077 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
4080 /* If set and oset are both null, then things are badly wrong. Bail out. */
4081 if ((oset == NULL) && (set == NULL)) {
4082 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
4086 /* If set's null, then we're just handling a fetch. */
4088 tempmask = sigblock(0);
4093 tempmask = sigsetmask(*set);
4096 tempmask = sigblock(*set);
4099 tempmask = sigblock(0);
4100 sigsetmask(*oset & ~tempmask);
4103 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4108 /* Did they pass us an oset? If so, stick our holding mask into it */
4115 #endif /* HOMEGROWN_POSIX_SIGNALS */
4118 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
4119 * my_utime(), and flex_stat(), all of which operate on UTC unless
4120 * VMSISH_TIMES is true.
4122 /* method used to handle UTC conversions:
4123 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
4125 static int gmtime_emulation_type;
4126 /* number of secs to add to UTC POSIX-style time to get local time */
4127 static long int utc_offset_secs;
4129 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
4130 * in vmsish.h. #undef them here so we can call the CRTL routines
4137 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
4138 # define RTL_USES_UTC 1
4142 * DEC C previous to 6.0 corrupts the behavior of the /prefix
4143 * qualifier with the extern prefix pragma. This provisional
4144 * hack circumvents this prefix pragma problem in previous
4147 #if defined(__VMS_VER) && __VMS_VER >= 70000000
4148 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
4149 # pragma __extern_prefix save
4150 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
4151 # define gmtime decc$__utctz_gmtime
4152 # define localtime decc$__utctz_localtime
4153 # define time decc$__utc_time
4154 # pragma __extern_prefix restore
4156 struct tm *gmtime(), *localtime();
4162 static time_t toutc_dst(time_t loc) {
4165 if ((rsltmp = localtime(&loc)) == NULL) return -1;
4166 loc -= utc_offset_secs;
4167 if (rsltmp->tm_isdst) loc -= 3600;
4170 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
4171 ((gmtime_emulation_type || my_time(NULL)), \
4172 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
4173 ((secs) - utc_offset_secs))))
4175 static time_t toloc_dst(time_t utc) {
4178 utc += utc_offset_secs;
4179 if ((rsltmp = localtime(&utc)) == NULL) return -1;
4180 if (rsltmp->tm_isdst) utc += 3600;
4183 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
4184 ((gmtime_emulation_type || my_time(NULL)), \
4185 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
4186 ((secs) + utc_offset_secs))))
4189 /* my_time(), my_localtime(), my_gmtime()
4190 * By default traffic in UTC time values, using CRTL gmtime() or
4191 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
4192 * Note: We need to use these functions even when the CRTL has working
4193 * UTC support, since they also handle C<use vmsish qw(times);>
4195 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
4196 * Modified by Charles Bailey <bailey@newman.upenn.edu>
4199 /*{{{time_t my_time(time_t *timep)*/
4200 time_t my_time(time_t *timep)
4206 if (gmtime_emulation_type == 0) {
4208 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
4209 /* results of calls to gmtime() and localtime() */
4210 /* for same &base */
4212 gmtime_emulation_type++;
4213 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
4214 char off[LNM$C_NAMLENGTH+1];;
4216 gmtime_emulation_type++;
4217 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
4218 gmtime_emulation_type++;
4219 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
4221 else { utc_offset_secs = atol(off); }
4223 else { /* We've got a working gmtime() */
4224 struct tm gmt, local;
4227 tm_p = localtime(&base);
4229 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
4230 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
4231 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
4232 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
4238 # ifdef RTL_USES_UTC
4239 if (VMSISH_TIME) when = _toloc(when);
4241 if (!VMSISH_TIME) when = _toutc(when);
4244 if (timep != NULL) *timep = when;
4247 } /* end of my_time() */
4251 /*{{{struct tm *my_gmtime(const time_t *timep)*/
4253 my_gmtime(const time_t *timep)
4260 if (timep == NULL) {
4261 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4264 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
4268 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
4270 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
4271 return gmtime(&when);
4273 /* CRTL localtime() wants local time as input, so does no tz correction */
4274 rsltmp = localtime(&when);
4275 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
4278 } /* end of my_gmtime() */
4282 /*{{{struct tm *my_localtime(const time_t *timep)*/
4284 my_localtime(const time_t *timep)
4290 if (timep == NULL) {
4291 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4294 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
4295 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
4298 # ifdef RTL_USES_UTC
4300 if (VMSISH_TIME) when = _toutc(when);
4302 /* CRTL localtime() wants UTC as input, does tz correction itself */
4303 return localtime(&when);
4306 if (!VMSISH_TIME) when = _toloc(when); /* Input was UTC */
4309 /* CRTL localtime() wants local time as input, so does no tz correction */
4310 rsltmp = localtime(&when);
4311 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = -1;
4314 } /* end of my_localtime() */
4317 /* Reset definitions for later calls */
4318 #define gmtime(t) my_gmtime(t)
4319 #define localtime(t) my_localtime(t)
4320 #define time(t) my_time(t)
4323 /* my_utime - update modification time of a file
4324 * calling sequence is identical to POSIX utime(), but under
4325 * VMS only the modification time is changed; ODS-2 does not
4326 * maintain access times. Restrictions differ from the POSIX
4327 * definition in that the time can be changed as long as the
4328 * caller has permission to execute the necessary IO$_MODIFY $QIO;
4329 * no separate checks are made to insure that the caller is the
4330 * owner of the file or has special privs enabled.
4331 * Code here is based on Joe Meadows' FILE utility.
4334 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
4335 * to VMS epoch (01-JAN-1858 00:00:00.00)
4336 * in 100 ns intervals.
4338 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
4340 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
4341 int my_utime(char *file, struct utimbuf *utimes)
4345 long int bintime[2], len = 2, lowbit, unixtime,
4346 secscale = 10000000; /* seconds --> 100 ns intervals */
4347 unsigned long int chan, iosb[2], retsts;
4348 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
4349 struct FAB myfab = cc$rms_fab;
4350 struct NAM mynam = cc$rms_nam;
4351 #if defined (__DECC) && defined (__VAX)
4352 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
4353 * at least through VMS V6.1, which causes a type-conversion warning.
4355 # pragma message save
4356 # pragma message disable cvtdiftypes
4358 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
4359 struct fibdef myfib;
4360 #if defined (__DECC) && defined (__VAX)
4361 /* This should be right after the declaration of myatr, but due
4362 * to a bug in VAX DEC C, this takes effect a statement early.
4364 # pragma message restore
4366 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
4367 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
4368 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
4370 if (file == NULL || *file == '\0') {
4372 set_vaxc_errno(LIB$_INVARG);
4375 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
4377 if (utimes != NULL) {
4378 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
4379 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
4380 * Since time_t is unsigned long int, and lib$emul takes a signed long int
4381 * as input, we force the sign bit to be clear by shifting unixtime right
4382 * one bit, then multiplying by an extra factor of 2 in lib$emul().
4384 lowbit = (utimes->modtime & 1) ? secscale : 0;
4385 unixtime = (long int) utimes->modtime;
4387 /* If input was UTC; convert to local for sys svc */
4388 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
4390 unixtime >>= 1; secscale <<= 1;
4391 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
4392 if (!(retsts & 1)) {
4394 set_vaxc_errno(retsts);
4397 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
4398 if (!(retsts & 1)) {
4400 set_vaxc_errno(retsts);
4405 /* Just get the current time in VMS format directly */
4406 retsts = sys$gettim(bintime);
4407 if (!(retsts & 1)) {
4409 set_vaxc_errno(retsts);
4414 myfab.fab$l_fna = vmsspec;
4415 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
4416 myfab.fab$l_nam = &mynam;
4417 mynam.nam$l_esa = esa;
4418 mynam.nam$b_ess = (unsigned char) sizeof esa;
4419 mynam.nam$l_rsa = rsa;
4420 mynam.nam$b_rss = (unsigned char) sizeof rsa;
4422 /* Look for the file to be affected, letting RMS parse the file
4423 * specification for us as well. I have set errno using only
4424 * values documented in the utime() man page for VMS POSIX.
4426 retsts = sys$parse(&myfab,0,0);
4427 if (!(retsts & 1)) {
4428 set_vaxc_errno(retsts);
4429 if (retsts == RMS$_PRV) set_errno(EACCES);
4430 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4431 else set_errno(EVMSERR);
4434 retsts = sys$search(&myfab,0,0);
4435 if (!(retsts & 1)) {
4436 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4437 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
4438 set_vaxc_errno(retsts);
4439 if (retsts == RMS$_PRV) set_errno(EACCES);
4440 else if (retsts == RMS$_FNF) set_errno(ENOENT);
4441 else set_errno(EVMSERR);
4445 devdsc.dsc$w_length = mynam.nam$b_dev;
4446 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
4448 retsts = sys$assign(&devdsc,&chan,0,0);
4449 if (!(retsts & 1)) {
4450 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4451 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
4452 set_vaxc_errno(retsts);
4453 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
4454 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
4455 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
4456 else set_errno(EVMSERR);
4460 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
4461 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
4463 memset((void *) &myfib, 0, sizeof myfib);
4465 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
4466 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
4467 /* This prevents the revision time of the file being reset to the current
4468 * time as a result of our IO$_MODIFY $QIO. */
4469 myfib.fib$l_acctl = FIB$M_NORECORD;
4471 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
4472 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
4473 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
4475 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
4476 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4477 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
4478 _ckvmssts(sys$dassgn(chan));
4479 if (retsts & 1) retsts = iosb[0];
4480 if (!(retsts & 1)) {
4481 set_vaxc_errno(retsts);
4482 if (retsts == SS$_NOPRIV) set_errno(EACCES);
4483 else set_errno(EVMSERR);
4488 } /* end of my_utime() */
4492 * flex_stat, flex_fstat
4493 * basic stat, but gets it right when asked to stat
4494 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
4497 /* encode_dev packs a VMS device name string into an integer to allow
4498 * simple comparisons. This can be used, for example, to check whether two
4499 * files are located on the same device, by comparing their encoded device
4500 * names. Even a string comparison would not do, because stat() reuses the
4501 * device name buffer for each call; so without encode_dev, it would be
4502 * necessary to save the buffer and use strcmp (this would mean a number of
4503 * changes to the standard Perl code, to say nothing of what a Perl script
4506 * The device lock id, if it exists, should be unique (unless perhaps compared
4507 * with lock ids transferred from other nodes). We have a lock id if the disk is
4508 * mounted cluster-wide, which is when we tend to get long (host-qualified)
4509 * device names. Thus we use the lock id in preference, and only if that isn't
4510 * available, do we try to pack the device name into an integer (flagged by
4511 * the sign bit (LOCKID_MASK) being set).
4513 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
4514 * name and its encoded form, but it seems very unlikely that we will find
4515 * two files on different disks that share the same encoded device names,
4516 * and even more remote that they will share the same file id (if the test
4517 * is to check for the same file).
4519 * A better method might be to use sys$device_scan on the first call, and to
4520 * search for the device, returning an index into the cached array.
4521 * The number returned would be more intelligable.
4522 * This is probably not worth it, and anyway would take quite a bit longer
4523 * on the first call.
4525 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
4526 static mydev_t encode_dev (const char *dev)
4529 unsigned long int f;
4535 if (!dev || !dev[0]) return 0;
4539 struct dsc$descriptor_s dev_desc;
4540 unsigned long int status, lockid, item = DVI$_LOCKID;
4542 /* For cluster-mounted disks, the disk lock identifier is unique, so we
4543 can try that first. */
4544 dev_desc.dsc$w_length = strlen (dev);
4545 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
4546 dev_desc.dsc$b_class = DSC$K_CLASS_S;
4547 dev_desc.dsc$a_pointer = (char *) dev;
4548 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
4549 if (lockid) return (lockid & ~LOCKID_MASK);
4553 /* Otherwise we try to encode the device name */
4557 for (q = dev + strlen(dev); q--; q >= dev) {
4560 else if (isalpha (toupper (*q)))
4561 c= toupper (*q) - 'A' + (char)10;
4563 continue; /* Skip '$'s */
4565 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
4567 enc += f * (unsigned long int) c;
4569 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
4571 } /* end of encode_dev() */
4573 static char namecache[NAM$C_MAXRSS+1];
4576 is_null_device(name)
4580 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
4581 The underscore prefix, controller letter, and unit number are
4582 independently optional; for our purposes, the colon punctuation
4583 is not. The colon can be trailed by optional directory and/or
4584 filename, but two consecutive colons indicates a nodename rather
4585 than a device. [pr] */
4586 if (*name == '_') ++name;
4587 if (tolower(*name++) != 'n') return 0;
4588 if (tolower(*name++) != 'l') return 0;
4589 if (tolower(*name) == 'a') ++name;
4590 if (*name == '0') ++name;
4591 return (*name++ == ':') && (*name != ':');
4594 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
4595 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
4596 * subset of the applicable information.
4599 Perl_cando(pTHX_ Mode_t bit, Uid_t effective, Stat_t *statbufp)
4601 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
4603 char fname[NAM$C_MAXRSS+1];
4604 unsigned long int retsts;
4605 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
4606 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4608 /* If the struct mystat is stale, we're OOL; stat() overwrites the
4609 device name on successive calls */
4610 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
4611 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
4612 namdsc.dsc$a_pointer = fname;
4613 namdsc.dsc$w_length = sizeof fname - 1;
4615 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
4616 &namdsc,&namdsc.dsc$w_length,0,0);
4618 fname[namdsc.dsc$w_length] = '\0';
4619 return cando_by_name(bit,effective,fname);
4621 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
4622 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
4626 return FALSE; /* Should never get to here */
4628 } /* end of cando() */
4632 /*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
4634 cando_by_name(I32 bit, Uid_t effective, char *fname)
4636 static char usrname[L_cuserid];
4637 static struct dsc$descriptor_s usrdsc =
4638 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
4639 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
4640 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
4641 unsigned short int retlen;
4643 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4644 union prvdef curprv;
4645 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
4646 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
4647 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
4650 if (!fname || !*fname) return FALSE;
4651 /* Make sure we expand logical names, since sys$check_access doesn't */
4652 if (!strpbrk(fname,"/]>:")) {
4653 strcpy(fileified,fname);
4654 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
4657 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
4658 retlen = namdsc.dsc$w_length = strlen(vmsname);
4659 namdsc.dsc$a_pointer = vmsname;
4660 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
4661 vmsname[retlen-1] == ':') {
4662 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
4663 namdsc.dsc$w_length = strlen(fileified);
4664 namdsc.dsc$a_pointer = fileified;
4667 if (!usrdsc.dsc$w_length) {
4669 usrdsc.dsc$w_length = strlen(usrname);
4673 case S_IXUSR: case S_IXGRP: case S_IXOTH:
4674 access = ARM$M_EXECUTE; break;
4675 case S_IRUSR: case S_IRGRP: case S_IROTH:
4676 access = ARM$M_READ; break;
4677 case S_IWUSR: case S_IWGRP: case S_IWOTH:
4678 access = ARM$M_WRITE; break;
4679 case S_IDUSR: case S_IDGRP: case S_IDOTH:
4680 access = ARM$M_DELETE; break;
4685 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
4686 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
4687 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
4688 retsts == RMS$_DIR || retsts == RMS$_DEV) {
4689 set_vaxc_errno(retsts);
4690 if (retsts == SS$_NOPRIV) set_errno(EACCES);
4691 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
4692 else set_errno(ENOENT);
4695 if (retsts == SS$_NORMAL) {
4696 if (!privused) return TRUE;
4697 /* We can get access, but only by using privs. Do we have the
4698 necessary privs currently enabled? */
4699 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
4700 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
4701 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
4702 !curprv.prv$v_bypass) return FALSE;
4703 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
4704 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
4705 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
4708 if (retsts == SS$_ACCONFLICT) {
4712 #if defined(__ALPHA) && defined(__VMS_VER) && __VMS_VER == 70100022 && defined(__DECC_VER) && __DECC_VER == 6009001
4713 /* XXX Hideous kluge to accomodate error in specific version of RTL;
4714 we hope it'll be buried soon */
4715 if (retsts == 114762) return TRUE;
4719 return FALSE; /* Should never get here */
4721 } /* end of cando_by_name() */
4725 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
4727 flex_fstat(int fd, Stat_t *statbufp)
4730 if (!fstat(fd,(stat_t *) statbufp)) {
4731 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
4732 statbufp->st_dev = encode_dev(statbufp->st_devnam);
4733 # ifdef RTL_USES_UTC
4736 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4737 statbufp->st_atime = _toloc(statbufp->st_atime);
4738 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4743 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4747 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4748 statbufp->st_atime = _toutc(statbufp->st_atime);
4749 statbufp->st_ctime = _toutc(statbufp->st_ctime);
4756 } /* end of flex_fstat() */
4759 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
4761 flex_stat(const char *fspec, Stat_t *statbufp)
4764 char fileified[NAM$C_MAXRSS+1];
4765 char temp_fspec[NAM$C_MAXRSS+300];
4768 strcpy(temp_fspec, fspec);
4769 if (statbufp == (Stat_t *) &PL_statcache)
4770 do_tovmsspec(temp_fspec,namecache,0);
4771 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
4772 memset(statbufp,0,sizeof *statbufp);
4773 statbufp->st_dev = encode_dev("_NLA0:");
4774 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
4775 statbufp->st_uid = 0x00010001;
4776 statbufp->st_gid = 0x0001;
4777 time((time_t *)&statbufp->st_mtime);
4778 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
4782 /* Try for a directory name first. If fspec contains a filename without
4783 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
4784 * and sea:[wine.dark]water. exist, we prefer the directory here.
4785 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
4786 * not sea:[wine.dark]., if the latter exists. If the intended target is
4787 * the file with null type, specify this by calling flex_stat() with
4788 * a '.' at the end of fspec.
4790 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
4791 retval = stat(fileified,(stat_t *) statbufp);
4792 if (!retval && statbufp == (Stat_t *) &PL_statcache)
4793 strcpy(namecache,fileified);
4795 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
4797 statbufp->st_dev = encode_dev(statbufp->st_devnam);
4798 # ifdef RTL_USES_UTC
4801 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4802 statbufp->st_atime = _toloc(statbufp->st_atime);
4803 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4808 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4812 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4813 statbufp->st_atime = _toutc(statbufp->st_atime);
4814 statbufp->st_ctime = _toutc(statbufp->st_ctime);
4820 } /* end of flex_stat() */
4824 /*{{{char *my_getlogin()*/
4825 /* VMS cuserid == Unix getlogin, except calling sequence */
4829 static char user[L_cuserid];
4830 return cuserid(user);
4835 /* rmscopy - copy a file using VMS RMS routines
4837 * Copies contents and attributes of spec_in to spec_out, except owner
4838 * and protection information. Name and type of spec_in are used as
4839 * defaults for spec_out. The third parameter specifies whether rmscopy()
4840 * should try to propagate timestamps from the input file to the output file.
4841 * If it is less than 0, no timestamps are preserved. If it is 0, then
4842 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
4843 * propagated to the output file at creation iff the output file specification
4844 * did not contain an explicit name or type, and the revision date is always
4845 * updated at the end of the copy operation. If it is greater than 0, then
4846 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
4847 * other than the revision date should be propagated, and bit 1 indicates
4848 * that the revision date should be propagated.
4850 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
4852 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
4853 * Incorporates, with permission, some code from EZCOPY by Tim Adye
4854 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
4855 * as part of the Perl standard distribution under the terms of the
4856 * GNU General Public License or the Perl Artistic License. Copies
4857 * of each may be found in the Perl standard distribution.
4859 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
4861 rmscopy(char *spec_in, char *spec_out, int preserve_dates)
4863 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
4864 rsa[NAM$C_MAXRSS], ubf[32256];
4865 unsigned long int i, sts, sts2;
4866 struct FAB fab_in, fab_out;
4867 struct RAB rab_in, rab_out;
4869 struct XABDAT xabdat;
4870 struct XABFHC xabfhc;
4871 struct XABRDT xabrdt;
4872 struct XABSUM xabsum;
4874 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
4875 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
4876 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4880 fab_in = cc$rms_fab;
4881 fab_in.fab$l_fna = vmsin;
4882 fab_in.fab$b_fns = strlen(vmsin);
4883 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
4884 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
4885 fab_in.fab$l_fop = FAB$M_SQO;
4886 fab_in.fab$l_nam = &nam;
4887 fab_in.fab$l_xab = (void *) &xabdat;
4890 nam.nam$l_rsa = rsa;
4891 nam.nam$b_rss = sizeof(rsa);
4892 nam.nam$l_esa = esa;
4893 nam.nam$b_ess = sizeof (esa);
4894 nam.nam$b_esl = nam.nam$b_rsl = 0;
4896 xabdat = cc$rms_xabdat; /* To get creation date */
4897 xabdat.xab$l_nxt = (void *) &xabfhc;
4899 xabfhc = cc$rms_xabfhc; /* To get record length */
4900 xabfhc.xab$l_nxt = (void *) &xabsum;
4902 xabsum = cc$rms_xabsum; /* To get key and area information */
4904 if (!((sts = sys$open(&fab_in)) & 1)) {
4905 set_vaxc_errno(sts);
4907 case RMS$_FNF: case RMS$_DNF:
4908 set_errno(ENOENT); break;
4910 set_errno(ENOTDIR); break;
4912 set_errno(ENODEV); break;
4914 set_errno(EINVAL); break;
4916 set_errno(EACCES); break;
4924 fab_out.fab$w_ifi = 0;
4925 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
4926 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
4927 fab_out.fab$l_fop = FAB$M_SQO;
4928 fab_out.fab$l_fna = vmsout;
4929 fab_out.fab$b_fns = strlen(vmsout);
4930 fab_out.fab$l_dna = nam.nam$l_name;
4931 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
4933 if (preserve_dates == 0) { /* Act like DCL COPY */
4934 nam.nam$b_nop = NAM$M_SYNCHK;
4935 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
4936 if (!((sts = sys$parse(&fab_out)) & 1)) {
4937 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
4938 set_vaxc_errno(sts);
4941 fab_out.fab$l_xab = (void *) &xabdat;
4942 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
4944 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
4945 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
4946 preserve_dates =0; /* bitmask from this point forward */
4948 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
4949 if (!((sts = sys$create(&fab_out)) & 1)) {
4950 set_vaxc_errno(sts);
4953 set_errno(ENOENT); break;
4955 set_errno(ENOTDIR); break;
4957 set_errno(ENODEV); break;
4959 set_errno(EINVAL); break;
4961 set_errno(EACCES); break;
4967 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
4968 if (preserve_dates & 2) {
4969 /* sys$close() will process xabrdt, not xabdat */
4970 xabrdt = cc$rms_xabrdt;
4972 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
4974 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
4975 * is unsigned long[2], while DECC & VAXC use a struct */
4976 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
4978 fab_out.fab$l_xab = (void *) &xabrdt;
4981 rab_in = cc$rms_rab;
4982 rab_in.rab$l_fab = &fab_in;
4983 rab_in.rab$l_rop = RAB$M_BIO;
4984 rab_in.rab$l_ubf = ubf;
4985 rab_in.rab$w_usz = sizeof ubf;
4986 if (!((sts = sys$connect(&rab_in)) & 1)) {
4987 sys$close(&fab_in); sys$close(&fab_out);
4988 set_errno(EVMSERR); set_vaxc_errno(sts);
4992 rab_out = cc$rms_rab;
4993 rab_out.rab$l_fab = &fab_out;
4994 rab_out.rab$l_rbf = ubf;
4995 if (!((sts = sys$connect(&rab_out)) & 1)) {
4996 sys$close(&fab_in); sys$close(&fab_out);
4997 set_errno(EVMSERR); set_vaxc_errno(sts);
5001 while ((sts = sys$read(&rab_in))) { /* always true */
5002 if (sts == RMS$_EOF) break;
5003 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
5004 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
5005 sys$close(&fab_in); sys$close(&fab_out);
5006 set_errno(EVMSERR); set_vaxc_errno(sts);
5011 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
5012 sys$close(&fab_in); sys$close(&fab_out);
5013 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
5015 set_errno(EVMSERR); set_vaxc_errno(sts);
5021 } /* end of rmscopy() */
5025 /*** The following glue provides 'hooks' to make some of the routines
5026 * from this file available from Perl. These routines are sufficiently
5027 * basic, and are required sufficiently early in the build process,
5028 * that's it's nice to have them available to miniperl as well as the
5029 * full Perl, so they're set up here instead of in an extension. The
5030 * Perl code which handles importation of these names into a given
5031 * package lives in [.VMS]Filespec.pm in @INC.
5035 rmsexpand_fromperl(pTHX_ CV *cv)
5038 char *fspec, *defspec = NULL, *rslt;
5041 if (!items || items > 2)
5042 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
5043 fspec = SvPV(ST(0),n_a);
5044 if (!fspec || !*fspec) XSRETURN_UNDEF;
5045 if (items == 2) defspec = SvPV(ST(1),n_a);
5047 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
5048 ST(0) = sv_newmortal();
5049 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
5054 vmsify_fromperl(pTHX_ CV *cv)
5060 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
5061 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
5062 ST(0) = sv_newmortal();
5063 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
5068 unixify_fromperl(pTHX_ CV *cv)
5074 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
5075 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
5076 ST(0) = sv_newmortal();
5077 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
5082 fileify_fromperl(pTHX_ CV *cv)
5088 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
5089 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
5090 ST(0) = sv_newmortal();
5091 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
5096 pathify_fromperl(pTHX_ CV *cv)
5102 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
5103 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
5104 ST(0) = sv_newmortal();
5105 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
5110 vmspath_fromperl(pTHX_ CV *cv)
5116 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
5117 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
5118 ST(0) = sv_newmortal();
5119 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
5124 unixpath_fromperl(pTHX_ CV *cv)
5130 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
5131 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
5132 ST(0) = sv_newmortal();
5133 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
5138 candelete_fromperl(pTHX_ CV *cv)
5141 char fspec[NAM$C_MAXRSS+1], *fsp;
5146 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
5148 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
5149 if (SvTYPE(mysv) == SVt_PVGV) {
5150 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec,1)) {
5151 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5158 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
5159 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5165 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
5170 rmscopy_fromperl(pTHX_ CV *cv)
5173 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
5175 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
5176 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5177 unsigned long int sts;
5182 if (items < 2 || items > 3)
5183 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
5185 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
5186 if (SvTYPE(mysv) == SVt_PVGV) {
5187 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec,1)) {
5188 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5195 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
5196 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5201 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
5202 if (SvTYPE(mysv) == SVt_PVGV) {
5203 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec,1)) {
5204 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5211 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
5212 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5217 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
5219 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
5226 char* file = __FILE__;
5228 char temp_buff[512];
5229 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
5230 no_translate_barewords = TRUE;
5232 no_translate_barewords = FALSE;
5235 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
5236 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
5237 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
5238 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
5239 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
5240 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
5241 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
5242 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
5243 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);