This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Regenerate Configure.
[perl5.git] / vms / vms.c
CommitLineData
748a9306 1/* vms.c
a0d0e21e 2 *
748a9306
LW
3 * VMS-specific routines for perl5
4 *
93948341
CB
5 * Last revised: 15-Aug-1999 by Charles Bailey bailey@newman.upenn.edu
6 * Version: 5.5.60
a0d0e21e
LW
7 */
8
9#include <acedef.h>
10#include <acldef.h>
11#include <armdef.h>
748a9306 12#include <atrdef.h>
a0d0e21e 13#include <chpdef.h>
8fde5078 14#include <clidef.h>
a3e9d8c9 15#include <climsgdef.h>
a0d0e21e
LW
16#include <descrip.h>
17#include <dvidef.h>
748a9306 18#include <fibdef.h>
a0d0e21e
LW
19#include <float.h>
20#include <fscndef.h>
21#include <iodef.h>
22#include <jpidef.h>
61bb5906 23#include <kgbdef.h>
f675dbe5 24#include <libclidef.h>
a0d0e21e
LW
25#include <libdef.h>
26#include <lib$routines.h>
27#include <lnmdef.h>
748a9306 28#include <prvdef.h>
a0d0e21e
LW
29#include <psldef.h>
30#include <rms.h>
31#include <shrdef.h>
32#include <ssdef.h>
33#include <starlet.h>
f86702cc 34#include <strdef.h>
35#include <str$routines.h>
a0d0e21e 36#include <syidef.h>
748a9306
LW
37#include <uaidef.h>
38#include <uicdef.h>
a0d0e21e 39
740ce14c 40/* Older versions of ssdef.h don't have these */
41#ifndef SS$_INVFILFOROP
42# define SS$_INVFILFOROP 3930
43#endif
44#ifndef SS$_NOSUCHOBJECT
b7ae7a0d 45# define SS$_NOSUCHOBJECT 2696
46#endif
47
aa689395 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
a0d0e21e
LW
51#include "EXTERN.h"
52#include "perl.h"
748a9306 53#include "XSUB.h"
3eeba6fb
CB
54/* Anticipating future expansion in lexical warnings . . . */
55#ifndef WARN_INTERNAL
56# define WARN_INTERNAL WARN_MISC
57#endif
a0d0e21e 58
c07a80fd 59/* gcc's header files don't #define direct access macros
60 * corresponding to VAXC's variant structs */
61#ifdef __GNUC__
482b294c 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
c07a80fd 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
69#endif
70
71
a0d0e21e
LW
72struct itmlst_3 {
73 unsigned short int buflen;
74 unsigned short int itmcode;
75 void *bufadr;
748a9306 76 unsigned short int *retlen;
a0d0e21e
LW
77};
78
01b8edb6 79static char *__mystrtolower(char *str)
80{
81 if (str) for (; *str; ++str) *str= tolower(*str);
82 return str;
83}
84
f675dbe5
CB
85static struct dsc$descriptor_s fildevdsc =
86 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
87static struct dsc$descriptor_s crtlenvdsc =
88 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
89static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
90static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
91static struct dsc$descriptor_s **env_tables = defenv;
92static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
93
ebd8c45c
DS
94/* True if we shouldn't treat barewords as logicals during directory */
95/* munching */
96static int no_translate_barewords;
97
de736a29
GS
98/* True if we shouldn't treat barewords as logicals during directory */
99/* munching */
100static int no_translate_barewords;
101
f675dbe5 102/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
c07a80fd 103int
f675dbe5
CB
104vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
105 struct dsc$descriptor_s **tabvec, unsigned long int flags)
748a9306 106{
f675dbe5
CB
107 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
108 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
748a9306 109 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
f675dbe5
CB
110 unsigned char acmode;
111 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
112 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
113 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
114 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
748a9306 115 {0, 0, 0, 0}};
f675dbe5 116 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
cc077a9f
HM
117#if defined(USE_THREADS)
118 /* We jump through these hoops because we can be called at */
119 /* platform-specific initialization time, which is before anything is */
d28f7c37 120 /* set up--we can't even do a plain dTHX since that relies on the */
cc077a9f
HM
121 /* interpreter structure to be initialized */
122 struct perl_thread *thr;
123 if (PL_curinterp) {
124 thr = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
125 } else {
126 thr = NULL;
127 }
128#endif
748a9306 129
f675dbe5 130 if (!lnm || !eqv || idx > LNM$_MAX_INDEX) {
b7ae7a0d 131 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
132 }
f675dbe5
CB
133 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
134 *cp2 = _toupper(*cp1);
135 if (cp1 - lnm > LNM$C_NAMLENGTH) {
136 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
137 return 0;
138 }
139 }
140 lnmdsc.dsc$w_length = cp1 - lnm;
141 lnmdsc.dsc$a_pointer = uplnm;
142 secure = flags & PERL__TRNENV_SECURE;
143 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
144 if (!tabvec || !*tabvec) tabvec = env_tables;
145
146 for (curtab = 0; tabvec[curtab]; curtab++) {
147 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
148 if (!ivenv && !secure) {
149 char *eq, *end;
150 int i;
151 if (!environ) {
152 ivenv = 1;
d28f7c37 153 Perl_warn(aTHX_ "Can't read CRTL environ\n");
f675dbe5
CB
154 continue;
155 }
156 retsts = SS$_NOLOGNAM;
157 for (i = 0; environ[i]; i++) {
158 if ((eq = strchr(environ[i],'=')) &&
159 !strncmp(environ[i],uplnm,eq - environ[i])) {
160 eq++;
161 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
162 if (!eqvlen) continue;
163 retsts = SS$_NORMAL;
164 break;
165 }
166 }
167 if (retsts != SS$_NOLOGNAM) break;
168 }
169 }
170 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
171 !str$case_blind_compare(&tmpdsc,&clisym)) {
172 if (!ivsym && !secure) {
173 unsigned short int deflen = LNM$C_NAMLENGTH;
174 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
175 /* dynamic dsc to accomodate possible long value */
176 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
177 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
178 if (retsts & 1) {
179 if (eqvlen > 1024) {
f675dbe5 180 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
3eeba6fb 181 eqvlen = 1024;
cc077a9f
HM
182 /* Special hack--we might be called before the interpreter's */
183 /* fully initialized, in which case either thr or PL_curcop */
184 /* might be bogus. We have to check, since ckWARN needs them */
185 /* both to be valid if running threaded */
186#if defined(USE_THREADS)
187 if (thr && PL_curcop) {
188#endif
189 if (ckWARN(WARN_MISC)) {
d28f7c37 190 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
cc077a9f
HM
191 }
192#if defined(USE_THREADS)
193 } else {
d28f7c37 194 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
cc077a9f
HM
195 }
196#endif
197
f675dbe5
CB
198 }
199 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
200 }
201 _ckvmssts(lib$sfree1_dd(&eqvdsc));
202 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
203 if (retsts == LIB$_NOSUCHSYM) continue;
204 break;
205 }
206 }
207 else if (!ivlnm) {
208 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
209 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
210 if (retsts == SS$_NOLOGNAM) continue;
211 break;
212 }
c07a80fd 213 }
f675dbe5
CB
214 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
215 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
216 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
217 retsts == SS$_NOLOGNAM) {
218 set_errno(EINVAL); set_vaxc_errno(retsts);
748a9306 219 }
f675dbe5
CB
220 else _ckvmssts(retsts);
221 return 0;
222} /* end of vmstrnenv */
223/*}}}*/
c07a80fd 224
f675dbe5
CB
225/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
226/* Define as a function so we can access statics. */
227int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)
228{
229 return vmstrnenv(lnm,eqv,idx,fildev,
230#ifdef SECURE_INTERNAL_GETENV
231 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
232#else
233 0
234#endif
235 );
236}
237/*}}}*/
a0d0e21e
LW
238
239/* my_getenv
61bb5906
CB
240 * Note: Uses Perl temp to store result so char * can be returned to
241 * caller; this pointer will be invalidated at next Perl statement
242 * transition.
a6c40364 243 * We define this as a function rather than a macro in terms of my_getenv_len()
f675dbe5
CB
244 * so that it'll work when PL_curinterp is undefined (and we therefore can't
245 * allocate SVs).
a0d0e21e 246 */
f675dbe5 247/*{{{ char *my_getenv(const char *lnm, bool sys)*/
a0d0e21e 248char *
d28f7c37 249Perl_my_getenv(pTHX_ const char *lnm, bool sys)
a0d0e21e
LW
250{
251 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
f675dbe5 252 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
c07a80fd 253 unsigned long int idx = 0;
edc7bc49 254 int trnsuccess;
61bb5906 255 SV *tmpsv;
a0d0e21e 256
6b88bc9c 257 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
61bb5906
CB
258 /* Set up a temporary buffer for the return value; Perl will
259 * clean it up at the next statement transition */
260 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
261 if (!tmpsv) return NULL;
262 eqv = SvPVX(tmpsv);
263 }
264 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
f675dbe5
CB
265 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
266 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
61bb5906
CB
267 getcwd(eqv,LNM$C_NAMLENGTH);
268 return eqv;
748a9306 269 }
a0d0e21e 270 else {
f675dbe5
CB
271 if ((cp2 = strchr(lnm,';')) != NULL) {
272 strcpy(uplnm,lnm);
273 uplnm[cp2-lnm] = '\0';
c07a80fd 274 idx = strtoul(cp2+1,NULL,0);
f675dbe5 275 lnm = uplnm;
c07a80fd 276 }
f675dbe5
CB
277 if (vmstrnenv(lnm,eqv,idx,
278 sys ? fildev : NULL,
279#ifdef SECURE_INTERNAL_GETENV
280 sys ? PERL__TRNENV_SECURE : 0
281#else
282 0
283#endif
284 )) return eqv;
285 else return Nullch;
a0d0e21e 286 }
a0d0e21e
LW
287
288} /* end of my_getenv() */
289/*}}}*/
290
f675dbe5 291
a6c40364
GS
292/*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
293char *
294my_getenv_len(const char *lnm, unsigned long *len, bool sys)
f675dbe5 295{
d28f7c37 296 dTHX;
cc077a9f 297 char *buf, *cp1, *cp2;
a6c40364 298 unsigned long idx = 0;
cc077a9f
HM
299 static char __my_getenv_len_eqv[LNM$C_NAMLENGTH+1];
300 SV *tmpsv;
301
302 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
303 /* Set up a temporary buffer for the return value; Perl will
304 * clean it up at the next statement transition */
305 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
306 if (!tmpsv) return NULL;
307 buf = SvPVX(tmpsv);
308 }
309 else buf = __my_getenv_len_eqv; /* Assume no interpreter ==> single thread */
f675dbe5
CB
310 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
311 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
312 getcwd(buf,LNM$C_NAMLENGTH);
a6c40364
GS
313 *len = strlen(buf);
314 return buf;
f675dbe5
CB
315 }
316 else {
317 if ((cp2 = strchr(lnm,';')) != NULL) {
318 strcpy(buf,lnm);
319 buf[cp2-lnm] = '\0';
320 idx = strtoul(cp2+1,NULL,0);
321 lnm = buf;
322 }
a6c40364 323 if ((*len = vmstrnenv(lnm,buf,idx,
f675dbe5
CB
324 sys ? fildev : NULL,
325#ifdef SECURE_INTERNAL_GETENV
326 sys ? PERL__TRNENV_SECURE : 0
327#else
328 0
329#endif
a6c40364
GS
330 )))
331 return buf;
cc077a9f
HM
332 else
333 return Nullch;
f675dbe5
CB
334 }
335
a6c40364 336} /* end of my_getenv_len() */
f675dbe5
CB
337/*}}}*/
338
8fde5078
CB
339static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
340
341static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1e422769 342
740ce14c 343/*{{{ void prime_env_iter() */
344void
345prime_env_iter(void)
346/* Fill the %ENV associative array with all logical names we can
347 * find, in preparation for iterating over it.
348 */
349{
d28f7c37 350 dTHX;
17f28c40 351 static int primed = 0;
3eeba6fb 352 HV *seenhv = NULL, *envhv;
f675dbe5 353 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
8fde5078
CB
354 unsigned short int chan;
355#ifndef CLI$M_TRUSTED
356# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
357#endif
f675dbe5
CB
358 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
359 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
360 long int i;
361 bool have_sym = FALSE, have_lnm = FALSE;
362 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
363 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
364 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
365 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
366 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
61bb5906 367#ifdef USE_THREADS
b2b3adea
HM
368 static perl_mutex primenv_mutex;
369 MUTEX_INIT(&primenv_mutex);
61bb5906 370#endif
740ce14c 371
3eeba6fb 372 if (primed || !PL_envgv) return;
61bb5906
CB
373 MUTEX_LOCK(&primenv_mutex);
374 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
3eeba6fb 375 envhv = GvHVn(PL_envgv);
740ce14c 376 /* Perform a dummy fetch as an lval to insure that the hash table is
8fde5078 377 * set up. Otherwise, the hv_store() will turn into a nullop. */
740ce14c 378 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
740ce14c 379
f675dbe5
CB
380 for (i = 0; env_tables[i]; i++) {
381 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
382 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
383 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
8fde5078 384 }
f675dbe5
CB
385 if (have_sym || have_lnm) {
386 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
387 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
388 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
389 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
61bb5906 390 }
f675dbe5
CB
391
392 for (i--; i >= 0; i--) {
393 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
394 char *start;
395 int j;
396 for (j = 0; environ[j]; j++) {
397 if (!(start = strchr(environ[j],'='))) {
3eeba6fb 398 if (ckWARN(WARN_INTERNAL))
d28f7c37 399 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
f675dbe5
CB
400 }
401 else {
402 start++;
403 (void) hv_store(envhv,environ[j],start - environ[j] - 1,
404 newSVpv(start,0),0);
405 }
406 }
407 continue;
740ce14c 408 }
f675dbe5
CB
409 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
410 !str$case_blind_compare(&tmpdsc,&clisym)) {
411 strcpy(cmd,"Show Symbol/Global *");
412 cmddsc.dsc$w_length = 20;
413 if (env_tables[i]->dsc$w_length == 12 &&
414 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
415 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
416 flags = defflags | CLI$M_NOLOGNAM;
417 }
418 else {
419 strcpy(cmd,"Show Logical *");
420 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
421 strcat(cmd," /Table=");
422 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
423 cmddsc.dsc$w_length = strlen(cmd);
424 }
425 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
426 flags = defflags | CLI$M_NOCLISYM;
427 }
428
429 /* Create a new subprocess to execute each command, to exclude the
430 * remote possibility that someone could subvert a mbx or file used
431 * to write multiple commands to a single subprocess.
432 */
433 do {
434 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
435 0,&riseandshine,0,0,&clidsc,&clitabdsc);
436 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
437 defflags &= ~CLI$M_TRUSTED;
438 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
439 _ckvmssts(retsts);
440 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
441 if (seenhv) SvREFCNT_dec(seenhv);
442 seenhv = newHV();
443 while (1) {
444 char *cp1, *cp2, *key;
445 unsigned long int sts, iosb[2], retlen, keylen;
446 register U32 hash;
447
448 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
449 if (sts & 1) sts = iosb[0] & 0xffff;
450 if (sts == SS$_ENDOFFILE) {
451 int wakect = 0;
452 while (substs == 0) { sys$hiber(); wakect++;}
453 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
454 _ckvmssts(substs);
455 break;
456 }
457 _ckvmssts(sts);
458 retlen = iosb[0] >> 16;
459 if (!retlen) continue; /* blank line */
460 buf[retlen] = '\0';
461 if (iosb[1] != subpid) {
462 if (iosb[1]) {
d28f7c37 463 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
f675dbe5
CB
464 }
465 continue;
466 }
3eeba6fb 467 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
d28f7c37 468 Perl_warner(aTHX_ WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
f675dbe5
CB
469
470 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
471 if (*cp1 == '(' || /* Logical name table name */
472 *cp1 == '=' /* Next eqv of searchlist */) continue;
473 if (*cp1 == '"') cp1++;
474 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
475 key = cp1; keylen = cp2 - cp1;
476 if (keylen && hv_exists(seenhv,key,keylen)) continue;
477 while (*cp2 && *cp2 != '=') cp2++;
644a2880
JH
478 while (*cp2 && *cp2 == '=') cp2++;
479 while (*cp2 && *cp2 == ' ') cp2++;
480 if (*cp2 == '"') { /* String translation; may embed "" */
481 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
482 cp2++; cp1--; /* Skip "" surrounding translation */
483 }
484 else { /* Numeric translation */
485 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
486 cp1--; /* stop on last non-space char */
487 }
488 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
d28f7c37 489 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
edc7bc49
CB
490 continue;
491 }
f675dbe5 492 PERL_HASH(hash,key,keylen);
644a2880 493 hv_store(envhv,key,keylen,newSVpvn(cp2,cp1 - cp2 + 1),hash);
f675dbe5 494 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
edc7bc49 495 }
f675dbe5
CB
496 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
497 /* get the PPFs for this process, not the subprocess */
498 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
499 char eqv[LNM$C_NAMLENGTH+1];
500 int trnlen, i;
501 for (i = 0; ppfs[i]; i++) {
502 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
503 hv_store(envhv,ppfs[i],strlen(ppfs[i]),newSVpv(eqv,trnlen),0);
504 }
740ce14c 505 }
506 }
f675dbe5
CB
507 primed = 1;
508 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
509 if (buf) Safefree(buf);
510 if (seenhv) SvREFCNT_dec(seenhv);
511 MUTEX_UNLOCK(&primenv_mutex);
512 return;
513
740ce14c 514} /* end of prime_env_iter */
515/*}}}*/
740ce14c 516
f675dbe5
CB
517
518/*{{{ int vmssetenv(char *lnm, char *eqv)*/
519/* Define or delete an element in the same "environment" as
520 * vmstrnenv(). If an element is to be deleted, it's removed from
521 * the first place it's found. If it's to be set, it's set in the
522 * place designated by the first element of the table vector.
3eeba6fb 523 * Like setenv() returns 0 for success, non-zero on error.
a0d0e21e 524 */
f675dbe5
CB
525int
526vmssetenv(char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
a0d0e21e
LW
527{
528 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
f675dbe5 529 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
a0d0e21e 530 unsigned long int retsts, usermode = PSL$C_USER;
a0d0e21e 531 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
f675dbe5
CB
532 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
533 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
534 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
535 $DESCRIPTOR(local,"_LOCAL");
d28f7c37 536 dTHX;
f675dbe5
CB
537
538 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
539 *cp2 = _toupper(*cp1);
540 if (cp1 - lnm > LNM$C_NAMLENGTH) {
541 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
542 return SS$_IVLOGNAM;
543 }
544 }
a0d0e21e 545 lnmdsc.dsc$w_length = cp1 - lnm;
f675dbe5
CB
546 if (!tabvec || !*tabvec) tabvec = env_tables;
547
3eeba6fb 548 if (!eqv) { /* we're deleting n element */
f675dbe5
CB
549 for (curtab = 0; tabvec[curtab]; curtab++) {
550 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
551 int i;
f675dbe5
CB
552 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
553 if ((cp1 = strchr(environ[i],'=')) &&
554 !strncmp(environ[i],lnm,cp1 - environ[i])) {
3eeba6fb
CB
555#ifdef HAS_SETENV
556 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
f675dbe5
CB
557 }
558 }
559 ivenv = 1; retsts = SS$_NOLOGNAM;
560#else
3eeba6fb 561 if (ckWARN(WARN_INTERNAL))
d28f7c37 562 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
3eeba6fb
CB
563 ivenv = 1; retsts = SS$_NOSUCHPGM;
564 break;
565 }
566 }
f675dbe5
CB
567#endif
568 }
569 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
570 !str$case_blind_compare(&tmpdsc,&clisym)) {
571 unsigned int symtype;
572 if (tabvec[curtab]->dsc$w_length == 12 &&
573 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
574 !str$case_blind_compare(&tmpdsc,&local))
575 symtype = LIB$K_CLI_LOCAL_SYM;
576 else symtype = LIB$K_CLI_GLOBAL_SYM;
577 retsts = lib$delete_symbol(&lnmdsc,&symtype);
3eeba6fb
CB
578 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
579 if (retsts == LIB$_NOSUCHSYM) continue;
f675dbe5
CB
580 break;
581 }
582 else if (!ivlnm) {
583 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
584 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
585 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
586 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
587 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
588 }
a0d0e21e
LW
589 }
590 }
f675dbe5
CB
591 else { /* we're defining a value */
592 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
593#ifdef HAS_SETENV
3eeba6fb 594 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
f675dbe5 595#else
3eeba6fb 596 if (ckWARN(WARN_INTERNAL))
d28f7c37 597 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
f675dbe5
CB
598 retsts = SS$_NOSUCHPGM;
599#endif
600 }
601 else {
602 eqvdsc.dsc$a_pointer = eqv;
603 eqvdsc.dsc$w_length = strlen(eqv);
604 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
605 !str$case_blind_compare(&tmpdsc,&clisym)) {
606 unsigned int symtype;
607 if (tabvec[0]->dsc$w_length == 12 &&
608 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
609 !str$case_blind_compare(&tmpdsc,&local))
610 symtype = LIB$K_CLI_LOCAL_SYM;
611 else symtype = LIB$K_CLI_GLOBAL_SYM;
612 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
613 }
3eeba6fb
CB
614 else {
615 if (!*eqv) eqvdsc.dsc$w_length = 1;
616 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
617 }
f675dbe5
CB
618 }
619 }
620 if (!(retsts & 1)) {
621 switch (retsts) {
622 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
623 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
624 set_errno(EVMSERR); break;
625 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
626 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
627 set_errno(EINVAL); break;
628 case SS$_NOPRIV:
629 set_errno(EACCES);
630 default:
631 _ckvmssts(retsts);
632 set_errno(EVMSERR);
633 }
634 set_vaxc_errno(retsts);
635 return (int) retsts || 44; /* retsts should never be 0, but just in case */
a0d0e21e 636 }
3eeba6fb
CB
637 else {
638 /* We reset error values on success because Perl does an hv_fetch()
639 * before each hv_store(), and if the thing we're setting didn't
640 * previously exist, we've got a leftover error message. (Of course,
641 * this fails in the face of
642 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
643 * in that the error reported in $! isn't spurious,
644 * but it's right more often than not.)
645 */
f675dbe5
CB
646 set_errno(0); set_vaxc_errno(retsts);
647 return 0;
648 }
649
650} /* end of vmssetenv() */
651/*}}}*/
a0d0e21e 652
f675dbe5
CB
653/*{{{ void my_setenv(char *lnm, char *eqv)*/
654/* This has to be a function since there's a prototype for it in proto.h */
655void
d28f7c37 656Perl_my_setenv(pTHX_ char *lnm,char *eqv)
f675dbe5
CB
657{
658 if (lnm && *lnm && strlen(lnm) == 7) {
659 char uplnm[8];
660 int i;
661 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
662 if (!strcmp(uplnm,"DEFAULT")) {
663 if (eqv && *eqv) chdir(eqv);
664 return;
665 }
666 }
667 (void) vmssetenv(lnm,eqv,NULL);
668}
a0d0e21e
LW
669/*}}}*/
670
c07a80fd 671
f675dbe5 672
c07a80fd 673/*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
674/* my_crypt - VMS password hashing
675 * my_crypt() provides an interface compatible with the Unix crypt()
676 * C library function, and uses sys$hash_password() to perform VMS
677 * password hashing. The quadword hashed password value is returned
678 * as a NUL-terminated 8 character string. my_crypt() does not change
679 * the case of its string arguments; in order to match the behavior
680 * of LOGINOUT et al., alphabetic characters in both arguments must
681 * be upcased by the caller.
682 */
683char *
684my_crypt(const char *textpasswd, const char *usrname)
685{
686# ifndef UAI$C_PREFERRED_ALGORITHM
687# define UAI$C_PREFERRED_ALGORITHM 127
688# endif
689 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
690 unsigned short int salt = 0;
691 unsigned long int sts;
692 struct const_dsc {
693 unsigned short int dsc$w_length;
694 unsigned char dsc$b_type;
695 unsigned char dsc$b_class;
696 const char * dsc$a_pointer;
697 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
698 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
699 struct itmlst_3 uailst[3] = {
700 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
701 { sizeof salt, UAI$_SALT, &salt, 0},
702 { 0, 0, NULL, NULL}};
703 static char hash[9];
704
705 usrdsc.dsc$w_length = strlen(usrname);
706 usrdsc.dsc$a_pointer = usrname;
707 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
708 switch (sts) {
709 case SS$_NOGRPPRV:
710 case SS$_NOSYSPRV:
711 set_errno(EACCES);
712 break;
713 case RMS$_RNF:
714 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
715 break;
716 default:
717 set_errno(EVMSERR);
718 }
719 set_vaxc_errno(sts);
720 if (sts != RMS$_RNF) return NULL;
721 }
722
723 txtdsc.dsc$w_length = strlen(textpasswd);
724 txtdsc.dsc$a_pointer = textpasswd;
725 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
726 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
727 }
728
729 return (char *) hash;
730
731} /* end of my_crypt() */
732/*}}}*/
733
734
bbce6d69 735static char *do_rmsexpand(char *, char *, int, char *, unsigned);
a0d0e21e
LW
736static char *do_fileify_dirspec(char *, char *, int);
737static char *do_tovmsspec(char *, char *, int);
738
739/*{{{int do_rmdir(char *name)*/
740int
741do_rmdir(char *name)
742{
743 char dirfile[NAM$C_MAXRSS+1];
744 int retval;
61bb5906 745 Stat_t st;
a0d0e21e
LW
746
747 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
748 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
749 else retval = kill_file(dirfile);
750 return retval;
751
752} /* end of do_rmdir */
753/*}}}*/
754
755/* kill_file
756 * Delete any file to which user has control access, regardless of whether
757 * delete access is explicitly allowed.
758 * Limitations: User must have write access to parent directory.
759 * Does not block signals or ASTs; if interrupted in midstream
760 * may leave file with an altered ACL.
761 * HANDLE WITH CARE!
762 */
763/*{{{int kill_file(char *name)*/
764int
765kill_file(char *name)
766{
bbce6d69 767 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
a0d0e21e 768 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
748a9306 769 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
d28f7c37 770 dTHX;
a0d0e21e
LW
771 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
772 struct myacedef {
748a9306
LW
773 unsigned char myace$b_length;
774 unsigned char myace$b_type;
775 unsigned short int myace$w_flags;
776 unsigned long int myace$l_access;
777 unsigned long int myace$l_ident;
a0d0e21e
LW
778 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
779 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
780 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
781 struct itmlst_3
748a9306
LW
782 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
783 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
784 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
785 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
786 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
787 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
a0d0e21e 788
bbce6d69 789 /* Expand the input spec using RMS, since the CRTL remove() and
790 * system services won't do this by themselves, so we may miss
791 * a file "hiding" behind a logical name or search list. */
792 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
793 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
794 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
740ce14c 795 /* If not, can changing protections help? */
796 if (vaxc$errno != RMS$_PRV) return -1;
a0d0e21e
LW
797
798 /* No, so we get our own UIC to use as a rights identifier,
799 * and the insert an ACE at the head of the ACL which allows us
800 * to delete the file.
801 */
748a9306 802 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
bbce6d69 803 fildsc.dsc$w_length = strlen(rspec);
804 fildsc.dsc$a_pointer = rspec;
a0d0e21e 805 cxt = 0;
748a9306 806 newace.myace$l_ident = oldace.myace$l_ident;
a0d0e21e 807 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
740ce14c 808 switch (aclsts) {
809 case RMS$_FNF:
810 case RMS$_DNF:
811 case RMS$_DIR:
812 case SS$_NOSUCHOBJECT:
813 set_errno(ENOENT); break;
814 case RMS$_DEV:
815 set_errno(ENODEV); break;
816 case RMS$_SYN:
817 case SS$_INVFILFOROP:
818 set_errno(EINVAL); break;
819 case RMS$_PRV:
820 set_errno(EACCES); break;
821 default:
822 _ckvmssts(aclsts);
823 }
748a9306 824 set_vaxc_errno(aclsts);
a0d0e21e
LW
825 return -1;
826 }
827 /* Grab any existing ACEs with this identifier in case we fail */
828 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
e518068a 829 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
830 || fndsts == SS$_NOMOREACE ) {
a0d0e21e
LW
831 /* Add the new ACE . . . */
832 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
833 goto yourroom;
748a9306 834 if ((rmsts = remove(name))) {
a0d0e21e
LW
835 /* We blew it - dir with files in it, no write priv for
836 * parent directory, etc. Put things back the way they were. */
837 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
838 goto yourroom;
839 if (fndsts & 1) {
840 addlst[0].bufadr = &oldace;
841 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
842 goto yourroom;
843 }
844 }
845 }
846
847 yourroom:
b7ae7a0d 848 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
849 /* We just deleted it, so of course it's not there. Some versions of
850 * VMS seem to return success on the unlock operation anyhow (after all
851 * the unlock is successful), but others don't.
852 */
760ac839 853 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
b7ae7a0d 854 if (aclsts & 1) aclsts = fndsts;
a0d0e21e 855 if (!(aclsts & 1)) {
748a9306
LW
856 set_errno(EVMSERR);
857 set_vaxc_errno(aclsts);
a0d0e21e
LW
858 return -1;
859 }
860
861 return rmsts;
862
863} /* end of kill_file() */
864/*}}}*/
865
8cc95fdb 866
84902520 867/*{{{int my_mkdir(char *,Mode_t)*/
8cc95fdb 868int
84902520 869my_mkdir(char *dir, Mode_t mode)
8cc95fdb 870{
871 STRLEN dirlen = strlen(dir);
d28f7c37 872 dTHX;
8cc95fdb 873
874 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
875 * null file name/type. However, it's commonplace under Unix,
876 * so we'll allow it for a gain in portability.
877 */
878 if (dir[dirlen-1] == '/') {
879 char *newdir = savepvn(dir,dirlen-1);
880 int ret = mkdir(newdir,mode);
881 Safefree(newdir);
882 return ret;
883 }
884 else return mkdir(dir,mode);
885} /* end of my_mkdir */
886/*}}}*/
887
888
a0d0e21e
LW
889static void
890create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
891{
892 static unsigned long int mbxbufsiz;
893 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
d28f7c37 894 dTHX;
a0d0e21e
LW
895
896 if (!mbxbufsiz) {
897 /*
898 * Get the SYSGEN parameter MAXBUF, and the smaller of it and the
899 * preprocessor consant BUFSIZ from stdio.h as the size of the
900 * 'pipe' mailbox.
901 */
748a9306 902 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
a0d0e21e
LW
903 if (mbxbufsiz > BUFSIZ) mbxbufsiz = BUFSIZ;
904 }
748a9306 905 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
a0d0e21e 906
748a9306 907 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
a0d0e21e
LW
908 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
909
910} /* end of create_mbx() */
911
912/*{{{ my_popen and my_pclose*/
913struct pipe_details
914{
915 struct pipe_details *next;
740ce14c 916 PerlIO *fp; /* stdio file pointer to pipe mailbox */
748a9306
LW
917 int pid; /* PID of subprocess */
918 int mode; /* == 'r' if pipe open for reading */
919 int done; /* subprocess has completed */
920 unsigned long int completion; /* termination status of subprocess */
a0d0e21e
LW
921};
922
748a9306
LW
923struct exit_control_block
924{
925 struct exit_control_block *flink;
926 unsigned long int (*exit_routine)();
927 unsigned long int arg_count;
928 unsigned long int *status_address;
929 unsigned long int exit_status;
930};
931
a0d0e21e
LW
932static struct pipe_details *open_pipes = NULL;
933static $DESCRIPTOR(nl_desc, "NL:");
934static int waitpid_asleep = 0;
935
3eeba6fb
CB
936/* Send an EOF to a mbx. N.B. We don't check that fp actually points
937 * to a mbx; that's the caller's responsibility.
938 */
939static unsigned long int
644a2880 940pipe_eof(FILE *fp, int immediate)
3eeba6fb
CB
941{
942 char devnam[NAM$C_MAXRSS+1], *cp;
943 unsigned long int chan, iosb[2], retsts, retsts2;
944 struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
d28f7c37 945 dTHX;
3eeba6fb
CB
946
947 if (fgetname(fp,devnam,1)) {
948 /* It oughta be a mailbox, so fgetname should give just the device
949 * name, but just in case . . . */
950 if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
951 devdsc.dsc$w_length = strlen(devnam);
952 _ckvmssts(sys$assign(&devdsc,&chan,0,0));
644a2880
JH
953 retsts = sys$qiow(0,chan,IO$_WRITEOF|(immediate?IO$M_NOW|IO$M_NORSWAIT:0),
954 iosb,0,0,0,0,0,0,0,0);
3eeba6fb
CB
955 if (retsts & 1) retsts = iosb[0];
956 retsts2 = sys$dassgn(chan); /* Be sure to deassign the channel */
957 if (retsts & 1) retsts = retsts2;
958 _ckvmssts(retsts);
959 return retsts;
960 }
961 else _ckvmssts(vaxc$errno); /* Should never happen */
962 return (unsigned long int) vaxc$errno;
963}
964
748a9306
LW
965static unsigned long int
966pipe_exit_routine()
967{
3eeba6fb 968 struct pipe_details *info;
1e422769 969 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3eeba6fb 970 int sts, did_stuff;
d28f7c37 971 dTHX;
3eeba6fb
CB
972
973 /*
974 first we try sending an EOF...ignore if doesn't work, make sure we
975 don't hang
976 */
977 did_stuff = 0;
978 info = open_pipes;
748a9306 979
3eeba6fb
CB
980 while (info) {
981 if (info->mode != 'r' && !info->done) {
644a2880 982 if (pipe_eof(info->fp, 1) & 1) did_stuff = 1;
748a9306 983 }
3eeba6fb
CB
984 info = info->next;
985 }
986 if (did_stuff) sleep(1); /* wait for EOF to have an effect */
987
988 did_stuff = 0;
989 info = open_pipes;
990 while (info) {
991 if (!info->done) { /* Tap them gently on the shoulder . . .*/
992 sts = sys$forcex(&info->pid,0,&abort);
993 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
994 did_stuff = 1;
995 }
996 info = info->next;
997 }
998 if (did_stuff) sleep(1); /* wait for them to respond */
999
1000 info = open_pipes;
1001 while (info) {
1002 if (!info->done) { /* We tried to be nice . . . */
1003 sts = sys$delprc(&info->pid,0);
1004 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1005 info->done = 1; /* so my_pclose doesn't try to write EOF */
1006 }
1007 info = info->next;
1008 }
1009
1010 while(open_pipes) {
1e422769 1011 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1012 else if (!(sts & 1)) retsts = sts;
748a9306
LW
1013 }
1014 return retsts;
1015}
1016
1017static struct exit_control_block pipe_exitblock =
1018 {(struct exit_control_block *) 0,
1019 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1020
1021
a0d0e21e 1022static void
748a9306 1023popen_completion_ast(struct pipe_details *thispipe)
a0d0e21e 1024{
748a9306 1025 thispipe->done = TRUE;
a0d0e21e
LW
1026 if (waitpid_asleep) {
1027 waitpid_asleep = 0;
1028 sys$wake(0,0);
1029 }
1030}
1031
8fde5078 1032static PerlIO *
1e422769 1033safe_popen(char *cmd, char *mode)
a0d0e21e 1034{
748a9306 1035 static int handler_set_up = FALSE;
a0d0e21e
LW
1036 char mbxname[64];
1037 unsigned short int chan;
1038 unsigned long int flags=1; /* nowait - gnu c doesn't allow &1 */
d28f7c37 1039 dTHX;
a0d0e21e
LW
1040 struct pipe_details *info;
1041 struct dsc$descriptor_s namdsc = {sizeof mbxname, DSC$K_DTYPE_T,
1042 DSC$K_CLASS_S, mbxname},
1043 cmddsc = {0, DSC$K_DTYPE_T,
1044 DSC$K_CLASS_S, 0};
1045
1046
a3e9d8c9 1047 cmddsc.dsc$w_length=strlen(cmd);
1048 cmddsc.dsc$a_pointer=cmd;
1049 if (cmddsc.dsc$w_length > 255) {
1050 set_errno(E2BIG); set_vaxc_errno(CLI$_BUFOVF);
1051 return Nullfp;
1052 }
1053
fc36a67e 1054 New(1301,info,1,struct pipe_details);
a0d0e21e 1055
a0d0e21e
LW
1056 /* create mailbox */
1057 create_mbx(&chan,&namdsc);
1058
1059 /* open a FILE* onto it */
740ce14c 1060 info->fp = PerlIO_open(mbxname, mode);
a0d0e21e
LW
1061
1062 /* give up other channel onto it */
748a9306 1063 _ckvmssts(sys$dassgn(chan));
a0d0e21e
LW
1064
1065 if (!info->fp)
1066 return Nullfp;
1067
748a9306
LW
1068 info->mode = *mode;
1069 info->done = FALSE;
1070 info->completion=0;
1071
1072 if (*mode == 'r') {
1073 _ckvmssts(lib$spawn(&cmddsc, &nl_desc, &namdsc, &flags,
a0d0e21e 1074 0 /* name */, &info->pid, &info->completion,
748a9306 1075 0, popen_completion_ast,info,0,0,0));
a0d0e21e
LW
1076 }
1077 else {
748a9306
LW
1078 _ckvmssts(lib$spawn(&cmddsc, &namdsc, 0 /* sys$output */, &flags,
1079 0 /* name */, &info->pid, &info->completion,
1080 0, popen_completion_ast,info,0,0,0));
a0d0e21e
LW
1081 }
1082
748a9306
LW
1083 if (!handler_set_up) {
1084 _ckvmssts(sys$dclexh(&pipe_exitblock));
1085 handler_set_up = TRUE;
1086 }
a0d0e21e
LW
1087 info->next=open_pipes; /* prepend to list */
1088 open_pipes=info;
1089
6b88bc9c 1090 PL_forkprocess = info->pid;
a0d0e21e 1091 return info->fp;
1e422769 1092} /* end of safe_popen */
1093
1094
1095/*{{{ FILE *my_popen(char *cmd, char *mode)*/
1096FILE *
d28f7c37 1097Perl_my_popen(pTHX_ char *cmd, char *mode)
1e422769 1098{
1099 TAINT_ENV();
1100 TAINT_PROPER("popen");
45bc9206 1101 PERL_FLUSHALL_FOR_CHILD;
1e422769 1102 return safe_popen(cmd,mode);
a0d0e21e 1103}
1e422769 1104
a0d0e21e
LW
1105/*}}}*/
1106
1107/*{{{ I32 my_pclose(FILE *fp)*/
d28f7c37 1108I32 Perl_my_pclose(pTHX_ FILE *fp)
a0d0e21e
LW
1109{
1110 struct pipe_details *info, *last = NULL;
748a9306 1111 unsigned long int retsts;
a0d0e21e
LW
1112
1113 for (info = open_pipes; info != NULL; last = info, info = info->next)
1114 if (info->fp == fp) break;
1115
1e422769 1116 if (info == NULL) { /* no such pipe open */
1117 set_errno(ECHILD); /* quoth POSIX */
1118 set_vaxc_errno(SS$_NONEXPR);
1119 return -1;
1120 }
748a9306 1121
bbce6d69 1122 /* If we were writing to a subprocess, insure that someone reading from
1123 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
1124 * produce an EOF record in the mailbox. */
644a2880 1125 if (info->mode != 'r' && !info->done) pipe_eof(info->fp,0);
740ce14c 1126 PerlIO_close(info->fp);
c07a80fd 1127
748a9306
LW
1128 if (info->done) retsts = info->completion;
1129 else waitpid(info->pid,(int *) &retsts,0);
a0d0e21e 1130
a0d0e21e
LW
1131 /* remove from list of open pipes */
1132 if (last) last->next = info->next;
1133 else open_pipes = info->next;
a0d0e21e
LW
1134 Safefree(info);
1135
1136 return retsts;
748a9306 1137
a0d0e21e
LW
1138} /* end of my_pclose() */
1139
a0d0e21e 1140/* sort-of waitpid; use only with popen() */
4fdae800 1141/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
1142Pid_t
1143my_waitpid(Pid_t pid, int *statusp, int flags)
a0d0e21e
LW
1144{
1145 struct pipe_details *info;
d28f7c37 1146 dTHX;
a0d0e21e
LW
1147
1148 for (info = open_pipes; info != NULL; info = info->next)
1149 if (info->pid == pid) break;
1150
1151 if (info != NULL) { /* we know about this child */
748a9306 1152 while (!info->done) {
a0d0e21e
LW
1153 waitpid_asleep = 1;
1154 sys$hiber();
1155 }
1156
1157 *statusp = info->completion;
1158 return pid;
1159 }
1160 else { /* we haven't heard of this child */
1161 $DESCRIPTOR(intdsc,"0 00:00:01");
1162 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
748a9306 1163 unsigned long int interval[2],sts;
a0d0e21e 1164
3eeba6fb 1165 if (ckWARN(WARN_EXEC)) {
748a9306
LW
1166 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
1167 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
1168 if (ownerpid != mypid)
d28f7c37 1169 Perl_warner(aTHX_ WARN_EXEC,"pid %x not a child",pid);
748a9306 1170 }
a0d0e21e 1171
748a9306 1172 _ckvmssts(sys$bintim(&intdsc,interval));
a0d0e21e 1173 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
748a9306
LW
1174 _ckvmssts(sys$schdwk(0,0,interval,0));
1175 _ckvmssts(sys$hiber());
a0d0e21e 1176 }
748a9306 1177 _ckvmssts(sts);
a0d0e21e
LW
1178
1179 /* There's no easy way to find the termination status a child we're
1180 * not aware of beforehand. If we're really interested in the future,
1181 * we can go looking for a termination mailbox, or chase after the
1182 * accounting record for the process.
1183 */
1184 *statusp = 0;
1185 return pid;
1186 }
1187
1188} /* end of waitpid() */
a0d0e21e
LW
1189/*}}}*/
1190/*}}}*/
1191/*}}}*/
1192
1193/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
1194char *
1195my_gconvert(double val, int ndig, int trail, char *buf)
1196{
1197 static char __gcvtbuf[DBL_DIG+1];
1198 char *loc;
1199
1200 loc = buf ? buf : __gcvtbuf;
71be2cbc 1201
1202#ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
1203 if (val < 1) {
1204 sprintf(loc,"%.*g",ndig,val);
1205 return loc;
1206 }
1207#endif
1208
a0d0e21e
LW
1209 if (val) {
1210 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
1211 return gcvt(val,ndig,loc);
1212 }
1213 else {
1214 loc[0] = '0'; loc[1] = '\0';
1215 return loc;
1216 }
1217
1218}
1219/*}}}*/
1220
bbce6d69 1221
1222/*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
1223/* Shortcut for common case of simple calls to $PARSE and $SEARCH
1224 * to expand file specification. Allows for a single default file
1225 * specification and a simple mask of options. If outbuf is non-NULL,
1226 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
1227 * the resultant file specification is placed. If outbuf is NULL, the
1228 * resultant file specification is placed into a static buffer.
1229 * The third argument, if non-NULL, is taken to be a default file
1230 * specification string. The fourth argument is unused at present.
1231 * rmesexpand() returns the address of the resultant string if
1232 * successful, and NULL on error.
1233 */
96e4d5b1 1234static char *do_tounixspec(char *, char *, int);
1235
bbce6d69 1236static char *
1237do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
1238{
1239 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
96e4d5b1 1240 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
bbce6d69 1241 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
1242 struct FAB myfab = cc$rms_fab;
1243 struct NAM mynam = cc$rms_nam;
1244 STRLEN speclen;
3eeba6fb 1245 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
bbce6d69 1246
1247 if (!filespec || !*filespec) {
1248 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
1249 return NULL;
1250 }
1251 if (!outbuf) {
fc36a67e 1252 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
bbce6d69 1253 else outbuf = __rmsexpand_retbuf;
1254 }
96e4d5b1 1255 if ((isunix = (strchr(filespec,'/') != NULL))) {
1256 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
1257 filespec = vmsfspec;
1258 }
bbce6d69 1259
1260 myfab.fab$l_fna = filespec;
1261 myfab.fab$b_fns = strlen(filespec);
1262 myfab.fab$l_nam = &mynam;
1263
1264 if (defspec && *defspec) {
96e4d5b1 1265 if (strchr(defspec,'/') != NULL) {
1266 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
1267 defspec = tmpfspec;
1268 }
bbce6d69 1269 myfab.fab$l_dna = defspec;
1270 myfab.fab$b_dns = strlen(defspec);
1271 }
1272
1273 mynam.nam$l_esa = esa;
1274 mynam.nam$b_ess = sizeof esa;
1275 mynam.nam$l_rsa = outbuf;
1276 mynam.nam$b_rss = NAM$C_MAXRSS;
1277
1278 retsts = sys$parse(&myfab,0,0);
1279 if (!(retsts & 1)) {
17f28c40 1280 mynam.nam$b_nop |= NAM$M_SYNCHK;
bbce6d69 1281 if (retsts == RMS$_DNF || retsts == RMS$_DIR ||
1282 retsts == RMS$_DEV || retsts == RMS$_DEV) {
bbce6d69 1283 retsts = sys$parse(&myfab,0,0);
1284 if (retsts & 1) goto expanded;
1285 }
17f28c40
CB
1286 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
1287 (void) sys$parse(&myfab,0,0); /* Free search context */
bbce6d69 1288 if (out) Safefree(out);
1289 set_vaxc_errno(retsts);
1290 if (retsts == RMS$_PRV) set_errno(EACCES);
1291 else if (retsts == RMS$_DEV) set_errno(ENODEV);
1292 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
1293 else set_errno(EVMSERR);
1294 return NULL;
1295 }
1296 retsts = sys$search(&myfab,0,0);
1297 if (!(retsts & 1) && retsts != RMS$_FNF) {
17f28c40
CB
1298 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
1299 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
bbce6d69 1300 if (out) Safefree(out);
1301 set_vaxc_errno(retsts);
1302 if (retsts == RMS$_PRV) set_errno(EACCES);
1303 else set_errno(EVMSERR);
1304 return NULL;
1305 }
1306
1307 /* If the input filespec contained any lowercase characters,
1308 * downcase the result for compatibility with Unix-minded code. */
1309 expanded:
1310 for (out = myfab.fab$l_fna; *out; out++)
1311 if (islower(*out)) { haslower = 1; break; }
1312 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
1313 else { out = esa; speclen = mynam.nam$b_esl; }
3eeba6fb
CB
1314 /* Trim off null fields added by $PARSE
1315 * If type > 1 char, must have been specified in original or default spec
1316 * (not true for version; $SEARCH may have added version of existing file).
1317 */
1318 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
1319 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
1320 (mynam.nam$l_ver - mynam.nam$l_type == 1);
1321 if (trimver || trimtype) {
1322 if (defspec && *defspec) {
1323 char defesa[NAM$C_MAXRSS];
1324 struct FAB deffab = cc$rms_fab;
1325 struct NAM defnam = cc$rms_nam;
1326
1327 deffab.fab$l_nam = &defnam;
1328 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
1329 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
1330 defnam.nam$b_nop = NAM$M_SYNCHK;
1331 if (sys$parse(&deffab,0,0) & 1) {
1332 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
1333 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
1334 }
1335 }
1336 if (trimver) speclen = mynam.nam$l_ver - out;
1337 if (trimtype) {
1338 /* If we didn't already trim version, copy down */
1339 if (speclen > mynam.nam$l_ver - out)
1340 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
1341 speclen - (mynam.nam$l_ver - out));
1342 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
1343 }
1344 }
bbce6d69 1345 /* If we just had a directory spec on input, $PARSE "helpfully"
1346 * adds an empty name and type for us */
1347 if (mynam.nam$l_name == mynam.nam$l_type &&
1348 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
1349 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
1350 speclen = mynam.nam$l_name - out;
1351 out[speclen] = '\0';
1352 if (haslower) __mystrtolower(out);
1353
1354 /* Have we been working with an expanded, but not resultant, spec? */
96e4d5b1 1355 /* Also, convert back to Unix syntax if necessary. */
1356 if (!mynam.nam$b_rsl) {
1357 if (isunix) {
1358 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
1359 }
1360 else strcpy(outbuf,esa);
1361 }
1362 else if (isunix) {
1363 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
1364 strcpy(outbuf,tmpfspec);
1365 }
17f28c40
CB
1366 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
1367 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
1368 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
bbce6d69 1369 return outbuf;
1370}
1371/*}}}*/
1372/* External entry points */
1373char *rmsexpand(char *spec, char *buf, char *def, unsigned opt)
1374{ return do_rmsexpand(spec,buf,0,def,opt); }
1375char *rmsexpand_ts(char *spec, char *buf, char *def, unsigned opt)
1376{ return do_rmsexpand(spec,buf,1,def,opt); }
1377
1378
a0d0e21e
LW
1379/*
1380** The following routines are provided to make life easier when
1381** converting among VMS-style and Unix-style directory specifications.
1382** All will take input specifications in either VMS or Unix syntax. On
1383** failure, all return NULL. If successful, the routines listed below
748a9306 1384** return a pointer to a buffer containing the appropriately
a0d0e21e
LW
1385** reformatted spec (and, therefore, subsequent calls to that routine
1386** will clobber the result), while the routines of the same names with
1387** a _ts suffix appended will return a pointer to a mallocd string
1388** containing the appropriately reformatted spec.
1389** In all cases, only explicit syntax is altered; no check is made that
1390** the resulting string is valid or that the directory in question
1391** actually exists.
1392**
1393** fileify_dirspec() - convert a directory spec into the name of the
1394** directory file (i.e. what you can stat() to see if it's a dir).
1395** The style (VMS or Unix) of the result is the same as the style
1396** of the parameter passed in.
1397** pathify_dirspec() - convert a directory spec into a path (i.e.
1398** what you prepend to a filename to indicate what directory it's in).
1399** The style (VMS or Unix) of the result is the same as the style
1400** of the parameter passed in.
1401** tounixpath() - convert a directory spec into a Unix-style path.
1402** tovmspath() - convert a directory spec into a VMS-style path.
1403** tounixspec() - convert any file spec into a Unix-style file spec.
1404** tovmsspec() - convert any file spec into a VMS-style spec.
e518068a 1405**
bd3fa61c 1406** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
01b8edb6 1407** Permission is given to distribute this code as part of the Perl
1408** standard distribution under the terms of the GNU General Public
1409** License or the Perl Artistic License. Copies of each may be
1410** found in the Perl standard distribution.
a0d0e21e
LW
1411 */
1412
1413/*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
1414static char *do_fileify_dirspec(char *dir,char *buf,int ts)
1415{
1416 static char __fileify_retbuf[NAM$C_MAXRSS+1];
b7ae7a0d 1417 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
a0d0e21e 1418 char *retspec, *cp1, *cp2, *lastdir;
61bb5906 1419 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
a0d0e21e 1420
c07a80fd 1421 if (!dir || !*dir) {
1422 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1423 }
a0d0e21e 1424 dirlen = strlen(dir);
61bb5906
CB
1425 while (dir[dirlen-1] == '/') --dirlen;
1426 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
1427 strcpy(trndir,"/sys$disk/000000");
1428 dir = trndir;
1429 dirlen = 16;
1430 }
1431 if (dirlen > NAM$C_MAXRSS) {
1432 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
c07a80fd 1433 }
e518068a 1434 if (!strpbrk(dir+1,"/]>:")) {
1435 strcpy(trndir,*dir == '/' ? dir + 1: dir);
c07a80fd 1436 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
e518068a 1437 dir = trndir;
1438 dirlen = strlen(dir);
1439 }
01b8edb6 1440 else {
1441 strncpy(trndir,dir,dirlen);
1442 trndir[dirlen] = '\0';
1443 dir = trndir;
1444 }
c07a80fd 1445 /* If we were handed a rooted logical name or spec, treat it like a
1446 * simple directory, so that
1447 * $ Define myroot dev:[dir.]
1448 * ... do_fileify_dirspec("myroot",buf,1) ...
1449 * does something useful.
1450 */
1451 if (!strcmp(dir+dirlen-2,".]")) {
1452 dir[--dirlen] = '\0';
1453 dir[dirlen-1] = ']';
1454 }
e518068a 1455
b7ae7a0d 1456 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
1457 /* If we've got an explicit filename, we can just shuffle the string. */
1458 if (*(cp1+1)) hasfilename = 1;
1459 /* Similarly, we can just back up a level if we've got multiple levels
1460 of explicit directories in a VMS spec which ends with directories. */
1461 else {
1462 for (cp2 = cp1; cp2 > dir; cp2--) {
1463 if (*cp2 == '.') {
1464 *cp2 = *cp1; *cp1 = '\0';
1465 hasfilename = 1;
1466 break;
1467 }
1468 if (*cp2 == '[' || *cp2 == '<') break;
1469 }
1470 }
1471 }
1472
1473 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
748a9306
LW
1474 if (dir[0] == '.') {
1475 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
1476 return do_fileify_dirspec("[]",buf,ts);
1477 else if (dir[1] == '.' &&
1478 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
1479 return do_fileify_dirspec("[-]",buf,ts);
1480 }
a0d0e21e
LW
1481 if (dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
1482 dirlen -= 1; /* to last element */
1483 lastdir = strrchr(dir,'/');
1484 }
01b8edb6 1485 else if ((cp1 = strstr(dir,"/.")) != NULL) {
1486 /* If we have "/." or "/..", VMSify it and let the VMS code
1487 * below expand it, rather than repeating the code to handle
1488 * relative components of a filespec here */
4633a7c4
LW
1489 do {
1490 if (*(cp1+2) == '.') cp1++;
1491 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
01b8edb6 1492 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
fc1ce8cc
CB
1493 if (strchr(vmsdir,'/') != NULL) {
1494 /* If do_tovmsspec() returned it, it must have VMS syntax
1495 * delimiters in it, so it's a mixed VMS/Unix spec. We take
1496 * the time to check this here only so we avoid a recursion
1497 * loop; otherwise, gigo.
1498 */
1499 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
1500 }
01b8edb6 1501 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1502 return do_tounixspec(trndir,buf,ts);
4633a7c4
LW
1503 }
1504 cp1++;
1505 } while ((cp1 = strstr(cp1,"/.")) != NULL);
17f28c40 1506 lastdir = strrchr(dir,'/');
748a9306 1507 }
61bb5906
CB
1508 else if (!strcmp(&dir[dirlen-7],"/000000")) {
1509 /* Ditto for specs that end in an MFD -- let the VMS code
1510 * figure out whether it's a real device or a rooted logical. */
1511 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
1512 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1513 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1514 return do_tounixspec(trndir,buf,ts);
1515 }
a0d0e21e 1516 else {
b7ae7a0d 1517 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
1518 !(lastdir = cp1 = strrchr(dir,']')) &&
1519 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
a0d0e21e 1520 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
b7ae7a0d 1521 int ver; char *cp3;
1522 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1523 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1524 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1525 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1526 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1527 (ver || *cp3)))))) {
1528 set_errno(ENOTDIR);
748a9306 1529 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
1530 return NULL;
1531 }
b7ae7a0d 1532 dirlen = cp2 - dir;
a0d0e21e 1533 }
748a9306
LW
1534 }
1535 /* If we lead off with a device or rooted logical, add the MFD
1536 if we're specifying a top-level directory. */
1537 if (lastdir && *dir == '/') {
1538 addmfd = 1;
1539 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
1540 if (*cp1 == '/') {
1541 addmfd = 0;
1542 break;
a0d0e21e
LW
1543 }
1544 }
748a9306 1545 }
4633a7c4 1546 retlen = dirlen + (addmfd ? 13 : 6);
748a9306 1547 if (buf) retspec = buf;
fc36a67e 1548 else if (ts) New(1309,retspec,retlen+1,char);
748a9306
LW
1549 else retspec = __fileify_retbuf;
1550 if (addmfd) {
1551 dirlen = lastdir - dir;
1552 memcpy(retspec,dir,dirlen);
1553 strcpy(&retspec[dirlen],"/000000");
1554 strcpy(&retspec[dirlen+7],lastdir);
1555 }
1556 else {
1557 memcpy(retspec,dir,dirlen);
1558 retspec[dirlen] = '\0';
a0d0e21e
LW
1559 }
1560 /* We've picked up everything up to the directory file name.
1561 Now just add the type and version, and we're set. */
1562 strcat(retspec,".dir;1");
1563 return retspec;
1564 }
1565 else { /* VMS-style directory spec */
01b8edb6 1566 char esa[NAM$C_MAXRSS+1], term, *cp;
1567 unsigned long int sts, cmplen, haslower = 0;
a0d0e21e
LW
1568 struct FAB dirfab = cc$rms_fab;
1569 struct NAM savnam, dirnam = cc$rms_nam;
1570
1571 dirfab.fab$b_fns = strlen(dir);
1572 dirfab.fab$l_fna = dir;
1573 dirfab.fab$l_nam = &dirnam;
748a9306
LW
1574 dirfab.fab$l_dna = ".DIR;1";
1575 dirfab.fab$b_dns = 6;
a0d0e21e
LW
1576 dirnam.nam$b_ess = NAM$C_MAXRSS;
1577 dirnam.nam$l_esa = esa;
01b8edb6 1578
1579 for (cp = dir; *cp; cp++)
1580 if (islower(*cp)) { haslower = 1; break; }
e518068a 1581 if (!((sts = sys$parse(&dirfab))&1)) {
1582 if (dirfab.fab$l_sts == RMS$_DIR) {
1583 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1584 sts = sys$parse(&dirfab) & 1;
1585 }
1586 if (!sts) {
748a9306
LW
1587 set_errno(EVMSERR);
1588 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
1589 return NULL;
1590 }
e518068a 1591 }
1592 else {
1593 savnam = dirnam;
1594 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
1595 /* Yes; fake the fnb bits so we'll check type below */
1596 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
1597 }
1598 else {
1599 if (dirfab.fab$l_sts != RMS$_FNF) {
1600 set_errno(EVMSERR);
1601 set_vaxc_errno(dirfab.fab$l_sts);
1602 return NULL;
1603 }
1604 dirnam = savnam; /* No; just work with potential name */
1605 }
a0d0e21e 1606 }
748a9306
LW
1607 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
1608 cp1 = strchr(esa,']');
1609 if (!cp1) cp1 = strchr(esa,'>');
1610 if (cp1) { /* Should always be true */
1611 dirnam.nam$b_esl -= cp1 - esa - 1;
1612 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
1613 }
1614 }
a0d0e21e
LW
1615 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1616 /* Yep; check version while we're at it, if it's there. */
1617 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1618 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1619 /* Something other than .DIR[;1]. Bzzt. */
748a9306
LW
1620 set_errno(ENOTDIR);
1621 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
1622 return NULL;
1623 }
748a9306
LW
1624 }
1625 esa[dirnam.nam$b_esl] = '\0';
1626 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
1627 /* They provided at least the name; we added the type, if necessary, */
1628 if (buf) retspec = buf; /* in sys$parse() */
fc36a67e 1629 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
748a9306
LW
1630 else retspec = __fileify_retbuf;
1631 strcpy(retspec,esa);
1632 return retspec;
1633 }
c07a80fd 1634 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
1635 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
1636 *cp1 = '\0';
1637 dirnam.nam$b_esl -= 9;
1638 }
748a9306
LW
1639 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
1640 if (cp1 == NULL) return NULL; /* should never happen */
1641 term = *cp1;
1642 *cp1 = '\0';
1643 retlen = strlen(esa);
1644 if ((cp1 = strrchr(esa,'.')) != NULL) {
1645 /* There's more than one directory in the path. Just roll back. */
1646 *cp1 = term;
1647 if (buf) retspec = buf;
fc36a67e 1648 else if (ts) New(1311,retspec,retlen+7,char);
748a9306
LW
1649 else retspec = __fileify_retbuf;
1650 strcpy(retspec,esa);
a0d0e21e
LW
1651 }
1652 else {
748a9306
LW
1653 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
1654 /* Go back and expand rooted logical name */
1655 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
1656 if (!(sys$parse(&dirfab) & 1)) {
1657 set_errno(EVMSERR);
1658 set_vaxc_errno(dirfab.fab$l_sts);
1659 return NULL;
1660 }
1661 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
a0d0e21e 1662 if (buf) retspec = buf;
fc36a67e 1663 else if (ts) New(1312,retspec,retlen+16,char);
a0d0e21e 1664 else retspec = __fileify_retbuf;
748a9306
LW
1665 cp1 = strstr(esa,"][");
1666 dirlen = cp1 - esa;
1667 memcpy(retspec,esa,dirlen);
1668 if (!strncmp(cp1+2,"000000]",7)) {
1669 retspec[dirlen-1] = '\0';
4633a7c4
LW
1670 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1671 if (*cp1 == '.') *cp1 = ']';
1672 else {
1673 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1674 memcpy(cp1+1,"000000]",7);
1675 }
748a9306
LW
1676 }
1677 else {
1678 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
1679 retspec[retlen] = '\0';
1680 /* Convert last '.' to ']' */
4633a7c4
LW
1681 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1682 if (*cp1 == '.') *cp1 = ']';
1683 else {
1684 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1685 memcpy(cp1+1,"000000]",7);
1686 }
748a9306 1687 }
a0d0e21e 1688 }
748a9306 1689 else { /* This is a top-level dir. Add the MFD to the path. */
a0d0e21e 1690 if (buf) retspec = buf;
fc36a67e 1691 else if (ts) New(1312,retspec,retlen+16,char);
a0d0e21e
LW
1692 else retspec = __fileify_retbuf;
1693 cp1 = esa;
1694 cp2 = retspec;
1695 while (*cp1 != ':') *(cp2++) = *(cp1++);
1696 strcpy(cp2,":[000000]");
1697 cp1 += 2;
1698 strcpy(cp2+9,cp1);
1699 }
748a9306
LW
1700 }
1701 /* We've set up the string up through the filename. Add the
a0d0e21e
LW
1702 type and version, and we're done. */
1703 strcat(retspec,".DIR;1");
01b8edb6 1704
1705 /* $PARSE may have upcased filespec, so convert output to lower
1706 * case if input contained any lowercase characters. */
1707 if (haslower) __mystrtolower(retspec);
a0d0e21e
LW
1708 return retspec;
1709 }
1710} /* end of do_fileify_dirspec() */
1711/*}}}*/
1712/* External entry points */
1713char *fileify_dirspec(char *dir, char *buf)
1714{ return do_fileify_dirspec(dir,buf,0); }
1715char *fileify_dirspec_ts(char *dir, char *buf)
1716{ return do_fileify_dirspec(dir,buf,1); }
1717
1718/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
1719static char *do_pathify_dirspec(char *dir,char *buf, int ts)
1720{
1721 static char __pathify_retbuf[NAM$C_MAXRSS+1];
1722 unsigned long int retlen;
748a9306 1723 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
a0d0e21e 1724
c07a80fd 1725 if (!dir || !*dir) {
1726 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1727 }
1728
1729 if (*dir) strcpy(trndir,dir);
1730 else getcwd(trndir,sizeof trndir - 1);
1731
ebd8c45c
DS
1732 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
1733 && my_trnlnm(trndir,trndir,0)) {
c07a80fd 1734 STRLEN trnlen = strlen(trndir);
a0d0e21e 1735
c07a80fd 1736 /* Trap simple rooted lnms, and return lnm:[000000] */
1737 if (!strcmp(trndir+trnlen-2,".]")) {
1738 if (buf) retpath = buf;
fc36a67e 1739 else if (ts) New(1318,retpath,strlen(dir)+10,char);
c07a80fd 1740 else retpath = __pathify_retbuf;
1741 strcpy(retpath,dir);
1742 strcat(retpath,":[000000]");
1743 return retpath;
1744 }
1745 }
748a9306
LW
1746 dir = trndir;
1747
b7ae7a0d 1748 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
748a9306
LW
1749 if (*dir == '.' && (*(dir+1) == '\0' ||
1750 (*(dir+1) == '.' && *(dir+2) == '\0')))
1751 retlen = 2 + (*(dir+1) != '\0');
1752 else {
b7ae7a0d 1753 if ( !(cp1 = strrchr(dir,'/')) &&
1754 !(cp1 = strrchr(dir,']')) &&
1755 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
f86702cc 1756 if ((cp2 = strchr(cp1,'.')) != NULL &&
1757 (*(cp2-1) != '/' || /* Trailing '.', '..', */
1758 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
1759 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
1760 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
b7ae7a0d 1761 int ver; char *cp3;
1762 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1763 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1764 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1765 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1766 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1767 (ver || *cp3)))))) {
748a9306
LW
1768 set_errno(ENOTDIR);
1769 set_vaxc_errno(RMS$_DIR);
1770 return NULL;
1771 }
b7ae7a0d 1772 retlen = cp2 - dir + 1;
a0d0e21e 1773 }
748a9306
LW
1774 else { /* No file type present. Treat the filename as a directory. */
1775 retlen = strlen(dir) + 1;
a0d0e21e
LW
1776 }
1777 }
a0d0e21e 1778 if (buf) retpath = buf;
fc36a67e 1779 else if (ts) New(1313,retpath,retlen+1,char);
a0d0e21e
LW
1780 else retpath = __pathify_retbuf;
1781 strncpy(retpath,dir,retlen-1);
1782 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
1783 retpath[retlen-1] = '/'; /* with '/', add it. */
1784 retpath[retlen] = '\0';
1785 }
1786 else retpath[retlen-1] = '\0';
1787 }
1788 else { /* VMS-style directory spec */
01b8edb6 1789 char esa[NAM$C_MAXRSS+1], *cp;
1790 unsigned long int sts, cmplen, haslower;
a0d0e21e
LW
1791 struct FAB dirfab = cc$rms_fab;
1792 struct NAM savnam, dirnam = cc$rms_nam;
1793
b7ae7a0d 1794 /* If we've got an explicit filename, we can just shuffle the string. */
1795 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
1796 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
1797 if ((cp2 = strchr(cp1,'.')) != NULL) {
1798 int ver; char *cp3;
1799 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1800 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1801 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1802 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1803 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1804 (ver || *cp3)))))) {
1805 set_errno(ENOTDIR);
1806 set_vaxc_errno(RMS$_DIR);
1807 return NULL;
1808 }
1809 }
1810 else { /* No file type, so just draw name into directory part */
1811 for (cp2 = cp1; *cp2; cp2++) ;
1812 }
1813 *cp2 = *cp1;
1814 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
1815 *cp1 = '.';
1816 /* We've now got a VMS 'path'; fall through */
1817 }
a0d0e21e
LW
1818 dirfab.fab$b_fns = strlen(dir);
1819 dirfab.fab$l_fna = dir;
748a9306
LW
1820 if (dir[dirfab.fab$b_fns-1] == ']' ||
1821 dir[dirfab.fab$b_fns-1] == '>' ||
1822 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
1823 if (buf) retpath = buf;
fc36a67e 1824 else if (ts) New(1314,retpath,strlen(dir)+1,char);
748a9306
LW
1825 else retpath = __pathify_retbuf;
1826 strcpy(retpath,dir);
1827 return retpath;
1828 }
1829 dirfab.fab$l_dna = ".DIR;1";
1830 dirfab.fab$b_dns = 6;
a0d0e21e 1831 dirfab.fab$l_nam = &dirnam;
e518068a 1832 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
a0d0e21e 1833 dirnam.nam$l_esa = esa;
01b8edb6 1834
1835 for (cp = dir; *cp; cp++)
1836 if (islower(*cp)) { haslower = 1; break; }
1837
1838 if (!(sts = (sys$parse(&dirfab)&1))) {
e518068a 1839 if (dirfab.fab$l_sts == RMS$_DIR) {
1840 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1841 sts = sys$parse(&dirfab) & 1;
1842 }
1843 if (!sts) {
748a9306
LW
1844 set_errno(EVMSERR);
1845 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
1846 return NULL;
1847 }
a0d0e21e 1848 }
e518068a 1849 else {
1850 savnam = dirnam;
1851 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
1852 if (dirfab.fab$l_sts != RMS$_FNF) {
1853 set_errno(EVMSERR);
1854 set_vaxc_errno(dirfab.fab$l_sts);
1855 return NULL;
1856 }
1857 dirnam = savnam; /* No; just work with potential name */
1858 }
1859 }
a0d0e21e
LW
1860 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1861 /* Yep; check version while we're at it, if it's there. */
1862 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1863 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1864 /* Something other than .DIR[;1]. Bzzt. */
748a9306
LW
1865 set_errno(ENOTDIR);
1866 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
1867 return NULL;
1868 }
a0d0e21e 1869 }
748a9306
LW
1870 /* OK, the type was fine. Now pull any file name into the
1871 directory path. */
1872 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
a0d0e21e 1873 else {
748a9306
LW
1874 cp1 = strrchr(esa,'>');
1875 *dirnam.nam$l_type = '>';
a0d0e21e 1876 }
748a9306
LW
1877 *cp1 = '.';
1878 *(dirnam.nam$l_type + 1) = '\0';
1879 retlen = dirnam.nam$l_type - esa + 2;
a0d0e21e 1880 if (buf) retpath = buf;
fc36a67e 1881 else if (ts) New(1314,retpath,retlen,char);
a0d0e21e
LW
1882 else retpath = __pathify_retbuf;
1883 strcpy(retpath,esa);
01b8edb6 1884 /* $PARSE may have upcased filespec, so convert output to lower
1885 * case if input contained any lowercase characters. */
1886 if (haslower) __mystrtolower(retpath);
a0d0e21e
LW
1887 }
1888
1889 return retpath;
1890} /* end of do_pathify_dirspec() */
1891/*}}}*/
1892/* External entry points */
1893char *pathify_dirspec(char *dir, char *buf)
1894{ return do_pathify_dirspec(dir,buf,0); }
1895char *pathify_dirspec_ts(char *dir, char *buf)
1896{ return do_pathify_dirspec(dir,buf,1); }
1897
1898/*{{{ char *tounixspec[_ts](char *path, char *buf)*/
1899static char *do_tounixspec(char *spec, char *buf, int ts)
1900{
1901 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
1902 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
f86702cc 1903 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
a0d0e21e 1904
748a9306 1905 if (spec == NULL) return NULL;
e518068a 1906 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
a0d0e21e 1907 if (buf) rslt = buf;
e518068a 1908 else if (ts) {
1909 retlen = strlen(spec);
1910 cp1 = strchr(spec,'[');
1911 if (!cp1) cp1 = strchr(spec,'<');
1912 if (cp1) {
f86702cc 1913 for (cp1++; *cp1; cp1++) {
1914 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
1915 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
1916 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
1917 }
e518068a 1918 }
fc36a67e 1919 New(1315,rslt,retlen+2+2*expand,char);
e518068a 1920 }
a0d0e21e
LW
1921 else rslt = __tounixspec_retbuf;
1922 if (strchr(spec,'/') != NULL) {
1923 strcpy(rslt,spec);
1924 return rslt;
1925 }
1926
1927 cp1 = rslt;
1928 cp2 = spec;
1929 dirend = strrchr(spec,']');
1930 if (dirend == NULL) dirend = strrchr(spec,'>');
1931 if (dirend == NULL) dirend = strchr(spec,':');
1932 if (dirend == NULL) {
1933 strcpy(rslt,spec);
1934 return rslt;
1935 }
a5f75d66 1936 if (*cp2 != '[' && *cp2 != '<') {
a0d0e21e
LW
1937 *(cp1++) = '/';
1938 }
1939 else { /* the VMS spec begins with directories */
1940 cp2++;
a5f75d66 1941 if (*cp2 == ']' || *cp2 == '>') {
f86702cc 1942 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
a5f75d66
AD
1943 return rslt;
1944 }
f86702cc 1945 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
a0d0e21e
LW
1946 if (getcwd(tmp,sizeof tmp,1) == NULL) {
1947 if (ts) Safefree(rslt);
1948 return NULL;
1949 }
1950 do {
1951 cp3 = tmp;
1952 while (*cp3 != ':' && *cp3) cp3++;
1953 *(cp3++) = '\0';
1954 if (strchr(cp3,']') != NULL) break;
f675dbe5 1955 } while (vmstrnenv(tmp,tmp,0,fildev,0));
f86702cc 1956 if (ts && !buf &&
e518068a 1957 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
e518068a 1958 retlen = devlen + dirlen;
f86702cc 1959 Renew(rslt,retlen+1+2*expand,char);
1960 cp1 = rslt;
1961 }
1962 cp3 = tmp;
1963 *(cp1++) = '/';
1964 while (*cp3) {
1965 *(cp1++) = *(cp3++);
1966 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
a0d0e21e 1967 }
f86702cc 1968 *(cp1++) = '/';
1969 }
1970 else if ( *cp2 == '.') {
1971 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
1972 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
1973 cp2 += 3;
1974 }
1975 else cp2++;
a0d0e21e 1976 }
a0d0e21e
LW
1977 }
1978 for (; cp2 <= dirend; cp2++) {
1979 if (*cp2 == ':') {
1980 *(cp1++) = '/';
1981 if (*(cp2+1) == '[') cp2++;
1982 }
f86702cc 1983 else if (*cp2 == ']' || *cp2 == '>') {
1984 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
1985 }
a0d0e21e
LW
1986 else if (*cp2 == '.') {
1987 *(cp1++) = '/';
e518068a 1988 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
1989 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
1990 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
1991 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
1992 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
1993 }
f86702cc 1994 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
1995 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
1996 cp2 += 2;
1997 }
a0d0e21e
LW
1998 }
1999 else if (*cp2 == '-') {
2000 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
2001 while (*cp2 == '-') {
2002 cp2++;
2003 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
2004 }
2005 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
2006 if (ts) Safefree(rslt); /* filespecs like */
01b8edb6 2007 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
a0d0e21e
LW
2008 return NULL;
2009 }
a0d0e21e
LW
2010 }
2011 else *(cp1++) = *cp2;
2012 }
2013 else *(cp1++) = *cp2;
2014 }
2015 while (*cp2) *(cp1++) = *(cp2++);
2016 *cp1 = '\0';
2017
2018 return rslt;
2019
2020} /* end of do_tounixspec() */
2021/*}}}*/
2022/* External entry points */
2023char *tounixspec(char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
2024char *tounixspec_ts(char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
2025
2026/*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
2027static char *do_tovmsspec(char *path, char *buf, int ts) {
2028 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
e518068a 2029 char *rslt, *dirend;
2030 register char *cp1, *cp2;
2031 unsigned long int infront = 0, hasdir = 1;
a0d0e21e 2032
748a9306 2033 if (path == NULL) return NULL;
a0d0e21e 2034 if (buf) rslt = buf;
fc36a67e 2035 else if (ts) New(1316,rslt,strlen(path)+9,char);
a0d0e21e 2036 else rslt = __tovmsspec_retbuf;
748a9306 2037 if (strpbrk(path,"]:>") ||
a0d0e21e 2038 (dirend = strrchr(path,'/')) == NULL) {
748a9306
LW
2039 if (path[0] == '.') {
2040 if (path[1] == '\0') strcpy(rslt,"[]");
2041 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
2042 else strcpy(rslt,path); /* probably garbage */
2043 }
2044 else strcpy(rslt,path);
a0d0e21e
LW
2045 return rslt;
2046 }
f86702cc 2047 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
748a9306
LW
2048 if (!*(dirend+2)) dirend +=2;
2049 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
f86702cc 2050 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
748a9306 2051 }
a0d0e21e
LW
2052 cp1 = rslt;
2053 cp2 = path;
2054 if (*cp2 == '/') {
e518068a 2055 char trndev[NAM$C_MAXRSS+1];
2056 int islnm, rooted;
2057 STRLEN trnend;
2058
b7ae7a0d 2059 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
61bb5906
CB
2060 if (!*(cp2+1)) {
2061 if (!buf & ts) Renew(rslt,18,char);
2062 strcpy(rslt,"sys$disk:[000000]");
2063 return rslt;
2064 }
a0d0e21e 2065 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
e518068a 2066 *cp1 = '\0';
c07a80fd 2067 islnm = my_trnlnm(rslt,trndev,0);
e518068a 2068 trnend = islnm ? strlen(trndev) - 1 : 0;
2069 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
2070 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
2071 /* If the first element of the path is a logical name, determine
2072 * whether it has to be translated so we can add more directories. */
2073 if (!islnm || rooted) {
2074 *(cp1++) = ':';
2075 *(cp1++) = '[';
2076 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
2077 else cp2++;
2078 }
2079 else {
2080 if (cp2 != dirend) {
2081 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
2082 strcpy(rslt,trndev);
2083 cp1 = rslt + trnend;
2084 *(cp1++) = '.';
2085 cp2++;
2086 }
2087 else {
2088 *(cp1++) = ':';
2089 hasdir = 0;
2090 }
2091 }
748a9306 2092 }
a0d0e21e
LW
2093 else {
2094 *(cp1++) = '[';
748a9306
LW
2095 if (*cp2 == '.') {
2096 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
2097 cp2 += 2; /* skip over "./" - it's redundant */
2098 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
2099 }
2100 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
2101 *(cp1++) = '-'; /* "../" --> "-" */
2102 cp2 += 3;
2103 }
f86702cc 2104 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
2105 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
2106 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
2107 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
2108 cp2 += 4;
2109 }
748a9306
LW
2110 if (cp2 > dirend) cp2 = dirend;
2111 }
2112 else *(cp1++) = '.';
2113 }
2114 for (; cp2 < dirend; cp2++) {
2115 if (*cp2 == '/') {
01b8edb6 2116 if (*(cp2-1) == '/') continue;
748a9306
LW
2117 if (*(cp1-1) != '.') *(cp1++) = '.';
2118 infront = 0;
2119 }
2120 else if (!infront && *cp2 == '.') {
01b8edb6 2121 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
2122 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
748a9306
LW
2123 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
2124 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
2125 else if (*(cp1-2) == '[') *(cp1-1) = '-';
2126 else { /* back up over previous directory name */
2127 cp1--;
2128 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
4633a7c4
LW
2129 if (*(cp1-1) == '[') {
2130 memcpy(cp1,"000000.",7);
2131 cp1 += 7;
2132 }
748a9306
LW
2133 }
2134 cp2 += 2;
01b8edb6 2135 if (cp2 == dirend) break;
748a9306 2136 }
f86702cc 2137 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
2138 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
2139 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
2140 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
2141 if (!*(cp2+3)) {
2142 *(cp1++) = '.'; /* Simulate trailing '/' */
2143 cp2 += 2; /* for loop will incr this to == dirend */
2144 }
2145 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
2146 }
748a9306
LW
2147 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
2148 }
2149 else {
e518068a 2150 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
01b8edb6 2151 if (*cp2 == '.') *(cp1++) = '_';
748a9306
LW
2152 else *(cp1++) = *cp2;
2153 infront = 1;
2154 }
a0d0e21e 2155 }
748a9306 2156 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
e518068a 2157 if (hasdir) *(cp1++) = ']';
748a9306 2158 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
a0d0e21e
LW
2159 while (*cp2) *(cp1++) = *(cp2++);
2160 *cp1 = '\0';
2161
2162 return rslt;
2163
2164} /* end of do_tovmsspec() */
2165/*}}}*/
2166/* External entry points */
2167char *tovmsspec(char *path, char *buf) { return do_tovmsspec(path,buf,0); }
2168char *tovmsspec_ts(char *path, char *buf) { return do_tovmsspec(path,buf,1); }
2169
2170/*{{{ char *tovmspath[_ts](char *path, char *buf)*/
2171static char *do_tovmspath(char *path, char *buf, int ts) {
2172 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
2173 int vmslen;
2174 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
2175
748a9306 2176 if (path == NULL) return NULL;
a0d0e21e
LW
2177 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
2178 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
2179 if (buf) return buf;
2180 else if (ts) {
2181 vmslen = strlen(vmsified);
fc36a67e 2182 New(1317,cp,vmslen+1,char);
a0d0e21e
LW
2183 memcpy(cp,vmsified,vmslen);
2184 cp[vmslen] = '\0';
2185 return cp;
2186 }
2187 else {
2188 strcpy(__tovmspath_retbuf,vmsified);
2189 return __tovmspath_retbuf;
2190 }
2191
2192} /* end of do_tovmspath() */
2193/*}}}*/
2194/* External entry points */
2195char *tovmspath(char *path, char *buf) { return do_tovmspath(path,buf,0); }
2196char *tovmspath_ts(char *path, char *buf) { return do_tovmspath(path,buf,1); }
2197
2198
2199/*{{{ char *tounixpath[_ts](char *path, char *buf)*/
2200static char *do_tounixpath(char *path, char *buf, int ts) {
2201 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
2202 int unixlen;
2203 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
2204
748a9306 2205 if (path == NULL) return NULL;
a0d0e21e
LW
2206 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
2207 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
2208 if (buf) return buf;
2209 else if (ts) {
2210 unixlen = strlen(unixified);
fc36a67e 2211 New(1317,cp,unixlen+1,char);
a0d0e21e
LW
2212 memcpy(cp,unixified,unixlen);
2213 cp[unixlen] = '\0';
2214 return cp;
2215 }
2216 else {
2217 strcpy(__tounixpath_retbuf,unixified);
2218 return __tounixpath_retbuf;
2219 }
2220
2221} /* end of do_tounixpath() */
2222/*}}}*/
2223/* External entry points */
2224char *tounixpath(char *path, char *buf) { return do_tounixpath(path,buf,0); }
2225char *tounixpath_ts(char *path, char *buf) { return do_tounixpath(path,buf,1); }
2226
2227/*
2228 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
2229 *
2230 *****************************************************************************
2231 * *
2232 * Copyright (C) 1989-1994 by *
2233 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
2234 * *
2235 * Permission is hereby granted for the reproduction of this software, *
2236 * on condition that this copyright notice is included in the reproduction, *
2237 * and that such reproduction is not for purposes of profit or material *
2238 * gain. *
2239 * *
2240 * 27-Aug-1994 Modified for inclusion in perl5 *
bd3fa61c 2241 * by Charles Bailey bailey@newman.upenn.edu *
a0d0e21e
LW
2242 *****************************************************************************
2243 */
2244
2245/*
2246 * getredirection() is intended to aid in porting C programs
2247 * to VMS (Vax-11 C). The native VMS environment does not support
2248 * '>' and '<' I/O redirection, or command line wild card expansion,
2249 * or a command line pipe mechanism using the '|' AND background
2250 * command execution '&'. All of these capabilities are provided to any
2251 * C program which calls this procedure as the first thing in the
2252 * main program.
2253 * The piping mechanism will probably work with almost any 'filter' type
2254 * of program. With suitable modification, it may useful for other
2255 * portability problems as well.
2256 *
2257 * Author: Mark Pizzolato mark@infocomm.com
2258 */
2259struct list_item
2260 {
2261 struct list_item *next;
2262 char *value;
2263 };
2264
2265static void add_item(struct list_item **head,
2266 struct list_item **tail,
2267 char *value,
2268 int *count);
2269
2270static void expand_wild_cards(char *item,
2271 struct list_item **head,
2272 struct list_item **tail,
2273 int *count);
2274
2275static int background_process(int argc, char **argv);
2276
2277static void pipe_and_fork(char **cmargv);
2278
2279/*{{{ void getredirection(int *ac, char ***av)*/
84902520 2280static void
a0d0e21e
LW
2281getredirection(int *ac, char ***av)
2282/*
2283 * Process vms redirection arg's. Exit if any error is seen.
2284 * If getredirection() processes an argument, it is erased
2285 * from the vector. getredirection() returns a new argc and argv value.
2286 * In the event that a background command is requested (by a trailing "&"),
2287 * this routine creates a background subprocess, and simply exits the program.
2288 *
2289 * Warning: do not try to simplify the code for vms. The code
2290 * presupposes that getredirection() is called before any data is
2291 * read from stdin or written to stdout.
2292 *
2293 * Normal usage is as follows:
2294 *
2295 * main(argc, argv)
2296 * int argc;
2297 * char *argv[];
2298 * {
2299 * getredirection(&argc, &argv);
2300 * }
2301 */
2302{
2303 int argc = *ac; /* Argument Count */
2304 char **argv = *av; /* Argument Vector */
2305 char *ap; /* Argument pointer */
2306 int j; /* argv[] index */
2307 int item_count = 0; /* Count of Items in List */
2308 struct list_item *list_head = 0; /* First Item in List */
2309 struct list_item *list_tail; /* Last Item in List */
2310 char *in = NULL; /* Input File Name */
2311 char *out = NULL; /* Output File Name */
2312 char *outmode = "w"; /* Mode to Open Output File */
2313 char *err = NULL; /* Error File Name */
2314 char *errmode = "w"; /* Mode to Open Error File */
2315 int cmargc = 0; /* Piped Command Arg Count */
2316 char **cmargv = NULL;/* Piped Command Arg Vector */
a0d0e21e
LW
2317
2318 /*
2319 * First handle the case where the last thing on the line ends with
2320 * a '&'. This indicates the desire for the command to be run in a
2321 * subprocess, so we satisfy that desire.
2322 */
2323 ap = argv[argc-1];
2324 if (0 == strcmp("&", ap))
2325 exit(background_process(--argc, argv));
e518068a 2326 if (*ap && '&' == ap[strlen(ap)-1])
a0d0e21e
LW
2327 {
2328 ap[strlen(ap)-1] = '\0';
2329 exit(background_process(argc, argv));
2330 }
2331 /*
2332 * Now we handle the general redirection cases that involve '>', '>>',
2333 * '<', and pipes '|'.
2334 */
2335 for (j = 0; j < argc; ++j)
2336 {
2337 if (0 == strcmp("<", argv[j]))
2338 {
2339 if (j+1 >= argc)
2340 {
740ce14c 2341 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
748a9306 2342 exit(LIB$_WRONUMARG);
a0d0e21e
LW
2343 }
2344 in = argv[++j];
2345 continue;
2346 }
2347 if ('<' == *(ap = argv[j]))
2348 {
2349 in = 1 + ap;
2350 continue;
2351 }
2352 if (0 == strcmp(">", ap))
2353 {
2354 if (j+1 >= argc)
2355 {
740ce14c 2356 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
748a9306 2357 exit(LIB$_WRONUMARG);
a0d0e21e
LW
2358 }
2359 out = argv[++j];
2360 continue;
2361 }
2362 if ('>' == *ap)
2363 {
2364 if ('>' == ap[1])
2365 {
2366 outmode = "a";
2367 if ('\0' == ap[2])
2368 out = argv[++j];
2369 else
2370 out = 2 + ap;
2371 }
2372 else
2373 out = 1 + ap;
2374 if (j >= argc)
2375 {
740ce14c 2376 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
748a9306 2377 exit(LIB$_WRONUMARG);
a0d0e21e
LW
2378 }
2379 continue;
2380 }
2381 if (('2' == *ap) && ('>' == ap[1]))
2382 {
2383 if ('>' == ap[2])
2384 {
2385 errmode = "a";
2386 if ('\0' == ap[3])
2387 err = argv[++j];
2388 else
2389 err = 3 + ap;
2390 }
2391 else
2392 if ('\0' == ap[2])
2393 err = argv[++j];
2394 else
748a9306 2395 err = 2 + ap;
a0d0e21e
LW
2396 if (j >= argc)
2397 {
740ce14c 2398 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
748a9306 2399 exit(LIB$_WRONUMARG);
a0d0e21e
LW
2400 }
2401 continue;
2402 }
2403 if (0 == strcmp("|", argv[j]))
2404 {
2405 if (j+1 >= argc)
2406 {
740ce14c 2407 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
748a9306 2408 exit(LIB$_WRONUMARG);
a0d0e21e
LW
2409 }
2410 cmargc = argc-(j+1);
2411 cmargv = &argv[j+1];
2412 argc = j;
2413 continue;
2414 }
2415 if ('|' == *(ap = argv[j]))
2416 {
2417 ++argv[j];
2418 cmargc = argc-j;
2419 cmargv = &argv[j];
2420 argc = j;
2421 continue;
2422 }
2423 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
2424 }
2425 /*
2426 * Allocate and fill in the new argument vector, Some Unix's terminate
2427 * the list with an extra null pointer.
2428 */
fc36a67e 2429 New(1302, argv, item_count+1, char *);
a0d0e21e
LW
2430 *av = argv;
2431 for (j = 0; j < item_count; ++j, list_head = list_head->next)
2432 argv[j] = list_head->value;
2433 *ac = item_count;
2434 if (cmargv != NULL)
2435 {
2436 if (out != NULL)
2437 {
740ce14c 2438 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
748a9306 2439 exit(LIB$_INVARGORD);
a0d0e21e
LW
2440 }
2441 pipe_and_fork(cmargv);
2442 }
2443
2444 /* Check for input from a pipe (mailbox) */
2445
a5f75d66 2446 if (in == NULL && 1 == isapipe(0))
a0d0e21e
LW
2447 {
2448 char mbxname[L_tmpnam];
2449 long int bufsize;
2450 long int dvi_item = DVI$_DEVBUFSIZ;
2451 $DESCRIPTOR(mbxnam, "");
2452 $DESCRIPTOR(mbxdevnam, "");
2453
2454 /* Input from a pipe, reopen it in binary mode to disable */
2455 /* carriage control processing. */
2456
740ce14c 2457 PerlIO_getname(stdin, mbxname);
a0d0e21e
LW
2458 mbxnam.dsc$a_pointer = mbxname;
2459 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
2460 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
2461 mbxdevnam.dsc$a_pointer = mbxname;
2462 mbxdevnam.dsc$w_length = sizeof(mbxname);
2463 dvi_item = DVI$_DEVNAM;
2464 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
2465 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
748a9306
LW
2466 set_errno(0);
2467 set_vaxc_errno(1);
a0d0e21e
LW
2468 freopen(mbxname, "rb", stdin);
2469 if (errno != 0)
2470 {
740ce14c 2471 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
748a9306 2472 exit(vaxc$errno);
a0d0e21e
LW
2473 }
2474 }
2475 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
2476 {
740ce14c 2477 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
748a9306 2478 exit(vaxc$errno);
a0d0e21e
LW
2479 }
2480 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
2481 {
740ce14c 2482 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
748a9306 2483 exit(vaxc$errno);
a0d0e21e 2484 }
748a9306
LW
2485 if (err != NULL) {
2486 FILE *tmperr;
2487 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
2488 {
740ce14c 2489 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
748a9306
LW
2490 exit(vaxc$errno);
2491 }
2492 fclose(tmperr);
b7ae7a0d 2493 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
748a9306
LW
2494 {
2495 exit(vaxc$errno);
2496 }
a0d0e21e
LW
2497 }
2498#ifdef ARGPROC_DEBUG
740ce14c 2499 PerlIO_printf(Perl_debug_log, "Arglist:\n");
a0d0e21e 2500 for (j = 0; j < *ac; ++j)
740ce14c 2501 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
a0d0e21e 2502#endif
b7ae7a0d 2503 /* Clear errors we may have hit expanding wildcards, so they don't
2504 show up in Perl's $! later */
2505 set_errno(0); set_vaxc_errno(1);
a0d0e21e
LW
2506} /* end of getredirection() */
2507/*}}}*/
2508
2509static void add_item(struct list_item **head,
2510 struct list_item **tail,
2511 char *value,
2512 int *count)
2513{
2514 if (*head == 0)
2515 {
fc36a67e 2516 New(1303,*head,1,struct list_item);
a0d0e21e
LW
2517 *tail = *head;
2518 }
2519 else {
fc36a67e 2520 New(1304,(*tail)->next,1,struct list_item);
a0d0e21e
LW
2521 *tail = (*tail)->next;
2522 }
2523 (*tail)->value = value;
2524 ++(*count);
2525}
2526
2527static void expand_wild_cards(char *item,
2528 struct list_item **head,
2529 struct list_item **tail,
2530 int *count)
2531{
2532int expcount = 0;
748a9306 2533unsigned long int context = 0;
a0d0e21e 2534int isunix = 0;
a0d0e21e
LW
2535char *had_version;
2536char *had_device;
2537int had_directory;
f675dbe5 2538char *devdir,*cp;
a0d0e21e
LW
2539char vmsspec[NAM$C_MAXRSS+1];
2540$DESCRIPTOR(filespec, "");
748a9306 2541$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
a0d0e21e 2542$DESCRIPTOR(resultspec, "");
c07a80fd 2543unsigned long int zero = 0, sts;
a0d0e21e 2544
f675dbe5
CB
2545 for (cp = item; *cp; cp++) {
2546 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
2547 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
2548 }
2549 if (!*cp || isspace(*cp))
a0d0e21e
LW
2550 {
2551 add_item(head, tail, item, count);
2552 return;
2553 }
2554 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
2555 resultspec.dsc$b_class = DSC$K_CLASS_D;
2556 resultspec.dsc$a_pointer = NULL;
748a9306 2557 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
a0d0e21e
LW
2558 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
2559 if (!isunix || !filespec.dsc$a_pointer)
2560 filespec.dsc$a_pointer = item;
2561 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
2562 /*
2563 * Only return version specs, if the caller specified a version
2564 */
2565 had_version = strchr(item, ';');
2566 /*
2567 * Only return device and directory specs, if the caller specifed either.
2568 */
2569 had_device = strchr(item, ':');
2570 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
2571
c07a80fd 2572 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
2573 &defaultspec, 0, 0, &zero))))
a0d0e21e
LW
2574 {
2575 char *string;
2576 char *c;
2577
fc36a67e 2578 New(1305,string,resultspec.dsc$w_length+1,char);
a0d0e21e
LW
2579 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
2580 string[resultspec.dsc$w_length] = '\0';
2581 if (NULL == had_version)
2582 *((char *)strrchr(string, ';')) = '\0';
2583 if ((!had_directory) && (had_device == NULL))
2584 {
2585 if (NULL == (devdir = strrchr(string, ']')))
2586 devdir = strrchr(string, '>');
2587 strcpy(string, devdir + 1);
2588 }
2589 /*
2590 * Be consistent with what the C RTL has already done to the rest of
2591 * the argv items and lowercase all of these names.
2592 */
2593 for (c = string; *c; ++c)
2594 if (isupper(*c))
2595 *c = tolower(*c);
f86702cc 2596 if (isunix) trim_unixpath(string,item,1);
a0d0e21e
LW
2597 add_item(head, tail, string, count);
2598 ++expcount;
2599 }
c07a80fd 2600 if (sts != RMS$_NMF)
2601 {
2602 set_vaxc_errno(sts);
2603 switch (sts)
2604 {
2605 case RMS$_FNF:
b7ae7a0d 2606 case RMS$_DNF:
c07a80fd 2607 case RMS$_DIR:
2608 set_errno(ENOENT); break;
2609 case RMS$_DEV:
2610 set_errno(ENODEV); break;
71be2cbc 2611 case RMS$_FNM:
c07a80fd 2612 case RMS$_SYN:
2613 set_errno(EINVAL); break;
2614 case RMS$_PRV:
2615 set_errno(EACCES); break;
2616 default:
b7ae7a0d 2617 _ckvmssts_noperl(sts);
c07a80fd 2618 }
2619 }
a0d0e21e
LW
2620 if (expcount == 0)
2621 add_item(head, tail, item, count);
b7ae7a0d 2622 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
2623 _ckvmssts_noperl(lib$find_file_end(&context));
a0d0e21e
LW
2624}
2625
2626static int child_st[2];/* Event Flag set when child process completes */
2627
748a9306 2628static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
a0d0e21e 2629
748a9306 2630static unsigned long int exit_handler(int *status)
a0d0e21e
LW
2631{
2632short iosb[4];
2633
2634 if (0 == child_st[0])
2635 {
2636#ifdef ARGPROC_DEBUG
740ce14c 2637 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
a0d0e21e
LW
2638#endif
2639 fflush(stdout); /* Have to flush pipe for binary data to */
2640 /* terminate properly -- <tp@mccall.com> */
2641 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
2642 sys$dassgn(child_chan);
2643 fclose(stdout);
2644 sys$synch(0, child_st);
2645 }
2646 return(1);
2647}
2648
2649static void sig_child(int chan)
2650{
2651#ifdef ARGPROC_DEBUG
740ce14c 2652 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
a0d0e21e
LW
2653#endif
2654 if (child_st[0] == 0)
2655 child_st[0] = 1;
2656}
2657
748a9306 2658static struct exit_control_block exit_block =
a0d0e21e
LW
2659 {
2660 0,
2661 exit_handler,
2662 1,
2663 &exit_block.exit_status,
2664 0
2665 };
2666
2667static void pipe_and_fork(char **cmargv)
2668{
2669 char subcmd[2048];
2670 $DESCRIPTOR(cmddsc, "");
2671 static char mbxname[64];
2672 $DESCRIPTOR(mbxdsc, mbxname);
a0d0e21e 2673 int pid, j;
a0d0e21e
LW
2674 unsigned long int zero = 0, one = 1;
2675
2676 strcpy(subcmd, cmargv[0]);
2677 for (j = 1; NULL != cmargv[j]; ++j)
2678 {
2679 strcat(subcmd, " \"");
2680 strcat(subcmd, cmargv[j]);
2681 strcat(subcmd, "\"");
2682 }
2683 cmddsc.dsc$a_pointer = subcmd;
2684 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
2685
2686 create_mbx(&child_chan,&mbxdsc);
2687#ifdef ARGPROC_DEBUG
740ce14c 2688 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
2689 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
a0d0e21e 2690#endif
b7ae7a0d 2691 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
2692 0, &pid, child_st, &zero, sig_child,
2693 &child_chan));
a0d0e21e 2694#ifdef ARGPROC_DEBUG
740ce14c 2695 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
a0d0e21e
LW
2696#endif
2697 sys$dclexh(&exit_block);
2698 if (NULL == freopen(mbxname, "wb", stdout))
2699 {
740ce14c 2700 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
a0d0e21e
LW
2701 }
2702}
2703
2704static int background_process(int argc, char **argv)
2705{
2706char command[2048] = "$";
2707$DESCRIPTOR(value, "");
2708static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
2709static $DESCRIPTOR(null, "NLA0:");
2710static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
2711char pidstring[80];
2712$DESCRIPTOR(pidstr, "");
2713int pid;
748a9306 2714unsigned long int flags = 17, one = 1, retsts;
a0d0e21e
LW
2715
2716 strcat(command, argv[0]);
2717 while (--argc)
2718 {
2719 strcat(command, " \"");
2720 strcat(command, *(++argv));
2721 strcat(command, "\"");
2722 }
2723 value.dsc$a_pointer = command;
2724 value.dsc$w_length = strlen(value.dsc$a_pointer);
b7ae7a0d 2725 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
748a9306
LW
2726 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
2727 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
b7ae7a0d 2728 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
748a9306
LW
2729 }
2730 else {
b7ae7a0d 2731 _ckvmssts_noperl(retsts);
748a9306 2732 }
a0d0e21e 2733#ifdef ARGPROC_DEBUG
740ce14c 2734 PerlIO_printf(Perl_debug_log, "%s\n", command);
a0d0e21e
LW
2735#endif
2736 sprintf(pidstring, "%08X", pid);
740ce14c 2737 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
a0d0e21e
LW
2738 pidstr.dsc$a_pointer = pidstring;
2739 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
2740 lib$set_symbol(&pidsymbol, &pidstr);
2741 return(SS$_NORMAL);
2742}
2743/*}}}*/
2744/***** End of code taken from Mark Pizzolato's argproc.c package *****/
2745
84902520
TB
2746
2747/* OS-specific initialization at image activation (not thread startup) */
61bb5906
CB
2748/* Older VAXC header files lack these constants */
2749#ifndef JPI$_RIGHTS_SIZE
2750# define JPI$_RIGHTS_SIZE 817
2751#endif
2752#ifndef KGB$M_SUBSYSTEM
2753# define KGB$M_SUBSYSTEM 0x8
2754#endif
2755
84902520
TB
2756/*{{{void vms_image_init(int *, char ***)*/
2757void
2758vms_image_init(int *argcp, char ***argvp)
2759{
f675dbe5
CB
2760 char eqv[LNM$C_NAMLENGTH+1] = "";
2761 unsigned int len, tabct = 8, tabidx = 0;
2762 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
61bb5906
CB
2763 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
2764 unsigned short int dummy, rlen;
f675dbe5 2765 struct dsc$descriptor_s **tabvec;
d28f7c37 2766 dTHX;
61bb5906
CB
2767 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
2768 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
2769 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
2770 { 0, 0, 0, 0} };
84902520
TB
2771
2772 _ckvmssts(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
2773 _ckvmssts(iosb[0]);
61bb5906
CB
2774 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
2775 if (iprv[i]) { /* Running image installed with privs? */
2776 _ckvmssts(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
f675dbe5 2777 will_taint = TRUE;
84902520
TB
2778 break;
2779 }
2780 }
61bb5906 2781 /* Rights identifiers might trigger tainting as well. */
f675dbe5 2782 if (!will_taint && (rlen || rsz)) {
61bb5906
CB
2783 while (rlen < rsz) {
2784 /* We didn't get all the identifiers on the first pass. Allocate a
2785 * buffer much larger than $GETJPI wants (rsz is size in bytes that
2786 * were needed to hold all identifiers at time of last call; we'll
2787 * allocate that many unsigned long ints), and go back and get 'em.
2788 */
2789 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
2790 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
2791 jpilist[1].buflen = rsz * sizeof(unsigned long int);
2792 _ckvmssts(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
2793 _ckvmssts(iosb[0]);
2794 }
2795 mask = jpilist[1].bufadr;
2796 /* Check attribute flags for each identifier (2nd longword); protected
2797 * subsystem identifiers trigger tainting.
2798 */
2799 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
2800 if (mask[i] & KGB$M_SUBSYSTEM) {
f675dbe5 2801 will_taint = TRUE;
61bb5906
CB
2802 break;
2803 }
2804 }
2805 if (mask != rlst) Safefree(mask);
2806 }
2807 /* We need to use this hack to tell Perl it should run with tainting,
6b88bc9c 2808 * since its tainting flag may be part of the PL_curinterp struct, which
61bb5906
CB
2809 * hasn't been allocated when vms_image_init() is called.
2810 */
f675dbe5 2811 if (will_taint) {
61bb5906
CB
2812 char ***newap;
2813 New(1320,newap,*argcp+2,char **);
2814 newap[0] = argvp[0];
2815 *newap[1] = "-T";
2816 Copy(argvp[1],newap[2],*argcp-1,char **);
2817 /* We orphan the old argv, since we don't know where it's come from,
2818 * so we don't know how to free it.
2819 */
2820 *argcp++; argvp = newap;
2821 }
f675dbe5
CB
2822 else { /* Did user explicitly request tainting? */
2823 int i;
2824 char *cp, **av = *argvp;
2825 for (i = 1; i < *argcp; i++) {
2826 if (*av[i] != '-') break;
2827 for (cp = av[i]+1; *cp; cp++) {
2828 if (*cp == 'T') { will_taint = 1; break; }
2829 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
2830 strchr("DFIiMmx",*cp)) break;
2831 }
2832 if (will_taint) break;
2833 }
2834 }
2835
2836 for (tabidx = 0;
2837 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
2838 tabidx++) {
2839 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
2840 else if (tabidx >= tabct) {
2841 tabct += 8;
2842 Renew(tabvec,tabct,struct dsc$descriptor_s *);
2843 }
2844 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
2845 tabvec[tabidx]->dsc$w_length = 0;
2846 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
2847 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
2848 tabvec[tabidx]->dsc$a_pointer = NULL;
2849 _ckvmssts(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
2850 }
2851 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
2852
84902520 2853 getredirection(argcp,argvp);
09b7f37c
CB
2854#if defined(USE_THREADS) && defined(__DECC)
2855 {
2856# include <reentrancy.h>
2857 (void) decc$set_reentrancy(C$C_MULTITHREAD);
2858 }
2859#endif
84902520
TB
2860 return;
2861}
2862/*}}}*/
2863
2864
a0d0e21e
LW
2865/* trim_unixpath()
2866 * Trim Unix-style prefix off filespec, so it looks like what a shell
2867 * glob expansion would return (i.e. from specified prefix on, not
2868 * full path). Note that returned filespec is Unix-style, regardless
2869 * of whether input filespec was VMS-style or Unix-style.
2870 *
a3e9d8c9 2871 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
f86702cc 2872 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
2873 * vector of options; at present, only bit 0 is used, and if set tells
2874 * trim unixpath to try the current default directory as a prefix when
2875 * presented with a possibly ambiguous ... wildcard.
a3e9d8c9 2876 *
2877 * Returns !=0 on success, with trimmed filespec replacing contents of
2878 * fspec, and 0 on failure, with contents of fpsec unchanged.
a0d0e21e 2879 */
f86702cc 2880/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
a0d0e21e 2881int
f86702cc 2882trim_unixpath(char *fspec, char *wildspec, int opts)
a0d0e21e 2883{
a3e9d8c9 2884 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
f86702cc 2885 *template, *base, *end, *cp1, *cp2;
2886 register int tmplen, reslen = 0, dirs = 0;
a0d0e21e 2887
a3e9d8c9 2888 if (!wildspec || !fspec) return 0;
2889 if (strpbrk(wildspec,"]>:") != NULL) {
2890 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
f86702cc 2891 else template = unixwild;
a3e9d8c9 2892 }
2893 else template = wildspec;
a0d0e21e
LW
2894 if (strpbrk(fspec,"]>:") != NULL) {
2895 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
2896 else base = unixified;
a3e9d8c9 2897 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
2898 * check to see that final result fits into (isn't longer than) fspec */
2899 reslen = strlen(fspec);
a0d0e21e
LW
2900 }
2901 else base = fspec;
a3e9d8c9 2902
2903 /* No prefix or absolute path on wildcard, so nothing to remove */
2904 if (!*template || *template == '/') {
2905 if (base == fspec) return 1;
2906 tmplen = strlen(unixified);
2907 if (tmplen > reslen) return 0; /* not enough space */
2908 /* Copy unixified resultant, including trailing NUL */
2909 memmove(fspec,unixified,tmplen+1);
2910 return 1;
2911 }
a0d0e21e 2912
f86702cc 2913 for (end = base; *end; end++) ; /* Find end of resultant filespec */
2914 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
2915 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
2916 for (cp1 = end ;cp1 >= base; cp1--)
2917 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
2918 { cp1++; break; }
2919 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
a3e9d8c9 2920 return 1;
2921 }
f86702cc 2922 else {
2923 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
2924 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
2925 int ells = 1, totells, segdirs, match;
2926 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
2927 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2928
2929 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
2930 totells = ells;
2931 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
2932 if (ellipsis == template && opts & 1) {
2933 /* Template begins with an ellipsis. Since we can't tell how many
2934 * directory names at the front of the resultant to keep for an
2935 * arbitrary starting point, we arbitrarily choose the current
2936 * default directory as a starting point. If it's there as a prefix,
2937 * clip it off. If not, fall through and act as if the leading
2938 * ellipsis weren't there (i.e. return shortest possible path that
2939 * could match template).
2940 */
2941 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
2942 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
2943 if (_tolower(*cp1) != _tolower(*cp2)) break;
2944 segdirs = dirs - totells; /* Min # of dirs we must have left */
2945 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
2946 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
2947 memcpy(fspec,cp2+1,end - cp2);
2948 return 1;
a3e9d8c9 2949 }
a3e9d8c9 2950 }
f86702cc 2951 /* First off, back up over constant elements at end of path */
2952 if (dirs) {
2953 for (front = end ; front >= base; front--)
2954 if (*front == '/' && !dirs--) { front++; break; }
a3e9d8c9 2955 }
17f28c40 2956 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
f86702cc 2957 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
2958 if (cp1 != '\0') return 0; /* Path too long. */
2959 lcend = cp2;
2960 *cp2 = '\0'; /* Pick up with memcpy later */
2961 lcfront = lcres + (front - base);
2962 /* Now skip over each ellipsis and try to match the path in front of it. */
2963 while (ells--) {
2964 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
2965 if (*(cp1) == '.' && *(cp1+1) == '.' &&
2966 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
2967 if (cp1 < template) break; /* template started with an ellipsis */
2968 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
2969 ellipsis = cp1; continue;
2970 }
2971 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
2972 nextell = cp1;
2973 for (segdirs = 0, cp2 = tpl;
2974 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
2975 cp1++, cp2++) {
2976 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
2977 else *cp2 = _tolower(*cp1); /* else lowercase for match */
2978 if (*cp2 == '/') segdirs++;
2979 }
2980 if (cp1 != ellipsis - 1) return 0; /* Path too long */
2981 /* Back up at least as many dirs as in template before matching */
2982 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
2983 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
2984 for (match = 0; cp1 > lcres;) {
2985 resdsc.dsc$a_pointer = cp1;
2986 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
2987 match++;
2988 if (match == 1) lcfront = cp1;
2989 }
2990 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
2991 }
2992 if (!match) return 0; /* Can't find prefix ??? */
2993 if (match > 1 && opts & 1) {
2994 /* This ... wildcard could cover more than one set of dirs (i.e.
2995 * a set of similar dir names is repeated). If the template
2996 * contains more than 1 ..., upstream elements could resolve the
2997 * ambiguity, but it's not worth a full backtracking setup here.
2998 * As a quick heuristic, clip off the current default directory
2999 * if it's present to find the trimmed spec, else use the
3000 * shortest string that this ... could cover.
3001 */
3002 char def[NAM$C_MAXRSS+1], *st;
3003
3004 if (getcwd(def, sizeof def,0) == NULL) return 0;
3005 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
3006 if (_tolower(*cp1) != _tolower(*cp2)) break;
3007 segdirs = dirs - totells; /* Min # of dirs we must have left */
3008 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
3009 if (*cp1 == '\0' && *cp2 == '/') {
3010 memcpy(fspec,cp2+1,end - cp2);
3011 return 1;
3012 }
3013 /* Nope -- stick with lcfront from above and keep going. */
3014 }
3015 }
3016 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
a3e9d8c9 3017 return 1;
f86702cc 3018 ellipsis = nextell;
a0d0e21e 3019 }
a0d0e21e
LW
3020
3021} /* end of trim_unixpath() */
3022/*}}}*/
3023
a0d0e21e
LW
3024
3025/*
3026 * VMS readdir() routines.
3027 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
a0d0e21e 3028 *
bd3fa61c 3029 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
a0d0e21e
LW
3030 * Minor modifications to original routines.
3031 */
3032
3033 /* Number of elements in vms_versions array */
3034#define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
3035
3036/*
3037 * Open a directory, return a handle for later use.
3038 */
3039/*{{{ DIR *opendir(char*name) */
3040DIR *
3041opendir(char *name)
3042{
3043 DIR *dd;
3044 char dir[NAM$C_MAXRSS+1];
61bb5906
CB
3045 Stat_t sb;
3046
a0d0e21e 3047 if (do_tovmspath(name,dir,0) == NULL) {
61bb5906 3048 return NULL;
a0d0e21e 3049 }
61bb5906
CB
3050 if (flex_stat(dir,&sb) == -1) return NULL;
3051 if (!S_ISDIR(sb.st_mode)) {
3052 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
3053 return NULL;
3054 }
3055 if (!cando_by_name(S_IRUSR,0,dir)) {
3056 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
3057 return NULL;
3058 }
3059 /* Get memory for the handle, and the pattern. */
3060 New(1306,dd,1,DIR);
fc36a67e 3061 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
a0d0e21e
LW
3062
3063 /* Fill in the fields; mainly playing with the descriptor. */
3064 (void)sprintf(dd->pattern, "%s*.*",dir);
3065 dd->context = 0;
3066 dd->count = 0;
3067 dd->vms_wantversions = 0;
3068 dd->pat.dsc$a_pointer = dd->pattern;
3069 dd->pat.dsc$w_length = strlen(dd->pattern);
3070 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
3071 dd->pat.dsc$b_class = DSC$K_CLASS_S;
3072
3073 return dd;
3074} /* end of opendir() */
3075/*}}}*/
3076
3077/*
3078 * Set the flag to indicate we want versions or not.
3079 */
3080/*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
3081void
3082vmsreaddirversions(DIR *dd, int flag)
3083{
3084 dd->vms_wantversions = flag;
3085}
3086/*}}}*/
3087
3088/*
3089 * Free up an opened directory.
3090 */
3091/*{{{ void closedir(DIR *dd)*/
3092void
3093closedir(DIR *dd)
3094{
3095 (void)lib$find_file_end(&dd->context);
3096 Safefree(dd->pattern);
3097 Safefree((char *)dd);
3098}
3099/*}}}*/
3100
3101/*
3102 * Collect all the version numbers for the current file.
3103 */
3104static void
3105collectversions(dd)
3106 DIR *dd;
3107{
3108 struct dsc$descriptor_s pat;
3109 struct dsc$descriptor_s res;
3110 struct dirent *e;
3111 char *p, *text, buff[sizeof dd->entry.d_name];
3112 int i;
3113 unsigned long context, tmpsts;
d28f7c37 3114 dTHX;
a0d0e21e
LW
3115
3116 /* Convenient shorthand. */
3117 e = &dd->entry;
3118
3119 /* Add the version wildcard, ignoring the "*.*" put on before */
3120 i = strlen(dd->pattern);
fc36a67e 3121 New(1308,text,i + e->d_namlen + 3,char);
a0d0e21e
LW
3122 (void)strcpy(text, dd->pattern);
3123 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
3124
3125 /* Set up the pattern descriptor. */
3126 pat.dsc$a_pointer = text;
3127 pat.dsc$w_length = i + e->d_namlen - 1;
3128 pat.dsc$b_dtype = DSC$K_DTYPE_T;
3129 pat.dsc$b_class = DSC$K_CLASS_S;
3130
3131 /* Set up result descriptor. */
3132 res.dsc$a_pointer = buff;
3133 res.dsc$w_length = sizeof buff - 2;
3134 res.dsc$b_dtype = DSC$K_DTYPE_T;
3135 res.dsc$b_class = DSC$K_CLASS_S;
3136
3137 /* Read files, collecting versions. */
3138 for (context = 0, e->vms_verscount = 0;
3139 e->vms_verscount < VERSIZE(e);
3140 e->vms_verscount++) {
3141 tmpsts = lib$find_file(&pat, &res, &context);
3142 if (tmpsts == RMS$_NMF || context == 0) break;
748a9306 3143 _ckvmssts(tmpsts);
a0d0e21e 3144 buff[sizeof buff - 1] = '\0';
748a9306 3145 if ((p = strchr(buff, ';')))
a0d0e21e
LW
3146 e->vms_versions[e->vms_verscount] = atoi(p + 1);
3147 else
3148 e->vms_versions[e->vms_verscount] = -1;
3149 }
3150
748a9306 3151 _ckvmssts(lib$find_file_end(&context));
a0d0e21e
LW
3152 Safefree(text);
3153
3154} /* end of collectversions() */
3155
3156/*
3157 * Read the next entry from the directory.
3158 */
3159/*{{{ struct dirent *readdir(DIR *dd)*/
3160struct dirent *
3161readdir(DIR *dd)
3162{
3163 struct dsc$descriptor_s res;
3164 char *p, buff[sizeof dd->entry.d_name];
a0d0e21e
LW
3165 unsigned long int tmpsts;
3166
3167 /* Set up result descriptor, and get next file. */
3168 res.dsc$a_pointer = buff;
3169 res.dsc$w_length = sizeof buff - 2;
3170 res.dsc$b_dtype = DSC$K_DTYPE_T;
3171 res.dsc$b_class = DSC$K_CLASS_S;
a0d0e21e 3172 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4633a7c4
LW
3173 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
3174 if (!(tmpsts & 1)) {
3175 set_vaxc_errno(tmpsts);
3176 switch (tmpsts) {
3177 case RMS$_PRV:
c07a80fd 3178 set_errno(EACCES); break;
4633a7c4 3179 case RMS$_DEV:
c07a80fd 3180 set_errno(ENODEV); break;
4633a7c4 3181 case RMS$_DIR:
4633a7c4 3182 case RMS$_FNF:
c07a80fd 3183 set_errno(ENOENT); break;
4633a7c4
LW
3184 default:
3185 set_errno(EVMSERR);
3186 }
3187 return NULL;
3188 }
3189 dd->count++;
a0d0e21e
LW
3190 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
3191 buff[sizeof buff - 1] = '\0';
f675dbe5
CB
3192 for (p = buff; *p; p++) *p = _tolower(*p);
3193 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
a0d0e21e
LW
3194 *p = '\0';
3195
3196 /* Skip any directory component and just copy the name. */
748a9306 3197 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
a0d0e21e
LW
3198 else (void)strcpy(dd->entry.d_name, buff);
3199
3200 /* Clobber the version. */
748a9306 3201 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
a0d0e21e
LW
3202
3203 dd->entry.d_namlen = strlen(dd->entry.d_name);
3204 dd->entry.vms_verscount = 0;
3205 if (dd->vms_wantversions) collectversions(dd);
3206 return &dd->entry;
3207
3208} /* end of readdir() */
3209/*}}}*/
3210
3211/*
3212 * Return something that can be used in a seekdir later.
3213 */
3214/*{{{ long telldir(DIR *dd)*/
3215long
3216telldir(DIR *dd)
3217{
3218 return dd->count;
3219}
3220/*}}}*/
3221
3222/*
3223 * Return to a spot where we used to be. Brute force.
3224 */
3225/*{{{ void seekdir(DIR *dd,long count)*/
3226void
3227seekdir(DIR *dd, long count)
3228{
3229 int vms_wantversions;
d28f7c37 3230 dTHX;
a0d0e21e
LW
3231
3232 /* If we haven't done anything yet... */
3233 if (dd->count == 0)
3234 return;
3235
3236 /* Remember some state, and clear it. */
3237 vms_wantversions = dd->vms_wantversions;
3238 dd->vms_wantversions = 0;
748a9306 3239 _ckvmssts(lib$find_file_end(&dd->context));
a0d0e21e
LW
3240 dd->context = 0;
3241
3242 /* The increment is in readdir(). */
3243 for (dd->count = 0; dd->count < count; )
3244 (void)readdir(dd);
3245
3246 dd->vms_wantversions = vms_wantversions;
3247
3248} /* end of seekdir() */
3249/*}}}*/
3250
3251/* VMS subprocess management
3252 *
3253 * my_vfork() - just a vfork(), after setting a flag to record that
3254 * the current script is trying a Unix-style fork/exec.
3255 *
3256 * vms_do_aexec() and vms_do_exec() are called in response to the
3257 * perl 'exec' function. If this follows a vfork call, then they
3258 * call out the the regular perl routines in doio.c which do an
3259 * execvp (for those who really want to try this under VMS).
3260 * Otherwise, they do exactly what the perl docs say exec should
3261 * do - terminate the current script and invoke a new command
3262 * (See below for notes on command syntax.)
3263 *
3264 * do_aspawn() and do_spawn() implement the VMS side of the perl
3265 * 'system' function.
3266 *
3267 * Note on command arguments to perl 'exec' and 'system': When handled
3268 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
3269 * are concatenated to form a DCL command string. If the first arg
3270 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
3eeba6fb 3271 * the the command string is handed off to DCL directly. Otherwise,
a0d0e21e
LW
3272 * the first token of the command is taken as the filespec of an image
3273 * to run. The filespec is expanded using a default type of '.EXE' and
3eeba6fb 3274 * the process defaults for device, directory, etc., and if found, the resultant
a0d0e21e 3275 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
3eeba6fb 3276 * the command string as parameters. This is perhaps a bit complicated,
a0d0e21e
LW
3277 * but I hope it will form a happy medium between what VMS folks expect
3278 * from lib$spawn and what Unix folks expect from exec.
3279 */
3280
3281static int vfork_called;
3282
3283/*{{{int my_vfork()*/
3284int
3285my_vfork()
3286{
748a9306 3287 vfork_called++;
a0d0e21e
LW
3288 return vfork();
3289}
3290/*}}}*/
3291
4633a7c4
LW
3292
3293static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
3294
a0d0e21e 3295static void
4633a7c4 3296vms_execfree() {
6b88bc9c
GS
3297 if (PL_Cmd) {
3298 Safefree(PL_Cmd);
3299 PL_Cmd = Nullch;
4633a7c4
LW
3300 }
3301 if (VMScmd.dsc$a_pointer) {
3302 Safefree(VMScmd.dsc$a_pointer);
3303 VMScmd.dsc$w_length = 0;
3304 VMScmd.dsc$a_pointer = Nullch;
3305 }
3306}
3307
3308static char *
3309setup_argstr(SV *really, SV **mark, SV **sp)
a0d0e21e 3310{
d28f7c37 3311 dTHX;
4633a7c4 3312 char *junk, *tmps = Nullch;
a0d0e21e
LW
3313 register size_t cmdlen = 0;
3314 size_t rlen;
3315 register SV **idx;
2d8e6c8d 3316 STRLEN n_a;
a0d0e21e
LW
3317
3318 idx = mark;
4633a7c4
LW
3319 if (really) {
3320 tmps = SvPV(really,rlen);
3321 if (*tmps) {
3322 cmdlen += rlen + 1;
3323 idx++;
3324 }
a0d0e21e
LW
3325 }
3326
3327 for (idx++; idx <= sp; idx++) {
3328 if (*idx) {
3329 junk = SvPVx(*idx,rlen);
3330 cmdlen += rlen ? rlen + 1 : 0;
3331 }
3332 }
6b88bc9c 3333 New(401,PL_Cmd,cmdlen+1,char);
a0d0e21e 3334
4633a7c4 3335 if (tmps && *tmps) {
6b88bc9c 3336 strcpy(PL_Cmd,tmps);
a0d0e21e
LW
3337 mark++;
3338 }
6b88bc9c 3339 else *PL_Cmd = '\0';
a0d0e21e
LW
3340 while (++mark <= sp) {
3341 if (*mark) {
3eeba6fb
CB
3342 char *s = SvPVx(*mark,n_a);
3343 if (!*s) continue;
3344 if (*PL_Cmd) strcat(PL_Cmd," ");
3345 strcat(PL_Cmd,s);
a0d0e21e
LW
3346 }
3347 }
6b88bc9c 3348 return PL_Cmd;
a0d0e21e
LW
3349
3350} /* end of setup_argstr() */
3351
4633a7c4 3352
a0d0e21e 3353static unsigned long int
4633a7c4 3354setup_cmddsc(char *cmd, int check_img)
a0d0e21e
LW
3355{
3356 char resspec[NAM$C_MAXRSS+1];
3357 $DESCRIPTOR(defdsc,".EXE");
3358 $DESCRIPTOR(resdsc,resspec);
3359 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3eeba6fb 3360 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
a0d0e21e
LW
3361 register char *s, *rest, *cp;
3362 register int isdcl = 0;
d28f7c37 3363 dTHX;
a0d0e21e
LW
3364
3365 s = cmd;
3366 while (*s && isspace(*s)) s++;
3367 if (check_img) {
3368 if (*s == '$') { /* Check whether this is a DCL command: leading $ and */
3369 isdcl = 1; /* no dev/dir separators (i.e. not a foreign command) */
3370 for (cp = s; *cp && *cp != '/' && !isspace(*cp); cp++) {
3371 if (*cp == ':' || *cp == '[' || *cp == '<') {
3372 isdcl = 0;
3373 break;
3374 }
3375 }
3376 }
3377 }
3378 else isdcl = 1;
3eeba6fb 3379 if (!isdcl) {
a0d0e21e
LW
3380 cmd = s;
3381 while (*s && !isspace(*s)) s++;
3382 rest = *s ? s : 0;
3383 imgdsc.dsc$a_pointer = cmd;
3384 imgdsc.dsc$w_length = s - cmd;
3385 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
3eeba6fb 3386 if (retsts & 1) {
748a9306 3387 _ckvmssts(lib$find_file_end(&cxt));
a0d0e21e
LW
3388 s = resspec;
3389 while (*s && !isspace(*s)) s++;
3390 *s = '\0';
3eeba6fb
CB
3391 if (cando_by_name(S_IXUSR,0,resspec)) {
3392 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
3393 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
3394 strcat(VMScmd.dsc$a_pointer,resspec);
3395 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
3396 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
3397 return retsts;
3398 }
3399 else retsts = RMS$_PRV;
a0d0e21e
LW
3400 }
3401 }
3eeba6fb
CB
3402 /* It's either a DCL command or we couldn't find a suitable image */
3403 VMScmd.dsc$w_length = strlen(cmd);
3404 if (cmd == PL_Cmd) {
3405 VMScmd.dsc$a_pointer = PL_Cmd;
3406 PL_Cmd = Nullch; /* Don't try to free twice in vms_execfree() */
3407 }
3408 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
3409 if (!(retsts & 1)) {
3410 /* just hand off status values likely to be due to user error */
3411 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
3412 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
3413 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
3414 else { _ckvmssts(retsts); }
3415 }
a0d0e21e 3416
3eeba6fb 3417 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : retsts);
a3e9d8c9 3418
a0d0e21e
LW
3419} /* end of setup_cmddsc() */
3420
a3e9d8c9 3421
a0d0e21e
LW
3422/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
3423bool
3424vms_do_aexec(SV *really,SV **mark,SV **sp)
3425{
d28f7c37 3426 dTHX;
a0d0e21e
LW
3427 if (sp > mark) {
3428 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
3429 vfork_called--;
3430 if (vfork_called < 0) {
d28f7c37 3431 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
3432 vfork_called = 0;
3433 }
3434 else return do_aexec(really,mark,sp);
a0d0e21e 3435 }
4633a7c4
LW
3436 /* no vfork - act VMSish */
3437 return vms_do_exec(setup_argstr(really,mark,sp));
748a9306 3438
a0d0e21e
LW
3439 }
3440
3441 return FALSE;
3442} /* end of vms_do_aexec() */
3443/*}}}*/
3444
3445/* {{{bool vms_do_exec(char *cmd) */
3446bool
3447vms_do_exec(char *cmd)
3448{
3449
d28f7c37 3450 dTHX;
a0d0e21e 3451 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
3452 vfork_called--;
3453 if (vfork_called < 0) {
d28f7c37 3454 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
3455 vfork_called = 0;
3456 }
3457 else return do_exec(cmd);
a0d0e21e 3458 }
748a9306
LW
3459
3460 { /* no vfork - act VMSish */
748a9306 3461 unsigned long int retsts;
a0d0e21e 3462
1e422769 3463 TAINT_ENV();
3464 TAINT_PROPER("exec");
4633a7c4
LW
3465 if ((retsts = setup_cmddsc(cmd,1)) & 1)
3466 retsts = lib$do_command(&VMScmd);
a0d0e21e 3467
09b7f37c
CB
3468 switch (retsts) {
3469 case RMS$_FNF:
3470 set_errno(ENOENT); break;
3471 case RMS$_DNF: case RMS$_DIR: case RMS$_DEV:
3472 set_errno(ENOTDIR); break;
3473 case RMS$_PRV:
3474 set_errno(EACCES); break;
3475 case RMS$_SYN:
3476 set_errno(EINVAL); break;
3477 case CLI$_BUFOVF:
3478 set_errno(E2BIG); break;
3479 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3480 _ckvmssts(retsts); /* fall through */
3481 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3482 set_errno(EVMSERR);
3483 }
748a9306 3484 set_vaxc_errno(retsts);
3eeba6fb 3485 if (ckWARN(WARN_EXEC)) {
d28f7c37 3486 Perl_warner(aTHX_ WARN_EXEC,"Can't exec \"%*s\": %s",
3eeba6fb
CB
3487 VMScmd.dsc$w_length, VMScmd.dsc$a_pointer, Strerror(errno));
3488 }
4633a7c4 3489 vms_execfree();
a0d0e21e
LW
3490 }
3491
3492 return FALSE;
3493
3494} /* end of vms_do_exec() */
3495/*}}}*/
3496
3497unsigned long int do_spawn(char *);
3498
61bb5906 3499/* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
a0d0e21e 3500unsigned long int
61bb5906 3501do_aspawn(void *really,void **mark,void **sp)
a0d0e21e 3502{
d28f7c37 3503 dTHX;
61bb5906 3504 if (sp > mark) return do_spawn(setup_argstr((SV *)really,(SV **)mark,(SV **)sp));
a0d0e21e
LW
3505
3506 return SS$_ABORT;
3507} /* end of do_aspawn() */
3508/*}}}*/
3509
3510/* {{{unsigned long int do_spawn(char *cmd) */
3511unsigned long int
3512do_spawn(char *cmd)
3513{
09b7f37c 3514 unsigned long int sts, substs, hadcmd = 1;
d28f7c37 3515 dTHX;
a0d0e21e 3516
1e422769 3517 TAINT_ENV();
3518 TAINT_PROPER("spawn");
748a9306 3519 if (!cmd || !*cmd) {
4633a7c4 3520 hadcmd = 0;
09b7f37c 3521 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
748a9306 3522 }
09b7f37c
CB
3523 else if ((sts = setup_cmddsc(cmd,0)) & 1) {
3524 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
748a9306 3525 }
a0d0e21e 3526
09b7f37c
CB
3527 if (!(sts & 1)) {
3528 switch (sts) {
3529 case RMS$_FNF:
3530 set_errno(ENOENT); break;
3531 case RMS$_DNF: case RMS$_DIR: case RMS$_DEV:
3532 set_errno(ENOTDIR); break;
3533 case RMS$_PRV:
3534 set_errno(EACCES); break;
3535 case RMS$_SYN:
3536 set_errno(EINVAL); break;
3537 case CLI$_BUFOVF:
3538 set_errno(E2BIG); break;
3539 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3540 _ckvmssts(sts); /* fall through */
3541 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3542 set_errno(EVMSERR);
3543 }
3544 set_vaxc_errno(sts);
3eeba6fb 3545 if (ckWARN(WARN_EXEC)) {
d28f7c37 3546 Perl_warner(aTHX_ WARN_EXEC,"Can't spawn \"%*s\": %s",
3eeba6fb
CB
3547 hadcmd ? VMScmd.dsc$w_length : 0,
3548 hadcmd ? VMScmd.dsc$a_pointer : "",
3549 Strerror(errno));
3550 }
a0d0e21e 3551 }
4633a7c4 3552 vms_execfree();
a0d0e21e
LW
3553 return substs;
3554
3555} /* end of do_spawn() */
3556/*}}}*/
3557
3558/*
3559 * A simple fwrite replacement which outputs itmsz*nitm chars without
3560 * introducing record boundaries every itmsz chars.
3561 */
3562/*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
3563int
3564my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
3565{
3566 register char *cp, *end;
3567
3568 end = (char *)src + itmsz * nitm;
3569
3570 while ((char *)src <= end) {
3571 for (cp = src; cp <= end; cp++) if (!*cp) break;
3572 if (fputs(src,dest) == EOF) return EOF;
3573 if (cp < end)
3574 if (fputc('\0',dest) == EOF) return EOF;
3575 src = cp + 1;
3576 }
3577
3578 return 1;
3579
3580} /* end of my_fwrite() */
3581/*}}}*/
3582
d27fe803
JH
3583/*{{{ int my_flush(FILE *fp)*/
3584int
3585my_flush(FILE *fp)
3586{
3587 int res;
a5da9353 3588 if ((res = fflush(fp)) == 0 && fp) {
d27fe803 3589#ifdef VMS_DO_SOCKETS
61bb5906 3590 Stat_t s;
d27fe803
JH
3591 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
3592#endif
3593 res = fsync(fileno(fp));
3594 }
3595 return res;
3596}
3597/*}}}*/
3598
748a9306
LW
3599/*
3600 * Here are replacements for the following Unix routines in the VMS environment:
3601 * getpwuid Get information for a particular UIC or UID
3602 * getpwnam Get information for a named user
3603 * getpwent Get information for each user in the rights database
3604 * setpwent Reset search to the start of the rights database
3605 * endpwent Finish searching for users in the rights database
3606 *
3607 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
3608 * (defined in pwd.h), which contains the following fields:-
3609 * struct passwd {
3610 * char *pw_name; Username (in lower case)
3611 * char *pw_passwd; Hashed password
3612 * unsigned int pw_uid; UIC
3613 * unsigned int pw_gid; UIC group number
3614 * char *pw_unixdir; Default device/directory (VMS-style)
3615 * char *pw_gecos; Owner name
3616 * char *pw_dir; Default device/directory (Unix-style)
3617 * char *pw_shell; Default CLI name (eg. DCL)
3618 * };
3619 * If the specified user does not exist, getpwuid and getpwnam return NULL.
3620 *
3621 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
3622 * not the UIC member number (eg. what's returned by getuid()),
3623 * getpwuid() can accept either as input (if uid is specified, the caller's
3624 * UIC group is used), though it won't recognise gid=0.
3625 *
3626 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
3627 * information about other users in your group or in other groups, respectively.
3628 * If the required privilege is not available, then these routines fill only
3629 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
3630 * string).
3631 *
3632 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
3633 */
3634
3635/* sizes of various UAF record fields */
3636#define UAI$S_USERNAME 12
3637#define UAI$S_IDENT 31
3638#define UAI$S_OWNER 31
3639#define UAI$S_DEFDEV 31
3640#define UAI$S_DEFDIR 63
3641#define UAI$S_DEFCLI 31
3642#define UAI$S_PWD 8
3643
3644#define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
3645 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
3646 (uic).uic$v_group != UIC$K_WILD_GROUP)
3647
4633a7c4
LW
3648static char __empty[]= "";
3649static struct passwd __passwd_empty=
748a9306
LW
3650 {(char *) __empty, (char *) __empty, 0, 0,
3651 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
3652static int contxt= 0;
3653static struct passwd __pwdcache;
3654static char __pw_namecache[UAI$S_IDENT+1];
3655
748a9306
LW
3656/*
3657 * This routine does most of the work extracting the user information.
3658 */
3659static int fillpasswd (const char *name, struct passwd *pwd)
a0d0e21e 3660{
d28f7c37 3661 dTHX;
748a9306
LW
3662 static struct {
3663 unsigned char length;
3664 char pw_gecos[UAI$S_OWNER+1];
3665 } owner;
3666 static union uicdef uic;
3667 static struct {
3668 unsigned char length;
3669 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
3670 } defdev;
3671 static struct {
3672 unsigned char length;
3673 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
3674 } defdir;
3675 static struct {
3676 unsigned char length;
3677 char pw_shell[UAI$S_DEFCLI+1];
3678 } defcli;
3679 static char pw_passwd[UAI$S_PWD+1];
3680
3681 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
3682 struct dsc$descriptor_s name_desc;
c07a80fd 3683 unsigned long int sts;
748a9306 3684
4633a7c4 3685 static struct itmlst_3 itmlst[]= {
748a9306
LW
3686 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
3687 {sizeof(uic), UAI$_UIC, &uic, &luic},
3688 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
3689 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
3690 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
3691 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
3692 {0, 0, NULL, NULL}};
3693
3694 name_desc.dsc$w_length= strlen(name);
3695 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3696 name_desc.dsc$b_class= DSC$K_CLASS_S;
3697 name_desc.dsc$a_pointer= (char *) name;
3698
3699/* Note that sys$getuai returns many fields as counted strings. */
c07a80fd 3700 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
3701 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
3702 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
3703 }
3704 else { _ckvmssts(sts); }
3705 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
748a9306
LW
3706
3707 if ((int) owner.length < lowner) lowner= (int) owner.length;
3708 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
3709 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
3710 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
3711 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
3712 owner.pw_gecos[lowner]= '\0';
3713 defdev.pw_dir[ldefdev+ldefdir]= '\0';
3714 defcli.pw_shell[ldefcli]= '\0';
3715 if (valid_uic(uic)) {
3716 pwd->pw_uid= uic.uic$l_uic;
3717 pwd->pw_gid= uic.uic$v_group;
3718 }
3719 else
d28f7c37 3720 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
748a9306
LW
3721 pwd->pw_passwd= pw_passwd;
3722 pwd->pw_gecos= owner.pw_gecos;
3723 pwd->pw_dir= defdev.pw_dir;
3724 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
3725 pwd->pw_shell= defcli.pw_shell;
3726 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
3727 int ldir;
3728 ldir= strlen(pwd->pw_unixdir) - 1;
3729 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
3730 }
3731 else
3732 strcpy(pwd->pw_unixdir, pwd->pw_dir);
01b8edb6 3733 __mystrtolower(pwd->pw_unixdir);
c07a80fd 3734 return 1;
a0d0e21e 3735}
748a9306
LW
3736
3737/*
3738 * Get information for a named user.
3739*/
3740/*{{{struct passwd *getpwnam(char *name)*/
3741struct passwd *my_getpwnam(char *name)
3742{
3743 struct dsc$descriptor_s name_desc;
3744 union uicdef uic;
aa689395 3745 unsigned long int status, sts;
d28f7c37 3746 dTHX;
748a9306
LW
3747
3748 __pwdcache = __passwd_empty;
c07a80fd 3749 if (!fillpasswd(name, &__pwdcache)) {
748a9306
LW
3750 /* We still may be able to determine pw_uid and pw_gid */
3751 name_desc.dsc$w_length= strlen(name);
3752 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3753 name_desc.dsc$b_class= DSC$K_CLASS_S;
3754 name_desc.dsc$a_pointer= (char *) name;
aa689395 3755 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
748a9306
LW
3756 __pwdcache.pw_uid= uic.uic$l_uic;
3757 __pwdcache.pw_gid= uic.uic$v_group;
3758 }
c07a80fd 3759 else {
aa689395 3760 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
3761 set_vaxc_errno(sts);
3762 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
c07a80fd 3763 return NULL;
3764 }
aa689395 3765 else { _ckvmssts(sts); }
c07a80fd 3766 }
748a9306 3767 }
748a9306
LW
3768 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
3769 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
3770 __pwdcache.pw_name= __pw_namecache;
3771 return &__pwdcache;
3772} /* end of my_getpwnam() */
a0d0e21e
LW
3773/*}}}*/
3774
748a9306
LW
3775/*
3776 * Get information for a particular UIC or UID.
3777 * Called by my_getpwent with uid=-1 to list all users.
3778*/
3779/*{{{struct passwd *my_getpwuid(Uid_t uid)*/
3780struct passwd *my_getpwuid(Uid_t uid)
a0d0e21e 3781{
748a9306
LW
3782 const $DESCRIPTOR(name_desc,__pw_namecache);
3783 unsigned short lname;
3784 union uicdef uic;
3785 unsigned long int status;
d28f7c37 3786 dTHX;
748a9306
LW
3787
3788 if (uid == (unsigned int) -1) {
3789 do {
3790 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
3791 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
c07a80fd 3792 set_vaxc_errno(status);
3793 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
748a9306
LW
3794 my_endpwent();
3795 return NULL;
3796 }
3797 else { _ckvmssts(status); }
3798 } while (!valid_uic (uic));
3799 }
3800 else {
3801 uic.uic$l_uic= uid;
c07a80fd 3802 if (!uic.uic$v_group)
76e3520e 3803 uic.uic$v_group= PerlProc_getgid();
748a9306
LW
3804 if (valid_uic(uic))
3805 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
3806 else status = SS$_IVIDENT;
c07a80fd 3807 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
3808 status == RMS$_PRV) {
3809 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3810 return NULL;
3811 }
3812 else { _ckvmssts(status); }
748a9306
LW
3813 }
3814 __pw_namecache[lname]= '\0';
01b8edb6 3815 __mystrtolower(__pw_namecache);
748a9306
LW
3816
3817 __pwdcache = __passwd_empty;
3818 __pwdcache.pw_name = __pw_namecache;
3819
3820/* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
3821 The identifier's value is usually the UIC, but it doesn't have to be,
3822 so if we can, we let fillpasswd update this. */
3823 __pwdcache.pw_uid = uic.uic$l_uic;
3824 __pwdcache.pw_gid = uic.uic$v_group;
3825
c07a80fd 3826 fillpasswd(__pw_namecache, &__pwdcache);
748a9306 3827 return &__pwdcache;
a0d0e21e 3828
748a9306
LW
3829} /* end of my_getpwuid() */
3830/*}}}*/
3831
3832/*
3833 * Get information for next user.
3834*/
3835/*{{{struct passwd *my_getpwent()*/
3836struct passwd *my_getpwent()
3837{
3838 return (my_getpwuid((unsigned int) -1));
3839}
3840/*}}}*/
a0d0e21e 3841
748a9306
LW
3842/*
3843 * Finish searching rights database for users.
3844*/
3845/*{{{void my_endpwent()*/
3846void my_endpwent()
3847{
d28f7c37 3848 dTHX;
748a9306
LW
3849 if (contxt) {
3850 _ckvmssts(sys$finish_rdb(&contxt));
3851 contxt= 0;
3852 }
a0d0e21e
LW
3853}
3854/*}}}*/
748a9306 3855
61bb5906
CB
3856#ifdef HOMEGROWN_POSIX_SIGNALS
3857 /* Signal handling routines, pulled into the core from POSIX.xs.
3858 *
3859 * We need these for threads, so they've been rolled into the core,
3860 * rather than left in POSIX.xs.
3861 *
3862 * (DRS, Oct 23, 1997)
3863 */
5b411029 3864
61bb5906
CB
3865 /* sigset_t is atomic under VMS, so these routines are easy */
3866/*{{{int my_sigemptyset(sigset_t *) */
5b411029 3867int my_sigemptyset(sigset_t *set) {
61bb5906
CB
3868 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3869 *set = 0; return 0;
5b411029 3870}
61bb5906
CB
3871/*}}}*/
3872
3873
3874/*{{{int my_sigfillset(sigset_t *)*/
5b411029 3875int my_sigfillset(sigset_t *set) {
61bb5906
CB
3876 int i;
3877 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3878 for (i = 0; i < NSIG; i++) *set |= (1 << i);
3879 return 0;
5b411029 3880}
61bb5906
CB
3881/*}}}*/
3882
3883
3884/*{{{int my_sigaddset(sigset_t *set, int sig)*/
5b411029 3885int my_sigaddset(sigset_t *set, int sig) {
61bb5906
CB
3886 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3887 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3888 *set |= (1 << (sig - 1));
3889 return 0;
5b411029 3890}
61bb5906
CB
3891/*}}}*/
3892
3893
3894/*{{{int my_sigdelset(sigset_t *set, int sig)*/
5b411029 3895int my_sigdelset(sigset_t *set, int sig) {
61bb5906
CB
3896 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3897 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3898 *set &= ~(1 << (sig - 1));
3899 return 0;
5b411029 3900}
61bb5906
CB
3901/*}}}*/
3902
3903
3904/*{{{int my_sigismember(sigset_t *set, int sig)*/
5b411029 3905int my_sigismember(sigset_t *set, int sig) {
61bb5906
CB
3906 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3907 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3908 *set & (1 << (sig - 1));
5b411029 3909}
61bb5906 3910/*}}}*/
5b411029 3911
5b411029 3912
61bb5906
CB
3913/*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
3914int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
3915 sigset_t tempmask;
3916
3917 /* If set and oset are both null, then things are badly wrong. Bail out. */
3918 if ((oset == NULL) && (set == NULL)) {
3919 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5b411029
MB
3920 return -1;
3921 }
5b411029 3922
61bb5906
CB
3923 /* If set's null, then we're just handling a fetch. */
3924 if (set == NULL) {
3925 tempmask = sigblock(0);
3926 }
3927 else {
3928 switch (how) {
3929 case SIG_SETMASK:
3930 tempmask = sigsetmask(*set);
3931 break;
3932 case SIG_BLOCK:
3933 tempmask = sigblock(*set);
3934 break;
3935 case SIG_UNBLOCK:
3936 tempmask = sigblock(0);
3937 sigsetmask(*oset & ~tempmask);
3938 break;
3939 default:
3940 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3941 return -1;
3942 }
3943 }
3944
3945 /* Did they pass us an oset? If so, stick our holding mask into it */
3946 if (oset)
3947 *oset = tempmask;
5b411029 3948
61bb5906 3949 return 0;
5b411029 3950}
61bb5906
CB
3951/*}}}*/
3952#endif /* HOMEGROWN_POSIX_SIGNALS */
3953
5b411029 3954
ff0cee69 3955/* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
3956 * my_utime(), and flex_stat(), all of which operate on UTC unless
3957 * VMSISH_TIMES is true.
3958 */
3959/* method used to handle UTC conversions:
3960 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
e518068a 3961 */
ff0cee69 3962static int gmtime_emulation_type;
3963/* number of secs to add to UTC POSIX-style time to get local time */
3964static long int utc_offset_secs;
e518068a 3965
ff0cee69 3966/* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
3967 * in vmsish.h. #undef them here so we can call the CRTL routines
3968 * directly.
e518068a 3969 */
3970#undef gmtime
ff0cee69 3971#undef localtime
3972#undef time
3973
61bb5906
CB
3974#if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
3975# define RTL_USES_UTC 1
3976#endif
3977
3978static time_t toutc_dst(time_t loc) {
3979 struct tm *rsltmp;
3980
3981 if ((rsltmp = localtime(&loc)) == NULL) return -1;
3982 loc -= utc_offset_secs;
3983 if (rsltmp->tm_isdst) loc -= 3600;
3984 return loc;
3985}
3986#define _toutc(secs) ((secs) == -1 ? -1 : \
3987 ((gmtime_emulation_type || my_time(NULL)), \
3988 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
3989 ((secs) - utc_offset_secs))))
3990
3991static time_t toloc_dst(time_t utc) {
3992 struct tm *rsltmp;
3993
3994 utc += utc_offset_secs;
3995 if ((rsltmp = localtime(&utc)) == NULL) return -1;
3996 if (rsltmp->tm_isdst) utc += 3600;
3997 return utc;
3998}
3999#define _toloc(secs) ((secs) == -1 ? -1 : \
4000 ((gmtime_emulation_type || my_time(NULL)), \
4001 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
4002 ((secs) + utc_offset_secs))))
4003
4004
ff0cee69 4005/* my_time(), my_localtime(), my_gmtime()
61bb5906 4006 * By default traffic in UTC time values, using CRTL gmtime() or
ff0cee69 4007 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
61bb5906
CB
4008 * Note: We need to use these functions even when the CRTL has working
4009 * UTC support, since they also handle C<use vmsish qw(times);>
4010 *
ff0cee69 4011 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
bd3fa61c 4012 * Modified by Charles Bailey <bailey@newman.upenn.edu>
ff0cee69 4013 */
4014
4015/*{{{time_t my_time(time_t *timep)*/
4016time_t my_time(time_t *timep)
e518068a 4017{
d28f7c37 4018 dTHX;
e518068a 4019 time_t when;
61bb5906 4020 struct tm *tm_p;
e518068a 4021
4022 if (gmtime_emulation_type == 0) {
61bb5906
CB
4023 int dstnow;
4024 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
4025 /* results of calls to gmtime() and localtime() */
4026 /* for same &base */
ff0cee69 4027
e518068a 4028 gmtime_emulation_type++;
ff0cee69 4029 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
f675dbe5 4030 char off[LNM$C_NAMLENGTH+1];;
ff0cee69 4031
e518068a 4032 gmtime_emulation_type++;
f675dbe5 4033 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
e518068a 4034 gmtime_emulation_type++;
d28f7c37 4035 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
ff0cee69 4036 }
4037 else { utc_offset_secs = atol(off); }
e518068a 4038 }
ff0cee69 4039 else { /* We've got a working gmtime() */
4040 struct tm gmt, local;
e518068a 4041
ff0cee69 4042 gmt = *tm_p;
4043 tm_p = localtime(&base);
4044 local = *tm_p;
4045 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
4046 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
4047 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
4048 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
4049 }
e518068a 4050 }
ff0cee69 4051
4052 when = time(NULL);
61bb5906
CB
4053# ifdef VMSISH_TIME
4054# ifdef RTL_USES_UTC
4055 if (VMSISH_TIME) when = _toloc(when);
4056# else
4057 if (!VMSISH_TIME) when = _toutc(when);
4058# endif
4059# endif
ff0cee69 4060 if (timep != NULL) *timep = when;
4061 return when;
4062
4063} /* end of my_time() */
4064/*}}}*/
4065
4066
4067/*{{{struct tm *my_gmtime(const time_t *timep)*/
4068struct tm *
4069my_gmtime(const time_t *timep)
4070{
d28f7c37 4071 dTHX;
ff0cee69 4072 char *p;
4073 time_t when;
61bb5906 4074 struct tm *rsltmp;
ff0cee69 4075
68dc0745 4076 if (timep == NULL) {
4077 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4078 return NULL;
4079 }
4080 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 4081
4082 when = *timep;
4083# ifdef VMSISH_TIME
61bb5906
CB
4084 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
4085# endif
4086# ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
4087 return gmtime(&when);
4088# else
ff0cee69 4089 /* CRTL localtime() wants local time as input, so does no tz correction */
61bb5906
CB
4090 rsltmp = localtime(&when);
4091 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
4092 return rsltmp;
4093#endif
e518068a 4094} /* end of my_gmtime() */
e518068a 4095/*}}}*/
4096
4097
ff0cee69 4098/*{{{struct tm *my_localtime(const time_t *timep)*/
4099struct tm *
4100my_localtime(const time_t *timep)
4101{
d28f7c37 4102 dTHX;
ff0cee69 4103 time_t when;
61bb5906 4104 struct tm *rsltmp;
ff0cee69 4105
68dc0745 4106 if (timep == NULL) {
4107 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4108 return NULL;
4109 }
4110 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 4111 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
4112
4113 when = *timep;
61bb5906 4114# ifdef RTL_USES_UTC
ff0cee69 4115# ifdef VMSISH_TIME
61bb5906 4116 if (VMSISH_TIME) when = _toutc(when);
ff0cee69 4117# endif
61bb5906 4118 /* CRTL localtime() wants UTC as input, does tz correction itself */
ff0cee69 4119 return localtime(&when);
61bb5906
CB
4120# else
4121# ifdef VMSISH_TIME
4122 if (!VMSISH_TIME) when = _toloc(when); /* Input was UTC */
4123# endif
4124# endif
4125 /* CRTL localtime() wants local time as input, so does no tz correction */
4126 rsltmp = localtime(&when);
4127 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = -1;
4128 return rsltmp;
ff0cee69 4129
4130} /* end of my_localtime() */
4131/*}}}*/
4132
4133/* Reset definitions for later calls */
4134#define gmtime(t) my_gmtime(t)
4135#define localtime(t) my_localtime(t)
4136#define time(t) my_time(t)
4137
4138
4139/* my_utime - update modification time of a file
4140 * calling sequence is identical to POSIX utime(), but under
4141 * VMS only the modification time is changed; ODS-2 does not
4142 * maintain access times. Restrictions differ from the POSIX
4143 * definition in that the time can be changed as long as the
4144 * caller has permission to execute the necessary IO$_MODIFY $QIO;
4145 * no separate checks are made to insure that the caller is the
4146 * owner of the file or has special privs enabled.
4147 * Code here is based on Joe Meadows' FILE utility.
4148 */
4149
4150/* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
4151 * to VMS epoch (01-JAN-1858 00:00:00.00)
4152 * in 100 ns intervals.
4153 */
4154static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
4155
4156/*{{{int my_utime(char *path, struct utimbuf *utimes)*/
4157int my_utime(char *file, struct utimbuf *utimes)
4158{
d28f7c37 4159 dTHX;
ff0cee69 4160 register int i;
4161 long int bintime[2], len = 2, lowbit, unixtime,
4162 secscale = 10000000; /* seconds --> 100 ns intervals */
4163 unsigned long int chan, iosb[2], retsts;
4164 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
4165 struct FAB myfab = cc$rms_fab;
4166 struct NAM mynam = cc$rms_nam;
4167#if defined (__DECC) && defined (__VAX)
4168 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
4169 * at least through VMS V6.1, which causes a type-conversion warning.
4170 */
4171# pragma message save
4172# pragma message disable cvtdiftypes
4173#endif
4174 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
4175 struct fibdef myfib;
4176#if defined (__DECC) && defined (__VAX)
4177 /* This should be right after the declaration of myatr, but due
4178 * to a bug in VAX DEC C, this takes effect a statement early.
4179 */
4180# pragma message restore
4181#endif
4182 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
4183 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
4184 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
4185
4186 if (file == NULL || *file == '\0') {
4187 set_errno(ENOENT);
4188 set_vaxc_errno(LIB$_INVARG);
4189 return -1;
4190 }
4191 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
4192
4193 if (utimes != NULL) {
4194 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
4195 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
4196 * Since time_t is unsigned long int, and lib$emul takes a signed long int
4197 * as input, we force the sign bit to be clear by shifting unixtime right
4198 * one bit, then multiplying by an extra factor of 2 in lib$emul().
4199 */
4200 lowbit = (utimes->modtime & 1) ? secscale : 0;
4201 unixtime = (long int) utimes->modtime;
61bb5906
CB
4202# ifdef VMSISH_TIME
4203 /* If input was UTC; convert to local for sys svc */
4204 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
ff0cee69 4205# endif
4206 unixtime >> 1; secscale << 1;
4207 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
4208 if (!(retsts & 1)) {
4209 set_errno(EVMSERR);
4210 set_vaxc_errno(retsts);
4211 return -1;
4212 }
4213 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
4214 if (!(retsts & 1)) {
4215 set_errno(EVMSERR);
4216 set_vaxc_errno(retsts);
4217 return -1;
4218 }
4219 }
4220 else {
4221 /* Just get the current time in VMS format directly */
4222 retsts = sys$gettim(bintime);
4223 if (!(retsts & 1)) {
4224 set_errno(EVMSERR);
4225 set_vaxc_errno(retsts);
4226 return -1;
4227 }
4228 }
4229
4230 myfab.fab$l_fna = vmsspec;
4231 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
4232 myfab.fab$l_nam = &mynam;
4233 mynam.nam$l_esa = esa;
4234 mynam.nam$b_ess = (unsigned char) sizeof esa;
4235 mynam.nam$l_rsa = rsa;
4236 mynam.nam$b_rss = (unsigned char) sizeof rsa;
4237
4238 /* Look for the file to be affected, letting RMS parse the file
4239 * specification for us as well. I have set errno using only
4240 * values documented in the utime() man page for VMS POSIX.
4241 */
4242 retsts = sys$parse(&myfab,0,0);
4243 if (!(retsts & 1)) {
4244 set_vaxc_errno(retsts);
4245 if (retsts == RMS$_PRV) set_errno(EACCES);
4246 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4247 else set_errno(EVMSERR);
4248 return -1;
4249 }
4250 retsts = sys$search(&myfab,0,0);
4251 if (!(retsts & 1)) {
4252 set_vaxc_errno(retsts);
4253 if (retsts == RMS$_PRV) set_errno(EACCES);
4254 else if (retsts == RMS$_FNF) set_errno(ENOENT);
4255 else set_errno(EVMSERR);
4256 return -1;
4257 }
4258
4259 devdsc.dsc$w_length = mynam.nam$b_dev;
4260 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
4261
4262 retsts = sys$assign(&devdsc,&chan,0,0);
4263 if (!(retsts & 1)) {
4264 set_vaxc_errno(retsts);
4265 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
4266 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
4267 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
4268 else set_errno(EVMSERR);
4269 return -1;
4270 }
4271
4272 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
4273 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
4274
4275 memset((void *) &myfib, 0, sizeof myfib);
4276#ifdef __DECC
4277 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
4278 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
4279 /* This prevents the revision time of the file being reset to the current
4280 * time as a result of our IO$_MODIFY $QIO. */
4281 myfib.fib$l_acctl = FIB$M_NORECORD;
4282#else
4283 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
4284 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
4285 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
4286#endif
4287 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
4288 _ckvmssts(sys$dassgn(chan));
4289 if (retsts & 1) retsts = iosb[0];
4290 if (!(retsts & 1)) {
4291 set_vaxc_errno(retsts);
4292 if (retsts == SS$_NOPRIV) set_errno(EACCES);
4293 else set_errno(EVMSERR);
4294 return -1;
4295 }
4296
4297 return 0;
4298} /* end of my_utime() */
4299/*}}}*/
4300
748a9306
LW
4301/*
4302 * flex_stat, flex_fstat
4303 * basic stat, but gets it right when asked to stat
4304 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
4305 */
4306
4307/* encode_dev packs a VMS device name string into an integer to allow
4308 * simple comparisons. This can be used, for example, to check whether two
4309 * files are located on the same device, by comparing their encoded device
4310 * names. Even a string comparison would not do, because stat() reuses the
4311 * device name buffer for each call; so without encode_dev, it would be
4312 * necessary to save the buffer and use strcmp (this would mean a number of
4313 * changes to the standard Perl code, to say nothing of what a Perl script
4314 * would have to do.
4315 *
4316 * The device lock id, if it exists, should be unique (unless perhaps compared
4317 * with lock ids transferred from other nodes). We have a lock id if the disk is
4318 * mounted cluster-wide, which is when we tend to get long (host-qualified)
4319 * device names. Thus we use the lock id in preference, and only if that isn't
4320 * available, do we try to pack the device name into an integer (flagged by
4321 * the sign bit (LOCKID_MASK) being set).
4322 *
e518068a 4323 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
748a9306
LW
4324 * name and its encoded form, but it seems very unlikely that we will find
4325 * two files on different disks that share the same encoded device names,
4326 * and even more remote that they will share the same file id (if the test
4327 * is to check for the same file).
4328 *
4329 * A better method might be to use sys$device_scan on the first call, and to
4330 * search for the device, returning an index into the cached array.
4331 * The number returned would be more intelligable.
4332 * This is probably not worth it, and anyway would take quite a bit longer
4333 * on the first call.
4334 */
4335#define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
aa689395 4336static mydev_t encode_dev (const char *dev)
748a9306
LW
4337{
4338 int i;
4339 unsigned long int f;
aa689395 4340 mydev_t enc;
748a9306
LW
4341 char c;
4342 const char *q;
d28f7c37 4343 dTHX;
748a9306
LW
4344
4345 if (!dev || !dev[0]) return 0;
4346
4347#if LOCKID_MASK
4348 {
4349 struct dsc$descriptor_s dev_desc;
4350 unsigned long int status, lockid, item = DVI$_LOCKID;
4351
4352 /* For cluster-mounted disks, the disk lock identifier is unique, so we
4353 can try that first. */
4354 dev_desc.dsc$w_length = strlen (dev);
4355 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
4356 dev_desc.dsc$b_class = DSC$K_CLASS_S;
4357 dev_desc.dsc$a_pointer = (char *) dev;
4358 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
4359 if (lockid) return (lockid & ~LOCKID_MASK);
4360 }
a0d0e21e 4361#endif
748a9306
LW
4362
4363 /* Otherwise we try to encode the device name */
4364 enc = 0;
4365 f = 1;
4366 i = 0;
4367 for (q = dev + strlen(dev); q--; q >= dev) {
4368 if (isdigit (*q))
4369 c= (*q) - '0';
4370 else if (isalpha (toupper (*q)))
4371 c= toupper (*q) - 'A' + (char)10;
4372 else
4373 continue; /* Skip '$'s */
4374 i++;
4375 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
4376 if (i>1) f *= 36;
4377 enc += f * (unsigned long int) c;
4378 }
4379 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
4380
4381} /* end of encode_dev() */
4382
4383static char namecache[NAM$C_MAXRSS+1];
4384
4385static int
4386is_null_device(name)
4387 const char *name;
4388{
d28f7c37 4389 dTHX;
748a9306
LW
4390 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
4391 The underscore prefix, controller letter, and unit number are
4392 independently optional; for our purposes, the colon punctuation
4393 is not. The colon can be trailed by optional directory and/or
4394 filename, but two consecutive colons indicates a nodename rather
4395 than a device. [pr] */
4396 if (*name == '_') ++name;
4397 if (tolower(*name++) != 'n') return 0;
4398 if (tolower(*name++) != 'l') return 0;
4399 if (tolower(*name) == 'a') ++name;
4400 if (*name == '0') ++name;
4401 return (*name++ == ':') && (*name != ':');
4402}
4403
6b88bc9c 4404/* Do the permissions allow some operation? Assumes PL_statcache already set. */
748a9306 4405/* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
61bb5906 4406 * subset of the applicable information.
748a9306
LW
4407 */
4408/*{{{I32 cando(I32 bit, I32 effective, struct stat *statbufp)*/
4409I32
d28f7c37 4410Perl_cando(pTHX_ I32 bit, I32 effective, Stat_t *statbufp)
748a9306 4411{
6b88bc9c 4412 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
748a9306
LW
4413 else {
4414 char fname[NAM$C_MAXRSS+1];
4415 unsigned long int retsts;
4416 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
4417 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4418
4419 /* If the struct mystat is stale, we're OOL; stat() overwrites the
4420 device name on successive calls */
61bb5906
CB
4421 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
4422 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
748a9306
LW
4423 namdsc.dsc$a_pointer = fname;
4424 namdsc.dsc$w_length = sizeof fname - 1;
4425
61bb5906 4426 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
aa689395 4427 &namdsc,&namdsc.dsc$w_length,0,0);
748a9306
LW
4428 if (retsts & 1) {
4429 fname[namdsc.dsc$w_length] = '\0';
4430 return cando_by_name(bit,effective,fname);
4431 }
4432 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
d28f7c37 4433 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
748a9306
LW
4434 return FALSE;
4435 }
4436 _ckvmssts(retsts);
4437 return FALSE; /* Should never get to here */
4438 }
e518068a 4439} /* end of cando() */
748a9306
LW
4440/*}}}*/
4441
c07a80fd 4442
748a9306
LW
4443/*{{{I32 cando_by_name(I32 bit, I32 effective, char *fname)*/
4444I32
4445cando_by_name(I32 bit, I32 effective, char *fname)
4446{
4447 static char usrname[L_cuserid];
4448 static struct dsc$descriptor_s usrdsc =
4449 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
a5f75d66 4450 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
748a9306
LW
4451 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
4452 unsigned short int retlen;
d28f7c37 4453 dTHX;
748a9306
LW
4454 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4455 union prvdef curprv;
4456 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
4457 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
4458 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
4459 {0,0,0,0}};
4460
4461 if (!fname || !*fname) return FALSE;
01b8edb6 4462 /* Make sure we expand logical names, since sys$check_access doesn't */
4463 if (!strpbrk(fname,"/]>:")) {
4464 strcpy(fileified,fname);
4465 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
4466 fname = fileified;
4467 }
a5f75d66
AD
4468 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
4469 retlen = namdsc.dsc$w_length = strlen(vmsname);
4470 namdsc.dsc$a_pointer = vmsname;
4471 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
4472 vmsname[retlen-1] == ':') {
4473 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
4474 namdsc.dsc$w_length = strlen(fileified);
4475 namdsc.dsc$a_pointer = fileified;
4476 }
4477
748a9306
LW
4478 if (!usrdsc.dsc$w_length) {
4479 cuserid(usrname);
4480 usrdsc.dsc$w_length = strlen(usrname);
4481 }
a5f75d66 4482
748a9306
LW
4483 switch (bit) {
4484 case S_IXUSR:
4485 case S_IXGRP:
4486 case S_IXOTH:
4487 access = ARM$M_EXECUTE;
4488 break;
4489 case S_IRUSR:
4490 case S_IRGRP:
4491 case S_IROTH:
4492 access = ARM$M_READ;
4493 break;
4494 case S_IWUSR:
4495 case S_IWGRP:
4496 case S_IWOTH:
4497 access = ARM$M_WRITE;
4498 break;
4499 case S_IDUSR:
4500 case S_IDGRP:
4501 case S_IDOTH:
4502 access = ARM$M_DELETE;
4503 break;
4504 default:
4505 return FALSE;
4506 }
4507
4508 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
bbce6d69 4509 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
61bb5906 4510 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
bbce6d69 4511 retsts == RMS$_DIR || retsts == RMS$_DEV) {
4512 set_vaxc_errno(retsts);
4513 if (retsts == SS$_NOPRIV) set_errno(EACCES);
4514 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
4515 else set_errno(ENOENT);
a3e9d8c9 4516 return FALSE;
4517 }
748a9306
LW
4518 if (retsts == SS$_NORMAL) {
4519 if (!privused) return TRUE;
4520 /* We can get access, but only by using privs. Do we have the
4521 necessary privs currently enabled? */
4522 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
4523 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
c07a80fd 4524 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
4525 !curprv.prv$v_bypass) return FALSE;
4526 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
4527 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
748a9306
LW
4528 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
4529 return TRUE;
4530 }
3a385817
GS
4531 if (retsts == SS$_ACCONFLICT) {
4532 return TRUE;
4533 }
748a9306
LW
4534 _ckvmssts(retsts);
4535
4536 return FALSE; /* Should never get here */
4537
4538} /* end of cando_by_name() */
4539/*}}}*/
4540
4541
61bb5906 4542/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
748a9306 4543int
61bb5906 4544flex_fstat(int fd, Stat_t *statbufp)
748a9306 4545{
d28f7c37 4546 dTHX;
b7ae7a0d 4547 if (!fstat(fd,(stat_t *) statbufp)) {
6b88bc9c 4548 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
b7ae7a0d 4549 statbufp->st_dev = encode_dev(statbufp->st_devnam);
61bb5906
CB
4550# ifdef RTL_USES_UTC
4551# ifdef VMSISH_TIME
4552 if (VMSISH_TIME) {
4553 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4554 statbufp->st_atime = _toloc(statbufp->st_atime);
4555 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4556 }
4557# endif
4558# else
ff0cee69 4559# ifdef VMSISH_TIME
4560 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4561# else
4562 if (1) {
4563# endif
61bb5906
CB
4564 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4565 statbufp->st_atime = _toutc(statbufp->st_atime);
4566 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 4567 }
61bb5906 4568#endif
b7ae7a0d 4569 return 0;
4570 }
4571 return -1;
748a9306
LW
4572
4573} /* end of flex_fstat() */
4574/*}}}*/
4575
cc077a9f 4576/*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
748a9306 4577int
cc077a9f 4578flex_stat(const char *fspec, Stat_t *statbufp)
748a9306 4579{
d28f7c37 4580 dTHX;
748a9306 4581 char fileified[NAM$C_MAXRSS+1];
cc077a9f 4582 char temp_fspec[NAM$C_MAXRSS+300];
bbce6d69 4583 int retval = -1;
748a9306 4584
cc077a9f 4585 strcpy(temp_fspec, fspec);
6b88bc9c 4586 if (statbufp == (Stat_t *) &PL_statcache)
cc077a9f
HM
4587 do_tovmsspec(temp_fspec,namecache,0);
4588 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
748a9306
LW
4589 memset(statbufp,0,sizeof *statbufp);
4590 statbufp->st_dev = encode_dev("_NLA0:");
4591 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
4592 statbufp->st_uid = 0x00010001;
4593 statbufp->st_gid = 0x0001;
4594 time((time_t *)&statbufp->st_mtime);
4595 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
4596 return 0;
4597 }
4598
bbce6d69 4599 /* Try for a directory name first. If fspec contains a filename without
61bb5906 4600 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
bbce6d69 4601 * and sea:[wine.dark]water. exist, we prefer the directory here.
4602 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
4603 * not sea:[wine.dark]., if the latter exists. If the intended target is
4604 * the file with null type, specify this by calling flex_stat() with
4605 * a '.' at the end of fspec.
4606 */
cc077a9f 4607 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
bbce6d69 4608 retval = stat(fileified,(stat_t *) statbufp);
6b88bc9c 4609 if (!retval && statbufp == (Stat_t *) &PL_statcache)
aa689395 4610 strcpy(namecache,fileified);
748a9306 4611 }
cc077a9f 4612 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
ff0cee69 4613 if (!retval) {
4614 statbufp->st_dev = encode_dev(statbufp->st_devnam);
61bb5906
CB
4615# ifdef RTL_USES_UTC
4616# ifdef VMSISH_TIME
4617 if (VMSISH_TIME) {
4618 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4619 statbufp->st_atime = _toloc(statbufp->st_atime);
4620 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4621 }
4622# endif
4623# else
ff0cee69 4624# ifdef VMSISH_TIME
4625 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4626# else
4627 if (1) {
4628# endif
61bb5906
CB
4629 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4630 statbufp->st_atime = _toutc(statbufp->st_atime);
4631 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 4632 }
61bb5906 4633# endif
ff0cee69 4634 }
748a9306
LW
4635 return retval;
4636
4637} /* end of flex_stat() */
4638/*}}}*/
4639
b7ae7a0d 4640
c07a80fd 4641/*{{{char *my_getlogin()*/
4642/* VMS cuserid == Unix getlogin, except calling sequence */
4643char *
4644my_getlogin()
4645{
4646 static char user[L_cuserid];
4647 return cuserid(user);
4648}
4649/*}}}*/
4650
4651
a5f75d66
AD
4652/* rmscopy - copy a file using VMS RMS routines
4653 *
4654 * Copies contents and attributes of spec_in to spec_out, except owner
4655 * and protection information. Name and type of spec_in are used as
a3e9d8c9 4656 * defaults for spec_out. The third parameter specifies whether rmscopy()
4657 * should try to propagate timestamps from the input file to the output file.
4658 * If it is less than 0, no timestamps are preserved. If it is 0, then
4659 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
4660 * propagated to the output file at creation iff the output file specification
4661 * did not contain an explicit name or type, and the revision date is always
4662 * updated at the end of the copy operation. If it is greater than 0, then
4663 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
4664 * other than the revision date should be propagated, and bit 1 indicates
4665 * that the revision date should be propagated.
4666 *
4667 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
a5f75d66 4668 *
bd3fa61c 4669 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
a5f75d66 4670 * Incorporates, with permission, some code from EZCOPY by Tim Adye
01b8edb6 4671 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
4672 * as part of the Perl standard distribution under the terms of the
4673 * GNU General Public License or the Perl Artistic License. Copies
4674 * of each may be found in the Perl standard distribution.
a5f75d66 4675 */
a3e9d8c9 4676/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
a5f75d66 4677int
a3e9d8c9 4678rmscopy(char *spec_in, char *spec_out, int preserve_dates)
a5f75d66
AD
4679{
4680 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
4681 rsa[NAM$C_MAXRSS], ubf[32256];
4682 unsigned long int i, sts, sts2;
4683 struct FAB fab_in, fab_out;
4684 struct RAB rab_in, rab_out;
4685 struct NAM nam;
4686 struct XABDAT xabdat;
4687 struct XABFHC xabfhc;
4688 struct XABRDT xabrdt;
4689 struct XABSUM xabsum;
4690
4691 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
4692 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
4693 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4694 return 0;
4695 }
4696
4697 fab_in = cc$rms_fab;
4698 fab_in.fab$l_fna = vmsin;
4699 fab_in.fab$b_fns = strlen(vmsin);
4700 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
4701 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
4702 fab_in.fab$l_fop = FAB$M_SQO;
4703 fab_in.fab$l_nam = &nam;
a3e9d8c9 4704 fab_in.fab$l_xab = (void *) &xabdat;
a5f75d66
AD
4705
4706 nam = cc$rms_nam;
4707 nam.nam$l_rsa = rsa;
4708 nam.nam$b_rss = sizeof(rsa);
4709 nam.nam$l_esa = esa;
4710 nam.nam$b_ess = sizeof (esa);
4711 nam.nam$b_esl = nam.nam$b_rsl = 0;
4712
4713 xabdat = cc$rms_xabdat; /* To get creation date */
a3e9d8c9 4714 xabdat.xab$l_nxt = (void *) &xabfhc;
a5f75d66
AD
4715
4716 xabfhc = cc$rms_xabfhc; /* To get record length */
a3e9d8c9 4717 xabfhc.xab$l_nxt = (void *) &xabsum;
a5f75d66
AD
4718
4719 xabsum = cc$rms_xabsum; /* To get key and area information */
4720
4721 if (!((sts = sys$open(&fab_in)) & 1)) {
4722 set_vaxc_errno(sts);
4723 switch (sts) {
4724 case RMS$_FNF:
4725 case RMS$_DIR:
4726 set_errno(ENOENT); break;
4727 case RMS$_DEV:
4728 set_errno(ENODEV); break;
4729 case RMS$_SYN:
4730 set_errno(EINVAL); break;
4731 case RMS$_PRV:
4732 set_errno(EACCES); break;
4733 default:
4734 set_errno(EVMSERR);
4735 }
4736 return 0;
4737 }
4738
4739 fab_out = fab_in;
4740 fab_out.fab$w_ifi = 0;
4741 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
4742 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
4743 fab_out.fab$l_fop = FAB$M_SQO;
4744 fab_out.fab$l_fna = vmsout;
4745 fab_out.fab$b_fns = strlen(vmsout);
4746 fab_out.fab$l_dna = nam.nam$l_name;
4747 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
a3e9d8c9 4748
4749 if (preserve_dates == 0) { /* Act like DCL COPY */
4750 nam.nam$b_nop = NAM$M_SYNCHK;
4751 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
4752 if (!((sts = sys$parse(&fab_out)) & 1)) {
4753 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
4754 set_vaxc_errno(sts);
4755 return 0;
4756 }
4757 fab_out.fab$l_xab = (void *) &xabdat;
4758 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
4759 }
4760 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
4761 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
4762 preserve_dates =0; /* bitmask from this point forward */
4763
4764 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
a5f75d66
AD
4765 if (!((sts = sys$create(&fab_out)) & 1)) {
4766 set_vaxc_errno(sts);
4767 switch (sts) {
4768 case RMS$_DIR:
4769 set_errno(ENOENT); break;
4770 case RMS$_DEV:
4771 set_errno(ENODEV); break;
4772 case RMS$_SYN:
4773 set_errno(EINVAL); break;
4774 case RMS$_PRV:
4775 set_errno(EACCES); break;
4776 default:
4777 set_errno(EVMSERR);
4778 }
4779 return 0;
4780 }
4781 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
a3e9d8c9 4782 if (preserve_dates & 2) {
4783 /* sys$close() will process xabrdt, not xabdat */
4784 xabrdt = cc$rms_xabrdt;
b7ae7a0d 4785#ifndef __GNUC__
a3e9d8c9 4786 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
b7ae7a0d 4787#else
4788 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
4789 * is unsigned long[2], while DECC & VAXC use a struct */
4790 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
4791#endif
a3e9d8c9 4792 fab_out.fab$l_xab = (void *) &xabrdt;
4793 }
a5f75d66
AD
4794
4795 rab_in = cc$rms_rab;
4796 rab_in.rab$l_fab = &fab_in;
4797 rab_in.rab$l_rop = RAB$M_BIO;
4798 rab_in.rab$l_ubf = ubf;
4799 rab_in.rab$w_usz = sizeof ubf;
4800 if (!((sts = sys$connect(&rab_in)) & 1)) {
4801 sys$close(&fab_in); sys$close(&fab_out);
4802 set_errno(EVMSERR); set_vaxc_errno(sts);
4803 return 0;
4804 }
4805
4806 rab_out = cc$rms_rab;
4807 rab_out.rab$l_fab = &fab_out;
4808 rab_out.rab$l_rbf = ubf;
4809 if (!((sts = sys$connect(&rab_out)) & 1)) {
4810 sys$close(&fab_in); sys$close(&fab_out);
4811 set_errno(EVMSERR); set_vaxc_errno(sts);
4812 return 0;
4813 }
4814
4815 while ((sts = sys$read(&rab_in))) { /* always true */
4816 if (sts == RMS$_EOF) break;
4817 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
4818 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
4819 sys$close(&fab_in); sys$close(&fab_out);
4820 set_errno(EVMSERR); set_vaxc_errno(sts);
4821 return 0;
4822 }
4823 }
4824
4825 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
4826 sys$close(&fab_in); sys$close(&fab_out);
4827 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
4828 if (!(sts & 1)) {
4829 set_errno(EVMSERR); set_vaxc_errno(sts);
4830 return 0;
4831 }
4832
4833 return 1;
4834
4835} /* end of rmscopy() */
4836/*}}}*/
4837
4838
748a9306
LW
4839/*** The following glue provides 'hooks' to make some of the routines
4840 * from this file available from Perl. These routines are sufficiently
4841 * basic, and are required sufficiently early in the build process,
4842 * that's it's nice to have them available to miniperl as well as the
4843 * full Perl, so they're set up here instead of in an extension. The
4844 * Perl code which handles importation of these names into a given
4845 * package lives in [.VMS]Filespec.pm in @INC.
4846 */
4847
4848void
d28f7c37 4849rmsexpand_fromperl(pTHX_ CV *cv)
01b8edb6 4850{
4851 dXSARGS;
bbce6d69 4852 char *fspec, *defspec = NULL, *rslt;
2d8e6c8d 4853 STRLEN n_a;
01b8edb6 4854
bbce6d69 4855 if (!items || items > 2)
d28f7c37 4856 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
2d8e6c8d 4857 fspec = SvPV(ST(0),n_a);
bbce6d69 4858 if (!fspec || !*fspec) XSRETURN_UNDEF;
2d8e6c8d 4859 if (items == 2) defspec = SvPV(ST(1),n_a);
b7ae7a0d 4860
bbce6d69 4861 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
4862 ST(0) = sv_newmortal();
4863 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
740ce14c 4864 XSRETURN(1);
01b8edb6 4865}
4866
4867void
d28f7c37 4868vmsify_fromperl(pTHX_ CV *cv)
748a9306
LW
4869{
4870 dXSARGS;
4871 char *vmsified;
2d8e6c8d 4872 STRLEN n_a;
748a9306 4873
d28f7c37 4874 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
2d8e6c8d 4875 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
748a9306
LW
4876 ST(0) = sv_newmortal();
4877 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
4878 XSRETURN(1);
4879}
4880
4881void
d28f7c37 4882unixify_fromperl(pTHX_ CV *cv)
748a9306
LW
4883{
4884 dXSARGS;
4885 char *unixified;
2d8e6c8d 4886 STRLEN n_a;
748a9306 4887
d28f7c37 4888 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
2d8e6c8d 4889 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
748a9306
LW
4890 ST(0) = sv_newmortal();
4891 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
4892 XSRETURN(1);
4893}
4894
4895void
d28f7c37 4896fileify_fromperl(pTHX_ CV *cv)
748a9306
LW
4897{
4898 dXSARGS;
4899 char *fileified;
2d8e6c8d 4900 STRLEN n_a;
748a9306 4901
d28f7c37 4902 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
2d8e6c8d 4903 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
748a9306
LW
4904 ST(0) = sv_newmortal();
4905 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
4906 XSRETURN(1);
4907}
4908
4909void
d28f7c37 4910pathify_fromperl(pTHX_ CV *cv)
748a9306
LW
4911{
4912 dXSARGS;
4913 char *pathified;
2d8e6c8d 4914 STRLEN n_a;
748a9306 4915
d28f7c37 4916 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
2d8e6c8d 4917 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
748a9306
LW
4918 ST(0) = sv_newmortal();
4919 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
4920 XSRETURN(1);
4921}
4922
4923void
d28f7c37 4924vmspath_fromperl(pTHX_ CV *cv)
748a9306
LW
4925{
4926 dXSARGS;
4927 char *vmspath;
2d8e6c8d 4928 STRLEN n_a;
748a9306 4929
d28f7c37 4930 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
2d8e6c8d 4931 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
748a9306
LW
4932 ST(0) = sv_newmortal();
4933 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
4934 XSRETURN(1);
4935}
4936
4937void
d28f7c37 4938unixpath_fromperl(pTHX_ CV *cv)
748a9306
LW
4939{
4940 dXSARGS;
4941 char *unixpath;
2d8e6c8d 4942 STRLEN n_a;
748a9306 4943
d28f7c37 4944 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
2d8e6c8d 4945 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
748a9306
LW
4946 ST(0) = sv_newmortal();
4947 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
4948 XSRETURN(1);
4949}
4950
4951void
d28f7c37 4952candelete_fromperl(pTHX_ CV *cv)
748a9306
LW
4953{
4954 dXSARGS;
a5f75d66
AD
4955 char fspec[NAM$C_MAXRSS+1], *fsp;
4956 SV *mysv;
4957 IO *io;
2d8e6c8d 4958 STRLEN n_a;
748a9306 4959
d28f7c37 4960 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
a5f75d66
AD
4961
4962 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
4963 if (SvTYPE(mysv) == SVt_PVGV) {
c0020e85 4964 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec,1)) {
a5f75d66 4965 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 4966 ST(0) = &PL_sv_no;
a5f75d66
AD
4967 XSRETURN(1);
4968 }
4969 fsp = fspec;
4970 }
4971 else {
2d8e6c8d 4972 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
a5f75d66 4973 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 4974 ST(0) = &PL_sv_no;
a5f75d66
AD
4975 XSRETURN(1);
4976 }
4977 }
4978
54310121 4979 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
a5f75d66
AD
4980 XSRETURN(1);
4981}
4982
4983void
d28f7c37 4984rmscopy_fromperl(pTHX_ CV *cv)
a5f75d66
AD
4985{
4986 dXSARGS;
4987 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
a3e9d8c9 4988 int date_flag;
a5f75d66
AD
4989 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
4990 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4991 unsigned long int sts;
4992 SV *mysv;
4993 IO *io;
2d8e6c8d 4994 STRLEN n_a;
a5f75d66 4995
a3e9d8c9 4996 if (items < 2 || items > 3)
d28f7c37 4997 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
a5f75d66
AD
4998
4999 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
5000 if (SvTYPE(mysv) == SVt_PVGV) {
c0020e85 5001 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec,1)) {
a5f75d66 5002 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 5003 ST(0) = &PL_sv_no;
a5f75d66
AD
5004 XSRETURN(1);
5005 }
5006 inp = inspec;
5007 }
5008 else {
2d8e6c8d 5009 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
a5f75d66 5010 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 5011 ST(0) = &PL_sv_no;
a5f75d66
AD
5012 XSRETURN(1);
5013 }
5014 }
5015 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
5016 if (SvTYPE(mysv) == SVt_PVGV) {
c0020e85 5017 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec,1)) {
a5f75d66 5018 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 5019 ST(0) = &PL_sv_no;
a5f75d66
AD
5020 XSRETURN(1);
5021 }
5022 outp = outspec;
5023 }
5024 else {
2d8e6c8d 5025 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
a5f75d66 5026 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 5027 ST(0) = &PL_sv_no;
a5f75d66
AD
5028 XSRETURN(1);
5029 }
5030 }
a3e9d8c9 5031 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
a5f75d66 5032
54310121 5033 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
748a9306
LW
5034 XSRETURN(1);
5035}
5036
5037void
5038init_os_extras()
5039{
5040 char* file = __FILE__;
d28f7c37 5041 dTHX;
ebd8c45c
DS
5042 char temp_buff[512];
5043 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
5044 no_translate_barewords = TRUE;
5045 } else {
5046 no_translate_barewords = FALSE;
5047 }
748a9306 5048
740ce14c 5049 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
a5f75d66
AD
5050 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
5051 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
5052 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
5053 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
5054 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
5055 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
5056 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
5057 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
17f28c40 5058
748a9306
LW
5059 return;
5060}
5061
5062/* End of vms.c */