This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
test.pl runperl() nits from Chris Nandor and Craig Berry,
[perl5.git] / vms / vms.c
... / ...
CommitLineData
1/* vms.c
2 *
3 * VMS-specific routines for perl5
4 * Version: 5.7.0
5 *
6 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
7 * and Perl_cando by Craig Berry
8 * 29-Aug-2000 Charles Lane's piping improvements rolled in
9 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
10 */
11
12#include <accdef.h>
13#include <acedef.h>
14#include <acldef.h>
15#include <armdef.h>
16#include <atrdef.h>
17#include <chpdef.h>
18#include <clidef.h>
19#include <climsgdef.h>
20#include <descrip.h>
21#include <devdef.h>
22#include <dvidef.h>
23#include <fibdef.h>
24#include <float.h>
25#include <fscndef.h>
26#include <iodef.h>
27#include <jpidef.h>
28#include <kgbdef.h>
29#include <libclidef.h>
30#include <libdef.h>
31#include <lib$routines.h>
32#include <lnmdef.h>
33#include <msgdef.h>
34#include <prvdef.h>
35#include <psldef.h>
36#include <rms.h>
37#include <shrdef.h>
38#include <ssdef.h>
39#include <starlet.h>
40#include <strdef.h>
41#include <str$routines.h>
42#include <syidef.h>
43#include <uaidef.h>
44#include <uicdef.h>
45
46/* Older versions of ssdef.h don't have these */
47#ifndef SS$_INVFILFOROP
48# define SS$_INVFILFOROP 3930
49#endif
50#ifndef SS$_NOSUCHOBJECT
51# define SS$_NOSUCHOBJECT 2696
52#endif
53
54/* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
55#define PERLIO_NOT_STDIO 0
56
57/* Don't replace system definitions of vfork, getenv, and stat,
58 * code below needs to get to the underlying CRTL routines. */
59#define DONT_MASK_RTL_CALLS
60#include "EXTERN.h"
61#include "perl.h"
62#include "XSUB.h"
63/* Anticipating future expansion in lexical warnings . . . */
64#ifndef WARN_INTERNAL
65# define WARN_INTERNAL WARN_MISC
66#endif
67
68#if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
69# define RTL_USES_UTC 1
70#endif
71
72
73/* gcc's header files don't #define direct access macros
74 * corresponding to VAXC's variant structs */
75#ifdef __GNUC__
76# define uic$v_format uic$r_uic_form.uic$v_format
77# define uic$v_group uic$r_uic_form.uic$v_group
78# define uic$v_member uic$r_uic_form.uic$v_member
79# define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
80# define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
81# define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
82# define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
83#endif
84
85#if defined(NEED_AN_H_ERRNO)
86dEXT int h_errno;
87#endif
88
89struct itmlst_3 {
90 unsigned short int buflen;
91 unsigned short int itmcode;
92 void *bufadr;
93 unsigned short int *retlen;
94};
95
96#define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
97#define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
98#define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
99#define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
100#define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
101#define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
102#define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
103#define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
104#define getredirection(a,b) mp_getredirection(aTHX_ a,b)
105
106/* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
107#define PERL_LNM_MAX_ALLOWED_INDEX 127
108
109static char *__mystrtolower(char *str)
110{
111 if (str) for (; *str; ++str) *str= tolower(*str);
112 return str;
113}
114
115static struct dsc$descriptor_s fildevdsc =
116 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
117static struct dsc$descriptor_s crtlenvdsc =
118 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
119static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
120static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
121static struct dsc$descriptor_s **env_tables = defenv;
122static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
123
124/* True if we shouldn't treat barewords as logicals during directory */
125/* munching */
126static int no_translate_barewords;
127
128/* Temp for subprocess commands */
129static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
130
131#ifndef RTL_USES_UTC
132static int tz_updated = 1;
133#endif
134
135/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
136int
137Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
138 struct dsc$descriptor_s **tabvec, unsigned long int flags)
139{
140 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
141 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
142 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
143 unsigned char acmode;
144 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
145 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
146 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
147 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
148 {0, 0, 0, 0}};
149 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
150#if defined(PERL_IMPLICIT_CONTEXT)
151 pTHX = NULL;
152# if defined(USE_5005THREADS)
153 /* We jump through these hoops because we can be called at */
154 /* platform-specific initialization time, which is before anything is */
155 /* set up--we can't even do a plain dTHX since that relies on the */
156 /* interpreter structure to be initialized */
157 if (PL_curinterp) {
158 aTHX = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
159 } else {
160 aTHX = NULL;
161 }
162# else
163 if (PL_curinterp) {
164 aTHX = PERL_GET_INTERP;
165 } else {
166 aTHX = NULL;
167 }
168
169# endif
170#endif
171
172 if (!lnm || !eqv || idx > PERL_LNM_MAX_ALLOWED_INDEX) {
173 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
174 }
175 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
176 *cp2 = _toupper(*cp1);
177 if (cp1 - lnm > LNM$C_NAMLENGTH) {
178 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
179 return 0;
180 }
181 }
182 lnmdsc.dsc$w_length = cp1 - lnm;
183 lnmdsc.dsc$a_pointer = uplnm;
184 uplnm[lnmdsc.dsc$w_length] = '\0';
185 secure = flags & PERL__TRNENV_SECURE;
186 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
187 if (!tabvec || !*tabvec) tabvec = env_tables;
188
189 for (curtab = 0; tabvec[curtab]; curtab++) {
190 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
191 if (!ivenv && !secure) {
192 char *eq, *end;
193 int i;
194 if (!environ) {
195 ivenv = 1;
196 Perl_warn(aTHX_ "Can't read CRTL environ\n");
197 continue;
198 }
199 retsts = SS$_NOLOGNAM;
200 for (i = 0; environ[i]; i++) {
201 if ((eq = strchr(environ[i],'=')) &&
202 !strncmp(environ[i],uplnm,eq - environ[i])) {
203 eq++;
204 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
205 if (!eqvlen) continue;
206 retsts = SS$_NORMAL;
207 break;
208 }
209 }
210 if (retsts != SS$_NOLOGNAM) break;
211 }
212 }
213 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
214 !str$case_blind_compare(&tmpdsc,&clisym)) {
215 if (!ivsym && !secure) {
216 unsigned short int deflen = LNM$C_NAMLENGTH;
217 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
218 /* dynamic dsc to accomodate possible long value */
219 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
220 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
221 if (retsts & 1) {
222 if (eqvlen > 1024) {
223 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
224 eqvlen = 1024;
225 /* Special hack--we might be called before the interpreter's */
226 /* fully initialized, in which case either thr or PL_curcop */
227 /* might be bogus. We have to check, since ckWARN needs them */
228 /* both to be valid if running threaded */
229#if defined(USE_5005THREADS)
230 if (thr && PL_curcop) {
231#endif
232 if (ckWARN(WARN_MISC)) {
233 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
234 }
235#if defined(USE_5005THREADS)
236 } else {
237 Perl_warner(aTHX_ WARN_MISC,"Value of CLI symbol \"%s\" too long",lnm);
238 }
239#endif
240
241 }
242 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
243 }
244 _ckvmssts(lib$sfree1_dd(&eqvdsc));
245 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
246 if (retsts == LIB$_NOSUCHSYM) continue;
247 break;
248 }
249 }
250 else if (!ivlnm) {
251 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
252 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
253 if (retsts == SS$_NOLOGNAM) continue;
254 /* PPFs have a prefix */
255 if (
256#if INTSIZE == 4
257 *((int *)uplnm) == *((int *)"SYS$") &&
258#endif
259 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
260 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
261 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
262 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
263 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
264 memcpy(eqv,eqv+4,eqvlen-4);
265 eqvlen -= 4;
266 }
267 break;
268 }
269 }
270 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
271 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
272 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
273 retsts == SS$_NOLOGNAM) {
274 set_errno(EINVAL); set_vaxc_errno(retsts);
275 }
276 else _ckvmssts(retsts);
277 return 0;
278} /* end of vmstrnenv */
279/*}}}*/
280
281/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
282/* Define as a function so we can access statics. */
283int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
284{
285 return vmstrnenv(lnm,eqv,idx,fildev,
286#ifdef SECURE_INTERNAL_GETENV
287 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
288#else
289 0
290#endif
291 );
292}
293/*}}}*/
294
295/* my_getenv
296 * Note: Uses Perl temp to store result so char * can be returned to
297 * caller; this pointer will be invalidated at next Perl statement
298 * transition.
299 * We define this as a function rather than a macro in terms of my_getenv_len()
300 * so that it'll work when PL_curinterp is undefined (and we therefore can't
301 * allocate SVs).
302 */
303/*{{{ char *my_getenv(const char *lnm, bool sys)*/
304char *
305Perl_my_getenv(pTHX_ const char *lnm, bool sys)
306{
307 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
308 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
309 unsigned long int idx = 0;
310 int trnsuccess, success, secure, saverr, savvmserr;
311 SV *tmpsv;
312
313 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
314 /* Set up a temporary buffer for the return value; Perl will
315 * clean it up at the next statement transition */
316 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
317 if (!tmpsv) return NULL;
318 eqv = SvPVX(tmpsv);
319 }
320 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
321 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
322 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
323 getcwd(eqv,LNM$C_NAMLENGTH);
324 return eqv;
325 }
326 else {
327 if ((cp2 = strchr(lnm,';')) != NULL) {
328 strcpy(uplnm,lnm);
329 uplnm[cp2-lnm] = '\0';
330 idx = strtoul(cp2+1,NULL,0);
331 lnm = uplnm;
332 }
333 /* Impose security constraints only if tainting */
334 if (sys) {
335 /* Impose security constraints only if tainting */
336 secure = PL_curinterp ? PL_tainting : will_taint;
337 saverr = errno; savvmserr = vaxc$errno;
338 }
339 else secure = 0;
340 success = vmstrnenv(lnm,eqv,idx,
341 secure ? fildev : NULL,
342#ifdef SECURE_INTERNAL_GETENV
343 secure ? PERL__TRNENV_SECURE : 0
344#else
345 0
346#endif
347 );
348 /* Discard NOLOGNAM on internal calls since we're often looking
349 * for an optional name, and this "error" often shows up as the
350 * (bogus) exit status for a die() call later on. */
351 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
352 return success ? eqv : Nullch;
353 }
354
355} /* end of my_getenv() */
356/*}}}*/
357
358
359/*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
360char *
361Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
362{
363 char *buf, *cp1, *cp2;
364 unsigned long idx = 0;
365 static char __my_getenv_len_eqv[LNM$C_NAMLENGTH+1];
366 int secure, saverr, savvmserr;
367 SV *tmpsv;
368
369 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
370 /* Set up a temporary buffer for the return value; Perl will
371 * clean it up at the next statement transition */
372 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
373 if (!tmpsv) return NULL;
374 buf = SvPVX(tmpsv);
375 }
376 else buf = __my_getenv_len_eqv; /* Assume no interpreter ==> single thread */
377 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
378 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
379 getcwd(buf,LNM$C_NAMLENGTH);
380 *len = strlen(buf);
381 return buf;
382 }
383 else {
384 if ((cp2 = strchr(lnm,';')) != NULL) {
385 strcpy(buf,lnm);
386 buf[cp2-lnm] = '\0';
387 idx = strtoul(cp2+1,NULL,0);
388 lnm = buf;
389 }
390 if (sys) {
391 /* Impose security constraints only if tainting */
392 secure = PL_curinterp ? PL_tainting : will_taint;
393 saverr = errno; savvmserr = vaxc$errno;
394 }
395 else secure = 0;
396 *len = vmstrnenv(lnm,buf,idx,
397 secure ? fildev : NULL,
398#ifdef SECURE_INTERNAL_GETENV
399 secure ? PERL__TRNENV_SECURE : 0
400#else
401 0
402#endif
403 );
404 /* Discard NOLOGNAM on internal calls since we're often looking
405 * for an optional name, and this "error" often shows up as the
406 * (bogus) exit status for a die() call later on. */
407 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
408 return *len ? buf : Nullch;
409 }
410
411} /* end of my_getenv_len() */
412/*}}}*/
413
414static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
415
416static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
417
418/*{{{ void prime_env_iter() */
419void
420prime_env_iter(void)
421/* Fill the %ENV associative array with all logical names we can
422 * find, in preparation for iterating over it.
423 */
424{
425 static int primed = 0;
426 HV *seenhv = NULL, *envhv;
427 SV *sv = NULL;
428 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
429 unsigned short int chan;
430#ifndef CLI$M_TRUSTED
431# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
432#endif
433 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
434 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
435 long int i;
436 bool have_sym = FALSE, have_lnm = FALSE;
437 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
438 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
439 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
440 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
441 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
442#if defined(PERL_IMPLICIT_CONTEXT)
443 pTHX;
444#endif
445#if defined(USE_5005THREADS) || defined(USE_ITHREADS)
446 static perl_mutex primenv_mutex;
447 MUTEX_INIT(&primenv_mutex);
448#endif
449
450#if defined(PERL_IMPLICIT_CONTEXT)
451 /* We jump through these hoops because we can be called at */
452 /* platform-specific initialization time, which is before anything is */
453 /* set up--we can't even do a plain dTHX since that relies on the */
454 /* interpreter structure to be initialized */
455#if defined(USE_5005THREADS)
456 if (PL_curinterp) {
457 aTHX = PL_threadnum? THR : (struct perl_thread*)SvPVX(PL_thrsv);
458 } else {
459 aTHX = NULL;
460 }
461#else
462 if (PL_curinterp) {
463 aTHX = PERL_GET_INTERP;
464 } else {
465 aTHX = NULL;
466 }
467#endif
468#endif
469
470 if (primed || !PL_envgv) return;
471 MUTEX_LOCK(&primenv_mutex);
472 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
473 envhv = GvHVn(PL_envgv);
474 /* Perform a dummy fetch as an lval to insure that the hash table is
475 * set up. Otherwise, the hv_store() will turn into a nullop. */
476 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
477
478 for (i = 0; env_tables[i]; i++) {
479 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
480 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
481 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
482 }
483 if (have_sym || have_lnm) {
484 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
485 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
486 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
487 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
488 }
489
490 for (i--; i >= 0; i--) {
491 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
492 char *start;
493 int j;
494 for (j = 0; environ[j]; j++) {
495 if (!(start = strchr(environ[j],'='))) {
496 if (ckWARN(WARN_INTERNAL))
497 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
498 }
499 else {
500 start++;
501 sv = newSVpv(start,0);
502 SvTAINTED_on(sv);
503 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
504 }
505 }
506 continue;
507 }
508 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
509 !str$case_blind_compare(&tmpdsc,&clisym)) {
510 strcpy(cmd,"Show Symbol/Global *");
511 cmddsc.dsc$w_length = 20;
512 if (env_tables[i]->dsc$w_length == 12 &&
513 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
514 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
515 flags = defflags | CLI$M_NOLOGNAM;
516 }
517 else {
518 strcpy(cmd,"Show Logical *");
519 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
520 strcat(cmd," /Table=");
521 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
522 cmddsc.dsc$w_length = strlen(cmd);
523 }
524 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
525 flags = defflags | CLI$M_NOCLISYM;
526 }
527
528 /* Create a new subprocess to execute each command, to exclude the
529 * remote possibility that someone could subvert a mbx or file used
530 * to write multiple commands to a single subprocess.
531 */
532 do {
533 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
534 0,&riseandshine,0,0,&clidsc,&clitabdsc);
535 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
536 defflags &= ~CLI$M_TRUSTED;
537 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
538 _ckvmssts(retsts);
539 if (!buf) New(1322,buf,mbxbufsiz + 1,char);
540 if (seenhv) SvREFCNT_dec(seenhv);
541 seenhv = newHV();
542 while (1) {
543 char *cp1, *cp2, *key;
544 unsigned long int sts, iosb[2], retlen, keylen;
545 register U32 hash;
546
547 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
548 if (sts & 1) sts = iosb[0] & 0xffff;
549 if (sts == SS$_ENDOFFILE) {
550 int wakect = 0;
551 while (substs == 0) { sys$hiber(); wakect++;}
552 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
553 _ckvmssts(substs);
554 break;
555 }
556 _ckvmssts(sts);
557 retlen = iosb[0] >> 16;
558 if (!retlen) continue; /* blank line */
559 buf[retlen] = '\0';
560 if (iosb[1] != subpid) {
561 if (iosb[1]) {
562 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
563 }
564 continue;
565 }
566 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
567 Perl_warner(aTHX_ WARN_INTERNAL,"Buffer overflow in prime_env_iter: %s",buf);
568
569 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
570 if (*cp1 == '(' || /* Logical name table name */
571 *cp1 == '=' /* Next eqv of searchlist */) continue;
572 if (*cp1 == '"') cp1++;
573 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
574 key = cp1; keylen = cp2 - cp1;
575 if (keylen && hv_exists(seenhv,key,keylen)) continue;
576 while (*cp2 && *cp2 != '=') cp2++;
577 while (*cp2 && *cp2 == '=') cp2++;
578 while (*cp2 && *cp2 == ' ') cp2++;
579 if (*cp2 == '"') { /* String translation; may embed "" */
580 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
581 cp2++; cp1--; /* Skip "" surrounding translation */
582 }
583 else { /* Numeric translation */
584 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
585 cp1--; /* stop on last non-space char */
586 }
587 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
588 Perl_warner(aTHX_ WARN_INTERNAL,"Ill-formed message in prime_env_iter: |%s|",buf);
589 continue;
590 }
591 PERL_HASH(hash,key,keylen);
592 sv = newSVpvn(cp2,cp1 - cp2 + 1);
593 SvTAINTED_on(sv);
594 hv_store(envhv,key,keylen,sv,hash);
595 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
596 }
597 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
598 /* get the PPFs for this process, not the subprocess */
599 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
600 char eqv[LNM$C_NAMLENGTH+1];
601 int trnlen, i;
602 for (i = 0; ppfs[i]; i++) {
603 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
604 sv = newSVpv(eqv,trnlen);
605 SvTAINTED_on(sv);
606 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
607 }
608 }
609 }
610 primed = 1;
611 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
612 if (buf) Safefree(buf);
613 if (seenhv) SvREFCNT_dec(seenhv);
614 MUTEX_UNLOCK(&primenv_mutex);
615 return;
616
617} /* end of prime_env_iter */
618/*}}}*/
619
620
621/*{{{ int vmssetenv(char *lnm, char *eqv)*/
622/* Define or delete an element in the same "environment" as
623 * vmstrnenv(). If an element is to be deleted, it's removed from
624 * the first place it's found. If it's to be set, it's set in the
625 * place designated by the first element of the table vector.
626 * Like setenv() returns 0 for success, non-zero on error.
627 */
628int
629Perl_vmssetenv(pTHX_ char *lnm, char *eqv, struct dsc$descriptor_s **tabvec)
630{
631 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
632 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
633 unsigned long int retsts, usermode = PSL$C_USER;
634 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
635 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
636 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
637 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
638 $DESCRIPTOR(local,"_LOCAL");
639
640 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
641 *cp2 = _toupper(*cp1);
642 if (cp1 - lnm > LNM$C_NAMLENGTH) {
643 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
644 return SS$_IVLOGNAM;
645 }
646 }
647 lnmdsc.dsc$w_length = cp1 - lnm;
648 if (!tabvec || !*tabvec) tabvec = env_tables;
649
650 if (!eqv) { /* we're deleting n element */
651 for (curtab = 0; tabvec[curtab]; curtab++) {
652 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
653 int i;
654 for (i = 0; environ[i]; i++) { /* Iff it's an environ elt, reset */
655 if ((cp1 = strchr(environ[i],'=')) &&
656 !strncmp(environ[i],lnm,cp1 - environ[i])) {
657#ifdef HAS_SETENV
658 return setenv(lnm,"",1) ? vaxc$errno : 0;
659 }
660 }
661 ivenv = 1; retsts = SS$_NOLOGNAM;
662#else
663 if (ckWARN(WARN_INTERNAL))
664 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't reset CRTL environ elements (%s)",lnm);
665 ivenv = 1; retsts = SS$_NOSUCHPGM;
666 break;
667 }
668 }
669#endif
670 }
671 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
672 !str$case_blind_compare(&tmpdsc,&clisym)) {
673 unsigned int symtype;
674 if (tabvec[curtab]->dsc$w_length == 12 &&
675 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
676 !str$case_blind_compare(&tmpdsc,&local))
677 symtype = LIB$K_CLI_LOCAL_SYM;
678 else symtype = LIB$K_CLI_GLOBAL_SYM;
679 retsts = lib$delete_symbol(&lnmdsc,&symtype);
680 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
681 if (retsts == LIB$_NOSUCHSYM) continue;
682 break;
683 }
684 else if (!ivlnm) {
685 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
686 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
687 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
688 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
689 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
690 }
691 }
692 }
693 else { /* we're defining a value */
694 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
695#ifdef HAS_SETENV
696 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
697#else
698 if (ckWARN(WARN_INTERNAL))
699 Perl_warner(aTHX_ WARN_INTERNAL,"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
700 retsts = SS$_NOSUCHPGM;
701#endif
702 }
703 else {
704 eqvdsc.dsc$a_pointer = eqv;
705 eqvdsc.dsc$w_length = strlen(eqv);
706 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
707 !str$case_blind_compare(&tmpdsc,&clisym)) {
708 unsigned int symtype;
709 if (tabvec[0]->dsc$w_length == 12 &&
710 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
711 !str$case_blind_compare(&tmpdsc,&local))
712 symtype = LIB$K_CLI_LOCAL_SYM;
713 else symtype = LIB$K_CLI_GLOBAL_SYM;
714 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
715 }
716 else {
717 if (!*eqv) eqvdsc.dsc$w_length = 1;
718 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
719 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH;
720 if (ckWARN(WARN_MISC)) {
721 Perl_warner(aTHX_ WARN_MISC,"Value of logical \"%s\" too long. Truncating to %i bytes",lnm, LNM$C_NAMLENGTH);
722 }
723 }
724 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
725 }
726 }
727 }
728 if (!(retsts & 1)) {
729 switch (retsts) {
730 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
731 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
732 set_errno(EVMSERR); break;
733 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
734 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
735 set_errno(EINVAL); break;
736 case SS$_NOPRIV:
737 set_errno(EACCES);
738 default:
739 _ckvmssts(retsts);
740 set_errno(EVMSERR);
741 }
742 set_vaxc_errno(retsts);
743 return (int) retsts || 44; /* retsts should never be 0, but just in case */
744 }
745 else {
746 /* We reset error values on success because Perl does an hv_fetch()
747 * before each hv_store(), and if the thing we're setting didn't
748 * previously exist, we've got a leftover error message. (Of course,
749 * this fails in the face of
750 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
751 * in that the error reported in $! isn't spurious,
752 * but it's right more often than not.)
753 */
754 set_errno(0); set_vaxc_errno(retsts);
755 return 0;
756 }
757
758} /* end of vmssetenv() */
759/*}}}*/
760
761/*{{{ void my_setenv(char *lnm, char *eqv)*/
762/* This has to be a function since there's a prototype for it in proto.h */
763void
764Perl_my_setenv(pTHX_ char *lnm,char *eqv)
765{
766 if (lnm && *lnm) {
767 int len = strlen(lnm);
768 if (len == 7) {
769 char uplnm[8];
770 int i;
771 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
772 if (!strcmp(uplnm,"DEFAULT")) {
773 if (eqv && *eqv) chdir(eqv);
774 return;
775 }
776 }
777#ifndef RTL_USES_UTC
778 if (len == 6 || len == 2) {
779 char uplnm[7];
780 int i;
781 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
782 uplnm[len] = '\0';
783 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
784 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
785 }
786#endif
787 }
788 (void) vmssetenv(lnm,eqv,NULL);
789}
790/*}}}*/
791
792/*{{{static void vmssetuserlnm(char *name, char *eqv);
793/* vmssetuserlnm
794 * sets a user-mode logical in the process logical name table
795 * used for redirection of sys$error
796 */
797void
798Perl_vmssetuserlnm(pTHX_ char *name, char *eqv)
799{
800 $DESCRIPTOR(d_tab, "LNM$PROCESS");
801 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
802 unsigned long int iss, attr = LNM$M_CONFINE;
803 unsigned char acmode = PSL$C_USER;
804 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
805 {0, 0, 0, 0}};
806 d_name.dsc$a_pointer = name;
807 d_name.dsc$w_length = strlen(name);
808
809 lnmlst[0].buflen = strlen(eqv);
810 lnmlst[0].bufadr = eqv;
811
812 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
813 if (!(iss&1)) lib$signal(iss);
814}
815/*}}}*/
816
817
818/*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
819/* my_crypt - VMS password hashing
820 * my_crypt() provides an interface compatible with the Unix crypt()
821 * C library function, and uses sys$hash_password() to perform VMS
822 * password hashing. The quadword hashed password value is returned
823 * as a NUL-terminated 8 character string. my_crypt() does not change
824 * the case of its string arguments; in order to match the behavior
825 * of LOGINOUT et al., alphabetic characters in both arguments must
826 * be upcased by the caller.
827 */
828char *
829Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
830{
831# ifndef UAI$C_PREFERRED_ALGORITHM
832# define UAI$C_PREFERRED_ALGORITHM 127
833# endif
834 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
835 unsigned short int salt = 0;
836 unsigned long int sts;
837 struct const_dsc {
838 unsigned short int dsc$w_length;
839 unsigned char dsc$b_type;
840 unsigned char dsc$b_class;
841 const char * dsc$a_pointer;
842 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
843 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
844 struct itmlst_3 uailst[3] = {
845 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
846 { sizeof salt, UAI$_SALT, &salt, 0},
847 { 0, 0, NULL, NULL}};
848 static char hash[9];
849
850 usrdsc.dsc$w_length = strlen(usrname);
851 usrdsc.dsc$a_pointer = usrname;
852 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
853 switch (sts) {
854 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
855 set_errno(EACCES);
856 break;
857 case RMS$_RNF:
858 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
859 break;
860 default:
861 set_errno(EVMSERR);
862 }
863 set_vaxc_errno(sts);
864 if (sts != RMS$_RNF) return NULL;
865 }
866
867 txtdsc.dsc$w_length = strlen(textpasswd);
868 txtdsc.dsc$a_pointer = textpasswd;
869 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
870 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
871 }
872
873 return (char *) hash;
874
875} /* end of my_crypt() */
876/*}}}*/
877
878
879static char *mp_do_rmsexpand(pTHX_ char *, char *, int, char *, unsigned);
880static char *mp_do_fileify_dirspec(pTHX_ char *, char *, int);
881static char *mp_do_tovmsspec(pTHX_ char *, char *, int);
882
883/*{{{int do_rmdir(char *name)*/
884int
885Perl_do_rmdir(pTHX_ char *name)
886{
887 char dirfile[NAM$C_MAXRSS+1];
888 int retval;
889 Stat_t st;
890
891 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
892 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
893 else retval = kill_file(dirfile);
894 return retval;
895
896} /* end of do_rmdir */
897/*}}}*/
898
899/* kill_file
900 * Delete any file to which user has control access, regardless of whether
901 * delete access is explicitly allowed.
902 * Limitations: User must have write access to parent directory.
903 * Does not block signals or ASTs; if interrupted in midstream
904 * may leave file with an altered ACL.
905 * HANDLE WITH CARE!
906 */
907/*{{{int kill_file(char *name)*/
908int
909Perl_kill_file(pTHX_ char *name)
910{
911 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
912 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
913 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
914 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
915 struct myacedef {
916 unsigned char myace$b_length;
917 unsigned char myace$b_type;
918 unsigned short int myace$w_flags;
919 unsigned long int myace$l_access;
920 unsigned long int myace$l_ident;
921 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
922 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
923 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
924 struct itmlst_3
925 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
926 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
927 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
928 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
929 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
930 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
931
932 /* Expand the input spec using RMS, since the CRTL remove() and
933 * system services won't do this by themselves, so we may miss
934 * a file "hiding" behind a logical name or search list. */
935 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
936 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
937 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
938 /* If not, can changing protections help? */
939 if (vaxc$errno != RMS$_PRV) return -1;
940
941 /* No, so we get our own UIC to use as a rights identifier,
942 * and the insert an ACE at the head of the ACL which allows us
943 * to delete the file.
944 */
945 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
946 fildsc.dsc$w_length = strlen(rspec);
947 fildsc.dsc$a_pointer = rspec;
948 cxt = 0;
949 newace.myace$l_ident = oldace.myace$l_ident;
950 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
951 switch (aclsts) {
952 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
953 set_errno(ENOENT); break;
954 case RMS$_DIR:
955 set_errno(ENOTDIR); break;
956 case RMS$_DEV:
957 set_errno(ENODEV); break;
958 case RMS$_SYN: case SS$_INVFILFOROP:
959 set_errno(EINVAL); break;
960 case RMS$_PRV:
961 set_errno(EACCES); break;
962 default:
963 _ckvmssts(aclsts);
964 }
965 set_vaxc_errno(aclsts);
966 return -1;
967 }
968 /* Grab any existing ACEs with this identifier in case we fail */
969 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
970 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
971 || fndsts == SS$_NOMOREACE ) {
972 /* Add the new ACE . . . */
973 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
974 goto yourroom;
975 if ((rmsts = remove(name))) {
976 /* We blew it - dir with files in it, no write priv for
977 * parent directory, etc. Put things back the way they were. */
978 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
979 goto yourroom;
980 if (fndsts & 1) {
981 addlst[0].bufadr = &oldace;
982 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
983 goto yourroom;
984 }
985 }
986 }
987
988 yourroom:
989 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
990 /* We just deleted it, so of course it's not there. Some versions of
991 * VMS seem to return success on the unlock operation anyhow (after all
992 * the unlock is successful), but others don't.
993 */
994 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
995 if (aclsts & 1) aclsts = fndsts;
996 if (!(aclsts & 1)) {
997 set_errno(EVMSERR);
998 set_vaxc_errno(aclsts);
999 return -1;
1000 }
1001
1002 return rmsts;
1003
1004} /* end of kill_file() */
1005/*}}}*/
1006
1007
1008/*{{{int my_mkdir(char *,Mode_t)*/
1009int
1010Perl_my_mkdir(pTHX_ char *dir, Mode_t mode)
1011{
1012 STRLEN dirlen = strlen(dir);
1013
1014 /* zero length string sometimes gives ACCVIO */
1015 if (dirlen == 0) return -1;
1016
1017 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1018 * null file name/type. However, it's commonplace under Unix,
1019 * so we'll allow it for a gain in portability.
1020 */
1021 if (dir[dirlen-1] == '/') {
1022 char *newdir = savepvn(dir,dirlen-1);
1023 int ret = mkdir(newdir,mode);
1024 Safefree(newdir);
1025 return ret;
1026 }
1027 else return mkdir(dir,mode);
1028} /* end of my_mkdir */
1029/*}}}*/
1030
1031/*{{{int my_chdir(char *)*/
1032int
1033Perl_my_chdir(pTHX_ char *dir)
1034{
1035 STRLEN dirlen = strlen(dir);
1036
1037 /* zero length string sometimes gives ACCVIO */
1038 if (dirlen == 0) return -1;
1039
1040 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1041 * that implies
1042 * null file name/type. However, it's commonplace under Unix,
1043 * so we'll allow it for a gain in portability.
1044 */
1045 if (dir[dirlen-1] == '/') {
1046 char *newdir = savepvn(dir,dirlen-1);
1047 int ret = chdir(newdir);
1048 Safefree(newdir);
1049 return ret;
1050 }
1051 else return chdir(dir);
1052} /* end of my_chdir */
1053/*}}}*/
1054
1055
1056/*{{{FILE *my_tmpfile()*/
1057FILE *
1058my_tmpfile(void)
1059{
1060 FILE *fp;
1061 char *cp;
1062
1063 if ((fp = tmpfile())) return fp;
1064
1065 New(1323,cp,L_tmpnam+24,char);
1066 strcpy(cp,"Sys$Scratch:");
1067 tmpnam(cp+strlen(cp));
1068 strcat(cp,".Perltmp");
1069 fp = fopen(cp,"w+","fop=dlt");
1070 Safefree(cp);
1071 return fp;
1072}
1073/*}}}*/
1074
1075
1076#ifndef HOMEGROWN_POSIX_SIGNALS
1077/*
1078 * The C RTL's sigaction fails to check for invalid signal numbers so we
1079 * help it out a bit. The docs are correct, but the actual routine doesn't
1080 * do what the docs say it will.
1081 */
1082/*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1083int
1084Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1085 struct sigaction* oact)
1086{
1087 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1088 SETERRNO(EINVAL, SS$_INVARG);
1089 return -1;
1090 }
1091 return sigaction(sig, act, oact);
1092}
1093/*}}}*/
1094#endif
1095
1096/* default piping mailbox size */
1097#define PERL_BUFSIZ 512
1098
1099
1100static void
1101create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
1102{
1103 unsigned long int mbxbufsiz;
1104 static unsigned long int syssize = 0;
1105 unsigned long int dviitm = DVI$_DEVNAM;
1106 char csize[LNM$C_NAMLENGTH+1];
1107
1108 if (!syssize) {
1109 unsigned long syiitm = SYI$_MAXBUF;
1110 /*
1111 * Get the SYSGEN parameter MAXBUF
1112 *
1113 * If the logical 'PERL_MBX_SIZE' is defined
1114 * use the value of the logical instead of PERL_BUFSIZ, but
1115 * keep the size between 128 and MAXBUF.
1116 *
1117 */
1118 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
1119 }
1120
1121 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
1122 mbxbufsiz = atoi(csize);
1123 } else {
1124 mbxbufsiz = PERL_BUFSIZ;
1125 }
1126 if (mbxbufsiz < 128) mbxbufsiz = 128;
1127 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
1128
1129 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
1130
1131 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
1132 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
1133
1134} /* end of create_mbx() */
1135
1136
1137/*{{{ my_popen and my_pclose*/
1138
1139typedef struct _iosb IOSB;
1140typedef struct _iosb* pIOSB;
1141typedef struct _pipe Pipe;
1142typedef struct _pipe* pPipe;
1143typedef struct pipe_details Info;
1144typedef struct pipe_details* pInfo;
1145typedef struct _srqp RQE;
1146typedef struct _srqp* pRQE;
1147typedef struct _tochildbuf CBuf;
1148typedef struct _tochildbuf* pCBuf;
1149
1150struct _iosb {
1151 unsigned short status;
1152 unsigned short count;
1153 unsigned long dvispec;
1154};
1155
1156#pragma member_alignment save
1157#pragma nomember_alignment quadword
1158struct _srqp { /* VMS self-relative queue entry */
1159 unsigned long qptr[2];
1160};
1161#pragma member_alignment restore
1162static RQE RQE_ZERO = {0,0};
1163
1164struct _tochildbuf {
1165 RQE q;
1166 int eof;
1167 unsigned short size;
1168 char *buf;
1169};
1170
1171struct _pipe {
1172 RQE free;
1173 RQE wait;
1174 int fd_out;
1175 unsigned short chan_in;
1176 unsigned short chan_out;
1177 char *buf;
1178 unsigned int bufsize;
1179 IOSB iosb;
1180 IOSB iosb2;
1181 int *pipe_done;
1182 int retry;
1183 int type;
1184 int shut_on_empty;
1185 int need_wake;
1186 pPipe *home;
1187 pInfo info;
1188 pCBuf curr;
1189 pCBuf curr2;
1190#if defined(PERL_IMPLICIT_CONTEXT)
1191 void *thx; /* Either a thread or an interpreter */
1192 /* pointer, depending on how we're built */
1193#endif
1194};
1195
1196
1197struct pipe_details
1198{
1199 pInfo next;
1200 PerlIO *fp; /* stdio file pointer to pipe mailbox */
1201 int pid; /* PID of subprocess */
1202 int mode; /* == 'r' if pipe open for reading */
1203 int done; /* subprocess has completed */
1204 int closing; /* my_pclose is closing this pipe */
1205 unsigned long completion; /* termination status of subprocess */
1206 pPipe in; /* pipe in to sub */
1207 pPipe out; /* pipe out of sub */
1208 pPipe err; /* pipe of sub's sys$error */
1209 int in_done; /* true when in pipe finished */
1210 int out_done;
1211 int err_done;
1212};
1213
1214struct exit_control_block
1215{
1216 struct exit_control_block *flink;
1217 unsigned long int (*exit_routine)();
1218 unsigned long int arg_count;
1219 unsigned long int *status_address;
1220 unsigned long int exit_status;
1221};
1222
1223#define RETRY_DELAY "0 ::0.20"
1224#define MAX_RETRY 50
1225
1226static int pipe_ef = 0; /* first call to safe_popen inits these*/
1227static unsigned long mypid;
1228static unsigned long delaytime[2];
1229
1230static pInfo open_pipes = NULL;
1231static $DESCRIPTOR(nl_desc, "NL:");
1232
1233
1234static unsigned long int
1235pipe_exit_routine(pTHX)
1236{
1237 pInfo info;
1238 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1239 int sts, did_stuff, need_eof;
1240
1241 /*
1242 first we try sending an EOF...ignore if doesn't work, make sure we
1243 don't hang
1244 */
1245 did_stuff = 0;
1246 info = open_pipes;
1247
1248 while (info) {
1249 int need_eof;
1250 _ckvmssts(sys$setast(0));
1251 if (info->in && !info->in->shut_on_empty) {
1252 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
1253 0, 0, 0, 0, 0, 0));
1254 did_stuff = 1;
1255 }
1256 _ckvmssts(sys$setast(1));
1257 info = info->next;
1258 }
1259 if (did_stuff) sleep(1); /* wait for EOF to have an effect */
1260
1261 did_stuff = 0;
1262 info = open_pipes;
1263 while (info) {
1264 _ckvmssts(sys$setast(0));
1265 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1266 sts = sys$forcex(&info->pid,0,&abort);
1267 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1268 did_stuff = 1;
1269 }
1270 _ckvmssts(sys$setast(1));
1271 info = info->next;
1272 }
1273 if (did_stuff) sleep(1); /* wait for them to respond */
1274
1275 info = open_pipes;
1276 while (info) {
1277 _ckvmssts(sys$setast(0));
1278 if (!info->done) { /* We tried to be nice . . . */
1279 sts = sys$delprc(&info->pid,0);
1280 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1281 }
1282 _ckvmssts(sys$setast(1));
1283 info = info->next;
1284 }
1285
1286 while(open_pipes) {
1287 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1288 else if (!(sts & 1)) retsts = sts;
1289 }
1290 return retsts;
1291}
1292
1293static struct exit_control_block pipe_exitblock =
1294 {(struct exit_control_block *) 0,
1295 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1296
1297static void pipe_mbxtofd_ast(pPipe p);
1298static void pipe_tochild1_ast(pPipe p);
1299static void pipe_tochild2_ast(pPipe p);
1300
1301static void
1302popen_completion_ast(pInfo info)
1303{
1304 pInfo i = open_pipes;
1305 int iss;
1306
1307 while (i) {
1308 if (i == info) break;
1309 i = i->next;
1310 }
1311 if (!i) return; /* unlinked, probably freed too */
1312
1313 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
1314 info->done = TRUE;
1315
1316/*
1317 Writing to subprocess ...
1318 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
1319
1320 chan_out may be waiting for "done" flag, or hung waiting
1321 for i/o completion to child...cancel the i/o. This will
1322 put it into "snarf mode" (done but no EOF yet) that discards
1323 input.
1324
1325 Output from subprocess (stdout, stderr) needs to be flushed and
1326 shut down. We try sending an EOF, but if the mbx is full the pipe
1327 routine should still catch the "shut_on_empty" flag, telling it to
1328 use immediate-style reads so that "mbx empty" -> EOF.
1329
1330
1331*/
1332 if (info->in && !info->in_done) { /* only for mode=w */
1333 if (info->in->shut_on_empty && info->in->need_wake) {
1334 info->in->need_wake = FALSE;
1335 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
1336 } else {
1337 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
1338 }
1339 }
1340
1341 if (info->out && !info->out_done) { /* were we also piping output? */
1342 info->out->shut_on_empty = TRUE;
1343 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1344 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1345 _ckvmssts_noperl(iss);
1346 }
1347
1348 if (info->err && !info->err_done) { /* we were piping stderr */
1349 info->err->shut_on_empty = TRUE;
1350 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1351 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1352 _ckvmssts_noperl(iss);
1353 }
1354 _ckvmssts_noperl(sys$setef(pipe_ef));
1355
1356}
1357
1358static unsigned long int setup_cmddsc(pTHX_ char *cmd, int check_img);
1359static void vms_execfree(pTHX);
1360
1361/*
1362 we actually differ from vmstrnenv since we use this to
1363 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
1364 are pointing to the same thing
1365*/
1366
1367static unsigned short
1368popen_translate(pTHX_ char *logical, char *result)
1369{
1370 int iss;
1371 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
1372 $DESCRIPTOR(d_log,"");
1373 struct _il3 {
1374 unsigned short length;
1375 unsigned short code;
1376 char * buffer_addr;
1377 unsigned short *retlenaddr;
1378 } itmlst[2];
1379 unsigned short l, ifi;
1380
1381 d_log.dsc$a_pointer = logical;
1382 d_log.dsc$w_length = strlen(logical);
1383
1384 itmlst[0].code = LNM$_STRING;
1385 itmlst[0].length = 255;
1386 itmlst[0].buffer_addr = result;
1387 itmlst[0].retlenaddr = &l;
1388
1389 itmlst[1].code = 0;
1390 itmlst[1].length = 0;
1391 itmlst[1].buffer_addr = 0;
1392 itmlst[1].retlenaddr = 0;
1393
1394 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
1395 if (iss == SS$_NOLOGNAM) {
1396 iss = SS$_NORMAL;
1397 l = 0;
1398 }
1399 if (!(iss&1)) lib$signal(iss);
1400 result[l] = '\0';
1401/*
1402 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
1403 strip it off and return the ifi, if any
1404*/
1405 ifi = 0;
1406 if (result[0] == 0x1b && result[1] == 0x00) {
1407 memcpy(&ifi,result+2,2);
1408 strcpy(result,result+4);
1409 }
1410 return ifi; /* this is the RMS internal file id */
1411}
1412
1413#define MAX_DCL_SYMBOL 255
1414static void pipe_infromchild_ast(pPipe p);
1415
1416/*
1417 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
1418 inside an AST routine without worrying about reentrancy and which Perl
1419 memory allocator is being used.
1420
1421 We read data and queue up the buffers, then spit them out one at a
1422 time to the output mailbox when the output mailbox is ready for one.
1423
1424*/
1425#define INITIAL_TOCHILDQUEUE 2
1426
1427static pPipe
1428pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
1429{
1430 pPipe p;
1431 pCBuf b;
1432 char mbx1[64], mbx2[64];
1433 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1434 DSC$K_CLASS_S, mbx1},
1435 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1436 DSC$K_CLASS_S, mbx2};
1437 unsigned int dviitm = DVI$_DEVBUFSIZ;
1438 int j, n;
1439
1440 New(1368, p, 1, Pipe);
1441
1442 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1443 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1444 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1445
1446 p->buf = 0;
1447 p->shut_on_empty = FALSE;
1448 p->need_wake = FALSE;
1449 p->type = 0;
1450 p->retry = 0;
1451 p->iosb.status = SS$_NORMAL;
1452 p->iosb2.status = SS$_NORMAL;
1453 p->free = RQE_ZERO;
1454 p->wait = RQE_ZERO;
1455 p->curr = 0;
1456 p->curr2 = 0;
1457 p->info = 0;
1458#ifdef PERL_IMPLICIT_CONTEXT
1459 p->thx = aTHX;
1460#endif
1461
1462 n = sizeof(CBuf) + p->bufsize;
1463
1464 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
1465 _ckvmssts(lib$get_vm(&n, &b));
1466 b->buf = (char *) b + sizeof(CBuf);
1467 _ckvmssts(lib$insqhi(b, &p->free));
1468 }
1469
1470 pipe_tochild2_ast(p);
1471 pipe_tochild1_ast(p);
1472 strcpy(wmbx, mbx1);
1473 strcpy(rmbx, mbx2);
1474 return p;
1475}
1476
1477/* reads the MBX Perl is writing, and queues */
1478
1479static void
1480pipe_tochild1_ast(pPipe p)
1481{
1482 pCBuf b = p->curr;
1483 int iss = p->iosb.status;
1484 int eof = (iss == SS$_ENDOFFILE);
1485#ifdef PERL_IMPLICIT_CONTEXT
1486 pTHX = p->thx;
1487#endif
1488
1489 if (p->retry) {
1490 if (eof) {
1491 p->shut_on_empty = TRUE;
1492 b->eof = TRUE;
1493 _ckvmssts(sys$dassgn(p->chan_in));
1494 } else {
1495 _ckvmssts(iss);
1496 }
1497
1498 b->eof = eof;
1499 b->size = p->iosb.count;
1500 _ckvmssts(lib$insqhi(b, &p->wait));
1501 if (p->need_wake) {
1502 p->need_wake = FALSE;
1503 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
1504 }
1505 } else {
1506 p->retry = 1; /* initial call */
1507 }
1508
1509 if (eof) { /* flush the free queue, return when done */
1510 int n = sizeof(CBuf) + p->bufsize;
1511 while (1) {
1512 iss = lib$remqti(&p->free, &b);
1513 if (iss == LIB$_QUEWASEMP) return;
1514 _ckvmssts(iss);
1515 _ckvmssts(lib$free_vm(&n, &b));
1516 }
1517 }
1518
1519 iss = lib$remqti(&p->free, &b);
1520 if (iss == LIB$_QUEWASEMP) {
1521 int n = sizeof(CBuf) + p->bufsize;
1522 _ckvmssts(lib$get_vm(&n, &b));
1523 b->buf = (char *) b + sizeof(CBuf);
1524 } else {
1525 _ckvmssts(iss);
1526 }
1527
1528 p->curr = b;
1529 iss = sys$qio(0,p->chan_in,
1530 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
1531 &p->iosb,
1532 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
1533 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
1534 _ckvmssts(iss);
1535}
1536
1537
1538/* writes queued buffers to output, waits for each to complete before
1539 doing the next */
1540
1541static void
1542pipe_tochild2_ast(pPipe p)
1543{
1544 pCBuf b = p->curr2;
1545 int iss = p->iosb2.status;
1546 int n = sizeof(CBuf) + p->bufsize;
1547 int done = (p->info && p->info->done) ||
1548 iss == SS$_CANCEL || iss == SS$_ABORT;
1549#if defined(PERL_IMPLICIT_CONTEXT)
1550 pTHX = p->thx;
1551#endif
1552
1553 do {
1554 if (p->type) { /* type=1 has old buffer, dispose */
1555 if (p->shut_on_empty) {
1556 _ckvmssts(lib$free_vm(&n, &b));
1557 } else {
1558 _ckvmssts(lib$insqhi(b, &p->free));
1559 }
1560 p->type = 0;
1561 }
1562
1563 iss = lib$remqti(&p->wait, &b);
1564 if (iss == LIB$_QUEWASEMP) {
1565 if (p->shut_on_empty) {
1566 if (done) {
1567 _ckvmssts(sys$dassgn(p->chan_out));
1568 *p->pipe_done = TRUE;
1569 _ckvmssts(sys$setef(pipe_ef));
1570 } else {
1571 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1572 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1573 }
1574 return;
1575 }
1576 p->need_wake = TRUE;
1577 return;
1578 }
1579 _ckvmssts(iss);
1580 p->type = 1;
1581 } while (done);
1582
1583
1584 p->curr2 = b;
1585 if (b->eof) {
1586 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
1587 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
1588 } else {
1589 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
1590 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
1591 }
1592
1593 return;
1594
1595}
1596
1597
1598static pPipe
1599pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
1600{
1601 pPipe p;
1602 char mbx1[64], mbx2[64];
1603 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1604 DSC$K_CLASS_S, mbx1},
1605 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1606 DSC$K_CLASS_S, mbx2};
1607 unsigned int dviitm = DVI$_DEVBUFSIZ;
1608
1609 New(1367, p, 1, Pipe);
1610 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1611 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1612
1613 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1614 New(1367, p->buf, p->bufsize, char);
1615 p->shut_on_empty = FALSE;
1616 p->info = 0;
1617 p->type = 0;
1618 p->iosb.status = SS$_NORMAL;
1619#if defined(PERL_IMPLICIT_CONTEXT)
1620 p->thx = aTHX;
1621#endif
1622 pipe_infromchild_ast(p);
1623
1624 strcpy(wmbx, mbx1);
1625 strcpy(rmbx, mbx2);
1626 return p;
1627}
1628
1629static void
1630pipe_infromchild_ast(pPipe p)
1631{
1632 int iss = p->iosb.status;
1633 int eof = (iss == SS$_ENDOFFILE);
1634 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
1635 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
1636#if defined(PERL_IMPLICIT_CONTEXT)
1637 pTHX = p->thx;
1638#endif
1639
1640 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
1641 _ckvmssts(sys$dassgn(p->chan_out));
1642 p->chan_out = 0;
1643 }
1644
1645 /* read completed:
1646 input shutdown if EOF from self (done or shut_on_empty)
1647 output shutdown if closing flag set (my_pclose)
1648 send data/eof from child or eof from self
1649 otherwise, re-read (snarf of data from child)
1650 */
1651
1652 if (p->type == 1) {
1653 p->type = 0;
1654 if (myeof && p->chan_in) { /* input shutdown */
1655 _ckvmssts(sys$dassgn(p->chan_in));
1656 p->chan_in = 0;
1657 }
1658
1659 if (p->chan_out) {
1660 if (myeof || kideof) { /* pass EOF to parent */
1661 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
1662 pipe_infromchild_ast, p,
1663 0, 0, 0, 0, 0, 0));
1664 return;
1665 } else if (eof) { /* eat EOF --- fall through to read*/
1666
1667 } else { /* transmit data */
1668 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
1669 pipe_infromchild_ast,p,
1670 p->buf, p->iosb.count, 0, 0, 0, 0));
1671 return;
1672 }
1673 }
1674 }
1675
1676 /* everything shut? flag as done */
1677
1678 if (!p->chan_in && !p->chan_out) {
1679 *p->pipe_done = TRUE;
1680 _ckvmssts(sys$setef(pipe_ef));
1681 return;
1682 }
1683
1684 /* write completed (or read, if snarfing from child)
1685 if still have input active,
1686 queue read...immediate mode if shut_on_empty so we get EOF if empty
1687 otherwise,
1688 check if Perl reading, generate EOFs as needed
1689 */
1690
1691 if (p->type == 0) {
1692 p->type = 1;
1693 if (p->chan_in) {
1694 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
1695 pipe_infromchild_ast,p,
1696 p->buf, p->bufsize, 0, 0, 0, 0);
1697 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
1698 _ckvmssts(iss);
1699 } else { /* send EOFs for extra reads */
1700 p->iosb.status = SS$_ENDOFFILE;
1701 p->iosb.dvispec = 0;
1702 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
1703 0, 0, 0,
1704 pipe_infromchild_ast, p, 0, 0, 0, 0));
1705 }
1706 }
1707}
1708
1709static pPipe
1710pipe_mbxtofd_setup(pTHX_ int fd, char *out)
1711{
1712 pPipe p;
1713 char mbx[64];
1714 unsigned long dviitm = DVI$_DEVBUFSIZ;
1715 struct stat s;
1716 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
1717 DSC$K_CLASS_S, mbx};
1718
1719 /* things like terminals and mbx's don't need this filter */
1720 if (fd && fstat(fd,&s) == 0) {
1721 unsigned long dviitm = DVI$_DEVCHAR, devchar;
1722 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
1723 DSC$K_CLASS_S, s.st_dev};
1724
1725 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
1726 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
1727 strcpy(out, s.st_dev);
1728 return 0;
1729 }
1730 }
1731
1732 New(1366, p, 1, Pipe);
1733 p->fd_out = dup(fd);
1734 create_mbx(aTHX_ &p->chan_in, &d_mbx);
1735 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1736 New(1366, p->buf, p->bufsize+1, char);
1737 p->shut_on_empty = FALSE;
1738 p->retry = 0;
1739 p->info = 0;
1740 strcpy(out, mbx);
1741
1742 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
1743 pipe_mbxtofd_ast, p,
1744 p->buf, p->bufsize, 0, 0, 0, 0));
1745
1746 return p;
1747}
1748
1749static void
1750pipe_mbxtofd_ast(pPipe p)
1751{
1752 int iss = p->iosb.status;
1753 int done = p->info->done;
1754 int iss2;
1755 int eof = (iss == SS$_ENDOFFILE);
1756 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
1757 int err = !(iss&1) && !eof;
1758#if defined(PERL_IMPLICIT_CONTEXT)
1759 pTHX = p->thx;
1760#endif
1761
1762 if (done && myeof) { /* end piping */
1763 close(p->fd_out);
1764 sys$dassgn(p->chan_in);
1765 *p->pipe_done = TRUE;
1766 _ckvmssts(sys$setef(pipe_ef));
1767 return;
1768 }
1769
1770 if (!err && !eof) { /* good data to send to file */
1771 p->buf[p->iosb.count] = '\n';
1772 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
1773 if (iss2 < 0) {
1774 p->retry++;
1775 if (p->retry < MAX_RETRY) {
1776 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
1777 return;
1778 }
1779 }
1780 p->retry = 0;
1781 } else if (err) {
1782 _ckvmssts(iss);
1783 }
1784
1785
1786 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
1787 pipe_mbxtofd_ast, p,
1788 p->buf, p->bufsize, 0, 0, 0, 0);
1789 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
1790 _ckvmssts(iss);
1791}
1792
1793
1794typedef struct _pipeloc PLOC;
1795typedef struct _pipeloc* pPLOC;
1796
1797struct _pipeloc {
1798 pPLOC next;
1799 char dir[NAM$C_MAXRSS+1];
1800};
1801static pPLOC head_PLOC = 0;
1802
1803void
1804free_pipelocs(pTHX_ void *head)
1805{
1806 pPLOC p, pnext;
1807
1808 p = (pPLOC) head;
1809 while (p) {
1810 pnext = p->next;
1811 Safefree(p);
1812 p = pnext;
1813 }
1814}
1815
1816static void
1817store_pipelocs(pTHX)
1818{
1819 int i;
1820 pPLOC p;
1821 AV *av = GvAVn(PL_incgv);
1822 SV *dirsv;
1823 GV *gv;
1824 char *dir, *x;
1825 char *unixdir;
1826 char temp[NAM$C_MAXRSS+1];
1827 STRLEN n_a;
1828
1829/* the . directory from @INC comes last */
1830
1831 New(1370,p,1,PLOC);
1832 p->next = head_PLOC;
1833 head_PLOC = p;
1834 strcpy(p->dir,"./");
1835
1836/* get the directory from $^X */
1837
1838 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
1839 strcpy(temp, PL_origargv[0]);
1840 x = strrchr(temp,']');
1841 if (x) x[1] = '\0';
1842
1843 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
1844 New(1370,p,1,PLOC);
1845 p->next = head_PLOC;
1846 head_PLOC = p;
1847 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1848 p->dir[NAM$C_MAXRSS] = '\0';
1849 }
1850 }
1851
1852/* reverse order of @INC entries, skip "." since entered above */
1853
1854 for (i = 0; i <= AvFILL(av); i++) {
1855 dirsv = *av_fetch(av,i,TRUE);
1856
1857 if (SvROK(dirsv)) continue;
1858 dir = SvPVx(dirsv,n_a);
1859 if (strcmp(dir,".") == 0) continue;
1860 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
1861 continue;
1862
1863 New(1370,p,1,PLOC);
1864 p->next = head_PLOC;
1865 head_PLOC = p;
1866 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1867 p->dir[NAM$C_MAXRSS] = '\0';
1868 }
1869
1870/* most likely spot (ARCHLIB) put first in the list */
1871
1872#ifdef ARCHLIB_EXP
1873 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
1874 New(1370,p,1,PLOC);
1875 p->next = head_PLOC;
1876 head_PLOC = p;
1877 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
1878 p->dir[NAM$C_MAXRSS] = '\0';
1879 }
1880#endif
1881 Perl_call_atexit(aTHX_ &free_pipelocs, head_PLOC);
1882}
1883
1884
1885static char *
1886find_vmspipe(pTHX)
1887{
1888 static int vmspipe_file_status = 0;
1889 static char vmspipe_file[NAM$C_MAXRSS+1];
1890
1891 /* already found? Check and use ... need read+execute permission */
1892
1893 if (vmspipe_file_status == 1) {
1894 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
1895 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
1896 return vmspipe_file;
1897 }
1898 vmspipe_file_status = 0;
1899 }
1900
1901 /* scan through stored @INC, $^X */
1902
1903 if (vmspipe_file_status == 0) {
1904 char file[NAM$C_MAXRSS+1];
1905 pPLOC p = head_PLOC;
1906
1907 while (p) {
1908 strcpy(file, p->dir);
1909 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
1910 file[NAM$C_MAXRSS] = '\0';
1911 p = p->next;
1912
1913 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
1914
1915 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
1916 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
1917 vmspipe_file_status = 1;
1918 return vmspipe_file;
1919 }
1920 }
1921 vmspipe_file_status = -1; /* failed, use tempfiles */
1922 }
1923
1924 return 0;
1925}
1926
1927static FILE *
1928vmspipe_tempfile(pTHX)
1929{
1930 char file[NAM$C_MAXRSS+1];
1931 FILE *fp;
1932 static int index = 0;
1933 stat_t s0, s1;
1934
1935 /* create a tempfile */
1936
1937 /* we can't go from W, shr=get to R, shr=get without
1938 an intermediate vulnerable state, so don't bother trying...
1939
1940 and lib$spawn doesn't shr=put, so have to close the write
1941
1942 So... match up the creation date/time and the FID to
1943 make sure we're dealing with the same file
1944
1945 */
1946
1947 index++;
1948 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
1949 fp = fopen(file,"w");
1950 if (!fp) {
1951 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
1952 fp = fopen(file,"w");
1953 if (!fp) {
1954 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
1955 fp = fopen(file,"w");
1956 }
1957 }
1958 if (!fp) return 0; /* we're hosed */
1959
1960 fprintf(fp,"$! 'f$verify(0)\n");
1961 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
1962 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
1963 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
1964 fprintf(fp,"$ perl_on = \"set noon\"\n");
1965 fprintf(fp,"$ perl_exit = \"exit\"\n");
1966 fprintf(fp,"$ perl_del = \"delete\"\n");
1967 fprintf(fp,"$ pif = \"if\"\n");
1968 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
1969 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
1970 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
1971 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
1972 fprintf(fp,"$ cmd = perl_popen_cmd\n");
1973 fprintf(fp,"$! --- get rid of global symbols\n");
1974 fprintf(fp,"$ perl_del/symbol/global perl_popen_in\n");
1975 fprintf(fp,"$ perl_del/symbol/global perl_popen_err\n");
1976 fprintf(fp,"$ perl_del/symbol/global perl_popen_out\n");
1977 fprintf(fp,"$ perl_del/symbol/global perl_popen_cmd\n");
1978 fprintf(fp,"$ perl_on\n");
1979 fprintf(fp,"$ 'cmd\n");
1980 fprintf(fp,"$ perl_status = $STATUS\n");
1981 fprintf(fp,"$ perl_del 'perl_cfile'\n");
1982 fprintf(fp,"$ perl_exit 'perl_status'\n");
1983 fsync(fileno(fp));
1984
1985 fgetname(fp, file, 1);
1986 fstat(fileno(fp), &s0);
1987 fclose(fp);
1988
1989 fp = fopen(file,"r","shr=get");
1990 if (!fp) return 0;
1991 fstat(fileno(fp), &s1);
1992
1993 if (s0.st_ino[0] != s1.st_ino[0] ||
1994 s0.st_ino[1] != s1.st_ino[1] ||
1995 s0.st_ino[2] != s1.st_ino[2] ||
1996 s0.st_ctime != s1.st_ctime ) {
1997 fclose(fp);
1998 return 0;
1999 }
2000
2001 return fp;
2002}
2003
2004
2005
2006static PerlIO *
2007safe_popen(pTHX_ char *cmd, char *mode)
2008{
2009 static int handler_set_up = FALSE;
2010 unsigned long int sts, flags=1; /* nowait - gnu c doesn't allow &1 */
2011 unsigned int table = LIB$K_CLI_GLOBAL_SYM;
2012 char *p, symbol[MAX_DCL_SYMBOL+1], *vmspipe;
2013 char in[512], out[512], err[512], mbx[512];
2014 FILE *tpipe = 0;
2015 char tfilebuf[NAM$C_MAXRSS+1];
2016 pInfo info;
2017 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
2018 DSC$K_CLASS_S, symbol};
2019 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
2020 DSC$K_CLASS_S, 0};
2021
2022 $DESCRIPTOR(d_sym_cmd,"PERL_POPEN_CMD");
2023 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
2024 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
2025 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
2026
2027 /* once-per-program initialization...
2028 note that the SETAST calls and the dual test of pipe_ef
2029 makes sure that only the FIRST thread through here does
2030 the initialization...all other threads wait until it's
2031 done.
2032
2033 Yeah, uglier than a pthread call, it's got all the stuff inline
2034 rather than in a separate routine.
2035 */
2036
2037 if (!pipe_ef) {
2038 _ckvmssts(sys$setast(0));
2039 if (!pipe_ef) {
2040 unsigned long int pidcode = JPI$_PID;
2041 $DESCRIPTOR(d_delay, RETRY_DELAY);
2042 _ckvmssts(lib$get_ef(&pipe_ef));
2043 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2044 _ckvmssts(sys$bintim(&d_delay, delaytime));
2045 }
2046 if (!handler_set_up) {
2047 _ckvmssts(sys$dclexh(&pipe_exitblock));
2048 handler_set_up = TRUE;
2049 }
2050 _ckvmssts(sys$setast(1));
2051 }
2052
2053 /* see if we can find a VMSPIPE.COM */
2054
2055 tfilebuf[0] = '@';
2056 vmspipe = find_vmspipe(aTHX);
2057 if (vmspipe) {
2058 strcpy(tfilebuf+1,vmspipe);
2059 } else { /* uh, oh...we're in tempfile hell */
2060 tpipe = vmspipe_tempfile(aTHX);
2061 if (!tpipe) { /* a fish popular in Boston */
2062 if (ckWARN(WARN_PIPE)) {
2063 Perl_warner(aTHX_ WARN_PIPE,"unable to find VMSPIPE.COM for i/o piping");
2064 }
2065 return Nullfp;
2066 }
2067 fgetname(tpipe,tfilebuf+1,1);
2068 }
2069 vmspipedsc.dsc$a_pointer = tfilebuf;
2070 vmspipedsc.dsc$w_length = strlen(tfilebuf);
2071
2072 sts = setup_cmddsc(aTHX_ cmd,0);
2073 if (!(sts & 1)) {
2074 switch (sts) {
2075 case RMS$_FNF: case RMS$_DNF:
2076 set_errno(ENOENT); break;
2077 case RMS$_DIR:
2078 set_errno(ENOTDIR); break;
2079 case RMS$_DEV:
2080 set_errno(ENODEV); break;
2081 case RMS$_PRV:
2082 set_errno(EACCES); break;
2083 case RMS$_SYN:
2084 set_errno(EINVAL); break;
2085 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
2086 set_errno(E2BIG); break;
2087 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
2088 _ckvmssts(sts); /* fall through */
2089 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
2090 set_errno(EVMSERR);
2091 }
2092 set_vaxc_errno(sts);
2093 if (ckWARN(WARN_PIPE)) {
2094 Perl_warner(aTHX_ WARN_PIPE,"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
2095 }
2096 return Nullfp;
2097 }
2098 New(1301,info,1,Info);
2099
2100 info->mode = *mode;
2101 info->done = FALSE;
2102 info->completion = 0;
2103 info->closing = FALSE;
2104 info->in = 0;
2105 info->out = 0;
2106 info->err = 0;
2107 info->in_done = TRUE;
2108 info->out_done = TRUE;
2109 info->err_done = TRUE;
2110 in[0] = out[0] = err[0] = '\0';
2111
2112 if (*mode == 'r') { /* piping from subroutine */
2113
2114 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
2115 if (info->out) {
2116 info->out->pipe_done = &info->out_done;
2117 info->out_done = FALSE;
2118 info->out->info = info;
2119 }
2120 info->fp = PerlIO_open(mbx, mode);
2121 if (!info->fp && info->out) {
2122 sys$cancel(info->out->chan_out);
2123
2124 while (!info->out_done) {
2125 int done;
2126 _ckvmssts(sys$setast(0));
2127 done = info->out_done;
2128 if (!done) _ckvmssts(sys$clref(pipe_ef));
2129 _ckvmssts(sys$setast(1));
2130 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2131 }
2132
2133 if (info->out->buf) Safefree(info->out->buf);
2134 Safefree(info->out);
2135 Safefree(info);
2136 return Nullfp;
2137 }
2138
2139 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2140 if (info->err) {
2141 info->err->pipe_done = &info->err_done;
2142 info->err_done = FALSE;
2143 info->err->info = info;
2144 }
2145
2146 } else { /* piping to subroutine , mode=w*/
2147
2148 info->in = pipe_tochild_setup(aTHX_ in,mbx);
2149 info->fp = PerlIO_open(mbx, mode);
2150 if (info->in) {
2151 info->in->pipe_done = &info->in_done;
2152 info->in_done = FALSE;
2153 info->in->info = info;
2154 }
2155
2156 /* error cleanup */
2157 if (!info->fp && info->in) {
2158 info->done = TRUE;
2159 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
2160 0, 0, 0, 0, 0, 0, 0, 0));
2161
2162 while (!info->in_done) {
2163 int done;
2164 _ckvmssts(sys$setast(0));
2165 done = info->in_done;
2166 if (!done) _ckvmssts(sys$clref(pipe_ef));
2167 _ckvmssts(sys$setast(1));
2168 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2169 }
2170
2171 if (info->in->buf) Safefree(info->in->buf);
2172 Safefree(info->in);
2173 Safefree(info);
2174 return Nullfp;
2175 }
2176
2177
2178 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2179 if (info->out) {
2180 info->out->pipe_done = &info->out_done;
2181 info->out_done = FALSE;
2182 info->out->info = info;
2183 }
2184
2185 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2186 if (info->err) {
2187 info->err->pipe_done = &info->err_done;
2188 info->err_done = FALSE;
2189 info->err->info = info;
2190 }
2191 }
2192
2193 symbol[MAX_DCL_SYMBOL] = '\0';
2194
2195 strncpy(symbol, in, MAX_DCL_SYMBOL);
2196 d_symbol.dsc$w_length = strlen(symbol);
2197 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
2198
2199 strncpy(symbol, err, MAX_DCL_SYMBOL);
2200 d_symbol.dsc$w_length = strlen(symbol);
2201 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
2202
2203 strncpy(symbol, out, MAX_DCL_SYMBOL);
2204 d_symbol.dsc$w_length = strlen(symbol);
2205 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
2206
2207 p = VMScmd.dsc$a_pointer;
2208 while (*p && *p != '\n') p++;
2209 *p = '\0'; /* truncate on \n */
2210 p = VMScmd.dsc$a_pointer;
2211 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
2212 if (*p == '$') p++; /* remove leading $ */
2213 while (*p == ' ' || *p == '\t') p++;
2214 strncpy(symbol, p, MAX_DCL_SYMBOL);
2215 d_symbol.dsc$w_length = strlen(symbol);
2216 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
2217
2218 _ckvmssts(sys$setast(0));
2219 info->next=open_pipes; /* prepend to list */
2220 open_pipes=info;
2221 _ckvmssts(sys$setast(1));
2222 _ckvmssts(lib$spawn(&vmspipedsc, &nl_desc, &nl_desc, &flags,
2223 0, &info->pid, &info->completion,
2224 0, popen_completion_ast,info,0,0,0));
2225
2226 /* if we were using a tempfile, close it now */
2227
2228 if (tpipe) fclose(tpipe);
2229
2230 /* once the subprocess is spawned, its copied the symbols and
2231 we can get rid of ours */
2232
2233 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
2234 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
2235 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
2236 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
2237 vms_execfree(aTHX);
2238
2239 PL_forkprocess = info->pid;
2240 return info->fp;
2241} /* end of safe_popen */
2242
2243
2244/*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
2245PerlIO *
2246Perl_my_popen(pTHX_ char *cmd, char *mode)
2247{
2248 TAINT_ENV();
2249 TAINT_PROPER("popen");
2250 PERL_FLUSHALL_FOR_CHILD;
2251 return safe_popen(aTHX_ cmd,mode);
2252}
2253
2254/*}}}*/
2255
2256/*{{{ I32 my_pclose(PerlIO *fp)*/
2257I32 Perl_my_pclose(pTHX_ PerlIO *fp)
2258{
2259 pInfo info, last = NULL;
2260 unsigned long int retsts;
2261 int done, iss;
2262
2263 for (info = open_pipes; info != NULL; last = info, info = info->next)
2264 if (info->fp == fp) break;
2265
2266 if (info == NULL) { /* no such pipe open */
2267 set_errno(ECHILD); /* quoth POSIX */
2268 set_vaxc_errno(SS$_NONEXPR);
2269 return -1;
2270 }
2271
2272 /* If we were writing to a subprocess, insure that someone reading from
2273 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
2274 * produce an EOF record in the mailbox.
2275 *
2276 * well, at least sometimes it *does*, so we have to watch out for
2277 * the first EOF closing the pipe (and DASSGN'ing the channel)...
2278 */
2279
2280 PerlIO_flush(info->fp); /* first, flush data */
2281
2282 _ckvmssts(sys$setast(0));
2283 info->closing = TRUE;
2284 done = info->done && info->in_done && info->out_done && info->err_done;
2285 /* hanging on write to Perl's input? cancel it */
2286 if (info->mode == 'r' && info->out && !info->out_done) {
2287 if (info->out->chan_out) {
2288 _ckvmssts(sys$cancel(info->out->chan_out));
2289 if (!info->out->chan_in) { /* EOF generation, need AST */
2290 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
2291 }
2292 }
2293 }
2294 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
2295 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2296 0, 0, 0, 0, 0, 0));
2297 _ckvmssts(sys$setast(1));
2298 PerlIO_close(info->fp);
2299
2300 /*
2301 we have to wait until subprocess completes, but ALSO wait until all
2302 the i/o completes...otherwise we'll be freeing the "info" structure
2303 that the i/o ASTs could still be using...
2304 */
2305
2306 while (!done) {
2307 _ckvmssts(sys$setast(0));
2308 done = info->done && info->in_done && info->out_done && info->err_done;
2309 if (!done) _ckvmssts(sys$clref(pipe_ef));
2310 _ckvmssts(sys$setast(1));
2311 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2312 }
2313 retsts = info->completion;
2314
2315 /* remove from list of open pipes */
2316 _ckvmssts(sys$setast(0));
2317 if (last) last->next = info->next;
2318 else open_pipes = info->next;
2319 _ckvmssts(sys$setast(1));
2320
2321 /* free buffers and structures */
2322
2323 if (info->in) {
2324 if (info->in->buf) Safefree(info->in->buf);
2325 Safefree(info->in);
2326 }
2327 if (info->out) {
2328 if (info->out->buf) Safefree(info->out->buf);
2329 Safefree(info->out);
2330 }
2331 if (info->err) {
2332 if (info->err->buf) Safefree(info->err->buf);
2333 Safefree(info->err);
2334 }
2335 Safefree(info);
2336
2337 return retsts;
2338
2339} /* end of my_pclose() */
2340
2341#if defined(__CRTL_VER) && __CRTL_VER >= 70100322
2342 /* Roll our own prototype because we want this regardless of whether
2343 * _VMS_WAIT is defined.
2344 */
2345 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
2346#endif
2347/* sort-of waitpid; special handling of pipe clean-up for subprocesses
2348 created with popen(); otherwise partially emulate waitpid() unless
2349 we have a suitable one from the CRTL that came with VMS 7.2 and later.
2350 Also check processes not considered by the CRTL waitpid().
2351 */
2352/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
2353Pid_t
2354Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
2355{
2356 pInfo info;
2357 int done;
2358 int sts;
2359
2360 if (statusp) *statusp = 0;
2361
2362 for (info = open_pipes; info != NULL; info = info->next)
2363 if (info->pid == pid) break;
2364
2365 if (info != NULL) { /* we know about this child */
2366 while (!info->done) {
2367 _ckvmssts(sys$setast(0));
2368 done = info->done;
2369 if (!done) _ckvmssts(sys$clref(pipe_ef));
2370 _ckvmssts(sys$setast(1));
2371 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2372 }
2373
2374 if (statusp) *statusp = info->completion;
2375 return pid;
2376
2377 }
2378 else { /* this child is not one of our own pipe children */
2379
2380#if defined(__CRTL_VER) && __CRTL_VER >= 70100322
2381
2382 /* waitpid() became available in the CRTL as of VMS 7.0, but only
2383 * in 7.2 did we get a version that fills in the VMS completion
2384 * status as Perl has always tried to do.
2385 */
2386
2387 sts = __vms_waitpid( pid, statusp, flags );
2388
2389 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
2390 return sts;
2391
2392 /* If the real waitpid tells us the child does not exist, we
2393 * fall through here to implement waiting for a child that
2394 * was created by some means other than exec() (say, spawned
2395 * from DCL) or to wait for a process that is not a subprocess
2396 * of the current process.
2397 */
2398
2399#endif /* defined(__CRTL_VER) && __CRTL_VER >= 70100322 */
2400
2401 $DESCRIPTOR(intdsc,"0 00:00:01");
2402 unsigned long int ownercode = JPI$_OWNER, ownerpid;
2403 unsigned long int pidcode = JPI$_PID, mypid;
2404 unsigned long int interval[2];
2405 int termination_mbu = 0;
2406 unsigned short qio_iosb[4];
2407 unsigned int jpi_iosb[2];
2408 struct itmlst_3 jpilist[3] = {
2409 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
2410 {sizeof(termination_mbu), JPI$_TMBU, &termination_mbu, 0},
2411 { 0, 0, 0, 0}
2412 };
2413 char trmmbx[NAM$C_DVI+1];
2414 $DESCRIPTOR(trmmbxdsc,trmmbx);
2415 struct accdef trmmsg;
2416 unsigned short int mbxchan;
2417
2418 if (pid <= 0) {
2419 /* Sorry folks, we don't presently implement rooting around for
2420 the first child we can find, and we definitely don't want to
2421 pass a pid of -1 to $getjpi, where it is a wildcard operation.
2422 */
2423 set_errno(ENOTSUP);
2424 return -1;
2425 }
2426
2427 /* Get the owner of the child so I can warn if it's not mine, plus
2428 * get the termination mailbox. If the process doesn't exist or I
2429 * don't have the privs to look at it, I can go home early.
2430 */
2431 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
2432 if (sts & 1) sts = jpi_iosb[0];
2433 if (!(sts & 1)) {
2434 switch (sts) {
2435 case SS$_NONEXPR:
2436 set_errno(ECHILD);
2437 break;
2438 case SS$_NOPRIV:
2439 set_errno(EACCES);
2440 break;
2441 default:
2442 _ckvmssts(sts);
2443 }
2444 set_vaxc_errno(sts);
2445 return -1;
2446 }
2447
2448 if (ckWARN(WARN_EXEC)) {
2449 /* remind folks they are asking for non-standard waitpid behavior */
2450 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2451 if (ownerpid != mypid)
2452 Perl_warner(aTHX_ WARN_EXEC,
2453 "waitpid: process %x is not a child of process %x",
2454 pid,mypid);
2455 }
2456
2457 /* It's possible to have a mailbox unit number but no actual mailbox; we
2458 * check for this by assigning a channel to it, which we need anyway.
2459 */
2460 if (termination_mbu != 0) {
2461 sprintf(trmmbx, "MBA%d:", termination_mbu);
2462 trmmbxdsc.dsc$w_length = strlen(trmmbx);
2463 sts = sys$assign(&trmmbxdsc, &mbxchan, 0, 0);
2464 if (sts == SS$_NOSUCHDEV) {
2465 termination_mbu = 0; /* set up to take "no mailbox" case */
2466 sts = SS$_NORMAL;
2467 }
2468 _ckvmssts(sts);
2469 }
2470 /* If the process doesn't have a termination mailbox, then simply check
2471 * on it once a second until it's not there anymore.
2472 */
2473 if (termination_mbu == 0) {
2474 _ckvmssts(sys$bintim(&intdsc,interval));
2475 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
2476 _ckvmssts(sys$schdwk(0,0,interval,0));
2477 _ckvmssts(sys$hiber());
2478 }
2479 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
2480 }
2481 else {
2482 /* If we do have a termination mailbox, post reads to it until we get a
2483 * termination message, discarding messages of the wrong type or for other
2484 * processes. If there is a place to put the final status, then do so.
2485 */
2486 sts = SS$_NORMAL;
2487 while (sts & 1) {
2488 memset((void *) &trmmsg, 0, sizeof(trmmsg));
2489 sts = sys$qiow(0,mbxchan,IO$_READVBLK,&qio_iosb,0,0,
2490 &trmmsg,ACC$K_TERMLEN,0,0,0,0);
2491 if (sts & 1) sts = qio_iosb[0];
2492
2493 if ( sts & 1
2494 && trmmsg.acc$w_msgtyp == MSG$_DELPROC
2495 && trmmsg.acc$l_pid == pid ) {
2496
2497 if (statusp) *statusp = trmmsg.acc$l_finalsts;
2498 sts = sys$dassgn(mbxchan);
2499 break;
2500 }
2501 }
2502 } /* termination_mbu ? */
2503
2504 _ckvmssts(sts);
2505 return pid;
2506
2507 } /* else one of our own pipe children */
2508
2509} /* end of waitpid() */
2510/*}}}*/
2511/*}}}*/
2512/*}}}*/
2513
2514/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
2515char *
2516my_gconvert(double val, int ndig, int trail, char *buf)
2517{
2518 static char __gcvtbuf[DBL_DIG+1];
2519 char *loc;
2520
2521 loc = buf ? buf : __gcvtbuf;
2522
2523#ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
2524 if (val < 1) {
2525 sprintf(loc,"%.*g",ndig,val);
2526 return loc;
2527 }
2528#endif
2529
2530 if (val) {
2531 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
2532 return gcvt(val,ndig,loc);
2533 }
2534 else {
2535 loc[0] = '0'; loc[1] = '\0';
2536 return loc;
2537 }
2538
2539}
2540/*}}}*/
2541
2542
2543/*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
2544/* Shortcut for common case of simple calls to $PARSE and $SEARCH
2545 * to expand file specification. Allows for a single default file
2546 * specification and a simple mask of options. If outbuf is non-NULL,
2547 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
2548 * the resultant file specification is placed. If outbuf is NULL, the
2549 * resultant file specification is placed into a static buffer.
2550 * The third argument, if non-NULL, is taken to be a default file
2551 * specification string. The fourth argument is unused at present.
2552 * rmesexpand() returns the address of the resultant string if
2553 * successful, and NULL on error.
2554 */
2555static char *mp_do_tounixspec(pTHX_ char *, char *, int);
2556
2557static char *
2558mp_do_rmsexpand(pTHX_ char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
2559{
2560 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
2561 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
2562 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
2563 struct FAB myfab = cc$rms_fab;
2564 struct NAM mynam = cc$rms_nam;
2565 STRLEN speclen;
2566 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
2567
2568 if (!filespec || !*filespec) {
2569 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
2570 return NULL;
2571 }
2572 if (!outbuf) {
2573 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
2574 else outbuf = __rmsexpand_retbuf;
2575 }
2576 if ((isunix = (strchr(filespec,'/') != NULL))) {
2577 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
2578 filespec = vmsfspec;
2579 }
2580
2581 myfab.fab$l_fna = filespec;
2582 myfab.fab$b_fns = strlen(filespec);
2583 myfab.fab$l_nam = &mynam;
2584
2585 if (defspec && *defspec) {
2586 if (strchr(defspec,'/') != NULL) {
2587 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
2588 defspec = tmpfspec;
2589 }
2590 myfab.fab$l_dna = defspec;
2591 myfab.fab$b_dns = strlen(defspec);
2592 }
2593
2594 mynam.nam$l_esa = esa;
2595 mynam.nam$b_ess = sizeof esa;
2596 mynam.nam$l_rsa = outbuf;
2597 mynam.nam$b_rss = NAM$C_MAXRSS;
2598
2599 retsts = sys$parse(&myfab,0,0);
2600 if (!(retsts & 1)) {
2601 mynam.nam$b_nop |= NAM$M_SYNCHK;
2602 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
2603 retsts = sys$parse(&myfab,0,0);
2604 if (retsts & 1) goto expanded;
2605 }
2606 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
2607 (void) sys$parse(&myfab,0,0); /* Free search context */
2608 if (out) Safefree(out);
2609 set_vaxc_errno(retsts);
2610 if (retsts == RMS$_PRV) set_errno(EACCES);
2611 else if (retsts == RMS$_DEV) set_errno(ENODEV);
2612 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
2613 else set_errno(EVMSERR);
2614 return NULL;
2615 }
2616 retsts = sys$search(&myfab,0,0);
2617 if (!(retsts & 1) && retsts != RMS$_FNF) {
2618 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2619 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2620 if (out) Safefree(out);
2621 set_vaxc_errno(retsts);
2622 if (retsts == RMS$_PRV) set_errno(EACCES);
2623 else set_errno(EVMSERR);
2624 return NULL;
2625 }
2626
2627 /* If the input filespec contained any lowercase characters,
2628 * downcase the result for compatibility with Unix-minded code. */
2629 expanded:
2630 for (out = myfab.fab$l_fna; *out; out++)
2631 if (islower(*out)) { haslower = 1; break; }
2632 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
2633 else { out = esa; speclen = mynam.nam$b_esl; }
2634 /* Trim off null fields added by $PARSE
2635 * If type > 1 char, must have been specified in original or default spec
2636 * (not true for version; $SEARCH may have added version of existing file).
2637 */
2638 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
2639 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
2640 (mynam.nam$l_ver - mynam.nam$l_type == 1);
2641 if (trimver || trimtype) {
2642 if (defspec && *defspec) {
2643 char defesa[NAM$C_MAXRSS];
2644 struct FAB deffab = cc$rms_fab;
2645 struct NAM defnam = cc$rms_nam;
2646
2647 deffab.fab$l_nam = &defnam;
2648 deffab.fab$l_fna = defspec; deffab.fab$b_fns = myfab.fab$b_dns;
2649 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
2650 defnam.nam$b_nop = NAM$M_SYNCHK;
2651 if (sys$parse(&deffab,0,0) & 1) {
2652 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
2653 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
2654 }
2655 }
2656 if (trimver) speclen = mynam.nam$l_ver - out;
2657 if (trimtype) {
2658 /* If we didn't already trim version, copy down */
2659 if (speclen > mynam.nam$l_ver - out)
2660 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
2661 speclen - (mynam.nam$l_ver - out));
2662 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
2663 }
2664 }
2665 /* If we just had a directory spec on input, $PARSE "helpfully"
2666 * adds an empty name and type for us */
2667 if (mynam.nam$l_name == mynam.nam$l_type &&
2668 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
2669 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
2670 speclen = mynam.nam$l_name - out;
2671 out[speclen] = '\0';
2672 if (haslower) __mystrtolower(out);
2673
2674 /* Have we been working with an expanded, but not resultant, spec? */
2675 /* Also, convert back to Unix syntax if necessary. */
2676 if (!mynam.nam$b_rsl) {
2677 if (isunix) {
2678 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
2679 }
2680 else strcpy(outbuf,esa);
2681 }
2682 else if (isunix) {
2683 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
2684 strcpy(outbuf,tmpfspec);
2685 }
2686 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
2687 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
2688 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
2689 return outbuf;
2690}
2691/*}}}*/
2692/* External entry points */
2693char *Perl_rmsexpand(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2694{ return do_rmsexpand(spec,buf,0,def,opt); }
2695char *Perl_rmsexpand_ts(pTHX_ char *spec, char *buf, char *def, unsigned opt)
2696{ return do_rmsexpand(spec,buf,1,def,opt); }
2697
2698
2699/*
2700** The following routines are provided to make life easier when
2701** converting among VMS-style and Unix-style directory specifications.
2702** All will take input specifications in either VMS or Unix syntax. On
2703** failure, all return NULL. If successful, the routines listed below
2704** return a pointer to a buffer containing the appropriately
2705** reformatted spec (and, therefore, subsequent calls to that routine
2706** will clobber the result), while the routines of the same names with
2707** a _ts suffix appended will return a pointer to a mallocd string
2708** containing the appropriately reformatted spec.
2709** In all cases, only explicit syntax is altered; no check is made that
2710** the resulting string is valid or that the directory in question
2711** actually exists.
2712**
2713** fileify_dirspec() - convert a directory spec into the name of the
2714** directory file (i.e. what you can stat() to see if it's a dir).
2715** The style (VMS or Unix) of the result is the same as the style
2716** of the parameter passed in.
2717** pathify_dirspec() - convert a directory spec into a path (i.e.
2718** what you prepend to a filename to indicate what directory it's in).
2719** The style (VMS or Unix) of the result is the same as the style
2720** of the parameter passed in.
2721** tounixpath() - convert a directory spec into a Unix-style path.
2722** tovmspath() - convert a directory spec into a VMS-style path.
2723** tounixspec() - convert any file spec into a Unix-style file spec.
2724** tovmsspec() - convert any file spec into a VMS-style spec.
2725**
2726** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
2727** Permission is given to distribute this code as part of the Perl
2728** standard distribution under the terms of the GNU General Public
2729** License or the Perl Artistic License. Copies of each may be
2730** found in the Perl standard distribution.
2731 */
2732
2733/*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
2734static char *mp_do_fileify_dirspec(pTHX_ char *dir,char *buf,int ts)
2735{
2736 static char __fileify_retbuf[NAM$C_MAXRSS+1];
2737 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
2738 char *retspec, *cp1, *cp2, *lastdir;
2739 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
2740
2741 if (!dir || !*dir) {
2742 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
2743 }
2744 dirlen = strlen(dir);
2745 while (dirlen && dir[dirlen-1] == '/') --dirlen;
2746 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
2747 strcpy(trndir,"/sys$disk/000000");
2748 dir = trndir;
2749 dirlen = 16;
2750 }
2751 if (dirlen > NAM$C_MAXRSS) {
2752 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
2753 }
2754 if (!strpbrk(dir+1,"/]>:")) {
2755 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2756 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
2757 dir = trndir;
2758 dirlen = strlen(dir);
2759 }
2760 else {
2761 strncpy(trndir,dir,dirlen);
2762 trndir[dirlen] = '\0';
2763 dir = trndir;
2764 }
2765 /* If we were handed a rooted logical name or spec, treat it like a
2766 * simple directory, so that
2767 * $ Define myroot dev:[dir.]
2768 * ... do_fileify_dirspec("myroot",buf,1) ...
2769 * does something useful.
2770 */
2771 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".]")) {
2772 dir[--dirlen] = '\0';
2773 dir[dirlen-1] = ']';
2774 }
2775 if (dirlen >= 2 && !strcmp(dir+dirlen-2,".>")) {
2776 dir[--dirlen] = '\0';
2777 dir[dirlen-1] = '>';
2778 }
2779
2780 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
2781 /* If we've got an explicit filename, we can just shuffle the string. */
2782 if (*(cp1+1)) hasfilename = 1;
2783 /* Similarly, we can just back up a level if we've got multiple levels
2784 of explicit directories in a VMS spec which ends with directories. */
2785 else {
2786 for (cp2 = cp1; cp2 > dir; cp2--) {
2787 if (*cp2 == '.') {
2788 *cp2 = *cp1; *cp1 = '\0';
2789 hasfilename = 1;
2790 break;
2791 }
2792 if (*cp2 == '[' || *cp2 == '<') break;
2793 }
2794 }
2795 }
2796
2797 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
2798 if (dir[0] == '.') {
2799 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
2800 return do_fileify_dirspec("[]",buf,ts);
2801 else if (dir[1] == '.' &&
2802 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
2803 return do_fileify_dirspec("[-]",buf,ts);
2804 }
2805 if (dirlen && dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
2806 dirlen -= 1; /* to last element */
2807 lastdir = strrchr(dir,'/');
2808 }
2809 else if ((cp1 = strstr(dir,"/.")) != NULL) {
2810 /* If we have "/." or "/..", VMSify it and let the VMS code
2811 * below expand it, rather than repeating the code to handle
2812 * relative components of a filespec here */
2813 do {
2814 if (*(cp1+2) == '.') cp1++;
2815 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
2816 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
2817 if (strchr(vmsdir,'/') != NULL) {
2818 /* If do_tovmsspec() returned it, it must have VMS syntax
2819 * delimiters in it, so it's a mixed VMS/Unix spec. We take
2820 * the time to check this here only so we avoid a recursion
2821 * loop; otherwise, gigo.
2822 */
2823 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
2824 }
2825 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
2826 return do_tounixspec(trndir,buf,ts);
2827 }
2828 cp1++;
2829 } while ((cp1 = strstr(cp1,"/.")) != NULL);
2830 lastdir = strrchr(dir,'/');
2831 }
2832 else if (dirlen >= 7 && !strcmp(&dir[dirlen-7],"/000000")) {
2833 /* Ditto for specs that end in an MFD -- let the VMS code
2834 * figure out whether it's a real device or a rooted logical. */
2835 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
2836 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
2837 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
2838 return do_tounixspec(trndir,buf,ts);
2839 }
2840 else {
2841 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
2842 !(lastdir = cp1 = strrchr(dir,']')) &&
2843 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
2844 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
2845 int ver; char *cp3;
2846 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
2847 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
2848 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
2849 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
2850 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
2851 (ver || *cp3)))))) {
2852 set_errno(ENOTDIR);
2853 set_vaxc_errno(RMS$_DIR);
2854 return NULL;
2855 }
2856 dirlen = cp2 - dir;
2857 }
2858 }
2859 /* If we lead off with a device or rooted logical, add the MFD
2860 if we're specifying a top-level directory. */
2861 if (lastdir && *dir == '/') {
2862 addmfd = 1;
2863 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
2864 if (*cp1 == '/') {
2865 addmfd = 0;
2866 break;
2867 }
2868 }
2869 }
2870 retlen = dirlen + (addmfd ? 13 : 6);
2871 if (buf) retspec = buf;
2872 else if (ts) New(1309,retspec,retlen+1,char);
2873 else retspec = __fileify_retbuf;
2874 if (addmfd) {
2875 dirlen = lastdir - dir;
2876 memcpy(retspec,dir,dirlen);
2877 strcpy(&retspec[dirlen],"/000000");
2878 strcpy(&retspec[dirlen+7],lastdir);
2879 }
2880 else {
2881 memcpy(retspec,dir,dirlen);
2882 retspec[dirlen] = '\0';
2883 }
2884 /* We've picked up everything up to the directory file name.
2885 Now just add the type and version, and we're set. */
2886 strcat(retspec,".dir;1");
2887 return retspec;
2888 }
2889 else { /* VMS-style directory spec */
2890 char esa[NAM$C_MAXRSS+1], term, *cp;
2891 unsigned long int sts, cmplen, haslower = 0;
2892 struct FAB dirfab = cc$rms_fab;
2893 struct NAM savnam, dirnam = cc$rms_nam;
2894
2895 dirfab.fab$b_fns = strlen(dir);
2896 dirfab.fab$l_fna = dir;
2897 dirfab.fab$l_nam = &dirnam;
2898 dirfab.fab$l_dna = ".DIR;1";
2899 dirfab.fab$b_dns = 6;
2900 dirnam.nam$b_ess = NAM$C_MAXRSS;
2901 dirnam.nam$l_esa = esa;
2902
2903 for (cp = dir; *cp; cp++)
2904 if (islower(*cp)) { haslower = 1; break; }
2905 if (!((sts = sys$parse(&dirfab))&1)) {
2906 if (dirfab.fab$l_sts == RMS$_DIR) {
2907 dirnam.nam$b_nop |= NAM$M_SYNCHK;
2908 sts = sys$parse(&dirfab) & 1;
2909 }
2910 if (!sts) {
2911 set_errno(EVMSERR);
2912 set_vaxc_errno(dirfab.fab$l_sts);
2913 return NULL;
2914 }
2915 }
2916 else {
2917 savnam = dirnam;
2918 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
2919 /* Yes; fake the fnb bits so we'll check type below */
2920 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
2921 }
2922 else { /* No; just work with potential name */
2923 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
2924 else {
2925 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
2926 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2927 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2928 return NULL;
2929 }
2930 }
2931 }
2932 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
2933 cp1 = strchr(esa,']');
2934 if (!cp1) cp1 = strchr(esa,'>');
2935 if (cp1) { /* Should always be true */
2936 dirnam.nam$b_esl -= cp1 - esa - 1;
2937 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
2938 }
2939 }
2940 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
2941 /* Yep; check version while we're at it, if it's there. */
2942 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
2943 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
2944 /* Something other than .DIR[;1]. Bzzt. */
2945 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2946 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2947 set_errno(ENOTDIR);
2948 set_vaxc_errno(RMS$_DIR);
2949 return NULL;
2950 }
2951 }
2952 esa[dirnam.nam$b_esl] = '\0';
2953 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
2954 /* They provided at least the name; we added the type, if necessary, */
2955 if (buf) retspec = buf; /* in sys$parse() */
2956 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
2957 else retspec = __fileify_retbuf;
2958 strcpy(retspec,esa);
2959 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2960 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2961 return retspec;
2962 }
2963 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
2964 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
2965 *cp1 = '\0';
2966 dirnam.nam$b_esl -= 9;
2967 }
2968 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
2969 if (cp1 == NULL) { /* should never happen */
2970 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
2971 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2972 return NULL;
2973 }
2974 term = *cp1;
2975 *cp1 = '\0';
2976 retlen = strlen(esa);
2977 if ((cp1 = strrchr(esa,'.')) != NULL) {
2978 /* There's more than one directory in the path. Just roll back. */
2979 *cp1 = term;
2980 if (buf) retspec = buf;
2981 else if (ts) New(1311,retspec,retlen+7,char);
2982 else retspec = __fileify_retbuf;
2983 strcpy(retspec,esa);
2984 }
2985 else {
2986 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
2987 /* Go back and expand rooted logical name */
2988 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
2989 if (!(sys$parse(&dirfab) & 1)) {
2990 dirnam.nam$l_rlf = NULL;
2991 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
2992 set_errno(EVMSERR);
2993 set_vaxc_errno(dirfab.fab$l_sts);
2994 return NULL;
2995 }
2996 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
2997 if (buf) retspec = buf;
2998 else if (ts) New(1312,retspec,retlen+16,char);
2999 else retspec = __fileify_retbuf;
3000 cp1 = strstr(esa,"][");
3001 if (!cp1) cp1 = strstr(esa,"]<");
3002 dirlen = cp1 - esa;
3003 memcpy(retspec,esa,dirlen);
3004 if (!strncmp(cp1+2,"000000]",7)) {
3005 retspec[dirlen-1] = '\0';
3006 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3007 if (*cp1 == '.') *cp1 = ']';
3008 else {
3009 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3010 memcpy(cp1+1,"000000]",7);
3011 }
3012 }
3013 else {
3014 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
3015 retspec[retlen] = '\0';
3016 /* Convert last '.' to ']' */
3017 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3018 if (*cp1 == '.') *cp1 = ']';
3019 else {
3020 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3021 memcpy(cp1+1,"000000]",7);
3022 }
3023 }
3024 }
3025 else { /* This is a top-level dir. Add the MFD to the path. */
3026 if (buf) retspec = buf;
3027 else if (ts) New(1312,retspec,retlen+16,char);
3028 else retspec = __fileify_retbuf;
3029 cp1 = esa;
3030 cp2 = retspec;
3031 while (*cp1 != ':') *(cp2++) = *(cp1++);
3032 strcpy(cp2,":[000000]");
3033 cp1 += 2;
3034 strcpy(cp2+9,cp1);
3035 }
3036 }
3037 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3038 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3039 /* We've set up the string up through the filename. Add the
3040 type and version, and we're done. */
3041 strcat(retspec,".DIR;1");
3042
3043 /* $PARSE may have upcased filespec, so convert output to lower
3044 * case if input contained any lowercase characters. */
3045 if (haslower) __mystrtolower(retspec);
3046 return retspec;
3047 }
3048} /* end of do_fileify_dirspec() */
3049/*}}}*/
3050/* External entry points */
3051char *Perl_fileify_dirspec(pTHX_ char *dir, char *buf)
3052{ return do_fileify_dirspec(dir,buf,0); }
3053char *Perl_fileify_dirspec_ts(pTHX_ char *dir, char *buf)
3054{ return do_fileify_dirspec(dir,buf,1); }
3055
3056/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
3057static char *mp_do_pathify_dirspec(pTHX_ char *dir,char *buf, int ts)
3058{
3059 static char __pathify_retbuf[NAM$C_MAXRSS+1];
3060 unsigned long int retlen;
3061 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
3062
3063 if (!dir || !*dir) {
3064 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
3065 }
3066
3067 if (*dir) strcpy(trndir,dir);
3068 else getcwd(trndir,sizeof trndir - 1);
3069
3070 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
3071 && my_trnlnm(trndir,trndir,0)) {
3072 STRLEN trnlen = strlen(trndir);
3073
3074 /* Trap simple rooted lnms, and return lnm:[000000] */
3075 if (!strcmp(trndir+trnlen-2,".]")) {
3076 if (buf) retpath = buf;
3077 else if (ts) New(1318,retpath,strlen(dir)+10,char);
3078 else retpath = __pathify_retbuf;
3079 strcpy(retpath,dir);
3080 strcat(retpath,":[000000]");
3081 return retpath;
3082 }
3083 }
3084 dir = trndir;
3085
3086 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
3087 if (*dir == '.' && (*(dir+1) == '\0' ||
3088 (*(dir+1) == '.' && *(dir+2) == '\0')))
3089 retlen = 2 + (*(dir+1) != '\0');
3090 else {
3091 if ( !(cp1 = strrchr(dir,'/')) &&
3092 !(cp1 = strrchr(dir,']')) &&
3093 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
3094 if ((cp2 = strchr(cp1,'.')) != NULL &&
3095 (*(cp2-1) != '/' || /* Trailing '.', '..', */
3096 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
3097 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
3098 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
3099 int ver; char *cp3;
3100 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3101 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3102 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3103 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3104 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3105 (ver || *cp3)))))) {
3106 set_errno(ENOTDIR);
3107 set_vaxc_errno(RMS$_DIR);
3108 return NULL;
3109 }
3110 retlen = cp2 - dir + 1;
3111 }
3112 else { /* No file type present. Treat the filename as a directory. */
3113 retlen = strlen(dir) + 1;
3114 }
3115 }
3116 if (buf) retpath = buf;
3117 else if (ts) New(1313,retpath,retlen+1,char);
3118 else retpath = __pathify_retbuf;
3119 strncpy(retpath,dir,retlen-1);
3120 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
3121 retpath[retlen-1] = '/'; /* with '/', add it. */
3122 retpath[retlen] = '\0';
3123 }
3124 else retpath[retlen-1] = '\0';
3125 }
3126 else { /* VMS-style directory spec */
3127 char esa[NAM$C_MAXRSS+1], *cp;
3128 unsigned long int sts, cmplen, haslower;
3129 struct FAB dirfab = cc$rms_fab;
3130 struct NAM savnam, dirnam = cc$rms_nam;
3131
3132 /* If we've got an explicit filename, we can just shuffle the string. */
3133 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
3134 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
3135 if ((cp2 = strchr(cp1,'.')) != NULL) {
3136 int ver; char *cp3;
3137 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3138 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3139 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3140 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3141 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3142 (ver || *cp3)))))) {
3143 set_errno(ENOTDIR);
3144 set_vaxc_errno(RMS$_DIR);
3145 return NULL;
3146 }
3147 }
3148 else { /* No file type, so just draw name into directory part */
3149 for (cp2 = cp1; *cp2; cp2++) ;
3150 }
3151 *cp2 = *cp1;
3152 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
3153 *cp1 = '.';
3154 /* We've now got a VMS 'path'; fall through */
3155 }
3156 dirfab.fab$b_fns = strlen(dir);
3157 dirfab.fab$l_fna = dir;
3158 if (dir[dirfab.fab$b_fns-1] == ']' ||
3159 dir[dirfab.fab$b_fns-1] == '>' ||
3160 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
3161 if (buf) retpath = buf;
3162 else if (ts) New(1314,retpath,strlen(dir)+1,char);
3163 else retpath = __pathify_retbuf;
3164 strcpy(retpath,dir);
3165 return retpath;
3166 }
3167 dirfab.fab$l_dna = ".DIR;1";
3168 dirfab.fab$b_dns = 6;
3169 dirfab.fab$l_nam = &dirnam;
3170 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
3171 dirnam.nam$l_esa = esa;
3172
3173 for (cp = dir; *cp; cp++)
3174 if (islower(*cp)) { haslower = 1; break; }
3175
3176 if (!(sts = (sys$parse(&dirfab)&1))) {
3177 if (dirfab.fab$l_sts == RMS$_DIR) {
3178 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3179 sts = sys$parse(&dirfab) & 1;
3180 }
3181 if (!sts) {
3182 set_errno(EVMSERR);
3183 set_vaxc_errno(dirfab.fab$l_sts);
3184 return NULL;
3185 }
3186 }
3187 else {
3188 savnam = dirnam;
3189 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
3190 if (dirfab.fab$l_sts != RMS$_FNF) {
3191 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3192 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3193 set_errno(EVMSERR);
3194 set_vaxc_errno(dirfab.fab$l_sts);
3195 return NULL;
3196 }
3197 dirnam = savnam; /* No; just work with potential name */
3198 }
3199 }
3200 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3201 /* Yep; check version while we're at it, if it's there. */
3202 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3203 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3204 /* Something other than .DIR[;1]. Bzzt. */
3205 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3206 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3207 set_errno(ENOTDIR);
3208 set_vaxc_errno(RMS$_DIR);
3209 return NULL;
3210 }
3211 }
3212 /* OK, the type was fine. Now pull any file name into the
3213 directory path. */
3214 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
3215 else {
3216 cp1 = strrchr(esa,'>');
3217 *dirnam.nam$l_type = '>';
3218 }
3219 *cp1 = '.';
3220 *(dirnam.nam$l_type + 1) = '\0';
3221 retlen = dirnam.nam$l_type - esa + 2;
3222 if (buf) retpath = buf;
3223 else if (ts) New(1314,retpath,retlen,char);
3224 else retpath = __pathify_retbuf;
3225 strcpy(retpath,esa);
3226 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3227 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3228 /* $PARSE may have upcased filespec, so convert output to lower
3229 * case if input contained any lowercase characters. */
3230 if (haslower) __mystrtolower(retpath);
3231 }
3232
3233 return retpath;
3234} /* end of do_pathify_dirspec() */
3235/*}}}*/
3236/* External entry points */
3237char *Perl_pathify_dirspec(pTHX_ char *dir, char *buf)
3238{ return do_pathify_dirspec(dir,buf,0); }
3239char *Perl_pathify_dirspec_ts(pTHX_ char *dir, char *buf)
3240{ return do_pathify_dirspec(dir,buf,1); }
3241
3242/*{{{ char *tounixspec[_ts](char *path, char *buf)*/
3243static char *mp_do_tounixspec(pTHX_ char *spec, char *buf, int ts)
3244{
3245 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
3246 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
3247 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
3248
3249 if (spec == NULL) return NULL;
3250 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
3251 if (buf) rslt = buf;
3252 else if (ts) {
3253 retlen = strlen(spec);
3254 cp1 = strchr(spec,'[');
3255 if (!cp1) cp1 = strchr(spec,'<');
3256 if (cp1) {
3257 for (cp1++; *cp1; cp1++) {
3258 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
3259 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
3260 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
3261 }
3262 }
3263 New(1315,rslt,retlen+2+2*expand,char);
3264 }
3265 else rslt = __tounixspec_retbuf;
3266 if (strchr(spec,'/') != NULL) {
3267 strcpy(rslt,spec);
3268 return rslt;
3269 }
3270
3271 cp1 = rslt;
3272 cp2 = spec;
3273 dirend = strrchr(spec,']');
3274 if (dirend == NULL) dirend = strrchr(spec,'>');
3275 if (dirend == NULL) dirend = strchr(spec,':');
3276 if (dirend == NULL) {
3277 strcpy(rslt,spec);
3278 return rslt;
3279 }
3280 if (*cp2 != '[' && *cp2 != '<') {
3281 *(cp1++) = '/';
3282 }
3283 else { /* the VMS spec begins with directories */
3284 cp2++;
3285 if (*cp2 == ']' || *cp2 == '>') {
3286 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
3287 return rslt;
3288 }
3289 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
3290 if (getcwd(tmp,sizeof tmp,1) == NULL) {
3291 if (ts) Safefree(rslt);
3292 return NULL;
3293 }
3294 do {
3295 cp3 = tmp;
3296 while (*cp3 != ':' && *cp3) cp3++;
3297 *(cp3++) = '\0';
3298 if (strchr(cp3,']') != NULL) break;
3299 } while (vmstrnenv(tmp,tmp,0,fildev,0));
3300 if (ts && !buf &&
3301 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
3302 retlen = devlen + dirlen;
3303 Renew(rslt,retlen+1+2*expand,char);
3304 cp1 = rslt;
3305 }
3306 cp3 = tmp;
3307 *(cp1++) = '/';
3308 while (*cp3) {
3309 *(cp1++) = *(cp3++);
3310 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
3311 }
3312 *(cp1++) = '/';
3313 }
3314 else if ( *cp2 == '.') {
3315 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
3316 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3317 cp2 += 3;
3318 }
3319 else cp2++;
3320 }
3321 }
3322 for (; cp2 <= dirend; cp2++) {
3323 if (*cp2 == ':') {
3324 *(cp1++) = '/';
3325 if (*(cp2+1) == '[') cp2++;
3326 }
3327 else if (*cp2 == ']' || *cp2 == '>') {
3328 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
3329 }
3330 else if (*cp2 == '.') {
3331 *(cp1++) = '/';
3332 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
3333 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
3334 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
3335 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
3336 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
3337 }
3338 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
3339 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
3340 cp2 += 2;
3341 }
3342 }
3343 else if (*cp2 == '-') {
3344 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
3345 while (*cp2 == '-') {
3346 cp2++;
3347 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3348 }
3349 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
3350 if (ts) Safefree(rslt); /* filespecs like */
3351 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
3352 return NULL;
3353 }
3354 }
3355 else *(cp1++) = *cp2;
3356 }
3357 else *(cp1++) = *cp2;
3358 }
3359 while (*cp2) *(cp1++) = *(cp2++);
3360 *cp1 = '\0';
3361
3362 return rslt;
3363
3364} /* end of do_tounixspec() */
3365/*}}}*/
3366/* External entry points */
3367char *Perl_tounixspec(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
3368char *Perl_tounixspec_ts(pTHX_ char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
3369
3370/*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
3371static char *mp_do_tovmsspec(pTHX_ char *path, char *buf, int ts) {
3372 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
3373 char *rslt, *dirend;
3374 register char *cp1, *cp2;
3375 unsigned long int infront = 0, hasdir = 1;
3376
3377 if (path == NULL) return NULL;
3378 if (buf) rslt = buf;
3379 else if (ts) New(1316,rslt,strlen(path)+9,char);
3380 else rslt = __tovmsspec_retbuf;
3381 if (strpbrk(path,"]:>") ||
3382 (dirend = strrchr(path,'/')) == NULL) {
3383 if (path[0] == '.') {
3384 if (path[1] == '\0') strcpy(rslt,"[]");
3385 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
3386 else strcpy(rslt,path); /* probably garbage */
3387 }
3388 else strcpy(rslt,path);
3389 return rslt;
3390 }
3391 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
3392 if (!*(dirend+2)) dirend +=2;
3393 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
3394 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
3395 }
3396 cp1 = rslt;
3397 cp2 = path;
3398 if (*cp2 == '/') {
3399 char trndev[NAM$C_MAXRSS+1];
3400 int islnm, rooted;
3401 STRLEN trnend;
3402
3403 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
3404 if (!*(cp2+1)) {
3405 if (!buf & ts) Renew(rslt,18,char);
3406 strcpy(rslt,"sys$disk:[000000]");
3407 return rslt;
3408 }
3409 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
3410 *cp1 = '\0';
3411 islnm = my_trnlnm(rslt,trndev,0);
3412 trnend = islnm ? strlen(trndev) - 1 : 0;
3413 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
3414 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
3415 /* If the first element of the path is a logical name, determine
3416 * whether it has to be translated so we can add more directories. */
3417 if (!islnm || rooted) {
3418 *(cp1++) = ':';
3419 *(cp1++) = '[';
3420 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
3421 else cp2++;
3422 }
3423 else {
3424 if (cp2 != dirend) {
3425 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
3426 strcpy(rslt,trndev);
3427 cp1 = rslt + trnend;
3428 *(cp1++) = '.';
3429 cp2++;
3430 }
3431 else {
3432 *(cp1++) = ':';
3433 hasdir = 0;
3434 }
3435 }
3436 }
3437 else {
3438 *(cp1++) = '[';
3439 if (*cp2 == '.') {
3440 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
3441 cp2 += 2; /* skip over "./" - it's redundant */
3442 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
3443 }
3444 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3445 *(cp1++) = '-'; /* "../" --> "-" */
3446 cp2 += 3;
3447 }
3448 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
3449 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
3450 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3451 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
3452 cp2 += 4;
3453 }
3454 if (cp2 > dirend) cp2 = dirend;
3455 }
3456 else *(cp1++) = '.';
3457 }
3458 for (; cp2 < dirend; cp2++) {
3459 if (*cp2 == '/') {
3460 if (*(cp2-1) == '/') continue;
3461 if (*(cp1-1) != '.') *(cp1++) = '.';
3462 infront = 0;
3463 }
3464 else if (!infront && *cp2 == '.') {
3465 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
3466 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
3467 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
3468 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
3469 else if (*(cp1-2) == '[') *(cp1-1) = '-';
3470 else { /* back up over previous directory name */
3471 cp1--;
3472 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
3473 if (*(cp1-1) == '[') {
3474 memcpy(cp1,"000000.",7);
3475 cp1 += 7;
3476 }
3477 }
3478 cp2 += 2;
3479 if (cp2 == dirend) break;
3480 }
3481 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
3482 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
3483 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
3484 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
3485 if (!*(cp2+3)) {
3486 *(cp1++) = '.'; /* Simulate trailing '/' */
3487 cp2 += 2; /* for loop will incr this to == dirend */
3488 }
3489 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
3490 }
3491 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
3492 }
3493 else {
3494 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
3495 if (*cp2 == '.') *(cp1++) = '_';
3496 else *(cp1++) = *cp2;
3497 infront = 1;
3498 }
3499 }
3500 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
3501 if (hasdir) *(cp1++) = ']';
3502 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
3503 while (*cp2) *(cp1++) = *(cp2++);
3504 *cp1 = '\0';
3505
3506 return rslt;
3507
3508} /* end of do_tovmsspec() */
3509/*}}}*/
3510/* External entry points */
3511char *Perl_tovmsspec(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,0); }
3512char *Perl_tovmsspec_ts(pTHX_ char *path, char *buf) { return do_tovmsspec(path,buf,1); }
3513
3514/*{{{ char *tovmspath[_ts](char *path, char *buf)*/
3515static char *mp_do_tovmspath(pTHX_ char *path, char *buf, int ts) {
3516 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
3517 int vmslen;
3518 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
3519
3520 if (path == NULL) return NULL;
3521 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3522 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
3523 if (buf) return buf;
3524 else if (ts) {
3525 vmslen = strlen(vmsified);
3526 New(1317,cp,vmslen+1,char);
3527 memcpy(cp,vmsified,vmslen);
3528 cp[vmslen] = '\0';
3529 return cp;
3530 }
3531 else {
3532 strcpy(__tovmspath_retbuf,vmsified);
3533 return __tovmspath_retbuf;
3534 }
3535
3536} /* end of do_tovmspath() */
3537/*}}}*/
3538/* External entry points */
3539char *Perl_tovmspath(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,0); }
3540char *Perl_tovmspath_ts(pTHX_ char *path, char *buf) { return do_tovmspath(path,buf,1); }
3541
3542
3543/*{{{ char *tounixpath[_ts](char *path, char *buf)*/
3544static char *mp_do_tounixpath(pTHX_ char *path, char *buf, int ts) {
3545 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
3546 int unixlen;
3547 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
3548
3549 if (path == NULL) return NULL;
3550 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
3551 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
3552 if (buf) return buf;
3553 else if (ts) {
3554 unixlen = strlen(unixified);
3555 New(1317,cp,unixlen+1,char);
3556 memcpy(cp,unixified,unixlen);
3557 cp[unixlen] = '\0';
3558 return cp;
3559 }
3560 else {
3561 strcpy(__tounixpath_retbuf,unixified);
3562 return __tounixpath_retbuf;
3563 }
3564
3565} /* end of do_tounixpath() */
3566/*}}}*/
3567/* External entry points */
3568char *Perl_tounixpath(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,0); }
3569char *Perl_tounixpath_ts(pTHX_ char *path, char *buf) { return do_tounixpath(path,buf,1); }
3570
3571/*
3572 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
3573 *
3574 *****************************************************************************
3575 * *
3576 * Copyright (C) 1989-1994 by *
3577 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
3578 * *
3579 * Permission is hereby granted for the reproduction of this software, *
3580 * on condition that this copyright notice is included in the reproduction, *
3581 * and that such reproduction is not for purposes of profit or material *
3582 * gain. *
3583 * *
3584 * 27-Aug-1994 Modified for inclusion in perl5 *
3585 * by Charles Bailey bailey@newman.upenn.edu *
3586 *****************************************************************************
3587 */
3588
3589/*
3590 * getredirection() is intended to aid in porting C programs
3591 * to VMS (Vax-11 C). The native VMS environment does not support
3592 * '>' and '<' I/O redirection, or command line wild card expansion,
3593 * or a command line pipe mechanism using the '|' AND background
3594 * command execution '&'. All of these capabilities are provided to any
3595 * C program which calls this procedure as the first thing in the
3596 * main program.
3597 * The piping mechanism will probably work with almost any 'filter' type
3598 * of program. With suitable modification, it may useful for other
3599 * portability problems as well.
3600 *
3601 * Author: Mark Pizzolato mark@infocomm.com
3602 */
3603struct list_item
3604 {
3605 struct list_item *next;
3606 char *value;
3607 };
3608
3609static void add_item(struct list_item **head,
3610 struct list_item **tail,
3611 char *value,
3612 int *count);
3613
3614static void mp_expand_wild_cards(pTHX_ char *item,
3615 struct list_item **head,
3616 struct list_item **tail,
3617 int *count);
3618
3619static int background_process(int argc, char **argv);
3620
3621static void pipe_and_fork(pTHX_ char **cmargv);
3622
3623/*{{{ void getredirection(int *ac, char ***av)*/
3624static void
3625mp_getredirection(pTHX_ int *ac, char ***av)
3626/*
3627 * Process vms redirection arg's. Exit if any error is seen.
3628 * If getredirection() processes an argument, it is erased
3629 * from the vector. getredirection() returns a new argc and argv value.
3630 * In the event that a background command is requested (by a trailing "&"),
3631 * this routine creates a background subprocess, and simply exits the program.
3632 *
3633 * Warning: do not try to simplify the code for vms. The code
3634 * presupposes that getredirection() is called before any data is
3635 * read from stdin or written to stdout.
3636 *
3637 * Normal usage is as follows:
3638 *
3639 * main(argc, argv)
3640 * int argc;
3641 * char *argv[];
3642 * {
3643 * getredirection(&argc, &argv);
3644 * }
3645 */
3646{
3647 int argc = *ac; /* Argument Count */
3648 char **argv = *av; /* Argument Vector */
3649 char *ap; /* Argument pointer */
3650 int j; /* argv[] index */
3651 int item_count = 0; /* Count of Items in List */
3652 struct list_item *list_head = 0; /* First Item in List */
3653 struct list_item *list_tail; /* Last Item in List */
3654 char *in = NULL; /* Input File Name */
3655 char *out = NULL; /* Output File Name */
3656 char *outmode = "w"; /* Mode to Open Output File */
3657 char *err = NULL; /* Error File Name */
3658 char *errmode = "w"; /* Mode to Open Error File */
3659 int cmargc = 0; /* Piped Command Arg Count */
3660 char **cmargv = NULL;/* Piped Command Arg Vector */
3661
3662 /*
3663 * First handle the case where the last thing on the line ends with
3664 * a '&'. This indicates the desire for the command to be run in a
3665 * subprocess, so we satisfy that desire.
3666 */
3667 ap = argv[argc-1];
3668 if (0 == strcmp("&", ap))
3669 exit(background_process(--argc, argv));
3670 if (*ap && '&' == ap[strlen(ap)-1])
3671 {
3672 ap[strlen(ap)-1] = '\0';
3673 exit(background_process(argc, argv));
3674 }
3675 /*
3676 * Now we handle the general redirection cases that involve '>', '>>',
3677 * '<', and pipes '|'.
3678 */
3679 for (j = 0; j < argc; ++j)
3680 {
3681 if (0 == strcmp("<", argv[j]))
3682 {
3683 if (j+1 >= argc)
3684 {
3685 fprintf(stderr,"No input file after < on command line");
3686 exit(LIB$_WRONUMARG);
3687 }
3688 in = argv[++j];
3689 continue;
3690 }
3691 if ('<' == *(ap = argv[j]))
3692 {
3693 in = 1 + ap;
3694 continue;
3695 }
3696 if (0 == strcmp(">", ap))
3697 {
3698 if (j+1 >= argc)
3699 {
3700 fprintf(stderr,"No output file after > on command line");
3701 exit(LIB$_WRONUMARG);
3702 }
3703 out = argv[++j];
3704 continue;
3705 }
3706 if ('>' == *ap)
3707 {
3708 if ('>' == ap[1])
3709 {
3710 outmode = "a";
3711 if ('\0' == ap[2])
3712 out = argv[++j];
3713 else
3714 out = 2 + ap;
3715 }
3716 else
3717 out = 1 + ap;
3718 if (j >= argc)
3719 {
3720 fprintf(stderr,"No output file after > or >> on command line");
3721 exit(LIB$_WRONUMARG);
3722 }
3723 continue;
3724 }
3725 if (('2' == *ap) && ('>' == ap[1]))
3726 {
3727 if ('>' == ap[2])
3728 {
3729 errmode = "a";
3730 if ('\0' == ap[3])
3731 err = argv[++j];
3732 else
3733 err = 3 + ap;
3734 }
3735 else
3736 if ('\0' == ap[2])
3737 err = argv[++j];
3738 else
3739 err = 2 + ap;
3740 if (j >= argc)
3741 {
3742 fprintf(stderr,"No output file after 2> or 2>> on command line");
3743 exit(LIB$_WRONUMARG);
3744 }
3745 continue;
3746 }
3747 if (0 == strcmp("|", argv[j]))
3748 {
3749 if (j+1 >= argc)
3750 {
3751 fprintf(stderr,"No command into which to pipe on command line");
3752 exit(LIB$_WRONUMARG);
3753 }
3754 cmargc = argc-(j+1);
3755 cmargv = &argv[j+1];
3756 argc = j;
3757 continue;
3758 }
3759 if ('|' == *(ap = argv[j]))
3760 {
3761 ++argv[j];
3762 cmargc = argc-j;
3763 cmargv = &argv[j];
3764 argc = j;
3765 continue;
3766 }
3767 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
3768 }
3769 /*
3770 * Allocate and fill in the new argument vector, Some Unix's terminate
3771 * the list with an extra null pointer.
3772 */
3773 New(1302, argv, item_count+1, char *);
3774 *av = argv;
3775 for (j = 0; j < item_count; ++j, list_head = list_head->next)
3776 argv[j] = list_head->value;
3777 *ac = item_count;
3778 if (cmargv != NULL)
3779 {
3780 if (out != NULL)
3781 {
3782 fprintf(stderr,"'|' and '>' may not both be specified on command line");
3783 exit(LIB$_INVARGORD);
3784 }
3785 pipe_and_fork(aTHX_ cmargv);
3786 }
3787
3788 /* Check for input from a pipe (mailbox) */
3789
3790 if (in == NULL && 1 == isapipe(0))
3791 {
3792 char mbxname[L_tmpnam];
3793 long int bufsize;
3794 long int dvi_item = DVI$_DEVBUFSIZ;
3795 $DESCRIPTOR(mbxnam, "");
3796 $DESCRIPTOR(mbxdevnam, "");
3797
3798 /* Input from a pipe, reopen it in binary mode to disable */
3799 /* carriage control processing. */
3800
3801 fgetname(stdin, mbxname);
3802 mbxnam.dsc$a_pointer = mbxname;
3803 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
3804 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
3805 mbxdevnam.dsc$a_pointer = mbxname;
3806 mbxdevnam.dsc$w_length = sizeof(mbxname);
3807 dvi_item = DVI$_DEVNAM;
3808 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
3809 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
3810 set_errno(0);
3811 set_vaxc_errno(1);
3812 freopen(mbxname, "rb", stdin);
3813 if (errno != 0)
3814 {
3815 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
3816 exit(vaxc$errno);
3817 }
3818 }
3819 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
3820 {
3821 fprintf(stderr,"Can't open input file %s as stdin",in);
3822 exit(vaxc$errno);
3823 }
3824 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
3825 {
3826 fprintf(stderr,"Can't open output file %s as stdout",out);
3827 exit(vaxc$errno);
3828 }
3829 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
3830
3831 if (err != NULL) {
3832 if (strcmp(err,"&1") == 0) {
3833 dup2(fileno(stdout), fileno(stderr));
3834 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
3835 } else {
3836 FILE *tmperr;
3837 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
3838 {
3839 fprintf(stderr,"Can't open error file %s as stderr",err);
3840 exit(vaxc$errno);
3841 }
3842 fclose(tmperr);
3843 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
3844 {
3845 exit(vaxc$errno);
3846 }
3847 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
3848 }
3849 }
3850#ifdef ARGPROC_DEBUG
3851 PerlIO_printf(Perl_debug_log, "Arglist:\n");
3852 for (j = 0; j < *ac; ++j)
3853 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
3854#endif
3855 /* Clear errors we may have hit expanding wildcards, so they don't
3856 show up in Perl's $! later */
3857 set_errno(0); set_vaxc_errno(1);
3858} /* end of getredirection() */
3859/*}}}*/
3860
3861static void add_item(struct list_item **head,
3862 struct list_item **tail,
3863 char *value,
3864 int *count)
3865{
3866 if (*head == 0)
3867 {
3868 New(1303,*head,1,struct list_item);
3869 *tail = *head;
3870 }
3871 else {
3872 New(1304,(*tail)->next,1,struct list_item);
3873 *tail = (*tail)->next;
3874 }
3875 (*tail)->value = value;
3876 ++(*count);
3877}
3878
3879static void mp_expand_wild_cards(pTHX_ char *item,
3880 struct list_item **head,
3881 struct list_item **tail,
3882 int *count)
3883{
3884int expcount = 0;
3885unsigned long int context = 0;
3886int isunix = 0;
3887char *had_version;
3888char *had_device;
3889int had_directory;
3890char *devdir,*cp;
3891char vmsspec[NAM$C_MAXRSS+1];
3892$DESCRIPTOR(filespec, "");
3893$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
3894$DESCRIPTOR(resultspec, "");
3895unsigned long int zero = 0, sts;
3896
3897 for (cp = item; *cp; cp++) {
3898 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
3899 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
3900 }
3901 if (!*cp || isspace(*cp))
3902 {
3903 add_item(head, tail, item, count);
3904 return;
3905 }
3906 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
3907 resultspec.dsc$b_class = DSC$K_CLASS_D;
3908 resultspec.dsc$a_pointer = NULL;
3909 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
3910 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
3911 if (!isunix || !filespec.dsc$a_pointer)
3912 filespec.dsc$a_pointer = item;
3913 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
3914 /*
3915 * Only return version specs, if the caller specified a version
3916 */
3917 had_version = strchr(item, ';');
3918 /*
3919 * Only return device and directory specs, if the caller specifed either.
3920 */
3921 had_device = strchr(item, ':');
3922 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
3923
3924 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
3925 &defaultspec, 0, 0, &zero))))
3926 {
3927 char *string;
3928 char *c;
3929
3930 New(1305,string,resultspec.dsc$w_length+1,char);
3931 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
3932 string[resultspec.dsc$w_length] = '\0';
3933 if (NULL == had_version)
3934 *((char *)strrchr(string, ';')) = '\0';
3935 if ((!had_directory) && (had_device == NULL))
3936 {
3937 if (NULL == (devdir = strrchr(string, ']')))
3938 devdir = strrchr(string, '>');
3939 strcpy(string, devdir + 1);
3940 }
3941 /*
3942 * Be consistent with what the C RTL has already done to the rest of
3943 * the argv items and lowercase all of these names.
3944 */
3945 for (c = string; *c; ++c)
3946 if (isupper(*c))
3947 *c = tolower(*c);
3948 if (isunix) trim_unixpath(string,item,1);
3949 add_item(head, tail, string, count);
3950 ++expcount;
3951 }
3952 if (sts != RMS$_NMF)
3953 {
3954 set_vaxc_errno(sts);
3955 switch (sts)
3956 {
3957 case RMS$_FNF: case RMS$_DNF:
3958 set_errno(ENOENT); break;
3959 case RMS$_DIR:
3960 set_errno(ENOTDIR); break;
3961 case RMS$_DEV:
3962 set_errno(ENODEV); break;
3963 case RMS$_FNM: case RMS$_SYN:
3964 set_errno(EINVAL); break;
3965 case RMS$_PRV:
3966 set_errno(EACCES); break;
3967 default:
3968 _ckvmssts_noperl(sts);
3969 }
3970 }
3971 if (expcount == 0)
3972 add_item(head, tail, item, count);
3973 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
3974 _ckvmssts_noperl(lib$find_file_end(&context));
3975}
3976
3977static int child_st[2];/* Event Flag set when child process completes */
3978
3979static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
3980
3981static unsigned long int exit_handler(int *status)
3982{
3983short iosb[4];
3984
3985 if (0 == child_st[0])
3986 {
3987#ifdef ARGPROC_DEBUG
3988 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
3989#endif
3990 fflush(stdout); /* Have to flush pipe for binary data to */
3991 /* terminate properly -- <tp@mccall.com> */
3992 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
3993 sys$dassgn(child_chan);
3994 fclose(stdout);
3995 sys$synch(0, child_st);
3996 }
3997 return(1);
3998}
3999
4000static void sig_child(int chan)
4001{
4002#ifdef ARGPROC_DEBUG
4003 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
4004#endif
4005 if (child_st[0] == 0)
4006 child_st[0] = 1;
4007}
4008
4009static struct exit_control_block exit_block =
4010 {
4011 0,
4012 exit_handler,
4013 1,
4014 &exit_block.exit_status,
4015 0
4016 };
4017
4018static void pipe_and_fork(pTHX_ char **cmargv)
4019{
4020 char subcmd[2048];
4021 $DESCRIPTOR(cmddsc, "");
4022 static char mbxname[64];
4023 $DESCRIPTOR(mbxdsc, mbxname);
4024 int pid, j;
4025 unsigned long int zero = 0, one = 1;
4026
4027 strcpy(subcmd, cmargv[0]);
4028 for (j = 1; NULL != cmargv[j]; ++j)
4029 {
4030 strcat(subcmd, " \"");
4031 strcat(subcmd, cmargv[j]);
4032 strcat(subcmd, "\"");
4033 }
4034 cmddsc.dsc$a_pointer = subcmd;
4035 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
4036
4037 create_mbx(aTHX_ &child_chan,&mbxdsc);
4038#ifdef ARGPROC_DEBUG
4039 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
4040 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
4041#endif
4042 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
4043 0, &pid, child_st, &zero, sig_child,
4044 &child_chan));
4045#ifdef ARGPROC_DEBUG
4046 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
4047#endif
4048 sys$dclexh(&exit_block);
4049 if (NULL == freopen(mbxname, "wb", stdout))
4050 {
4051 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
4052 }
4053}
4054
4055static int background_process(int argc, char **argv)
4056{
4057char command[2048] = "$";
4058$DESCRIPTOR(value, "");
4059static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
4060static $DESCRIPTOR(null, "NLA0:");
4061static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
4062char pidstring[80];
4063$DESCRIPTOR(pidstr, "");
4064int pid;
4065unsigned long int flags = 17, one = 1, retsts;
4066
4067 strcat(command, argv[0]);
4068 while (--argc)
4069 {
4070 strcat(command, " \"");
4071 strcat(command, *(++argv));
4072 strcat(command, "\"");
4073 }
4074 value.dsc$a_pointer = command;
4075 value.dsc$w_length = strlen(value.dsc$a_pointer);
4076 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
4077 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
4078 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
4079 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
4080 }
4081 else {
4082 _ckvmssts_noperl(retsts);
4083 }
4084#ifdef ARGPROC_DEBUG
4085 PerlIO_printf(Perl_debug_log, "%s\n", command);
4086#endif
4087 sprintf(pidstring, "%08X", pid);
4088 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
4089 pidstr.dsc$a_pointer = pidstring;
4090 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
4091 lib$set_symbol(&pidsymbol, &pidstr);
4092 return(SS$_NORMAL);
4093}
4094/*}}}*/
4095/***** End of code taken from Mark Pizzolato's argproc.c package *****/
4096
4097
4098/* OS-specific initialization at image activation (not thread startup) */
4099/* Older VAXC header files lack these constants */
4100#ifndef JPI$_RIGHTS_SIZE
4101# define JPI$_RIGHTS_SIZE 817
4102#endif
4103#ifndef KGB$M_SUBSYSTEM
4104# define KGB$M_SUBSYSTEM 0x8
4105#endif
4106
4107/*{{{void vms_image_init(int *, char ***)*/
4108void
4109vms_image_init(int *argcp, char ***argvp)
4110{
4111 char eqv[LNM$C_NAMLENGTH+1] = "";
4112 unsigned int len, tabct = 8, tabidx = 0;
4113 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
4114 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
4115 unsigned short int dummy, rlen;
4116 struct dsc$descriptor_s **tabvec;
4117#if defined(PERL_IMPLICIT_CONTEXT)
4118 pTHX = NULL;
4119#endif
4120 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
4121 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
4122 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
4123 { 0, 0, 0, 0} };
4124
4125 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
4126 _ckvmssts_noperl(iosb[0]);
4127 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
4128 if (iprv[i]) { /* Running image installed with privs? */
4129 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
4130 will_taint = TRUE;
4131 break;
4132 }
4133 }
4134 /* Rights identifiers might trigger tainting as well. */
4135 if (!will_taint && (rlen || rsz)) {
4136 while (rlen < rsz) {
4137 /* We didn't get all the identifiers on the first pass. Allocate a
4138 * buffer much larger than $GETJPI wants (rsz is size in bytes that
4139 * were needed to hold all identifiers at time of last call; we'll
4140 * allocate that many unsigned long ints), and go back and get 'em.
4141 * If it gave us less than it wanted to despite ample buffer space,
4142 * something's broken. Is your system missing a system identifier?
4143 */
4144 if (rsz <= jpilist[1].buflen) {
4145 /* Perl_croak accvios when used this early in startup. */
4146 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
4147 rsz, (unsigned long) jpilist[1].buflen,
4148 "Check your rights database for corruption.\n");
4149 exit(SS$_ABORT);
4150 }
4151 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
4152 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
4153 jpilist[1].buflen = rsz * sizeof(unsigned long int);
4154 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
4155 _ckvmssts_noperl(iosb[0]);
4156 }
4157 mask = jpilist[1].bufadr;
4158 /* Check attribute flags for each identifier (2nd longword); protected
4159 * subsystem identifiers trigger tainting.
4160 */
4161 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
4162 if (mask[i] & KGB$M_SUBSYSTEM) {
4163 will_taint = TRUE;
4164 break;
4165 }
4166 }
4167 if (mask != rlst) Safefree(mask);
4168 }
4169 /* We need to use this hack to tell Perl it should run with tainting,
4170 * since its tainting flag may be part of the PL_curinterp struct, which
4171 * hasn't been allocated when vms_image_init() is called.
4172 */
4173 if (will_taint) {
4174 char ***newap;
4175 New(1320,newap,*argcp+2,char **);
4176 newap[0] = argvp[0];
4177 *newap[1] = "-T";
4178 Copy(argvp[1],newap[2],*argcp-1,char **);
4179 /* We orphan the old argv, since we don't know where it's come from,
4180 * so we don't know how to free it.
4181 */
4182 *argcp++; argvp = newap;
4183 }
4184 else { /* Did user explicitly request tainting? */
4185 int i;
4186 char *cp, **av = *argvp;
4187 for (i = 1; i < *argcp; i++) {
4188 if (*av[i] != '-') break;
4189 for (cp = av[i]+1; *cp; cp++) {
4190 if (*cp == 'T') { will_taint = 1; break; }
4191 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
4192 strchr("DFIiMmx",*cp)) break;
4193 }
4194 if (will_taint) break;
4195 }
4196 }
4197
4198 for (tabidx = 0;
4199 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
4200 tabidx++) {
4201 if (!tabidx) New(1321,tabvec,tabct,struct dsc$descriptor_s *);
4202 else if (tabidx >= tabct) {
4203 tabct += 8;
4204 Renew(tabvec,tabct,struct dsc$descriptor_s *);
4205 }
4206 New(1322,tabvec[tabidx],1,struct dsc$descriptor_s);
4207 tabvec[tabidx]->dsc$w_length = 0;
4208 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
4209 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
4210 tabvec[tabidx]->dsc$a_pointer = NULL;
4211 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
4212 }
4213 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
4214
4215 getredirection(argcp,argvp);
4216#if defined(USE_5005THREADS) && ( defined(__DECC) || defined(__DECCXX) )
4217 {
4218# include <reentrancy.h>
4219 (void) decc$set_reentrancy(C$C_MULTITHREAD);
4220 }
4221#endif
4222 return;
4223}
4224/*}}}*/
4225
4226
4227/* trim_unixpath()
4228 * Trim Unix-style prefix off filespec, so it looks like what a shell
4229 * glob expansion would return (i.e. from specified prefix on, not
4230 * full path). Note that returned filespec is Unix-style, regardless
4231 * of whether input filespec was VMS-style or Unix-style.
4232 *
4233 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
4234 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
4235 * vector of options; at present, only bit 0 is used, and if set tells
4236 * trim unixpath to try the current default directory as a prefix when
4237 * presented with a possibly ambiguous ... wildcard.
4238 *
4239 * Returns !=0 on success, with trimmed filespec replacing contents of
4240 * fspec, and 0 on failure, with contents of fpsec unchanged.
4241 */
4242/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
4243int
4244Perl_trim_unixpath(pTHX_ char *fspec, char *wildspec, int opts)
4245{
4246 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
4247 *template, *base, *end, *cp1, *cp2;
4248 register int tmplen, reslen = 0, dirs = 0;
4249
4250 if (!wildspec || !fspec) return 0;
4251 if (strpbrk(wildspec,"]>:") != NULL) {
4252 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
4253 else template = unixwild;
4254 }
4255 else template = wildspec;
4256 if (strpbrk(fspec,"]>:") != NULL) {
4257 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
4258 else base = unixified;
4259 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
4260 * check to see that final result fits into (isn't longer than) fspec */
4261 reslen = strlen(fspec);
4262 }
4263 else base = fspec;
4264
4265 /* No prefix or absolute path on wildcard, so nothing to remove */
4266 if (!*template || *template == '/') {
4267 if (base == fspec) return 1;
4268 tmplen = strlen(unixified);
4269 if (tmplen > reslen) return 0; /* not enough space */
4270 /* Copy unixified resultant, including trailing NUL */
4271 memmove(fspec,unixified,tmplen+1);
4272 return 1;
4273 }
4274
4275 for (end = base; *end; end++) ; /* Find end of resultant filespec */
4276 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
4277 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
4278 for (cp1 = end ;cp1 >= base; cp1--)
4279 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
4280 { cp1++; break; }
4281 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
4282 return 1;
4283 }
4284 else {
4285 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
4286 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
4287 int ells = 1, totells, segdirs, match;
4288 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
4289 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4290
4291 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
4292 totells = ells;
4293 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
4294 if (ellipsis == template && opts & 1) {
4295 /* Template begins with an ellipsis. Since we can't tell how many
4296 * directory names at the front of the resultant to keep for an
4297 * arbitrary starting point, we arbitrarily choose the current
4298 * default directory as a starting point. If it's there as a prefix,
4299 * clip it off. If not, fall through and act as if the leading
4300 * ellipsis weren't there (i.e. return shortest possible path that
4301 * could match template).
4302 */
4303 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
4304 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4305 if (_tolower(*cp1) != _tolower(*cp2)) break;
4306 segdirs = dirs - totells; /* Min # of dirs we must have left */
4307 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
4308 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
4309 memcpy(fspec,cp2+1,end - cp2);
4310 return 1;
4311 }
4312 }
4313 /* First off, back up over constant elements at end of path */
4314 if (dirs) {
4315 for (front = end ; front >= base; front--)
4316 if (*front == '/' && !dirs--) { front++; break; }
4317 }
4318 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
4319 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
4320 if (cp1 != '\0') return 0; /* Path too long. */
4321 lcend = cp2;
4322 *cp2 = '\0'; /* Pick up with memcpy later */
4323 lcfront = lcres + (front - base);
4324 /* Now skip over each ellipsis and try to match the path in front of it. */
4325 while (ells--) {
4326 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
4327 if (*(cp1) == '.' && *(cp1+1) == '.' &&
4328 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
4329 if (cp1 < template) break; /* template started with an ellipsis */
4330 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
4331 ellipsis = cp1; continue;
4332 }
4333 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
4334 nextell = cp1;
4335 for (segdirs = 0, cp2 = tpl;
4336 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
4337 cp1++, cp2++) {
4338 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
4339 else *cp2 = _tolower(*cp1); /* else lowercase for match */
4340 if (*cp2 == '/') segdirs++;
4341 }
4342 if (cp1 != ellipsis - 1) return 0; /* Path too long */
4343 /* Back up at least as many dirs as in template before matching */
4344 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
4345 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
4346 for (match = 0; cp1 > lcres;) {
4347 resdsc.dsc$a_pointer = cp1;
4348 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
4349 match++;
4350 if (match == 1) lcfront = cp1;
4351 }
4352 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
4353 }
4354 if (!match) return 0; /* Can't find prefix ??? */
4355 if (match > 1 && opts & 1) {
4356 /* This ... wildcard could cover more than one set of dirs (i.e.
4357 * a set of similar dir names is repeated). If the template
4358 * contains more than 1 ..., upstream elements could resolve the
4359 * ambiguity, but it's not worth a full backtracking setup here.
4360 * As a quick heuristic, clip off the current default directory
4361 * if it's present to find the trimmed spec, else use the
4362 * shortest string that this ... could cover.
4363 */
4364 char def[NAM$C_MAXRSS+1], *st;
4365
4366 if (getcwd(def, sizeof def,0) == NULL) return 0;
4367 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4368 if (_tolower(*cp1) != _tolower(*cp2)) break;
4369 segdirs = dirs - totells; /* Min # of dirs we must have left */
4370 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
4371 if (*cp1 == '\0' && *cp2 == '/') {
4372 memcpy(fspec,cp2+1,end - cp2);
4373 return 1;
4374 }
4375 /* Nope -- stick with lcfront from above and keep going. */
4376 }
4377 }
4378 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
4379 return 1;
4380 ellipsis = nextell;
4381 }
4382
4383} /* end of trim_unixpath() */
4384/*}}}*/
4385
4386
4387/*
4388 * VMS readdir() routines.
4389 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
4390 *
4391 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
4392 * Minor modifications to original routines.
4393 */
4394
4395 /* Number of elements in vms_versions array */
4396#define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
4397
4398/*
4399 * Open a directory, return a handle for later use.
4400 */
4401/*{{{ DIR *opendir(char*name) */
4402DIR *
4403Perl_opendir(pTHX_ char *name)
4404{
4405 DIR *dd;
4406 char dir[NAM$C_MAXRSS+1];
4407 Stat_t sb;
4408
4409 if (do_tovmspath(name,dir,0) == NULL) {
4410 return NULL;
4411 }
4412 if (flex_stat(dir,&sb) == -1) return NULL;
4413 if (!S_ISDIR(sb.st_mode)) {
4414 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
4415 return NULL;
4416 }
4417 if (!cando_by_name(S_IRUSR,0,dir)) {
4418 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
4419 return NULL;
4420 }
4421 /* Get memory for the handle, and the pattern. */
4422 New(1306,dd,1,DIR);
4423 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
4424
4425 /* Fill in the fields; mainly playing with the descriptor. */
4426 (void)sprintf(dd->pattern, "%s*.*",dir);
4427 dd->context = 0;
4428 dd->count = 0;
4429 dd->vms_wantversions = 0;
4430 dd->pat.dsc$a_pointer = dd->pattern;
4431 dd->pat.dsc$w_length = strlen(dd->pattern);
4432 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
4433 dd->pat.dsc$b_class = DSC$K_CLASS_S;
4434
4435 return dd;
4436} /* end of opendir() */
4437/*}}}*/
4438
4439/*
4440 * Set the flag to indicate we want versions or not.
4441 */
4442/*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
4443void
4444vmsreaddirversions(DIR *dd, int flag)
4445{
4446 dd->vms_wantversions = flag;
4447}
4448/*}}}*/
4449
4450/*
4451 * Free up an opened directory.
4452 */
4453/*{{{ void closedir(DIR *dd)*/
4454void
4455closedir(DIR *dd)
4456{
4457 (void)lib$find_file_end(&dd->context);
4458 Safefree(dd->pattern);
4459 Safefree((char *)dd);
4460}
4461/*}}}*/
4462
4463/*
4464 * Collect all the version numbers for the current file.
4465 */
4466static void
4467collectversions(pTHX_ DIR *dd)
4468{
4469 struct dsc$descriptor_s pat;
4470 struct dsc$descriptor_s res;
4471 struct dirent *e;
4472 char *p, *text, buff[sizeof dd->entry.d_name];
4473 int i;
4474 unsigned long context, tmpsts;
4475
4476 /* Convenient shorthand. */
4477 e = &dd->entry;
4478
4479 /* Add the version wildcard, ignoring the "*.*" put on before */
4480 i = strlen(dd->pattern);
4481 New(1308,text,i + e->d_namlen + 3,char);
4482 (void)strcpy(text, dd->pattern);
4483 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
4484
4485 /* Set up the pattern descriptor. */
4486 pat.dsc$a_pointer = text;
4487 pat.dsc$w_length = i + e->d_namlen - 1;
4488 pat.dsc$b_dtype = DSC$K_DTYPE_T;
4489 pat.dsc$b_class = DSC$K_CLASS_S;
4490
4491 /* Set up result descriptor. */
4492 res.dsc$a_pointer = buff;
4493 res.dsc$w_length = sizeof buff - 2;
4494 res.dsc$b_dtype = DSC$K_DTYPE_T;
4495 res.dsc$b_class = DSC$K_CLASS_S;
4496
4497 /* Read files, collecting versions. */
4498 for (context = 0, e->vms_verscount = 0;
4499 e->vms_verscount < VERSIZE(e);
4500 e->vms_verscount++) {
4501 tmpsts = lib$find_file(&pat, &res, &context);
4502 if (tmpsts == RMS$_NMF || context == 0) break;
4503 _ckvmssts(tmpsts);
4504 buff[sizeof buff - 1] = '\0';
4505 if ((p = strchr(buff, ';')))
4506 e->vms_versions[e->vms_verscount] = atoi(p + 1);
4507 else
4508 e->vms_versions[e->vms_verscount] = -1;
4509 }
4510
4511 _ckvmssts(lib$find_file_end(&context));
4512 Safefree(text);
4513
4514} /* end of collectversions() */
4515
4516/*
4517 * Read the next entry from the directory.
4518 */
4519/*{{{ struct dirent *readdir(DIR *dd)*/
4520struct dirent *
4521Perl_readdir(pTHX_ DIR *dd)
4522{
4523 struct dsc$descriptor_s res;
4524 char *p, buff[sizeof dd->entry.d_name];
4525 unsigned long int tmpsts;
4526
4527 /* Set up result descriptor, and get next file. */
4528 res.dsc$a_pointer = buff;
4529 res.dsc$w_length = sizeof buff - 2;
4530 res.dsc$b_dtype = DSC$K_DTYPE_T;
4531 res.dsc$b_class = DSC$K_CLASS_S;
4532 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4533 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
4534 if (!(tmpsts & 1)) {
4535 set_vaxc_errno(tmpsts);
4536 switch (tmpsts) {
4537 case RMS$_PRV:
4538 set_errno(EACCES); break;
4539 case RMS$_DEV:
4540 set_errno(ENODEV); break;
4541 case RMS$_DIR:
4542 set_errno(ENOTDIR); break;
4543 case RMS$_FNF: case RMS$_DNF:
4544 set_errno(ENOENT); break;
4545 default:
4546 set_errno(EVMSERR);
4547 }
4548 return NULL;
4549 }
4550 dd->count++;
4551 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
4552 buff[sizeof buff - 1] = '\0';
4553 for (p = buff; *p; p++) *p = _tolower(*p);
4554 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
4555 *p = '\0';
4556
4557 /* Skip any directory component and just copy the name. */
4558 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
4559 else (void)strcpy(dd->entry.d_name, buff);
4560
4561 /* Clobber the version. */
4562 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
4563
4564 dd->entry.d_namlen = strlen(dd->entry.d_name);
4565 dd->entry.vms_verscount = 0;
4566 if (dd->vms_wantversions) collectversions(aTHX_ dd);
4567 return &dd->entry;
4568
4569} /* end of readdir() */
4570/*}}}*/
4571
4572/*
4573 * Return something that can be used in a seekdir later.
4574 */
4575/*{{{ long telldir(DIR *dd)*/
4576long
4577telldir(DIR *dd)
4578{
4579 return dd->count;
4580}
4581/*}}}*/
4582
4583/*
4584 * Return to a spot where we used to be. Brute force.
4585 */
4586/*{{{ void seekdir(DIR *dd,long count)*/
4587void
4588Perl_seekdir(pTHX_ DIR *dd, long count)
4589{
4590 int vms_wantversions;
4591
4592 /* If we haven't done anything yet... */
4593 if (dd->count == 0)
4594 return;
4595
4596 /* Remember some state, and clear it. */
4597 vms_wantversions = dd->vms_wantversions;
4598 dd->vms_wantversions = 0;
4599 _ckvmssts(lib$find_file_end(&dd->context));
4600 dd->context = 0;
4601
4602 /* The increment is in readdir(). */
4603 for (dd->count = 0; dd->count < count; )
4604 (void)readdir(dd);
4605
4606 dd->vms_wantversions = vms_wantversions;
4607
4608} /* end of seekdir() */
4609/*}}}*/
4610
4611/* VMS subprocess management
4612 *
4613 * my_vfork() - just a vfork(), after setting a flag to record that
4614 * the current script is trying a Unix-style fork/exec.
4615 *
4616 * vms_do_aexec() and vms_do_exec() are called in response to the
4617 * perl 'exec' function. If this follows a vfork call, then they
4618 * call out the the regular perl routines in doio.c which do an
4619 * execvp (for those who really want to try this under VMS).
4620 * Otherwise, they do exactly what the perl docs say exec should
4621 * do - terminate the current script and invoke a new command
4622 * (See below for notes on command syntax.)
4623 *
4624 * do_aspawn() and do_spawn() implement the VMS side of the perl
4625 * 'system' function.
4626 *
4627 * Note on command arguments to perl 'exec' and 'system': When handled
4628 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
4629 * are concatenated to form a DCL command string. If the first arg
4630 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
4631 * the the command string is handed off to DCL directly. Otherwise,
4632 * the first token of the command is taken as the filespec of an image
4633 * to run. The filespec is expanded using a default type of '.EXE' and
4634 * the process defaults for device, directory, etc., and if found, the resultant
4635 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
4636 * the command string as parameters. This is perhaps a bit complicated,
4637 * but I hope it will form a happy medium between what VMS folks expect
4638 * from lib$spawn and what Unix folks expect from exec.
4639 */
4640
4641static int vfork_called;
4642
4643/*{{{int my_vfork()*/
4644int
4645my_vfork()
4646{
4647 vfork_called++;
4648 return vfork();
4649}
4650/*}}}*/
4651
4652
4653static void
4654vms_execfree(pTHX) {
4655 if (PL_Cmd) {
4656 if (PL_Cmd != VMScmd.dsc$a_pointer) Safefree(PL_Cmd);
4657 PL_Cmd = Nullch;
4658 }
4659 if (VMScmd.dsc$a_pointer) {
4660 Safefree(VMScmd.dsc$a_pointer);
4661 VMScmd.dsc$w_length = 0;
4662 VMScmd.dsc$a_pointer = Nullch;
4663 }
4664}
4665
4666static char *
4667setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
4668{
4669 char *junk, *tmps = Nullch;
4670 register size_t cmdlen = 0;
4671 size_t rlen;
4672 register SV **idx;
4673 STRLEN n_a;
4674
4675 idx = mark;
4676 if (really) {
4677 tmps = SvPV(really,rlen);
4678 if (*tmps) {
4679 cmdlen += rlen + 1;
4680 idx++;
4681 }
4682 }
4683
4684 for (idx++; idx <= sp; idx++) {
4685 if (*idx) {
4686 junk = SvPVx(*idx,rlen);
4687 cmdlen += rlen ? rlen + 1 : 0;
4688 }
4689 }
4690 New(401,PL_Cmd,cmdlen+1,char);
4691
4692 if (tmps && *tmps) {
4693 strcpy(PL_Cmd,tmps);
4694 mark++;
4695 }
4696 else *PL_Cmd = '\0';
4697 while (++mark <= sp) {
4698 if (*mark) {
4699 char *s = SvPVx(*mark,n_a);
4700 if (!*s) continue;
4701 if (*PL_Cmd) strcat(PL_Cmd," ");
4702 strcat(PL_Cmd,s);
4703 }
4704 }
4705 return PL_Cmd;
4706
4707} /* end of setup_argstr() */
4708
4709#define MAX_DCL_LINE_LENGTH 255
4710
4711static unsigned long int
4712setup_cmddsc(pTHX_ char *cmd, int check_img)
4713{
4714 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
4715 $DESCRIPTOR(defdsc,".EXE");
4716 $DESCRIPTOR(defdsc2,".");
4717 $DESCRIPTOR(resdsc,resspec);
4718 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4719 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
4720 register char *s, *rest, *cp, *wordbreak;
4721 register int isdcl;
4722
4723 if (strlen(cmd) > MAX_DCL_LINE_LENGTH)
4724 return CLI$_BUFOVF; /* continuation lines currently unsupported */
4725 s = cmd;
4726 while (*s && isspace(*s)) s++;
4727
4728 if (*s == '@' || *s == '$') {
4729 vmsspec[0] = *s; rest = s + 1;
4730 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
4731 }
4732 else { cp = vmsspec; rest = s; }
4733 if (*rest == '.' || *rest == '/') {
4734 char *cp2;
4735 for (cp2 = resspec;
4736 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
4737 rest++, cp2++) *cp2 = *rest;
4738 *cp2 = '\0';
4739 if (do_tovmsspec(resspec,cp,0)) {
4740 s = vmsspec;
4741 if (*rest) {
4742 for (cp2 = vmsspec + strlen(vmsspec);
4743 *rest && cp2 - vmsspec < sizeof vmsspec;
4744 rest++, cp2++) *cp2 = *rest;
4745 *cp2 = '\0';
4746 }
4747 }
4748 }
4749 /* Intuit whether verb (first word of cmd) is a DCL command:
4750 * - if first nonspace char is '@', it's a DCL indirection
4751 * otherwise
4752 * - if verb contains a filespec separator, it's not a DCL command
4753 * - if it doesn't, caller tells us whether to default to a DCL
4754 * command, or to a local image unless told it's DCL (by leading '$')
4755 */
4756 if (*s == '@') isdcl = 1;
4757 else {
4758 register char *filespec = strpbrk(s,":<[.;");
4759 rest = wordbreak = strpbrk(s," \"\t/");
4760 if (!wordbreak) wordbreak = s + strlen(s);
4761 if (*s == '$') check_img = 0;
4762 if (filespec && (filespec < wordbreak)) isdcl = 0;
4763 else isdcl = !check_img;
4764 }
4765
4766 if (!isdcl) {
4767 imgdsc.dsc$a_pointer = s;
4768 imgdsc.dsc$w_length = wordbreak - s;
4769 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4770 if (!(retsts&1)) {
4771 _ckvmssts(lib$find_file_end(&cxt));
4772 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
4773 if (!(retsts & 1) && *s == '$') {
4774 _ckvmssts(lib$find_file_end(&cxt));
4775 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
4776 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4777 if (!(retsts&1)) {
4778 _ckvmssts(lib$find_file_end(&cxt));
4779 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
4780 }
4781 }
4782 }
4783 _ckvmssts(lib$find_file_end(&cxt));
4784
4785 if (retsts & 1) {
4786 FILE *fp;
4787 s = resspec;
4788 while (*s && !isspace(*s)) s++;
4789 *s = '\0';
4790
4791 /* check that it's really not DCL with no file extension */
4792 fp = fopen(resspec,"r","ctx=bin,shr=get");
4793 if (fp) {
4794 char b[4] = {0,0,0,0};
4795 read(fileno(fp),b,4);
4796 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
4797 fclose(fp);
4798 }
4799 if (check_img && isdcl) return RMS$_FNF;
4800
4801 if (cando_by_name(S_IXUSR,0,resspec)) {
4802 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
4803 if (!isdcl) {
4804 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
4805 } else {
4806 strcpy(VMScmd.dsc$a_pointer,"@");
4807 }
4808 strcat(VMScmd.dsc$a_pointer,resspec);
4809 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
4810 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
4811 return (VMScmd.dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
4812 }
4813 else retsts = RMS$_PRV;
4814 }
4815 }
4816 /* It's either a DCL command or we couldn't find a suitable image */
4817 VMScmd.dsc$w_length = strlen(cmd);
4818 if (cmd == PL_Cmd) VMScmd.dsc$a_pointer = PL_Cmd;
4819 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
4820 if (!(retsts & 1)) {
4821 /* just hand off status values likely to be due to user error */
4822 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
4823 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
4824 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
4825 else { _ckvmssts(retsts); }
4826 }
4827
4828 return (VMScmd.dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
4829
4830} /* end of setup_cmddsc() */
4831
4832
4833/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
4834bool
4835Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
4836{
4837 if (sp > mark) {
4838 if (vfork_called) { /* this follows a vfork - act Unixish */
4839 vfork_called--;
4840 if (vfork_called < 0) {
4841 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
4842 vfork_called = 0;
4843 }
4844 else return do_aexec(really,mark,sp);
4845 }
4846 /* no vfork - act VMSish */
4847 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
4848
4849 }
4850
4851 return FALSE;
4852} /* end of vms_do_aexec() */
4853/*}}}*/
4854
4855/* {{{bool vms_do_exec(char *cmd) */
4856bool
4857Perl_vms_do_exec(pTHX_ char *cmd)
4858{
4859
4860 if (vfork_called) { /* this follows a vfork - act Unixish */
4861 vfork_called--;
4862 if (vfork_called < 0) {
4863 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
4864 vfork_called = 0;
4865 }
4866 else return do_exec(cmd);
4867 }
4868
4869 { /* no vfork - act VMSish */
4870 unsigned long int retsts;
4871
4872 TAINT_ENV();
4873 TAINT_PROPER("exec");
4874 if ((retsts = setup_cmddsc(aTHX_ cmd,1)) & 1)
4875 retsts = lib$do_command(&VMScmd);
4876
4877 switch (retsts) {
4878 case RMS$_FNF: case RMS$_DNF:
4879 set_errno(ENOENT); break;
4880 case RMS$_DIR:
4881 set_errno(ENOTDIR); break;
4882 case RMS$_DEV:
4883 set_errno(ENODEV); break;
4884 case RMS$_PRV:
4885 set_errno(EACCES); break;
4886 case RMS$_SYN:
4887 set_errno(EINVAL); break;
4888 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4889 set_errno(E2BIG); break;
4890 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4891 _ckvmssts(retsts); /* fall through */
4892 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4893 set_errno(EVMSERR);
4894 }
4895 set_vaxc_errno(retsts);
4896 if (ckWARN(WARN_EXEC)) {
4897 Perl_warner(aTHX_ WARN_EXEC,"Can't exec \"%*s\": %s",
4898 VMScmd.dsc$w_length, VMScmd.dsc$a_pointer, Strerror(errno));
4899 }
4900 vms_execfree(aTHX);
4901 }
4902
4903 return FALSE;
4904
4905} /* end of vms_do_exec() */
4906/*}}}*/
4907
4908unsigned long int Perl_do_spawn(pTHX_ char *);
4909
4910/* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
4911unsigned long int
4912Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
4913{
4914 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
4915
4916 return SS$_ABORT;
4917} /* end of do_aspawn() */
4918/*}}}*/
4919
4920/* {{{unsigned long int do_spawn(char *cmd) */
4921unsigned long int
4922Perl_do_spawn(pTHX_ char *cmd)
4923{
4924 unsigned long int sts, substs, hadcmd = 1;
4925
4926 TAINT_ENV();
4927 TAINT_PROPER("spawn");
4928 if (!cmd || !*cmd) {
4929 hadcmd = 0;
4930 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
4931 }
4932 else {
4933 sts = setup_cmddsc(aTHX_ cmd,0);
4934 if (sts & 1) {
4935 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
4936 } else {
4937 substs = sts; /* didn't spawn, use command setup failure for return */
4938 }
4939 }
4940
4941 if (!(sts & 1)) {
4942 switch (sts) {
4943 case RMS$_FNF: case RMS$_DNF:
4944 set_errno(ENOENT); break;
4945 case RMS$_DIR:
4946 set_errno(ENOTDIR); break;
4947 case RMS$_DEV:
4948 set_errno(ENODEV); break;
4949 case RMS$_PRV:
4950 set_errno(EACCES); break;
4951 case RMS$_SYN:
4952 set_errno(EINVAL); break;
4953 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4954 set_errno(E2BIG); break;
4955 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4956 _ckvmssts(sts); /* fall through */
4957 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4958 set_errno(EVMSERR);
4959 }
4960 set_vaxc_errno(sts);
4961 if (ckWARN(WARN_EXEC)) {
4962 Perl_warner(aTHX_ WARN_EXEC,"Can't spawn \"%*s\": %s",
4963 hadcmd ? VMScmd.dsc$w_length : 0,
4964 hadcmd ? VMScmd.dsc$a_pointer : "",
4965 Strerror(errno));
4966 }
4967 }
4968 vms_execfree(aTHX);
4969 return substs;
4970
4971} /* end of do_spawn() */
4972/*}}}*/
4973
4974
4975static unsigned int *sockflags, sockflagsize;
4976
4977/*
4978 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
4979 * routines found in some versions of the CRTL can't deal with sockets.
4980 * We don't shim the other file open routines since a socket isn't
4981 * likely to be opened by a name.
4982 */
4983/*{{{ FILE *my_fdopen(int fd, const char *mode)*/
4984FILE *my_fdopen(int fd, const char *mode)
4985{
4986 FILE *fp = fdopen(fd, (char *) mode);
4987
4988 if (fp) {
4989 unsigned int fdoff = fd / sizeof(unsigned int);
4990 struct stat sbuf; /* native stat; we don't need flex_stat */
4991 if (!sockflagsize || fdoff > sockflagsize) {
4992 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
4993 else New (1324,sockflags,fdoff+2,unsigned int);
4994 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
4995 sockflagsize = fdoff + 2;
4996 }
4997 if (fstat(fd,&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
4998 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
4999 }
5000 return fp;
5001
5002}
5003/*}}}*/
5004
5005
5006/*
5007 * Clear the corresponding bit when the (possibly) socket stream is closed.
5008 * There still a small hole: we miss an implicit close which might occur
5009 * via freopen(). >> Todo
5010 */
5011/*{{{ int my_fclose(FILE *fp)*/
5012int my_fclose(FILE *fp) {
5013 if (fp) {
5014 unsigned int fd = fileno(fp);
5015 unsigned int fdoff = fd / sizeof(unsigned int);
5016
5017 if (sockflagsize && fdoff <= sockflagsize)
5018 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
5019 }
5020 return fclose(fp);
5021}
5022/*}}}*/
5023
5024
5025/*
5026 * A simple fwrite replacement which outputs itmsz*nitm chars without
5027 * introducing record boundaries every itmsz chars.
5028 * We are using fputs, which depends on a terminating null. We may
5029 * well be writing binary data, so we need to accommodate not only
5030 * data with nulls sprinkled in the middle but also data with no null
5031 * byte at the end.
5032 */
5033/*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
5034int
5035my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
5036{
5037 register char *cp, *end, *cpd, *data;
5038 register unsigned int fd = fileno(dest);
5039 register unsigned int fdoff = fd / sizeof(unsigned int);
5040 int retval;
5041 int bufsize = itmsz * nitm + 1;
5042
5043 if (fdoff < sockflagsize &&
5044 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
5045 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
5046 return nitm;
5047 }
5048
5049 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
5050 memcpy( data, src, itmsz*nitm );
5051 data[itmsz*nitm] = '\0';
5052
5053 end = data + itmsz * nitm;
5054 retval = (int) nitm; /* on success return # items written */
5055
5056 cpd = data;
5057 while (cpd <= end) {
5058 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
5059 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
5060 if (cp < end)
5061 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
5062 cpd = cp + 1;
5063 }
5064
5065 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
5066 return retval;
5067
5068} /* end of my_fwrite() */
5069/*}}}*/
5070
5071/*{{{ int my_flush(FILE *fp)*/
5072int
5073Perl_my_flush(pTHX_ FILE *fp)
5074{
5075 int res;
5076 if ((res = fflush(fp)) == 0 && fp) {
5077#ifdef VMS_DO_SOCKETS
5078 Stat_t s;
5079 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
5080#endif
5081 res = fsync(fileno(fp));
5082 }
5083/*
5084 * If the flush succeeded but set end-of-file, we need to clear
5085 * the error because our caller may check ferror(). BTW, this
5086 * probably means we just flushed an empty file.
5087 */
5088 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
5089
5090 return res;
5091}
5092/*}}}*/
5093
5094/*
5095 * Here are replacements for the following Unix routines in the VMS environment:
5096 * getpwuid Get information for a particular UIC or UID
5097 * getpwnam Get information for a named user
5098 * getpwent Get information for each user in the rights database
5099 * setpwent Reset search to the start of the rights database
5100 * endpwent Finish searching for users in the rights database
5101 *
5102 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
5103 * (defined in pwd.h), which contains the following fields:-
5104 * struct passwd {
5105 * char *pw_name; Username (in lower case)
5106 * char *pw_passwd; Hashed password
5107 * unsigned int pw_uid; UIC
5108 * unsigned int pw_gid; UIC group number
5109 * char *pw_unixdir; Default device/directory (VMS-style)
5110 * char *pw_gecos; Owner name
5111 * char *pw_dir; Default device/directory (Unix-style)
5112 * char *pw_shell; Default CLI name (eg. DCL)
5113 * };
5114 * If the specified user does not exist, getpwuid and getpwnam return NULL.
5115 *
5116 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
5117 * not the UIC member number (eg. what's returned by getuid()),
5118 * getpwuid() can accept either as input (if uid is specified, the caller's
5119 * UIC group is used), though it won't recognise gid=0.
5120 *
5121 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
5122 * information about other users in your group or in other groups, respectively.
5123 * If the required privilege is not available, then these routines fill only
5124 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
5125 * string).
5126 *
5127 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
5128 */
5129
5130/* sizes of various UAF record fields */
5131#define UAI$S_USERNAME 12
5132#define UAI$S_IDENT 31
5133#define UAI$S_OWNER 31
5134#define UAI$S_DEFDEV 31
5135#define UAI$S_DEFDIR 63
5136#define UAI$S_DEFCLI 31
5137#define UAI$S_PWD 8
5138
5139#define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
5140 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
5141 (uic).uic$v_group != UIC$K_WILD_GROUP)
5142
5143static char __empty[]= "";
5144static struct passwd __passwd_empty=
5145 {(char *) __empty, (char *) __empty, 0, 0,
5146 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
5147static int contxt= 0;
5148static struct passwd __pwdcache;
5149static char __pw_namecache[UAI$S_IDENT+1];
5150
5151/*
5152 * This routine does most of the work extracting the user information.
5153 */
5154static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
5155{
5156 static struct {
5157 unsigned char length;
5158 char pw_gecos[UAI$S_OWNER+1];
5159 } owner;
5160 static union uicdef uic;
5161 static struct {
5162 unsigned char length;
5163 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
5164 } defdev;
5165 static struct {
5166 unsigned char length;
5167 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
5168 } defdir;
5169 static struct {
5170 unsigned char length;
5171 char pw_shell[UAI$S_DEFCLI+1];
5172 } defcli;
5173 static char pw_passwd[UAI$S_PWD+1];
5174
5175 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
5176 struct dsc$descriptor_s name_desc;
5177 unsigned long int sts;
5178
5179 static struct itmlst_3 itmlst[]= {
5180 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
5181 {sizeof(uic), UAI$_UIC, &uic, &luic},
5182 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
5183 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
5184 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
5185 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
5186 {0, 0, NULL, NULL}};
5187
5188 name_desc.dsc$w_length= strlen(name);
5189 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5190 name_desc.dsc$b_class= DSC$K_CLASS_S;
5191 name_desc.dsc$a_pointer= (char *) name;
5192
5193/* Note that sys$getuai returns many fields as counted strings. */
5194 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
5195 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
5196 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
5197 }
5198 else { _ckvmssts(sts); }
5199 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
5200
5201 if ((int) owner.length < lowner) lowner= (int) owner.length;
5202 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
5203 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
5204 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
5205 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
5206 owner.pw_gecos[lowner]= '\0';
5207 defdev.pw_dir[ldefdev+ldefdir]= '\0';
5208 defcli.pw_shell[ldefcli]= '\0';
5209 if (valid_uic(uic)) {
5210 pwd->pw_uid= uic.uic$l_uic;
5211 pwd->pw_gid= uic.uic$v_group;
5212 }
5213 else
5214 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
5215 pwd->pw_passwd= pw_passwd;
5216 pwd->pw_gecos= owner.pw_gecos;
5217 pwd->pw_dir= defdev.pw_dir;
5218 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
5219 pwd->pw_shell= defcli.pw_shell;
5220 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
5221 int ldir;
5222 ldir= strlen(pwd->pw_unixdir) - 1;
5223 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
5224 }
5225 else
5226 strcpy(pwd->pw_unixdir, pwd->pw_dir);
5227 __mystrtolower(pwd->pw_unixdir);
5228 return 1;
5229}
5230
5231/*
5232 * Get information for a named user.
5233*/
5234/*{{{struct passwd *getpwnam(char *name)*/
5235struct passwd *Perl_my_getpwnam(pTHX_ char *name)
5236{
5237 struct dsc$descriptor_s name_desc;
5238 union uicdef uic;
5239 unsigned long int status, sts;
5240
5241 __pwdcache = __passwd_empty;
5242 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
5243 /* We still may be able to determine pw_uid and pw_gid */
5244 name_desc.dsc$w_length= strlen(name);
5245 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5246 name_desc.dsc$b_class= DSC$K_CLASS_S;
5247 name_desc.dsc$a_pointer= (char *) name;
5248 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
5249 __pwdcache.pw_uid= uic.uic$l_uic;
5250 __pwdcache.pw_gid= uic.uic$v_group;
5251 }
5252 else {
5253 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
5254 set_vaxc_errno(sts);
5255 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
5256 return NULL;
5257 }
5258 else { _ckvmssts(sts); }
5259 }
5260 }
5261 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
5262 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
5263 __pwdcache.pw_name= __pw_namecache;
5264 return &__pwdcache;
5265} /* end of my_getpwnam() */
5266/*}}}*/
5267
5268/*
5269 * Get information for a particular UIC or UID.
5270 * Called by my_getpwent with uid=-1 to list all users.
5271*/
5272/*{{{struct passwd *my_getpwuid(Uid_t uid)*/
5273struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
5274{
5275 const $DESCRIPTOR(name_desc,__pw_namecache);
5276 unsigned short lname;
5277 union uicdef uic;
5278 unsigned long int status;
5279
5280 if (uid == (unsigned int) -1) {
5281 do {
5282 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
5283 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
5284 set_vaxc_errno(status);
5285 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5286 my_endpwent();
5287 return NULL;
5288 }
5289 else { _ckvmssts(status); }
5290 } while (!valid_uic (uic));
5291 }
5292 else {
5293 uic.uic$l_uic= uid;
5294 if (!uic.uic$v_group)
5295 uic.uic$v_group= PerlProc_getgid();
5296 if (valid_uic(uic))
5297 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
5298 else status = SS$_IVIDENT;
5299 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
5300 status == RMS$_PRV) {
5301 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5302 return NULL;
5303 }
5304 else { _ckvmssts(status); }
5305 }
5306 __pw_namecache[lname]= '\0';
5307 __mystrtolower(__pw_namecache);
5308
5309 __pwdcache = __passwd_empty;
5310 __pwdcache.pw_name = __pw_namecache;
5311
5312/* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
5313 The identifier's value is usually the UIC, but it doesn't have to be,
5314 so if we can, we let fillpasswd update this. */
5315 __pwdcache.pw_uid = uic.uic$l_uic;
5316 __pwdcache.pw_gid = uic.uic$v_group;
5317
5318 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
5319 return &__pwdcache;
5320
5321} /* end of my_getpwuid() */
5322/*}}}*/
5323
5324/*
5325 * Get information for next user.
5326*/
5327/*{{{struct passwd *my_getpwent()*/
5328struct passwd *Perl_my_getpwent(pTHX)
5329{
5330 return (my_getpwuid((unsigned int) -1));
5331}
5332/*}}}*/
5333
5334/*
5335 * Finish searching rights database for users.
5336*/
5337/*{{{void my_endpwent()*/
5338void Perl_my_endpwent(pTHX)
5339{
5340 if (contxt) {
5341 _ckvmssts(sys$finish_rdb(&contxt));
5342 contxt= 0;
5343 }
5344}
5345/*}}}*/
5346
5347#ifdef HOMEGROWN_POSIX_SIGNALS
5348 /* Signal handling routines, pulled into the core from POSIX.xs.
5349 *
5350 * We need these for threads, so they've been rolled into the core,
5351 * rather than left in POSIX.xs.
5352 *
5353 * (DRS, Oct 23, 1997)
5354 */
5355
5356 /* sigset_t is atomic under VMS, so these routines are easy */
5357/*{{{int my_sigemptyset(sigset_t *) */
5358int my_sigemptyset(sigset_t *set) {
5359 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5360 *set = 0; return 0;
5361}
5362/*}}}*/
5363
5364
5365/*{{{int my_sigfillset(sigset_t *)*/
5366int my_sigfillset(sigset_t *set) {
5367 int i;
5368 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5369 for (i = 0; i < NSIG; i++) *set |= (1 << i);
5370 return 0;
5371}
5372/*}}}*/
5373
5374
5375/*{{{int my_sigaddset(sigset_t *set, int sig)*/
5376int my_sigaddset(sigset_t *set, int sig) {
5377 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5378 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5379 *set |= (1 << (sig - 1));
5380 return 0;
5381}
5382/*}}}*/
5383
5384
5385/*{{{int my_sigdelset(sigset_t *set, int sig)*/
5386int my_sigdelset(sigset_t *set, int sig) {
5387 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5388 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5389 *set &= ~(1 << (sig - 1));
5390 return 0;
5391}
5392/*}}}*/
5393
5394
5395/*{{{int my_sigismember(sigset_t *set, int sig)*/
5396int my_sigismember(sigset_t *set, int sig) {
5397 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
5398 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
5399 *set & (1 << (sig - 1));
5400}
5401/*}}}*/
5402
5403
5404/*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
5405int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
5406 sigset_t tempmask;
5407
5408 /* If set and oset are both null, then things are badly wrong. Bail out. */
5409 if ((oset == NULL) && (set == NULL)) {
5410 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5411 return -1;
5412 }
5413
5414 /* If set's null, then we're just handling a fetch. */
5415 if (set == NULL) {
5416 tempmask = sigblock(0);
5417 }
5418 else {
5419 switch (how) {
5420 case SIG_SETMASK:
5421 tempmask = sigsetmask(*set);
5422 break;
5423 case SIG_BLOCK:
5424 tempmask = sigblock(*set);
5425 break;
5426 case SIG_UNBLOCK:
5427 tempmask = sigblock(0);
5428 sigsetmask(*oset & ~tempmask);
5429 break;
5430 default:
5431 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5432 return -1;
5433 }
5434 }
5435
5436 /* Did they pass us an oset? If so, stick our holding mask into it */
5437 if (oset)
5438 *oset = tempmask;
5439
5440 return 0;
5441}
5442/*}}}*/
5443#endif /* HOMEGROWN_POSIX_SIGNALS */
5444
5445
5446/* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
5447 * my_utime(), and flex_stat(), all of which operate on UTC unless
5448 * VMSISH_TIMES is true.
5449 */
5450/* method used to handle UTC conversions:
5451 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
5452 */
5453static int gmtime_emulation_type;
5454/* number of secs to add to UTC POSIX-style time to get local time */
5455static long int utc_offset_secs;
5456
5457/* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
5458 * in vmsish.h. #undef them here so we can call the CRTL routines
5459 * directly.
5460 */
5461#undef gmtime
5462#undef localtime
5463#undef time
5464
5465
5466/*
5467 * DEC C previous to 6.0 corrupts the behavior of the /prefix
5468 * qualifier with the extern prefix pragma. This provisional
5469 * hack circumvents this prefix pragma problem in previous
5470 * precompilers.
5471 */
5472#if defined(__VMS_VER) && __VMS_VER >= 70000000
5473# if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
5474# pragma __extern_prefix save
5475# pragma __extern_prefix "" /* set to empty to prevent prefixing */
5476# define gmtime decc$__utctz_gmtime
5477# define localtime decc$__utctz_localtime
5478# define time decc$__utc_time
5479# pragma __extern_prefix restore
5480
5481 struct tm *gmtime(), *localtime();
5482
5483# endif
5484#endif
5485
5486
5487static time_t toutc_dst(time_t loc) {
5488 struct tm *rsltmp;
5489
5490 if ((rsltmp = localtime(&loc)) == NULL) return -1;
5491 loc -= utc_offset_secs;
5492 if (rsltmp->tm_isdst) loc -= 3600;
5493 return loc;
5494}
5495#define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5496 ((gmtime_emulation_type || my_time(NULL)), \
5497 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
5498 ((secs) - utc_offset_secs))))
5499
5500static time_t toloc_dst(time_t utc) {
5501 struct tm *rsltmp;
5502
5503 utc += utc_offset_secs;
5504 if ((rsltmp = localtime(&utc)) == NULL) return -1;
5505 if (rsltmp->tm_isdst) utc += 3600;
5506 return utc;
5507}
5508#define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
5509 ((gmtime_emulation_type || my_time(NULL)), \
5510 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
5511 ((secs) + utc_offset_secs))))
5512
5513#ifndef RTL_USES_UTC
5514/*
5515
5516 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
5517 DST starts on 1st sun of april at 02:00 std time
5518 ends on last sun of october at 02:00 dst time
5519 see the UCX management command reference, SET CONFIG TIMEZONE
5520 for formatting info.
5521
5522 No, it's not as general as it should be, but then again, NOTHING
5523 will handle UK times in a sensible way.
5524*/
5525
5526
5527/*
5528 parse the DST start/end info:
5529 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
5530*/
5531
5532static char *
5533tz_parse_startend(char *s, struct tm *w, int *past)
5534{
5535 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
5536 int ly, dozjd, d, m, n, hour, min, sec, j, k;
5537 time_t g;
5538
5539 if (!s) return 0;
5540 if (!w) return 0;
5541 if (!past) return 0;
5542
5543 ly = 0;
5544 if (w->tm_year % 4 == 0) ly = 1;
5545 if (w->tm_year % 100 == 0) ly = 0;
5546 if (w->tm_year+1900 % 400 == 0) ly = 1;
5547 if (ly) dinm[1]++;
5548
5549 dozjd = isdigit(*s);
5550 if (*s == 'J' || *s == 'j' || dozjd) {
5551 if (!dozjd && !isdigit(*++s)) return 0;
5552 d = *s++ - '0';
5553 if (isdigit(*s)) {
5554 d = d*10 + *s++ - '0';
5555 if (isdigit(*s)) {
5556 d = d*10 + *s++ - '0';
5557 }
5558 }
5559 if (d == 0) return 0;
5560 if (d > 366) return 0;
5561 d--;
5562 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
5563 g = d * 86400;
5564 dozjd = 1;
5565 } else if (*s == 'M' || *s == 'm') {
5566 if (!isdigit(*++s)) return 0;
5567 m = *s++ - '0';
5568 if (isdigit(*s)) m = 10*m + *s++ - '0';
5569 if (*s != '.') return 0;
5570 if (!isdigit(*++s)) return 0;
5571 n = *s++ - '0';
5572 if (n < 1 || n > 5) return 0;
5573 if (*s != '.') return 0;
5574 if (!isdigit(*++s)) return 0;
5575 d = *s++ - '0';
5576 if (d > 6) return 0;
5577 }
5578
5579 if (*s == '/') {
5580 if (!isdigit(*++s)) return 0;
5581 hour = *s++ - '0';
5582 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
5583 if (*s == ':') {
5584 if (!isdigit(*++s)) return 0;
5585 min = *s++ - '0';
5586 if (isdigit(*s)) min = 10*min + *s++ - '0';
5587 if (*s == ':') {
5588 if (!isdigit(*++s)) return 0;
5589 sec = *s++ - '0';
5590 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
5591 }
5592 }
5593 } else {
5594 hour = 2;
5595 min = 0;
5596 sec = 0;
5597 }
5598
5599 if (dozjd) {
5600 if (w->tm_yday < d) goto before;
5601 if (w->tm_yday > d) goto after;
5602 } else {
5603 if (w->tm_mon+1 < m) goto before;
5604 if (w->tm_mon+1 > m) goto after;
5605
5606 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
5607 k = d - j; /* mday of first d */
5608 if (k <= 0) k += 7;
5609 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
5610 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
5611 if (w->tm_mday < k) goto before;
5612 if (w->tm_mday > k) goto after;
5613 }
5614
5615 if (w->tm_hour < hour) goto before;
5616 if (w->tm_hour > hour) goto after;
5617 if (w->tm_min < min) goto before;
5618 if (w->tm_min > min) goto after;
5619 if (w->tm_sec < sec) goto before;
5620 goto after;
5621
5622before:
5623 *past = 0;
5624 return s;
5625after:
5626 *past = 1;
5627 return s;
5628}
5629
5630
5631
5632
5633/* parse the offset: (+|-)hh[:mm[:ss]] */
5634
5635static char *
5636tz_parse_offset(char *s, int *offset)
5637{
5638 int hour = 0, min = 0, sec = 0;
5639 int neg = 0;
5640 if (!s) return 0;
5641 if (!offset) return 0;
5642
5643 if (*s == '-') {neg++; s++;}
5644 if (*s == '+') s++;
5645 if (!isdigit(*s)) return 0;
5646 hour = *s++ - '0';
5647 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
5648 if (hour > 24) return 0;
5649 if (*s == ':') {
5650 if (!isdigit(*++s)) return 0;
5651 min = *s++ - '0';
5652 if (isdigit(*s)) min = min*10 + (*s++ - '0');
5653 if (min > 59) return 0;
5654 if (*s == ':') {
5655 if (!isdigit(*++s)) return 0;
5656 sec = *s++ - '0';
5657 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
5658 if (sec > 59) return 0;
5659 }
5660 }
5661
5662 *offset = (hour*60+min)*60 + sec;
5663 if (neg) *offset = -*offset;
5664 return s;
5665}
5666
5667/*
5668 input time is w, whatever type of time the CRTL localtime() uses.
5669 sets dst, the zone, and the gmtoff (seconds)
5670
5671 caches the value of TZ and UCX$TZ env variables; note that
5672 my_setenv looks for these and sets a flag if they're changed
5673 for efficiency.
5674
5675 We have to watch out for the "australian" case (dst starts in
5676 october, ends in april)...flagged by "reverse" and checked by
5677 scanning through the months of the previous year.
5678
5679*/
5680
5681static int
5682tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
5683{
5684 time_t when;
5685 struct tm *w2;
5686 char *s,*s2;
5687 char *dstzone, *tz, *s_start, *s_end;
5688 int std_off, dst_off, isdst;
5689 int y, dststart, dstend;
5690 static char envtz[1025]; /* longer than any logical, symbol, ... */
5691 static char ucxtz[1025];
5692 static char reversed = 0;
5693
5694 if (!w) return 0;
5695
5696 if (tz_updated) {
5697 tz_updated = 0;
5698 reversed = -1; /* flag need to check */
5699 envtz[0] = ucxtz[0] = '\0';
5700 tz = my_getenv("TZ",0);
5701 if (tz) strcpy(envtz, tz);
5702 tz = my_getenv("UCX$TZ",0);
5703 if (tz) strcpy(ucxtz, tz);
5704 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
5705 }
5706 tz = envtz;
5707 if (!*tz) tz = ucxtz;
5708
5709 s = tz;
5710 while (isalpha(*s)) s++;
5711 s = tz_parse_offset(s, &std_off);
5712 if (!s) return 0;
5713 if (!*s) { /* no DST, hurray we're done! */
5714 isdst = 0;
5715 goto done;
5716 }
5717
5718 dstzone = s;
5719 while (isalpha(*s)) s++;
5720 s2 = tz_parse_offset(s, &dst_off);
5721 if (s2) {
5722 s = s2;
5723 } else {
5724 dst_off = std_off - 3600;
5725 }
5726
5727 if (!*s) { /* default dst start/end?? */
5728 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
5729 s = strchr(ucxtz,',');
5730 }
5731 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
5732 }
5733 if (*s != ',') return 0;
5734
5735 when = *w;
5736 when = _toutc(when); /* convert to utc */
5737 when = when - std_off; /* convert to pseudolocal time*/
5738
5739 w2 = localtime(&when);
5740 y = w2->tm_year;
5741 s_start = s+1;
5742 s = tz_parse_startend(s_start,w2,&dststart);
5743 if (!s) return 0;
5744 if (*s != ',') return 0;
5745
5746 when = *w;
5747 when = _toutc(when); /* convert to utc */
5748 when = when - dst_off; /* convert to pseudolocal time*/
5749 w2 = localtime(&when);
5750 if (w2->tm_year != y) { /* spans a year, just check one time */
5751 when += dst_off - std_off;
5752 w2 = localtime(&when);
5753 }
5754 s_end = s+1;
5755 s = tz_parse_startend(s_end,w2,&dstend);
5756 if (!s) return 0;
5757
5758 if (reversed == -1) { /* need to check if start later than end */
5759 int j, ds, de;
5760
5761 when = *w;
5762 if (when < 2*365*86400) {
5763 when += 2*365*86400;
5764 } else {
5765 when -= 365*86400;
5766 }
5767 w2 =localtime(&when);
5768 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
5769
5770 for (j = 0; j < 12; j++) {
5771 w2 =localtime(&when);
5772 (void) tz_parse_startend(s_start,w2,&ds);
5773 (void) tz_parse_startend(s_end,w2,&de);
5774 if (ds != de) break;
5775 when += 30*86400;
5776 }
5777 reversed = 0;
5778 if (de && !ds) reversed = 1;
5779 }
5780
5781 isdst = dststart && !dstend;
5782 if (reversed) isdst = dststart || !dstend;
5783
5784done:
5785 if (dst) *dst = isdst;
5786 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
5787 if (isdst) tz = dstzone;
5788 if (zone) {
5789 while(isalpha(*tz)) *zone++ = *tz++;
5790 *zone = '\0';
5791 }
5792 return 1;
5793}
5794
5795#endif /* !RTL_USES_UTC */
5796
5797/* my_time(), my_localtime(), my_gmtime()
5798 * By default traffic in UTC time values, using CRTL gmtime() or
5799 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
5800 * Note: We need to use these functions even when the CRTL has working
5801 * UTC support, since they also handle C<use vmsish qw(times);>
5802 *
5803 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
5804 * Modified by Charles Bailey <bailey@newman.upenn.edu>
5805 */
5806
5807/*{{{time_t my_time(time_t *timep)*/
5808time_t Perl_my_time(pTHX_ time_t *timep)
5809{
5810 time_t when;
5811 struct tm *tm_p;
5812
5813 if (gmtime_emulation_type == 0) {
5814 int dstnow;
5815 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
5816 /* results of calls to gmtime() and localtime() */
5817 /* for same &base */
5818
5819 gmtime_emulation_type++;
5820 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
5821 char off[LNM$C_NAMLENGTH+1];;
5822
5823 gmtime_emulation_type++;
5824 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
5825 gmtime_emulation_type++;
5826 utc_offset_secs = 0;
5827 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
5828 }
5829 else { utc_offset_secs = atol(off); }
5830 }
5831 else { /* We've got a working gmtime() */
5832 struct tm gmt, local;
5833
5834 gmt = *tm_p;
5835 tm_p = localtime(&base);
5836 local = *tm_p;
5837 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
5838 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
5839 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
5840 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
5841 }
5842 }
5843
5844 when = time(NULL);
5845# ifdef VMSISH_TIME
5846# ifdef RTL_USES_UTC
5847 if (VMSISH_TIME) when = _toloc(when);
5848# else
5849 if (!VMSISH_TIME) when = _toutc(when);
5850# endif
5851# endif
5852 if (timep != NULL) *timep = when;
5853 return when;
5854
5855} /* end of my_time() */
5856/*}}}*/
5857
5858
5859/*{{{struct tm *my_gmtime(const time_t *timep)*/
5860struct tm *
5861Perl_my_gmtime(pTHX_ const time_t *timep)
5862{
5863 char *p;
5864 time_t when;
5865 struct tm *rsltmp;
5866
5867 if (timep == NULL) {
5868 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5869 return NULL;
5870 }
5871 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
5872
5873 when = *timep;
5874# ifdef VMSISH_TIME
5875 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
5876# endif
5877# ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
5878 return gmtime(&when);
5879# else
5880 /* CRTL localtime() wants local time as input, so does no tz correction */
5881 rsltmp = localtime(&when);
5882 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
5883 return rsltmp;
5884#endif
5885} /* end of my_gmtime() */
5886/*}}}*/
5887
5888
5889/*{{{struct tm *my_localtime(const time_t *timep)*/
5890struct tm *
5891Perl_my_localtime(pTHX_ const time_t *timep)
5892{
5893 time_t when, whenutc;
5894 struct tm *rsltmp;
5895 int dst, offset;
5896
5897 if (timep == NULL) {
5898 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
5899 return NULL;
5900 }
5901 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
5902 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
5903
5904 when = *timep;
5905# ifdef RTL_USES_UTC
5906# ifdef VMSISH_TIME
5907 if (VMSISH_TIME) when = _toutc(when);
5908# endif
5909 /* CRTL localtime() wants UTC as input, does tz correction itself */
5910 return localtime(&when);
5911
5912# else /* !RTL_USES_UTC */
5913 whenutc = when;
5914# ifdef VMSISH_TIME
5915 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
5916 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
5917# endif
5918 dst = -1;
5919#ifndef RTL_USES_UTC
5920 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
5921 when = whenutc - offset; /* pseudolocal time*/
5922 }
5923# endif
5924 /* CRTL localtime() wants local time as input, so does no tz correction */
5925 rsltmp = localtime(&when);
5926 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
5927 return rsltmp;
5928# endif
5929
5930} /* end of my_localtime() */
5931/*}}}*/
5932
5933/* Reset definitions for later calls */
5934#define gmtime(t) my_gmtime(t)
5935#define localtime(t) my_localtime(t)
5936#define time(t) my_time(t)
5937
5938
5939/* my_utime - update modification time of a file
5940 * calling sequence is identical to POSIX utime(), but under
5941 * VMS only the modification time is changed; ODS-2 does not
5942 * maintain access times. Restrictions differ from the POSIX
5943 * definition in that the time can be changed as long as the
5944 * caller has permission to execute the necessary IO$_MODIFY $QIO;
5945 * no separate checks are made to insure that the caller is the
5946 * owner of the file or has special privs enabled.
5947 * Code here is based on Joe Meadows' FILE utility.
5948 */
5949
5950/* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
5951 * to VMS epoch (01-JAN-1858 00:00:00.00)
5952 * in 100 ns intervals.
5953 */
5954static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
5955
5956/*{{{int my_utime(char *path, struct utimbuf *utimes)*/
5957int Perl_my_utime(pTHX_ char *file, struct utimbuf *utimes)
5958{
5959 register int i;
5960 long int bintime[2], len = 2, lowbit, unixtime,
5961 secscale = 10000000; /* seconds --> 100 ns intervals */
5962 unsigned long int chan, iosb[2], retsts;
5963 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
5964 struct FAB myfab = cc$rms_fab;
5965 struct NAM mynam = cc$rms_nam;
5966#if defined (__DECC) && defined (__VAX)
5967 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
5968 * at least through VMS V6.1, which causes a type-conversion warning.
5969 */
5970# pragma message save
5971# pragma message disable cvtdiftypes
5972#endif
5973 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
5974 struct fibdef myfib;
5975#if defined (__DECC) && defined (__VAX)
5976 /* This should be right after the declaration of myatr, but due
5977 * to a bug in VAX DEC C, this takes effect a statement early.
5978 */
5979# pragma message restore
5980#endif
5981 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
5982 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
5983 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
5984
5985 if (file == NULL || *file == '\0') {
5986 set_errno(ENOENT);
5987 set_vaxc_errno(LIB$_INVARG);
5988 return -1;
5989 }
5990 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
5991
5992 if (utimes != NULL) {
5993 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
5994 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
5995 * Since time_t is unsigned long int, and lib$emul takes a signed long int
5996 * as input, we force the sign bit to be clear by shifting unixtime right
5997 * one bit, then multiplying by an extra factor of 2 in lib$emul().
5998 */
5999 lowbit = (utimes->modtime & 1) ? secscale : 0;
6000 unixtime = (long int) utimes->modtime;
6001# ifdef VMSISH_TIME
6002 /* If input was UTC; convert to local for sys svc */
6003 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
6004# endif
6005 unixtime >>= 1; secscale <<= 1;
6006 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
6007 if (!(retsts & 1)) {
6008 set_errno(EVMSERR);
6009 set_vaxc_errno(retsts);
6010 return -1;
6011 }
6012 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
6013 if (!(retsts & 1)) {
6014 set_errno(EVMSERR);
6015 set_vaxc_errno(retsts);
6016 return -1;
6017 }
6018 }
6019 else {
6020 /* Just get the current time in VMS format directly */
6021 retsts = sys$gettim(bintime);
6022 if (!(retsts & 1)) {
6023 set_errno(EVMSERR);
6024 set_vaxc_errno(retsts);
6025 return -1;
6026 }
6027 }
6028
6029 myfab.fab$l_fna = vmsspec;
6030 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
6031 myfab.fab$l_nam = &mynam;
6032 mynam.nam$l_esa = esa;
6033 mynam.nam$b_ess = (unsigned char) sizeof esa;
6034 mynam.nam$l_rsa = rsa;
6035 mynam.nam$b_rss = (unsigned char) sizeof rsa;
6036
6037 /* Look for the file to be affected, letting RMS parse the file
6038 * specification for us as well. I have set errno using only
6039 * values documented in the utime() man page for VMS POSIX.
6040 */
6041 retsts = sys$parse(&myfab,0,0);
6042 if (!(retsts & 1)) {
6043 set_vaxc_errno(retsts);
6044 if (retsts == RMS$_PRV) set_errno(EACCES);
6045 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
6046 else set_errno(EVMSERR);
6047 return -1;
6048 }
6049 retsts = sys$search(&myfab,0,0);
6050 if (!(retsts & 1)) {
6051 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6052 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6053 set_vaxc_errno(retsts);
6054 if (retsts == RMS$_PRV) set_errno(EACCES);
6055 else if (retsts == RMS$_FNF) set_errno(ENOENT);
6056 else set_errno(EVMSERR);
6057 return -1;
6058 }
6059
6060 devdsc.dsc$w_length = mynam.nam$b_dev;
6061 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
6062
6063 retsts = sys$assign(&devdsc,&chan,0,0);
6064 if (!(retsts & 1)) {
6065 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6066 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6067 set_vaxc_errno(retsts);
6068 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
6069 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
6070 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
6071 else set_errno(EVMSERR);
6072 return -1;
6073 }
6074
6075 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
6076 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
6077
6078 memset((void *) &myfib, 0, sizeof myfib);
6079#if defined(__DECC) || defined(__DECCXX)
6080 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
6081 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
6082 /* This prevents the revision time of the file being reset to the current
6083 * time as a result of our IO$_MODIFY $QIO. */
6084 myfib.fib$l_acctl = FIB$M_NORECORD;
6085#else
6086 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
6087 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
6088 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
6089#endif
6090 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
6091 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6092 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6093 _ckvmssts(sys$dassgn(chan));
6094 if (retsts & 1) retsts = iosb[0];
6095 if (!(retsts & 1)) {
6096 set_vaxc_errno(retsts);
6097 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6098 else set_errno(EVMSERR);
6099 return -1;
6100 }
6101
6102 return 0;
6103} /* end of my_utime() */
6104/*}}}*/
6105
6106/*
6107 * flex_stat, flex_fstat
6108 * basic stat, but gets it right when asked to stat
6109 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
6110 */
6111
6112/* encode_dev packs a VMS device name string into an integer to allow
6113 * simple comparisons. This can be used, for example, to check whether two
6114 * files are located on the same device, by comparing their encoded device
6115 * names. Even a string comparison would not do, because stat() reuses the
6116 * device name buffer for each call; so without encode_dev, it would be
6117 * necessary to save the buffer and use strcmp (this would mean a number of
6118 * changes to the standard Perl code, to say nothing of what a Perl script
6119 * would have to do.
6120 *
6121 * The device lock id, if it exists, should be unique (unless perhaps compared
6122 * with lock ids transferred from other nodes). We have a lock id if the disk is
6123 * mounted cluster-wide, which is when we tend to get long (host-qualified)
6124 * device names. Thus we use the lock id in preference, and only if that isn't
6125 * available, do we try to pack the device name into an integer (flagged by
6126 * the sign bit (LOCKID_MASK) being set).
6127 *
6128 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
6129 * name and its encoded form, but it seems very unlikely that we will find
6130 * two files on different disks that share the same encoded device names,
6131 * and even more remote that they will share the same file id (if the test
6132 * is to check for the same file).
6133 *
6134 * A better method might be to use sys$device_scan on the first call, and to
6135 * search for the device, returning an index into the cached array.
6136 * The number returned would be more intelligable.
6137 * This is probably not worth it, and anyway would take quite a bit longer
6138 * on the first call.
6139 */
6140#define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
6141static mydev_t encode_dev (pTHX_ const char *dev)
6142{
6143 int i;
6144 unsigned long int f;
6145 mydev_t enc;
6146 char c;
6147 const char *q;
6148
6149 if (!dev || !dev[0]) return 0;
6150
6151#if LOCKID_MASK
6152 {
6153 struct dsc$descriptor_s dev_desc;
6154 unsigned long int status, lockid, item = DVI$_LOCKID;
6155
6156 /* For cluster-mounted disks, the disk lock identifier is unique, so we
6157 can try that first. */
6158 dev_desc.dsc$w_length = strlen (dev);
6159 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
6160 dev_desc.dsc$b_class = DSC$K_CLASS_S;
6161 dev_desc.dsc$a_pointer = (char *) dev;
6162 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
6163 if (lockid) return (lockid & ~LOCKID_MASK);
6164 }
6165#endif
6166
6167 /* Otherwise we try to encode the device name */
6168 enc = 0;
6169 f = 1;
6170 i = 0;
6171 for (q = dev + strlen(dev); q--; q >= dev) {
6172 if (isdigit (*q))
6173 c= (*q) - '0';
6174 else if (isalpha (toupper (*q)))
6175 c= toupper (*q) - 'A' + (char)10;
6176 else
6177 continue; /* Skip '$'s */
6178 i++;
6179 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
6180 if (i>1) f *= 36;
6181 enc += f * (unsigned long int) c;
6182 }
6183 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
6184
6185} /* end of encode_dev() */
6186
6187static char namecache[NAM$C_MAXRSS+1];
6188
6189static int
6190is_null_device(name)
6191 const char *name;
6192{
6193 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
6194 The underscore prefix, controller letter, and unit number are
6195 independently optional; for our purposes, the colon punctuation
6196 is not. The colon can be trailed by optional directory and/or
6197 filename, but two consecutive colons indicates a nodename rather
6198 than a device. [pr] */
6199 if (*name == '_') ++name;
6200 if (tolower(*name++) != 'n') return 0;
6201 if (tolower(*name++) != 'l') return 0;
6202 if (tolower(*name) == 'a') ++name;
6203 if (*name == '0') ++name;
6204 return (*name++ == ':') && (*name != ':');
6205}
6206
6207/* Do the permissions allow some operation? Assumes PL_statcache already set. */
6208/* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
6209 * subset of the applicable information.
6210 */
6211bool
6212Perl_cando(pTHX_ Mode_t bit, Uid_t effective, Stat_t *statbufp)
6213{
6214 char fname_phdev[NAM$C_MAXRSS+1];
6215 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
6216 else {
6217 char fname[NAM$C_MAXRSS+1];
6218 unsigned long int retsts;
6219 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6220 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6221
6222 /* If the struct mystat is stale, we're OOL; stat() overwrites the
6223 device name on successive calls */
6224 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
6225 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
6226 namdsc.dsc$a_pointer = fname;
6227 namdsc.dsc$w_length = sizeof fname - 1;
6228
6229 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
6230 &namdsc,&namdsc.dsc$w_length,0,0);
6231 if (retsts & 1) {
6232 fname[namdsc.dsc$w_length] = '\0';
6233/*
6234 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
6235 * but if someone has redefined that logical, Perl gets very lost. Since
6236 * we have the physical device name from the stat buffer, just paste it on.
6237 */
6238 strcpy( fname_phdev, statbufp->st_devnam );
6239 strcat( fname_phdev, strrchr(fname, ':') );
6240
6241 return cando_by_name(bit,effective,fname_phdev);
6242 }
6243 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
6244 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
6245 return FALSE;
6246 }
6247 _ckvmssts(retsts);
6248 return FALSE; /* Should never get to here */
6249 }
6250} /* end of cando() */
6251/*}}}*/
6252
6253
6254/*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
6255I32
6256Perl_cando_by_name(pTHX_ I32 bit, Uid_t effective, char *fname)
6257{
6258 static char usrname[L_cuserid];
6259 static struct dsc$descriptor_s usrdsc =
6260 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
6261 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
6262 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
6263 unsigned short int retlen;
6264 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6265 union prvdef curprv;
6266 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
6267 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
6268 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
6269 {0,0,0,0}};
6270
6271 if (!fname || !*fname) return FALSE;
6272 /* Make sure we expand logical names, since sys$check_access doesn't */
6273 if (!strpbrk(fname,"/]>:")) {
6274 strcpy(fileified,fname);
6275 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
6276 fname = fileified;
6277 }
6278 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
6279 retlen = namdsc.dsc$w_length = strlen(vmsname);
6280 namdsc.dsc$a_pointer = vmsname;
6281 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
6282 vmsname[retlen-1] == ':') {
6283 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
6284 namdsc.dsc$w_length = strlen(fileified);
6285 namdsc.dsc$a_pointer = fileified;
6286 }
6287
6288 if (!usrdsc.dsc$w_length) {
6289 cuserid(usrname);
6290 usrdsc.dsc$w_length = strlen(usrname);
6291 }
6292
6293 switch (bit) {
6294 case S_IXUSR: case S_IXGRP: case S_IXOTH:
6295 access = ARM$M_EXECUTE; break;
6296 case S_IRUSR: case S_IRGRP: case S_IROTH:
6297 access = ARM$M_READ; break;
6298 case S_IWUSR: case S_IWGRP: case S_IWOTH:
6299 access = ARM$M_WRITE; break;
6300 case S_IDUSR: case S_IDGRP: case S_IDOTH:
6301 access = ARM$M_DELETE; break;
6302 default:
6303 return FALSE;
6304 }
6305
6306 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
6307 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
6308 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
6309 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
6310 set_vaxc_errno(retsts);
6311 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6312 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
6313 else set_errno(ENOENT);
6314 return FALSE;
6315 }
6316 if (retsts == SS$_NORMAL) {
6317 if (!privused) return TRUE;
6318 /* We can get access, but only by using privs. Do we have the
6319 necessary privs currently enabled? */
6320 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
6321 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
6322 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
6323 !curprv.prv$v_bypass) return FALSE;
6324 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
6325 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
6326 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
6327 return TRUE;
6328 }
6329 if (retsts == SS$_ACCONFLICT) {
6330 return TRUE;
6331 }
6332 _ckvmssts(retsts);
6333
6334 return FALSE; /* Should never get here */
6335
6336} /* end of cando_by_name() */
6337/*}}}*/
6338
6339
6340/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
6341int
6342Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
6343{
6344 if (!fstat(fd,(stat_t *) statbufp)) {
6345 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
6346 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
6347# ifdef RTL_USES_UTC
6348# ifdef VMSISH_TIME
6349 if (VMSISH_TIME) {
6350 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6351 statbufp->st_atime = _toloc(statbufp->st_atime);
6352 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6353 }
6354# endif
6355# else
6356# ifdef VMSISH_TIME
6357 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6358# else
6359 if (1) {
6360# endif
6361 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6362 statbufp->st_atime = _toutc(statbufp->st_atime);
6363 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6364 }
6365#endif
6366 return 0;
6367 }
6368 return -1;
6369
6370} /* end of flex_fstat() */
6371/*}}}*/
6372
6373/*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
6374int
6375Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
6376{
6377 char fileified[NAM$C_MAXRSS+1];
6378 char temp_fspec[NAM$C_MAXRSS+300];
6379 int retval = -1;
6380
6381 if (!fspec) return retval;
6382 strcpy(temp_fspec, fspec);
6383 if (statbufp == (Stat_t *) &PL_statcache)
6384 do_tovmsspec(temp_fspec,namecache,0);
6385 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
6386 memset(statbufp,0,sizeof *statbufp);
6387 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
6388 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
6389 statbufp->st_uid = 0x00010001;
6390 statbufp->st_gid = 0x0001;
6391 time((time_t *)&statbufp->st_mtime);
6392 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
6393 return 0;
6394 }
6395
6396 /* Try for a directory name first. If fspec contains a filename without
6397 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
6398 * and sea:[wine.dark]water. exist, we prefer the directory here.
6399 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
6400 * not sea:[wine.dark]., if the latter exists. If the intended target is
6401 * the file with null type, specify this by calling flex_stat() with
6402 * a '.' at the end of fspec.
6403 */
6404 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
6405 retval = stat(fileified,(stat_t *) statbufp);
6406 if (!retval && statbufp == (Stat_t *) &PL_statcache)
6407 strcpy(namecache,fileified);
6408 }
6409 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
6410 if (!retval) {
6411 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
6412# ifdef RTL_USES_UTC
6413# ifdef VMSISH_TIME
6414 if (VMSISH_TIME) {
6415 statbufp->st_mtime = _toloc(statbufp->st_mtime);
6416 statbufp->st_atime = _toloc(statbufp->st_atime);
6417 statbufp->st_ctime = _toloc(statbufp->st_ctime);
6418 }
6419# endif
6420# else
6421# ifdef VMSISH_TIME
6422 if (!VMSISH_TIME) { /* Return UTC instead of local time */
6423# else
6424 if (1) {
6425# endif
6426 statbufp->st_mtime = _toutc(statbufp->st_mtime);
6427 statbufp->st_atime = _toutc(statbufp->st_atime);
6428 statbufp->st_ctime = _toutc(statbufp->st_ctime);
6429 }
6430# endif
6431 }
6432 return retval;
6433
6434} /* end of flex_stat() */
6435/*}}}*/
6436
6437
6438/*{{{char *my_getlogin()*/
6439/* VMS cuserid == Unix getlogin, except calling sequence */
6440char *
6441my_getlogin()
6442{
6443 static char user[L_cuserid];
6444 return cuserid(user);
6445}
6446/*}}}*/
6447
6448
6449/* rmscopy - copy a file using VMS RMS routines
6450 *
6451 * Copies contents and attributes of spec_in to spec_out, except owner
6452 * and protection information. Name and type of spec_in are used as
6453 * defaults for spec_out. The third parameter specifies whether rmscopy()
6454 * should try to propagate timestamps from the input file to the output file.
6455 * If it is less than 0, no timestamps are preserved. If it is 0, then
6456 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
6457 * propagated to the output file at creation iff the output file specification
6458 * did not contain an explicit name or type, and the revision date is always
6459 * updated at the end of the copy operation. If it is greater than 0, then
6460 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
6461 * other than the revision date should be propagated, and bit 1 indicates
6462 * that the revision date should be propagated.
6463 *
6464 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
6465 *
6466 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
6467 * Incorporates, with permission, some code from EZCOPY by Tim Adye
6468 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
6469 * as part of the Perl standard distribution under the terms of the
6470 * GNU General Public License or the Perl Artistic License. Copies
6471 * of each may be found in the Perl standard distribution.
6472 */
6473/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
6474int
6475Perl_rmscopy(pTHX_ char *spec_in, char *spec_out, int preserve_dates)
6476{
6477 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
6478 rsa[NAM$C_MAXRSS], ubf[32256];
6479 unsigned long int i, sts, sts2;
6480 struct FAB fab_in, fab_out;
6481 struct RAB rab_in, rab_out;
6482 struct NAM nam;
6483 struct XABDAT xabdat;
6484 struct XABFHC xabfhc;
6485 struct XABRDT xabrdt;
6486 struct XABSUM xabsum;
6487
6488 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
6489 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
6490 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6491 return 0;
6492 }
6493
6494 fab_in = cc$rms_fab;
6495 fab_in.fab$l_fna = vmsin;
6496 fab_in.fab$b_fns = strlen(vmsin);
6497 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
6498 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
6499 fab_in.fab$l_fop = FAB$M_SQO;
6500 fab_in.fab$l_nam = &nam;
6501 fab_in.fab$l_xab = (void *) &xabdat;
6502
6503 nam = cc$rms_nam;
6504 nam.nam$l_rsa = rsa;
6505 nam.nam$b_rss = sizeof(rsa);
6506 nam.nam$l_esa = esa;
6507 nam.nam$b_ess = sizeof (esa);
6508 nam.nam$b_esl = nam.nam$b_rsl = 0;
6509
6510 xabdat = cc$rms_xabdat; /* To get creation date */
6511 xabdat.xab$l_nxt = (void *) &xabfhc;
6512
6513 xabfhc = cc$rms_xabfhc; /* To get record length */
6514 xabfhc.xab$l_nxt = (void *) &xabsum;
6515
6516 xabsum = cc$rms_xabsum; /* To get key and area information */
6517
6518 if (!((sts = sys$open(&fab_in)) & 1)) {
6519 set_vaxc_errno(sts);
6520 switch (sts) {
6521 case RMS$_FNF: case RMS$_DNF:
6522 set_errno(ENOENT); break;
6523 case RMS$_DIR:
6524 set_errno(ENOTDIR); break;
6525 case RMS$_DEV:
6526 set_errno(ENODEV); break;
6527 case RMS$_SYN:
6528 set_errno(EINVAL); break;
6529 case RMS$_PRV:
6530 set_errno(EACCES); break;
6531 default:
6532 set_errno(EVMSERR);
6533 }
6534 return 0;
6535 }
6536
6537 fab_out = fab_in;
6538 fab_out.fab$w_ifi = 0;
6539 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
6540 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
6541 fab_out.fab$l_fop = FAB$M_SQO;
6542 fab_out.fab$l_fna = vmsout;
6543 fab_out.fab$b_fns = strlen(vmsout);
6544 fab_out.fab$l_dna = nam.nam$l_name;
6545 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
6546
6547 if (preserve_dates == 0) { /* Act like DCL COPY */
6548 nam.nam$b_nop = NAM$M_SYNCHK;
6549 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
6550 if (!((sts = sys$parse(&fab_out)) & 1)) {
6551 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
6552 set_vaxc_errno(sts);
6553 return 0;
6554 }
6555 fab_out.fab$l_xab = (void *) &xabdat;
6556 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
6557 }
6558 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
6559 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
6560 preserve_dates =0; /* bitmask from this point forward */
6561
6562 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
6563 if (!((sts = sys$create(&fab_out)) & 1)) {
6564 set_vaxc_errno(sts);
6565 switch (sts) {
6566 case RMS$_DNF:
6567 set_errno(ENOENT); break;
6568 case RMS$_DIR:
6569 set_errno(ENOTDIR); break;
6570 case RMS$_DEV:
6571 set_errno(ENODEV); break;
6572 case RMS$_SYN:
6573 set_errno(EINVAL); break;
6574 case RMS$_PRV:
6575 set_errno(EACCES); break;
6576 default:
6577 set_errno(EVMSERR);
6578 }
6579 return 0;
6580 }
6581 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
6582 if (preserve_dates & 2) {
6583 /* sys$close() will process xabrdt, not xabdat */
6584 xabrdt = cc$rms_xabrdt;
6585#ifndef __GNUC__
6586 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
6587#else
6588 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
6589 * is unsigned long[2], while DECC & VAXC use a struct */
6590 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
6591#endif
6592 fab_out.fab$l_xab = (void *) &xabrdt;
6593 }
6594
6595 rab_in = cc$rms_rab;
6596 rab_in.rab$l_fab = &fab_in;
6597 rab_in.rab$l_rop = RAB$M_BIO;
6598 rab_in.rab$l_ubf = ubf;
6599 rab_in.rab$w_usz = sizeof ubf;
6600 if (!((sts = sys$connect(&rab_in)) & 1)) {
6601 sys$close(&fab_in); sys$close(&fab_out);
6602 set_errno(EVMSERR); set_vaxc_errno(sts);
6603 return 0;
6604 }
6605
6606 rab_out = cc$rms_rab;
6607 rab_out.rab$l_fab = &fab_out;
6608 rab_out.rab$l_rbf = ubf;
6609 if (!((sts = sys$connect(&rab_out)) & 1)) {
6610 sys$close(&fab_in); sys$close(&fab_out);
6611 set_errno(EVMSERR); set_vaxc_errno(sts);
6612 return 0;
6613 }
6614
6615 while ((sts = sys$read(&rab_in))) { /* always true */
6616 if (sts == RMS$_EOF) break;
6617 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
6618 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
6619 sys$close(&fab_in); sys$close(&fab_out);
6620 set_errno(EVMSERR); set_vaxc_errno(sts);
6621 return 0;
6622 }
6623 }
6624
6625 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
6626 sys$close(&fab_in); sys$close(&fab_out);
6627 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
6628 if (!(sts & 1)) {
6629 set_errno(EVMSERR); set_vaxc_errno(sts);
6630 return 0;
6631 }
6632
6633 return 1;
6634
6635} /* end of rmscopy() */
6636/*}}}*/
6637
6638
6639/*** The following glue provides 'hooks' to make some of the routines
6640 * from this file available from Perl. These routines are sufficiently
6641 * basic, and are required sufficiently early in the build process,
6642 * that's it's nice to have them available to miniperl as well as the
6643 * full Perl, so they're set up here instead of in an extension. The
6644 * Perl code which handles importation of these names into a given
6645 * package lives in [.VMS]Filespec.pm in @INC.
6646 */
6647
6648void
6649rmsexpand_fromperl(pTHX_ CV *cv)
6650{
6651 dXSARGS;
6652 char *fspec, *defspec = NULL, *rslt;
6653 STRLEN n_a;
6654
6655 if (!items || items > 2)
6656 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
6657 fspec = SvPV(ST(0),n_a);
6658 if (!fspec || !*fspec) XSRETURN_UNDEF;
6659 if (items == 2) defspec = SvPV(ST(1),n_a);
6660
6661 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
6662 ST(0) = sv_newmortal();
6663 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
6664 XSRETURN(1);
6665}
6666
6667void
6668vmsify_fromperl(pTHX_ CV *cv)
6669{
6670 dXSARGS;
6671 char *vmsified;
6672 STRLEN n_a;
6673
6674 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
6675 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
6676 ST(0) = sv_newmortal();
6677 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
6678 XSRETURN(1);
6679}
6680
6681void
6682unixify_fromperl(pTHX_ CV *cv)
6683{
6684 dXSARGS;
6685 char *unixified;
6686 STRLEN n_a;
6687
6688 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
6689 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
6690 ST(0) = sv_newmortal();
6691 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
6692 XSRETURN(1);
6693}
6694
6695void
6696fileify_fromperl(pTHX_ CV *cv)
6697{
6698 dXSARGS;
6699 char *fileified;
6700 STRLEN n_a;
6701
6702 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
6703 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
6704 ST(0) = sv_newmortal();
6705 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
6706 XSRETURN(1);
6707}
6708
6709void
6710pathify_fromperl(pTHX_ CV *cv)
6711{
6712 dXSARGS;
6713 char *pathified;
6714 STRLEN n_a;
6715
6716 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
6717 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
6718 ST(0) = sv_newmortal();
6719 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
6720 XSRETURN(1);
6721}
6722
6723void
6724vmspath_fromperl(pTHX_ CV *cv)
6725{
6726 dXSARGS;
6727 char *vmspath;
6728 STRLEN n_a;
6729
6730 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
6731 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
6732 ST(0) = sv_newmortal();
6733 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
6734 XSRETURN(1);
6735}
6736
6737void
6738unixpath_fromperl(pTHX_ CV *cv)
6739{
6740 dXSARGS;
6741 char *unixpath;
6742 STRLEN n_a;
6743
6744 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
6745 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
6746 ST(0) = sv_newmortal();
6747 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
6748 XSRETURN(1);
6749}
6750
6751void
6752candelete_fromperl(pTHX_ CV *cv)
6753{
6754 dXSARGS;
6755 char fspec[NAM$C_MAXRSS+1], *fsp;
6756 SV *mysv;
6757 IO *io;
6758 STRLEN n_a;
6759
6760 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
6761
6762 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
6763 if (SvTYPE(mysv) == SVt_PVGV) {
6764 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
6765 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6766 ST(0) = &PL_sv_no;
6767 XSRETURN(1);
6768 }
6769 fsp = fspec;
6770 }
6771 else {
6772 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
6773 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6774 ST(0) = &PL_sv_no;
6775 XSRETURN(1);
6776 }
6777 }
6778
6779 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
6780 XSRETURN(1);
6781}
6782
6783void
6784rmscopy_fromperl(pTHX_ CV *cv)
6785{
6786 dXSARGS;
6787 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
6788 int date_flag;
6789 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6790 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6791 unsigned long int sts;
6792 SV *mysv;
6793 IO *io;
6794 STRLEN n_a;
6795
6796 if (items < 2 || items > 3)
6797 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
6798
6799 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
6800 if (SvTYPE(mysv) == SVt_PVGV) {
6801 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
6802 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6803 ST(0) = &PL_sv_no;
6804 XSRETURN(1);
6805 }
6806 inp = inspec;
6807 }
6808 else {
6809 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
6810 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6811 ST(0) = &PL_sv_no;
6812 XSRETURN(1);
6813 }
6814 }
6815 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
6816 if (SvTYPE(mysv) == SVt_PVGV) {
6817 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
6818 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6819 ST(0) = &PL_sv_no;
6820 XSRETURN(1);
6821 }
6822 outp = outspec;
6823 }
6824 else {
6825 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
6826 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6827 ST(0) = &PL_sv_no;
6828 XSRETURN(1);
6829 }
6830 }
6831 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
6832
6833 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
6834 XSRETURN(1);
6835}
6836
6837
6838void
6839mod2fname(pTHX_ CV *cv)
6840{
6841 dXSARGS;
6842 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
6843 workbuff[NAM$C_MAXRSS*1 + 1];
6844 int total_namelen = 3, counter, num_entries;
6845 /* ODS-5 ups this, but we want to be consistent, so... */
6846 int max_name_len = 39;
6847 AV *in_array = (AV *)SvRV(ST(0));
6848
6849 num_entries = av_len(in_array);
6850
6851 /* All the names start with PL_. */
6852 strcpy(ultimate_name, "PL_");
6853
6854 /* Clean up our working buffer */
6855 Zero(work_name, sizeof(work_name), char);
6856
6857 /* Run through the entries and build up a working name */
6858 for(counter = 0; counter <= num_entries; counter++) {
6859 /* If it's not the first name then tack on a __ */
6860 if (counter) {
6861 strcat(work_name, "__");
6862 }
6863 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
6864 PL_na));
6865 }
6866
6867 /* Check to see if we actually have to bother...*/
6868 if (strlen(work_name) + 3 <= max_name_len) {
6869 strcat(ultimate_name, work_name);
6870 } else {
6871 /* It's too darned big, so we need to go strip. We use the same */
6872 /* algorithm as xsubpp does. First, strip out doubled __ */
6873 char *source, *dest, last;
6874 dest = workbuff;
6875 last = 0;
6876 for (source = work_name; *source; source++) {
6877 if (last == *source && last == '_') {
6878 continue;
6879 }
6880 *dest++ = *source;
6881 last = *source;
6882 }
6883 /* Go put it back */
6884 strcpy(work_name, workbuff);
6885 /* Is it still too big? */
6886 if (strlen(work_name) + 3 > max_name_len) {
6887 /* Strip duplicate letters */
6888 last = 0;
6889 dest = workbuff;
6890 for (source = work_name; *source; source++) {
6891 if (last == toupper(*source)) {
6892 continue;
6893 }
6894 *dest++ = *source;
6895 last = toupper(*source);
6896 }
6897 strcpy(work_name, workbuff);
6898 }
6899
6900 /* Is it *still* too big? */
6901 if (strlen(work_name) + 3 > max_name_len) {
6902 /* Too bad, we truncate */
6903 work_name[max_name_len - 2] = 0;
6904 }
6905 strcat(ultimate_name, work_name);
6906 }
6907
6908 /* Okay, return it */
6909 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
6910 XSRETURN(1);
6911}
6912
6913void
6914hushexit_fromperl(pTHX_ CV *cv)
6915{
6916 dXSARGS;
6917
6918 if (items > 0) {
6919 VMSISH_HUSHED = SvTRUE(ST(0));
6920 }
6921 ST(0) = boolSV(VMSISH_HUSHED);
6922 XSRETURN(1);
6923}
6924
6925void
6926Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
6927 struct interp_intern *dst)
6928{
6929 memcpy(dst,src,sizeof(struct interp_intern));
6930}
6931
6932void
6933Perl_sys_intern_clear(pTHX)
6934{
6935}
6936
6937void
6938Perl_sys_intern_init(pTHX)
6939{
6940 int ix = RAND_MAX;
6941 float x;
6942
6943 VMSISH_HUSHED = 0;
6944
6945 x = (float)ix;
6946 MY_INV_RAND_MAX = 1./x;
6947}
6948
6949
6950
6951void
6952init_os_extras()
6953{
6954 dTHX;
6955 char* file = __FILE__;
6956 char temp_buff[512];
6957 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
6958 no_translate_barewords = TRUE;
6959 } else {
6960 no_translate_barewords = FALSE;
6961 }
6962
6963 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
6964 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
6965 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
6966 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
6967 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
6968 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
6969 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
6970 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
6971 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
6972 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
6973 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
6974
6975 store_pipelocs(aTHX);
6976
6977 return;
6978}
6979
6980/* End of vms.c */