xref: /openbsd-src/gnu/usr.bin/perl/vms/vms.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*    vms.c
2  *
3  *    VMS-specific routines for perl5
4  *
5  *    Copyright (C) 1993-2013 by Charles Bailey and others.
6  *
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9  */
10 
11 /*
12  *   Yet small as was their hunted band
13  *   still fell and fearless was each hand,
14  *   and strong deeds they wrought yet oft,
15  *   and loved the woods, whose ways more soft
16  *   them seemed than thralls of that black throne
17  *   to live and languish in halls of stone.
18  *        "The Lay of Leithian", Canto II, lines 135-40
19  *
20  *     [p.162 of _The Lays of Beleriand_]
21  */
22 
23 #include <acedef.h>
24 #include <acldef.h>
25 #include <armdef.h>
26 #if __CRTL_VER < 70300000
27 /* needed for home-rolled utime() */
28 #include <atrdef.h>
29 #include <fibdef.h>
30 #endif
31 #include <chpdef.h>
32 #include <clidef.h>
33 #include <climsgdef.h>
34 #include <dcdef.h>
35 #include <descrip.h>
36 #include <devdef.h>
37 #include <dvidef.h>
38 #include <float.h>
39 #include <fscndef.h>
40 #include <iodef.h>
41 #include <jpidef.h>
42 #include <kgbdef.h>
43 #include <libclidef.h>
44 #include <libdef.h>
45 #include <lib$routines.h>
46 #include <lnmdef.h>
47 #include <ossdef.h>
48 #if __CRTL_VER >= 70301000 && !defined(__VAX)
49 #include <ppropdef.h>
50 #endif
51 #include <prvdef.h>
52 #include <psldef.h>
53 #include <rms.h>
54 #include <shrdef.h>
55 #include <ssdef.h>
56 #include <starlet.h>
57 #include <strdef.h>
58 #include <str$routines.h>
59 #include <syidef.h>
60 #include <uaidef.h>
61 #include <uicdef.h>
62 #include <stsdef.h>
63 #include <efndef.h>
64 #define NO_EFN EFN$C_ENF
65 
66 #if  __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
67 int   decc$feature_get_index(const char *name);
68 char* decc$feature_get_name(int index);
69 int   decc$feature_get_value(int index, int mode);
70 int   decc$feature_set_value(int index, int mode, int value);
71 #else
72 #include <unixlib.h>
73 #endif
74 
75 #pragma member_alignment save
76 #pragma nomember_alignment longword
77 struct item_list_3 {
78 	unsigned short len;
79 	unsigned short code;
80 	void * bufadr;
81 	unsigned short * retadr;
82 };
83 #pragma member_alignment restore
84 
85 /* Older versions of ssdef.h don't have these */
86 #ifndef SS$_INVFILFOROP
87 #  define SS$_INVFILFOROP 3930
88 #endif
89 #ifndef SS$_NOSUCHOBJECT
90 #  define SS$_NOSUCHOBJECT 2696
91 #endif
92 
93 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
94 #define PERLIO_NOT_STDIO 0
95 
96 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
97  * code below needs to get to the underlying CRTL routines. */
98 #define DONT_MASK_RTL_CALLS
99 #include "EXTERN.h"
100 #include "perl.h"
101 #include "XSUB.h"
102 /* Anticipating future expansion in lexical warnings . . . */
103 #ifndef WARN_INTERNAL
104 #  define WARN_INTERNAL WARN_MISC
105 #endif
106 
107 #ifdef VMS_LONGNAME_SUPPORT
108 #include <libfildef.h>
109 #endif
110 
111 #if !defined(__VAX) && __CRTL_VER >= 80200000
112 #ifdef lstat
113 #undef lstat
114 #endif
115 #else
116 #ifdef lstat
117 #undef lstat
118 #endif
119 #define lstat(_x, _y) stat(_x, _y)
120 #endif
121 
122 /* Routine to create a decterm for use with the Perl debugger */
123 /* No headers, this information was found in the Programming Concepts Manual */
124 
125 static int (*decw_term_port)
126    (const struct dsc$descriptor_s * display,
127     const struct dsc$descriptor_s * setup_file,
128     const struct dsc$descriptor_s * customization,
129     struct dsc$descriptor_s * result_device_name,
130     unsigned short * result_device_name_length,
131     void * controller,
132     void * char_buffer,
133     void * char_change_buffer) = 0;
134 
135 /* gcc's header files don't #define direct access macros
136  * corresponding to VAXC's variant structs */
137 #ifdef __GNUC__
138 #  define uic$v_format uic$r_uic_form.uic$v_format
139 #  define uic$v_group uic$r_uic_form.uic$v_group
140 #  define uic$v_member uic$r_uic_form.uic$v_member
141 #  define prv$v_bypass  prv$r_prvdef_bits0.prv$v_bypass
142 #  define prv$v_grpprv  prv$r_prvdef_bits0.prv$v_grpprv
143 #  define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
144 #  define prv$v_sysprv  prv$r_prvdef_bits0.prv$v_sysprv
145 #endif
146 
147 #if defined(NEED_AN_H_ERRNO)
148 dEXT int h_errno;
149 #endif
150 
151 #if defined(__DECC) || defined(__DECCXX)
152 #pragma member_alignment save
153 #pragma nomember_alignment longword
154 #pragma message save
155 #pragma message disable misalgndmem
156 #endif
157 struct itmlst_3 {
158   unsigned short int buflen;
159   unsigned short int itmcode;
160   void *bufadr;
161   unsigned short int *retlen;
162 };
163 
164 struct filescan_itmlst_2 {
165     unsigned short length;
166     unsigned short itmcode;
167     char * component;
168 };
169 
170 struct vs_str_st {
171     unsigned short length;
172     char str[VMS_MAXRSS];
173     unsigned short pad; /* for longword struct alignment */
174 };
175 
176 #if defined(__DECC) || defined(__DECCXX)
177 #pragma message restore
178 #pragma member_alignment restore
179 #endif
180 
181 #define do_fileify_dirspec(a,b,c,d)	mp_do_fileify_dirspec(aTHX_ a,b,c,d)
182 #define do_pathify_dirspec(a,b,c,d)	mp_do_pathify_dirspec(aTHX_ a,b,c,d)
183 #define do_tovmsspec(a,b,c,d)		mp_do_tovmsspec(aTHX_ a,b,c,0,d)
184 #define do_tovmspath(a,b,c,d)		mp_do_tovmspath(aTHX_ a,b,c,d)
185 #define do_rmsexpand(a,b,c,d,e,f,g)	mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
186 #define do_vms_realpath(a,b,c)		mp_do_vms_realpath(aTHX_ a,b,c)
187 #define do_vms_realname(a,b,c)		mp_do_vms_realname(aTHX_ a,b,c)
188 #define do_tounixspec(a,b,c,d)		mp_do_tounixspec(aTHX_ a,b,c,d)
189 #define do_tounixpath(a,b,c,d)		mp_do_tounixpath(aTHX_ a,b,c,d)
190 #define do_vms_case_tolerant(a)		mp_do_vms_case_tolerant(a)
191 #define expand_wild_cards(a,b,c,d)	mp_expand_wild_cards(aTHX_ a,b,c,d)
192 #define getredirection(a,b)		mp_getredirection(aTHX_ a,b)
193 
194 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
195 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
196 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
197 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
198 
199 static char *  int_rmsexpand_vms(
200     const char * filespec, char * outbuf, unsigned opts);
201 static char * int_rmsexpand_tovms(
202     const char * filespec, char * outbuf, unsigned opts);
203 static char *int_tovmsspec
204    (const char *path, char *buf, int dir_flag, int * utf8_flag);
205 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
206 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
207 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
208 
209 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
210 #define PERL_LNM_MAX_ALLOWED_INDEX 127
211 
212 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
213  * depending on the facility.  SHOW LOGICAL does 10, so we'll imitate that for
214  * the Perl facility.
215  */
216 #define PERL_LNM_MAX_ITER 10
217 
218   /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
219 #if __CRTL_VER >= 70302000 && !defined(__VAX)
220 #define MAX_DCL_SYMBOL		(8192)
221 #define MAX_DCL_LINE_LENGTH	(4096 - 4)
222 #else
223 #define MAX_DCL_SYMBOL		(1024)
224 #define MAX_DCL_LINE_LENGTH	(1024 - 4)
225 #endif
226 
227 static char *__mystrtolower(char *str)
228 {
229   if (str) for (; *str; ++str) *str= tolower(*str);
230   return str;
231 }
232 
233 static struct dsc$descriptor_s fildevdsc =
234   { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
235 static struct dsc$descriptor_s crtlenvdsc =
236   { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
237 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
238 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
239 static struct dsc$descriptor_s **env_tables = defenv;
240 static bool will_taint = FALSE;  /* tainting active, but no PL_curinterp yet */
241 
242 /* True if we shouldn't treat barewords as logicals during directory */
243 /* munching */
244 static int no_translate_barewords;
245 
246 /* DECC Features that may need to affect how Perl interprets
247  * displays filename information
248  */
249 static int decc_disable_to_vms_logname_translation = 1;
250 static int decc_disable_posix_root = 1;
251 int decc_efs_case_preserve = 0;
252 static int decc_efs_charset = 0;
253 static int decc_efs_charset_index = -1;
254 static int decc_filename_unix_no_version = 0;
255 static int decc_filename_unix_only = 0;
256 int decc_filename_unix_report = 0;
257 int decc_posix_compliant_pathnames = 0;
258 int decc_readdir_dropdotnotype = 0;
259 static int vms_process_case_tolerant = 1;
260 int vms_vtf7_filenames = 0;
261 int gnv_unix_shell = 0;
262 static int vms_unlink_all_versions = 0;
263 static int vms_posix_exit = 0;
264 
265 /* bug workarounds if needed */
266 int decc_bug_devnull = 1;
267 int vms_bug_stat_filename = 0;
268 
269 static int vms_debug_on_exception = 0;
270 static int vms_debug_fileify = 0;
271 
272 /* Simple logical name translation */
273 static int simple_trnlnm
274    (const char * logname,
275     char * value,
276     int value_len)
277 {
278     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
279     const unsigned long attr = LNM$M_CASE_BLIND;
280     struct dsc$descriptor_s name_dsc;
281     int status;
282     unsigned short result;
283     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
284                                 {0, 0, 0, 0}};
285 
286     name_dsc.dsc$w_length = strlen(logname);
287     name_dsc.dsc$a_pointer = (char *)logname;
288     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
289     name_dsc.dsc$b_class = DSC$K_CLASS_S;
290 
291     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
292 
293     if ($VMS_STATUS_SUCCESS(status)) {
294 
295 	 /* Null terminate and return the string */
296 	/*--------------------------------------*/
297 	value[result] = 0;
298         return result;
299     }
300 
301     return 0;
302 }
303 
304 
305 /* Is this a UNIX file specification?
306  *   No longer a simple check with EFS file specs
307  *   For now, not a full check, but need to
308  *   handle POSIX ^UP^ specifications
309  *   Fixing to handle ^/ cases would require
310  *   changes to many other conversion routines.
311  */
312 
313 static int is_unix_filespec(const char *path)
314 {
315 int ret_val;
316 const char * pch1;
317 
318     ret_val = 0;
319     if (strncmp(path,"\"^UP^",5) != 0) {
320 	pch1 = strchr(path, '/');
321 	if (pch1 != NULL)
322 	    ret_val = 1;
323 	else {
324 
325 	    /* If the user wants UNIX files, "." needs to be treated as in UNIX */
326 	    if (decc_filename_unix_report || decc_filename_unix_only) {
327 	    if (strcmp(path,".") == 0)
328 		ret_val = 1;
329 	    }
330 	}
331     }
332     return ret_val;
333 }
334 
335 /* This routine converts a UCS-2 character to be VTF-7 encoded.
336  */
337 
338 static void ucs2_to_vtf7
339    (char *outspec,
340     unsigned long ucs2_char,
341     int * output_cnt)
342 {
343 unsigned char * ucs_ptr;
344 int hex;
345 
346     ucs_ptr = (unsigned char *)&ucs2_char;
347 
348     outspec[0] = '^';
349     outspec[1] = 'U';
350     hex = (ucs_ptr[1] >> 4) & 0xf;
351     if (hex < 0xA)
352 	outspec[2] = hex + '0';
353     else
354 	outspec[2] = (hex - 9) + 'A';
355     hex = ucs_ptr[1] & 0xF;
356     if (hex < 0xA)
357 	outspec[3] = hex + '0';
358     else {
359 	outspec[3] = (hex - 9) + 'A';
360     }
361     hex = (ucs_ptr[0] >> 4) & 0xf;
362     if (hex < 0xA)
363 	outspec[4] = hex + '0';
364     else
365 	outspec[4] = (hex - 9) + 'A';
366     hex = ucs_ptr[1] & 0xF;
367     if (hex < 0xA)
368 	outspec[5] = hex + '0';
369     else {
370 	outspec[5] = (hex - 9) + 'A';
371     }
372     *output_cnt = 6;
373 }
374 
375 
376 /* This handles the conversion of a UNIX extended character set to a ^
377  * escaped VMS character.
378  * in a UNIX file specification.
379  *
380  * The output count variable contains the number of characters added
381  * to the output string.
382  *
383  * The return value is the number of characters read from the input string
384  */
385 static int copy_expand_unix_filename_escape
386   (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
387 {
388 int count;
389 int utf8_flag;
390 
391     utf8_flag = 0;
392     if (utf8_fl)
393       utf8_flag = *utf8_fl;
394 
395     count = 0;
396     *output_cnt = 0;
397     if (*inspec >= 0x80) {
398 	if (utf8_fl && vms_vtf7_filenames) {
399 	unsigned long ucs_char;
400 
401 	    ucs_char = 0;
402 
403 	    if ((*inspec & 0xE0) == 0xC0) {
404 		/* 2 byte Unicode */
405 		ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
406 		if (ucs_char >= 0x80) {
407 		    ucs2_to_vtf7(outspec, ucs_char, output_cnt);
408 		    return 2;
409 		}
410 	    } else if ((*inspec & 0xF0) == 0xE0) {
411 		/* 3 byte Unicode */
412 		ucs_char = ((inspec[0] & 0xF) << 12) +
413 		   ((inspec[1] & 0x3f) << 6) +
414 		   (inspec[2] & 0x3f);
415 		if (ucs_char >= 0x800) {
416 		    ucs2_to_vtf7(outspec, ucs_char, output_cnt);
417 		    return 3;
418 		}
419 
420 #if 0 /* I do not see longer sequences supported by OpenVMS */
421       /* Maybe some one can fix this later */
422 	    } else if ((*inspec & 0xF8) == 0xF0) {
423 		/* 4 byte Unicode */
424 		/* UCS-4 to UCS-2 */
425 	    } else if ((*inspec & 0xFC) == 0xF8) {
426 		/* 5 byte Unicode */
427 		/* UCS-4 to UCS-2 */
428 	    } else if ((*inspec & 0xFE) == 0xFC) {
429 		/* 6 byte Unicode */
430 		/* UCS-4 to UCS-2 */
431 #endif
432 	    }
433 	}
434 
435 	/* High bit set, but not a Unicode character! */
436 
437 	/* Non printing DECMCS or ISO Latin-1 character? */
438 	if ((unsigned char)*inspec <= 0x9F) {
439 	    int hex;
440 	    outspec[0] = '^';
441 	    outspec++;
442 	    hex = (*inspec >> 4) & 0xF;
443 	    if (hex < 0xA)
444 		outspec[1] = hex + '0';
445 	    else {
446 		outspec[1] = (hex - 9) + 'A';
447 	    }
448 	    hex = *inspec & 0xF;
449 	    if (hex < 0xA)
450 		outspec[2] = hex + '0';
451 	    else {
452 		outspec[2] = (hex - 9) + 'A';
453 	    }
454 	    *output_cnt = 3;
455 	    return 1;
456 	} else if ((unsigned char)*inspec == 0xA0) {
457 	    outspec[0] = '^';
458 	    outspec[1] = 'A';
459 	    outspec[2] = '0';
460 	    *output_cnt = 3;
461 	    return 1;
462 	} else if ((unsigned char)*inspec == 0xFF) {
463 	    outspec[0] = '^';
464 	    outspec[1] = 'F';
465 	    outspec[2] = 'F';
466 	    *output_cnt = 3;
467 	    return 1;
468 	}
469 	*outspec = *inspec;
470 	*output_cnt = 1;
471 	return 1;
472     }
473 
474     /* Is this a macro that needs to be passed through?
475      * Macros start with $( and an alpha character, followed
476      * by a string of alpha numeric characters ending with a )
477      * If this does not match, then encode it as ODS-5.
478      */
479     if ((inspec[0] == '$') && (inspec[1] == '(')) {
480     int tcnt;
481 
482 	if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
483 	    tcnt = 3;
484 	    outspec[0] = inspec[0];
485 	    outspec[1] = inspec[1];
486 	    outspec[2] = inspec[2];
487 
488 	    while(isalnum(inspec[tcnt]) ||
489 		  (inspec[2] == '.') || (inspec[2] == '_')) {
490 		outspec[tcnt] = inspec[tcnt];
491 		tcnt++;
492 	    }
493 	    if (inspec[tcnt] == ')') {
494 		outspec[tcnt] = inspec[tcnt];
495 		tcnt++;
496 		*output_cnt = tcnt;
497 		return tcnt;
498 	    }
499 	}
500     }
501 
502     switch (*inspec) {
503     case 0x7f:
504 	outspec[0] = '^';
505 	outspec[1] = '7';
506 	outspec[2] = 'F';
507 	*output_cnt = 3;
508 	return 1;
509 	break;
510     case '?':
511 	if (decc_efs_charset == 0)
512 	  outspec[0] = '%';
513 	else
514 	  outspec[0] = '?';
515 	*output_cnt = 1;
516 	return 1;
517 	break;
518     case '.':
519     case '~':
520     case '!':
521     case '#':
522     case '&':
523     case '\'':
524     case '`':
525     case '(':
526     case ')':
527     case '+':
528     case '@':
529     case '{':
530     case '}':
531     case ',':
532     case ';':
533     case '[':
534     case ']':
535     case '%':
536     case '^':
537     case '\\':
538         /* Don't escape again if following character is
539          * already something we escape.
540          */
541         if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
542 	    *outspec = *inspec;
543 	    *output_cnt = 1;
544 	    return 1;
545 	    break;
546         }
547         /* But otherwise fall through and escape it. */
548     case '=':
549 	/* Assume that this is to be escaped */
550 	outspec[0] = '^';
551 	outspec[1] = *inspec;
552 	*output_cnt = 2;
553 	return 1;
554 	break;
555     case ' ': /* space */
556 	/* Assume that this is to be escaped */
557 	outspec[0] = '^';
558 	outspec[1] = '_';
559 	*output_cnt = 2;
560 	return 1;
561 	break;
562     default:
563 	*outspec = *inspec;
564 	*output_cnt = 1;
565 	return 1;
566 	break;
567     }
568     return 0;
569 }
570 
571 
572 /* This handles the expansion of a '^' prefix to the proper character
573  * in a UNIX file specification.
574  *
575  * The output count variable contains the number of characters added
576  * to the output string.
577  *
578  * The return value is the number of characters read from the input
579  * string
580  */
581 static int copy_expand_vms_filename_escape
582   (char *outspec, const char *inspec, int *output_cnt)
583 {
584 int count;
585 int scnt;
586 
587     count = 0;
588     *output_cnt = 0;
589     if (*inspec == '^') {
590 	inspec++;
591 	switch (*inspec) {
592         /* Spaces and non-trailing dots should just be passed through,
593          * but eat the escape character.
594          */
595 	case '.':
596 	    *outspec = *inspec;
597 	    count += 2;
598 	    (*output_cnt)++;
599 	    break;
600 	case '_': /* space */
601 	    *outspec = ' ';
602 	    count += 2;
603 	    (*output_cnt)++;
604 	    break;
605 	case '^':
606             /* Hmm.  Better leave the escape escaped. */
607             outspec[0] = '^';
608             outspec[1] = '^';
609 	    count += 2;
610 	    (*output_cnt) += 2;
611 	    break;
612 	case 'U': /* Unicode - FIX-ME this is wrong. */
613 	    inspec++;
614 	    count++;
615 	    scnt = strspn(inspec, "0123456789ABCDEFabcdef");
616 	    if (scnt == 4) {
617 		unsigned int c1, c2;
618 		scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
619 		outspec[0] = c1 & 0xff;
620 		outspec[1] = c2 & 0xff;
621 		if (scnt > 1) {
622 		    (*output_cnt) += 2;
623 		    count += 4;
624 		}
625 	    }
626 	    else {
627 		/* Error - do best we can to continue */
628 		*outspec = 'U';
629 		outspec++;
630 		(*output_cnt++);
631 		*outspec = *inspec;
632 		count++;
633 		(*output_cnt++);
634 	    }
635 	    break;
636 	default:
637 	    scnt = strspn(inspec, "0123456789ABCDEFabcdef");
638 	    if (scnt == 2) {
639 		/* Hex encoded */
640 		unsigned int c1;
641 		scnt = sscanf(inspec, "%2x", &c1);
642 		outspec[0] = c1 & 0xff;
643 		if (scnt > 0) {
644 		    (*output_cnt++);
645 		    count += 2;
646 	        }
647 	    }
648 	    else {
649 		*outspec = *inspec;
650 		count++;
651 		(*output_cnt++);
652 	    }
653 	}
654     }
655     else {
656 	*outspec = *inspec;
657 	count++;
658 	(*output_cnt)++;
659     }
660     return count;
661 }
662 
663 /* vms_split_path - Verify that the input file specification is a
664  * VMS format file specification, and provide pointers to the components of
665  * it.  With EFS format filenames, this is virtually the only way to
666  * parse a VMS path specification into components.
667  *
668  * If the sum of the components do not add up to the length of the
669  * string, then the passed file specification is probably a UNIX style
670  * path.
671  */
672 static int vms_split_path
673    (const char * path,
674     char * * volume,
675     int * vol_len,
676     char * * root,
677     int * root_len,
678     char * * dir,
679     int * dir_len,
680     char * * name,
681     int * name_len,
682     char * * ext,
683     int * ext_len,
684     char * * version,
685     int * ver_len)
686 {
687 struct dsc$descriptor path_desc;
688 int status;
689 unsigned long flags;
690 int ret_stat;
691 struct filescan_itmlst_2 item_list[9];
692 const int filespec = 0;
693 const int nodespec = 1;
694 const int devspec = 2;
695 const int rootspec = 3;
696 const int dirspec = 4;
697 const int namespec = 5;
698 const int typespec = 6;
699 const int verspec = 7;
700 
701     /* Assume the worst for an easy exit */
702     ret_stat = -1;
703     *volume = NULL;
704     *vol_len = 0;
705     *root = NULL;
706     *root_len = 0;
707     *dir = NULL;
708     *name = NULL;
709     *name_len = 0;
710     *ext = NULL;
711     *ext_len = 0;
712     *version = NULL;
713     *ver_len = 0;
714 
715     path_desc.dsc$a_pointer = (char *)path; /* cast ok */
716     path_desc.dsc$w_length = strlen(path);
717     path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
718     path_desc.dsc$b_class = DSC$K_CLASS_S;
719 
720     /* Get the total length, if it is shorter than the string passed
721      * then this was probably not a VMS formatted file specification
722      */
723     item_list[filespec].itmcode = FSCN$_FILESPEC;
724     item_list[filespec].length = 0;
725     item_list[filespec].component = NULL;
726 
727     /* If the node is present, then it gets considered as part of the
728      * volume name to hopefully make things simple.
729      */
730     item_list[nodespec].itmcode = FSCN$_NODE;
731     item_list[nodespec].length = 0;
732     item_list[nodespec].component = NULL;
733 
734     item_list[devspec].itmcode = FSCN$_DEVICE;
735     item_list[devspec].length = 0;
736     item_list[devspec].component = NULL;
737 
738     /* root is a special case,  adding it to either the directory or
739      * the device components will probably complicate things for the
740      * callers of this routine, so leave it separate.
741      */
742     item_list[rootspec].itmcode = FSCN$_ROOT;
743     item_list[rootspec].length = 0;
744     item_list[rootspec].component = NULL;
745 
746     item_list[dirspec].itmcode = FSCN$_DIRECTORY;
747     item_list[dirspec].length = 0;
748     item_list[dirspec].component = NULL;
749 
750     item_list[namespec].itmcode = FSCN$_NAME;
751     item_list[namespec].length = 0;
752     item_list[namespec].component = NULL;
753 
754     item_list[typespec].itmcode = FSCN$_TYPE;
755     item_list[typespec].length = 0;
756     item_list[typespec].component = NULL;
757 
758     item_list[verspec].itmcode = FSCN$_VERSION;
759     item_list[verspec].length = 0;
760     item_list[verspec].component = NULL;
761 
762     item_list[8].itmcode = 0;
763     item_list[8].length = 0;
764     item_list[8].component = NULL;
765 
766     status = sys$filescan
767        ((const struct dsc$descriptor_s *)&path_desc, item_list,
768 	&flags, NULL, NULL);
769     _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
770 
771     /* If we parsed it successfully these two lengths should be the same */
772     if (path_desc.dsc$w_length != item_list[filespec].length)
773 	return ret_stat;
774 
775     /* If we got here, then it is a VMS file specification */
776     ret_stat = 0;
777 
778     /* set the volume name */
779     if (item_list[nodespec].length > 0) {
780 	*volume = item_list[nodespec].component;
781 	*vol_len = item_list[nodespec].length + item_list[devspec].length;
782     }
783     else {
784 	*volume = item_list[devspec].component;
785 	*vol_len = item_list[devspec].length;
786     }
787 
788     *root = item_list[rootspec].component;
789     *root_len = item_list[rootspec].length;
790 
791     *dir = item_list[dirspec].component;
792     *dir_len = item_list[dirspec].length;
793 
794     /* Now fun with versions and EFS file specifications
795      * The parser can not tell the difference when a "." is a version
796      * delimiter or a part of the file specification.
797      */
798     if ((decc_efs_charset) &&
799 	(item_list[verspec].length > 0) &&
800 	(item_list[verspec].component[0] == '.')) {
801 	*name = item_list[namespec].component;
802 	*name_len = item_list[namespec].length + item_list[typespec].length;
803 	*ext = item_list[verspec].component;
804 	*ext_len = item_list[verspec].length;
805 	*version = NULL;
806 	*ver_len = 0;
807     }
808     else {
809 	*name = item_list[namespec].component;
810 	*name_len = item_list[namespec].length;
811 	*ext = item_list[typespec].component;
812 	*ext_len = item_list[typespec].length;
813 	*version = item_list[verspec].component;
814 	*ver_len = item_list[verspec].length;
815     }
816     return ret_stat;
817 }
818 
819 /* Routine to determine if the file specification ends with .dir */
820 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
821 
822     /* e_len must be 4, and version must be <= 2 characters */
823     if (e_len != 4 || vs_len > 2)
824         return 0;
825 
826     /* If a version number is present, it needs to be one */
827     if ((vs_len == 2) && (vs_spec[1] != '1'))
828         return 0;
829 
830     /* Look for the DIR on the extension */
831     if (vms_process_case_tolerant) {
832         if ((toupper(e_spec[1]) == 'D') &&
833             (toupper(e_spec[2]) == 'I') &&
834             (toupper(e_spec[3]) == 'R')) {
835             return 1;
836         }
837     } else {
838         /* Directory extensions are supposed to be in upper case only */
839         /* I would not be surprised if this rule can not be enforced */
840         /* if and when someone fully debugs the case sensitive mode */
841         if ((e_spec[1] == 'D') &&
842             (e_spec[2] == 'I') &&
843             (e_spec[3] == 'R')) {
844             return 1;
845         }
846     }
847     return 0;
848 }
849 
850 
851 /* my_maxidx
852  * Routine to retrieve the maximum equivalence index for an input
853  * logical name.  Some calls to this routine have no knowledge if
854  * the variable is a logical or not.  So on error we return a max
855  * index of zero.
856  */
857 /*{{{int my_maxidx(const char *lnm) */
858 static int
859 my_maxidx(const char *lnm)
860 {
861     int status;
862     int midx;
863     int attr = LNM$M_CASE_BLIND;
864     struct dsc$descriptor lnmdsc;
865     struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
866                                 {0, 0, 0, 0}};
867 
868     lnmdsc.dsc$w_length = strlen(lnm);
869     lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
870     lnmdsc.dsc$b_class = DSC$K_CLASS_S;
871     lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
872 
873     status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
874     if ((status & 1) == 0)
875        midx = 0;
876 
877     return (midx);
878 }
879 /*}}}*/
880 
881 /* Routine to remove the 2-byte prefix from the translation of a
882  * process-permanent file (PPF).
883  */
884 static inline unsigned short int
885 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
886 {
887     if (*((int *)lnm) == *((int *)"SYS$")                    &&
888         eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00      &&
889         ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT"))  ||
890           (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT"))   ||
891           (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR"))   ||
892           (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) )  ) {
893 
894         memmove(eqv, eqv+4, eqvlen-4);
895         eqvlen -= 4;
896     }
897     return eqvlen;
898 }
899 
900 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
901 int
902 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
903   struct dsc$descriptor_s **tabvec, unsigned long int flags)
904 {
905     const char *cp1;
906     char uplnm[LNM$C_NAMLENGTH+1], *cp2;
907     unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
908     unsigned long int retsts, attr = LNM$M_CASE_BLIND;
909     int midx;
910     unsigned char acmode;
911     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
912                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
913     struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
914                                  {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
915                                  {0, 0, 0, 0}};
916     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
917 #if defined(PERL_IMPLICIT_CONTEXT)
918     pTHX = NULL;
919     if (PL_curinterp) {
920       aTHX = PERL_GET_INTERP;
921     } else {
922       aTHX = NULL;
923     }
924 #endif
925 
926     if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
927       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
928     }
929     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
930       *cp2 = _toupper(*cp1);
931       if (cp1 - lnm > LNM$C_NAMLENGTH) {
932         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
933         return 0;
934       }
935     }
936     lnmdsc.dsc$w_length = cp1 - lnm;
937     lnmdsc.dsc$a_pointer = uplnm;
938     uplnm[lnmdsc.dsc$w_length] = '\0';
939     secure = flags & PERL__TRNENV_SECURE;
940     acmode = secure ? PSL$C_EXEC : PSL$C_USER;
941     if (!tabvec || !*tabvec) tabvec = env_tables;
942 
943     for (curtab = 0; tabvec[curtab]; curtab++) {
944       if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
945         if (!ivenv && !secure) {
946           char *eq;
947           int i;
948           if (!environ) {
949             ivenv = 1;
950 #if defined(PERL_IMPLICIT_CONTEXT)
951             if (aTHX == NULL) {
952                 fprintf(stderr,
953                     "Can't read CRTL environ\n");
954             } else
955 #endif
956                 Perl_warn(aTHX_ "Can't read CRTL environ\n");
957             continue;
958           }
959           retsts = SS$_NOLOGNAM;
960           for (i = 0; environ[i]; i++) {
961             if ((eq = strchr(environ[i],'=')) &&
962                 lnmdsc.dsc$w_length == (eq - environ[i]) &&
963                 !strncmp(environ[i],uplnm,eq - environ[i])) {
964               eq++;
965               for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
966               if (!eqvlen) continue;
967               retsts = SS$_NORMAL;
968               break;
969             }
970           }
971           if (retsts != SS$_NOLOGNAM) break;
972         }
973       }
974       else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
975                !str$case_blind_compare(&tmpdsc,&clisym)) {
976         if (!ivsym && !secure) {
977           unsigned short int deflen = LNM$C_NAMLENGTH;
978           struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
979           /* dynamic dsc to accommodate possible long value */
980           _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
981           retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
982           if (retsts & 1) {
983             if (eqvlen > MAX_DCL_SYMBOL) {
984               set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
985               eqvlen = MAX_DCL_SYMBOL;
986 	      /* Special hack--we might be called before the interpreter's */
987 	      /* fully initialized, in which case either thr or PL_curcop */
988 	      /* might be bogus. We have to check, since ckWARN needs them */
989 	      /* both to be valid if running threaded */
990 #if defined(PERL_IMPLICIT_CONTEXT)
991               if (aTHX == NULL) {
992                   fprintf(stderr,
993                      "Value of CLI symbol \"%s\" too long",lnm);
994               } else
995 #endif
996 		if (ckWARN(WARN_MISC)) {
997 		  Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
998 		}
999             }
1000             strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1001           }
1002           _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1003           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1004           if (retsts == LIB$_NOSUCHSYM) continue;
1005           break;
1006         }
1007       }
1008       else if (!ivlnm) {
1009         if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1010           midx = my_maxidx(lnm);
1011           for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1012             lnmlst[1].bufadr = cp2;
1013             eqvlen = 0;
1014             retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1015             if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1016             if (retsts == SS$_NOLOGNAM) break;
1017             eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1018             cp2 += eqvlen;
1019             *cp2 = '\0';
1020           }
1021           if ((retsts == SS$_IVLOGNAM) ||
1022               (retsts == SS$_NOLOGNAM)) { continue; }
1023           eqvlen = strlen(eqv);
1024         }
1025         else {
1026           retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1027           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1028           if (retsts == SS$_NOLOGNAM) continue;
1029           eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1030           eqv[eqvlen] = '\0';
1031         }
1032         break;
1033       }
1034     }
1035     if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1036     else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1037              retsts == SS$_IVLOGNAM   || retsts == SS$_IVLOGTAB   ||
1038              retsts == SS$_NOLOGNAM) {
1039       set_errno(EINVAL);  set_vaxc_errno(retsts);
1040     }
1041     else _ckvmssts_noperl(retsts);
1042     return 0;
1043 }  /* end of vmstrnenv */
1044 /*}}}*/
1045 
1046 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1047 /* Define as a function so we can access statics. */
1048 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1049 {
1050     int flags = 0;
1051 
1052 #if defined(PERL_IMPLICIT_CONTEXT)
1053     if (aTHX != NULL)
1054 #endif
1055 #ifdef SECURE_INTERNAL_GETENV
1056         flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1057                  PERL__TRNENV_SECURE : 0;
1058 #endif
1059 
1060     return vmstrnenv(lnm, eqv, idx, fildev, flags);
1061 }
1062 /*}}}*/
1063 
1064 /* my_getenv
1065  * Note: Uses Perl temp to store result so char * can be returned to
1066  * caller; this pointer will be invalidated at next Perl statement
1067  * transition.
1068  * We define this as a function rather than a macro in terms of my_getenv_len()
1069  * so that it'll work when PL_curinterp is undefined (and we therefore can't
1070  * allocate SVs).
1071  */
1072 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1073 char *
1074 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1075 {
1076     const char *cp1;
1077     static char *__my_getenv_eqv = NULL;
1078     char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1079     unsigned long int idx = 0;
1080     int success, secure, saverr, savvmserr;
1081     int midx, flags;
1082     SV *tmpsv;
1083 
1084     midx = my_maxidx(lnm) + 1;
1085 
1086     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1087       /* Set up a temporary buffer for the return value; Perl will
1088        * clean it up at the next statement transition */
1089       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1090       if (!tmpsv) return NULL;
1091       eqv = SvPVX(tmpsv);
1092     }
1093     else {
1094       /* Assume no interpreter ==> single thread */
1095       if (__my_getenv_eqv != NULL) {
1096         Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1097       }
1098       else {
1099         Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1100       }
1101       eqv = __my_getenv_eqv;
1102     }
1103 
1104     for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1105     if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1106       int len;
1107       getcwd(eqv,LNM$C_NAMLENGTH);
1108 
1109       len = strlen(eqv);
1110 
1111       /* Get rid of "000000/ in rooted filespecs */
1112       if (len > 7) {
1113         char * zeros;
1114 	zeros = strstr(eqv, "/000000/");
1115 	if (zeros != NULL) {
1116 	  int mlen;
1117 	  mlen = len - (zeros - eqv) - 7;
1118 	  memmove(zeros, &zeros[7], mlen);
1119 	  len = len - 7;
1120 	  eqv[len] = '\0';
1121 	}
1122       }
1123       return eqv;
1124     }
1125     else {
1126       /* Impose security constraints only if tainting */
1127       if (sys) {
1128         /* Impose security constraints only if tainting */
1129         secure = PL_curinterp ? TAINTING_get : will_taint;
1130         saverr = errno;  savvmserr = vaxc$errno;
1131       }
1132       else {
1133         secure = 0;
1134       }
1135 
1136       flags =
1137 #ifdef SECURE_INTERNAL_GETENV
1138               secure ? PERL__TRNENV_SECURE : 0
1139 #else
1140               0
1141 #endif
1142       ;
1143 
1144       /* For the getenv interface we combine all the equivalence names
1145        * of a search list logical into one value to acquire a maximum
1146        * value length of 255*128 (assuming %ENV is using logicals).
1147        */
1148       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1149 
1150       /* If the name contains a semicolon-delimited index, parse it
1151        * off and make sure we only retrieve the equivalence name for
1152        * that index.  */
1153       if ((cp2 = strchr(lnm,';')) != NULL) {
1154         my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1155         idx = strtoul(cp2+1,NULL,0);
1156         lnm = uplnm;
1157         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1158       }
1159 
1160       success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1161 
1162       /* Discard NOLOGNAM on internal calls since we're often looking
1163        * for an optional name, and this "error" often shows up as the
1164        * (bogus) exit status for a die() call later on.  */
1165       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1166       return success ? eqv : NULL;
1167     }
1168 
1169 }  /* end of my_getenv() */
1170 /*}}}*/
1171 
1172 
1173 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1174 char *
1175 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1176 {
1177     const char *cp1;
1178     char *buf, *cp2;
1179     unsigned long idx = 0;
1180     int midx, flags;
1181     static char *__my_getenv_len_eqv = NULL;
1182     int secure, saverr, savvmserr;
1183     SV *tmpsv;
1184 
1185     midx = my_maxidx(lnm) + 1;
1186 
1187     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1188       /* Set up a temporary buffer for the return value; Perl will
1189        * clean it up at the next statement transition */
1190       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1191       if (!tmpsv) return NULL;
1192       buf = SvPVX(tmpsv);
1193     }
1194     else {
1195       /* Assume no interpreter ==> single thread */
1196       if (__my_getenv_len_eqv != NULL) {
1197         Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1198       }
1199       else {
1200         Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1201       }
1202       buf = __my_getenv_len_eqv;
1203     }
1204 
1205     for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1206     if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1207     char * zeros;
1208 
1209       getcwd(buf,LNM$C_NAMLENGTH);
1210       *len = strlen(buf);
1211 
1212       /* Get rid of "000000/ in rooted filespecs */
1213       if (*len > 7) {
1214       zeros = strstr(buf, "/000000/");
1215       if (zeros != NULL) {
1216 	int mlen;
1217 	mlen = *len - (zeros - buf) - 7;
1218 	memmove(zeros, &zeros[7], mlen);
1219 	*len = *len - 7;
1220 	buf[*len] = '\0';
1221 	}
1222       }
1223       return buf;
1224     }
1225     else {
1226       if (sys) {
1227         /* Impose security constraints only if tainting */
1228         secure = PL_curinterp ? TAINTING_get : will_taint;
1229         saverr = errno;  savvmserr = vaxc$errno;
1230       }
1231       else {
1232         secure = 0;
1233       }
1234 
1235       flags =
1236 #ifdef SECURE_INTERNAL_GETENV
1237               secure ? PERL__TRNENV_SECURE : 0
1238 #else
1239               0
1240 #endif
1241       ;
1242 
1243       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1244 
1245       if ((cp2 = strchr(lnm,';')) != NULL) {
1246         my_strlcpy(buf, lnm, cp2 - lnm + 1);
1247         idx = strtoul(cp2+1,NULL,0);
1248         lnm = buf;
1249         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1250       }
1251 
1252       *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1253 
1254       /* Get rid of "000000/ in rooted filespecs */
1255       if (*len > 7) {
1256       char * zeros;
1257 	zeros = strstr(buf, "/000000/");
1258 	if (zeros != NULL) {
1259 	  int mlen;
1260 	  mlen = *len - (zeros - buf) - 7;
1261 	  memmove(zeros, &zeros[7], mlen);
1262 	  *len = *len - 7;
1263 	  buf[*len] = '\0';
1264 	}
1265       }
1266 
1267       /* Discard NOLOGNAM on internal calls since we're often looking
1268        * for an optional name, and this "error" often shows up as the
1269        * (bogus) exit status for a die() call later on.  */
1270       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1271       return *len ? buf : NULL;
1272     }
1273 
1274 }  /* end of my_getenv_len() */
1275 /*}}}*/
1276 
1277 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1278 
1279 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1280 
1281 /*{{{ void prime_env_iter() */
1282 void
1283 prime_env_iter(void)
1284 /* Fill the %ENV associative array with all logical names we can
1285  * find, in preparation for iterating over it.
1286  */
1287 {
1288   static int primed = 0;
1289   HV *seenhv = NULL, *envhv;
1290   SV *sv = NULL;
1291   char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1292   unsigned short int chan;
1293 #ifndef CLI$M_TRUSTED
1294 #  define CLI$M_TRUSTED 0x40  /* Missing from VAXC headers */
1295 #endif
1296   unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1297   unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1298   long int i;
1299   bool have_sym = FALSE, have_lnm = FALSE;
1300   struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1301   $DESCRIPTOR(cmddsc,cmd);    $DESCRIPTOR(nldsc,"_NLA0:");
1302   $DESCRIPTOR(clidsc,"DCL");  $DESCRIPTOR(clitabdsc,"DCLTABLES");
1303   $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1304   $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1305 #if defined(PERL_IMPLICIT_CONTEXT)
1306   pTHX;
1307 #endif
1308 #if defined(USE_ITHREADS)
1309   static perl_mutex primenv_mutex;
1310   MUTEX_INIT(&primenv_mutex);
1311 #endif
1312 
1313 #if defined(PERL_IMPLICIT_CONTEXT)
1314     /* We jump through these hoops because we can be called at */
1315     /* platform-specific initialization time, which is before anything is */
1316     /* set up--we can't even do a plain dTHX since that relies on the */
1317     /* interpreter structure to be initialized */
1318     if (PL_curinterp) {
1319       aTHX = PERL_GET_INTERP;
1320     } else {
1321       /* we never get here because the NULL pointer will cause the */
1322       /* several of the routines called by this routine to access violate */
1323 
1324       /* This routine is only called by hv.c/hv_iterinit which has a */
1325       /* context, so the real fix may be to pass it through instead of */
1326       /* the hoops above */
1327       aTHX = NULL;
1328     }
1329 #endif
1330 
1331   if (primed || !PL_envgv) return;
1332   MUTEX_LOCK(&primenv_mutex);
1333   if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1334   envhv = GvHVn(PL_envgv);
1335   /* Perform a dummy fetch as an lval to insure that the hash table is
1336    * set up.  Otherwise, the hv_store() will turn into a nullop. */
1337   (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1338 
1339   for (i = 0; env_tables[i]; i++) {
1340      if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1341          !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1342      if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1343   }
1344   if (have_sym || have_lnm) {
1345     long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1346     _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1347     _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1348     _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1349   }
1350 
1351   for (i--; i >= 0; i--) {
1352     if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1353       char *start;
1354       int j;
1355       /* Start at the end, so if there is a duplicate we keep the first one. */
1356       for (j = 0; environ[j]; j++);
1357       for (j--; j >= 0; j--) {
1358         if (!(start = strchr(environ[j],'='))) {
1359           if (ckWARN(WARN_INTERNAL))
1360             Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1361         }
1362         else {
1363           start++;
1364           sv = newSVpv(start,0);
1365           SvTAINTED_on(sv);
1366           (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1367         }
1368       }
1369       continue;
1370     }
1371     else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1372              !str$case_blind_compare(&tmpdsc,&clisym)) {
1373       my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1374       cmddsc.dsc$w_length = 20;
1375       if (env_tables[i]->dsc$w_length == 12 &&
1376           (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1377           !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local  *", sizeof(cmd)-12);
1378       flags = defflags | CLI$M_NOLOGNAM;
1379     }
1380     else {
1381       my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1382       if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1383         my_strlcat(cmd," /Table=", sizeof(cmd));
1384         cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1385       }
1386       else cmddsc.dsc$w_length = 14;  /* N.B. We test this below */
1387       flags = defflags | CLI$M_NOCLISYM;
1388     }
1389 
1390     /* Create a new subprocess to execute each command, to exclude the
1391      * remote possibility that someone could subvert a mbx or file used
1392      * to write multiple commands to a single subprocess.
1393      */
1394     do {
1395       retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1396                          0,&riseandshine,0,0,&clidsc,&clitabdsc);
1397       flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1398       defflags &= ~CLI$M_TRUSTED;
1399     } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1400     _ckvmssts(retsts);
1401     if (!buf) Newx(buf,mbxbufsiz + 1,char);
1402     if (seenhv) SvREFCNT_dec(seenhv);
1403     seenhv = newHV();
1404     while (1) {
1405       char *cp1, *cp2, *key;
1406       unsigned long int sts, iosb[2], retlen, keylen;
1407       U32 hash;
1408 
1409       sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1410       if (sts & 1) sts = iosb[0] & 0xffff;
1411       if (sts == SS$_ENDOFFILE) {
1412         int wakect = 0;
1413         while (substs == 0) { sys$hiber(); wakect++;}
1414         if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
1415         _ckvmssts(substs);
1416         break;
1417       }
1418       _ckvmssts(sts);
1419       retlen = iosb[0] >> 16;
1420       if (!retlen) continue;  /* blank line */
1421       buf[retlen] = '\0';
1422       if (iosb[1] != subpid) {
1423         if (iosb[1]) {
1424           Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1425         }
1426         continue;
1427       }
1428       if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1429         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1430 
1431       for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1432       if (*cp1 == '(' || /* Logical name table name */
1433           *cp1 == '='    /* Next eqv of searchlist  */) continue;
1434       if (*cp1 == '"') cp1++;
1435       for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1436       key = cp1;  keylen = cp2 - cp1;
1437       if (keylen && hv_exists(seenhv,key,keylen)) continue;
1438       while (*cp2 && *cp2 != '=') cp2++;
1439       while (*cp2 && *cp2 == '=') cp2++;
1440       while (*cp2 && *cp2 == ' ') cp2++;
1441       if (*cp2 == '"') {  /* String translation; may embed "" */
1442         for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1443         cp2++;  cp1--; /* Skip "" surrounding translation */
1444       }
1445       else {  /* Numeric translation */
1446         for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1447         cp1--;  /* stop on last non-space char */
1448       }
1449       if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1450         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1451         continue;
1452       }
1453       PERL_HASH(hash,key,keylen);
1454 
1455       if (cp1 == cp2 && *cp2 == '.') {
1456         /* A single dot usually means an unprintable character, such as a null
1457          * to indicate a zero-length value.  Get the actual value to make sure.
1458          */
1459         char lnm[LNM$C_NAMLENGTH+1];
1460         char eqv[MAX_DCL_SYMBOL+1];
1461         int trnlen;
1462         strncpy(lnm, key, keylen);
1463         trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1464         sv = newSVpvn(eqv, strlen(eqv));
1465       }
1466       else {
1467         sv = newSVpvn(cp2,cp1 - cp2 + 1);
1468       }
1469 
1470       SvTAINTED_on(sv);
1471       hv_store(envhv,key,keylen,sv,hash);
1472       hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1473     }
1474     if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1475       /* get the PPFs for this process, not the subprocess */
1476       const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1477       char eqv[LNM$C_NAMLENGTH+1];
1478       int trnlen, i;
1479       for (i = 0; ppfs[i]; i++) {
1480         trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1481         sv = newSVpv(eqv,trnlen);
1482         SvTAINTED_on(sv);
1483         hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1484       }
1485     }
1486   }
1487   primed = 1;
1488   if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1489   if (buf) Safefree(buf);
1490   if (seenhv) SvREFCNT_dec(seenhv);
1491   MUTEX_UNLOCK(&primenv_mutex);
1492   return;
1493 
1494 }  /* end of prime_env_iter */
1495 /*}}}*/
1496 
1497 
1498 /*{{{ int  vmssetenv(const char *lnm, const char *eqv)*/
1499 /* Define or delete an element in the same "environment" as
1500  * vmstrnenv().  If an element is to be deleted, it's removed from
1501  * the first place it's found.  If it's to be set, it's set in the
1502  * place designated by the first element of the table vector.
1503  * Like setenv() returns 0 for success, non-zero on error.
1504  */
1505 int
1506 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1507 {
1508     const char *cp1;
1509     char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1510     unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1511     int nseg = 0, j;
1512     unsigned long int retsts, usermode = PSL$C_USER;
1513     struct itmlst_3 *ile, *ilist;
1514     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1515                             eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1516                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1517     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1518     $DESCRIPTOR(local,"_LOCAL");
1519 
1520     if (!lnm) {
1521         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1522         return SS$_IVLOGNAM;
1523     }
1524 
1525     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1526       *cp2 = _toupper(*cp1);
1527       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1528         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1529         return SS$_IVLOGNAM;
1530       }
1531     }
1532     lnmdsc.dsc$w_length = cp1 - lnm;
1533     if (!tabvec || !*tabvec) tabvec = env_tables;
1534 
1535     if (!eqv) {  /* we're deleting n element */
1536       for (curtab = 0; tabvec[curtab]; curtab++) {
1537         if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1538         int i;
1539           for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1540             if ((cp1 = strchr(environ[i],'=')) &&
1541                 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1542                 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1543 #ifdef HAS_SETENV
1544               return setenv(lnm,"",1) ? vaxc$errno : 0;
1545             }
1546           }
1547           ivenv = 1; retsts = SS$_NOLOGNAM;
1548 #else
1549               if (ckWARN(WARN_INTERNAL))
1550                 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1551               ivenv = 1; retsts = SS$_NOSUCHPGM;
1552               break;
1553             }
1554           }
1555 #endif
1556         }
1557         else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1558                  !str$case_blind_compare(&tmpdsc,&clisym)) {
1559           unsigned int symtype;
1560           if (tabvec[curtab]->dsc$w_length == 12 &&
1561               (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1562               !str$case_blind_compare(&tmpdsc,&local))
1563             symtype = LIB$K_CLI_LOCAL_SYM;
1564           else symtype = LIB$K_CLI_GLOBAL_SYM;
1565           retsts = lib$delete_symbol(&lnmdsc,&symtype);
1566           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1567           if (retsts == LIB$_NOSUCHSYM) continue;
1568           break;
1569         }
1570         else if (!ivlnm) {
1571           retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1572           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1573           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1574           retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1575           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1576         }
1577       }
1578     }
1579     else {  /* we're defining a value */
1580       if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1581 #ifdef HAS_SETENV
1582         return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1583 #else
1584         if (ckWARN(WARN_INTERNAL))
1585           Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1586         retsts = SS$_NOSUCHPGM;
1587 #endif
1588       }
1589       else {
1590         eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1591         eqvdsc.dsc$w_length  = strlen(eqv);
1592         if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1593             !str$case_blind_compare(&tmpdsc,&clisym)) {
1594           unsigned int symtype;
1595           if (tabvec[0]->dsc$w_length == 12 &&
1596               (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1597                !str$case_blind_compare(&tmpdsc,&local))
1598             symtype = LIB$K_CLI_LOCAL_SYM;
1599           else symtype = LIB$K_CLI_GLOBAL_SYM;
1600           retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1601         }
1602         else {
1603           if (!*eqv) eqvdsc.dsc$w_length = 1;
1604 	  if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1605 
1606             nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1607             if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1608 	      Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1609                           lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1610               eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1611               nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1612 	    }
1613 
1614             Newx(ilist,nseg+1,struct itmlst_3);
1615             ile = ilist;
1616             if (!ile) {
1617 	      set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1618               return SS$_INSFMEM;
1619 	    }
1620             memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1621 
1622             for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1623               ile->itmcode = LNM$_STRING;
1624               ile->bufadr = c;
1625               if ((j+1) == nseg) {
1626                 ile->buflen = strlen(c);
1627                 /* in case we are truncating one that's too long */
1628                 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1629               }
1630               else {
1631                 ile->buflen = LNM$C_NAMLENGTH;
1632               }
1633             }
1634 
1635             retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1636             Safefree (ilist);
1637 	  }
1638           else {
1639             retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1640 	  }
1641         }
1642       }
1643     }
1644     if (!(retsts & 1)) {
1645       switch (retsts) {
1646         case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1647         case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1648           set_errno(EVMSERR); break;
1649         case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1650         case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1651           set_errno(EINVAL); break;
1652         case SS$_NOPRIV:
1653           set_errno(EACCES); break;
1654         default:
1655           _ckvmssts(retsts);
1656           set_errno(EVMSERR);
1657        }
1658        set_vaxc_errno(retsts);
1659        return (int) retsts || 44; /* retsts should never be 0, but just in case */
1660     }
1661     else {
1662       /* We reset error values on success because Perl does an hv_fetch()
1663        * before each hv_store(), and if the thing we're setting didn't
1664        * previously exist, we've got a leftover error message.  (Of course,
1665        * this fails in the face of
1666        *    $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1667        * in that the error reported in $! isn't spurious,
1668        * but it's right more often than not.)
1669        */
1670       set_errno(0); set_vaxc_errno(retsts);
1671       return 0;
1672     }
1673 
1674 }  /* end of vmssetenv() */
1675 /*}}}*/
1676 
1677 /*{{{ void  my_setenv(const char *lnm, const char *eqv)*/
1678 /* This has to be a function since there's a prototype for it in proto.h */
1679 void
1680 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1681 {
1682     if (lnm && *lnm) {
1683       int len = strlen(lnm);
1684       if  (len == 7) {
1685         char uplnm[8];
1686         int i;
1687         for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1688         if (!strcmp(uplnm,"DEFAULT")) {
1689           if (eqv && *eqv) my_chdir(eqv);
1690           return;
1691         }
1692     }
1693   }
1694   (void) vmssetenv(lnm,eqv,NULL);
1695 }
1696 /*}}}*/
1697 
1698 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1699 /*  vmssetuserlnm
1700  *  sets a user-mode logical in the process logical name table
1701  *  used for redirection of sys$error
1702  */
1703 void
1704 Perl_vmssetuserlnm(const char *name, const char *eqv)
1705 {
1706     $DESCRIPTOR(d_tab, "LNM$PROCESS");
1707     struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1708     unsigned long int iss, attr = LNM$M_CONFINE;
1709     unsigned char acmode = PSL$C_USER;
1710     struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1711                                  {0, 0, 0, 0}};
1712     d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1713     d_name.dsc$w_length = strlen(name);
1714 
1715     lnmlst[0].buflen = strlen(eqv);
1716     lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1717 
1718     iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1719     if (!(iss&1)) lib$signal(iss);
1720 }
1721 /*}}}*/
1722 
1723 
1724 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1725 /* my_crypt - VMS password hashing
1726  * my_crypt() provides an interface compatible with the Unix crypt()
1727  * C library function, and uses sys$hash_password() to perform VMS
1728  * password hashing.  The quadword hashed password value is returned
1729  * as a NUL-terminated 8 character string.  my_crypt() does not change
1730  * the case of its string arguments; in order to match the behavior
1731  * of LOGINOUT et al., alphabetic characters in both arguments must
1732  *  be upcased by the caller.
1733  *
1734  * - fix me to call ACM services when available
1735  */
1736 char *
1737 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1738 {
1739 #   ifndef UAI$C_PREFERRED_ALGORITHM
1740 #     define UAI$C_PREFERRED_ALGORITHM 127
1741 #   endif
1742     unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1743     unsigned short int salt = 0;
1744     unsigned long int sts;
1745     struct const_dsc {
1746         unsigned short int dsc$w_length;
1747         unsigned char      dsc$b_type;
1748         unsigned char      dsc$b_class;
1749         const char *       dsc$a_pointer;
1750     }  usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1751        txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1752     struct itmlst_3 uailst[3] = {
1753         { sizeof alg,  UAI$_ENCRYPT, &alg, 0},
1754         { sizeof salt, UAI$_SALT,    &salt, 0},
1755         { 0,           0,            NULL,  NULL}};
1756     static char hash[9];
1757 
1758     usrdsc.dsc$w_length = strlen(usrname);
1759     usrdsc.dsc$a_pointer = usrname;
1760     if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1761       switch (sts) {
1762         case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1763           set_errno(EACCES);
1764           break;
1765         case RMS$_RNF:
1766           set_errno(ESRCH);  /* There isn't a Unix no-such-user error */
1767           break;
1768         default:
1769           set_errno(EVMSERR);
1770       }
1771       set_vaxc_errno(sts);
1772       if (sts != RMS$_RNF) return NULL;
1773     }
1774 
1775     txtdsc.dsc$w_length = strlen(textpasswd);
1776     txtdsc.dsc$a_pointer = textpasswd;
1777     if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1778       set_errno(EVMSERR);  set_vaxc_errno(sts);  return NULL;
1779     }
1780 
1781     return (char *) hash;
1782 
1783 }  /* end of my_crypt() */
1784 /*}}}*/
1785 
1786 
1787 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1788 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1789 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1790 
1791 /* fixup barenames that are directories for internal use.
1792  * There have been problems with the consistent handling of UNIX
1793  * style directory names when routines are presented with a name that
1794  * has no directory delimiters at all.  So this routine will eventually
1795  * fix the issue.
1796  */
1797 static char * fixup_bare_dirnames(const char * name)
1798 {
1799   if (decc_disable_to_vms_logname_translation) {
1800 /* fix me */
1801   }
1802   return NULL;
1803 }
1804 
1805 /* 8.3, remove() is now broken on symbolic links */
1806 static int rms_erase(const char * vmsname);
1807 
1808 
1809 /* mp_do_kill_file
1810  * A little hack to get around a bug in some implementation of remove()
1811  * that do not know how to delete a directory
1812  *
1813  * Delete any file to which user has control access, regardless of whether
1814  * delete access is explicitly allowed.
1815  * Limitations: User must have write access to parent directory.
1816  *              Does not block signals or ASTs; if interrupted in midstream
1817  *              may leave file with an altered ACL.
1818  * HANDLE WITH CARE!
1819  */
1820 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1821 static int
1822 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1823 {
1824     char *vmsname;
1825     char *rslt;
1826     unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1827     unsigned long int cxt = 0, aclsts, fndsts;
1828     int rmsts = -1;
1829     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1830     struct myacedef {
1831       unsigned char myace$b_length;
1832       unsigned char myace$b_type;
1833       unsigned short int myace$w_flags;
1834       unsigned long int myace$l_access;
1835       unsigned long int myace$l_ident;
1836     } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1837                  ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1838       oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1839      struct itmlst_3
1840        findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1841                      {sizeof oldace, ACL$C_READACE,   &oldace, 0},{0,0,0,0}},
1842        addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1843        dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1844        lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1845        ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1846 
1847     /* Expand the input spec using RMS, since the CRTL remove() and
1848      * system services won't do this by themselves, so we may miss
1849      * a file "hiding" behind a logical name or search list. */
1850     vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1851     if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1852 
1853     rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1854     if (rslt == NULL) {
1855         PerlMem_free(vmsname);
1856 	return -1;
1857       }
1858 
1859     /* Erase the file */
1860     rmsts = rms_erase(vmsname);
1861 
1862     /* Did it succeed */
1863     if ($VMS_STATUS_SUCCESS(rmsts)) {
1864 	PerlMem_free(vmsname);
1865 	return 0;
1866       }
1867 
1868     /* If not, can changing protections help? */
1869     if (rmsts != RMS$_PRV) {
1870       set_vaxc_errno(rmsts);
1871       PerlMem_free(vmsname);
1872       return -1;
1873     }
1874 
1875     /* No, so we get our own UIC to use as a rights identifier,
1876      * and the insert an ACE at the head of the ACL which allows us
1877      * to delete the file.
1878      */
1879     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1880     fildsc.dsc$w_length = strlen(vmsname);
1881     fildsc.dsc$a_pointer = vmsname;
1882     cxt = 0;
1883     newace.myace$l_ident = oldace.myace$l_ident;
1884     rmsts = -1;
1885     if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1886       switch (aclsts) {
1887         case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1888           set_errno(ENOENT); break;
1889         case RMS$_DIR:
1890           set_errno(ENOTDIR); break;
1891         case RMS$_DEV:
1892           set_errno(ENODEV); break;
1893         case RMS$_SYN: case SS$_INVFILFOROP:
1894           set_errno(EINVAL); break;
1895         case RMS$_PRV:
1896           set_errno(EACCES); break;
1897         default:
1898           _ckvmssts_noperl(aclsts);
1899       }
1900       set_vaxc_errno(aclsts);
1901       PerlMem_free(vmsname);
1902       return -1;
1903     }
1904     /* Grab any existing ACEs with this identifier in case we fail */
1905     aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1906     if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1907                     || fndsts == SS$_NOMOREACE ) {
1908       /* Add the new ACE . . . */
1909       if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1910         goto yourroom;
1911 
1912       rmsts = rms_erase(vmsname);
1913       if ($VMS_STATUS_SUCCESS(rmsts)) {
1914 	rmsts = 0;
1915 	}
1916 	else {
1917 	rmsts = -1;
1918         /* We blew it - dir with files in it, no write priv for
1919          * parent directory, etc.  Put things back the way they were. */
1920         if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1921           goto yourroom;
1922         if (fndsts & 1) {
1923           addlst[0].bufadr = &oldace;
1924           if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1925             goto yourroom;
1926         }
1927       }
1928     }
1929 
1930     yourroom:
1931     fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1932     /* We just deleted it, so of course it's not there.  Some versions of
1933      * VMS seem to return success on the unlock operation anyhow (after all
1934      * the unlock is successful), but others don't.
1935      */
1936     if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1937     if (aclsts & 1) aclsts = fndsts;
1938     if (!(aclsts & 1)) {
1939       set_errno(EVMSERR);
1940       set_vaxc_errno(aclsts);
1941     }
1942 
1943     PerlMem_free(vmsname);
1944     return rmsts;
1945 
1946 }  /* end of kill_file() */
1947 /*}}}*/
1948 
1949 
1950 /*{{{int do_rmdir(char *name)*/
1951 int
1952 Perl_do_rmdir(pTHX_ const char *name)
1953 {
1954     char * dirfile;
1955     int retval;
1956     Stat_t st;
1957 
1958     /* lstat returns a VMS fileified specification of the name */
1959     /* that is looked up, and also lets verifies that this is a directory */
1960 
1961     retval = flex_lstat(name, &st);
1962     if (retval != 0) {
1963         char * ret_spec;
1964 
1965         /* Due to a historical feature, flex_stat/lstat can not see some */
1966         /* Unix format file names that the rest of the CRTL can see */
1967         /* Fixing that feature will cause some perl tests to fail */
1968         /* So try this one more time. */
1969 
1970         retval = lstat(name, &st.crtl_stat);
1971         if (retval != 0)
1972             return -1;
1973 
1974         /* force it to a file spec for the kill file to work. */
1975         ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1976         if (ret_spec == NULL) {
1977             errno = EIO;
1978             return -1;
1979         }
1980     }
1981 
1982     if (!S_ISDIR(st.st_mode)) {
1983 	errno = ENOTDIR;
1984 	retval = -1;
1985     }
1986     else {
1987         dirfile = st.st_devnam;
1988 
1989         /* It may be possible for flex_stat to find a file and vmsify() to */
1990         /* fail with ODS-2 specifications.  mp_do_kill_file can not deal */
1991         /* with that case, so fail it */
1992         if (dirfile[0] == 0) {
1993             errno = EIO;
1994             return -1;
1995         }
1996 
1997 	retval = mp_do_kill_file(aTHX_ dirfile, 1);
1998     }
1999 
2000     return retval;
2001 
2002 }  /* end of do_rmdir */
2003 /*}}}*/
2004 
2005 /* kill_file
2006  * Delete any file to which user has control access, regardless of whether
2007  * delete access is explicitly allowed.
2008  * Limitations: User must have write access to parent directory.
2009  *              Does not block signals or ASTs; if interrupted in midstream
2010  *              may leave file with an altered ACL.
2011  * HANDLE WITH CARE!
2012  */
2013 /*{{{int kill_file(char *name)*/
2014 int
2015 Perl_kill_file(pTHX_ const char *name)
2016 {
2017     char * vmsfile;
2018     Stat_t st;
2019     int rmsts;
2020 
2021     /* Convert the filename to VMS format and see if it is a directory */
2022     /* flex_lstat returns a vmsified file specification */
2023     rmsts = flex_lstat(name, &st);
2024     if (rmsts != 0) {
2025 
2026         /* Due to a historical feature, flex_stat/lstat can not see some */
2027         /* Unix format file names that the rest of the CRTL can see when */
2028         /* ODS-2 file specifications are in use. */
2029         /* Fixing that feature will cause some perl tests to fail */
2030         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2031         st.st_mode = 0;
2032         vmsfile = (char *) name; /* cast ok */
2033 
2034     } else {
2035         vmsfile = st.st_devnam;
2036         if (vmsfile[0] == 0) {
2037             /* It may be possible for flex_stat to find a file and vmsify() */
2038             /* to fail with ODS-2 specifications.  mp_do_kill_file can not */
2039             /* deal with that case, so fail it */
2040             errno = EIO;
2041             return -1;
2042         }
2043     }
2044 
2045     /* Remove() is allowed to delete directories, according to the X/Open
2046      * specifications.
2047      * This may need special handling to work with the ACL hacks.
2048      */
2049     if (S_ISDIR(st.st_mode)) {
2050         rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2051         return rmsts;
2052     }
2053 
2054     rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2055 
2056     /* Need to delete all versions ? */
2057     if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2058         int i = 0;
2059 
2060         /* Just use lstat() here as do not need st_dev */
2061         /* and we know that the file is in VMS format or that */
2062         /* because of a historical bug, flex_stat can not see the file */
2063         while (lstat(vmsfile, (stat_t *)&st) == 0) {
2064             rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2065             if (rmsts != 0)
2066                 break;
2067             i++;
2068 
2069             /* Make sure that we do not loop forever */
2070             if (i > 32767) {
2071                 errno = EIO;
2072                 rmsts = -1;
2073                 break;
2074             }
2075         }
2076     }
2077 
2078     return rmsts;
2079 
2080 }  /* end of kill_file() */
2081 /*}}}*/
2082 
2083 
2084 /*{{{int my_mkdir(char *,Mode_t)*/
2085 int
2086 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2087 {
2088   STRLEN dirlen = strlen(dir);
2089 
2090   /* zero length string sometimes gives ACCVIO */
2091   if (dirlen == 0) return -1;
2092 
2093   /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2094    * null file name/type.  However, it's commonplace under Unix,
2095    * so we'll allow it for a gain in portability.
2096    */
2097   if (dir[dirlen-1] == '/') {
2098     char *newdir = savepvn(dir,dirlen-1);
2099     int ret = mkdir(newdir,mode);
2100     Safefree(newdir);
2101     return ret;
2102   }
2103   else return mkdir(dir,mode);
2104 }  /* end of my_mkdir */
2105 /*}}}*/
2106 
2107 /*{{{int my_chdir(char *)*/
2108 int
2109 Perl_my_chdir(pTHX_ const char *dir)
2110 {
2111   STRLEN dirlen = strlen(dir);
2112   const char *dir1 = dir;
2113 
2114   /* zero length string sometimes gives ACCVIO */
2115   if (dirlen == 0) {
2116     SETERRNO(EINVAL, SS$_BADPARAM);
2117     return -1;
2118   }
2119 
2120   /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2121    * This does not work if DECC$EFS_CHARSET is active.  Hack it here
2122    * so that existing scripts do not need to be changed.
2123    */
2124   while ((dirlen > 0) && (*dir1 == ' ')) {
2125     dir1++;
2126     dirlen--;
2127   }
2128 
2129   /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2130    * that implies
2131    * null file name/type.  However, it's commonplace under Unix,
2132    * so we'll allow it for a gain in portability.
2133    *
2134    *  '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2135    */
2136   if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2137       char *newdir;
2138       int ret;
2139       newdir = (char *)PerlMem_malloc(dirlen);
2140       if (newdir ==NULL)
2141           _ckvmssts_noperl(SS$_INSFMEM);
2142       memcpy(newdir, dir1, dirlen-1);
2143       newdir[dirlen-1] = '\0';
2144       ret = chdir(newdir);
2145       PerlMem_free(newdir);
2146       return ret;
2147   }
2148   else return chdir(dir1);
2149 }  /* end of my_chdir */
2150 /*}}}*/
2151 
2152 
2153 /*{{{int my_chmod(char *, mode_t)*/
2154 int
2155 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2156 {
2157   Stat_t st;
2158   int ret = -1;
2159   char * changefile;
2160   STRLEN speclen = strlen(file_spec);
2161 
2162   /* zero length string sometimes gives ACCVIO */
2163   if (speclen == 0) return -1;
2164 
2165   /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2166    * that implies null file name/type.  However, it's commonplace under Unix,
2167    * so we'll allow it for a gain in portability.
2168    *
2169    * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2170    * in VMS file.dir notation.
2171    */
2172   changefile = (char *) file_spec; /* cast ok */
2173   ret = flex_lstat(file_spec, &st);
2174   if (ret != 0) {
2175 
2176         /* Due to a historical feature, flex_stat/lstat can not see some */
2177         /* Unix format file names that the rest of the CRTL can see when */
2178         /* ODS-2 file specifications are in use. */
2179         /* Fixing that feature will cause some perl tests to fail */
2180         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2181         st.st_mode = 0;
2182 
2183   } else {
2184       /* It may be possible to get here with nothing in st_devname */
2185       /* chmod still may work though */
2186       if (st.st_devnam[0] != 0) {
2187           changefile = st.st_devnam;
2188       }
2189   }
2190   ret = chmod(changefile, mode);
2191   return ret;
2192 }  /* end of my_chmod */
2193 /*}}}*/
2194 
2195 
2196 /*{{{FILE *my_tmpfile()*/
2197 FILE *
2198 my_tmpfile(void)
2199 {
2200   FILE *fp;
2201   char *cp;
2202 
2203   if ((fp = tmpfile())) return fp;
2204 
2205   cp = (char *)PerlMem_malloc(L_tmpnam+24);
2206   if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2207 
2208   if (decc_filename_unix_only == 0)
2209     strcpy(cp,"Sys$Scratch:");
2210   else
2211     strcpy(cp,"/tmp/");
2212   tmpnam(cp+strlen(cp));
2213   strcat(cp,".Perltmp");
2214   fp = fopen(cp,"w+","fop=dlt");
2215   PerlMem_free(cp);
2216   return fp;
2217 }
2218 /*}}}*/
2219 
2220 
2221 /*
2222  * The C RTL's sigaction fails to check for invalid signal numbers so we
2223  * help it out a bit.  The docs are correct, but the actual routine doesn't
2224  * do what the docs say it will.
2225  */
2226 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2227 int
2228 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2229                    struct sigaction* oact)
2230 {
2231   if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2232 	SETERRNO(EINVAL, SS$_INVARG);
2233 	return -1;
2234   }
2235   return sigaction(sig, act, oact);
2236 }
2237 /*}}}*/
2238 
2239 #ifdef KILL_BY_SIGPRC
2240 #include <errnodef.h>
2241 
2242 /* We implement our own kill() using the undocumented system service
2243    sys$sigprc for one of two reasons:
2244 
2245    1.) If the kill() in an older CRTL uses sys$forcex, causing the
2246    target process to do a sys$exit, which usually can't be handled
2247    gracefully...certainly not by Perl and the %SIG{} mechanism.
2248 
2249    2.) If the kill() in the CRTL can't be called from a signal
2250    handler without disappearing into the ether, i.e., the signal
2251    it purportedly sends is never trapped. Still true as of VMS 7.3.
2252 
2253    sys$sigprc has the same parameters as sys$forcex, but throws an exception
2254    in the target process rather than calling sys$exit.
2255 
2256    Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2257    on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2258    provide.  On VMS 7.0+ this is taken care of by doing sys$sigprc
2259    with condition codes C$_SIG0+nsig*8, catching the exception on the
2260    target process and resignaling with appropriate arguments.
2261 
2262    But we don't have that VMS 7.0+ exception handler, so if you
2263    Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS.  Oh well.
2264 
2265    Also note that SIGTERM is listed in the docs as being "unimplemented",
2266    yet always seems to be signaled with a VMS condition code of 4 (and
2267    correctly handled for that code).  So we hardwire it in.
2268 
2269    Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2270    number to see if it's valid.  So Perl_my_kill(pid,0) returns -1 rather
2271    than signalling with an unrecognized (and unhandled by CRTL) code.
2272 */
2273 
2274 #define _MY_SIG_MAX 28
2275 
2276 static unsigned int
2277 Perl_sig_to_vmscondition_int(int sig)
2278 {
2279     static unsigned int sig_code[_MY_SIG_MAX+1] =
2280     {
2281         0,                  /*  0 ZERO     */
2282         SS$_HANGUP,         /*  1 SIGHUP   */
2283         SS$_CONTROLC,       /*  2 SIGINT   */
2284         SS$_CONTROLY,       /*  3 SIGQUIT  */
2285         SS$_RADRMOD,        /*  4 SIGILL   */
2286         SS$_BREAK,          /*  5 SIGTRAP  */
2287         SS$_OPCCUS,         /*  6 SIGABRT  */
2288         SS$_COMPAT,         /*  7 SIGEMT   */
2289 #ifdef __VAX
2290         SS$_FLTOVF,         /*  8 SIGFPE VAX */
2291 #else
2292         SS$_HPARITH,        /*  8 SIGFPE AXP */
2293 #endif
2294         SS$_ABORT,          /*  9 SIGKILL  */
2295         SS$_ACCVIO,         /* 10 SIGBUS   */
2296         SS$_ACCVIO,         /* 11 SIGSEGV  */
2297         SS$_BADPARAM,       /* 12 SIGSYS   */
2298         SS$_NOMBX,          /* 13 SIGPIPE  */
2299         SS$_ASTFLT,         /* 14 SIGALRM  */
2300         4,                  /* 15 SIGTERM  */
2301         0,                  /* 16 SIGUSR1  */
2302         0,                  /* 17 SIGUSR2  */
2303         0,                  /* 18 */
2304         0,                  /* 19 */
2305         0,                  /* 20 SIGCHLD  */
2306         0,                  /* 21 SIGCONT  */
2307         0,                  /* 22 SIGSTOP  */
2308         0,                  /* 23 SIGTSTP  */
2309         0,                  /* 24 SIGTTIN  */
2310         0,                  /* 25 SIGTTOU  */
2311         0,                  /* 26 */
2312         0,                  /* 27 */
2313         0                   /* 28 SIGWINCH  */
2314     };
2315 
2316     static int initted = 0;
2317     if (!initted) {
2318         initted = 1;
2319         sig_code[16] = C$_SIGUSR1;
2320         sig_code[17] = C$_SIGUSR2;
2321         sig_code[20] = C$_SIGCHLD;
2322 #if __CRTL_VER >= 70300000
2323         sig_code[28] = C$_SIGWINCH;
2324 #endif
2325     }
2326 
2327     if (sig < _SIG_MIN) return 0;
2328     if (sig > _MY_SIG_MAX) return 0;
2329     return sig_code[sig];
2330 }
2331 
2332 unsigned int
2333 Perl_sig_to_vmscondition(int sig)
2334 {
2335 #ifdef SS$_DEBUG
2336     if (vms_debug_on_exception != 0)
2337 	lib$signal(SS$_DEBUG);
2338 #endif
2339     return Perl_sig_to_vmscondition_int(sig);
2340 }
2341 
2342 
2343 #define sys$sigprc SYS$SIGPRC
2344 #ifdef __cplusplus
2345 extern "C" {
2346 #endif
2347 int sys$sigprc(unsigned int *pidadr,
2348                struct dsc$descriptor_s *prcname,
2349                unsigned int code);
2350 #ifdef __cplusplus
2351 }
2352 #endif
2353 
2354 int
2355 Perl_my_kill(int pid, int sig)
2356 {
2357     int iss;
2358     unsigned int code;
2359 
2360      /* sig 0 means validate the PID */
2361     /*------------------------------*/
2362     if (sig == 0) {
2363 	const unsigned long int jpicode = JPI$_PID;
2364 	pid_t ret_pid;
2365 	int status;
2366         status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2367 	if ($VMS_STATUS_SUCCESS(status))
2368 	   return 0;
2369 	switch (status) {
2370         case SS$_NOSUCHNODE:
2371         case SS$_UNREACHABLE:
2372 	case SS$_NONEXPR:
2373 	   errno = ESRCH;
2374 	   break;
2375 	case SS$_NOPRIV:
2376 	   errno = EPERM;
2377 	   break;
2378 	default:
2379 	   errno = EVMSERR;
2380 	}
2381 	vaxc$errno=status;
2382 	return -1;
2383     }
2384 
2385     code = Perl_sig_to_vmscondition_int(sig);
2386 
2387     if (!code) {
2388 	SETERRNO(EINVAL, SS$_BADPARAM);
2389         return -1;
2390     }
2391 
2392     /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2393      * signals are to be sent to multiple processes.
2394      *  pid = 0 - all processes in group except ones that the system exempts
2395      *  pid = -1 - all processes except ones that the system exempts
2396      *  pid = -n - all processes in group (abs(n)) except ...
2397      * For now, just report as not supported.
2398      */
2399 
2400     if (pid <= 0) {
2401 	SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2402         return -1;
2403     }
2404 
2405     iss = sys$sigprc((unsigned int *)&pid,0,code);
2406     if (iss&1) return 0;
2407 
2408     switch (iss) {
2409       case SS$_NOPRIV:
2410         set_errno(EPERM);  break;
2411       case SS$_NONEXPR:
2412       case SS$_NOSUCHNODE:
2413       case SS$_UNREACHABLE:
2414         set_errno(ESRCH);  break;
2415       case SS$_INSFMEM:
2416         set_errno(ENOMEM); break;
2417       default:
2418         _ckvmssts_noperl(iss);
2419         set_errno(EVMSERR);
2420     }
2421     set_vaxc_errno(iss);
2422 
2423     return -1;
2424 }
2425 #endif
2426 
2427 /* Routine to convert a VMS status code to a UNIX status code.
2428 ** More tricky than it appears because of conflicting conventions with
2429 ** existing code.
2430 **
2431 ** VMS status codes are a bit mask, with the least significant bit set for
2432 ** success.
2433 **
2434 ** Special UNIX status of EVMSERR indicates that no translation is currently
2435 ** available, and programs should check the VMS status code.
2436 **
2437 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2438 ** decoding.
2439 */
2440 
2441 #ifndef C_FACILITY_NO
2442 #define C_FACILITY_NO 0x350000
2443 #endif
2444 #ifndef DCL_IVVERB
2445 #define DCL_IVVERB 0x38090
2446 #endif
2447 
2448 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2449 {
2450 int facility;
2451 int fac_sp;
2452 int msg_no;
2453 int msg_status;
2454 int unix_status;
2455 
2456   /* Assume the best or the worst */
2457   if (vms_status & STS$M_SUCCESS)
2458     unix_status = 0;
2459   else
2460     unix_status = EVMSERR;
2461 
2462   msg_status = vms_status & ~STS$M_CONTROL;
2463 
2464   facility = vms_status & STS$M_FAC_NO;
2465   fac_sp = vms_status & STS$M_FAC_SP;
2466   msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2467 
2468   if (((facility == 0) || (fac_sp == 0))  && (child_flag == 0)) {
2469     switch(msg_no) {
2470     case SS$_NORMAL:
2471 	unix_status = 0;
2472 	break;
2473     case SS$_ACCVIO:
2474 	unix_status = EFAULT;
2475 	break;
2476     case SS$_DEVOFFLINE:
2477 	unix_status = EBUSY;
2478 	break;
2479     case SS$_CLEARED:
2480 	unix_status = ENOTCONN;
2481 	break;
2482     case SS$_IVCHAN:
2483     case SS$_IVLOGNAM:
2484     case SS$_BADPARAM:
2485     case SS$_IVLOGTAB:
2486     case SS$_NOLOGNAM:
2487     case SS$_NOLOGTAB:
2488     case SS$_INVFILFOROP:
2489     case SS$_INVARG:
2490     case SS$_NOSUCHID:
2491     case SS$_IVIDENT:
2492 	unix_status = EINVAL;
2493 	break;
2494     case SS$_UNSUPPORTED:
2495 	unix_status = ENOTSUP;
2496 	break;
2497     case SS$_FILACCERR:
2498     case SS$_NOGRPPRV:
2499     case SS$_NOSYSPRV:
2500 	unix_status = EACCES;
2501 	break;
2502     case SS$_DEVICEFULL:
2503 	unix_status = ENOSPC;
2504 	break;
2505     case SS$_NOSUCHDEV:
2506 	unix_status = ENODEV;
2507 	break;
2508     case SS$_NOSUCHFILE:
2509     case SS$_NOSUCHOBJECT:
2510 	unix_status = ENOENT;
2511 	break;
2512     case SS$_ABORT:				    /* Fatal case */
2513     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2514     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2515 	unix_status = EINTR;
2516 	break;
2517     case SS$_BUFFEROVF:
2518 	unix_status = E2BIG;
2519 	break;
2520     case SS$_INSFMEM:
2521 	unix_status = ENOMEM;
2522 	break;
2523     case SS$_NOPRIV:
2524 	unix_status = EPERM;
2525 	break;
2526     case SS$_NOSUCHNODE:
2527     case SS$_UNREACHABLE:
2528 	unix_status = ESRCH;
2529 	break;
2530     case SS$_NONEXPR:
2531 	unix_status = ECHILD;
2532 	break;
2533     default:
2534 	if ((facility == 0) && (msg_no < 8)) {
2535 	  /* These are not real VMS status codes so assume that they are
2536           ** already UNIX status codes
2537 	  */
2538 	  unix_status = msg_no;
2539 	  break;
2540 	}
2541     }
2542   }
2543   else {
2544     /* Translate a POSIX exit code to a UNIX exit code */
2545     if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000))  {
2546 	unix_status = (msg_no & 0x07F8) >> 3;
2547     }
2548     else {
2549 
2550 	 /* Documented traditional behavior for handling VMS child exits */
2551 	/*--------------------------------------------------------------*/
2552 	if (child_flag != 0) {
2553 
2554 	     /* Success / Informational return 0 */
2555 	    /*----------------------------------*/
2556 	    if (msg_no & STS$K_SUCCESS)
2557 		return 0;
2558 
2559 	     /* Warning returns 1 */
2560 	    /*-------------------*/
2561 	    if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2562 	    	return 1;
2563 
2564 	     /* Everything else pass through the severity bits */
2565 	    /*------------------------------------------------*/
2566 	    return (msg_no & STS$M_SEVERITY);
2567 	}
2568 
2569 	 /* Normal VMS status to ERRNO mapping attempt */
2570 	/*--------------------------------------------*/
2571 	switch(msg_status) {
2572 	/* case RMS$_EOF: */ /* End of File */
2573 	case RMS$_FNF:	/* File Not Found */
2574 	case RMS$_DNF:	/* Dir Not Found */
2575 		unix_status = ENOENT;
2576 		break;
2577 	case RMS$_RNF:	/* Record Not Found */
2578 		unix_status = ESRCH;
2579 		break;
2580 	case RMS$_DIR:
2581 		unix_status = ENOTDIR;
2582 		break;
2583 	case RMS$_DEV:
2584 		unix_status = ENODEV;
2585 		break;
2586 	case RMS$_IFI:
2587 	case RMS$_FAC:
2588 	case RMS$_ISI:
2589 		unix_status = EBADF;
2590 		break;
2591 	case RMS$_FEX:
2592 		unix_status = EEXIST;
2593 		break;
2594 	case RMS$_SYN:
2595 	case RMS$_FNM:
2596 	case LIB$_INVSTRDES:
2597 	case LIB$_INVARG:
2598 	case LIB$_NOSUCHSYM:
2599 	case LIB$_INVSYMNAM:
2600 	case DCL_IVVERB:
2601 		unix_status = EINVAL;
2602 		break;
2603 	case CLI$_BUFOVF:
2604 	case RMS$_RTB:
2605 	case CLI$_TKNOVF:
2606 	case CLI$_RSLOVF:
2607 		unix_status = E2BIG;
2608 		break;
2609 	case RMS$_PRV:	/* No privilege */
2610 	case RMS$_ACC:	/* ACP file access failed */
2611 	case RMS$_WLK:	/* Device write locked */
2612 		unix_status = EACCES;
2613 		break;
2614 	case RMS$_MKD:  /* Failed to mark for delete */
2615 		unix_status = EPERM;
2616 		break;
2617 	/* case RMS$_NMF: */  /* No more files */
2618 	}
2619     }
2620   }
2621 
2622   return unix_status;
2623 }
2624 
2625 /* Try to guess at what VMS error status should go with a UNIX errno
2626  * value.  This is hard to do as there could be many possible VMS
2627  * error statuses that caused the errno value to be set.
2628  */
2629 
2630 int Perl_unix_status_to_vms(int unix_status)
2631 {
2632 int test_unix_status;
2633 
2634      /* Trivial cases first */
2635     /*---------------------*/
2636     if (unix_status == EVMSERR)
2637 	return vaxc$errno;
2638 
2639      /* Is vaxc$errno sane? */
2640     /*---------------------*/
2641     test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2642     if (test_unix_status == unix_status)
2643 	return vaxc$errno;
2644 
2645      /* If way out of range, must be VMS code already */
2646     /*-----------------------------------------------*/
2647     if (unix_status > EVMSERR)
2648 	return unix_status;
2649 
2650      /* If out of range, punt */
2651     /*-----------------------*/
2652     if (unix_status > __ERRNO_MAX)
2653 	return SS$_ABORT;
2654 
2655 
2656      /* Ok, now we have to do it the hard way. */
2657     /*----------------------------------------*/
2658     switch(unix_status) {
2659     case 0:	return SS$_NORMAL;
2660     case EPERM: return SS$_NOPRIV;
2661     case ENOENT: return SS$_NOSUCHOBJECT;
2662     case ESRCH: return SS$_UNREACHABLE;
2663     case EINTR: return SS$_ABORT;
2664     /* case EIO: */
2665     /* case ENXIO:  */
2666     case E2BIG: return SS$_BUFFEROVF;
2667     /* case ENOEXEC */
2668     case EBADF: return RMS$_IFI;
2669     case ECHILD: return SS$_NONEXPR;
2670     /* case EAGAIN */
2671     case ENOMEM: return SS$_INSFMEM;
2672     case EACCES: return SS$_FILACCERR;
2673     case EFAULT: return SS$_ACCVIO;
2674     /* case ENOTBLK */
2675     case EBUSY: return SS$_DEVOFFLINE;
2676     case EEXIST: return RMS$_FEX;
2677     /* case EXDEV */
2678     case ENODEV: return SS$_NOSUCHDEV;
2679     case ENOTDIR: return RMS$_DIR;
2680     /* case EISDIR */
2681     case EINVAL: return SS$_INVARG;
2682     /* case ENFILE */
2683     /* case EMFILE */
2684     /* case ENOTTY */
2685     /* case ETXTBSY */
2686     /* case EFBIG */
2687     case ENOSPC: return SS$_DEVICEFULL;
2688     case ESPIPE: return LIB$_INVARG;
2689     /* case EROFS: */
2690     /* case EMLINK: */
2691     /* case EPIPE: */
2692     /* case EDOM */
2693     case ERANGE: return LIB$_INVARG;
2694     /* case EWOULDBLOCK */
2695     /* case EINPROGRESS */
2696     /* case EALREADY */
2697     /* case ENOTSOCK */
2698     /* case EDESTADDRREQ */
2699     /* case EMSGSIZE */
2700     /* case EPROTOTYPE */
2701     /* case ENOPROTOOPT */
2702     /* case EPROTONOSUPPORT */
2703     /* case ESOCKTNOSUPPORT */
2704     /* case EOPNOTSUPP */
2705     /* case EPFNOSUPPORT */
2706     /* case EAFNOSUPPORT */
2707     /* case EADDRINUSE */
2708     /* case EADDRNOTAVAIL */
2709     /* case ENETDOWN */
2710     /* case ENETUNREACH */
2711     /* case ENETRESET */
2712     /* case ECONNABORTED */
2713     /* case ECONNRESET */
2714     /* case ENOBUFS */
2715     /* case EISCONN */
2716     case ENOTCONN: return SS$_CLEARED;
2717     /* case ESHUTDOWN */
2718     /* case ETOOMANYREFS */
2719     /* case ETIMEDOUT */
2720     /* case ECONNREFUSED */
2721     /* case ELOOP */
2722     /* case ENAMETOOLONG */
2723     /* case EHOSTDOWN */
2724     /* case EHOSTUNREACH */
2725     /* case ENOTEMPTY */
2726     /* case EPROCLIM */
2727     /* case EUSERS  */
2728     /* case EDQUOT  */
2729     /* case ENOMSG  */
2730     /* case EIDRM */
2731     /* case EALIGN */
2732     /* case ESTALE */
2733     /* case EREMOTE */
2734     /* case ENOLCK */
2735     /* case ENOSYS */
2736     /* case EFTYPE */
2737     /* case ECANCELED */
2738     /* case EFAIL */
2739     /* case EINPROG */
2740     case ENOTSUP:
2741 	return SS$_UNSUPPORTED;
2742     /* case EDEADLK */
2743     /* case ENWAIT */
2744     /* case EILSEQ */
2745     /* case EBADCAT */
2746     /* case EBADMSG */
2747     /* case EABANDONED */
2748     default:
2749 	return SS$_ABORT; /* punt */
2750     }
2751 }
2752 
2753 
2754 /* default piping mailbox size */
2755 #ifdef __VAX
2756 #  define PERL_BUFSIZ        512
2757 #else
2758 #  define PERL_BUFSIZ        8192
2759 #endif
2760 
2761 
2762 static void
2763 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2764 {
2765   unsigned long int mbxbufsiz;
2766   static unsigned long int syssize = 0;
2767   unsigned long int dviitm = DVI$_DEVNAM;
2768   char csize[LNM$C_NAMLENGTH+1];
2769   int sts;
2770 
2771   if (!syssize) {
2772     unsigned long syiitm = SYI$_MAXBUF;
2773     /*
2774      * Get the SYSGEN parameter MAXBUF
2775      *
2776      * If the logical 'PERL_MBX_SIZE' is defined
2777      * use the value of the logical instead of PERL_BUFSIZ, but
2778      * keep the size between 128 and MAXBUF.
2779      *
2780      */
2781     _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2782   }
2783 
2784   if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2785       mbxbufsiz = atoi(csize);
2786   } else {
2787       mbxbufsiz = PERL_BUFSIZ;
2788   }
2789   if (mbxbufsiz < 128) mbxbufsiz = 128;
2790   if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2791 
2792   _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2793 
2794   sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2795   _ckvmssts_noperl(sts);
2796   namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2797 
2798 }  /* end of create_mbx() */
2799 
2800 
2801 /*{{{  my_popen and my_pclose*/
2802 
2803 typedef struct _iosb           IOSB;
2804 typedef struct _iosb*         pIOSB;
2805 typedef struct _pipe           Pipe;
2806 typedef struct _pipe*         pPipe;
2807 typedef struct pipe_details    Info;
2808 typedef struct pipe_details*  pInfo;
2809 typedef struct _srqp            RQE;
2810 typedef struct _srqp*          pRQE;
2811 typedef struct _tochildbuf      CBuf;
2812 typedef struct _tochildbuf*    pCBuf;
2813 
2814 struct _iosb {
2815     unsigned short status;
2816     unsigned short count;
2817     unsigned long  dvispec;
2818 };
2819 
2820 #pragma member_alignment save
2821 #pragma nomember_alignment quadword
2822 struct _srqp {          /* VMS self-relative queue entry */
2823     unsigned long qptr[2];
2824 };
2825 #pragma member_alignment restore
2826 static RQE  RQE_ZERO = {0,0};
2827 
2828 struct _tochildbuf {
2829     RQE             q;
2830     int             eof;
2831     unsigned short  size;
2832     char            *buf;
2833 };
2834 
2835 struct _pipe {
2836     RQE            free;
2837     RQE            wait;
2838     int            fd_out;
2839     unsigned short chan_in;
2840     unsigned short chan_out;
2841     char          *buf;
2842     unsigned int   bufsize;
2843     IOSB           iosb;
2844     IOSB           iosb2;
2845     int           *pipe_done;
2846     int            retry;
2847     int            type;
2848     int            shut_on_empty;
2849     int            need_wake;
2850     pPipe         *home;
2851     pInfo          info;
2852     pCBuf          curr;
2853     pCBuf          curr2;
2854 #if defined(PERL_IMPLICIT_CONTEXT)
2855     void	    *thx;	    /* Either a thread or an interpreter */
2856                                     /* pointer, depending on how we're built */
2857 #endif
2858 };
2859 
2860 
2861 struct pipe_details
2862 {
2863     pInfo           next;
2864     PerlIO *fp;  /* file pointer to pipe mailbox */
2865     int useFILE; /* using stdio, not perlio */
2866     int pid;   /* PID of subprocess */
2867     int mode;  /* == 'r' if pipe open for reading */
2868     int done;  /* subprocess has completed */
2869     int waiting; /* waiting for completion/closure */
2870     int             closing;        /* my_pclose is closing this pipe */
2871     unsigned long   completion;     /* termination status of subprocess */
2872     pPipe           in;             /* pipe in to sub */
2873     pPipe           out;            /* pipe out of sub */
2874     pPipe           err;            /* pipe of sub's sys$error */
2875     int             in_done;        /* true when in pipe finished */
2876     int             out_done;
2877     int             err_done;
2878     unsigned short  xchan;	    /* channel to debug xterm */
2879     unsigned short  xchan_valid;    /* channel is assigned */
2880 };
2881 
2882 struct exit_control_block
2883 {
2884     struct exit_control_block *flink;
2885     unsigned long int (*exit_routine)(void);
2886     unsigned long int arg_count;
2887     unsigned long int *status_address;
2888     unsigned long int exit_status;
2889 };
2890 
2891 typedef struct _closed_pipes    Xpipe;
2892 typedef struct _closed_pipes*  pXpipe;
2893 
2894 struct _closed_pipes {
2895     int             pid;            /* PID of subprocess */
2896     unsigned long   completion;     /* termination status of subprocess */
2897 };
2898 #define NKEEPCLOSED 50
2899 static Xpipe closed_list[NKEEPCLOSED];
2900 static int   closed_index = 0;
2901 static int   closed_num = 0;
2902 
2903 #define RETRY_DELAY     "0 ::0.20"
2904 #define MAX_RETRY              50
2905 
2906 static int pipe_ef = 0;          /* first call to safe_popen inits these*/
2907 static unsigned long mypid;
2908 static unsigned long delaytime[2];
2909 
2910 static pInfo open_pipes = NULL;
2911 static $DESCRIPTOR(nl_desc, "NL:");
2912 
2913 #define PIPE_COMPLETION_WAIT    30  /* seconds, for EOF/FORCEX wait */
2914 
2915 
2916 
2917 static unsigned long int
2918 pipe_exit_routine(void)
2919 {
2920     pInfo info;
2921     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2922     int sts, did_stuff, j;
2923 
2924    /*
2925     * Flush any pending i/o, but since we are in process run-down, be
2926     * careful about referencing PerlIO structures that may already have
2927     * been deallocated.  We may not even have an interpreter anymore.
2928     */
2929     info = open_pipes;
2930     while (info) {
2931         if (info->fp) {
2932 #if defined(PERL_IMPLICIT_CONTEXT)
2933            /* We need to use the Perl context of the thread that created */
2934            /* the pipe. */
2935            pTHX;
2936            if (info->err)
2937                aTHX = info->err->thx;
2938            else if (info->out)
2939                aTHX = info->out->thx;
2940            else if (info->in)
2941                aTHX = info->in->thx;
2942 #endif
2943            if (!info->useFILE
2944 #if defined(USE_ITHREADS)
2945              && my_perl
2946 #endif
2947 #ifdef USE_PERLIO
2948              && PL_perlio_fd_refcnt
2949 #endif
2950               )
2951                PerlIO_flush(info->fp);
2952            else
2953                fflush((FILE *)info->fp);
2954         }
2955         info = info->next;
2956     }
2957 
2958     /*
2959      next we try sending an EOF...ignore if doesn't work, make sure we
2960      don't hang
2961     */
2962     did_stuff = 0;
2963     info = open_pipes;
2964 
2965     while (info) {
2966       _ckvmssts_noperl(sys$setast(0));
2967       if (info->in && !info->in->shut_on_empty) {
2968         _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2969                                  0, 0, 0, 0, 0, 0));
2970         info->waiting = 1;
2971         did_stuff = 1;
2972       }
2973       _ckvmssts_noperl(sys$setast(1));
2974       info = info->next;
2975     }
2976 
2977     /* wait for EOF to have effect, up to ~ 30 sec [default] */
2978 
2979     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2980         int nwait = 0;
2981 
2982         info = open_pipes;
2983         while (info) {
2984           _ckvmssts_noperl(sys$setast(0));
2985           if (info->waiting && info->done)
2986                 info->waiting = 0;
2987           nwait += info->waiting;
2988           _ckvmssts_noperl(sys$setast(1));
2989           info = info->next;
2990         }
2991         if (!nwait) break;
2992         sleep(1);
2993     }
2994 
2995     did_stuff = 0;
2996     info = open_pipes;
2997     while (info) {
2998       _ckvmssts_noperl(sys$setast(0));
2999       if (!info->done) { /* Tap them gently on the shoulder . . .*/
3000         sts = sys$forcex(&info->pid,0,&abort);
3001         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3002         did_stuff = 1;
3003       }
3004       _ckvmssts_noperl(sys$setast(1));
3005       info = info->next;
3006     }
3007 
3008     /* again, wait for effect */
3009 
3010     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3011         int nwait = 0;
3012 
3013         info = open_pipes;
3014         while (info) {
3015           _ckvmssts_noperl(sys$setast(0));
3016           if (info->waiting && info->done)
3017                 info->waiting = 0;
3018           nwait += info->waiting;
3019           _ckvmssts_noperl(sys$setast(1));
3020           info = info->next;
3021         }
3022         if (!nwait) break;
3023         sleep(1);
3024     }
3025 
3026     info = open_pipes;
3027     while (info) {
3028       _ckvmssts_noperl(sys$setast(0));
3029       if (!info->done) {  /* We tried to be nice . . . */
3030         sts = sys$delprc(&info->pid,0);
3031         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3032         info->done = 1;  /* sys$delprc is as done as we're going to get. */
3033       }
3034       _ckvmssts_noperl(sys$setast(1));
3035       info = info->next;
3036     }
3037 
3038     while(open_pipes) {
3039 
3040 #if defined(PERL_IMPLICIT_CONTEXT)
3041       /* We need to use the Perl context of the thread that created */
3042       /* the pipe. */
3043       pTHX;
3044       if (open_pipes->err)
3045           aTHX = open_pipes->err->thx;
3046       else if (open_pipes->out)
3047           aTHX = open_pipes->out->thx;
3048       else if (open_pipes->in)
3049           aTHX = open_pipes->in->thx;
3050 #endif
3051       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3052       else if (!(sts & 1)) retsts = sts;
3053     }
3054     return retsts;
3055 }
3056 
3057 static struct exit_control_block pipe_exitblock =
3058        {(struct exit_control_block *) 0,
3059         pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3060 
3061 static void pipe_mbxtofd_ast(pPipe p);
3062 static void pipe_tochild1_ast(pPipe p);
3063 static void pipe_tochild2_ast(pPipe p);
3064 
3065 static void
3066 popen_completion_ast(pInfo info)
3067 {
3068   pInfo i = open_pipes;
3069   int iss;
3070 
3071   info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3072   closed_list[closed_index].pid = info->pid;
3073   closed_list[closed_index].completion = info->completion;
3074   closed_index++;
3075   if (closed_index == NKEEPCLOSED)
3076     closed_index = 0;
3077   closed_num++;
3078 
3079   while (i) {
3080     if (i == info) break;
3081     i = i->next;
3082   }
3083   if (!i) return;       /* unlinked, probably freed too */
3084 
3085   info->done = TRUE;
3086 
3087 /*
3088     Writing to subprocess ...
3089             if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3090 
3091             chan_out may be waiting for "done" flag, or hung waiting
3092             for i/o completion to child...cancel the i/o.  This will
3093             put it into "snarf mode" (done but no EOF yet) that discards
3094             input.
3095 
3096     Output from subprocess (stdout, stderr) needs to be flushed and
3097     shut down.   We try sending an EOF, but if the mbx is full the pipe
3098     routine should still catch the "shut_on_empty" flag, telling it to
3099     use immediate-style reads so that "mbx empty" -> EOF.
3100 
3101 
3102 */
3103   if (info->in && !info->in_done) {               /* only for mode=w */
3104         if (info->in->shut_on_empty && info->in->need_wake) {
3105             info->in->need_wake = FALSE;
3106             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3107         } else {
3108             _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3109         }
3110   }
3111 
3112   if (info->out && !info->out_done) {             /* were we also piping output? */
3113       info->out->shut_on_empty = TRUE;
3114       iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3115       if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3116       _ckvmssts_noperl(iss);
3117   }
3118 
3119   if (info->err && !info->err_done) {        /* we were piping stderr */
3120         info->err->shut_on_empty = TRUE;
3121         iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3122         if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3123         _ckvmssts_noperl(iss);
3124   }
3125   _ckvmssts_noperl(sys$setef(pipe_ef));
3126 
3127 }
3128 
3129 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3130 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3131 static void pipe_infromchild_ast(pPipe p);
3132 
3133 /*
3134     I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3135     inside an AST routine without worrying about reentrancy and which Perl
3136     memory allocator is being used.
3137 
3138     We read data and queue up the buffers, then spit them out one at a
3139     time to the output mailbox when the output mailbox is ready for one.
3140 
3141 */
3142 #define INITIAL_TOCHILDQUEUE  2
3143 
3144 static pPipe
3145 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3146 {
3147     pPipe p;
3148     pCBuf b;
3149     char mbx1[64], mbx2[64];
3150     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3151                                       DSC$K_CLASS_S, mbx1},
3152                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3153                                       DSC$K_CLASS_S, mbx2};
3154     unsigned int dviitm = DVI$_DEVBUFSIZ;
3155     int j, n;
3156 
3157     n = sizeof(Pipe);
3158     _ckvmssts_noperl(lib$get_vm(&n, &p));
3159 
3160     create_mbx(&p->chan_in , &d_mbx1);
3161     create_mbx(&p->chan_out, &d_mbx2);
3162     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3163 
3164     p->buf           = 0;
3165     p->shut_on_empty = FALSE;
3166     p->need_wake     = FALSE;
3167     p->type          = 0;
3168     p->retry         = 0;
3169     p->iosb.status   = SS$_NORMAL;
3170     p->iosb2.status  = SS$_NORMAL;
3171     p->free          = RQE_ZERO;
3172     p->wait          = RQE_ZERO;
3173     p->curr          = 0;
3174     p->curr2         = 0;
3175     p->info          = 0;
3176 #ifdef PERL_IMPLICIT_CONTEXT
3177     p->thx	     = aTHX;
3178 #endif
3179 
3180     n = sizeof(CBuf) + p->bufsize;
3181 
3182     for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3183         _ckvmssts_noperl(lib$get_vm(&n, &b));
3184         b->buf = (char *) b + sizeof(CBuf);
3185         _ckvmssts_noperl(lib$insqhi(b, &p->free));
3186     }
3187 
3188     pipe_tochild2_ast(p);
3189     pipe_tochild1_ast(p);
3190     strcpy(wmbx, mbx1);
3191     strcpy(rmbx, mbx2);
3192     return p;
3193 }
3194 
3195 /*  reads the MBX Perl is writing, and queues */
3196 
3197 static void
3198 pipe_tochild1_ast(pPipe p)
3199 {
3200     pCBuf b = p->curr;
3201     int iss = p->iosb.status;
3202     int eof = (iss == SS$_ENDOFFILE);
3203     int sts;
3204 #ifdef PERL_IMPLICIT_CONTEXT
3205     pTHX = p->thx;
3206 #endif
3207 
3208     if (p->retry) {
3209         if (eof) {
3210             p->shut_on_empty = TRUE;
3211             b->eof     = TRUE;
3212             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3213         } else  {
3214             _ckvmssts_noperl(iss);
3215         }
3216 
3217         b->eof  = eof;
3218         b->size = p->iosb.count;
3219         _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3220         if (p->need_wake) {
3221             p->need_wake = FALSE;
3222             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3223         }
3224     } else {
3225         p->retry = 1;   /* initial call */
3226     }
3227 
3228     if (eof) {                  /* flush the free queue, return when done */
3229         int n = sizeof(CBuf) + p->bufsize;
3230         while (1) {
3231             iss = lib$remqti(&p->free, &b);
3232             if (iss == LIB$_QUEWASEMP) return;
3233             _ckvmssts_noperl(iss);
3234             _ckvmssts_noperl(lib$free_vm(&n, &b));
3235         }
3236     }
3237 
3238     iss = lib$remqti(&p->free, &b);
3239     if (iss == LIB$_QUEWASEMP) {
3240         int n = sizeof(CBuf) + p->bufsize;
3241         _ckvmssts_noperl(lib$get_vm(&n, &b));
3242         b->buf = (char *) b + sizeof(CBuf);
3243     } else {
3244        _ckvmssts_noperl(iss);
3245     }
3246 
3247     p->curr = b;
3248     iss = sys$qio(0,p->chan_in,
3249              IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3250              &p->iosb,
3251              pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3252     if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3253     _ckvmssts_noperl(iss);
3254 }
3255 
3256 
3257 /* writes queued buffers to output, waits for each to complete before
3258    doing the next */
3259 
3260 static void
3261 pipe_tochild2_ast(pPipe p)
3262 {
3263     pCBuf b = p->curr2;
3264     int iss = p->iosb2.status;
3265     int n = sizeof(CBuf) + p->bufsize;
3266     int done = (p->info && p->info->done) ||
3267               iss == SS$_CANCEL || iss == SS$_ABORT;
3268 #if defined(PERL_IMPLICIT_CONTEXT)
3269     pTHX = p->thx;
3270 #endif
3271 
3272     do {
3273         if (p->type) {         /* type=1 has old buffer, dispose */
3274             if (p->shut_on_empty) {
3275                 _ckvmssts_noperl(lib$free_vm(&n, &b));
3276             } else {
3277                 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3278             }
3279             p->type = 0;
3280         }
3281 
3282         iss = lib$remqti(&p->wait, &b);
3283         if (iss == LIB$_QUEWASEMP) {
3284             if (p->shut_on_empty) {
3285                 if (done) {
3286                     _ckvmssts_noperl(sys$dassgn(p->chan_out));
3287                     *p->pipe_done = TRUE;
3288                     _ckvmssts_noperl(sys$setef(pipe_ef));
3289                 } else {
3290                     _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3291                         &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3292                 }
3293                 return;
3294             }
3295             p->need_wake = TRUE;
3296             return;
3297         }
3298         _ckvmssts_noperl(iss);
3299         p->type = 1;
3300     } while (done);
3301 
3302 
3303     p->curr2 = b;
3304     if (b->eof) {
3305         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3306             &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3307     } else {
3308         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3309             &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3310     }
3311 
3312     return;
3313 
3314 }
3315 
3316 
3317 static pPipe
3318 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3319 {
3320     pPipe p;
3321     char mbx1[64], mbx2[64];
3322     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3323                                       DSC$K_CLASS_S, mbx1},
3324                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3325                                       DSC$K_CLASS_S, mbx2};
3326     unsigned int dviitm = DVI$_DEVBUFSIZ;
3327 
3328     int n = sizeof(Pipe);
3329     _ckvmssts_noperl(lib$get_vm(&n, &p));
3330     create_mbx(&p->chan_in , &d_mbx1);
3331     create_mbx(&p->chan_out, &d_mbx2);
3332 
3333     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3334     n = p->bufsize * sizeof(char);
3335     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3336     p->shut_on_empty = FALSE;
3337     p->info   = 0;
3338     p->type   = 0;
3339     p->iosb.status = SS$_NORMAL;
3340 #if defined(PERL_IMPLICIT_CONTEXT)
3341     p->thx = aTHX;
3342 #endif
3343     pipe_infromchild_ast(p);
3344 
3345     strcpy(wmbx, mbx1);
3346     strcpy(rmbx, mbx2);
3347     return p;
3348 }
3349 
3350 static void
3351 pipe_infromchild_ast(pPipe p)
3352 {
3353     int iss = p->iosb.status;
3354     int eof = (iss == SS$_ENDOFFILE);
3355     int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3356     int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3357 #if defined(PERL_IMPLICIT_CONTEXT)
3358     pTHX = p->thx;
3359 #endif
3360 
3361     if (p->info && p->info->closing && p->chan_out)  {           /* output shutdown */
3362         _ckvmssts_noperl(sys$dassgn(p->chan_out));
3363         p->chan_out = 0;
3364     }
3365 
3366     /* read completed:
3367             input shutdown if EOF from self (done or shut_on_empty)
3368             output shutdown if closing flag set (my_pclose)
3369             send data/eof from child or eof from self
3370             otherwise, re-read (snarf of data from child)
3371     */
3372 
3373     if (p->type == 1) {
3374         p->type = 0;
3375         if (myeof && p->chan_in) {                  /* input shutdown */
3376             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3377             p->chan_in = 0;
3378         }
3379 
3380         if (p->chan_out) {
3381             if (myeof || kideof) {      /* pass EOF to parent */
3382                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3383                                          pipe_infromchild_ast, p,
3384                                          0, 0, 0, 0, 0, 0));
3385                 return;
3386             } else if (eof) {       /* eat EOF --- fall through to read*/
3387 
3388             } else {                /* transmit data */
3389                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3390                                          pipe_infromchild_ast,p,
3391                                          p->buf, p->iosb.count, 0, 0, 0, 0));
3392                 return;
3393             }
3394         }
3395     }
3396 
3397     /*  everything shut? flag as done */
3398 
3399     if (!p->chan_in && !p->chan_out) {
3400         *p->pipe_done = TRUE;
3401         _ckvmssts_noperl(sys$setef(pipe_ef));
3402         return;
3403     }
3404 
3405     /* write completed (or read, if snarfing from child)
3406             if still have input active,
3407                queue read...immediate mode if shut_on_empty so we get EOF if empty
3408             otherwise,
3409                check if Perl reading, generate EOFs as needed
3410     */
3411 
3412     if (p->type == 0) {
3413         p->type = 1;
3414         if (p->chan_in) {
3415             iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3416                           pipe_infromchild_ast,p,
3417                           p->buf, p->bufsize, 0, 0, 0, 0);
3418             if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3419             _ckvmssts_noperl(iss);
3420         } else {           /* send EOFs for extra reads */
3421             p->iosb.status = SS$_ENDOFFILE;
3422             p->iosb.dvispec = 0;
3423             _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3424                                      0, 0, 0,
3425                                      pipe_infromchild_ast, p, 0, 0, 0, 0));
3426         }
3427     }
3428 }
3429 
3430 static pPipe
3431 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3432 {
3433     pPipe p;
3434     char mbx[64];
3435     unsigned long dviitm = DVI$_DEVBUFSIZ;
3436     struct stat s;
3437     struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3438                                       DSC$K_CLASS_S, mbx};
3439     int n = sizeof(Pipe);
3440 
3441     /* things like terminals and mbx's don't need this filter */
3442     if (fd && fstat(fd,&s) == 0) {
3443         unsigned long devchar;
3444 	char device[65];
3445 	unsigned short dev_len;
3446 	struct dsc$descriptor_s d_dev;
3447 	char * cptr;
3448 	struct item_list_3 items[3];
3449 	int status;
3450 	unsigned short dvi_iosb[4];
3451 
3452 	cptr = getname(fd, out, 1);
3453 	if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3454 	d_dev.dsc$a_pointer = out;
3455 	d_dev.dsc$w_length = strlen(out);
3456 	d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3457 	d_dev.dsc$b_class = DSC$K_CLASS_S;
3458 
3459 	items[0].len = 4;
3460 	items[0].code = DVI$_DEVCHAR;
3461 	items[0].bufadr = &devchar;
3462 	items[0].retadr = NULL;
3463 	items[1].len = 64;
3464 	items[1].code = DVI$_FULLDEVNAM;
3465 	items[1].bufadr = device;
3466 	items[1].retadr = &dev_len;
3467 	items[2].len = 0;
3468 	items[2].code = 0;
3469 
3470 	status = sys$getdviw
3471 	        (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3472 	_ckvmssts_noperl(status);
3473 	if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3474 	    device[dev_len] = 0;
3475 
3476 	    if (!(devchar & DEV$M_DIR)) {
3477 		strcpy(out, device);
3478 		return 0;
3479 	    }
3480 	}
3481     }
3482 
3483     _ckvmssts_noperl(lib$get_vm(&n, &p));
3484     p->fd_out = dup(fd);
3485     create_mbx(&p->chan_in, &d_mbx);
3486     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3487     n = (p->bufsize+1) * sizeof(char);
3488     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3489     p->shut_on_empty = FALSE;
3490     p->retry = 0;
3491     p->info  = 0;
3492     strcpy(out, mbx);
3493 
3494     _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3495                              pipe_mbxtofd_ast, p,
3496                              p->buf, p->bufsize, 0, 0, 0, 0));
3497 
3498     return p;
3499 }
3500 
3501 static void
3502 pipe_mbxtofd_ast(pPipe p)
3503 {
3504     int iss = p->iosb.status;
3505     int done = p->info->done;
3506     int iss2;
3507     int eof = (iss == SS$_ENDOFFILE);
3508     int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3509     int err = !(iss&1) && !eof;
3510 #if defined(PERL_IMPLICIT_CONTEXT)
3511     pTHX = p->thx;
3512 #endif
3513 
3514     if (done && myeof) {               /* end piping */
3515         close(p->fd_out);
3516         sys$dassgn(p->chan_in);
3517         *p->pipe_done = TRUE;
3518         _ckvmssts_noperl(sys$setef(pipe_ef));
3519         return;
3520     }
3521 
3522     if (!err && !eof) {             /* good data to send to file */
3523         p->buf[p->iosb.count] = '\n';
3524         iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3525         if (iss2 < 0) {
3526             p->retry++;
3527             if (p->retry < MAX_RETRY) {
3528                 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3529                 return;
3530             }
3531         }
3532         p->retry = 0;
3533     } else if (err) {
3534         _ckvmssts_noperl(iss);
3535     }
3536 
3537 
3538     iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3539           pipe_mbxtofd_ast, p,
3540           p->buf, p->bufsize, 0, 0, 0, 0);
3541     if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3542     _ckvmssts_noperl(iss);
3543 }
3544 
3545 
3546 typedef struct _pipeloc     PLOC;
3547 typedef struct _pipeloc*   pPLOC;
3548 
3549 struct _pipeloc {
3550     pPLOC   next;
3551     char    dir[NAM$C_MAXRSS+1];
3552 };
3553 static pPLOC  head_PLOC = 0;
3554 
3555 void
3556 free_pipelocs(pTHX_ void *head)
3557 {
3558     pPLOC p, pnext;
3559     pPLOC *pHead = (pPLOC *)head;
3560 
3561     p = *pHead;
3562     while (p) {
3563         pnext = p->next;
3564         PerlMem_free(p);
3565         p = pnext;
3566     }
3567     *pHead = 0;
3568 }
3569 
3570 static void
3571 store_pipelocs(pTHX)
3572 {
3573     int    i;
3574     pPLOC  p;
3575     AV    *av = 0;
3576     SV    *dirsv;
3577     char  *dir, *x;
3578     char  *unixdir;
3579     char  temp[NAM$C_MAXRSS+1];
3580     STRLEN n_a;
3581 
3582     if (head_PLOC)
3583         free_pipelocs(aTHX_ &head_PLOC);
3584 
3585 /*  the . directory from @INC comes last */
3586 
3587     p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3588     if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3589     p->next = head_PLOC;
3590     head_PLOC = p;
3591     strcpy(p->dir,"./");
3592 
3593 /*  get the directory from $^X */
3594 
3595     unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3596     if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3597 
3598 #ifdef PERL_IMPLICIT_CONTEXT
3599     if (aTHX && PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3600 #else
3601     if (PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3602 #endif
3603         my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3604         x = strrchr(temp,']');
3605 	if (x == NULL) {
3606 	x = strrchr(temp,'>');
3607 	  if (x == NULL) {
3608 	    /* It could be a UNIX path */
3609 	    x = strrchr(temp,'/');
3610 	  }
3611 	}
3612 	if (x)
3613 	  x[1] = '\0';
3614 	else {
3615 	  /* Got a bare name, so use default directory */
3616 	  temp[0] = '.';
3617 	  temp[1] = '\0';
3618 	}
3619 
3620         if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3621             p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3622 	    if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3623             p->next = head_PLOC;
3624             head_PLOC = p;
3625             my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3626 	}
3627     }
3628 
3629 /*  reverse order of @INC entries, skip "." since entered above */
3630 
3631 #ifdef PERL_IMPLICIT_CONTEXT
3632     if (aTHX)
3633 #endif
3634     if (PL_incgv) av = GvAVn(PL_incgv);
3635 
3636     for (i = 0; av && i <= AvFILL(av); i++) {
3637         dirsv = *av_fetch(av,i,TRUE);
3638 
3639         if (SvROK(dirsv)) continue;
3640         dir = SvPVx(dirsv,n_a);
3641         if (strcmp(dir,".") == 0) continue;
3642         if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3643             continue;
3644 
3645         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3646         p->next = head_PLOC;
3647         head_PLOC = p;
3648         my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3649     }
3650 
3651 /* most likely spot (ARCHLIB) put first in the list */
3652 
3653 #ifdef ARCHLIB_EXP
3654     if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3655         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3656 	if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3657         p->next = head_PLOC;
3658         head_PLOC = p;
3659         my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3660     }
3661 #endif
3662     PerlMem_free(unixdir);
3663 }
3664 
3665 static I32
3666 Perl_cando_by_name_int
3667    (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3668 #if !defined(PERL_IMPLICIT_CONTEXT)
3669 #define cando_by_name_int		Perl_cando_by_name_int
3670 #else
3671 #define cando_by_name_int(a,b,c,d)	Perl_cando_by_name_int(aTHX_ a,b,c,d)
3672 #endif
3673 
3674 static char *
3675 find_vmspipe(pTHX)
3676 {
3677     static int   vmspipe_file_status = 0;
3678     static char  vmspipe_file[NAM$C_MAXRSS+1];
3679 
3680     /* already found? Check and use ... need read+execute permission */
3681 
3682     if (vmspipe_file_status == 1) {
3683         if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3684          && cando_by_name_int
3685 	   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3686             return vmspipe_file;
3687         }
3688         vmspipe_file_status = 0;
3689     }
3690 
3691     /* scan through stored @INC, $^X */
3692 
3693     if (vmspipe_file_status == 0) {
3694         char file[NAM$C_MAXRSS+1];
3695         pPLOC  p = head_PLOC;
3696 
3697         while (p) {
3698 	    char * exp_res;
3699 	    int dirlen;
3700 	    dirlen = my_strlcpy(file, p->dir, sizeof(file));
3701             my_strlcat(file, "vmspipe.com", sizeof(file));
3702             p = p->next;
3703 
3704             exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3705             if (!exp_res) continue;
3706 
3707             if (cando_by_name_int
3708 		(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3709              && cando_by_name_int
3710 		   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3711                 vmspipe_file_status = 1;
3712                 return vmspipe_file;
3713             }
3714         }
3715         vmspipe_file_status = -1;   /* failed, use tempfiles */
3716     }
3717 
3718     return 0;
3719 }
3720 
3721 static FILE *
3722 vmspipe_tempfile(pTHX)
3723 {
3724     char file[NAM$C_MAXRSS+1];
3725     FILE *fp;
3726     static int index = 0;
3727     Stat_t s0, s1;
3728     int cmp_result;
3729 
3730     /* create a tempfile */
3731 
3732     /* we can't go from   W, shr=get to  R, shr=get without
3733        an intermediate vulnerable state, so don't bother trying...
3734 
3735        and lib$spawn doesn't shr=put, so have to close the write
3736 
3737        So... match up the creation date/time and the FID to
3738        make sure we're dealing with the same file
3739 
3740     */
3741 
3742     index++;
3743     if (!decc_filename_unix_only) {
3744       sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3745       fp = fopen(file,"w");
3746       if (!fp) {
3747         sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3748         fp = fopen(file,"w");
3749         if (!fp) {
3750             sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3751             fp = fopen(file,"w");
3752 	}
3753       }
3754      }
3755      else {
3756       sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3757       fp = fopen(file,"w");
3758       if (!fp) {
3759 	sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3760 	fp = fopen(file,"w");
3761 	if (!fp) {
3762 	  sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3763 	  fp = fopen(file,"w");
3764 	}
3765       }
3766     }
3767     if (!fp) return 0;  /* we're hosed */
3768 
3769     fprintf(fp,"$! 'f$verify(0)'\n");
3770     fprintf(fp,"$!  ---  protect against nonstandard definitions ---\n");
3771     fprintf(fp,"$ perl_cfile  = f$environment(\"procedure\")\n");
3772     fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3773     fprintf(fp,"$ perl_on     = \"set noon\"\n");
3774     fprintf(fp,"$ perl_exit   = \"exit\"\n");
3775     fprintf(fp,"$ perl_del    = \"delete\"\n");
3776     fprintf(fp,"$ pif         = \"if\"\n");
3777     fprintf(fp,"$!  --- define i/o redirection (sys$output set by lib$spawn)\n");
3778     fprintf(fp,"$ pif perl_popen_in  .nes. \"\" then perl_define/user/name_attributes=confine sys$input  'perl_popen_in'\n");
3779     fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error  'perl_popen_err'\n");
3780     fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define      sys$output 'perl_popen_out'\n");
3781     fprintf(fp,"$!  --- build command line to get max possible length\n");
3782     fprintf(fp,"$c=perl_popen_cmd0\n");
3783     fprintf(fp,"$c=c+perl_popen_cmd1\n");
3784     fprintf(fp,"$c=c+perl_popen_cmd2\n");
3785     fprintf(fp,"$x=perl_popen_cmd3\n");
3786     fprintf(fp,"$c=c+x\n");
3787     fprintf(fp,"$ perl_on\n");
3788     fprintf(fp,"$ 'c'\n");
3789     fprintf(fp,"$ perl_status = $STATUS\n");
3790     fprintf(fp,"$ perl_del  'perl_cfile'\n");
3791     fprintf(fp,"$ perl_exit 'perl_status'\n");
3792     fsync(fileno(fp));
3793 
3794     fgetname(fp, file, 1);
3795     fstat(fileno(fp), &s0.crtl_stat);
3796     fclose(fp);
3797 
3798     if (decc_filename_unix_only)
3799 	int_tounixspec(file, file, NULL);
3800     fp = fopen(file,"r","shr=get");
3801     if (!fp) return 0;
3802     fstat(fileno(fp), &s1.crtl_stat);
3803 
3804     cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3805     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
3806         fclose(fp);
3807         return 0;
3808     }
3809 
3810     return fp;
3811 }
3812 
3813 
3814 static int vms_is_syscommand_xterm(void)
3815 {
3816     const static struct dsc$descriptor_s syscommand_dsc =
3817       { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3818 
3819     const static struct dsc$descriptor_s decwdisplay_dsc =
3820       { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3821 
3822     struct item_list_3 items[2];
3823     unsigned short dvi_iosb[4];
3824     unsigned long devchar;
3825     unsigned long devclass;
3826     int status;
3827 
3828     /* Very simple check to guess if sys$command is a decterm? */
3829     /* First see if the DECW$DISPLAY: device exists */
3830     items[0].len = 4;
3831     items[0].code = DVI$_DEVCHAR;
3832     items[0].bufadr = &devchar;
3833     items[0].retadr = NULL;
3834     items[1].len = 0;
3835     items[1].code = 0;
3836 
3837     status = sys$getdviw
3838 	(NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3839 
3840     if ($VMS_STATUS_SUCCESS(status)) {
3841         status = dvi_iosb[0];
3842     }
3843 
3844     if (!$VMS_STATUS_SUCCESS(status)) {
3845         SETERRNO(EVMSERR, status);
3846 	return -1;
3847     }
3848 
3849     /* If it does, then for now assume that we are on a workstation */
3850     /* Now verify that SYS$COMMAND is a terminal */
3851     /* for creating the debugger DECTerm */
3852 
3853     items[0].len = 4;
3854     items[0].code = DVI$_DEVCLASS;
3855     items[0].bufadr = &devclass;
3856     items[0].retadr = NULL;
3857     items[1].len = 0;
3858     items[1].code = 0;
3859 
3860     status = sys$getdviw
3861 	(NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3862 
3863     if ($VMS_STATUS_SUCCESS(status)) {
3864         status = dvi_iosb[0];
3865     }
3866 
3867     if (!$VMS_STATUS_SUCCESS(status)) {
3868         SETERRNO(EVMSERR, status);
3869 	return -1;
3870     }
3871     else {
3872 	if (devclass == DC$_TERM) {
3873 	    return 0;
3874 	}
3875     }
3876     return -1;
3877 }
3878 
3879 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3880 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3881 {
3882     int status;
3883     int ret_stat;
3884     char * ret_char;
3885     char device_name[65];
3886     unsigned short device_name_len;
3887     struct dsc$descriptor_s customization_dsc;
3888     struct dsc$descriptor_s device_name_dsc;
3889     const char * cptr;
3890     char customization[200];
3891     char title[40];
3892     pInfo info = NULL;
3893     char mbx1[64];
3894     unsigned short p_chan;
3895     int n;
3896     unsigned short iosb[4];
3897     const char * cust_str =
3898         "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3899     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3900                                           DSC$K_CLASS_S, mbx1};
3901 
3902      /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3903     /*---------------------------------------*/
3904     VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3905 
3906 
3907     /* Make sure that this is from the Perl debugger */
3908     ret_char = strstr(cmd," xterm ");
3909     if (ret_char == NULL)
3910 	return NULL;
3911     cptr = ret_char + 7;
3912     ret_char = strstr(cmd,"tty");
3913     if (ret_char == NULL)
3914 	return NULL;
3915     ret_char = strstr(cmd,"sleep");
3916     if (ret_char == NULL)
3917 	return NULL;
3918 
3919     if (decw_term_port == 0) {
3920 	$DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3921 	$DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3922 	$DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3923 
3924        status = lib$find_image_symbol
3925 			       (&filename1_dsc,
3926 				&decw_term_port_dsc,
3927 				(void *)&decw_term_port,
3928 				NULL,
3929 				0);
3930 
3931 	/* Try again with the other image name */
3932 	if (!$VMS_STATUS_SUCCESS(status)) {
3933 
3934            status = lib$find_image_symbol
3935 			       (&filename2_dsc,
3936 				&decw_term_port_dsc,
3937 				(void *)&decw_term_port,
3938 				NULL,
3939 				0);
3940 
3941 	}
3942 
3943     }
3944 
3945 
3946     /* No decw$term_port, give it up */
3947     if (!$VMS_STATUS_SUCCESS(status))
3948 	return NULL;
3949 
3950     /* Are we on a workstation? */
3951     /* to do: capture the rows / columns and pass their properties */
3952     ret_stat = vms_is_syscommand_xterm();
3953     if (ret_stat < 0)
3954 	return NULL;
3955 
3956     /* Make the title: */
3957     ret_char = strstr(cptr,"-title");
3958     if (ret_char != NULL) {
3959 	while ((*cptr != 0) && (*cptr != '\"')) {
3960 	    cptr++;
3961 	}
3962 	if (*cptr == '\"')
3963 	    cptr++;
3964 	n = 0;
3965 	while ((*cptr != 0) && (*cptr != '\"')) {
3966 	    title[n] = *cptr;
3967 	    n++;
3968 	    if (n == 39) {
3969 		title[39] = 0;
3970 		break;
3971 	    }
3972 	    cptr++;
3973 	}
3974 	title[n] = 0;
3975     }
3976     else {
3977 	    /* Default title */
3978 	    strcpy(title,"Perl Debug DECTerm");
3979     }
3980     sprintf(customization, cust_str, title);
3981 
3982     customization_dsc.dsc$a_pointer = customization;
3983     customization_dsc.dsc$w_length = strlen(customization);
3984     customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3985     customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3986 
3987     device_name_dsc.dsc$a_pointer = device_name;
3988     device_name_dsc.dsc$w_length = sizeof device_name -1;
3989     device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3990     device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3991 
3992     device_name_len = 0;
3993 
3994     /* Try to create the window */
3995      status = (*decw_term_port)
3996        (NULL,
3997 	NULL,
3998 	&customization_dsc,
3999 	&device_name_dsc,
4000 	&device_name_len,
4001 	NULL,
4002 	NULL,
4003 	NULL);
4004     if (!$VMS_STATUS_SUCCESS(status)) {
4005         SETERRNO(EVMSERR, status);
4006 	return NULL;
4007     }
4008 
4009     device_name[device_name_len] = '\0';
4010 
4011     /* Need to set this up to look like a pipe for cleanup */
4012     n = sizeof(Info);
4013     status = lib$get_vm(&n, &info);
4014     if (!$VMS_STATUS_SUCCESS(status)) {
4015         SETERRNO(ENOMEM, status);
4016         return NULL;
4017     }
4018 
4019     info->mode = *mode;
4020     info->done = FALSE;
4021     info->completion = 0;
4022     info->closing    = FALSE;
4023     info->in         = 0;
4024     info->out        = 0;
4025     info->err        = 0;
4026     info->fp         = NULL;
4027     info->useFILE    = 0;
4028     info->waiting    = 0;
4029     info->in_done    = TRUE;
4030     info->out_done   = TRUE;
4031     info->err_done   = TRUE;
4032 
4033     /* Assign a channel on this so that it will persist, and not login */
4034     /* We stash this channel in the info structure for reference. */
4035     /* The created xterm self destructs when the last channel is removed */
4036     /* and it appears that perl5db.pl (perl debugger) does this routinely */
4037     /* So leave this assigned. */
4038     device_name_dsc.dsc$w_length = device_name_len;
4039     status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4040     if (!$VMS_STATUS_SUCCESS(status)) {
4041         SETERRNO(EVMSERR, status);
4042 	return NULL;
4043     }
4044     info->xchan_valid = 1;
4045 
4046     /* Now create a mailbox to be read by the application */
4047 
4048     create_mbx(&p_chan, &d_mbx1);
4049 
4050     /* write the name of the created terminal to the mailbox */
4051     status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4052             iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4053 
4054     if (!$VMS_STATUS_SUCCESS(status)) {
4055         SETERRNO(EVMSERR, status);
4056 	return NULL;
4057     }
4058 
4059     info->fp  = PerlIO_open(mbx1, mode);
4060 
4061     /* Done with this channel */
4062     sys$dassgn(p_chan);
4063 
4064     /* If any errors, then clean up */
4065     if (!info->fp) {
4066        	n = sizeof(Info);
4067 	_ckvmssts_noperl(lib$free_vm(&n, &info));
4068 	return NULL;
4069         }
4070 
4071     /* All done */
4072     return info->fp;
4073 }
4074 
4075 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4076 
4077 static PerlIO *
4078 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4079 {
4080     static int handler_set_up = FALSE;
4081     PerlIO * ret_fp;
4082     unsigned long int sts, flags = CLI$M_NOWAIT;
4083     /* The use of a GLOBAL table (as was done previously) rendered
4084      * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4085      * environment.  Hence we've switched to LOCAL symbol table.
4086      */
4087     unsigned int table = LIB$K_CLI_LOCAL_SYM;
4088     int j, wait = 0, n;
4089     char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4090     char *in, *out, *err, mbx[512];
4091     FILE *tpipe = 0;
4092     char tfilebuf[NAM$C_MAXRSS+1];
4093     pInfo info = NULL;
4094     char cmd_sym_name[20];
4095     struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4096                                       DSC$K_CLASS_S, symbol};
4097     struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4098                                       DSC$K_CLASS_S, 0};
4099     struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4100                                       DSC$K_CLASS_S, cmd_sym_name};
4101     struct dsc$descriptor_s *vmscmd;
4102     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4103     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4104     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4105 
4106     /* Check here for Xterm create request.  This means looking for
4107      * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4108      *  is possible to create an xterm.
4109      */
4110     if (*in_mode == 'r') {
4111         PerlIO * xterm_fd;
4112 
4113 #if defined(PERL_IMPLICIT_CONTEXT)
4114         /* Can not fork an xterm with a NULL context */
4115         /* This probably could never happen */
4116         xterm_fd = NULL;
4117         if (aTHX != NULL)
4118 #endif
4119 	xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4120 	if (xterm_fd != NULL)
4121 	    return xterm_fd;
4122     }
4123 
4124     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static vmspipe file */
4125 
4126     /* once-per-program initialization...
4127        note that the SETAST calls and the dual test of pipe_ef
4128        makes sure that only the FIRST thread through here does
4129        the initialization...all other threads wait until it's
4130        done.
4131 
4132        Yeah, uglier than a pthread call, it's got all the stuff inline
4133        rather than in a separate routine.
4134     */
4135 
4136     if (!pipe_ef) {
4137         _ckvmssts_noperl(sys$setast(0));
4138         if (!pipe_ef) {
4139             unsigned long int pidcode = JPI$_PID;
4140             $DESCRIPTOR(d_delay, RETRY_DELAY);
4141             _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4142             _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4143             _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4144         }
4145         if (!handler_set_up) {
4146           _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4147           handler_set_up = TRUE;
4148         }
4149         _ckvmssts_noperl(sys$setast(1));
4150     }
4151 
4152     /* see if we can find a VMSPIPE.COM */
4153 
4154     tfilebuf[0] = '@';
4155     vmspipe = find_vmspipe(aTHX);
4156     if (vmspipe) {
4157         vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4158     } else {        /* uh, oh...we're in tempfile hell */
4159         tpipe = vmspipe_tempfile(aTHX);
4160         if (!tpipe) {       /* a fish popular in Boston */
4161             if (ckWARN(WARN_PIPE)) {
4162                 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4163             }
4164         return NULL;
4165         }
4166         fgetname(tpipe,tfilebuf+1,1);
4167         vmspipedsc.dsc$w_length  = strlen(tfilebuf);
4168     }
4169     vmspipedsc.dsc$a_pointer = tfilebuf;
4170 
4171     sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4172     if (!(sts & 1)) {
4173       switch (sts) {
4174         case RMS$_FNF:  case RMS$_DNF:
4175           set_errno(ENOENT); break;
4176         case RMS$_DIR:
4177           set_errno(ENOTDIR); break;
4178         case RMS$_DEV:
4179           set_errno(ENODEV); break;
4180         case RMS$_PRV:
4181           set_errno(EACCES); break;
4182         case RMS$_SYN:
4183           set_errno(EINVAL); break;
4184         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4185           set_errno(E2BIG); break;
4186         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4187           _ckvmssts_noperl(sts); /* fall through */
4188         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4189           set_errno(EVMSERR);
4190       }
4191       set_vaxc_errno(sts);
4192       if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4193         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4194       }
4195       *psts = sts;
4196       return NULL;
4197     }
4198     n = sizeof(Info);
4199     _ckvmssts_noperl(lib$get_vm(&n, &info));
4200 
4201     my_strlcpy(mode, in_mode, sizeof(mode));
4202     info->mode = *mode;
4203     info->done = FALSE;
4204     info->completion = 0;
4205     info->closing    = FALSE;
4206     info->in         = 0;
4207     info->out        = 0;
4208     info->err        = 0;
4209     info->fp         = NULL;
4210     info->useFILE    = 0;
4211     info->waiting    = 0;
4212     info->in_done    = TRUE;
4213     info->out_done   = TRUE;
4214     info->err_done   = TRUE;
4215     info->xchan      = 0;
4216     info->xchan_valid = 0;
4217 
4218     in = (char *)PerlMem_malloc(VMS_MAXRSS);
4219     if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4220     out = (char *)PerlMem_malloc(VMS_MAXRSS);
4221     if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4222     err = (char *)PerlMem_malloc(VMS_MAXRSS);
4223     if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4224 
4225     in[0] = out[0] = err[0] = '\0';
4226 
4227     if ((p = strchr(mode,'F')) != NULL) {   /* F -> use FILE* */
4228         info->useFILE = 1;
4229         strcpy(p,p+1);
4230     }
4231     if ((p = strchr(mode,'W')) != NULL) {   /* W -> wait for completion */
4232         wait = 1;
4233         strcpy(p,p+1);
4234     }
4235 
4236     if (*mode == 'r') {             /* piping from subroutine */
4237 
4238         info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4239         if (info->out) {
4240             info->out->pipe_done = &info->out_done;
4241             info->out_done = FALSE;
4242             info->out->info = info;
4243         }
4244         if (!info->useFILE) {
4245 	    info->fp  = PerlIO_open(mbx, mode);
4246         } else {
4247             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4248             vmssetuserlnm("SYS$INPUT", mbx);
4249         }
4250 
4251         if (!info->fp && info->out) {
4252             sys$cancel(info->out->chan_out);
4253 
4254             while (!info->out_done) {
4255                 int done;
4256                 _ckvmssts_noperl(sys$setast(0));
4257                 done = info->out_done;
4258                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4259                 _ckvmssts_noperl(sys$setast(1));
4260                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4261             }
4262 
4263             if (info->out->buf) {
4264                 n = info->out->bufsize * sizeof(char);
4265                 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4266             }
4267             n = sizeof(Pipe);
4268             _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4269             n = sizeof(Info);
4270             _ckvmssts_noperl(lib$free_vm(&n, &info));
4271             *psts = RMS$_FNF;
4272             return NULL;
4273         }
4274 
4275         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4276         if (info->err) {
4277             info->err->pipe_done = &info->err_done;
4278             info->err_done = FALSE;
4279             info->err->info = info;
4280         }
4281 
4282     } else if (*mode == 'w') {      /* piping to subroutine */
4283 
4284         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4285         if (info->out) {
4286             info->out->pipe_done = &info->out_done;
4287             info->out_done = FALSE;
4288             info->out->info = info;
4289         }
4290 
4291         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4292         if (info->err) {
4293             info->err->pipe_done = &info->err_done;
4294             info->err_done = FALSE;
4295             info->err->info = info;
4296         }
4297 
4298         info->in = pipe_tochild_setup(aTHX_ in,mbx);
4299         if (!info->useFILE) {
4300 	    info->fp  = PerlIO_open(mbx, mode);
4301         } else {
4302             info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4303             vmssetuserlnm("SYS$OUTPUT", mbx);
4304         }
4305 
4306         if (info->in) {
4307             info->in->pipe_done = &info->in_done;
4308             info->in_done = FALSE;
4309             info->in->info = info;
4310         }
4311 
4312         /* error cleanup */
4313         if (!info->fp && info->in) {
4314             info->done = TRUE;
4315             _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4316                                       0, 0, 0, 0, 0, 0, 0, 0));
4317 
4318             while (!info->in_done) {
4319                 int done;
4320                 _ckvmssts_noperl(sys$setast(0));
4321                 done = info->in_done;
4322                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4323                 _ckvmssts_noperl(sys$setast(1));
4324                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4325             }
4326 
4327             if (info->in->buf) {
4328                 n = info->in->bufsize * sizeof(char);
4329                 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4330             }
4331             n = sizeof(Pipe);
4332             _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4333             n = sizeof(Info);
4334             _ckvmssts_noperl(lib$free_vm(&n, &info));
4335             *psts = RMS$_FNF;
4336             return NULL;
4337         }
4338 
4339 
4340     } else if (*mode == 'n') {       /* separate subprocess, no Perl i/o */
4341         /* Let the child inherit standard input, unless it's a directory. */
4342         Stat_t st;
4343         if (my_trnlnm("SYS$INPUT", in, 0)) {
4344             if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4345                 *in = '\0';
4346         }
4347 
4348         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4349         if (info->out) {
4350             info->out->pipe_done = &info->out_done;
4351             info->out_done = FALSE;
4352             info->out->info = info;
4353         }
4354 
4355         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4356         if (info->err) {
4357             info->err->pipe_done = &info->err_done;
4358             info->err_done = FALSE;
4359             info->err->info = info;
4360         }
4361     }
4362 
4363     d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4364     _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4365 
4366     d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4367     _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4368 
4369     d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4370     _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4371 
4372     /* Done with the names for the pipes */
4373     PerlMem_free(err);
4374     PerlMem_free(out);
4375     PerlMem_free(in);
4376 
4377     p = vmscmd->dsc$a_pointer;
4378     while (*p == ' ' || *p == '\t') p++;        /* remove leading whitespace */
4379     if (*p == '$') p++;                         /* remove leading $ */
4380     while (*p == ' ' || *p == '\t') p++;
4381 
4382     for (j = 0; j < 4; j++) {
4383         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4384         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4385 
4386     d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4387     _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4388 
4389         if (strlen(p) > MAX_DCL_SYMBOL) {
4390             p += MAX_DCL_SYMBOL;
4391         } else {
4392             p += strlen(p);
4393         }
4394     }
4395     _ckvmssts_noperl(sys$setast(0));
4396     info->next=open_pipes;  /* prepend to list */
4397     open_pipes=info;
4398     _ckvmssts_noperl(sys$setast(1));
4399     /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4400      * and SYS$COMMAND.  vmspipe.com will redefine SYS$INPUT, but we'll still
4401      * have SYS$COMMAND if we need it.
4402      */
4403     _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4404                       0, &info->pid, &info->completion,
4405                       0, popen_completion_ast,info,0,0,0));
4406 
4407     /* if we were using a tempfile, close it now */
4408 
4409     if (tpipe) fclose(tpipe);
4410 
4411     /* once the subprocess is spawned, it has copied the symbols and
4412        we can get rid of ours */
4413 
4414     for (j = 0; j < 4; j++) {
4415         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4416         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4417     _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4418     }
4419     _ckvmssts_noperl(lib$delete_symbol(&d_sym_in,  &table));
4420     _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4421     _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4422     vms_execfree(vmscmd);
4423 
4424 #ifdef PERL_IMPLICIT_CONTEXT
4425     if (aTHX)
4426 #endif
4427     PL_forkprocess = info->pid;
4428 
4429     ret_fp = info->fp;
4430     if (wait) {
4431          dSAVEDERRNO;
4432          int done = 0;
4433          while (!done) {
4434              _ckvmssts_noperl(sys$setast(0));
4435              done = info->done;
4436              if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4437              _ckvmssts_noperl(sys$setast(1));
4438              if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4439          }
4440         *psts = info->completion;
4441 /* Caller thinks it is open and tries to close it. */
4442 /* This causes some problems, as it changes the error status */
4443 /*        my_pclose(info->fp); */
4444 
4445          /* If we did not have a file pointer open, then we have to */
4446          /* clean up here or eventually we will run out of something */
4447          SAVE_ERRNO;
4448          if (info->fp == NULL) {
4449              my_pclose_pinfo(aTHX_ info);
4450          }
4451          RESTORE_ERRNO;
4452 
4453     } else {
4454         *psts = info->pid;
4455     }
4456     return ret_fp;
4457 }  /* end of safe_popen */
4458 
4459 
4460 /*{{{  PerlIO *my_popen(char *cmd, char *mode)*/
4461 PerlIO *
4462 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4463 {
4464     int sts;
4465     TAINT_ENV();
4466     TAINT_PROPER("popen");
4467     PERL_FLUSHALL_FOR_CHILD;
4468     return safe_popen(aTHX_ cmd,mode,&sts);
4469 }
4470 
4471 /*}}}*/
4472 
4473 
4474 /* Routine to close and cleanup a pipe info structure */
4475 
4476 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4477 
4478     unsigned long int retsts;
4479     int done, n;
4480     pInfo next, last;
4481 
4482     /* If we were writing to a subprocess, insure that someone reading from
4483      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
4484      * produce an EOF record in the mailbox.
4485      *
4486      *  well, at least sometimes it *does*, so we have to watch out for
4487      *  the first EOF closing the pipe (and DASSGN'ing the channel)...
4488      */
4489      if (info->fp) {
4490         if (!info->useFILE
4491 #if defined(USE_ITHREADS)
4492           && my_perl
4493 #endif
4494 #ifdef USE_PERLIO
4495           && PL_perlio_fd_refcnt
4496 #endif
4497            )
4498             PerlIO_flush(info->fp);
4499         else
4500             fflush((FILE *)info->fp);
4501     }
4502 
4503     _ckvmssts(sys$setast(0));
4504      info->closing = TRUE;
4505      done = info->done && info->in_done && info->out_done && info->err_done;
4506      /* hanging on write to Perl's input? cancel it */
4507      if (info->mode == 'r' && info->out && !info->out_done) {
4508         if (info->out->chan_out) {
4509             _ckvmssts(sys$cancel(info->out->chan_out));
4510             if (!info->out->chan_in) {   /* EOF generation, need AST */
4511                 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4512             }
4513         }
4514      }
4515      if (info->in && !info->in_done && !info->in->shut_on_empty)  /* EOF if hasn't had one yet */
4516          _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4517                            0, 0, 0, 0, 0, 0));
4518     _ckvmssts(sys$setast(1));
4519     if (info->fp) {
4520      if (!info->useFILE
4521 #if defined(USE_ITHREADS)
4522          && my_perl
4523 #endif
4524 #ifdef USE_PERLIO
4525          && PL_perlio_fd_refcnt
4526 #endif
4527         )
4528         PerlIO_close(info->fp);
4529      else
4530         fclose((FILE *)info->fp);
4531     }
4532      /*
4533         we have to wait until subprocess completes, but ALSO wait until all
4534         the i/o completes...otherwise we'll be freeing the "info" structure
4535         that the i/o ASTs could still be using...
4536      */
4537 
4538      while (!done) {
4539          _ckvmssts(sys$setast(0));
4540          done = info->done && info->in_done && info->out_done && info->err_done;
4541          if (!done) _ckvmssts(sys$clref(pipe_ef));
4542          _ckvmssts(sys$setast(1));
4543          if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4544      }
4545      retsts = info->completion;
4546 
4547     /* remove from list of open pipes */
4548     _ckvmssts(sys$setast(0));
4549     last = NULL;
4550     for (next = open_pipes; next != NULL; last = next, next = next->next) {
4551         if (next == info)
4552             break;
4553     }
4554 
4555     if (last)
4556         last->next = info->next;
4557     else
4558         open_pipes = info->next;
4559     _ckvmssts(sys$setast(1));
4560 
4561     /* free buffers and structures */
4562 
4563     if (info->in) {
4564         if (info->in->buf) {
4565             n = info->in->bufsize * sizeof(char);
4566             _ckvmssts(lib$free_vm(&n, &info->in->buf));
4567         }
4568         n = sizeof(Pipe);
4569         _ckvmssts(lib$free_vm(&n, &info->in));
4570     }
4571     if (info->out) {
4572         if (info->out->buf) {
4573             n = info->out->bufsize * sizeof(char);
4574             _ckvmssts(lib$free_vm(&n, &info->out->buf));
4575         }
4576         n = sizeof(Pipe);
4577         _ckvmssts(lib$free_vm(&n, &info->out));
4578     }
4579     if (info->err) {
4580         if (info->err->buf) {
4581             n = info->err->bufsize * sizeof(char);
4582             _ckvmssts(lib$free_vm(&n, &info->err->buf));
4583         }
4584         n = sizeof(Pipe);
4585         _ckvmssts(lib$free_vm(&n, &info->err));
4586     }
4587     n = sizeof(Info);
4588     _ckvmssts(lib$free_vm(&n, &info));
4589 
4590     return retsts;
4591 }
4592 
4593 
4594 /*{{{  I32 my_pclose(PerlIO *fp)*/
4595 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4596 {
4597     pInfo info, last = NULL;
4598     I32 ret_status;
4599 
4600     /* Fixme - need ast and mutex protection here */
4601     for (info = open_pipes; info != NULL; last = info, info = info->next)
4602         if (info->fp == fp) break;
4603 
4604     if (info == NULL) {  /* no such pipe open */
4605       set_errno(ECHILD); /* quoth POSIX */
4606       set_vaxc_errno(SS$_NONEXPR);
4607       return -1;
4608     }
4609 
4610     ret_status = my_pclose_pinfo(aTHX_ info);
4611 
4612     return ret_status;
4613 
4614 }  /* end of my_pclose() */
4615 
4616 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4617   /* Roll our own prototype because we want this regardless of whether
4618    * _VMS_WAIT is defined.
4619    */
4620 
4621 #ifdef __cplusplus
4622 extern "C" {
4623 #endif
4624   __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4625 #ifdef __cplusplus
4626 }
4627 #endif
4628 
4629 #endif
4630 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4631    created with popen(); otherwise partially emulate waitpid() unless
4632    we have a suitable one from the CRTL that came with VMS 7.2 and later.
4633    Also check processes not considered by the CRTL waitpid().
4634  */
4635 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4636 Pid_t
4637 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4638 {
4639     pInfo info;
4640     int done;
4641     int sts;
4642     int j;
4643 
4644     if (statusp) *statusp = 0;
4645 
4646     for (info = open_pipes; info != NULL; info = info->next)
4647         if (info->pid == pid) break;
4648 
4649     if (info != NULL) {  /* we know about this child */
4650       while (!info->done) {
4651           _ckvmssts(sys$setast(0));
4652           done = info->done;
4653           if (!done) _ckvmssts(sys$clref(pipe_ef));
4654           _ckvmssts(sys$setast(1));
4655           if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4656       }
4657 
4658       if (statusp) *statusp = info->completion;
4659       return pid;
4660     }
4661 
4662     /* child that already terminated? */
4663 
4664     for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4665         if (closed_list[j].pid == pid) {
4666             if (statusp) *statusp = closed_list[j].completion;
4667             return pid;
4668         }
4669     }
4670 
4671     /* fall through if this child is not one of our own pipe children */
4672 
4673 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4674 
4675       /* waitpid() became available in the CRTL as of VMS 7.0, but only
4676        * in 7.2 did we get a version that fills in the VMS completion
4677        * status as Perl has always tried to do.
4678        */
4679 
4680       sts = __vms_waitpid( pid, statusp, flags );
4681 
4682       if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4683          return sts;
4684 
4685       /* If the real waitpid tells us the child does not exist, we
4686        * fall through here to implement waiting for a child that
4687        * was created by some means other than exec() (say, spawned
4688        * from DCL) or to wait for a process that is not a subprocess
4689        * of the current process.
4690        */
4691 
4692 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4693 
4694     {
4695       $DESCRIPTOR(intdsc,"0 00:00:01");
4696       unsigned long int ownercode = JPI$_OWNER, ownerpid;
4697       unsigned long int pidcode = JPI$_PID, mypid;
4698       unsigned long int interval[2];
4699       unsigned int jpi_iosb[2];
4700       struct itmlst_3 jpilist[2] = {
4701           {sizeof(ownerpid),        JPI$_OWNER, &ownerpid,        0},
4702           {                      0,         0,                 0, 0}
4703       };
4704 
4705       if (pid <= 0) {
4706         /* Sorry folks, we don't presently implement rooting around for
4707            the first child we can find, and we definitely don't want to
4708            pass a pid of -1 to $getjpi, where it is a wildcard operation.
4709          */
4710         set_errno(ENOTSUP);
4711         return -1;
4712       }
4713 
4714       /* Get the owner of the child so I can warn if it's not mine. If the
4715        * process doesn't exist or I don't have the privs to look at it,
4716        * I can go home early.
4717        */
4718       sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4719       if (sts & 1) sts = jpi_iosb[0];
4720       if (!(sts & 1)) {
4721         switch (sts) {
4722             case SS$_NONEXPR:
4723                 set_errno(ECHILD);
4724                 break;
4725             case SS$_NOPRIV:
4726                 set_errno(EACCES);
4727                 break;
4728             default:
4729                 _ckvmssts(sts);
4730         }
4731         set_vaxc_errno(sts);
4732         return -1;
4733       }
4734 
4735       if (ckWARN(WARN_EXEC)) {
4736         /* remind folks they are asking for non-standard waitpid behavior */
4737         _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4738         if (ownerpid != mypid)
4739           Perl_warner(aTHX_ packWARN(WARN_EXEC),
4740                       "waitpid: process %x is not a child of process %x",
4741                       pid,mypid);
4742       }
4743 
4744       /* simply check on it once a second until it's not there anymore. */
4745 
4746       _ckvmssts(sys$bintim(&intdsc,interval));
4747       while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4748             _ckvmssts(sys$schdwk(0,0,interval,0));
4749             _ckvmssts(sys$hiber());
4750       }
4751       if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4752 
4753       _ckvmssts(sts);
4754       return pid;
4755     }
4756 }  /* end of waitpid() */
4757 /*}}}*/
4758 /*}}}*/
4759 /*}}}*/
4760 
4761 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4762 char *
4763 my_gconvert(double val, int ndig, int trail, char *buf)
4764 {
4765   static char __gcvtbuf[DBL_DIG+1];
4766   char *loc;
4767 
4768   loc = buf ? buf : __gcvtbuf;
4769 
4770   if (val) {
4771     if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4772     return gcvt(val,ndig,loc);
4773   }
4774   else {
4775     loc[0] = '0'; loc[1] = '\0';
4776     return loc;
4777   }
4778 
4779 }
4780 /*}}}*/
4781 
4782 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4783 static int rms_free_search_context(struct FAB * fab)
4784 {
4785 struct NAM * nam;
4786 
4787     nam = fab->fab$l_nam;
4788     nam->nam$b_nop |= NAM$M_SYNCHK;
4789     nam->nam$l_rlf = NULL;
4790     fab->fab$b_dns = 0;
4791     return sys$parse(fab, NULL, NULL);
4792 }
4793 
4794 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4795 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4796 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4797 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4798 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4799 #define rms_nam_esll(nam) nam.nam$b_esl
4800 #define rms_nam_esl(nam) nam.nam$b_esl
4801 #define rms_nam_name(nam) nam.nam$l_name
4802 #define rms_nam_namel(nam) nam.nam$l_name
4803 #define rms_nam_type(nam) nam.nam$l_type
4804 #define rms_nam_typel(nam) nam.nam$l_type
4805 #define rms_nam_ver(nam) nam.nam$l_ver
4806 #define rms_nam_verl(nam) nam.nam$l_ver
4807 #define rms_nam_rsll(nam) nam.nam$b_rsl
4808 #define rms_nam_rsl(nam) nam.nam$b_rsl
4809 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4810 #define rms_set_fna(fab, nam, name, size) \
4811 	{ fab.fab$b_fns = size; fab.fab$l_fna = name; }
4812 #define rms_get_fna(fab, nam) fab.fab$l_fna
4813 #define rms_set_dna(fab, nam, name, size) \
4814 	{ fab.fab$b_dns = size; fab.fab$l_dna = name; }
4815 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4816 #define rms_set_esa(nam, name, size) \
4817 	{ nam.nam$b_ess = size; nam.nam$l_esa = name; }
4818 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4819 	{ nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4820 #define rms_set_rsa(nam, name, size) \
4821 	{ nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4822 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4823 	{ nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4824 #define rms_nam_name_type_l_size(nam) \
4825 	(nam.nam$b_name + nam.nam$b_type)
4826 #else
4827 static int rms_free_search_context(struct FAB * fab)
4828 {
4829 struct NAML * nam;
4830 
4831     nam = fab->fab$l_naml;
4832     nam->naml$b_nop |= NAM$M_SYNCHK;
4833     nam->naml$l_rlf = NULL;
4834     nam->naml$l_long_defname_size = 0;
4835 
4836     fab->fab$b_dns = 0;
4837     return sys$parse(fab, NULL, NULL);
4838 }
4839 
4840 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4841 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4842 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4843 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4844 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4845 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4846 #define rms_nam_esl(nam) nam.naml$b_esl
4847 #define rms_nam_name(nam) nam.naml$l_name
4848 #define rms_nam_namel(nam) nam.naml$l_long_name
4849 #define rms_nam_type(nam) nam.naml$l_type
4850 #define rms_nam_typel(nam) nam.naml$l_long_type
4851 #define rms_nam_ver(nam) nam.naml$l_ver
4852 #define rms_nam_verl(nam) nam.naml$l_long_ver
4853 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4854 #define rms_nam_rsl(nam) nam.naml$b_rsl
4855 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4856 #define rms_set_fna(fab, nam, name, size) \
4857 	{ fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4858 	nam.naml$l_long_filename_size = size; \
4859 	nam.naml$l_long_filename = name;}
4860 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4861 #define rms_set_dna(fab, nam, name, size) \
4862 	{ fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4863 	nam.naml$l_long_defname_size = size; \
4864 	nam.naml$l_long_defname = name; }
4865 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4866 #define rms_set_esa(nam, name, size) \
4867 	{ nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4868 	nam.naml$l_long_expand_alloc = size; \
4869 	nam.naml$l_long_expand = name; }
4870 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4871 	{ nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4872 	nam.naml$l_long_expand = l_name; \
4873 	nam.naml$l_long_expand_alloc = l_size; }
4874 #define rms_set_rsa(nam, name, size) \
4875 	{ nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4876 	nam.naml$l_long_result = name; \
4877 	nam.naml$l_long_result_alloc = size; }
4878 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4879 	{ nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4880 	nam.naml$l_long_result = l_name; \
4881 	nam.naml$l_long_result_alloc = l_size; }
4882 #define rms_nam_name_type_l_size(nam) \
4883 	(nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4884 #endif
4885 
4886 
4887 /* rms_erase
4888  * The CRTL for 8.3 and later can create symbolic links in any mode,
4889  * however in 8.3 the unlink/remove/delete routines will only properly handle
4890  * them if one of the PCP modes is active.
4891  */
4892 static int rms_erase(const char * vmsname)
4893 {
4894   int status;
4895   struct FAB myfab = cc$rms_fab;
4896   rms_setup_nam(mynam);
4897 
4898   rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4899   rms_bind_fab_nam(myfab, mynam);
4900 
4901 #ifdef NAML$M_OPEN_SPECIAL
4902   rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4903 #endif
4904 
4905   status = sys$erase(&myfab, 0, 0);
4906 
4907   return status;
4908 }
4909 
4910 
4911 static int
4912 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4913 		    const struct dsc$descriptor_s * vms_dst_dsc,
4914 		    unsigned long flags)
4915 {
4916     /*  VMS and UNIX handle file permissions differently and the
4917      * the same ACL trick may be needed for renaming files,
4918      * especially if they are directories.
4919      */
4920 
4921    /* todo: get kill_file and rename to share common code */
4922    /* I can not find online documentation for $change_acl
4923     * it appears to be replaced by $set_security some time ago */
4924 
4925 const unsigned int access_mode = 0;
4926 $DESCRIPTOR(obj_file_dsc,"FILE");
4927 char *vmsname;
4928 char *rslt;
4929 unsigned long int jpicode = JPI$_UIC;
4930 int aclsts, fndsts, rnsts = -1;
4931 unsigned int ctx = 0;
4932 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4933 struct dsc$descriptor_s * clean_dsc;
4934 
4935 struct myacedef {
4936     unsigned char myace$b_length;
4937     unsigned char myace$b_type;
4938     unsigned short int myace$w_flags;
4939     unsigned long int myace$l_access;
4940     unsigned long int myace$l_ident;
4941 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4942 	     ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4943 	     0},
4944 	     oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4945 
4946 struct item_list_3
4947 	findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4948 		      {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4949 		      {0,0,0,0}},
4950 	addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4951 	dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4952 		     {0,0,0,0}};
4953 
4954 
4955     /* Expand the input spec using RMS, since we do not want to put
4956      * ACLs on the target of a symbolic link */
4957     vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4958     if (vmsname == NULL)
4959 	return SS$_INSFMEM;
4960 
4961     rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4962 			vmsname,
4963 			PERL_RMSEXPAND_M_SYMLINK);
4964     if (rslt == NULL) {
4965 	PerlMem_free(vmsname);
4966 	return SS$_INSFMEM;
4967     }
4968 
4969     /* So we get our own UIC to use as a rights identifier,
4970      * and the insert an ACE at the head of the ACL which allows us
4971      * to delete the file.
4972      */
4973     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4974 
4975     fildsc.dsc$w_length = strlen(vmsname);
4976     fildsc.dsc$a_pointer = vmsname;
4977     ctx = 0;
4978     newace.myace$l_ident = oldace.myace$l_ident;
4979     rnsts = SS$_ABORT;
4980 
4981     /* Grab any existing ACEs with this identifier in case we fail */
4982     clean_dsc = &fildsc;
4983     aclsts = fndsts = sys$get_security(&obj_file_dsc,
4984 			       &fildsc,
4985 			       NULL,
4986 			       OSS$M_WLOCK,
4987 			       findlst,
4988 			       &ctx,
4989 			       &access_mode);
4990 
4991     if ($VMS_STATUS_SUCCESS(fndsts)  || (fndsts == SS$_ACLEMPTY)) {
4992 	/* Add the new ACE . . . */
4993 
4994 	/* if the sys$get_security succeeded, then ctx is valid, and the
4995 	 * object/file descriptors will be ignored.  But otherwise they
4996 	 * are needed
4997 	 */
4998 	aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4999 				  OSS$M_RELCTX, addlst, &ctx, &access_mode);
5000 	if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5001 	    set_errno(EVMSERR);
5002 	    set_vaxc_errno(aclsts);
5003 	    PerlMem_free(vmsname);
5004 	    return aclsts;
5005 	}
5006 
5007 	rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5008 				NULL, NULL,
5009 				&flags,
5010 				NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5011 
5012 	if ($VMS_STATUS_SUCCESS(rnsts)) {
5013 	    clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5014 	}
5015 
5016 	/* Put things back the way they were. */
5017 	ctx = 0;
5018 	aclsts = sys$get_security(&obj_file_dsc,
5019 				  clean_dsc,
5020 				  NULL,
5021 				  OSS$M_WLOCK,
5022 				  findlst,
5023 				  &ctx,
5024 				  &access_mode);
5025 
5026 	if ($VMS_STATUS_SUCCESS(aclsts)) {
5027 	int sec_flags;
5028 
5029 	    sec_flags = 0;
5030 	    if (!$VMS_STATUS_SUCCESS(fndsts))
5031 		sec_flags = OSS$M_RELCTX;
5032 
5033 	    /* Get rid of the new ACE */
5034 	    aclsts = sys$set_security(NULL, NULL, NULL,
5035 				  sec_flags, dellst, &ctx, &access_mode);
5036 
5037 	    /* If there was an old ACE, put it back */
5038 	    if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5039 		addlst[0].bufadr = &oldace;
5040 		aclsts = sys$set_security(NULL, NULL, NULL,
5041 				      OSS$M_RELCTX, addlst, &ctx, &access_mode);
5042 		if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5043 		    set_errno(EVMSERR);
5044 		    set_vaxc_errno(aclsts);
5045 		    rnsts = aclsts;
5046 		}
5047 	    } else {
5048 	    int aclsts2;
5049 
5050 		/* Try to clear the lock on the ACL list */
5051 		aclsts2 = sys$set_security(NULL, NULL, NULL,
5052 				      OSS$M_RELCTX, NULL, &ctx, &access_mode);
5053 
5054 		/* Rename errors are most important */
5055 		if (!$VMS_STATUS_SUCCESS(rnsts))
5056 		    aclsts = rnsts;
5057 		set_errno(EVMSERR);
5058 		set_vaxc_errno(aclsts);
5059 		rnsts = aclsts;
5060 	    }
5061 	}
5062 	else {
5063 	    if (aclsts != SS$_ACLEMPTY)
5064 		rnsts = aclsts;
5065 	}
5066     }
5067     else
5068 	rnsts = fndsts;
5069 
5070     PerlMem_free(vmsname);
5071     return rnsts;
5072 }
5073 
5074 
5075 /*{{{int rename(const char *, const char * */
5076 /* Not exactly what X/Open says to do, but doing it absolutely right
5077  * and efficiently would require a lot more work.  This should be close
5078  * enough to pass all but the most strict X/Open compliance test.
5079  */
5080 int
5081 Perl_rename(pTHX_ const char *src, const char * dst)
5082 {
5083 int retval;
5084 int pre_delete = 0;
5085 int src_sts;
5086 int dst_sts;
5087 Stat_t src_st;
5088 Stat_t dst_st;
5089 
5090     /* Validate the source file */
5091     src_sts = flex_lstat(src, &src_st);
5092     if (src_sts != 0) {
5093 
5094 	/* No source file or other problem */
5095 	return src_sts;
5096     }
5097     if (src_st.st_devnam[0] == 0)  {
5098         /* This may be possible so fail if it is seen. */
5099         errno = EIO;
5100         return -1;
5101     }
5102 
5103     dst_sts = flex_lstat(dst, &dst_st);
5104     if (dst_sts == 0) {
5105 
5106 	if (dst_st.st_dev != src_st.st_dev) {
5107 	    /* Must be on the same device */
5108 	    errno = EXDEV;
5109 	    return -1;
5110 	}
5111 
5112 	/* VMS_INO_T_COMPARE is true if the inodes are different
5113 	 * to match the output of memcmp
5114 	 */
5115 
5116 	if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5117 	    /* That was easy, the files are the same! */
5118 	    return 0;
5119 	}
5120 
5121 	if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5122 	    /* If source is a directory, so must be dest */
5123 		errno = EISDIR;
5124 		return -1;
5125 	}
5126 
5127     }
5128 
5129 
5130     if ((dst_sts == 0) &&
5131 	(vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5132 
5133 	/* We have issues here if vms_unlink_all_versions is set
5134 	 * If the destination exists, and is not a directory, then
5135 	 * we must delete in advance.
5136 	 *
5137 	 * If the src is a directory, then we must always pre-delete
5138 	 * the destination.
5139 	 *
5140 	 * If we successfully delete the dst in advance, and the rename fails
5141 	 * X/Open requires that errno be EIO.
5142 	 *
5143 	 */
5144 
5145 	if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5146 	    int d_sts;
5147 	    d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5148 	                             S_ISDIR(dst_st.st_mode));
5149 
5150            /* Need to delete all versions ? */
5151            if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5152                 int i = 0;
5153 
5154                 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5155                     d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5156                     if (d_sts != 0)
5157                         break;
5158                     i++;
5159 
5160                     /* Make sure that we do not loop forever */
5161                     if (i > 32767) {
5162                         errno = EIO;
5163                         d_sts = -1;
5164                         break;
5165                     }
5166                 }
5167            }
5168 
5169 	    if (d_sts != 0)
5170 		return d_sts;
5171 
5172 	    /* We killed the destination, so only errno now is EIO */
5173 	    pre_delete = 1;
5174 	}
5175     }
5176 
5177     /* Originally the idea was to call the CRTL rename() and only
5178      * try the lib$rename_file if it failed.
5179      * It turns out that there are too many variants in what the
5180      * the CRTL rename might do, so only use lib$rename_file
5181      */
5182     retval = -1;
5183 
5184     {
5185 	/* Is the source and dest both in VMS format */
5186 	/* if the source is a directory, then need to fileify */
5187 	/*  and dest must be a directory or non-existent. */
5188 
5189 	char * vms_dst;
5190 	int sts;
5191 	char * ret_str;
5192 	unsigned long flags;
5193 	struct dsc$descriptor_s old_file_dsc;
5194 	struct dsc$descriptor_s new_file_dsc;
5195 
5196 	/* We need to modify the src and dst depending
5197 	 * on if one or more of them are directories.
5198 	 */
5199 
5200 	vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5201 	if (vms_dst == NULL)
5202 	    _ckvmssts_noperl(SS$_INSFMEM);
5203 
5204 	if (S_ISDIR(src_st.st_mode)) {
5205 	char * ret_str;
5206 	char * vms_dir_file;
5207 
5208 	    vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5209 	    if (vms_dir_file == NULL)
5210 		_ckvmssts_noperl(SS$_INSFMEM);
5211 
5212 	    /* If the dest is a directory, we must remove it */
5213 	    if (dst_sts == 0) {
5214 		int d_sts;
5215 		d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5216 		if (d_sts != 0) {
5217 		    PerlMem_free(vms_dst);
5218 		    errno = EIO;
5219 		    return d_sts;
5220 		}
5221 
5222 		pre_delete = 1;
5223 	    }
5224 
5225 	   /* The dest must be a VMS file specification */
5226 	   ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5227 	   if (ret_str == NULL) {
5228 		PerlMem_free(vms_dst);
5229 		errno = EIO;
5230 		return -1;
5231 	   }
5232 
5233 	    /* The source must be a file specification */
5234 	    ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5235 	    if (ret_str == NULL) {
5236 		PerlMem_free(vms_dst);
5237 		PerlMem_free(vms_dir_file);
5238 		errno = EIO;
5239 		return -1;
5240 	    }
5241 	    PerlMem_free(vms_dst);
5242 	    vms_dst = vms_dir_file;
5243 
5244 	} else {
5245 	    /* File to file or file to new dir */
5246 
5247 	    if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5248 		/* VMS pathify a dir target */
5249 		ret_str = int_tovmspath(dst, vms_dst, NULL);
5250 		if (ret_str == NULL) {
5251 		    PerlMem_free(vms_dst);
5252 		    errno = EIO;
5253 		    return -1;
5254 		}
5255 	    } else {
5256                 char * v_spec, * r_spec, * d_spec, * n_spec;
5257                 char * e_spec, * vs_spec;
5258                 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5259 
5260 		/* fileify a target VMS file specification */
5261 		ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5262 		if (ret_str == NULL) {
5263 		    PerlMem_free(vms_dst);
5264 		    errno = EIO;
5265 		    return -1;
5266 		}
5267 
5268 		sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5269                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5270                              &e_len, &vs_spec, &vs_len);
5271 		if (sts == 0) {
5272 		     if (e_len == 0) {
5273 		         /* Get rid of the version */
5274 		         if (vs_len != 0) {
5275 		             *vs_spec = '\0';
5276 		         }
5277 		         /* Need to specify a '.' so that the extension */
5278 		         /* is not inherited */
5279 		         strcat(vms_dst,".");
5280 		     }
5281 		}
5282 	    }
5283 	}
5284 
5285 	old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5286 	old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5287 	old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5288 	old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5289 
5290 	new_file_dsc.dsc$a_pointer = vms_dst;
5291 	new_file_dsc.dsc$w_length = strlen(vms_dst);
5292 	new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5293 	new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5294 
5295 	flags = 0;
5296 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5297 	flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5298 #endif
5299 
5300 	sts = lib$rename_file(&old_file_dsc,
5301 			      &new_file_dsc,
5302 			      NULL, NULL,
5303 			      &flags,
5304 			      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5305 	if (!$VMS_STATUS_SUCCESS(sts)) {
5306 
5307 	   /* We could have failed because VMS style permissions do not
5308 	    * permit renames that UNIX will allow.  Just like the hack
5309 	    * in for kill_file.
5310 	    */
5311 	   sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5312 	}
5313 
5314 	PerlMem_free(vms_dst);
5315 	if (!$VMS_STATUS_SUCCESS(sts)) {
5316 	    errno = EIO;
5317 	    return -1;
5318 	}
5319 	retval = 0;
5320     }
5321 
5322     if (vms_unlink_all_versions) {
5323 	/* Now get rid of any previous versions of the source file that
5324 	 * might still exist
5325 	 */
5326 	int i = 0;
5327 	dSAVEDERRNO;
5328 	SAVE_ERRNO;
5329 	src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5330 	                           S_ISDIR(src_st.st_mode));
5331 	while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5332 	     src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5333 	                               S_ISDIR(src_st.st_mode));
5334 	     if (src_sts != 0)
5335 	         break;
5336 	     i++;
5337 
5338 	     /* Make sure that we do not loop forever */
5339 	     if (i > 32767) {
5340 	         src_sts = -1;
5341 	         break;
5342 	     }
5343 	}
5344 	RESTORE_ERRNO;
5345     }
5346 
5347     /* We deleted the destination, so must force the error to be EIO */
5348     if ((retval != 0) && (pre_delete != 0))
5349 	errno = EIO;
5350 
5351     return retval;
5352 }
5353 /*}}}*/
5354 
5355 
5356 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5357 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5358  * to expand file specification.  Allows for a single default file
5359  * specification and a simple mask of options.  If outbuf is non-NULL,
5360  * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5361  * the resultant file specification is placed.  If outbuf is NULL, the
5362  * resultant file specification is placed into a static buffer.
5363  * The third argument, if non-NULL, is taken to be a default file
5364  * specification string.  The fourth argument is unused at present.
5365  * rmesexpand() returns the address of the resultant string if
5366  * successful, and NULL on error.
5367  *
5368  * New functionality for previously unused opts value:
5369  *  PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5370  *  PERL_RMSEXPAND_M_LONG - Want output in long formst
5371  *  PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5372  *  PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5373  */
5374 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5375 
5376 static char *
5377 int_rmsexpand
5378    (const char *filespec,
5379     char *outbuf,
5380     const char *defspec,
5381     unsigned opts,
5382     int * fs_utf8,
5383     int * dfs_utf8)
5384 {
5385   char * ret_spec;
5386   const char * in_spec;
5387   char * spec_buf;
5388   const char * def_spec;
5389   char * vmsfspec, *vmsdefspec;
5390   char * esa;
5391   char * esal = NULL;
5392   char * outbufl;
5393   struct FAB myfab = cc$rms_fab;
5394   rms_setup_nam(mynam);
5395   STRLEN speclen;
5396   unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5397   int sts;
5398 
5399   /* temp hack until UTF8 is actually implemented */
5400   if (fs_utf8 != NULL)
5401     *fs_utf8 = 0;
5402 
5403   if (!filespec || !*filespec) {
5404     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5405     return NULL;
5406   }
5407 
5408   vmsfspec = NULL;
5409   vmsdefspec = NULL;
5410   outbufl = NULL;
5411 
5412   in_spec = filespec;
5413   isunix = 0;
5414   if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5415       char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5416       int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5417 
5418       /* If this is a UNIX file spec, convert it to VMS */
5419       sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5420                            &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5421                            &e_len, &vs_spec, &vs_len);
5422       if (sts != 0) {
5423           isunix = 1;
5424           char * ret_spec;
5425 
5426           vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5427           if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5428           ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5429           if (ret_spec == NULL) {
5430               PerlMem_free(vmsfspec);
5431               return NULL;
5432           }
5433           in_spec = (const char *)vmsfspec;
5434 
5435           /* Unless we are forcing to VMS format, a UNIX input means
5436            * UNIX output, and that requires long names to be used
5437            */
5438           if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5439 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5440               opts |= PERL_RMSEXPAND_M_LONG;
5441 #else
5442               NOOP;
5443 #endif
5444           else
5445               isunix = 0;
5446       }
5447 
5448   }
5449 
5450   rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5451   rms_bind_fab_nam(myfab, mynam);
5452 
5453   /* Process the default file specification if present */
5454   def_spec = defspec;
5455   if (defspec && *defspec) {
5456     int t_isunix;
5457     t_isunix = is_unix_filespec(defspec);
5458     if (t_isunix) {
5459       vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5460       if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5461       ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5462 
5463       if (ret_spec == NULL) {
5464           /* Clean up and bail */
5465           PerlMem_free(vmsdefspec);
5466           if (vmsfspec != NULL)
5467               PerlMem_free(vmsfspec);
5468               return NULL;
5469           }
5470           def_spec = (const char *)vmsdefspec;
5471       }
5472       rms_set_dna(myfab, mynam,
5473                   (char *)def_spec, strlen(def_spec)); /* cast ok */
5474   }
5475 
5476   /* Now we need the expansion buffers */
5477   esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5478   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5479 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5480   esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5481   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5482 #endif
5483   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5484 
5485   /* If a NAML block is used RMS always writes to the long and short
5486    * addresses unless you suppress the short name.
5487    */
5488 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5489   outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5490   if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5491 #endif
5492    rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5493 
5494 #ifdef NAM$M_NO_SHORT_UPCASE
5495   if (decc_efs_case_preserve)
5496     rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5497 #endif
5498 
5499    /* We may not want to follow symbolic links */
5500 #ifdef NAML$M_OPEN_SPECIAL
5501   if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5502     rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5503 #endif
5504 
5505   /* First attempt to parse as an existing file */
5506   retsts = sys$parse(&myfab,0,0);
5507   if (!(retsts & STS$K_SUCCESS)) {
5508 
5509     /* Could not find the file, try as syntax only if error is not fatal */
5510     rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5511     if (retsts == RMS$_DNF ||
5512         retsts == RMS$_DIR ||
5513         retsts == RMS$_DEV ||
5514         retsts == RMS$_PRV) {
5515       retsts = sys$parse(&myfab,0,0);
5516       if (retsts & STS$K_SUCCESS) goto int_expanded;
5517     }
5518 
5519      /* Still could not parse the file specification */
5520     /*----------------------------------------------*/
5521     sts = rms_free_search_context(&myfab); /* Free search context */
5522     if (vmsdefspec != NULL)
5523 	PerlMem_free(vmsdefspec);
5524     if (vmsfspec != NULL)
5525 	PerlMem_free(vmsfspec);
5526     if (outbufl != NULL)
5527 	PerlMem_free(outbufl);
5528     PerlMem_free(esa);
5529     if (esal != NULL)
5530 	PerlMem_free(esal);
5531     set_vaxc_errno(retsts);
5532     if      (retsts == RMS$_PRV) set_errno(EACCES);
5533     else if (retsts == RMS$_DEV) set_errno(ENODEV);
5534     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5535     else                         set_errno(EVMSERR);
5536     return NULL;
5537   }
5538   retsts = sys$search(&myfab,0,0);
5539   if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5540     sts = rms_free_search_context(&myfab); /* Free search context */
5541     if (vmsdefspec != NULL)
5542 	PerlMem_free(vmsdefspec);
5543     if (vmsfspec != NULL)
5544 	PerlMem_free(vmsfspec);
5545     if (outbufl != NULL)
5546 	PerlMem_free(outbufl);
5547     PerlMem_free(esa);
5548     if (esal != NULL)
5549 	PerlMem_free(esal);
5550     set_vaxc_errno(retsts);
5551     if      (retsts == RMS$_PRV) set_errno(EACCES);
5552     else                         set_errno(EVMSERR);
5553     return NULL;
5554   }
5555 
5556   /* If the input filespec contained any lowercase characters,
5557    * downcase the result for compatibility with Unix-minded code. */
5558 int_expanded:
5559   if (!decc_efs_case_preserve) {
5560     char * tbuf;
5561     for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5562       if (islower(*tbuf)) { haslower = 1; break; }
5563   }
5564 
5565    /* Is a long or a short name expected */
5566   /*------------------------------------*/
5567   spec_buf = NULL;
5568 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5569   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5570     if (rms_nam_rsll(mynam)) {
5571 	spec_buf = outbufl;
5572 	speclen = rms_nam_rsll(mynam);
5573     }
5574     else {
5575 	spec_buf = esal; /* Not esa */
5576 	speclen = rms_nam_esll(mynam);
5577     }
5578   }
5579   else {
5580 #endif
5581     if (rms_nam_rsl(mynam)) {
5582 	spec_buf = outbuf;
5583 	speclen = rms_nam_rsl(mynam);
5584     }
5585     else {
5586 	spec_buf = esa; /* Not esal */
5587 	speclen = rms_nam_esl(mynam);
5588     }
5589 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5590   }
5591 #endif
5592   spec_buf[speclen] = '\0';
5593 
5594   /* Trim off null fields added by $PARSE
5595    * If type > 1 char, must have been specified in original or default spec
5596    * (not true for version; $SEARCH may have added version of existing file).
5597    */
5598   trimver  = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5599   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5600     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5601              ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5602   }
5603   else {
5604     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5605              ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5606   }
5607   if (trimver || trimtype) {
5608     if (defspec && *defspec) {
5609       char *defesal = NULL;
5610       char *defesa = NULL;
5611       defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5612       if (defesa != NULL) {
5613         struct FAB deffab = cc$rms_fab;
5614 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5615         defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5616         if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5617 #endif
5618 	rms_setup_nam(defnam);
5619 
5620 	rms_bind_fab_nam(deffab, defnam);
5621 
5622 	/* Cast ok */
5623 	rms_set_fna
5624 	    (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5625 
5626 	/* RMS needs the esa/esal as a work area if wildcards are involved */
5627 	rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5628 
5629 	rms_clear_nam_nop(defnam);
5630 	rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5631 #ifdef NAM$M_NO_SHORT_UPCASE
5632 	if (decc_efs_case_preserve)
5633 	  rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5634 #endif
5635 #ifdef NAML$M_OPEN_SPECIAL
5636 	if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5637 	  rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5638 #endif
5639 	if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5640 	  if (trimver) {
5641 	     trimver  = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5642 	  }
5643 	  if (trimtype) {
5644 	    trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5645 	  }
5646 	}
5647 	if (defesal != NULL)
5648 	    PerlMem_free(defesal);
5649 	PerlMem_free(defesa);
5650       } else {
5651           _ckvmssts_noperl(SS$_INSFMEM);
5652       }
5653     }
5654     if (trimver) {
5655       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5656 	if (*(rms_nam_verl(mynam)) != '\"')
5657 	  speclen = rms_nam_verl(mynam) - spec_buf;
5658       }
5659       else {
5660 	if (*(rms_nam_ver(mynam)) != '\"')
5661 	  speclen = rms_nam_ver(mynam) - spec_buf;
5662       }
5663     }
5664     if (trimtype) {
5665       /* If we didn't already trim version, copy down */
5666       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5667 	if (speclen > rms_nam_verl(mynam) - spec_buf)
5668 	  memmove
5669 	   (rms_nam_typel(mynam),
5670 	    rms_nam_verl(mynam),
5671 	    speclen - (rms_nam_verl(mynam) - spec_buf));
5672 	  speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5673       }
5674       else {
5675 	if (speclen > rms_nam_ver(mynam) - spec_buf)
5676 	  memmove
5677 	   (rms_nam_type(mynam),
5678 	    rms_nam_ver(mynam),
5679 	    speclen - (rms_nam_ver(mynam) - spec_buf));
5680 	  speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5681       }
5682     }
5683   }
5684 
5685    /* Done with these copies of the input files */
5686   /*-------------------------------------------*/
5687   if (vmsfspec != NULL)
5688 	PerlMem_free(vmsfspec);
5689   if (vmsdefspec != NULL)
5690 	PerlMem_free(vmsdefspec);
5691 
5692   /* If we just had a directory spec on input, $PARSE "helpfully"
5693    * adds an empty name and type for us */
5694 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5695   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5696     if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5697 	rms_nam_verl(mynam)  == rms_nam_typel(mynam) + 1 &&
5698 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5699       speclen = rms_nam_namel(mynam) - spec_buf;
5700   }
5701   else
5702 #endif
5703   {
5704     if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5705 	rms_nam_ver(mynam)  == rms_nam_ver(mynam) + 1 &&
5706 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5707       speclen = rms_nam_name(mynam) - spec_buf;
5708   }
5709 
5710   /* Posix format specifications must have matching quotes */
5711   if (speclen < (VMS_MAXRSS - 1)) {
5712     if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5713       if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5714         spec_buf[speclen] = '\"';
5715         speclen++;
5716       }
5717     }
5718   }
5719   spec_buf[speclen] = '\0';
5720   if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5721 
5722   /* Have we been working with an expanded, but not resultant, spec? */
5723   /* Also, convert back to Unix syntax if necessary. */
5724   {
5725   int rsl;
5726 
5727 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5728     if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5729       rsl = rms_nam_rsll(mynam);
5730     } else
5731 #endif
5732     {
5733       rsl = rms_nam_rsl(mynam);
5734     }
5735     if (!rsl) {
5736       /* rsl is not present, it means that spec_buf is either */
5737       /* esa or esal, and needs to be copied to outbuf */
5738       /* convert to Unix if desired */
5739       if (isunix) {
5740         ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5741       } else {
5742         /* VMS file specs are not in UTF-8 */
5743         if (fs_utf8 != NULL)
5744             *fs_utf8 = 0;
5745         my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5746         ret_spec = outbuf;
5747       }
5748     }
5749     else {
5750       /* Now spec_buf is either outbuf or outbufl */
5751       /* We need the result into outbuf */
5752       if (isunix) {
5753            /* If we need this in UNIX, then we need another buffer */
5754            /* to keep things in order */
5755            char * src;
5756            char * new_src = NULL;
5757            if (spec_buf == outbuf) {
5758                new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5759                my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5760            } else {
5761                src = spec_buf;
5762            }
5763            ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5764            if (new_src) {
5765                PerlMem_free(new_src);
5766            }
5767       } else {
5768            /* VMS file specs are not in UTF-8 */
5769            if (fs_utf8 != NULL)
5770                *fs_utf8 = 0;
5771 
5772            /* Copy the buffer if needed */
5773            if (outbuf != spec_buf)
5774                my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5775            ret_spec = outbuf;
5776       }
5777     }
5778   }
5779 
5780   /* Need to clean up the search context */
5781   rms_set_rsal(mynam, NULL, 0, NULL, 0);
5782   sts = rms_free_search_context(&myfab); /* Free search context */
5783 
5784   /* Clean up the extra buffers */
5785   if (esal != NULL)
5786       PerlMem_free(esal);
5787   PerlMem_free(esa);
5788   if (outbufl != NULL)
5789      PerlMem_free(outbufl);
5790 
5791   /* Return the result */
5792   return ret_spec;
5793 }
5794 
5795 /* Common simple case - Expand an already VMS spec */
5796 static char *
5797 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5798     opts |= PERL_RMSEXPAND_M_VMS_IN;
5799     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5800 }
5801 
5802 /* Common simple case - Expand to a VMS spec */
5803 static char *
5804 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5805     opts |= PERL_RMSEXPAND_M_VMS;
5806     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5807 }
5808 
5809 
5810 /* Entry point used by perl routines */
5811 static char *
5812 mp_do_rmsexpand
5813    (pTHX_ const char *filespec,
5814     char *outbuf,
5815     int ts,
5816     const char *defspec,
5817     unsigned opts,
5818     int * fs_utf8,
5819     int * dfs_utf8)
5820 {
5821     static char __rmsexpand_retbuf[VMS_MAXRSS];
5822     char * expanded, *ret_spec, *ret_buf;
5823 
5824     expanded = NULL;
5825     ret_buf = outbuf;
5826     if (ret_buf == NULL) {
5827         if (ts) {
5828             Newx(expanded, VMS_MAXRSS, char);
5829             if (expanded == NULL)
5830                 _ckvmssts(SS$_INSFMEM);
5831             ret_buf = expanded;
5832         } else {
5833             ret_buf = __rmsexpand_retbuf;
5834         }
5835     }
5836 
5837 
5838     ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5839                              opts, fs_utf8,  dfs_utf8);
5840 
5841     if (ret_spec == NULL) {
5842        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5843        if (expanded)
5844            Safefree(expanded);
5845     }
5846 
5847     return ret_spec;
5848 }
5849 /*}}}*/
5850 /* External entry points */
5851 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5852 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5853 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5854 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5855 char *Perl_rmsexpand_utf8
5856   (pTHX_ const char *spec, char *buf, const char *def,
5857    unsigned opt, int * fs_utf8, int * dfs_utf8)
5858 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5859 char *Perl_rmsexpand_utf8_ts
5860   (pTHX_ const char *spec, char *buf, const char *def,
5861    unsigned opt, int * fs_utf8, int * dfs_utf8)
5862 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5863 
5864 
5865 /*
5866 ** The following routines are provided to make life easier when
5867 ** converting among VMS-style and Unix-style directory specifications.
5868 ** All will take input specifications in either VMS or Unix syntax. On
5869 ** failure, all return NULL.  If successful, the routines listed below
5870 ** return a pointer to a buffer containing the appropriately
5871 ** reformatted spec (and, therefore, subsequent calls to that routine
5872 ** will clobber the result), while the routines of the same names with
5873 ** a _ts suffix appended will return a pointer to a mallocd string
5874 ** containing the appropriately reformatted spec.
5875 ** In all cases, only explicit syntax is altered; no check is made that
5876 ** the resulting string is valid or that the directory in question
5877 ** actually exists.
5878 **
5879 **   fileify_dirspec() - convert a directory spec into the name of the
5880 **     directory file (i.e. what you can stat() to see if it's a dir).
5881 **     The style (VMS or Unix) of the result is the same as the style
5882 **     of the parameter passed in.
5883 **   pathify_dirspec() - convert a directory spec into a path (i.e.
5884 **     what you prepend to a filename to indicate what directory it's in).
5885 **     The style (VMS or Unix) of the result is the same as the style
5886 **     of the parameter passed in.
5887 **   tounixpath() - convert a directory spec into a Unix-style path.
5888 **   tovmspath() - convert a directory spec into a VMS-style path.
5889 **   tounixspec() - convert any file spec into a Unix-style file spec.
5890 **   tovmsspec() - convert any file spec into a VMS-style spec.
5891 **   xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5892 **
5893 ** Copyright 1996 by Charles Bailey  <bailey@newman.upenn.edu>
5894 ** Permission is given to distribute this code as part of the Perl
5895 ** standard distribution under the terms of the GNU General Public
5896 ** License or the Perl Artistic License.  Copies of each may be
5897 ** found in the Perl standard distribution.
5898  */
5899 
5900 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5901 static char *
5902 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5903 {
5904     unsigned long int dirlen, retlen, hasfilename = 0;
5905     char *cp1, *cp2, *lastdir;
5906     char *trndir, *vmsdir;
5907     unsigned short int trnlnm_iter_count;
5908     int sts;
5909     if (utf8_fl != NULL)
5910 	*utf8_fl = 0;
5911 
5912     if (!dir || !*dir) {
5913       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5914     }
5915     dirlen = strlen(dir);
5916     while (dirlen && dir[dirlen-1] == '/') --dirlen;
5917     if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5918       if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5919         dir = "/sys$disk";
5920         dirlen = 9;
5921       }
5922       else
5923 	dirlen = 1;
5924     }
5925     if (dirlen > (VMS_MAXRSS - 1)) {
5926       set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5927       return NULL;
5928     }
5929     trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5930     if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5931     if (!strpbrk(dir+1,"/]>:")  &&
5932 	(!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5933       strcpy(trndir,*dir == '/' ? dir + 1: dir);
5934       trnlnm_iter_count = 0;
5935       while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5936         trnlnm_iter_count++;
5937         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5938       }
5939       dirlen = strlen(trndir);
5940     }
5941     else {
5942       memcpy(trndir, dir, dirlen);
5943       trndir[dirlen] = '\0';
5944     }
5945 
5946     /* At this point we are done with *dir and use *trndir which is a
5947      * copy that can be modified.  *dir must not be modified.
5948      */
5949 
5950     /* If we were handed a rooted logical name or spec, treat it like a
5951      * simple directory, so that
5952      *    $ Define myroot dev:[dir.]
5953      *    ... do_fileify_dirspec("myroot",buf,1) ...
5954      * does something useful.
5955      */
5956     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5957       trndir[--dirlen] = '\0';
5958       trndir[dirlen-1] = ']';
5959     }
5960     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5961       trndir[--dirlen] = '\0';
5962       trndir[dirlen-1] = '>';
5963     }
5964 
5965     if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5966       /* If we've got an explicit filename, we can just shuffle the string. */
5967       if (*(cp1+1)) hasfilename = 1;
5968       /* Similarly, we can just back up a level if we've got multiple levels
5969          of explicit directories in a VMS spec which ends with directories. */
5970       else {
5971         for (cp2 = cp1; cp2 > trndir; cp2--) {
5972 	  if (*cp2 == '.') {
5973 	    if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5974 /* fix-me, can not scan EFS file specs backward like this */
5975               *cp2 = *cp1; *cp1 = '\0';
5976               hasfilename = 1;
5977 	      break;
5978 	    }
5979           }
5980           if (*cp2 == '[' || *cp2 == '<') break;
5981         }
5982       }
5983     }
5984 
5985     vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5986     if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5987     cp1 = strpbrk(trndir,"]:>");
5988     if (cp1 && *(cp1+1) == ':')   /* DECNet node spec with :: */
5989         cp1 = strpbrk(cp1+2,"]:>");
5990 
5991     if (hasfilename || !cp1) { /* filename present or not VMS */
5992 
5993       if (trndir[0] == '.') {
5994         if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5995 	  PerlMem_free(trndir);
5996 	  PerlMem_free(vmsdir);
5997           return int_fileify_dirspec("[]", buf, NULL);
5998 	}
5999         else if (trndir[1] == '.' &&
6000                (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6001 	  PerlMem_free(trndir);
6002 	  PerlMem_free(vmsdir);
6003           return int_fileify_dirspec("[-]", buf, NULL);
6004 	}
6005       }
6006       if (dirlen && trndir[dirlen-1] == '/') {    /* path ends with '/'; just add .dir;1 */
6007         dirlen -= 1;                 /* to last element */
6008         lastdir = strrchr(trndir,'/');
6009       }
6010       else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6011         /* If we have "/." or "/..", VMSify it and let the VMS code
6012          * below expand it, rather than repeating the code to handle
6013          * relative components of a filespec here */
6014         do {
6015           if (*(cp1+2) == '.') cp1++;
6016           if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6017 	    char * ret_chr;
6018             if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6019 		PerlMem_free(trndir);
6020 		PerlMem_free(vmsdir);
6021 		return NULL;
6022 	    }
6023             if (strchr(vmsdir,'/') != NULL) {
6024               /* If int_tovmsspec() returned it, it must have VMS syntax
6025                * delimiters in it, so it's a mixed VMS/Unix spec.  We take
6026                * the time to check this here only so we avoid a recursion
6027                * loop; otherwise, gigo.
6028                */
6029 	      PerlMem_free(trndir);
6030 	      PerlMem_free(vmsdir);
6031               set_errno(EINVAL);  set_vaxc_errno(RMS$_SYN);
6032 	      return NULL;
6033             }
6034             if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6035 		PerlMem_free(trndir);
6036 		PerlMem_free(vmsdir);
6037 		return NULL;
6038 	    }
6039 	    ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6040 	    PerlMem_free(trndir);
6041 	    PerlMem_free(vmsdir);
6042             return ret_chr;
6043           }
6044           cp1++;
6045         } while ((cp1 = strstr(cp1,"/.")) != NULL);
6046         lastdir = strrchr(trndir,'/');
6047       }
6048       else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6049 	char * ret_chr;
6050         /* Ditto for specs that end in an MFD -- let the VMS code
6051          * figure out whether it's a real device or a rooted logical. */
6052 
6053         /* This should not happen any more.  Allowing the fake /000000
6054          * in a UNIX pathname causes all sorts of problems when trying
6055          * to run in UNIX emulation.  So the VMS to UNIX conversions
6056          * now remove the fake /000000 directories.
6057          */
6058 
6059         trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6060         if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6061 	    PerlMem_free(trndir);
6062 	    PerlMem_free(vmsdir);
6063 	    return NULL;
6064 	}
6065         if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6066 	    PerlMem_free(trndir);
6067 	    PerlMem_free(vmsdir);
6068 	    return NULL;
6069 	}
6070 	ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6071 	PerlMem_free(trndir);
6072 	PerlMem_free(vmsdir);
6073         return ret_chr;
6074       }
6075       else {
6076 
6077         if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6078              !(lastdir = cp1 = strrchr(trndir,']')) &&
6079              !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6080 
6081         cp2 = strrchr(cp1,'.');
6082         if (cp2) {
6083             int e_len, vs_len = 0;
6084             int is_dir = 0;
6085             char * cp3;
6086             cp3 = strchr(cp2,';');
6087             e_len = strlen(cp2);
6088             if (cp3) {
6089                 vs_len = strlen(cp3);
6090                 e_len = e_len - vs_len;
6091             }
6092             is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6093             if (!is_dir) {
6094                 if (!decc_efs_charset) {
6095                     /* If this is not EFS, then not a directory */
6096                     PerlMem_free(trndir);
6097                     PerlMem_free(vmsdir);
6098                     set_errno(ENOTDIR);
6099                     set_vaxc_errno(RMS$_DIR);
6100                     return NULL;
6101                 }
6102             } else {
6103                 /* Ok, here we have an issue, technically if a .dir shows */
6104                 /* from inside a directory, then we should treat it as */
6105                 /* xxx^.dir.dir.  But we do not have that context at this */
6106                 /* point unless this is totally restructured, so we remove */
6107                 /* The .dir for now, and fix this better later */
6108                 dirlen = cp2 - trndir;
6109             }
6110             if (decc_efs_charset && !strchr(trndir,'/')) {
6111                 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6112                 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6113 
6114                 for (; cp4 > cp1; cp4--) {
6115                     if (*cp4 == '.') {
6116                         if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6117                             memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6118                             *cp4 = '^';
6119                             dirlen++;
6120 	                }
6121                     }
6122                 }
6123             }
6124         }
6125 
6126       }
6127 
6128       retlen = dirlen + 6;
6129       memcpy(buf, trndir, dirlen);
6130       buf[dirlen] = '\0';
6131 
6132       /* We've picked up everything up to the directory file name.
6133          Now just add the type and version, and we're set. */
6134       if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6135           strcat(buf,".dir");
6136       else
6137           strcat(buf,".DIR");
6138       if (!decc_filename_unix_no_version)
6139           strcat(buf,";1");
6140       PerlMem_free(trndir);
6141       PerlMem_free(vmsdir);
6142       return buf;
6143     }
6144     else {  /* VMS-style directory spec */
6145 
6146       char *esa, *esal, term, *cp;
6147       char *my_esa;
6148       int my_esa_len;
6149       unsigned long int cmplen, haslower = 0;
6150       struct FAB dirfab = cc$rms_fab;
6151       rms_setup_nam(savnam);
6152       rms_setup_nam(dirnam);
6153 
6154       esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6155       if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6156       esal = NULL;
6157 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6158       esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6159       if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6160 #endif
6161       rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6162       rms_bind_fab_nam(dirfab, dirnam);
6163       rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6164       rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6165 #ifdef NAM$M_NO_SHORT_UPCASE
6166       if (decc_efs_case_preserve)
6167 	rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6168 #endif
6169 
6170       for (cp = trndir; *cp; cp++)
6171         if (islower(*cp)) { haslower = 1; break; }
6172       if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6173         if ((dirfab.fab$l_sts == RMS$_DIR) ||
6174             (dirfab.fab$l_sts == RMS$_DNF) ||
6175             (dirfab.fab$l_sts == RMS$_PRV)) {
6176             rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6177             sts = sys$parse(&dirfab);
6178         }
6179         if (!sts) {
6180 	  PerlMem_free(esa);
6181 	  if (esal != NULL)
6182 	      PerlMem_free(esal);
6183 	  PerlMem_free(trndir);
6184 	  PerlMem_free(vmsdir);
6185           set_errno(EVMSERR);
6186           set_vaxc_errno(dirfab.fab$l_sts);
6187           return NULL;
6188         }
6189       }
6190       else {
6191         savnam = dirnam;
6192 	/* Does the file really exist? */
6193         if (sys$search(&dirfab)& STS$K_SUCCESS) {
6194           /* Yes; fake the fnb bits so we'll check type below */
6195           rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6196         }
6197         else { /* No; just work with potential name */
6198           if (dirfab.fab$l_sts    == RMS$_FNF
6199               || dirfab.fab$l_sts == RMS$_DNF
6200               || dirfab.fab$l_sts == RMS$_FND)
6201                 dirnam = savnam;
6202           else {
6203 	    int fab_sts;
6204 	    fab_sts = dirfab.fab$l_sts;
6205 	    sts = rms_free_search_context(&dirfab);
6206 	    PerlMem_free(esa);
6207 	    if (esal != NULL)
6208 		PerlMem_free(esal);
6209 	    PerlMem_free(trndir);
6210 	    PerlMem_free(vmsdir);
6211             set_errno(EVMSERR);  set_vaxc_errno(fab_sts);
6212             return NULL;
6213           }
6214         }
6215       }
6216 
6217       /* Make sure we are using the right buffer */
6218 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6219       if (esal != NULL) {
6220 	my_esa = esal;
6221 	my_esa_len = rms_nam_esll(dirnam);
6222       } else {
6223 #endif
6224 	my_esa = esa;
6225         my_esa_len = rms_nam_esl(dirnam);
6226 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6227       }
6228 #endif
6229       my_esa[my_esa_len] = '\0';
6230       if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6231         cp1 = strchr(my_esa,']');
6232         if (!cp1) cp1 = strchr(my_esa,'>');
6233         if (cp1) {  /* Should always be true */
6234           my_esa_len -= cp1 - my_esa - 1;
6235           memmove(my_esa, cp1 + 1, my_esa_len);
6236         }
6237       }
6238       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) {  /* Was type specified? */
6239         /* Yep; check version while we're at it, if it's there. */
6240         cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6241         if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6242           /* Something other than .DIR[;1].  Bzzt. */
6243 	  sts = rms_free_search_context(&dirfab);
6244 	  PerlMem_free(esa);
6245 	  if (esal != NULL)
6246 	     PerlMem_free(esal);
6247 	  PerlMem_free(trndir);
6248 	  PerlMem_free(vmsdir);
6249           set_errno(ENOTDIR);
6250           set_vaxc_errno(RMS$_DIR);
6251           return NULL;
6252         }
6253       }
6254 
6255       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6256         /* They provided at least the name; we added the type, if necessary, */
6257         my_strlcpy(buf, my_esa, VMS_MAXRSS);
6258 	sts = rms_free_search_context(&dirfab);
6259 	PerlMem_free(trndir);
6260 	PerlMem_free(esa);
6261 	if (esal != NULL)
6262 	    PerlMem_free(esal);
6263 	PerlMem_free(vmsdir);
6264         return buf;
6265       }
6266       if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6267         for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6268         *cp1 = '\0';
6269         my_esa_len -= 9;
6270       }
6271       if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6272       if (cp1 == NULL) { /* should never happen */
6273 	sts = rms_free_search_context(&dirfab);
6274 	PerlMem_free(trndir);
6275 	PerlMem_free(esa);
6276 	if (esal != NULL)
6277 	    PerlMem_free(esal);
6278 	PerlMem_free(vmsdir);
6279         return NULL;
6280       }
6281       term = *cp1;
6282       *cp1 = '\0';
6283       retlen = strlen(my_esa);
6284       cp1 = strrchr(my_esa,'.');
6285       /* ODS-5 directory specifications can have extra "." in them. */
6286       /* Fix-me, can not scan EFS file specifications backwards */
6287       while (cp1 != NULL) {
6288         if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6289 	  break;
6290 	else {
6291 	   cp1--;
6292 	   while ((cp1 > my_esa) && (*cp1 != '.'))
6293 	     cp1--;
6294 	}
6295 	if (cp1 == my_esa)
6296 	  cp1 = NULL;
6297       }
6298 
6299       if ((cp1) != NULL) {
6300         /* There's more than one directory in the path.  Just roll back. */
6301         *cp1 = term;
6302         my_strlcpy(buf, my_esa, VMS_MAXRSS);
6303       }
6304       else {
6305         if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6306           /* Go back and expand rooted logical name */
6307           rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6308 #ifdef NAM$M_NO_SHORT_UPCASE
6309 	  if (decc_efs_case_preserve)
6310 	    rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6311 #endif
6312           if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6313 	    sts = rms_free_search_context(&dirfab);
6314 	    PerlMem_free(esa);
6315 	    if (esal != NULL)
6316 		PerlMem_free(esal);
6317 	    PerlMem_free(trndir);
6318 	    PerlMem_free(vmsdir);
6319             set_errno(EVMSERR);
6320             set_vaxc_errno(dirfab.fab$l_sts);
6321             return NULL;
6322           }
6323 
6324 	  /* This changes the length of the string of course */
6325 	  if (esal != NULL) {
6326 	      my_esa_len = rms_nam_esll(dirnam);
6327 	  } else {
6328 	      my_esa_len = rms_nam_esl(dirnam);
6329 	  }
6330 
6331           retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6332           cp1 = strstr(my_esa,"][");
6333           if (!cp1) cp1 = strstr(my_esa,"]<");
6334           dirlen = cp1 - my_esa;
6335           memcpy(buf, my_esa, dirlen);
6336           if (!strncmp(cp1+2,"000000]",7)) {
6337             buf[dirlen-1] = '\0';
6338 	    /* fix-me Not full ODS-5, just extra dots in directories for now */
6339 	    cp1 = buf + dirlen - 1;
6340 	    while (cp1 > buf)
6341 	    {
6342 	      if (*cp1 == '[')
6343 		break;
6344 	      if (*cp1 == '.') {
6345 		if (*(cp1-1) != '^')
6346 		  break;
6347 	      }
6348 	      cp1--;
6349 	    }
6350             if (*cp1 == '.') *cp1 = ']';
6351             else {
6352               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6353               memmove(cp1+1,"000000]",7);
6354             }
6355           }
6356           else {
6357             memmove(buf+dirlen, cp1+2, retlen-dirlen);
6358             buf[retlen] = '\0';
6359             /* Convert last '.' to ']' */
6360             cp1 = buf+retlen-1;
6361 	    while (*cp != '[') {
6362 	      cp1--;
6363 	      if (*cp1 == '.') {
6364 		/* Do not trip on extra dots in ODS-5 directories */
6365 		if ((cp1 == buf) || (*(cp1-1) != '^'))
6366 		break;
6367 	      }
6368 	    }
6369             if (*cp1 == '.') *cp1 = ']';
6370             else {
6371               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6372               memmove(cp1+1,"000000]",7);
6373             }
6374           }
6375         }
6376         else {  /* This is a top-level dir.  Add the MFD to the path. */
6377           cp1 = strrchr(my_esa, ':');
6378           assert(cp1);
6379           memmove(buf, my_esa, cp1 - my_esa + 1);
6380           memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6381           memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6382           buf[retlen + 7] = '\0';  /* We've inserted '000000]' */
6383         }
6384       }
6385       sts = rms_free_search_context(&dirfab);
6386       /* We've set up the string up through the filename.  Add the
6387          type and version, and we're done. */
6388       strcat(buf,".DIR;1");
6389 
6390       /* $PARSE may have upcased filespec, so convert output to lower
6391        * case if input contained any lowercase characters. */
6392       if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6393       PerlMem_free(trndir);
6394       PerlMem_free(esa);
6395       if (esal != NULL)
6396 	PerlMem_free(esal);
6397       PerlMem_free(vmsdir);
6398       return buf;
6399     }
6400 }  /* end of int_fileify_dirspec() */
6401 
6402 
6403 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6404 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6405 {
6406     static char __fileify_retbuf[VMS_MAXRSS];
6407     char * fileified, *ret_spec, *ret_buf;
6408 
6409     fileified = NULL;
6410     ret_buf = buf;
6411     if (ret_buf == NULL) {
6412         if (ts) {
6413             Newx(fileified, VMS_MAXRSS, char);
6414             if (fileified == NULL)
6415                 _ckvmssts(SS$_INSFMEM);
6416             ret_buf = fileified;
6417         } else {
6418             ret_buf = __fileify_retbuf;
6419         }
6420     }
6421 
6422     ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6423 
6424     if (ret_spec == NULL) {
6425        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6426        if (fileified)
6427            Safefree(fileified);
6428     }
6429 
6430     return ret_spec;
6431 }  /* end of do_fileify_dirspec() */
6432 /*}}}*/
6433 
6434 /* External entry points */
6435 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6436 { return do_fileify_dirspec(dir,buf,0,NULL); }
6437 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6438 { return do_fileify_dirspec(dir,buf,1,NULL); }
6439 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6440 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6441 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6442 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6443 
6444 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6445     char * v_spec, int v_len, char * r_spec, int r_len,
6446     char * d_spec, int d_len, char * n_spec, int n_len,
6447     char * e_spec, int e_len, char * vs_spec, int vs_len) {
6448 
6449     /* VMS specification - Try to do this the simple way */
6450     if ((v_len + r_len > 0) || (d_len > 0)) {
6451         int is_dir;
6452 
6453         /* No name or extension component, already a directory */
6454         if ((n_len + e_len + vs_len) == 0) {
6455             strcpy(buf, dir);
6456             return buf;
6457         }
6458 
6459         /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6460         /* This results from catfile() being used instead of catdir() */
6461         /* So even though it should not work, we need to allow it */
6462 
6463         /* If this is .DIR;1 then do a simple conversion */
6464         is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6465         if (is_dir || (e_len == 0) && (d_len > 0)) {
6466              int len;
6467              len = v_len + r_len + d_len - 1;
6468              char dclose = d_spec[d_len - 1];
6469              memcpy(buf, dir, len);
6470              buf[len] = '.';
6471              len++;
6472              memcpy(&buf[len], n_spec, n_len);
6473              len += n_len;
6474              buf[len] = dclose;
6475              buf[len + 1] = '\0';
6476              return buf;
6477         }
6478 
6479 #ifdef HAS_SYMLINK
6480         else if (d_len > 0) {
6481             /* In the olden days, a directory needed to have a .DIR */
6482             /* extension to be a valid directory, but now it could  */
6483             /* be a symbolic link */
6484             int len;
6485             len = v_len + r_len + d_len - 1;
6486             char dclose = d_spec[d_len - 1];
6487             memcpy(buf, dir, len);
6488             buf[len] = '.';
6489             len++;
6490             memcpy(&buf[len], n_spec, n_len);
6491             len += n_len;
6492             if (e_len > 0) {
6493                 if (decc_efs_charset) {
6494                     if (e_len == 4
6495                         && (toupper(e_spec[1]) == 'D')
6496                         && (toupper(e_spec[2]) == 'I')
6497                         && (toupper(e_spec[3]) == 'R')) {
6498 
6499                         /* Corner case: directory spec with invalid version.
6500                          * Valid would have followed is_dir path above.
6501                          */
6502                         SETERRNO(ENOTDIR, RMS$_DIR);
6503                         return NULL;
6504                     }
6505                     else {
6506                         buf[len] = '^';
6507                         len++;
6508                         memcpy(&buf[len], e_spec, e_len);
6509                         len += e_len;
6510                     }
6511                 }
6512                 else {
6513                     SETERRNO(ENOTDIR, RMS$_DIR);
6514                     return NULL;
6515                 }
6516             }
6517             buf[len] = dclose;
6518             buf[len + 1] = '\0';
6519             return buf;
6520         }
6521 #else
6522         else {
6523             set_vaxc_errno(RMS$_DIR);
6524             set_errno(ENOTDIR);
6525             return NULL;
6526         }
6527 #endif
6528     }
6529     set_vaxc_errno(RMS$_DIR);
6530     set_errno(ENOTDIR);
6531     return NULL;
6532 }
6533 
6534 
6535 /* Internal routine to make sure or convert a directory to be in a */
6536 /* path specification.  No utf8 flag because it is not changed or used */
6537 static char *int_pathify_dirspec(const char *dir, char *buf)
6538 {
6539     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6540     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6541     char * exp_spec, *ret_spec;
6542     char * trndir;
6543     unsigned short int trnlnm_iter_count;
6544     STRLEN trnlen;
6545     int need_to_lower;
6546 
6547     if (vms_debug_fileify) {
6548         if (dir == NULL)
6549             fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6550         else
6551             fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6552     }
6553 
6554     /* We may need to lower case the result if we translated  */
6555     /* a logical name or got the current working directory */
6556     need_to_lower = 0;
6557 
6558     if (!dir || !*dir) {
6559       set_errno(EINVAL);
6560       set_vaxc_errno(SS$_BADPARAM);
6561       return NULL;
6562     }
6563 
6564     trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6565     if (trndir == NULL)
6566         _ckvmssts_noperl(SS$_INSFMEM);
6567 
6568     /* If no directory specified use the current default */
6569     if (*dir)
6570         my_strlcpy(trndir, dir, VMS_MAXRSS);
6571     else {
6572         getcwd(trndir, VMS_MAXRSS - 1);
6573         need_to_lower = 1;
6574     }
6575 
6576     /* now deal with bare names that could be logical names */
6577     trnlnm_iter_count = 0;
6578     while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6579            && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6580         trnlnm_iter_count++;
6581         need_to_lower = 1;
6582         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6583             break;
6584         trnlen = strlen(trndir);
6585 
6586         /* Trap simple rooted lnms, and return lnm:[000000] */
6587         if (!strcmp(trndir+trnlen-2,".]")) {
6588             my_strlcpy(buf, dir, VMS_MAXRSS);
6589             strcat(buf, ":[000000]");
6590             PerlMem_free(trndir);
6591 
6592             if (vms_debug_fileify) {
6593                 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6594             }
6595             return buf;
6596         }
6597     }
6598 
6599     /* At this point we do not work with *dir, but the copy in  *trndir */
6600 
6601     if (need_to_lower && !decc_efs_case_preserve) {
6602         /* Legacy mode, lower case the returned value */
6603         __mystrtolower(trndir);
6604     }
6605 
6606 
6607     /* Some special cases, '..', '.' */
6608     sts = 0;
6609     if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6610        /* Force UNIX filespec */
6611        sts = 1;
6612 
6613     } else {
6614         /* Is this Unix or VMS format? */
6615         sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6616                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6617                              &e_len, &vs_spec, &vs_len);
6618         if (sts == 0) {
6619 
6620             /* Just a filename? */
6621             if ((v_len + r_len + d_len) == 0) {
6622 
6623                 /* Now we have a problem, this could be Unix or VMS */
6624                 /* We have to guess.  .DIR usually means VMS */
6625 
6626                 /* In UNIX report mode, the .DIR extension is removed */
6627                 /* if one shows up, it is for a non-directory or a directory */
6628                 /* in EFS charset mode */
6629 
6630                 /* So if we are in Unix report mode, assume that this */
6631                 /* is a relative Unix directory specification */
6632 
6633                 sts = 1;
6634                 if (!decc_filename_unix_report && decc_efs_charset) {
6635                     int is_dir;
6636                     is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6637 
6638                     if (is_dir) {
6639                         /* Traditional mode, assume .DIR is directory */
6640                         buf[0] = '[';
6641                         buf[1] = '.';
6642                         memcpy(&buf[2], n_spec, n_len);
6643                         buf[n_len + 2] = ']';
6644                         buf[n_len + 3] = '\0';
6645                         PerlMem_free(trndir);
6646                         if (vms_debug_fileify) {
6647                             fprintf(stderr,
6648                                     "int_pathify_dirspec: buf = %s\n",
6649                                     buf);
6650                         }
6651                         return buf;
6652                     }
6653                 }
6654             }
6655         }
6656     }
6657     if (sts == 0) {
6658         ret_spec = int_pathify_dirspec_simple(trndir, buf,
6659             v_spec, v_len, r_spec, r_len,
6660             d_spec, d_len, n_spec, n_len,
6661             e_spec, e_len, vs_spec, vs_len);
6662 
6663         if (ret_spec != NULL) {
6664             PerlMem_free(trndir);
6665             if (vms_debug_fileify) {
6666                 fprintf(stderr,
6667                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6668             }
6669             return ret_spec;
6670         }
6671 
6672         /* Simple way did not work, which means that a logical name */
6673         /* was present for the directory specification.             */
6674         /* Need to use an rmsexpand variant to decode it completely */
6675         exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6676         if (exp_spec == NULL)
6677             _ckvmssts_noperl(SS$_INSFMEM);
6678 
6679         ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6680         if (ret_spec != NULL) {
6681             sts = vms_split_path(exp_spec, &v_spec, &v_len,
6682                                  &r_spec, &r_len, &d_spec, &d_len,
6683                                  &n_spec, &n_len, &e_spec,
6684                                  &e_len, &vs_spec, &vs_len);
6685             if (sts == 0) {
6686                 ret_spec = int_pathify_dirspec_simple(
6687                     exp_spec, buf, v_spec, v_len, r_spec, r_len,
6688                     d_spec, d_len, n_spec, n_len,
6689                     e_spec, e_len, vs_spec, vs_len);
6690 
6691                 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6692                     /* Legacy mode, lower case the returned value */
6693                     __mystrtolower(ret_spec);
6694                 }
6695             } else {
6696                 set_vaxc_errno(RMS$_DIR);
6697                 set_errno(ENOTDIR);
6698                 ret_spec = NULL;
6699             }
6700         }
6701         PerlMem_free(exp_spec);
6702         PerlMem_free(trndir);
6703         if (vms_debug_fileify) {
6704             if (ret_spec == NULL)
6705                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6706             else
6707                 fprintf(stderr,
6708                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6709         }
6710         return ret_spec;
6711 
6712     } else {
6713         /* Unix specification, Could be trivial conversion, */
6714         /* but have to deal with trailing '.dir' or extra '.' */
6715 
6716         char * lastdot;
6717         char * lastslash;
6718         int is_dir;
6719         STRLEN dir_len = strlen(trndir);
6720 
6721         lastslash = strrchr(trndir, '/');
6722         if (lastslash == NULL)
6723             lastslash = trndir;
6724         else
6725             lastslash++;
6726 
6727         lastdot = NULL;
6728 
6729         /* '..' or '.' are valid directory components */
6730         is_dir = 0;
6731         if (lastslash[0] == '.') {
6732             if (lastslash[1] == '\0') {
6733                is_dir = 1;
6734             } else if (lastslash[1] == '.') {
6735                 if (lastslash[2] == '\0') {
6736                     is_dir = 1;
6737                 } else {
6738                     /* And finally allow '...' */
6739                     if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6740                         is_dir = 1;
6741                     }
6742                 }
6743             }
6744         }
6745 
6746         if (!is_dir) {
6747            lastdot = strrchr(lastslash, '.');
6748         }
6749         if (lastdot != NULL) {
6750             STRLEN e_len;
6751              /* '.dir' is discarded, and any other '.' is invalid */
6752             e_len = strlen(lastdot);
6753 
6754             is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6755 
6756             if (is_dir) {
6757                 dir_len = dir_len - 4;
6758             }
6759         }
6760 
6761         my_strlcpy(buf, trndir, VMS_MAXRSS);
6762         if (buf[dir_len - 1] != '/') {
6763             buf[dir_len] = '/';
6764             buf[dir_len + 1] = '\0';
6765         }
6766 
6767         /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6768         if (!decc_efs_charset) {
6769              int dir_start = 0;
6770              char * str = buf;
6771              if (str[0] == '.') {
6772                  char * dots = str;
6773                  int cnt = 1;
6774                  while ((dots[cnt] == '.') && (cnt < 3))
6775                      cnt++;
6776                  if (cnt <= 3) {
6777                      if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6778                          dir_start = 1;
6779                          str += cnt;
6780                      }
6781                  }
6782              }
6783              for (; *str; ++str) {
6784                  while (*str == '/') {
6785                      dir_start = 1;
6786                      *str++;
6787                  }
6788                  if (dir_start) {
6789 
6790                      /* Have to skip up to three dots which could be */
6791                      /* directories, 3 dots being a VMS extension for Perl */
6792                      char * dots = str;
6793                      int cnt = 0;
6794                      while ((dots[cnt] == '.') && (cnt < 3)) {
6795                          cnt++;
6796                      }
6797                      if (dots[cnt] == '\0')
6798                          break;
6799                      if ((cnt > 1) && (dots[cnt] != '/')) {
6800                          dir_start = 0;
6801                      } else {
6802                          str += cnt;
6803                      }
6804 
6805                      /* too many dots? */
6806                      if ((cnt == 0) || (cnt > 3)) {
6807                          dir_start = 0;
6808                      }
6809                  }
6810                  if (!dir_start && (*str == '.')) {
6811                      *str = '_';
6812                  }
6813              }
6814         }
6815         PerlMem_free(trndir);
6816         ret_spec = buf;
6817         if (vms_debug_fileify) {
6818             if (ret_spec == NULL)
6819                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6820             else
6821                 fprintf(stderr,
6822                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6823         }
6824         return ret_spec;
6825     }
6826 }
6827 
6828 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6829 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6830 {
6831     static char __pathify_retbuf[VMS_MAXRSS];
6832     char * pathified, *ret_spec, *ret_buf;
6833 
6834     pathified = NULL;
6835     ret_buf = buf;
6836     if (ret_buf == NULL) {
6837         if (ts) {
6838             Newx(pathified, VMS_MAXRSS, char);
6839             if (pathified == NULL)
6840                 _ckvmssts(SS$_INSFMEM);
6841             ret_buf = pathified;
6842         } else {
6843             ret_buf = __pathify_retbuf;
6844         }
6845     }
6846 
6847     ret_spec = int_pathify_dirspec(dir, ret_buf);
6848 
6849     if (ret_spec == NULL) {
6850        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6851        if (pathified)
6852            Safefree(pathified);
6853     }
6854 
6855     return ret_spec;
6856 
6857 }  /* end of do_pathify_dirspec() */
6858 
6859 
6860 /* External entry points */
6861 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6862 { return do_pathify_dirspec(dir,buf,0,NULL); }
6863 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6864 { return do_pathify_dirspec(dir,buf,1,NULL); }
6865 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6866 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6867 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6868 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6869 
6870 /* Internal tounixspec routine that does not use a thread context */
6871 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6872 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6873 {
6874   char *dirend, *cp1, *cp3, *tmp;
6875   const char *cp2;
6876   int dirlen;
6877   unsigned short int trnlnm_iter_count;
6878   int cmp_rslt, outchars_added;
6879   if (utf8_fl != NULL)
6880     *utf8_fl = 0;
6881 
6882   if (vms_debug_fileify) {
6883       if (spec == NULL)
6884           fprintf(stderr, "int_tounixspec: spec = NULL\n");
6885       else
6886           fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6887   }
6888 
6889 
6890   if (spec == NULL) {
6891       set_errno(EINVAL);
6892       set_vaxc_errno(SS$_BADPARAM);
6893       return NULL;
6894   }
6895   if (strlen(spec) > (VMS_MAXRSS-1)) {
6896       set_errno(E2BIG);
6897       set_vaxc_errno(SS$_BUFFEROVF);
6898       return NULL;
6899   }
6900 
6901   /* New VMS specific format needs translation
6902    * glob passes filenames with trailing '\n' and expects this preserved.
6903    */
6904   if (decc_posix_compliant_pathnames) {
6905     if (strncmp(spec, "\"^UP^", 5) == 0) {
6906       char * uspec;
6907       char *tunix;
6908       int tunix_len;
6909       int nl_flag;
6910 
6911       tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6912       if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6913       tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6914       nl_flag = 0;
6915       if (tunix[tunix_len - 1] == '\n') {
6916 	tunix[tunix_len - 1] = '\"';
6917 	tunix[tunix_len] = '\0';
6918 	tunix_len--;
6919 	nl_flag = 1;
6920       }
6921       uspec = decc$translate_vms(tunix);
6922       PerlMem_free(tunix);
6923       if ((int)uspec > 0) {
6924 	my_strlcpy(rslt, uspec, VMS_MAXRSS);
6925 	if (nl_flag) {
6926 	  strcat(rslt,"\n");
6927 	}
6928 	else {
6929 	  /* If we can not translate it, makemaker wants as-is */
6930 	  my_strlcpy(rslt, spec, VMS_MAXRSS);
6931 	}
6932 	return rslt;
6933       }
6934     }
6935   }
6936 
6937   cmp_rslt = 0; /* Presume VMS */
6938   cp1 = strchr(spec, '/');
6939   if (cp1 == NULL)
6940     cmp_rslt = 0;
6941 
6942     /* Look for EFS ^/ */
6943     if (decc_efs_charset) {
6944       while (cp1 != NULL) {
6945 	cp2 = cp1 - 1;
6946 	if (*cp2 != '^') {
6947 	  /* Found illegal VMS, assume UNIX */
6948 	  cmp_rslt = 1;
6949 	  break;
6950 	}
6951       cp1++;
6952       cp1 = strchr(cp1, '/');
6953     }
6954   }
6955 
6956   /* Look for "." and ".." */
6957   if (decc_filename_unix_report) {
6958     if (spec[0] == '.') {
6959       if ((spec[1] == '\0') || (spec[1] == '\n')) {
6960 	cmp_rslt = 1;
6961       }
6962       else {
6963 	if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6964 	  cmp_rslt = 1;
6965 	}
6966       }
6967     }
6968   }
6969 
6970   cp1 = rslt;
6971   cp2 = spec;
6972 
6973   /* This is already UNIX or at least nothing VMS understands,
6974    * so all we can reasonably do is unescape extended chars.
6975    */
6976   if (cmp_rslt) {
6977     while (*cp2) {
6978         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6979         cp1 += outchars_added;
6980     }
6981     *cp1 = '\0';
6982     if (vms_debug_fileify) {
6983         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6984     }
6985     return rslt;
6986   }
6987 
6988   dirend = strrchr(spec,']');
6989   if (dirend == NULL) dirend = strrchr(spec,'>');
6990   if (dirend == NULL) dirend = strchr(spec,':');
6991   if (dirend == NULL) {
6992     while (*cp2) {
6993         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6994         cp1 += outchars_added;
6995     }
6996     *cp1 = '\0';
6997     if (vms_debug_fileify) {
6998         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6999     }
7000     return rslt;
7001   }
7002 
7003   /* Special case 1 - sys$posix_root = / */
7004   if (!decc_disable_posix_root) {
7005     if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7006       *cp1 = '/';
7007       cp1++;
7008       cp2 = cp2 + 15;
7009       }
7010   }
7011 
7012   /* Special case 2 - Convert NLA0: to /dev/null */
7013   cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7014   if (cmp_rslt == 0) {
7015     strcpy(rslt, "/dev/null");
7016     cp1 = cp1 + 9;
7017     cp2 = cp2 + 5;
7018     if (spec[6] != '\0') {
7019       cp1[9] = '/';
7020       cp1++;
7021       cp2++;
7022     }
7023   }
7024 
7025    /* Also handle special case "SYS$SCRATCH:" */
7026   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7027   tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7028   if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7029   if (cmp_rslt == 0) {
7030   int islnm;
7031 
7032     islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7033     if (!islnm) {
7034       strcpy(rslt, "/tmp");
7035       cp1 = cp1 + 4;
7036       cp2 = cp2 + 12;
7037       if (spec[12] != '\0') {
7038 	cp1[4] = '/';
7039 	cp1++;
7040 	cp2++;
7041       }
7042     }
7043   }
7044 
7045   if (*cp2 != '[' && *cp2 != '<') {
7046     *(cp1++) = '/';
7047   }
7048   else {  /* the VMS spec begins with directories */
7049     cp2++;
7050     if (*cp2 == ']' || *cp2 == '>') {
7051       *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7052       PerlMem_free(tmp);
7053       return rslt;
7054     }
7055     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7056       if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7057 	PerlMem_free(tmp);
7058         if (vms_debug_fileify) {
7059             fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7060         }
7061         return NULL;
7062       }
7063       trnlnm_iter_count = 0;
7064       do {
7065         cp3 = tmp;
7066         while (*cp3 != ':' && *cp3) cp3++;
7067         *(cp3++) = '\0';
7068         if (strchr(cp3,']') != NULL) break;
7069         trnlnm_iter_count++;
7070         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7071       } while (vmstrnenv(tmp,tmp,0,fildev,0));
7072       cp1 = rslt;
7073       cp3 = tmp;
7074       *(cp1++) = '/';
7075       while (*cp3) {
7076         *(cp1++) = *(cp3++);
7077         if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7078 	    PerlMem_free(tmp);
7079             set_errno(ENAMETOOLONG);
7080             set_vaxc_errno(SS$_BUFFEROVF);
7081             if (vms_debug_fileify) {
7082                 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7083             }
7084 	    return NULL; /* No room */
7085 	}
7086       }
7087       *(cp1++) = '/';
7088     }
7089     if ((*cp2 == '^')) {
7090         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7091         cp1 += outchars_added;
7092     }
7093     else if ( *cp2 == '.') {
7094       if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7095         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7096         cp2 += 3;
7097       }
7098       else cp2++;
7099     }
7100   }
7101   PerlMem_free(tmp);
7102   for (; cp2 <= dirend; cp2++) {
7103     if ((*cp2 == '^')) {
7104 	/* EFS file escape, pass the next character as is */
7105 	/* Fix me: HEX encoding for Unicode not implemented */
7106 	*(cp1++) = *(++cp2);
7107         /* An escaped dot stays as is -- don't convert to slash */
7108         if (*cp2 == '.') cp2++;
7109     }
7110     if (*cp2 == ':') {
7111       *(cp1++) = '/';
7112       if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7113     }
7114     else if (*cp2 == ']' || *cp2 == '>') {
7115       if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7116     }
7117     else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7118       *(cp1++) = '/';
7119       if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7120         while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7121                *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7122         if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7123             *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7124       }
7125       else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7126         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7127         cp2 += 2;
7128       }
7129     }
7130     else if (*cp2 == '-') {
7131       if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7132         while (*cp2 == '-') {
7133           cp2++;
7134           *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7135         }
7136         if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7137                                                          /* filespecs like */
7138           set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);   /* [fred.--foo.bar] */
7139           if (vms_debug_fileify) {
7140               fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7141           }
7142           return NULL;
7143         }
7144       }
7145       else *(cp1++) = *cp2;
7146     }
7147     else *(cp1++) = *cp2;
7148   }
7149   /* Translate the rest of the filename. */
7150   while (*cp2) {
7151       int dot_seen = 0;
7152       switch(*cp2) {
7153       /* Fixme - for compatibility with the CRTL we should be removing */
7154       /* spaces from the file specifications, but this may show that */
7155       /* some tests that were appearing to pass are not really passing */
7156       case '%':
7157           cp2++;
7158           *(cp1++) = '?';
7159           break;
7160       case '^':
7161           cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7162           cp1 += outchars_added;
7163           break;
7164       case ';':
7165           if (decc_filename_unix_no_version) {
7166               /* Easy, drop the version */
7167               while (*cp2)
7168                   cp2++;
7169               break;
7170           } else {
7171               /* Punt - passing the version as a dot will probably */
7172               /* break perl in weird ways, but so did passing */
7173               /* through the ; as a version.  Follow the CRTL and */
7174               /* hope for the best. */
7175               cp2++;
7176               *(cp1++) = '.';
7177           }
7178           break;
7179       case '.':
7180           if (dot_seen) {
7181               /* We will need to fix this properly later */
7182               /* As Perl may be installed on an ODS-5 volume, but not */
7183               /* have the EFS_CHARSET enabled, it still may encounter */
7184               /* filenames with extra dots in them, and a precedent got */
7185               /* set which allowed them to work, that we will uphold here */
7186               /* If extra dots are present in a name and no ^ is on them */
7187               /* VMS assumes that the first one is the extension delimiter */
7188               /* the rest have an implied ^. */
7189 
7190               /* this is also a conflict as the . is also a version */
7191               /* delimiter in VMS, */
7192 
7193               *(cp1++) = *(cp2++);
7194               break;
7195           }
7196           dot_seen = 1;
7197           /* This is an extension */
7198           if (decc_readdir_dropdotnotype) {
7199               cp2++;
7200               if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7201                   /* Drop the dot for the extension */
7202                   break;
7203               } else {
7204                   *(cp1++) = '.';
7205               }
7206               break;
7207           }
7208       default:
7209           *(cp1++) = *(cp2++);
7210       }
7211   }
7212   *cp1 = '\0';
7213 
7214   /* This still leaves /000000/ when working with a
7215    * VMS device root or concealed root.
7216    */
7217   {
7218   int ulen;
7219   char * zeros;
7220 
7221       ulen = strlen(rslt);
7222 
7223       /* Get rid of "000000/ in rooted filespecs */
7224       if (ulen > 7) {
7225 	zeros = strstr(rslt, "/000000/");
7226 	if (zeros != NULL) {
7227 	  int mlen;
7228 	  mlen = ulen - (zeros - rslt) - 7;
7229 	  memmove(zeros, &zeros[7], mlen);
7230 	  ulen = ulen - 7;
7231 	  rslt[ulen] = '\0';
7232 	}
7233       }
7234   }
7235 
7236   if (vms_debug_fileify) {
7237       fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7238   }
7239   return rslt;
7240 
7241 }  /* end of int_tounixspec() */
7242 
7243 
7244 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7245 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7246 {
7247     static char __tounixspec_retbuf[VMS_MAXRSS];
7248     char * unixspec, *ret_spec, *ret_buf;
7249 
7250     unixspec = NULL;
7251     ret_buf = buf;
7252     if (ret_buf == NULL) {
7253         if (ts) {
7254             Newx(unixspec, VMS_MAXRSS, char);
7255             if (unixspec == NULL)
7256                 _ckvmssts(SS$_INSFMEM);
7257             ret_buf = unixspec;
7258         } else {
7259             ret_buf = __tounixspec_retbuf;
7260         }
7261     }
7262 
7263     ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7264 
7265     if (ret_spec == NULL) {
7266        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7267        if (unixspec)
7268            Safefree(unixspec);
7269     }
7270 
7271     return ret_spec;
7272 
7273 }  /* end of do_tounixspec() */
7274 /*}}}*/
7275 /* External entry points */
7276 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7277   { return do_tounixspec(spec,buf,0, NULL); }
7278 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7279   { return do_tounixspec(spec,buf,1, NULL); }
7280 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7281   { return do_tounixspec(spec,buf,0, utf8_fl); }
7282 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7283   { return do_tounixspec(spec,buf,1, utf8_fl); }
7284 
7285 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7286 
7287 /*
7288  This procedure is used to identify if a path is based in either
7289  the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7290  it returns the OpenVMS format directory for it.
7291 
7292  It is expecting specifications of only '/' or '/xxxx/'
7293 
7294  If a posix root does not exist, or 'xxxx' is not a directory
7295  in the posix root, it returns a failure.
7296 
7297  FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7298 
7299  It is used only internally by posix_to_vmsspec_hardway().
7300  */
7301 
7302 static int posix_root_to_vms
7303   (char *vmspath, int vmspath_len,
7304    const char *unixpath,
7305    const int * utf8_fl)
7306 {
7307 int sts;
7308 struct FAB myfab = cc$rms_fab;
7309 rms_setup_nam(mynam);
7310 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7311 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7312 char * esa, * esal, * rsa, * rsal;
7313 int dir_flag;
7314 int unixlen;
7315 
7316     dir_flag = 0;
7317     vmspath[0] = '\0';
7318     unixlen = strlen(unixpath);
7319     if (unixlen == 0) {
7320       return RMS$_FNF;
7321     }
7322 
7323 #if __CRTL_VER >= 80200000
7324   /* If not a posix spec already, convert it */
7325   if (decc_posix_compliant_pathnames) {
7326     if (strncmp(unixpath,"\"^UP^",5) != 0) {
7327       sprintf(vmspath,"\"^UP^%s\"",unixpath);
7328     }
7329     else {
7330       /* This is already a VMS specification, no conversion */
7331       unixlen--;
7332       my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7333     }
7334   }
7335   else
7336 #endif
7337   {
7338   int path_len;
7339   int i,j;
7340 
7341      /* Check to see if this is under the POSIX root */
7342      if (decc_disable_posix_root) {
7343 	return RMS$_FNF;
7344      }
7345 
7346      /* Skip leading / */
7347      if (unixpath[0] == '/') {
7348 	unixpath++;
7349 	unixlen--;
7350      }
7351 
7352 
7353      strcpy(vmspath,"SYS$POSIX_ROOT:");
7354 
7355      /* If this is only the / , or blank, then... */
7356      if (unixpath[0] == '\0') {
7357 	/* by definition, this is the answer */
7358 	return SS$_NORMAL;
7359      }
7360 
7361      /* Need to look up a directory */
7362      vmspath[15] = '[';
7363      vmspath[16] = '\0';
7364 
7365      /* Copy and add '^' escape characters as needed */
7366      j = 16;
7367      i = 0;
7368      while (unixpath[i] != 0) {
7369      int k;
7370 
7371 	j += copy_expand_unix_filename_escape
7372 	    (&vmspath[j], &unixpath[i], &k, utf8_fl);
7373 	i += k;
7374      }
7375 
7376      path_len = strlen(vmspath);
7377      if (vmspath[path_len - 1] == '/')
7378 	path_len--;
7379      vmspath[path_len] = ']';
7380      path_len++;
7381      vmspath[path_len] = '\0';
7382 
7383   }
7384   vmspath[vmspath_len] = 0;
7385   if (unixpath[unixlen - 1] == '/')
7386   dir_flag = 1;
7387   esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7388   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7389   esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7390   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7391   rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7392   if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7393   rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7394   if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7395   rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7396   rms_bind_fab_nam(myfab, mynam);
7397   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7398   rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7399   if (decc_efs_case_preserve)
7400     mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7401 #ifdef NAML$M_OPEN_SPECIAL
7402   mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7403 #endif
7404 
7405   /* Set up the remaining naml fields */
7406   sts = sys$parse(&myfab);
7407 
7408   /* It failed! Try again as a UNIX filespec */
7409   if (!(sts & 1)) {
7410     PerlMem_free(esal);
7411     PerlMem_free(esa);
7412     PerlMem_free(rsal);
7413     PerlMem_free(rsa);
7414     return sts;
7415   }
7416 
7417    /* get the Device ID and the FID */
7418    sts = sys$search(&myfab);
7419 
7420    /* These are no longer needed */
7421    PerlMem_free(esa);
7422    PerlMem_free(rsal);
7423    PerlMem_free(rsa);
7424 
7425    /* on any failure, returned the POSIX ^UP^ filespec */
7426    if (!(sts & 1)) {
7427       PerlMem_free(esal);
7428       return sts;
7429    }
7430    specdsc.dsc$a_pointer = vmspath;
7431    specdsc.dsc$w_length = vmspath_len;
7432 
7433    dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7434    dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7435    sts = lib$fid_to_name
7436       (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7437 
7438   /* on any failure, returned the POSIX ^UP^ filespec */
7439   if (!(sts & 1)) {
7440      /* This can happen if user does not have permission to read directories */
7441      if (strncmp(unixpath,"\"^UP^",5) != 0)
7442        sprintf(vmspath,"\"^UP^%s\"",unixpath);
7443      else
7444        my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7445   }
7446   else {
7447     vmspath[specdsc.dsc$w_length] = 0;
7448 
7449     /* Are we expecting a directory? */
7450     if (dir_flag != 0) {
7451     int i;
7452     char *eptr;
7453 
7454       eptr = NULL;
7455 
7456       i = specdsc.dsc$w_length - 1;
7457       while (i > 0) {
7458       int zercnt;
7459 	zercnt = 0;
7460 	/* Version must be '1' */
7461 	if (vmspath[i--] != '1')
7462 	  break;
7463 	/* Version delimiter is one of ".;" */
7464 	if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7465 	  break;
7466 	i--;
7467 	if (vmspath[i--] != 'R')
7468 	  break;
7469 	if (vmspath[i--] != 'I')
7470 	  break;
7471 	if (vmspath[i--] != 'D')
7472 	  break;
7473 	if (vmspath[i--] != '.')
7474 	  break;
7475 	eptr = &vmspath[i+1];
7476  	while (i > 0) {
7477 	  if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7478 	    if (vmspath[i-1] != '^') {
7479 	      if (zercnt != 6) {
7480   		*eptr = vmspath[i];
7481 		eptr[1] = '\0';
7482 		vmspath[i] = '.';
7483   		break;
7484 	      }
7485 	      else {
7486  		/* Get rid of 6 imaginary zero directory filename */
7487   		vmspath[i+1] = '\0';
7488  	      }
7489 	    }
7490 	  }
7491 	  if (vmspath[i] == '0')
7492 	    zercnt++;
7493 	  else
7494 	    zercnt = 10;
7495 	  i--;
7496 	}
7497 	break;
7498       }
7499     }
7500   }
7501   PerlMem_free(esal);
7502   return sts;
7503 }
7504 
7505 /* /dev/mumble needs to be handled special.
7506    /dev/null becomes NLA0:, And there is the potential for other stuff
7507    like /dev/tty which may need to be mapped to something.
7508 */
7509 
7510 static int
7511 slash_dev_special_to_vms
7512    (const char * unixptr,
7513     char * vmspath,
7514     int vmspath_len)
7515 {
7516 char * nextslash;
7517 int len;
7518 int cmp;
7519 
7520     unixptr += 4;
7521     nextslash = strchr(unixptr, '/');
7522     len = strlen(unixptr);
7523     if (nextslash != NULL)
7524 	len = nextslash - unixptr;
7525     cmp = strncmp("null", unixptr, 5);
7526     if (cmp == 0) {
7527 	if (vmspath_len >= 6) {
7528 	    strcpy(vmspath, "_NLA0:");
7529 	    return SS$_NORMAL;
7530 	}
7531     }
7532     return 0;
7533 }
7534 
7535 
7536 /* The built in routines do not understand perl's special needs, so
7537     doing a manual conversion from UNIX to VMS
7538 
7539     If the utf8_fl is not null and points to a non-zero value, then
7540     treat 8 bit characters as UTF-8.
7541 
7542     The sequence starting with '$(' and ending with ')' will be passed
7543     through with out interpretation instead of being escaped.
7544 
7545   */
7546 static int posix_to_vmsspec_hardway
7547   (char *vmspath, int vmspath_len,
7548    const char *unixpath,
7549    int dir_flag,
7550    int * utf8_fl) {
7551 
7552 char *esa;
7553 const char *unixptr;
7554 const char *unixend;
7555 char *vmsptr;
7556 const char *lastslash;
7557 const char *lastdot;
7558 int unixlen;
7559 int vmslen;
7560 int dir_start;
7561 int dir_dot;
7562 int quoted;
7563 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7564 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7565 
7566   if (utf8_fl != NULL)
7567     *utf8_fl = 0;
7568 
7569   unixptr = unixpath;
7570   dir_dot = 0;
7571 
7572   /* Ignore leading "/" characters */
7573   while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7574     unixptr++;
7575   }
7576   unixlen = strlen(unixptr);
7577 
7578   /* Do nothing with blank paths */
7579   if (unixlen == 0) {
7580     vmspath[0] = '\0';
7581     return SS$_NORMAL;
7582   }
7583 
7584   quoted = 0;
7585   /* This could have a "^UP^ on the front */
7586   if (strncmp(unixptr,"\"^UP^",5) == 0) {
7587     quoted = 1;
7588     unixptr+= 5;
7589     unixlen-= 5;
7590   }
7591 
7592   lastslash = strrchr(unixptr,'/');
7593   lastdot = strrchr(unixptr,'.');
7594   unixend = strrchr(unixptr,'\"');
7595   if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7596     unixend = unixptr + unixlen;
7597   }
7598 
7599   /* last dot is last dot or past end of string */
7600   if (lastdot == NULL)
7601     lastdot = unixptr + unixlen;
7602 
7603   /* if no directories, set last slash to beginning of string */
7604   if (lastslash == NULL) {
7605     lastslash = unixptr;
7606   }
7607   else {
7608     /* Watch out for trailing "." after last slash, still a directory */
7609     if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7610       lastslash = unixptr + unixlen;
7611     }
7612 
7613     /* Watch out for trailing ".." after last slash, still a directory */
7614     if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7615       lastslash = unixptr + unixlen;
7616     }
7617 
7618     /* dots in directories are aways escaped */
7619     if (lastdot < lastslash)
7620       lastdot = unixptr + unixlen;
7621   }
7622 
7623   /* if (unixptr < lastslash) then we are in a directory */
7624 
7625   dir_start = 0;
7626 
7627   vmsptr = vmspath;
7628   vmslen = 0;
7629 
7630   /* Start with the UNIX path */
7631   if (*unixptr != '/') {
7632     /* relative paths */
7633 
7634     /* If allowing logical names on relative pathnames, then handle here */
7635     if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7636 	!decc_posix_compliant_pathnames) {
7637     char * nextslash;
7638     int seg_len;
7639     char * trn;
7640     int islnm;
7641 
7642 	/* Find the next slash */
7643 	nextslash = strchr(unixptr,'/');
7644 
7645 	esa = (char *)PerlMem_malloc(vmspath_len);
7646 	if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7647 
7648 	trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7649 	if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7650 
7651 	if (nextslash != NULL) {
7652 
7653 	    seg_len = nextslash - unixptr;
7654 	    memcpy(esa, unixptr, seg_len);
7655 	    esa[seg_len] = 0;
7656 	}
7657 	else {
7658 	    seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7659 	}
7660 	/* trnlnm(section) */
7661 	islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7662 
7663 	if (islnm) {
7664 	    /* Now fix up the directory */
7665 
7666 	    /* Split up the path to find the components */
7667 	    sts = vms_split_path
7668 		  (trn,
7669 		   &v_spec,
7670 		   &v_len,
7671 		   &r_spec,
7672 		   &r_len,
7673 		   &d_spec,
7674 		   &d_len,
7675 		   &n_spec,
7676 		   &n_len,
7677 		   &e_spec,
7678 		   &e_len,
7679 		   &vs_spec,
7680 		   &vs_len);
7681 
7682 	    while (sts == 0) {
7683 	    int cmp;
7684 
7685 		/* A logical name must be a directory  or the full
7686 		   specification.  It is only a full specification if
7687 		   it is the only component */
7688 		if ((unixptr[seg_len] == '\0') ||
7689 		    (unixptr[seg_len+1] == '\0')) {
7690 
7691 		    /* Is a directory being required? */
7692 		    if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7693 			/* Not a logical name */
7694 			break;
7695 		    }
7696 
7697 
7698 		    if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7699 			/* This must be a directory */
7700 			if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7701 			    vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7702 			    vmsptr[vmslen] = ':';
7703 			    vmslen++;
7704 			    vmsptr[vmslen] = '\0';
7705 			    return SS$_NORMAL;
7706 			}
7707 		    }
7708 
7709 		}
7710 
7711 
7712 		/* must be dev/directory - ignore version */
7713 		if ((n_len + e_len) != 0)
7714 		    break;
7715 
7716 		/* transfer the volume */
7717 		if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7718 		    memcpy(vmsptr, v_spec, v_len);
7719 		    vmsptr += v_len;
7720 		    vmsptr[0] = '\0';
7721 		    vmslen += v_len;
7722 		}
7723 
7724 		/* unroot the rooted directory */
7725 		if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7726 		    r_spec[0] = '[';
7727 		    r_spec[r_len - 1] = ']';
7728 
7729 		    /* This should not be there, but nothing is perfect */
7730 		    if (r_len > 9) {
7731 			cmp = strcmp(&r_spec[1], "000000.");
7732 			if (cmp == 0) {
7733 			    r_spec += 7;
7734 			    r_spec[7] = '[';
7735 			    r_len -= 7;
7736 			    if (r_len == 2)
7737 				r_len = 0;
7738 			}
7739 		    }
7740 		    if (r_len > 0) {
7741 			memcpy(vmsptr, r_spec, r_len);
7742 			vmsptr += r_len;
7743 			vmslen += r_len;
7744 			vmsptr[0] = '\0';
7745 		    }
7746 		}
7747 		/* Bring over the directory. */
7748 		if ((d_len > 0) &&
7749 		    ((d_len + vmslen) < vmspath_len)) {
7750 		    d_spec[0] = '[';
7751 		    d_spec[d_len - 1] = ']';
7752 		    if (d_len > 9) {
7753 			cmp = strcmp(&d_spec[1], "000000.");
7754 			if (cmp == 0) {
7755 			    d_spec += 7;
7756 			    d_spec[7] = '[';
7757 			    d_len -= 7;
7758 			    if (d_len == 2)
7759 				d_len = 0;
7760 			}
7761 		    }
7762 
7763 		    if (r_len > 0) {
7764 			/* Remove the redundant root */
7765 			if (r_len > 0) {
7766 			    /* remove the ][ */
7767 			    vmsptr--;
7768 			    vmslen--;
7769 			    d_spec++;
7770 			    d_len--;
7771 			}
7772 			memcpy(vmsptr, d_spec, d_len);
7773 			    vmsptr += d_len;
7774 			    vmslen += d_len;
7775 			    vmsptr[0] = '\0';
7776 		    }
7777 		}
7778 		break;
7779 	    }
7780 	}
7781 
7782 	PerlMem_free(esa);
7783 	PerlMem_free(trn);
7784     }
7785 
7786     if (lastslash > unixptr) {
7787     int dotdir_seen;
7788 
7789       /* skip leading ./ */
7790       dotdir_seen = 0;
7791       while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7792 	dotdir_seen = 1;
7793 	unixptr++;
7794 	unixptr++;
7795       }
7796 
7797       /* Are we still in a directory? */
7798       if (unixptr <= lastslash) {
7799  	*vmsptr++ = '[';
7800  	vmslen = 1;
7801  	dir_start = 1;
7802 
7803  	/* if not backing up, then it is relative forward. */
7804  	if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7805  	      ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7806  	  *vmsptr++ = '.';
7807  	  vmslen++;
7808  	  dir_dot = 1;
7809  	  }
7810        }
7811        else {
7812 	 if (dotdir_seen) {
7813 	   /* Perl wants an empty directory here to tell the difference
7814 	    * between a DCL command and a filename
7815 	    */
7816 	  *vmsptr++ = '[';
7817 	  *vmsptr++ = ']';
7818 	  vmslen = 2;
7819  	}
7820       }
7821     }
7822     else {
7823       /* Handle two special files . and .. */
7824       if (unixptr[0] == '.') {
7825         if (&unixptr[1] == unixend) {
7826 	  *vmsptr++ = '[';
7827 	  *vmsptr++ = ']';
7828 	  vmslen += 2;
7829 	  *vmsptr++ = '\0';
7830 	  return SS$_NORMAL;
7831 	}
7832         if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7833 	  *vmsptr++ = '[';
7834 	  *vmsptr++ = '-';
7835 	  *vmsptr++ = ']';
7836 	  vmslen += 3;
7837 	  *vmsptr++ = '\0';
7838 	  return SS$_NORMAL;
7839 	}
7840       }
7841     }
7842   }
7843   else {	/* Absolute PATH handling */
7844   int sts;
7845   char * nextslash;
7846   int seg_len;
7847     /* Need to find out where root is */
7848 
7849     /* In theory, this procedure should never get an absolute POSIX pathname
7850      * that can not be found on the POSIX root.
7851      * In practice, that can not be relied on, and things will show up
7852      * here that are a VMS device name or concealed logical name instead.
7853      * So to make things work, this procedure must be tolerant.
7854      */
7855     esa = (char *)PerlMem_malloc(vmspath_len);
7856     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7857 
7858     sts = SS$_NORMAL;
7859     nextslash = strchr(&unixptr[1],'/');
7860     seg_len = 0;
7861     if (nextslash != NULL) {
7862       int cmp;
7863       seg_len = nextslash - &unixptr[1];
7864       my_strlcpy(vmspath, unixptr, seg_len + 2);
7865       cmp = 1;
7866       if (seg_len == 3) {
7867 	cmp = strncmp(vmspath, "dev", 4);
7868 	if (cmp == 0) {
7869 	    sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7870 	    if (sts == SS$_NORMAL)
7871 		return SS$_NORMAL;
7872 	}
7873       }
7874       sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7875     }
7876 
7877     if ($VMS_STATUS_SUCCESS(sts)) {
7878       /* This is verified to be a real path */
7879 
7880       sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7881       if ($VMS_STATUS_SUCCESS(sts)) {
7882 	vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7883 	vmsptr = vmspath + vmslen;
7884 	unixptr++;
7885 	if (unixptr < lastslash) {
7886 	char * rptr;
7887 	  vmsptr--;
7888 	  *vmsptr++ = '.';
7889 	  dir_start = 1;
7890 	  dir_dot = 1;
7891 	  if (vmslen > 7) {
7892 	  int cmp;
7893 	    rptr = vmsptr - 7;
7894 	    cmp = strcmp(rptr,"000000.");
7895 	    if (cmp == 0) {
7896 	      vmslen -= 7;
7897 	      vmsptr -= 7;
7898 	      vmsptr[1] = '\0';
7899 	    } /* removing 6 zeros */
7900 	  } /* vmslen < 7, no 6 zeros possible */
7901 	} /* Not in a directory */
7902       } /* Posix root found */
7903       else {
7904 	/* No posix root, fall back to default directory */
7905 	strcpy(vmspath, "SYS$DISK:[");
7906 	vmsptr = &vmspath[10];
7907 	vmslen = 10;
7908 	if (unixptr > lastslash) {
7909 	   *vmsptr = ']';
7910 	   vmsptr++;
7911 	   vmslen++;
7912 	}
7913 	else {
7914 	   dir_start = 1;
7915 	}
7916       }
7917     } /* end of verified real path handling */
7918     else {
7919     int add_6zero;
7920     int islnm;
7921 
7922       /* Ok, we have a device or a concealed root that is not in POSIX
7923        * or we have garbage.  Make the best of it.
7924        */
7925 
7926       /* Posix to VMS destroyed this, so copy it again */
7927       my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7928       vmslen = strlen(vmspath); /* We know we're truncating. */
7929       vmsptr = &vmsptr[vmslen];
7930       islnm = 0;
7931 
7932       /* Now do we need to add the fake 6 zero directory to it? */
7933       add_6zero = 1;
7934       if ((*lastslash == '/') && (nextslash < lastslash)) {
7935 	/* No there is another directory */
7936 	add_6zero = 0;
7937       }
7938       else {
7939       int trnend;
7940       int cmp;
7941 
7942 	/* now we have foo:bar or foo:[000000]bar to decide from */
7943 	islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7944 
7945         if (!islnm && !decc_posix_compliant_pathnames) {
7946 
7947 	    cmp = strncmp("bin", vmspath, 4);
7948 	    if (cmp == 0) {
7949 	        /* bin => SYS$SYSTEM: */
7950 		islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7951 	    }
7952 	    else {
7953 	        /* tmp => SYS$SCRATCH: */
7954 	        cmp = strncmp("tmp", vmspath, 4);
7955 		if (cmp == 0) {
7956 		    islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7957 		}
7958 	    }
7959 	}
7960 
7961         trnend = islnm ? islnm - 1 : 0;
7962 
7963 	/* if this was a logical name, ']' or '>' must be present */
7964 	/* if not a logical name, then assume a device and hope. */
7965 	islnm =  trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7966 
7967 	/* if log name and trailing '.' then rooted - treat as device */
7968 	add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7969 
7970 	/* Fix me, if not a logical name, a device lookup should be
7971          * done to see if the device is file structured.  If the device
7972          * is not file structured, the 6 zeros should not be put on.
7973          *
7974          * As it is, perl is occasionally looking for dev:[000000]tty.
7975 	 * which looks a little strange.
7976 	 *
7977 	 * Not that easy to detect as "/dev" may be file structured with
7978 	 * special device files.
7979          */
7980 
7981 	if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
7982 	    (&nextslash[1] == unixend)) {
7983 	  /* No real directory present */
7984 	  add_6zero = 1;
7985 	}
7986       }
7987 
7988       /* Put the device delimiter on */
7989       *vmsptr++ = ':';
7990       vmslen++;
7991       unixptr = nextslash;
7992       unixptr++;
7993 
7994       /* Start directory if needed */
7995       if (!islnm || add_6zero) {
7996 	*vmsptr++ = '[';
7997 	vmslen++;
7998 	dir_start = 1;
7999       }
8000 
8001       /* add fake 000000] if needed */
8002       if (add_6zero) {
8003 	*vmsptr++ = '0';
8004 	*vmsptr++ = '0';
8005 	*vmsptr++ = '0';
8006 	*vmsptr++ = '0';
8007 	*vmsptr++ = '0';
8008 	*vmsptr++ = '0';
8009 	*vmsptr++ = ']';
8010 	vmslen += 7;
8011 	dir_start = 0;
8012       }
8013 
8014     } /* non-POSIX translation */
8015     PerlMem_free(esa);
8016   } /* End of relative/absolute path handling */
8017 
8018   while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8019   int dash_flag;
8020   int in_cnt;
8021   int out_cnt;
8022 
8023     dash_flag = 0;
8024 
8025     if (dir_start != 0) {
8026 
8027       /* First characters in a directory are handled special */
8028       while ((*unixptr == '/') ||
8029 	     ((*unixptr == '.') &&
8030 	      ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8031 		(&unixptr[1]==unixend)))) {
8032       int loop_flag;
8033 
8034 	loop_flag = 0;
8035 
8036         /* Skip redundant / in specification */
8037         while ((*unixptr == '/') && (dir_start != 0)) {
8038 	  loop_flag = 1;
8039 	  unixptr++;
8040 	  if (unixptr == lastslash)
8041 	    break;
8042 	}
8043 	if (unixptr == lastslash)
8044 	  break;
8045 
8046         /* Skip redundant ./ characters */
8047 	while ((*unixptr == '.') &&
8048 	       ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8049 	  loop_flag = 1;
8050 	  unixptr++;
8051 	  if (unixptr == lastslash)
8052 	    break;
8053 	  if (*unixptr == '/')
8054 	    unixptr++;
8055 	}
8056 	if (unixptr == lastslash)
8057 	  break;
8058 
8059 	/* Skip redundant ../ characters */
8060 	while ((*unixptr == '.') && (unixptr[1] == '.') &&
8061 	     ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8062 	  /* Set the backing up flag */
8063 	  loop_flag = 1;
8064 	  dir_dot = 0;
8065 	  dash_flag = 1;
8066 	  *vmsptr++ = '-';
8067 	  vmslen++;
8068 	  unixptr++; /* first . */
8069 	  unixptr++; /* second . */
8070 	  if (unixptr == lastslash)
8071 	    break;
8072 	  if (*unixptr == '/') /* The slash */
8073 	    unixptr++;
8074 	}
8075 	if (unixptr == lastslash)
8076 	  break;
8077 
8078 	/* To do: Perl expects /.../ to be translated to [...] on VMS */
8079   	/* Not needed when VMS is pretending to be UNIX. */
8080 
8081 	/* Is this loop stuck because of too many dots? */
8082 	if (loop_flag == 0) {
8083 	  /* Exit the loop and pass the rest through */
8084 	  break;
8085 	}
8086       }
8087 
8088       /* Are we done with directories yet? */
8089       if (unixptr >= lastslash) {
8090 
8091 	/* Watch out for trailing dots */
8092 	if (dir_dot != 0) {
8093 	    vmslen --;
8094 	    vmsptr--;
8095 	}
8096 	*vmsptr++ = ']';
8097 	vmslen++;
8098 	dash_flag = 0;
8099 	dir_start = 0;
8100 	if (*unixptr == '/')
8101 	  unixptr++;
8102       }
8103       else {
8104 	/* Have we stopped backing up? */
8105 	if (dash_flag) {
8106 	  *vmsptr++ = '.';
8107 	  vmslen++;
8108 	  dash_flag = 0;
8109 	  /* dir_start continues to be = 1 */
8110 	}
8111 	if (*unixptr == '-') {
8112 	  *vmsptr++ = '^';
8113 	  *vmsptr++ = *unixptr++;
8114 	  vmslen += 2;
8115 	  dir_start = 0;
8116 
8117 	  /* Now are we done with directories yet? */
8118 	  if (unixptr >= lastslash) {
8119 
8120 	    /* Watch out for trailing dots */
8121 	    if (dir_dot != 0) {
8122 	      vmslen --;
8123 	      vmsptr--;
8124 	    }
8125 
8126 	    *vmsptr++ = ']';
8127 	    vmslen++;
8128 	    dash_flag = 0;
8129 	    dir_start = 0;
8130 	  }
8131 	}
8132       }
8133     }
8134 
8135     /* All done? */
8136     if (unixptr >= unixend)
8137       break;
8138 
8139     /* Normal characters - More EFS work probably needed */
8140     dir_start = 0;
8141     dir_dot = 0;
8142 
8143     switch(*unixptr) {
8144     case '/':
8145 	/* remove multiple / */
8146 	while (unixptr[1] == '/') {
8147 	   unixptr++;
8148 	}
8149 	if (unixptr == lastslash) {
8150 	  /* Watch out for trailing dots */
8151 	  if (dir_dot != 0) {
8152 	    vmslen --;
8153 	    vmsptr--;
8154 	  }
8155 	  *vmsptr++ = ']';
8156 	}
8157 	else {
8158 	  dir_start = 1;
8159 	  *vmsptr++ = '.';
8160 	  dir_dot = 1;
8161 
8162 	  /* To do: Perl expects /.../ to be translated to [...] on VMS */
8163  	  /* Not needed when VMS is pretending to be UNIX. */
8164 
8165 	}
8166 	dash_flag = 0;
8167 	if (unixptr != unixend)
8168 	  unixptr++;
8169 	vmslen++;
8170 	break;
8171     case '.':
8172 	if ((unixptr < lastdot) || (unixptr < lastslash) ||
8173 	    (&unixptr[1] == unixend)) {
8174 	  *vmsptr++ = '^';
8175 	  *vmsptr++ = '.';
8176 	  vmslen += 2;
8177 	  unixptr++;
8178 
8179 	  /* trailing dot ==> '^..' on VMS */
8180 	  if (unixptr == unixend) {
8181 	    *vmsptr++ = '.';
8182 	    vmslen++;
8183 	    unixptr++;
8184 	  }
8185 	  break;
8186 	}
8187 
8188 	*vmsptr++ = *unixptr++;
8189 	vmslen ++;
8190 	break;
8191     case '"':
8192 	if (quoted && (&unixptr[1] == unixend)) {
8193 	    unixptr++;
8194 	    break;
8195 	}
8196 	in_cnt = copy_expand_unix_filename_escape
8197 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8198 	vmsptr += out_cnt;
8199 	unixptr += in_cnt;
8200 	break;
8201     case '~':
8202     case ';':
8203     case '\\':
8204     case '?':
8205     case ' ':
8206     default:
8207 	in_cnt = copy_expand_unix_filename_escape
8208 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8209 	vmsptr += out_cnt;
8210 	unixptr += in_cnt;
8211 	break;
8212     }
8213   }
8214 
8215   /* Make sure directory is closed */
8216   if (unixptr == lastslash) {
8217     char *vmsptr2;
8218     vmsptr2 = vmsptr - 1;
8219 
8220     if (*vmsptr2 != ']') {
8221       *vmsptr2--;
8222 
8223       /* directories do not end in a dot bracket */
8224       if (*vmsptr2 == '.') {
8225 	vmsptr2--;
8226 
8227 	/* ^. is allowed */
8228         if (*vmsptr2 != '^') {
8229 	  vmsptr--; /* back up over the dot */
8230  	}
8231       }
8232       *vmsptr++ = ']';
8233     }
8234   }
8235   else {
8236     char *vmsptr2;
8237     /* Add a trailing dot if a file with no extension */
8238     vmsptr2 = vmsptr - 1;
8239     if ((vmslen > 1) &&
8240 	(*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8241 	(*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8242 	*vmsptr++ = '.';
8243         vmslen++;
8244     }
8245   }
8246 
8247   *vmsptr = '\0';
8248   return SS$_NORMAL;
8249 }
8250 #endif
8251 
8252  /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8253 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8254 {
8255 char * result;
8256 int utf8_flag;
8257 
8258    /* If a UTF8 flag is being passed, honor it */
8259    utf8_flag = 0;
8260    if (utf8_fl != NULL) {
8261      utf8_flag = *utf8_fl;
8262     *utf8_fl = 0;
8263    }
8264 
8265    if (utf8_flag) {
8266      /* If there is a possibility of UTF8, then if any UTF8 characters
8267         are present, then they must be converted to VTF-7
8268       */
8269      result = strcpy(rslt, path); /* FIX-ME */
8270    }
8271    else
8272      result = strcpy(rslt, path);
8273 
8274    return result;
8275 }
8276 
8277 /* A convenience macro for copying dots in filenames and escaping
8278  * them when they haven't already been escaped, with guards to
8279  * avoid checking before the start of the buffer or advancing
8280  * beyond the end of it (allowing room for the NUL terminator).
8281  */
8282 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8283     if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8284           || ((vmsefsdot) == (vmsefsbuf))) \
8285          && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8286        ) { \
8287         *((vmsefsdot)++) = '^'; \
8288     } \
8289     if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8290         *((vmsefsdot)++) = '.'; \
8291 } STMT_END
8292 
8293 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8294 static char *int_tovmsspec
8295    (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8296   char *dirend;
8297   char *lastdot;
8298   char *cp1;
8299   const char *cp2;
8300   unsigned long int infront = 0, hasdir = 1;
8301   int rslt_len;
8302   int no_type_seen;
8303   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8304   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8305 
8306   if (vms_debug_fileify) {
8307       if (path == NULL)
8308           fprintf(stderr, "int_tovmsspec: path = NULL\n");
8309       else
8310           fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8311   }
8312 
8313   if (path == NULL) {
8314       /* If we fail, we should be setting errno */
8315       set_errno(EINVAL);
8316       set_vaxc_errno(SS$_BADPARAM);
8317       return NULL;
8318   }
8319   rslt_len = VMS_MAXRSS-1;
8320 
8321   /* '.' and '..' are "[]" and "[-]" for a quick check */
8322   if (path[0] == '.') {
8323     if (path[1] == '\0') {
8324       strcpy(rslt,"[]");
8325       if (utf8_flag != NULL)
8326 	*utf8_flag = 0;
8327       return rslt;
8328     }
8329     else {
8330       if (path[1] == '.' && path[2] == '\0') {
8331 	strcpy(rslt,"[-]");
8332 	if (utf8_flag != NULL)
8333 	   *utf8_flag = 0;
8334 	return rslt;
8335       }
8336     }
8337   }
8338 
8339    /* Posix specifications are now a native VMS format */
8340   /*--------------------------------------------------*/
8341 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8342   if (decc_posix_compliant_pathnames) {
8343     if (strncmp(path,"\"^UP^",5) == 0) {
8344       posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8345       return rslt;
8346     }
8347   }
8348 #endif
8349 
8350   /* This is really the only way to see if this is already in VMS format */
8351   sts = vms_split_path
8352        (path,
8353 	&v_spec,
8354 	&v_len,
8355 	&r_spec,
8356 	&r_len,
8357 	&d_spec,
8358 	&d_len,
8359 	&n_spec,
8360 	&n_len,
8361 	&e_spec,
8362 	&e_len,
8363 	&vs_spec,
8364 	&vs_len);
8365   if (sts == 0) {
8366     /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8367        replacement, because the above parse just took care of most of
8368        what is needed to do vmspath when the specification is already
8369        in VMS format.
8370 
8371        And if it is not already, it is easier to do the conversion as
8372        part of this routine than to call this routine and then work on
8373        the result.
8374      */
8375 
8376     /* If VMS punctuation was found, it is already VMS format */
8377     if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8378       if (utf8_flag != NULL)
8379 	*utf8_flag = 0;
8380       my_strlcpy(rslt, path, VMS_MAXRSS);
8381       if (vms_debug_fileify) {
8382           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8383       }
8384       return rslt;
8385     }
8386     /* Now, what to do with trailing "." cases where there is no
8387        extension?  If this is a UNIX specification, and EFS characters
8388        are enabled, then the trailing "." should be converted to a "^.".
8389        But if this was already a VMS specification, then it should be
8390        left alone.
8391 
8392        So in the case of ambiguity, leave the specification alone.
8393      */
8394 
8395 
8396     /* If there is a possibility of UTF8, then if any UTF8 characters
8397         are present, then they must be converted to VTF-7
8398      */
8399     if (utf8_flag != NULL)
8400       *utf8_flag = 0;
8401     my_strlcpy(rslt, path, VMS_MAXRSS);
8402     if (vms_debug_fileify) {
8403         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8404     }
8405     return rslt;
8406   }
8407 
8408   dirend = strrchr(path,'/');
8409 
8410   if (dirend == NULL) {
8411      /* If we get here with no Unix directory delimiters, then this is an
8412       * ambiguous file specification, such as a Unix glob specification, a
8413       * shell or make macro, or a filespec that would be valid except for
8414       * unescaped extended characters.  The safest thing if it's a macro
8415       * is to pass it through as-is.
8416       */
8417       if (strstr(path, "$(")) {
8418           my_strlcpy(rslt, path, VMS_MAXRSS);
8419           if (vms_debug_fileify) {
8420               fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8421           }
8422           return rslt;
8423       }
8424       hasdir = 0;
8425   }
8426   else if (*(dirend+1) == '.') {  /* do we have trailing "/." or "/.." or "/..."? */
8427     if (!*(dirend+2)) dirend +=2;
8428     if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8429     if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8430   }
8431 
8432   cp1 = rslt;
8433   cp2 = path;
8434   lastdot = strrchr(cp2,'.');
8435   if (*cp2 == '/') {
8436     char *trndev;
8437     int islnm, rooted;
8438     STRLEN trnend;
8439 
8440     while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8441     if (!*(cp2+1)) {
8442       if (decc_disable_posix_root) {
8443 	strcpy(rslt,"sys$disk:[000000]");
8444       }
8445       else {
8446 	strcpy(rslt,"sys$posix_root:[000000]");
8447       }
8448       if (utf8_flag != NULL)
8449 	*utf8_flag = 0;
8450       if (vms_debug_fileify) {
8451           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8452       }
8453       return rslt;
8454     }
8455     while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8456     *cp1 = '\0';
8457     trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8458     if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8459     islnm =  simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8460 
8461      /* DECC special handling */
8462     if (!islnm) {
8463       if (strcmp(rslt,"bin") == 0) {
8464 	strcpy(rslt,"sys$system");
8465 	cp1 = rslt + 10;
8466 	*cp1 = 0;
8467 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8468       }
8469       else if (strcmp(rslt,"tmp") == 0) {
8470 	strcpy(rslt,"sys$scratch");
8471 	cp1 = rslt + 11;
8472 	*cp1 = 0;
8473 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8474       }
8475       else if (!decc_disable_posix_root) {
8476         strcpy(rslt, "sys$posix_root");
8477 	cp1 = rslt + 14;
8478 	*cp1 = 0;
8479 	cp2 = path;
8480         while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8481 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8482       }
8483       else if (strcmp(rslt,"dev") == 0) {
8484 	if (strncmp(cp2,"/null", 5) == 0) {
8485 	  if ((cp2[5] == 0) || (cp2[5] == '/')) {
8486 	    strcpy(rslt,"NLA0");
8487 	    cp1 = rslt + 4;
8488 	    *cp1 = 0;
8489 	    cp2 = cp2 + 5;
8490 	    islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8491 	  }
8492 	}
8493       }
8494     }
8495 
8496     trnend = islnm ? strlen(trndev) - 1 : 0;
8497     islnm =  trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8498     rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8499     /* If the first element of the path is a logical name, determine
8500      * whether it has to be translated so we can add more directories. */
8501     if (!islnm || rooted) {
8502       *(cp1++) = ':';
8503       *(cp1++) = '[';
8504       if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8505       else cp2++;
8506     }
8507     else {
8508       if (cp2 != dirend) {
8509         my_strlcpy(rslt, trndev, VMS_MAXRSS);
8510         cp1 = rslt + trnend;
8511 	if (*cp2 != 0) {
8512           *(cp1++) = '.';
8513           cp2++;
8514         }
8515       }
8516       else {
8517 	if (decc_disable_posix_root) {
8518 	  *(cp1++) = ':';
8519 	  hasdir = 0;
8520 	}
8521       }
8522     }
8523     PerlMem_free(trndev);
8524   }
8525   else if (hasdir) {
8526     *(cp1++) = '[';
8527     if (*cp2 == '.') {
8528       if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8529         cp2 += 2;         /* skip over "./" - it's redundant */
8530         *(cp1++) = '.';   /* but it does indicate a relative dirspec */
8531       }
8532       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8533         *(cp1++) = '-';                                 /* "../" --> "-" */
8534         cp2 += 3;
8535       }
8536       else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8537                (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8538         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8539         if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8540         cp2 += 4;
8541       }
8542       else if ((cp2 != lastdot) || (lastdot < dirend)) {
8543 	/* Escape the extra dots in EFS file specifications */
8544 	*(cp1++) = '^';
8545       }
8546       if (cp2 > dirend) cp2 = dirend;
8547     }
8548     else *(cp1++) = '.';
8549   }
8550   for (; cp2 < dirend; cp2++) {
8551     if (*cp2 == '/') {
8552       if (*(cp2-1) == '/') continue;
8553       if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8554       infront = 0;
8555     }
8556     else if (!infront && *cp2 == '.') {
8557       if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8558       else if (*(cp2+1) == '/') cp2++;   /* skip over "./" - it's redundant */
8559       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8560         if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8561         else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8562         else {
8563           *(cp1++) = '-';
8564         }
8565         cp2 += 2;
8566         if (cp2 == dirend) break;
8567       }
8568       else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8569                 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8570         if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8571         *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8572         if (!*(cp2+3)) {
8573           *(cp1++) = '.';  /* Simulate trailing '/' */
8574           cp2 += 2;  /* for loop will incr this to == dirend */
8575         }
8576         else cp2 += 3;  /* Trailing '/' was there, so skip it, too */
8577       }
8578       else {
8579         if (decc_efs_charset == 0) {
8580 	  if (cp1 > rslt && *(cp1-1) == '^')
8581 	    cp1--;         /* remove the escape, if any */
8582 	  *(cp1++) = '_';  /* fix up syntax - '.' in name not allowed */
8583 	}
8584 	else {
8585 	  VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8586 	}
8587       }
8588     }
8589     else {
8590       if (!infront && cp1 > rslt && *(cp1-1) == '-')  *(cp1++) = '.';
8591       if (*cp2 == '.') {
8592         if (decc_efs_charset == 0) {
8593 	  if (cp1 > rslt && *(cp1-1) == '^')
8594 	    cp1--;         /* remove the escape, if any */
8595 	  *(cp1++) = '_';
8596 	}
8597 	else {
8598 	  VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8599 	}
8600       }
8601       else                  *(cp1++) =  *cp2;
8602       infront = 1;
8603     }
8604   }
8605   if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8606   if (hasdir) *(cp1++) = ']';
8607   if (*cp2 && *cp2 == '/') cp2++;  /* check in case we ended with trailing '/' */
8608   no_type_seen = 0;
8609   if (cp2 > lastdot)
8610     no_type_seen = 1;
8611   while (*cp2) {
8612     switch(*cp2) {
8613     case '?':
8614         if (decc_efs_charset == 0)
8615 	  *(cp1++) = '%';
8616 	else
8617 	  *(cp1++) = '?';
8618 	cp2++;
8619     case ' ':
8620 	if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8621 	    *(cp1)++ = '^';
8622 	*(cp1)++ = '_';
8623 	cp2++;
8624 	break;
8625     case '.':
8626 	if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8627 	    decc_readdir_dropdotnotype) {
8628 	  VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8629 	  cp2++;
8630 
8631 	  /* trailing dot ==> '^..' on VMS */
8632 	  if (*cp2 == '\0') {
8633 	    *(cp1++) = '.';
8634 	    no_type_seen = 0;
8635 	  }
8636 	}
8637 	else {
8638 	  *(cp1++) = *(cp2++);
8639 	  no_type_seen = 0;
8640 	}
8641 	break;
8642     case '$':
8643 	 /* This could be a macro to be passed through */
8644 	*(cp1++) = *(cp2++);
8645 	if (*cp2 == '(') {
8646 	const char * save_cp2;
8647 	char * save_cp1;
8648 	int is_macro;
8649 
8650 	    /* paranoid check */
8651 	    save_cp2 = cp2;
8652 	    save_cp1 = cp1;
8653 	    is_macro = 0;
8654 
8655 	    /* Test through */
8656 	    *(cp1++) = *(cp2++);
8657 	    if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8658 		*(cp1++) = *(cp2++);
8659 		while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8660 		    *(cp1++) = *(cp2++);
8661 		}
8662 		if (*cp2 == ')') {
8663 		    *(cp1++) = *(cp2++);
8664 		    is_macro = 1;
8665 		}
8666 	    }
8667 	    if (is_macro == 0) {
8668 		/* Not really a macro - never mind */
8669 		cp2 = save_cp2;
8670 		cp1 = save_cp1;
8671 	    }
8672 	}
8673 	break;
8674     case '\"':
8675     case '~':
8676     case '`':
8677     case '!':
8678     case '#':
8679     case '%':
8680     case '^':
8681         /* Don't escape again if following character is
8682          * already something we escape.
8683          */
8684         if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8685 	    *(cp1++) = *(cp2++);
8686 	    break;
8687         }
8688         /* But otherwise fall through and escape it. */
8689     case '&':
8690     case '(':
8691     case ')':
8692     case '=':
8693     case '+':
8694     case '\'':
8695     case '@':
8696     case '[':
8697     case ']':
8698     case '{':
8699     case '}':
8700     case ':':
8701     case '\\':
8702     case '|':
8703     case '<':
8704     case '>':
8705 	if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */
8706 	    *(cp1++) = '^';
8707 	*(cp1++) = *(cp2++);
8708 	break;
8709     case ';':
8710         /* If it doesn't look like the beginning of a version number,
8711          * or we've been promised there are no version numbers, then
8712          * escape it.
8713          */
8714 	if (decc_filename_unix_no_version) {
8715 	  *(cp1++) = '^';
8716 	}
8717 	else {
8718 	  size_t all_nums = strspn(cp2+1, "0123456789");
8719 	  if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8720 	    *(cp1++) = '^';
8721 	}
8722 	*(cp1++) = *(cp2++);
8723 	break;
8724     default:
8725 	*(cp1++) = *(cp2++);
8726     }
8727   }
8728   if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8729   char *lcp1;
8730     lcp1 = cp1;
8731     lcp1--;
8732      /* Fix me for "^]", but that requires making sure that you do
8733       * not back up past the start of the filename
8734       */
8735     if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8736       *cp1++ = '.';
8737   }
8738   *cp1 = '\0';
8739 
8740   if (utf8_flag != NULL)
8741     *utf8_flag = 0;
8742   if (vms_debug_fileify) {
8743       fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8744   }
8745   return rslt;
8746 
8747 }  /* end of int_tovmsspec() */
8748 
8749 
8750 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8751 static char *mp_do_tovmsspec
8752    (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8753   static char __tovmsspec_retbuf[VMS_MAXRSS];
8754     char * vmsspec, *ret_spec, *ret_buf;
8755 
8756     vmsspec = NULL;
8757     ret_buf = buf;
8758     if (ret_buf == NULL) {
8759         if (ts) {
8760             Newx(vmsspec, VMS_MAXRSS, char);
8761             if (vmsspec == NULL)
8762                 _ckvmssts(SS$_INSFMEM);
8763             ret_buf = vmsspec;
8764         } else {
8765             ret_buf = __tovmsspec_retbuf;
8766         }
8767     }
8768 
8769     ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8770 
8771     if (ret_spec == NULL) {
8772        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8773        if (vmsspec)
8774            Safefree(vmsspec);
8775     }
8776 
8777     return ret_spec;
8778 
8779 }  /* end of mp_do_tovmsspec() */
8780 /*}}}*/
8781 /* External entry points */
8782 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8783   { return do_tovmsspec(path,buf,0,NULL); }
8784 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8785   { return do_tovmsspec(path,buf,1,NULL); }
8786 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8787   { return do_tovmsspec(path,buf,0,utf8_fl); }
8788 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8789   { return do_tovmsspec(path,buf,1,utf8_fl); }
8790 
8791 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8792 /* Internal routine for use with out an explicit context present */
8793 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8794 
8795     char * ret_spec, *pathified;
8796 
8797     if (path == NULL)
8798         return NULL;
8799 
8800     pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8801     if (pathified == NULL)
8802         _ckvmssts_noperl(SS$_INSFMEM);
8803 
8804     ret_spec = int_pathify_dirspec(path, pathified);
8805 
8806     if (ret_spec == NULL) {
8807         PerlMem_free(pathified);
8808         return NULL;
8809     }
8810 
8811     ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8812 
8813     PerlMem_free(pathified);
8814     return ret_spec;
8815 
8816 }
8817 
8818 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8819 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8820   static char __tovmspath_retbuf[VMS_MAXRSS];
8821   int vmslen;
8822   char *pathified, *vmsified, *cp;
8823 
8824   if (path == NULL) return NULL;
8825   pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8826   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8827   if (int_pathify_dirspec(path, pathified) == NULL) {
8828     PerlMem_free(pathified);
8829     return NULL;
8830   }
8831 
8832   vmsified = NULL;
8833   if (buf == NULL)
8834      Newx(vmsified, VMS_MAXRSS, char);
8835   if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8836     PerlMem_free(pathified);
8837     if (vmsified) Safefree(vmsified);
8838     return NULL;
8839   }
8840   PerlMem_free(pathified);
8841   if (buf) {
8842     return buf;
8843   }
8844   else if (ts) {
8845     vmslen = strlen(vmsified);
8846     Newx(cp,vmslen+1,char);
8847     memcpy(cp,vmsified,vmslen);
8848     cp[vmslen] = '\0';
8849     Safefree(vmsified);
8850     return cp;
8851   }
8852   else {
8853     my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8854     Safefree(vmsified);
8855     return __tovmspath_retbuf;
8856   }
8857 
8858 }  /* end of do_tovmspath() */
8859 /*}}}*/
8860 /* External entry points */
8861 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8862   { return do_tovmspath(path,buf,0, NULL); }
8863 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8864   { return do_tovmspath(path,buf,1, NULL); }
8865 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8866   { return do_tovmspath(path,buf,0,utf8_fl); }
8867 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8868   { return do_tovmspath(path,buf,1,utf8_fl); }
8869 
8870 
8871 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8872 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8873   static char __tounixpath_retbuf[VMS_MAXRSS];
8874   int unixlen;
8875   char *pathified, *unixified, *cp;
8876 
8877   if (path == NULL) return NULL;
8878   pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8879   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8880   if (int_pathify_dirspec(path, pathified) == NULL) {
8881     PerlMem_free(pathified);
8882     return NULL;
8883   }
8884 
8885   unixified = NULL;
8886   if (buf == NULL) {
8887       Newx(unixified, VMS_MAXRSS, char);
8888   }
8889   if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8890     PerlMem_free(pathified);
8891     if (unixified) Safefree(unixified);
8892     return NULL;
8893   }
8894   PerlMem_free(pathified);
8895   if (buf) {
8896     return buf;
8897   }
8898   else if (ts) {
8899     unixlen = strlen(unixified);
8900     Newx(cp,unixlen+1,char);
8901     memcpy(cp,unixified,unixlen);
8902     cp[unixlen] = '\0';
8903     Safefree(unixified);
8904     return cp;
8905   }
8906   else {
8907     my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8908     Safefree(unixified);
8909     return __tounixpath_retbuf;
8910   }
8911 
8912 }  /* end of do_tounixpath() */
8913 /*}}}*/
8914 /* External entry points */
8915 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8916   { return do_tounixpath(path,buf,0,NULL); }
8917 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8918   { return do_tounixpath(path,buf,1,NULL); }
8919 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8920   { return do_tounixpath(path,buf,0,utf8_fl); }
8921 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8922   { return do_tounixpath(path,buf,1,utf8_fl); }
8923 
8924 /*
8925  * @(#)argproc.c 2.2 94/08/16	Mark Pizzolato (mark AT infocomm DOT com)
8926  *
8927  *****************************************************************************
8928  *                                                                           *
8929  *  Copyright (C) 1989-1994, 2007 by                                         *
8930  *  Mark Pizzolato - INFO COMM, Danville, California  (510) 837-5600         *
8931  *                                                                           *
8932  *  Permission is hereby granted for the reproduction of this software       *
8933  *  on condition that this copyright notice is included in source            *
8934  *  distributions of the software.  The code may be modified and             *
8935  *  distributed under the same terms as Perl itself.                         *
8936  *                                                                           *
8937  *  27-Aug-1994 Modified for inclusion in perl5                              *
8938  *              by Charles Bailey  (bailey AT newman DOT upenn DOT edu)      *
8939  *****************************************************************************
8940  */
8941 
8942 /*
8943  * getredirection() is intended to aid in porting C programs
8944  * to VMS (Vax-11 C).  The native VMS environment does not support
8945  * '>' and '<' I/O redirection, or command line wild card expansion,
8946  * or a command line pipe mechanism using the '|' AND background
8947  * command execution '&'.  All of these capabilities are provided to any
8948  * C program which calls this procedure as the first thing in the
8949  * main program.
8950  * The piping mechanism will probably work with almost any 'filter' type
8951  * of program.  With suitable modification, it may useful for other
8952  * portability problems as well.
8953  *
8954  * Author:  Mark Pizzolato	(mark AT infocomm DOT com)
8955  */
8956 struct list_item
8957     {
8958     struct list_item *next;
8959     char *value;
8960     };
8961 
8962 static void add_item(struct list_item **head,
8963 		     struct list_item **tail,
8964 		     char *value,
8965 		     int *count);
8966 
8967 static void mp_expand_wild_cards(pTHX_ char *item,
8968 				struct list_item **head,
8969 				struct list_item **tail,
8970 				int *count);
8971 
8972 static int background_process(pTHX_ int argc, char **argv);
8973 
8974 static void pipe_and_fork(pTHX_ char **cmargv);
8975 
8976 /*{{{ void getredirection(int *ac, char ***av)*/
8977 static void
8978 mp_getredirection(pTHX_ int *ac, char ***av)
8979 /*
8980  * Process vms redirection arg's.  Exit if any error is seen.
8981  * If getredirection() processes an argument, it is erased
8982  * from the vector.  getredirection() returns a new argc and argv value.
8983  * In the event that a background command is requested (by a trailing "&"),
8984  * this routine creates a background subprocess, and simply exits the program.
8985  *
8986  * Warning: do not try to simplify the code for vms.  The code
8987  * presupposes that getredirection() is called before any data is
8988  * read from stdin or written to stdout.
8989  *
8990  * Normal usage is as follows:
8991  *
8992  *	main(argc, argv)
8993  *	int		argc;
8994  *    	char		*argv[];
8995  *	{
8996  *		getredirection(&argc, &argv);
8997  *	}
8998  */
8999 {
9000     int			argc = *ac;	/* Argument Count	  */
9001     char		**argv = *av;	/* Argument Vector	  */
9002     char		*ap;   		/* Argument pointer	  */
9003     int	       		j;		/* argv[] index		  */
9004     int			item_count = 0;	/* Count of Items in List */
9005     struct list_item 	*list_head = 0;	/* First Item in List	    */
9006     struct list_item	*list_tail;	/* Last Item in List	    */
9007     char 		*in = NULL;	/* Input File Name	    */
9008     char 		*out = NULL;	/* Output File Name	    */
9009     char 		*outmode = "w";	/* Mode to Open Output File */
9010     char 		*err = NULL;	/* Error File Name	    */
9011     char 		*errmode = "w";	/* Mode to Open Error File  */
9012     int			cmargc = 0;    	/* Piped Command Arg Count  */
9013     char		**cmargv = NULL;/* Piped Command Arg Vector */
9014 
9015     /*
9016      * First handle the case where the last thing on the line ends with
9017      * a '&'.  This indicates the desire for the command to be run in a
9018      * subprocess, so we satisfy that desire.
9019      */
9020     ap = argv[argc-1];
9021     if (0 == strcmp("&", ap))
9022        exit(background_process(aTHX_ --argc, argv));
9023     if (*ap && '&' == ap[strlen(ap)-1])
9024 	{
9025 	ap[strlen(ap)-1] = '\0';
9026        exit(background_process(aTHX_ argc, argv));
9027 	}
9028     /*
9029      * Now we handle the general redirection cases that involve '>', '>>',
9030      * '<', and pipes '|'.
9031      */
9032     for (j = 0; j < argc; ++j)
9033 	{
9034 	if (0 == strcmp("<", argv[j]))
9035 	    {
9036 	    if (j+1 >= argc)
9037 		{
9038 		fprintf(stderr,"No input file after < on command line");
9039 		exit(LIB$_WRONUMARG);
9040 		}
9041 	    in = argv[++j];
9042 	    continue;
9043 	    }
9044 	if ('<' == *(ap = argv[j]))
9045 	    {
9046 	    in = 1 + ap;
9047 	    continue;
9048 	    }
9049 	if (0 == strcmp(">", ap))
9050 	    {
9051 	    if (j+1 >= argc)
9052 		{
9053 		fprintf(stderr,"No output file after > on command line");
9054 		exit(LIB$_WRONUMARG);
9055 		}
9056 	    out = argv[++j];
9057 	    continue;
9058 	    }
9059 	if ('>' == *ap)
9060 	    {
9061 	    if ('>' == ap[1])
9062 		{
9063 		outmode = "a";
9064 		if ('\0' == ap[2])
9065 		    out = argv[++j];
9066 		else
9067 		    out = 2 + ap;
9068 		}
9069 	    else
9070 		out = 1 + ap;
9071 	    if (j >= argc)
9072 		{
9073 		fprintf(stderr,"No output file after > or >> on command line");
9074 		exit(LIB$_WRONUMARG);
9075 		}
9076 	    continue;
9077 	    }
9078 	if (('2' == *ap) && ('>' == ap[1]))
9079 	    {
9080 	    if ('>' == ap[2])
9081 		{
9082 		errmode = "a";
9083 		if ('\0' == ap[3])
9084 		    err = argv[++j];
9085 		else
9086 		    err = 3 + ap;
9087 		}
9088 	    else
9089 		if ('\0' == ap[2])
9090 		    err = argv[++j];
9091 		else
9092 		    err = 2 + ap;
9093 	    if (j >= argc)
9094 		{
9095 		fprintf(stderr,"No output file after 2> or 2>> on command line");
9096 		exit(LIB$_WRONUMARG);
9097 		}
9098 	    continue;
9099 	    }
9100 	if (0 == strcmp("|", argv[j]))
9101 	    {
9102 	    if (j+1 >= argc)
9103 		{
9104 		fprintf(stderr,"No command into which to pipe on command line");
9105 		exit(LIB$_WRONUMARG);
9106 		}
9107 	    cmargc = argc-(j+1);
9108 	    cmargv = &argv[j+1];
9109 	    argc = j;
9110 	    continue;
9111 	    }
9112 	if ('|' == *(ap = argv[j]))
9113 	    {
9114 	    ++argv[j];
9115 	    cmargc = argc-j;
9116 	    cmargv = &argv[j];
9117 	    argc = j;
9118 	    continue;
9119 	    }
9120 	expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9121 	}
9122     /*
9123      * Allocate and fill in the new argument vector, Some Unix's terminate
9124      * the list with an extra null pointer.
9125      */
9126     argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9127     if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9128     *av = argv;
9129     for (j = 0; j < item_count; ++j, list_head = list_head->next)
9130 	argv[j] = list_head->value;
9131     *ac = item_count;
9132     if (cmargv != NULL)
9133 	{
9134 	if (out != NULL)
9135 	    {
9136 	    fprintf(stderr,"'|' and '>' may not both be specified on command line");
9137 	    exit(LIB$_INVARGORD);
9138 	    }
9139 	pipe_and_fork(aTHX_ cmargv);
9140 	}
9141 
9142     /* Check for input from a pipe (mailbox) */
9143 
9144     if (in == NULL && 1 == isapipe(0))
9145 	{
9146 	char mbxname[L_tmpnam];
9147 	long int bufsize;
9148 	long int dvi_item = DVI$_DEVBUFSIZ;
9149 	$DESCRIPTOR(mbxnam, "");
9150 	$DESCRIPTOR(mbxdevnam, "");
9151 
9152 	/* Input from a pipe, reopen it in binary mode to disable	*/
9153 	/* carriage control processing.	 				*/
9154 
9155 	fgetname(stdin, mbxname, 1);
9156 	mbxnam.dsc$a_pointer = mbxname;
9157 	mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9158 	lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9159 	mbxdevnam.dsc$a_pointer = mbxname;
9160 	mbxdevnam.dsc$w_length = sizeof(mbxname);
9161 	dvi_item = DVI$_DEVNAM;
9162 	lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9163 	mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9164 	set_errno(0);
9165 	set_vaxc_errno(1);
9166 	freopen(mbxname, "rb", stdin);
9167 	if (errno != 0)
9168 	    {
9169 	    fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9170 	    exit(vaxc$errno);
9171 	    }
9172 	}
9173     if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9174 	{
9175 	fprintf(stderr,"Can't open input file %s as stdin",in);
9176 	exit(vaxc$errno);
9177 	}
9178     if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9179 	{
9180 	fprintf(stderr,"Can't open output file %s as stdout",out);
9181 	exit(vaxc$errno);
9182 	}
9183 	if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9184 
9185     if (err != NULL) {
9186         if (strcmp(err,"&1") == 0) {
9187             dup2(fileno(stdout), fileno(stderr));
9188             vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9189         } else {
9190 	FILE *tmperr;
9191 	if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9192 	    {
9193 	    fprintf(stderr,"Can't open error file %s as stderr",err);
9194 	    exit(vaxc$errno);
9195 	    }
9196 	    fclose(tmperr);
9197            if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9198 		{
9199 		exit(vaxc$errno);
9200 		}
9201 	    vmssetuserlnm("SYS$ERROR", err);
9202 	}
9203         }
9204 #ifdef ARGPROC_DEBUG
9205     PerlIO_printf(Perl_debug_log, "Arglist:\n");
9206     for (j = 0; j < *ac;  ++j)
9207 	PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9208 #endif
9209    /* Clear errors we may have hit expanding wildcards, so they don't
9210       show up in Perl's $! later */
9211    set_errno(0); set_vaxc_errno(1);
9212 }  /* end of getredirection() */
9213 /*}}}*/
9214 
9215 static void add_item(struct list_item **head,
9216 		     struct list_item **tail,
9217 		     char *value,
9218 		     int *count)
9219 {
9220     if (*head == 0)
9221 	{
9222 	*head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9223 	if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9224 	*tail = *head;
9225 	}
9226     else {
9227 	(*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9228 	if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9229 	*tail = (*tail)->next;
9230 	}
9231     (*tail)->value = value;
9232     ++(*count);
9233 }
9234 
9235 static void mp_expand_wild_cards(pTHX_ char *item,
9236 			      struct list_item **head,
9237 			      struct list_item **tail,
9238 			      int *count)
9239 {
9240 int expcount = 0;
9241 unsigned long int context = 0;
9242 int isunix = 0;
9243 int item_len = 0;
9244 char *had_version;
9245 char *had_device;
9246 int had_directory;
9247 char *devdir,*cp;
9248 char *vmsspec;
9249 $DESCRIPTOR(filespec, "");
9250 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9251 $DESCRIPTOR(resultspec, "");
9252 unsigned long int lff_flags = 0;
9253 int sts;
9254 int rms_sts;
9255 
9256 #ifdef VMS_LONGNAME_SUPPORT
9257     lff_flags = LIB$M_FIL_LONG_NAMES;
9258 #endif
9259 
9260     for (cp = item; *cp; cp++) {
9261 	if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9262 	if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9263     }
9264     if (!*cp || isspace(*cp))
9265 	{
9266 	add_item(head, tail, item, count);
9267 	return;
9268 	}
9269     else
9270         {
9271      /* "double quoted" wild card expressions pass as is */
9272      /* From DCL that means using e.g.:                  */
9273      /* perl program """perl.*"""                        */
9274      item_len = strlen(item);
9275      if ( '"' == *item && '"' == item[item_len-1] )
9276        {
9277        item++;
9278        item[item_len-2] = '\0';
9279        add_item(head, tail, item, count);
9280        return;
9281        }
9282      }
9283     resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9284     resultspec.dsc$b_class = DSC$K_CLASS_D;
9285     resultspec.dsc$a_pointer = NULL;
9286     vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9287     if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9288     if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9289       filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9290     if (!isunix || !filespec.dsc$a_pointer)
9291       filespec.dsc$a_pointer = item;
9292     filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9293     /*
9294      * Only return version specs, if the caller specified a version
9295      */
9296     had_version = strchr(item, ';');
9297     /*
9298      * Only return device and directory specs, if the caller specified either.
9299      */
9300     had_device = strchr(item, ':');
9301     had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9302 
9303     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9304 				 (&filespec, &resultspec, &context,
9305     				  &defaultspec, 0, &rms_sts, &lff_flags)))
9306 	{
9307 	char *string;
9308 	char *c;
9309 
9310 	string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9311         if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9312 	my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9313 	if (NULL == had_version)
9314 	    *(strrchr(string, ';')) = '\0';
9315 	if ((!had_directory) && (had_device == NULL))
9316 	    {
9317 	    if (NULL == (devdir = strrchr(string, ']')))
9318 		devdir = strrchr(string, '>');
9319 	    my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9320 	    }
9321 	/*
9322 	 * Be consistent with what the C RTL has already done to the rest of
9323 	 * the argv items and lowercase all of these names.
9324 	 */
9325 	if (!decc_efs_case_preserve) {
9326 	    for (c = string; *c; ++c)
9327 	    if (isupper(*c))
9328 		*c = tolower(*c);
9329 	}
9330 	if (isunix) trim_unixpath(string,item,1);
9331 	add_item(head, tail, string, count);
9332 	++expcount;
9333     }
9334     PerlMem_free(vmsspec);
9335     if (sts != RMS$_NMF)
9336 	{
9337 	set_vaxc_errno(sts);
9338 	switch (sts)
9339 	    {
9340 	    case RMS$_FNF: case RMS$_DNF:
9341 		set_errno(ENOENT); break;
9342 	    case RMS$_DIR:
9343 		set_errno(ENOTDIR); break;
9344 	    case RMS$_DEV:
9345 		set_errno(ENODEV); break;
9346 	    case RMS$_FNM: case RMS$_SYN:
9347 		set_errno(EINVAL); break;
9348 	    case RMS$_PRV:
9349 		set_errno(EACCES); break;
9350 	    default:
9351 		_ckvmssts_noperl(sts);
9352 	    }
9353 	}
9354     if (expcount == 0)
9355 	add_item(head, tail, item, count);
9356     _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9357     _ckvmssts_noperl(lib$find_file_end(&context));
9358 }
9359 
9360 static int child_st[2];/* Event Flag set when child process completes	*/
9361 
9362 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox		*/
9363 
9364 static unsigned long int exit_handler(void)
9365 {
9366 short iosb[4];
9367 
9368     if (0 == child_st[0])
9369 	{
9370 #ifdef ARGPROC_DEBUG
9371 	PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9372 #endif
9373 	fflush(stdout);	    /* Have to flush pipe for binary data to	*/
9374 			    /* terminate properly -- <tp@mccall.com>	*/
9375 	sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9376 	sys$dassgn(child_chan);
9377 	fclose(stdout);
9378 	sys$synch(0, child_st);
9379 	}
9380     return(1);
9381 }
9382 
9383 static void sig_child(int chan)
9384 {
9385 #ifdef ARGPROC_DEBUG
9386     PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9387 #endif
9388     if (child_st[0] == 0)
9389 	child_st[0] = 1;
9390 }
9391 
9392 static struct exit_control_block exit_block =
9393     {
9394     0,
9395     exit_handler,
9396     1,
9397     &exit_block.exit_status,
9398     0
9399     };
9400 
9401 static void
9402 pipe_and_fork(pTHX_ char **cmargv)
9403 {
9404     PerlIO *fp;
9405     struct dsc$descriptor_s *vmscmd;
9406     char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9407     int sts, j, l, ismcr, quote, tquote = 0;
9408 
9409     sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9410     vms_execfree(vmscmd);
9411 
9412     j = l = 0;
9413     p = subcmd;
9414     q = cmargv[0];
9415     ismcr = q && toupper(*q) == 'M'     && toupper(*(q+1)) == 'C'
9416               && toupper(*(q+2)) == 'R' && !*(q+3);
9417 
9418     while (q && l < MAX_DCL_LINE_LENGTH) {
9419         if (!*q) {
9420             if (j > 0 && quote) {
9421                 *p++ = '"';
9422                 l++;
9423             }
9424             q = cmargv[++j];
9425             if (q) {
9426                 if (ismcr && j > 1) quote = 1;
9427                 tquote =  (strchr(q,' ')) != NULL || *q == '\0';
9428                 *p++ = ' ';
9429                 l++;
9430                 if (quote || tquote) {
9431                     *p++ = '"';
9432                     l++;
9433                 }
9434 	    }
9435         } else {
9436             if ((quote||tquote) && *q == '"') {
9437                 *p++ = '"';
9438                 l++;
9439 	    }
9440             *p++ = *q++;
9441             l++;
9442         }
9443     }
9444     *p = '\0';
9445 
9446     fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9447     if (fp == NULL) {
9448         PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9449     }
9450 }
9451 
9452 static int background_process(pTHX_ int argc, char **argv)
9453 {
9454 char command[MAX_DCL_SYMBOL + 1] = "$";
9455 $DESCRIPTOR(value, "");
9456 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9457 static $DESCRIPTOR(null, "NLA0:");
9458 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9459 char pidstring[80];
9460 $DESCRIPTOR(pidstr, "");
9461 int pid;
9462 unsigned long int flags = 17, one = 1, retsts;
9463 int len;
9464 
9465     len = my_strlcat(command, argv[0], sizeof(command));
9466     while (--argc && (len < MAX_DCL_SYMBOL))
9467 	{
9468 	my_strlcat(command, " \"", sizeof(command));
9469 	my_strlcat(command, *(++argv), sizeof(command));
9470 	len = my_strlcat(command, "\"", sizeof(command));
9471 	}
9472     value.dsc$a_pointer = command;
9473     value.dsc$w_length = strlen(value.dsc$a_pointer);
9474     _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9475     retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9476     if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9477 	_ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9478     }
9479     else {
9480 	_ckvmssts_noperl(retsts);
9481     }
9482 #ifdef ARGPROC_DEBUG
9483     PerlIO_printf(Perl_debug_log, "%s\n", command);
9484 #endif
9485     sprintf(pidstring, "%08X", pid);
9486     PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9487     pidstr.dsc$a_pointer = pidstring;
9488     pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9489     lib$set_symbol(&pidsymbol, &pidstr);
9490     return(SS$_NORMAL);
9491 }
9492 /*}}}*/
9493 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9494 
9495 
9496 /* OS-specific initialization at image activation (not thread startup) */
9497 /* Older VAXC header files lack these constants */
9498 #ifndef JPI$_RIGHTS_SIZE
9499 #  define JPI$_RIGHTS_SIZE 817
9500 #endif
9501 #ifndef KGB$M_SUBSYSTEM
9502 #  define KGB$M_SUBSYSTEM 0x8
9503 #endif
9504 
9505 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9506 
9507 /*{{{void vms_image_init(int *, char ***)*/
9508 void
9509 vms_image_init(int *argcp, char ***argvp)
9510 {
9511   int status;
9512   char eqv[LNM$C_NAMLENGTH+1] = "";
9513   unsigned int len, tabct = 8, tabidx = 0;
9514   unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9515   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9516   unsigned short int dummy, rlen;
9517   struct dsc$descriptor_s **tabvec;
9518 #if defined(PERL_IMPLICIT_CONTEXT)
9519   pTHX = NULL;
9520 #endif
9521   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
9522                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
9523                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9524                                  {          0,                0,    0,      0} };
9525 
9526 #ifdef KILL_BY_SIGPRC
9527     Perl_csighandler_init();
9528 #endif
9529 
9530 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9531     /* This was moved from the pre-image init handler because on threaded */
9532     /* Perl it was always returning 0 for the default value. */
9533     status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9534     if (status > 0) {
9535         int s;
9536 	s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9537 	if (s > 0) {
9538             int initial;
9539 	    initial = decc$feature_get_value(s, 4);
9540 	    if (initial > 0) {
9541                 /* initial is: 0 if nothing has set the feature */
9542                 /*            -1 if initialized to default */
9543                 /*             1 if set by logical name */
9544                 /*             2 if set by decc$feature_set_value */
9545 		decc_disable_posix_root = decc$feature_get_value(s, 1);
9546 
9547                 /* If the value is not valid, force the feature off */
9548 		if (decc_disable_posix_root < 0) {
9549 		    decc$feature_set_value(s, 1, 1);
9550 		    decc_disable_posix_root = 1;
9551 		}
9552 	    }
9553 	    else {
9554 		/* Nothing has asked for it explicitly, so use our own default. */
9555 		decc_disable_posix_root = 1;
9556 		decc$feature_set_value(s, 1, 1);
9557 	    }
9558 	}
9559     }
9560 #endif
9561 
9562   _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9563   _ckvmssts_noperl(iosb[0]);
9564   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9565     if (iprv[i]) {           /* Running image installed with privs? */
9566       _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
9567       will_taint = TRUE;
9568       break;
9569     }
9570   }
9571   /* Rights identifiers might trigger tainting as well. */
9572   if (!will_taint && (rlen || rsz)) {
9573     while (rlen < rsz) {
9574       /* We didn't get all the identifiers on the first pass.  Allocate a
9575        * buffer much larger than $GETJPI wants (rsz is size in bytes that
9576        * were needed to hold all identifiers at time of last call; we'll
9577        * allocate that many unsigned long ints), and go back and get 'em.
9578        * If it gave us less than it wanted to despite ample buffer space,
9579        * something's broken.  Is your system missing a system identifier?
9580        */
9581       if (rsz <= jpilist[1].buflen) {
9582          /* Perl_croak accvios when used this early in startup. */
9583          fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9584                          rsz, (unsigned long) jpilist[1].buflen,
9585                          "Check your rights database for corruption.\n");
9586          exit(SS$_ABORT);
9587       }
9588       if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9589       jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9590       if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9591       jpilist[1].buflen = rsz * sizeof(unsigned long int);
9592       _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9593       _ckvmssts_noperl(iosb[0]);
9594     }
9595     mask = (unsigned long int *)jpilist[1].bufadr;
9596     /* Check attribute flags for each identifier (2nd longword); protected
9597      * subsystem identifiers trigger tainting.
9598      */
9599     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9600       if (mask[i] & KGB$M_SUBSYSTEM) {
9601         will_taint = TRUE;
9602         break;
9603       }
9604     }
9605     if (mask != rlst) PerlMem_free(mask);
9606   }
9607 
9608   /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9609    * logical, some versions of the CRTL will add a phanthom /000000/
9610    * directory.  This needs to be removed.
9611    */
9612   if (decc_filename_unix_report) {
9613   char * zeros;
9614   int ulen;
9615     ulen = strlen(argvp[0][0]);
9616     if (ulen > 7) {
9617       zeros = strstr(argvp[0][0], "/000000/");
9618       if (zeros != NULL) {
9619 	int mlen;
9620 	mlen = ulen - (zeros - argvp[0][0]) - 7;
9621 	memmove(zeros, &zeros[7], mlen);
9622 	ulen = ulen - 7;
9623 	argvp[0][0][ulen] = '\0';
9624       }
9625     }
9626     /* It also may have a trailing dot that needs to be removed otherwise
9627      * it will be converted to VMS mode incorrectly.
9628      */
9629     ulen--;
9630     if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9631       argvp[0][0][ulen] = '\0';
9632   }
9633 
9634   /* We need to use this hack to tell Perl it should run with tainting,
9635    * since its tainting flag may be part of the PL_curinterp struct, which
9636    * hasn't been allocated when vms_image_init() is called.
9637    */
9638   if (will_taint) {
9639     char **newargv, **oldargv;
9640     oldargv = *argvp;
9641     newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9642     if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9643     newargv[0] = oldargv[0];
9644     newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9645     if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9646     strcpy(newargv[1], "-T");
9647     Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9648     (*argcp)++;
9649     newargv[*argcp] = NULL;
9650     /* We orphan the old argv, since we don't know where it's come from,
9651      * so we don't know how to free it.
9652      */
9653     *argvp = newargv;
9654   }
9655   else {  /* Did user explicitly request tainting? */
9656     int i;
9657     char *cp, **av = *argvp;
9658     for (i = 1; i < *argcp; i++) {
9659       if (*av[i] != '-') break;
9660       for (cp = av[i]+1; *cp; cp++) {
9661         if (*cp == 'T') { will_taint = 1; break; }
9662         else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9663                   strchr("DFIiMmx",*cp)) break;
9664       }
9665       if (will_taint) break;
9666     }
9667   }
9668 
9669   for (tabidx = 0;
9670        len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9671        tabidx++) {
9672     if (!tabidx) {
9673       tabvec = (struct dsc$descriptor_s **)
9674 	    PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9675       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9676     }
9677     else if (tabidx >= tabct) {
9678       tabct += 8;
9679       tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9680       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9681     }
9682     tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9683     if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9684     tabvec[tabidx]->dsc$w_length  = len;
9685     tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
9686     tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_S;
9687     tabvec[tabidx]->dsc$a_pointer = PerlMem_malloc(len + 1);
9688     if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9689     my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9690   }
9691   if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9692 
9693   getredirection(argcp,argvp);
9694 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9695   {
9696 # include <reentrancy.h>
9697   decc$set_reentrancy(C$C_MULTITHREAD);
9698   }
9699 #endif
9700   return;
9701 }
9702 /*}}}*/
9703 
9704 
9705 /* trim_unixpath()
9706  * Trim Unix-style prefix off filespec, so it looks like what a shell
9707  * glob expansion would return (i.e. from specified prefix on, not
9708  * full path).  Note that returned filespec is Unix-style, regardless
9709  * of whether input filespec was VMS-style or Unix-style.
9710  *
9711  * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9712  * determine prefix (both may be in VMS or Unix syntax).  opts is a bit
9713  * vector of options; at present, only bit 0 is used, and if set tells
9714  * trim unixpath to try the current default directory as a prefix when
9715  * presented with a possibly ambiguous ... wildcard.
9716  *
9717  * Returns !=0 on success, with trimmed filespec replacing contents of
9718  * fspec, and 0 on failure, with contents of fpsec unchanged.
9719  */
9720 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9721 int
9722 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9723 {
9724   char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9725   int tmplen, reslen = 0, dirs = 0;
9726 
9727   if (!wildspec || !fspec) return 0;
9728 
9729   unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9730   if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9731   tplate = unixwild;
9732   if (strpbrk(wildspec,"]>:") != NULL) {
9733     if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9734         PerlMem_free(unixwild);
9735 	return 0;
9736     }
9737   }
9738   else {
9739     my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9740   }
9741   unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9742   if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9743   if (strpbrk(fspec,"]>:") != NULL) {
9744     if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9745         PerlMem_free(unixwild);
9746         PerlMem_free(unixified);
9747 	return 0;
9748     }
9749     else base = unixified;
9750     /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9751      * check to see that final result fits into (isn't longer than) fspec */
9752     reslen = strlen(fspec);
9753   }
9754   else base = fspec;
9755 
9756   /* No prefix or absolute path on wildcard, so nothing to remove */
9757   if (!*tplate || *tplate == '/') {
9758     PerlMem_free(unixwild);
9759     if (base == fspec) {
9760         PerlMem_free(unixified);
9761 	return 1;
9762     }
9763     tmplen = strlen(unixified);
9764     if (tmplen > reslen) {
9765         PerlMem_free(unixified);
9766 	return 0;  /* not enough space */
9767     }
9768     /* Copy unixified resultant, including trailing NUL */
9769     memmove(fspec,unixified,tmplen+1);
9770     PerlMem_free(unixified);
9771     return 1;
9772   }
9773 
9774   for (end = base; *end; end++) ;  /* Find end of resultant filespec */
9775   if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9776     for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9777     for (cp1 = end ;cp1 >= base; cp1--)
9778       if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9779         { cp1++; break; }
9780     if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9781     PerlMem_free(unixified);
9782     PerlMem_free(unixwild);
9783     return 1;
9784   }
9785   else {
9786     char *tpl, *lcres;
9787     char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9788     int ells = 1, totells, segdirs, match;
9789     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9790                             resdsc =  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9791 
9792     while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9793     totells = ells;
9794     for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9795     tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9796     if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9797     if (ellipsis == tplate && opts & 1) {
9798       /* Template begins with an ellipsis.  Since we can't tell how many
9799        * directory names at the front of the resultant to keep for an
9800        * arbitrary starting point, we arbitrarily choose the current
9801        * default directory as a starting point.  If it's there as a prefix,
9802        * clip it off.  If not, fall through and act as if the leading
9803        * ellipsis weren't there (i.e. return shortest possible path that
9804        * could match template).
9805        */
9806       if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9807 	  PerlMem_free(tpl);
9808 	  PerlMem_free(unixified);
9809 	  PerlMem_free(unixwild);
9810 	  return 0;
9811       }
9812       if (!decc_efs_case_preserve) {
9813  	for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9814 	  if (_tolower(*cp1) != _tolower(*cp2)) break;
9815       }
9816       segdirs = dirs - totells;  /* Min # of dirs we must have left */
9817       for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9818       if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9819         memmove(fspec,cp2+1,end - cp2);
9820 	PerlMem_free(tpl);
9821 	PerlMem_free(unixified);
9822 	PerlMem_free(unixwild);
9823         return 1;
9824       }
9825     }
9826     /* First off, back up over constant elements at end of path */
9827     if (dirs) {
9828       for (front = end ; front >= base; front--)
9829          if (*front == '/' && !dirs--) { front++; break; }
9830     }
9831     lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9832     if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9833     for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9834          cp1++,cp2++) {
9835 	    if (!decc_efs_case_preserve) {
9836 		*cp2 = _tolower(*cp1);  /* Make lc copy for match */
9837 	    }
9838 	    else {
9839 		*cp2 = *cp1;
9840 	    }
9841     }
9842     if (cp1 != '\0') {
9843 	PerlMem_free(tpl);
9844 	PerlMem_free(unixified);
9845 	PerlMem_free(unixwild);
9846 	PerlMem_free(lcres);
9847 	return 0;  /* Path too long. */
9848     }
9849     lcend = cp2;
9850     *cp2 = '\0';  /* Pick up with memcpy later */
9851     lcfront = lcres + (front - base);
9852     /* Now skip over each ellipsis and try to match the path in front of it. */
9853     while (ells--) {
9854       for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9855         if (*(cp1)   == '.' && *(cp1+1) == '.' &&
9856             *(cp1+2) == '.' && *(cp1+3) == '/'    ) break;
9857       if (cp1 < tplate) break; /* template started with an ellipsis */
9858       if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9859         ellipsis = cp1; continue;
9860       }
9861       wilddsc.dsc$a_pointer = tpl;
9862       wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9863       nextell = cp1;
9864       for (segdirs = 0, cp2 = tpl;
9865            cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9866            cp1++, cp2++) {
9867          if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9868          else {
9869 	    if (!decc_efs_case_preserve) {
9870 	      *cp2 = _tolower(*cp1);  /* else lowercase for match */
9871 	    }
9872 	    else {
9873 	      *cp2 = *cp1;  /* else preserve case for match */
9874 	    }
9875 	 }
9876          if (*cp2 == '/') segdirs++;
9877       }
9878       if (cp1 != ellipsis - 1) {
9879 	  PerlMem_free(tpl);
9880 	  PerlMem_free(unixified);
9881 	  PerlMem_free(unixwild);
9882 	  PerlMem_free(lcres);
9883 	  return 0; /* Path too long */
9884       }
9885       /* Back up at least as many dirs as in template before matching */
9886       for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9887         if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9888       for (match = 0; cp1 > lcres;) {
9889         resdsc.dsc$a_pointer = cp1;
9890         if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9891           match++;
9892           if (match == 1) lcfront = cp1;
9893         }
9894         for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9895       }
9896       if (!match) {
9897 	PerlMem_free(tpl);
9898 	PerlMem_free(unixified);
9899 	PerlMem_free(unixwild);
9900 	PerlMem_free(lcres);
9901 	return 0;  /* Can't find prefix ??? */
9902       }
9903       if (match > 1 && opts & 1) {
9904         /* This ... wildcard could cover more than one set of dirs (i.e.
9905          * a set of similar dir names is repeated).  If the template
9906          * contains more than 1 ..., upstream elements could resolve the
9907          * ambiguity, but it's not worth a full backtracking setup here.
9908          * As a quick heuristic, clip off the current default directory
9909          * if it's present to find the trimmed spec, else use the
9910          * shortest string that this ... could cover.
9911          */
9912         char def[NAM$C_MAXRSS+1], *st;
9913 
9914         if (getcwd(def, sizeof def,0) == NULL) {
9915 	    PerlMem_free(unixified);
9916 	    PerlMem_free(unixwild);
9917 	    PerlMem_free(lcres);
9918 	    PerlMem_free(tpl);
9919 	    return 0;
9920 	}
9921 	if (!decc_efs_case_preserve) {
9922 	  for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9923 	    if (_tolower(*cp1) != _tolower(*cp2)) break;
9924 	}
9925         segdirs = dirs - totells;  /* Min # of dirs we must have left */
9926         for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9927         if (*cp1 == '\0' && *cp2 == '/') {
9928           memmove(fspec,cp2+1,end - cp2);
9929 	  PerlMem_free(tpl);
9930 	  PerlMem_free(unixified);
9931 	  PerlMem_free(unixwild);
9932 	  PerlMem_free(lcres);
9933           return 1;
9934         }
9935         /* Nope -- stick with lcfront from above and keep going. */
9936       }
9937     }
9938     memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9939     PerlMem_free(tpl);
9940     PerlMem_free(unixified);
9941     PerlMem_free(unixwild);
9942     PerlMem_free(lcres);
9943     return 1;
9944   }
9945 
9946 }  /* end of trim_unixpath() */
9947 /*}}}*/
9948 
9949 
9950 /*
9951  *  VMS readdir() routines.
9952  *  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9953  *
9954  *  21-Jul-1994  Charles Bailey  bailey@newman.upenn.edu
9955  *  Minor modifications to original routines.
9956  */
9957 
9958 /* readdir may have been redefined by reentr.h, so make sure we get
9959  * the local version for what we do here.
9960  */
9961 #ifdef readdir
9962 # undef readdir
9963 #endif
9964 #if !defined(PERL_IMPLICIT_CONTEXT)
9965 # define readdir Perl_readdir
9966 #else
9967 # define readdir(a) Perl_readdir(aTHX_ a)
9968 #endif
9969 
9970     /* Number of elements in vms_versions array */
9971 #define VERSIZE(e)	(sizeof e->vms_versions / sizeof e->vms_versions[0])
9972 
9973 /*
9974  *  Open a directory, return a handle for later use.
9975  */
9976 /*{{{ DIR *opendir(char*name) */
9977 DIR *
9978 Perl_opendir(pTHX_ const char *name)
9979 {
9980     DIR *dd;
9981     char *dir;
9982     Stat_t sb;
9983 
9984     Newx(dir, VMS_MAXRSS, char);
9985     if (int_tovmspath(name, dir, NULL) == NULL) {
9986       Safefree(dir);
9987       return NULL;
9988     }
9989     /* Check access before stat; otherwise stat does not
9990      * accurately report whether it's a directory.
9991      */
9992     if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
9993         && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9994       /* cando_by_name has already set errno */
9995       Safefree(dir);
9996       return NULL;
9997     }
9998     if (flex_stat(dir,&sb) == -1) return NULL;
9999     if (!S_ISDIR(sb.st_mode)) {
10000       Safefree(dir);
10001       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
10002       return NULL;
10003     }
10004     /* Get memory for the handle, and the pattern. */
10005     Newx(dd,1,DIR);
10006     Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10007 
10008     /* Fill in the fields; mainly playing with the descriptor. */
10009     sprintf(dd->pattern, "%s*.*",dir);
10010     Safefree(dir);
10011     dd->context = 0;
10012     dd->count = 0;
10013     dd->flags = 0;
10014     /* By saying we want the result of readdir() in unix format, we are really
10015      * saying we want all the escapes removed, translating characters that
10016      * must be escaped in a VMS-format name to their unescaped form, which is
10017      * presumably allowed in a Unix-format name.
10018      */
10019     dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
10020     dd->pat.dsc$a_pointer = dd->pattern;
10021     dd->pat.dsc$w_length = strlen(dd->pattern);
10022     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10023     dd->pat.dsc$b_class = DSC$K_CLASS_S;
10024 #if defined(USE_ITHREADS)
10025     Newx(dd->mutex,1,perl_mutex);
10026     MUTEX_INIT( (perl_mutex *) dd->mutex );
10027 #else
10028     dd->mutex = NULL;
10029 #endif
10030 
10031     return dd;
10032 }  /* end of opendir() */
10033 /*}}}*/
10034 
10035 /*
10036  *  Set the flag to indicate we want versions or not.
10037  */
10038 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10039 void
10040 vmsreaddirversions(DIR *dd, int flag)
10041 {
10042     if (flag)
10043 	dd->flags |= PERL_VMSDIR_M_VERSIONS;
10044     else
10045 	dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10046 }
10047 /*}}}*/
10048 
10049 /*
10050  *  Free up an opened directory.
10051  */
10052 /*{{{ void closedir(DIR *dd)*/
10053 void
10054 Perl_closedir(DIR *dd)
10055 {
10056     int sts;
10057 
10058     sts = lib$find_file_end(&dd->context);
10059     Safefree(dd->pattern);
10060 #if defined(USE_ITHREADS)
10061     MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10062     Safefree(dd->mutex);
10063 #endif
10064     Safefree(dd);
10065 }
10066 /*}}}*/
10067 
10068 /*
10069  *  Collect all the version numbers for the current file.
10070  */
10071 static void
10072 collectversions(pTHX_ DIR *dd)
10073 {
10074     struct dsc$descriptor_s	pat;
10075     struct dsc$descriptor_s	res;
10076     struct dirent *e;
10077     char *p, *text, *buff;
10078     int i;
10079     unsigned long context, tmpsts;
10080 
10081     /* Convenient shorthand. */
10082     e = &dd->entry;
10083 
10084     /* Add the version wildcard, ignoring the "*.*" put on before */
10085     i = strlen(dd->pattern);
10086     Newx(text,i + e->d_namlen + 3,char);
10087     my_strlcpy(text, dd->pattern, i + 1);
10088     sprintf(&text[i - 3], "%s;*", e->d_name);
10089 
10090     /* Set up the pattern descriptor. */
10091     pat.dsc$a_pointer = text;
10092     pat.dsc$w_length = i + e->d_namlen - 1;
10093     pat.dsc$b_dtype = DSC$K_DTYPE_T;
10094     pat.dsc$b_class = DSC$K_CLASS_S;
10095 
10096     /* Set up result descriptor. */
10097     Newx(buff, VMS_MAXRSS, char);
10098     res.dsc$a_pointer = buff;
10099     res.dsc$w_length = VMS_MAXRSS - 1;
10100     res.dsc$b_dtype = DSC$K_DTYPE_T;
10101     res.dsc$b_class = DSC$K_CLASS_S;
10102 
10103     /* Read files, collecting versions. */
10104     for (context = 0, e->vms_verscount = 0;
10105          e->vms_verscount < VERSIZE(e);
10106          e->vms_verscount++) {
10107 	unsigned long rsts;
10108 	unsigned long flags = 0;
10109 
10110 #ifdef VMS_LONGNAME_SUPPORT
10111 	flags = LIB$M_FIL_LONG_NAMES;
10112 #endif
10113 	tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10114 	if (tmpsts == RMS$_NMF || context == 0) break;
10115 	_ckvmssts(tmpsts);
10116 	buff[VMS_MAXRSS - 1] = '\0';
10117 	if ((p = strchr(buff, ';')))
10118 	    e->vms_versions[e->vms_verscount] = atoi(p + 1);
10119 	else
10120 	    e->vms_versions[e->vms_verscount] = -1;
10121     }
10122 
10123     _ckvmssts(lib$find_file_end(&context));
10124     Safefree(text);
10125     Safefree(buff);
10126 
10127 }  /* end of collectversions() */
10128 
10129 /*
10130  *  Read the next entry from the directory.
10131  */
10132 /*{{{ struct dirent *readdir(DIR *dd)*/
10133 struct dirent *
10134 Perl_readdir(pTHX_ DIR *dd)
10135 {
10136     struct dsc$descriptor_s	res;
10137     char *p, *buff;
10138     unsigned long int tmpsts;
10139     unsigned long rsts;
10140     unsigned long flags = 0;
10141     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10142     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10143 
10144     /* Set up result descriptor, and get next file. */
10145     Newx(buff, VMS_MAXRSS, char);
10146     res.dsc$a_pointer = buff;
10147     res.dsc$w_length = VMS_MAXRSS - 1;
10148     res.dsc$b_dtype = DSC$K_DTYPE_T;
10149     res.dsc$b_class = DSC$K_CLASS_S;
10150 
10151 #ifdef VMS_LONGNAME_SUPPORT
10152     flags = LIB$M_FIL_LONG_NAMES;
10153 #endif
10154 
10155     tmpsts = lib$find_file
10156 	(&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10157     if (dd->context == 0)
10158         tmpsts = RMS$_NMF;  /* None left. (should be set, but make sure) */
10159 
10160     if (!(tmpsts & 1)) {
10161       switch (tmpsts) {
10162         case RMS$_NMF:
10163           break;  /* no more files considered success */
10164         case RMS$_PRV:
10165           SETERRNO(EACCES, tmpsts); break;
10166         case RMS$_DEV:
10167           SETERRNO(ENODEV, tmpsts); break;
10168         case RMS$_DIR:
10169           SETERRNO(ENOTDIR, tmpsts); break;
10170         case RMS$_FNF: case RMS$_DNF:
10171           SETERRNO(ENOENT, tmpsts); break;
10172         default:
10173           SETERRNO(EVMSERR, tmpsts);
10174       }
10175       Safefree(buff);
10176       return NULL;
10177     }
10178     dd->count++;
10179     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10180     buff[res.dsc$w_length] = '\0';
10181     p = buff + res.dsc$w_length;
10182     while (--p >= buff) if (!isspace(*p)) break;
10183     *p = '\0';
10184     if (!decc_efs_case_preserve) {
10185       for (p = buff; *p; p++) *p = _tolower(*p);
10186     }
10187 
10188     /* Skip any directory component and just copy the name. */
10189     sts = vms_split_path
10190        (buff,
10191 	&v_spec,
10192 	&v_len,
10193 	&r_spec,
10194 	&r_len,
10195 	&d_spec,
10196 	&d_len,
10197 	&n_spec,
10198 	&n_len,
10199 	&e_spec,
10200 	&e_len,
10201 	&vs_spec,
10202 	&vs_len);
10203 
10204     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10205 
10206         /* In Unix report mode, remove the ".dir;1" from the name */
10207         /* if it is a real directory. */
10208         if (decc_filename_unix_report && decc_efs_charset) {
10209             if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10210                 Stat_t statbuf;
10211                 int ret_sts;
10212 
10213                 ret_sts = flex_lstat(buff, &statbuf);
10214                 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10215                     e_len = 0;
10216                     e_spec[0] = 0;
10217                 }
10218             }
10219         }
10220 
10221         /* Drop NULL extensions on UNIX file specification */
10222 	if ((e_len == 1) && decc_readdir_dropdotnotype) {
10223 	    e_len = 0;
10224 	    e_spec[0] = '\0';
10225         }
10226     }
10227 
10228     memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10229     dd->entry.d_name[n_len + e_len] = '\0';
10230     dd->entry.d_namlen = n_len + e_len;
10231 
10232     /* Convert the filename to UNIX format if needed */
10233     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10234 
10235 	/* Translate the encoded characters. */
10236 	/* Fixme: Unicode handling could result in embedded 0 characters */
10237 	if (strchr(dd->entry.d_name, '^') != NULL) {
10238 	    char new_name[256];
10239 	    char * q;
10240 	    p = dd->entry.d_name;
10241 	    q = new_name;
10242 	    while (*p != 0) {
10243 		int inchars_read, outchars_added;
10244 		inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10245 		p += inchars_read;
10246 		q += outchars_added;
10247 		/* fix-me */
10248 		/* if outchars_added > 1, then this is a wide file specification */
10249 		/* Wide file specifications need to be passed in Perl */
10250 		/* counted strings apparently with a Unicode flag */
10251 	    }
10252 	    *q = 0;
10253 	    dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10254 	}
10255     }
10256 
10257     dd->entry.vms_verscount = 0;
10258     if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10259     Safefree(buff);
10260     return &dd->entry;
10261 
10262 }  /* end of readdir() */
10263 /*}}}*/
10264 
10265 /*
10266  *  Read the next entry from the directory -- thread-safe version.
10267  */
10268 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10269 int
10270 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10271 {
10272     int retval;
10273 
10274     MUTEX_LOCK( (perl_mutex *) dd->mutex );
10275 
10276     entry = readdir(dd);
10277     *result = entry;
10278     retval = ( *result == NULL ? errno : 0 );
10279 
10280     MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10281 
10282     return retval;
10283 
10284 }  /* end of readdir_r() */
10285 /*}}}*/
10286 
10287 /*
10288  *  Return something that can be used in a seekdir later.
10289  */
10290 /*{{{ long telldir(DIR *dd)*/
10291 long
10292 Perl_telldir(DIR *dd)
10293 {
10294     return dd->count;
10295 }
10296 /*}}}*/
10297 
10298 /*
10299  *  Return to a spot where we used to be.  Brute force.
10300  */
10301 /*{{{ void seekdir(DIR *dd,long count)*/
10302 void
10303 Perl_seekdir(pTHX_ DIR *dd, long count)
10304 {
10305     int old_flags;
10306 
10307     /* If we haven't done anything yet... */
10308     if (dd->count == 0)
10309 	return;
10310 
10311     /* Remember some state, and clear it. */
10312     old_flags = dd->flags;
10313     dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10314     _ckvmssts(lib$find_file_end(&dd->context));
10315     dd->context = 0;
10316 
10317     /* The increment is in readdir(). */
10318     for (dd->count = 0; dd->count < count; )
10319 	readdir(dd);
10320 
10321     dd->flags = old_flags;
10322 
10323 }  /* end of seekdir() */
10324 /*}}}*/
10325 
10326 /* VMS subprocess management
10327  *
10328  * my_vfork() - just a vfork(), after setting a flag to record that
10329  * the current script is trying a Unix-style fork/exec.
10330  *
10331  * vms_do_aexec() and vms_do_exec() are called in response to the
10332  * perl 'exec' function.  If this follows a vfork call, then they
10333  * call out the regular perl routines in doio.c which do an
10334  * execvp (for those who really want to try this under VMS).
10335  * Otherwise, they do exactly what the perl docs say exec should
10336  * do - terminate the current script and invoke a new command
10337  * (See below for notes on command syntax.)
10338  *
10339  * do_aspawn() and do_spawn() implement the VMS side of the perl
10340  * 'system' function.
10341  *
10342  * Note on command arguments to perl 'exec' and 'system': When handled
10343  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10344  * are concatenated to form a DCL command string.  If the first non-numeric
10345  * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10346  * the command string is handed off to DCL directly.  Otherwise,
10347  * the first token of the command is taken as the filespec of an image
10348  * to run.  The filespec is expanded using a default type of '.EXE' and
10349  * the process defaults for device, directory, etc., and if found, the resultant
10350  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10351  * the command string as parameters.  This is perhaps a bit complicated,
10352  * but I hope it will form a happy medium between what VMS folks expect
10353  * from lib$spawn and what Unix folks expect from exec.
10354  */
10355 
10356 static int vfork_called;
10357 
10358 /*{{{int my_vfork(void)*/
10359 int
10360 my_vfork(void)
10361 {
10362   vfork_called++;
10363   return vfork();
10364 }
10365 /*}}}*/
10366 
10367 
10368 static void
10369 vms_execfree(struct dsc$descriptor_s *vmscmd)
10370 {
10371   if (vmscmd) {
10372       if (vmscmd->dsc$a_pointer) {
10373           PerlMem_free(vmscmd->dsc$a_pointer);
10374       }
10375       PerlMem_free(vmscmd);
10376   }
10377 }
10378 
10379 static char *
10380 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10381 {
10382   char *junk, *tmps = NULL;
10383   size_t cmdlen = 0;
10384   size_t rlen;
10385   SV **idx;
10386   STRLEN n_a;
10387 
10388   idx = mark;
10389   if (really) {
10390     tmps = SvPV(really,rlen);
10391     if (*tmps) {
10392       cmdlen += rlen + 1;
10393       idx++;
10394     }
10395   }
10396 
10397   for (idx++; idx <= sp; idx++) {
10398     if (*idx) {
10399       junk = SvPVx(*idx,rlen);
10400       cmdlen += rlen ? rlen + 1 : 0;
10401     }
10402   }
10403   Newx(PL_Cmd, cmdlen+1, char);
10404 
10405   if (tmps && *tmps) {
10406     my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10407     mark++;
10408   }
10409   else *PL_Cmd = '\0';
10410   while (++mark <= sp) {
10411     if (*mark) {
10412       char *s = SvPVx(*mark,n_a);
10413       if (!*s) continue;
10414       if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10415       my_strlcat(PL_Cmd, s, cmdlen+1);
10416     }
10417   }
10418   return PL_Cmd;
10419 
10420 }  /* end of setup_argstr() */
10421 
10422 
10423 static unsigned long int
10424 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10425                    struct dsc$descriptor_s **pvmscmd)
10426 {
10427   char * vmsspec;
10428   char * resspec;
10429   char image_name[NAM$C_MAXRSS+1];
10430   char image_argv[NAM$C_MAXRSS+1];
10431   $DESCRIPTOR(defdsc,".EXE");
10432   $DESCRIPTOR(defdsc2,".");
10433   struct dsc$descriptor_s resdsc;
10434   struct dsc$descriptor_s *vmscmd;
10435   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10436   unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10437   char *s, *rest, *cp, *wordbreak;
10438   char * cmd;
10439   int cmdlen;
10440   int isdcl;
10441 
10442   vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10443   if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10444 
10445   /* vmsspec is a DCL command buffer, not just a filename */
10446   vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10447   if (vmsspec == NULL)
10448       _ckvmssts_noperl(SS$_INSFMEM);
10449 
10450   resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10451   if (resspec == NULL)
10452       _ckvmssts_noperl(SS$_INSFMEM);
10453 
10454   /* Make a copy for modification */
10455   cmdlen = strlen(incmd);
10456   cmd = (char *)PerlMem_malloc(cmdlen+1);
10457   if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10458   my_strlcpy(cmd, incmd, cmdlen + 1);
10459   image_name[0] = 0;
10460   image_argv[0] = 0;
10461 
10462   resdsc.dsc$a_pointer = resspec;
10463   resdsc.dsc$b_dtype  = DSC$K_DTYPE_T;
10464   resdsc.dsc$b_class  = DSC$K_CLASS_S;
10465   resdsc.dsc$w_length = VMS_MAXRSS - 1;
10466 
10467   vmscmd->dsc$a_pointer = NULL;
10468   vmscmd->dsc$b_dtype  = DSC$K_DTYPE_T;
10469   vmscmd->dsc$b_class  = DSC$K_CLASS_S;
10470   vmscmd->dsc$w_length = 0;
10471   if (pvmscmd) *pvmscmd = vmscmd;
10472 
10473   if (suggest_quote) *suggest_quote = 0;
10474 
10475   if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10476     PerlMem_free(cmd);
10477     PerlMem_free(vmsspec);
10478     PerlMem_free(resspec);
10479     return CLI$_BUFOVF;                /* continuation lines currently unsupported */
10480   }
10481 
10482   s = cmd;
10483 
10484   while (*s && isspace(*s)) s++;
10485 
10486   if (*s == '@' || *s == '$') {
10487     vmsspec[0] = *s;  rest = s + 1;
10488     for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10489   }
10490   else { cp = vmsspec; rest = s; }
10491 
10492   /* If the first word is quoted, then we need to unquote it and
10493    * escape spaces within it.  We'll expand into the resspec buffer,
10494    * then copy back into the cmd buffer, expanding the latter if
10495    * necessary.
10496    */
10497   if (*rest == '"') {
10498     char *cp2;
10499     char *r = rest;
10500     bool in_quote = 0;
10501     int clen = cmdlen;
10502     int soff = s - cmd;
10503 
10504     for (cp2 = resspec;
10505          *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10506          rest++) {
10507 
10508       if (*rest == ' ') {    /* Escape ' ' to '^_'. */
10509         *cp2 = '^';
10510         *(++cp2) = '_';
10511         cp2++;
10512         clen++;
10513       }
10514       else if (*rest == '"') {
10515         clen--;
10516         if (in_quote) {     /* Must be closing quote. */
10517           rest++;
10518           break;
10519         }
10520         in_quote = 1;
10521       }
10522       else {
10523         *cp2 = *rest;
10524         cp2++;
10525       }
10526     }
10527     *cp2 = '\0';
10528 
10529     /* Expand the command buffer if necessary. */
10530     if (clen > cmdlen) {
10531       cmd = (char *)PerlMem_realloc(cmd, clen);
10532       if (cmd == NULL)
10533         _ckvmssts_noperl(SS$_INSFMEM);
10534       /* Where we are may have changed, so recompute offsets */
10535       r = cmd + (r - s - soff);
10536       rest = cmd + (rest - s - soff);
10537       s = cmd + soff;
10538     }
10539 
10540     /* Shift the non-verb portion of the command (if any) up or
10541      * down as necessary.
10542      */
10543     if (*rest)
10544       memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10545 
10546     /* Copy the unquoted and escaped command verb into place. */
10547     memcpy(r, resspec, cp2 - resspec);
10548     cmd[clen] = '\0';
10549     cmdlen = clen;
10550     rest = r;         /* Rewind for subsequent operations. */
10551   }
10552 
10553   if (*rest == '.' || *rest == '/') {
10554     char *cp2;
10555     for (cp2 = resspec;
10556          *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10557          rest++, cp2++) *cp2 = *rest;
10558     *cp2 = '\0';
10559     if (int_tovmsspec(resspec, cp, 0, NULL)) {
10560       s = vmsspec;
10561 
10562       /* When a UNIX spec with no file type is translated to VMS, */
10563       /* A trailing '.' is appended under ODS-5 rules.            */
10564       /* Here we do not want that trailing "." as it prevents     */
10565       /* Looking for a implied ".exe" type. */
10566       if (decc_efs_charset) {
10567           int i;
10568           i = strlen(vmsspec);
10569           if (vmsspec[i-1] == '.') {
10570               vmsspec[i-1] = '\0';
10571           }
10572       }
10573 
10574       if (*rest) {
10575         for (cp2 = vmsspec + strlen(vmsspec);
10576              *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10577              rest++, cp2++) *cp2 = *rest;
10578         *cp2 = '\0';
10579       }
10580     }
10581   }
10582   /* Intuit whether verb (first word of cmd) is a DCL command:
10583    *   - if first nonspace char is '@', it's a DCL indirection
10584    * otherwise
10585    *   - if verb contains a filespec separator, it's not a DCL command
10586    *   - if it doesn't, caller tells us whether to default to a DCL
10587    *     command, or to a local image unless told it's DCL (by leading '$')
10588    */
10589   if (*s == '@') {
10590       isdcl = 1;
10591       if (suggest_quote) *suggest_quote = 1;
10592   } else {
10593     char *filespec = strpbrk(s,":<[.;");
10594     rest = wordbreak = strpbrk(s," \"\t/");
10595     if (!wordbreak) wordbreak = s + strlen(s);
10596     if (*s == '$') check_img = 0;
10597     if (filespec && (filespec < wordbreak)) isdcl = 0;
10598     else isdcl = !check_img;
10599   }
10600 
10601   if (!isdcl) {
10602     int rsts;
10603     imgdsc.dsc$a_pointer = s;
10604     imgdsc.dsc$w_length = wordbreak - s;
10605     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10606     if (!(retsts&1)) {
10607         _ckvmssts_noperl(lib$find_file_end(&cxt));
10608         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10609       if (!(retsts & 1) && *s == '$') {
10610         _ckvmssts_noperl(lib$find_file_end(&cxt));
10611 	imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10612 	retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10613 	if (!(retsts&1)) {
10614 	  _ckvmssts_noperl(lib$find_file_end(&cxt));
10615           retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10616         }
10617       }
10618     }
10619     _ckvmssts_noperl(lib$find_file_end(&cxt));
10620 
10621     if (retsts & 1) {
10622       FILE *fp;
10623       s = resspec;
10624       while (*s && !isspace(*s)) s++;
10625       *s = '\0';
10626 
10627       /* check that it's really not DCL with no file extension */
10628       fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10629       if (fp) {
10630         char b[256] = {0,0,0,0};
10631         read(fileno(fp), b, 256);
10632         isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10633 	if (isdcl) {
10634 	  int shebang_len;
10635 
10636 	  /* Check for script */
10637 	  shebang_len = 0;
10638 	  if ((b[0] == '#') && (b[1] == '!'))
10639 	     shebang_len = 2;
10640 #ifdef ALTERNATE_SHEBANG
10641 	  else {
10642 	    shebang_len = strlen(ALTERNATE_SHEBANG);
10643 	    if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10644 	      char * perlstr;
10645 		perlstr = strstr("perl",b);
10646 		if (perlstr == NULL)
10647 		  shebang_len = 0;
10648 	    }
10649 	    else
10650 	      shebang_len = 0;
10651 	  }
10652 #endif
10653 
10654 	  if (shebang_len > 0) {
10655 	  int i;
10656 	  int j;
10657 	  char tmpspec[NAM$C_MAXRSS + 1];
10658 
10659 	    i = shebang_len;
10660 	     /* Image is following after white space */
10661 	    /*--------------------------------------*/
10662 	    while (isprint(b[i]) && isspace(b[i]))
10663 		i++;
10664 
10665 	    j = 0;
10666 	    while (isprint(b[i]) && !isspace(b[i])) {
10667 		tmpspec[j++] = b[i++];
10668 		if (j >= NAM$C_MAXRSS)
10669 		   break;
10670 	    }
10671 	    tmpspec[j] = '\0';
10672 
10673 	     /* There may be some default parameters to the image */
10674 	    /*---------------------------------------------------*/
10675 	    j = 0;
10676 	    while (isprint(b[i])) {
10677 		image_argv[j++] = b[i++];
10678 		if (j >= NAM$C_MAXRSS)
10679 		   break;
10680 	    }
10681 	    while ((j > 0) && !isprint(image_argv[j-1]))
10682 		j--;
10683 	    image_argv[j] = 0;
10684 
10685 	    /* It will need to be converted to VMS format and validated */
10686 	    if (tmpspec[0] != '\0') {
10687 	      char * iname;
10688 
10689 	       /* Try to find the exact program requested to be run */
10690 	      /*---------------------------------------------------*/
10691 	      iname = int_rmsexpand
10692 		 (tmpspec, image_name, ".exe",
10693 		  PERL_RMSEXPAND_M_VMS, NULL, NULL);
10694 	      if (iname != NULL) {
10695 		if (cando_by_name_int
10696 			(S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10697 		  /* MCR prefix needed */
10698 		  isdcl = 0;
10699 		}
10700 		else {
10701 		   /* Try again with a null type */
10702 		  /*----------------------------*/
10703 		  iname = int_rmsexpand
10704 		    (tmpspec, image_name, ".",
10705 		     PERL_RMSEXPAND_M_VMS, NULL, NULL);
10706 		  if (iname != NULL) {
10707 		    if (cando_by_name_int
10708 			 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10709 		      /* MCR prefix needed */
10710 		      isdcl = 0;
10711 		    }
10712 		  }
10713 		}
10714 
10715 		 /* Did we find the image to run the script? */
10716 		/*------------------------------------------*/
10717 		if (isdcl) {
10718 		  char *tchr;
10719 
10720 		   /* Assume DCL or foreign command exists */
10721 		  /*--------------------------------------*/
10722 		  tchr = strrchr(tmpspec, '/');
10723 		  if (tchr != NULL) {
10724 		    tchr++;
10725 		  }
10726 		  else {
10727 		    tchr = tmpspec;
10728 		  }
10729 		  my_strlcpy(image_name, tchr, sizeof(image_name));
10730 		}
10731 	      }
10732 	    }
10733 	  }
10734 	}
10735         fclose(fp);
10736       }
10737       if (check_img && isdcl) {
10738           PerlMem_free(cmd);
10739           PerlMem_free(resspec);
10740           PerlMem_free(vmsspec);
10741           return RMS$_FNF;
10742       }
10743 
10744       if (cando_by_name(S_IXUSR,0,resspec)) {
10745         vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10746 	if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10747         if (!isdcl) {
10748             my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10749 	    if (image_name[0] != 0) {
10750 		my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10751 		my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10752 	    }
10753 	} else if (image_name[0] != 0) {
10754 	    my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10755 	    my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10756         } else {
10757             my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10758         }
10759         if (suggest_quote) *suggest_quote = 1;
10760 
10761 	/* If there is an image name, use original command */
10762 	if (image_name[0] == 0)
10763 	    my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10764 	else {
10765 	    rest = cmd;
10766 	    while (*rest && isspace(*rest)) rest++;
10767 	}
10768 
10769 	if (image_argv[0] != 0) {
10770 	  my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10771 	  my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10772 	}
10773         if (rest) {
10774 	   int rest_len;
10775 	   int vmscmd_len;
10776 
10777 	   rest_len = strlen(rest);
10778 	   vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10779 	   if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10780 	      my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10781 	   else
10782 	     retsts = CLI$_BUFOVF;
10783 	}
10784         vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10785         PerlMem_free(cmd);
10786         PerlMem_free(vmsspec);
10787         PerlMem_free(resspec);
10788         return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10789       }
10790       else
10791 	retsts = RMS$_PRV;
10792     }
10793   }
10794   /* It's either a DCL command or we couldn't find a suitable image */
10795   vmscmd->dsc$w_length = strlen(cmd);
10796 
10797   vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10798   my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10799 
10800   PerlMem_free(cmd);
10801   PerlMem_free(resspec);
10802   PerlMem_free(vmsspec);
10803 
10804   /* check if it's a symbol (for quoting purposes) */
10805   if (suggest_quote && !*suggest_quote) {
10806     int iss;
10807     char equiv[LNM$C_NAMLENGTH];
10808     struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10809     eqvdsc.dsc$a_pointer = equiv;
10810 
10811     iss = lib$get_symbol(vmscmd,&eqvdsc);
10812     if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10813   }
10814   if (!(retsts & 1)) {
10815     /* just hand off status values likely to be due to user error */
10816     if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10817         retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10818        (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10819     else { _ckvmssts_noperl(retsts); }
10820   }
10821 
10822   return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10823 
10824 }  /* end of setup_cmddsc() */
10825 
10826 
10827 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10828 bool
10829 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10830 {
10831 bool exec_sts;
10832 char * cmd;
10833 
10834   if (sp > mark) {
10835     if (vfork_called) {           /* this follows a vfork - act Unixish */
10836       vfork_called--;
10837       if (vfork_called < 0) {
10838         Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10839         vfork_called = 0;
10840       }
10841       else return do_aexec(really,mark,sp);
10842     }
10843                                            /* no vfork - act VMSish */
10844     cmd = setup_argstr(aTHX_ really,mark,sp);
10845     exec_sts = vms_do_exec(cmd);
10846     Safefree(cmd);  /* Clean up from setup_argstr() */
10847     return exec_sts;
10848   }
10849 
10850   return FALSE;
10851 }  /* end of vms_do_aexec() */
10852 /*}}}*/
10853 
10854 /* {{{bool vms_do_exec(char *cmd) */
10855 bool
10856 Perl_vms_do_exec(pTHX_ const char *cmd)
10857 {
10858   struct dsc$descriptor_s *vmscmd;
10859 
10860   if (vfork_called) {             /* this follows a vfork - act Unixish */
10861     vfork_called--;
10862     if (vfork_called < 0) {
10863       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10864       vfork_called = 0;
10865     }
10866     else return do_exec(cmd);
10867   }
10868 
10869   {                               /* no vfork - act VMSish */
10870     unsigned long int retsts;
10871 
10872     TAINT_ENV();
10873     TAINT_PROPER("exec");
10874     if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10875       retsts = lib$do_command(vmscmd);
10876 
10877     switch (retsts) {
10878       case RMS$_FNF: case RMS$_DNF:
10879         set_errno(ENOENT); break;
10880       case RMS$_DIR:
10881         set_errno(ENOTDIR); break;
10882       case RMS$_DEV:
10883         set_errno(ENODEV); break;
10884       case RMS$_PRV:
10885         set_errno(EACCES); break;
10886       case RMS$_SYN:
10887         set_errno(EINVAL); break;
10888       case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10889         set_errno(E2BIG); break;
10890       case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10891         _ckvmssts_noperl(retsts); /* fall through */
10892       default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10893         set_errno(EVMSERR);
10894     }
10895     set_vaxc_errno(retsts);
10896     if (ckWARN(WARN_EXEC)) {
10897       Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10898              vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10899     }
10900     vms_execfree(vmscmd);
10901   }
10902 
10903   return FALSE;
10904 
10905 }  /* end of vms_do_exec() */
10906 /*}}}*/
10907 
10908 int do_spawn2(pTHX_ const char *, int);
10909 
10910 int
10911 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10912 {
10913 unsigned long int sts;
10914 char * cmd;
10915 int flags = 0;
10916 
10917   if (sp > mark) {
10918 
10919     /* We'll copy the (undocumented?) Win32 behavior and allow a
10920      * numeric first argument.  But the only value we'll support
10921      * through do_aspawn is a value of 1, which means spawn without
10922      * waiting for completion -- other values are ignored.
10923      */
10924     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10925 	++mark;
10926 	flags = SvIVx(*mark);
10927     }
10928 
10929     if (flags && flags == 1)     /* the Win32 P_NOWAIT value */
10930         flags = CLI$M_NOWAIT;
10931     else
10932         flags = 0;
10933 
10934     cmd = setup_argstr(aTHX_ really, mark, sp);
10935     sts = do_spawn2(aTHX_ cmd, flags);
10936     /* pp_sys will clean up cmd */
10937     return sts;
10938   }
10939   return SS$_ABORT;
10940 }  /* end of do_aspawn() */
10941 /*}}}*/
10942 
10943 
10944 /* {{{int do_spawn(char* cmd) */
10945 int
10946 Perl_do_spawn(pTHX_ char* cmd)
10947 {
10948     PERL_ARGS_ASSERT_DO_SPAWN;
10949 
10950     return do_spawn2(aTHX_ cmd, 0);
10951 }
10952 /*}}}*/
10953 
10954 /* {{{int do_spawn_nowait(char* cmd) */
10955 int
10956 Perl_do_spawn_nowait(pTHX_ char* cmd)
10957 {
10958     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10959 
10960     return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10961 }
10962 /*}}}*/
10963 
10964 /* {{{int do_spawn2(char *cmd) */
10965 int
10966 do_spawn2(pTHX_ const char *cmd, int flags)
10967 {
10968   unsigned long int sts, substs;
10969 
10970   /* The caller of this routine expects to Safefree(PL_Cmd) */
10971   Newx(PL_Cmd,10,char);
10972 
10973   TAINT_ENV();
10974   TAINT_PROPER("spawn");
10975   if (!cmd || !*cmd) {
10976     sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10977     if (!(sts & 1)) {
10978       switch (sts) {
10979         case RMS$_FNF:  case RMS$_DNF:
10980           set_errno(ENOENT); break;
10981         case RMS$_DIR:
10982           set_errno(ENOTDIR); break;
10983         case RMS$_DEV:
10984           set_errno(ENODEV); break;
10985         case RMS$_PRV:
10986           set_errno(EACCES); break;
10987         case RMS$_SYN:
10988           set_errno(EINVAL); break;
10989         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10990           set_errno(E2BIG); break;
10991         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10992           _ckvmssts_noperl(sts); /* fall through */
10993         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10994           set_errno(EVMSERR);
10995       }
10996       set_vaxc_errno(sts);
10997       if (ckWARN(WARN_EXEC)) {
10998         Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10999 		    Strerror(errno));
11000       }
11001     }
11002     sts = substs;
11003   }
11004   else {
11005     char mode[3];
11006     PerlIO * fp;
11007     if (flags & CLI$M_NOWAIT)
11008         strcpy(mode, "n");
11009     else
11010         strcpy(mode, "nW");
11011 
11012     fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11013     if (fp != NULL)
11014       my_pclose(fp);
11015     /* sts will be the pid in the nowait case */
11016   }
11017   return sts;
11018 }  /* end of do_spawn2() */
11019 /*}}}*/
11020 
11021 
11022 static unsigned int *sockflags, sockflagsize;
11023 
11024 /*
11025  * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11026  * routines found in some versions of the CRTL can't deal with sockets.
11027  * We don't shim the other file open routines since a socket isn't
11028  * likely to be opened by a name.
11029  */
11030 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11031 FILE *my_fdopen(int fd, const char *mode)
11032 {
11033   FILE *fp = fdopen(fd, mode);
11034 
11035   if (fp) {
11036     unsigned int fdoff = fd / sizeof(unsigned int);
11037     Stat_t sbuf; /* native stat; we don't need flex_stat */
11038     if (!sockflagsize || fdoff > sockflagsize) {
11039       if (sockflags) Renew(     sockflags,fdoff+2,unsigned int);
11040       else           Newx  (sockflags,fdoff+2,unsigned int);
11041       memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11042       sockflagsize = fdoff + 2;
11043     }
11044     if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11045       sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11046   }
11047   return fp;
11048 
11049 }
11050 /*}}}*/
11051 
11052 
11053 /*
11054  * Clear the corresponding bit when the (possibly) socket stream is closed.
11055  * There still a small hole: we miss an implicit close which might occur
11056  * via freopen().  >> Todo
11057  */
11058 /*{{{ int my_fclose(FILE *fp)*/
11059 int my_fclose(FILE *fp) {
11060   if (fp) {
11061     unsigned int fd = fileno(fp);
11062     unsigned int fdoff = fd / sizeof(unsigned int);
11063 
11064     if (sockflagsize && fdoff < sockflagsize)
11065       sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11066   }
11067   return fclose(fp);
11068 }
11069 /*}}}*/
11070 
11071 
11072 /*
11073  * A simple fwrite replacement which outputs itmsz*nitm chars without
11074  * introducing record boundaries every itmsz chars.
11075  * We are using fputs, which depends on a terminating null.  We may
11076  * well be writing binary data, so we need to accommodate not only
11077  * data with nulls sprinkled in the middle but also data with no null
11078  * byte at the end.
11079  */
11080 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11081 int
11082 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11083 {
11084   char *cp, *end, *cpd;
11085   char *data;
11086   unsigned int fd = fileno(dest);
11087   unsigned int fdoff = fd / sizeof(unsigned int);
11088   int retval;
11089   int bufsize = itmsz * nitm + 1;
11090 
11091   if (fdoff < sockflagsize &&
11092       (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11093     if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11094     return nitm;
11095   }
11096 
11097   _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11098   memcpy( data, src, itmsz*nitm );
11099   data[itmsz*nitm] = '\0';
11100 
11101   end = data + itmsz * nitm;
11102   retval = (int) nitm; /* on success return # items written */
11103 
11104   cpd = data;
11105   while (cpd <= end) {
11106     for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11107     if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11108     if (cp < end)
11109       if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11110     cpd = cp + 1;
11111   }
11112 
11113   if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11114   return retval;
11115 
11116 }  /* end of my_fwrite() */
11117 /*}}}*/
11118 
11119 /*{{{ int my_flush(FILE *fp)*/
11120 int
11121 Perl_my_flush(pTHX_ FILE *fp)
11122 {
11123     int res;
11124     if ((res = fflush(fp)) == 0 && fp) {
11125 #ifdef VMS_DO_SOCKETS
11126 	Stat_t s;
11127 	if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11128 #endif
11129 	    res = fsync(fileno(fp));
11130     }
11131 /*
11132  * If the flush succeeded but set end-of-file, we need to clear
11133  * the error because our caller may check ferror().  BTW, this
11134  * probably means we just flushed an empty file.
11135  */
11136     if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11137 
11138     return res;
11139 }
11140 /*}}}*/
11141 
11142 /* fgetname() is not returning the correct file specifications when
11143  * decc_filename_unix_report mode is active.  So we have to have it
11144  * aways return filenames in VMS mode and convert it ourselves.
11145  */
11146 
11147 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11148 char *
11149 Perl_my_fgetname(FILE *fp, char * buf) {
11150     char * retname;
11151     char * vms_name;
11152 
11153     retname = fgetname(fp, buf, 1);
11154 
11155     /* If we are in VMS mode, then we are done */
11156     if (!decc_filename_unix_report || (retname == NULL)) {
11157        return retname;
11158     }
11159 
11160     /* Convert this to Unix format */
11161     vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11162     my_strlcpy(vms_name, retname, VMS_MAXRSS);
11163     retname = int_tounixspec(vms_name, buf, NULL);
11164     PerlMem_free(vms_name);
11165 
11166     return retname;
11167 }
11168 /*}}}*/
11169 
11170 /*
11171  * Here are replacements for the following Unix routines in the VMS environment:
11172  *      getpwuid    Get information for a particular UIC or UID
11173  *      getpwnam    Get information for a named user
11174  *      getpwent    Get information for each user in the rights database
11175  *      setpwent    Reset search to the start of the rights database
11176  *      endpwent    Finish searching for users in the rights database
11177  *
11178  * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11179  * (defined in pwd.h), which contains the following fields:-
11180  *      struct passwd {
11181  *              char        *pw_name;    Username (in lower case)
11182  *              char        *pw_passwd;  Hashed password
11183  *              unsigned int pw_uid;     UIC
11184  *              unsigned int pw_gid;     UIC group  number
11185  *              char        *pw_unixdir; Default device/directory (VMS-style)
11186  *              char        *pw_gecos;   Owner name
11187  *              char        *pw_dir;     Default device/directory (Unix-style)
11188  *              char        *pw_shell;   Default CLI name (eg. DCL)
11189  *      };
11190  * If the specified user does not exist, getpwuid and getpwnam return NULL.
11191  *
11192  * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11193  * not the UIC member number (eg. what's returned by getuid()),
11194  * getpwuid() can accept either as input (if uid is specified, the caller's
11195  * UIC group is used), though it won't recognise gid=0.
11196  *
11197  * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11198  * information about other users in your group or in other groups, respectively.
11199  * If the required privilege is not available, then these routines fill only
11200  * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11201  * string).
11202  *
11203  * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11204  */
11205 
11206 /* sizes of various UAF record fields */
11207 #define UAI$S_USERNAME 12
11208 #define UAI$S_IDENT    31
11209 #define UAI$S_OWNER    31
11210 #define UAI$S_DEFDEV   31
11211 #define UAI$S_DEFDIR   63
11212 #define UAI$S_DEFCLI   31
11213 #define UAI$S_PWD       8
11214 
11215 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT  && \
11216                         (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11217                         (uic).uic$v_group  != UIC$K_WILD_GROUP)
11218 
11219 static char __empty[]= "";
11220 static struct passwd __passwd_empty=
11221     {(char *) __empty, (char *) __empty, 0, 0,
11222      (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11223 static int contxt= 0;
11224 static struct passwd __pwdcache;
11225 static char __pw_namecache[UAI$S_IDENT+1];
11226 
11227 /*
11228  * This routine does most of the work extracting the user information.
11229  */
11230 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11231 {
11232     static struct {
11233         unsigned char length;
11234         char pw_gecos[UAI$S_OWNER+1];
11235     } owner;
11236     static union uicdef uic;
11237     static struct {
11238         unsigned char length;
11239         char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11240     } defdev;
11241     static struct {
11242         unsigned char length;
11243         char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11244     } defdir;
11245     static struct {
11246         unsigned char length;
11247         char pw_shell[UAI$S_DEFCLI+1];
11248     } defcli;
11249     static char pw_passwd[UAI$S_PWD+1];
11250 
11251     static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11252     struct dsc$descriptor_s name_desc;
11253     unsigned long int sts;
11254 
11255     static struct itmlst_3 itmlst[]= {
11256         {UAI$S_OWNER+1,    UAI$_OWNER,  &owner,    &lowner},
11257         {sizeof(uic),      UAI$_UIC,    &uic,      &luic},
11258         {UAI$S_DEFDEV+1,   UAI$_DEFDEV, &defdev,   &ldefdev},
11259         {UAI$S_DEFDIR+1,   UAI$_DEFDIR, &defdir,   &ldefdir},
11260         {UAI$S_DEFCLI+1,   UAI$_DEFCLI, &defcli,   &ldefcli},
11261         {UAI$S_PWD,        UAI$_PWD,    pw_passwd, &lpwd},
11262         {0,                0,           NULL,    NULL}};
11263 
11264     name_desc.dsc$w_length=  strlen(name);
11265     name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11266     name_desc.dsc$b_class=   DSC$K_CLASS_S;
11267     name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11268 
11269 /*  Note that sys$getuai returns many fields as counted strings. */
11270     sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11271     if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11272       set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11273     }
11274     else { _ckvmssts(sts); }
11275     if (!(sts & 1)) return 0;  /* out here in case _ckvmssts() doesn't abort */
11276 
11277     if ((int) owner.length  < lowner)  lowner=  (int) owner.length;
11278     if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11279     if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11280     if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11281     memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11282     owner.pw_gecos[lowner]=            '\0';
11283     defdev.pw_dir[ldefdev+ldefdir]= '\0';
11284     defcli.pw_shell[ldefcli]=          '\0';
11285     if (valid_uic(uic)) {
11286         pwd->pw_uid= uic.uic$l_uic;
11287         pwd->pw_gid= uic.uic$v_group;
11288     }
11289     else
11290       Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11291     pwd->pw_passwd=  pw_passwd;
11292     pwd->pw_gecos=   owner.pw_gecos;
11293     pwd->pw_dir=     defdev.pw_dir;
11294     pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11295     pwd->pw_shell=   defcli.pw_shell;
11296     if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11297         int ldir;
11298         ldir= strlen(pwd->pw_unixdir) - 1;
11299         if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11300     }
11301     else
11302         my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11303     if (!decc_efs_case_preserve)
11304         __mystrtolower(pwd->pw_unixdir);
11305     return 1;
11306 }
11307 
11308 /*
11309  * Get information for a named user.
11310 */
11311 /*{{{struct passwd *getpwnam(char *name)*/
11312 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11313 {
11314     struct dsc$descriptor_s name_desc;
11315     union uicdef uic;
11316     unsigned long int sts;
11317 
11318     __pwdcache = __passwd_empty;
11319     if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11320       /* We still may be able to determine pw_uid and pw_gid */
11321       name_desc.dsc$w_length=  strlen(name);
11322       name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11323       name_desc.dsc$b_class=   DSC$K_CLASS_S;
11324       name_desc.dsc$a_pointer= (char *) name;
11325       if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11326         __pwdcache.pw_uid= uic.uic$l_uic;
11327         __pwdcache.pw_gid= uic.uic$v_group;
11328       }
11329       else {
11330         if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11331           set_vaxc_errno(sts);
11332           set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11333           return NULL;
11334         }
11335         else { _ckvmssts(sts); }
11336       }
11337     }
11338     my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11339     __pwdcache.pw_name= __pw_namecache;
11340     return &__pwdcache;
11341 }  /* end of my_getpwnam() */
11342 /*}}}*/
11343 
11344 /*
11345  * Get information for a particular UIC or UID.
11346  * Called by my_getpwent with uid=-1 to list all users.
11347 */
11348 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11349 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11350 {
11351     const $DESCRIPTOR(name_desc,__pw_namecache);
11352     unsigned short lname;
11353     union uicdef uic;
11354     unsigned long int status;
11355 
11356     if (uid == (unsigned int) -1) {
11357       do {
11358         status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11359         if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11360           set_vaxc_errno(status);
11361           set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11362           my_endpwent();
11363           return NULL;
11364         }
11365         else { _ckvmssts(status); }
11366       } while (!valid_uic (uic));
11367     }
11368     else {
11369       uic.uic$l_uic= uid;
11370       if (!uic.uic$v_group)
11371         uic.uic$v_group= PerlProc_getgid();
11372       if (valid_uic(uic))
11373         status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11374       else status = SS$_IVIDENT;
11375       if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11376           status == RMS$_PRV) {
11377         set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11378         return NULL;
11379       }
11380       else { _ckvmssts(status); }
11381     }
11382     __pw_namecache[lname]= '\0';
11383     __mystrtolower(__pw_namecache);
11384 
11385     __pwdcache = __passwd_empty;
11386     __pwdcache.pw_name = __pw_namecache;
11387 
11388 /*  Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11389     The identifier's value is usually the UIC, but it doesn't have to be,
11390     so if we can, we let fillpasswd update this. */
11391     __pwdcache.pw_uid =  uic.uic$l_uic;
11392     __pwdcache.pw_gid =  uic.uic$v_group;
11393 
11394     fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11395     return &__pwdcache;
11396 
11397 }  /* end of my_getpwuid() */
11398 /*}}}*/
11399 
11400 /*
11401  * Get information for next user.
11402 */
11403 /*{{{struct passwd *my_getpwent()*/
11404 struct passwd *Perl_my_getpwent(pTHX)
11405 {
11406     return (my_getpwuid((unsigned int) -1));
11407 }
11408 /*}}}*/
11409 
11410 /*
11411  * Finish searching rights database for users.
11412 */
11413 /*{{{void my_endpwent()*/
11414 void Perl_my_endpwent(pTHX)
11415 {
11416     if (contxt) {
11417       _ckvmssts(sys$finish_rdb(&contxt));
11418       contxt= 0;
11419     }
11420 }
11421 /*}}}*/
11422 
11423 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11424  * my_utime(), and flex_stat(), all of which operate on UTC unless
11425  * VMSISH_TIMES is true.
11426  */
11427 /* method used to handle UTC conversions:
11428  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
11429  */
11430 static int gmtime_emulation_type;
11431 /* number of secs to add to UTC POSIX-style time to get local time */
11432 static long int utc_offset_secs;
11433 
11434 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11435  * in vmsish.h.  #undef them here so we can call the CRTL routines
11436  * directly.
11437  */
11438 #undef gmtime
11439 #undef localtime
11440 #undef time
11441 
11442 
11443 static time_t toutc_dst(time_t loc) {
11444   struct tm *rsltmp;
11445 
11446   if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11447   loc -= utc_offset_secs;
11448   if (rsltmp->tm_isdst) loc -= 3600;
11449   return loc;
11450 }
11451 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11452        ((gmtime_emulation_type || my_time(NULL)), \
11453        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11454        ((secs) - utc_offset_secs))))
11455 
11456 static time_t toloc_dst(time_t utc) {
11457   struct tm *rsltmp;
11458 
11459   utc += utc_offset_secs;
11460   if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11461   if (rsltmp->tm_isdst) utc += 3600;
11462   return utc;
11463 }
11464 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11465        ((gmtime_emulation_type || my_time(NULL)), \
11466        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11467        ((secs) + utc_offset_secs))))
11468 
11469 /* my_time(), my_localtime(), my_gmtime()
11470  * By default traffic in UTC time values, using CRTL gmtime() or
11471  * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11472  * Note: We need to use these functions even when the CRTL has working
11473  * UTC support, since they also handle C<use vmsish qw(times);>
11474  *
11475  * Contributed by Chuck Lane  <lane@duphy4.physics.drexel.edu>
11476  * Modified by Charles Bailey <bailey@newman.upenn.edu>
11477  */
11478 
11479 /*{{{time_t my_time(time_t *timep)*/
11480 time_t Perl_my_time(pTHX_ time_t *timep)
11481 {
11482   time_t when;
11483   struct tm *tm_p;
11484 
11485   if (gmtime_emulation_type == 0) {
11486     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
11487                               /* results of calls to gmtime() and localtime() */
11488                               /* for same &base */
11489 
11490     gmtime_emulation_type++;
11491     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11492       char off[LNM$C_NAMLENGTH+1];;
11493 
11494       gmtime_emulation_type++;
11495       if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11496         gmtime_emulation_type++;
11497         utc_offset_secs = 0;
11498         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11499       }
11500       else { utc_offset_secs = atol(off); }
11501     }
11502     else { /* We've got a working gmtime() */
11503       struct tm gmt, local;
11504 
11505       gmt = *tm_p;
11506       tm_p = localtime(&base);
11507       local = *tm_p;
11508       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
11509       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11510       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
11511       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
11512     }
11513   }
11514 
11515   when = time(NULL);
11516 # ifdef VMSISH_TIME
11517   if (VMSISH_TIME) when = _toloc(when);
11518 # endif
11519   if (timep != NULL) *timep = when;
11520   return when;
11521 
11522 }  /* end of my_time() */
11523 /*}}}*/
11524 
11525 
11526 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11527 struct tm *
11528 Perl_my_gmtime(pTHX_ const time_t *timep)
11529 {
11530   time_t when;
11531   struct tm *rsltmp;
11532 
11533   if (timep == NULL) {
11534     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11535     return NULL;
11536   }
11537   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
11538 
11539   when = *timep;
11540 # ifdef VMSISH_TIME
11541   if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11542 #  endif
11543   return gmtime(&when);
11544 }  /* end of my_gmtime() */
11545 /*}}}*/
11546 
11547 
11548 /*{{{struct tm *my_localtime(const time_t *timep)*/
11549 struct tm *
11550 Perl_my_localtime(pTHX_ const time_t *timep)
11551 {
11552   time_t when;
11553 
11554   if (timep == NULL) {
11555     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11556     return NULL;
11557   }
11558   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
11559   if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11560 
11561   when = *timep;
11562 # ifdef VMSISH_TIME
11563   if (VMSISH_TIME) when = _toutc(when);
11564 # endif
11565   /* CRTL localtime() wants UTC as input, does tz correction itself */
11566   return localtime(&when);
11567 } /*  end of my_localtime() */
11568 /*}}}*/
11569 
11570 /* Reset definitions for later calls */
11571 #define gmtime(t)    my_gmtime(t)
11572 #define localtime(t) my_localtime(t)
11573 #define time(t)      my_time(t)
11574 
11575 
11576 /* my_utime - update modification/access time of a file
11577  *
11578  * VMS 7.3 and later implementation
11579  * Only the UTC translation is home-grown. The rest is handled by the
11580  * CRTL utime(), which will take into account the relevant feature
11581  * logicals and ODS-5 volume characteristics for true access times.
11582  *
11583  * pre VMS 7.3 implementation:
11584  * The calling sequence is identical to POSIX utime(), but under
11585  * VMS with ODS-2, only the modification time is changed; ODS-2 does
11586  * not maintain access times.  Restrictions differ from the POSIX
11587  * definition in that the time can be changed as long as the
11588  * caller has permission to execute the necessary IO$_MODIFY $QIO;
11589  * no separate checks are made to insure that the caller is the
11590  * owner of the file or has special privs enabled.
11591  * Code here is based on Joe Meadows' FILE utility.
11592  *
11593  */
11594 
11595 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11596  *              to VMS epoch  (01-JAN-1858 00:00:00.00)
11597  * in 100 ns intervals.
11598  */
11599 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11600 
11601 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11602 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11603 {
11604 #if __CRTL_VER >= 70300000
11605   struct utimbuf utc_utimes, *utc_utimesp;
11606 
11607   if (utimes != NULL) {
11608     utc_utimes.actime = utimes->actime;
11609     utc_utimes.modtime = utimes->modtime;
11610 # ifdef VMSISH_TIME
11611     /* If input was local; convert to UTC for sys svc */
11612     if (VMSISH_TIME) {
11613       utc_utimes.actime = _toutc(utimes->actime);
11614       utc_utimes.modtime = _toutc(utimes->modtime);
11615     }
11616 # endif
11617     utc_utimesp = &utc_utimes;
11618   }
11619   else {
11620     utc_utimesp = NULL;
11621   }
11622 
11623   return utime(file, utc_utimesp);
11624 
11625 #else /* __CRTL_VER < 70300000 */
11626 
11627   int i;
11628   int sts;
11629   long int bintime[2], len = 2, lowbit, unixtime,
11630            secscale = 10000000; /* seconds --> 100 ns intervals */
11631   unsigned long int chan, iosb[2], retsts;
11632   char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11633   struct FAB myfab = cc$rms_fab;
11634   struct NAM mynam = cc$rms_nam;
11635 #if defined (__DECC) && defined (__VAX)
11636   /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11637    * at least through VMS V6.1, which causes a type-conversion warning.
11638    */
11639 #  pragma message save
11640 #  pragma message disable cvtdiftypes
11641 #endif
11642   struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11643   struct fibdef myfib;
11644 #if defined (__DECC) && defined (__VAX)
11645   /* This should be right after the declaration of myatr, but due
11646    * to a bug in VAX DEC C, this takes effect a statement early.
11647    */
11648 #  pragma message restore
11649 #endif
11650   /* cast ok for read only parameter */
11651   struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11652                         devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11653                         fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11654 
11655   if (file == NULL || *file == '\0') {
11656     SETERRNO(ENOENT, LIB$_INVARG);
11657     return -1;
11658   }
11659 
11660   /* Convert to VMS format ensuring that it will fit in 255 characters */
11661   if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11662       SETERRNO(ENOENT, LIB$_INVARG);
11663       return -1;
11664   }
11665   if (utimes != NULL) {
11666     /* Convert Unix time    (seconds since 01-JAN-1970 00:00:00.00)
11667      * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11668      * Since time_t is unsigned long int, and lib$emul takes a signed long int
11669      * as input, we force the sign bit to be clear by shifting unixtime right
11670      * one bit, then multiplying by an extra factor of 2 in lib$emul().
11671      */
11672     lowbit = (utimes->modtime & 1) ? secscale : 0;
11673     unixtime = (long int) utimes->modtime;
11674 #   ifdef VMSISH_TIME
11675     /* If input was UTC; convert to local for sys svc */
11676     if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11677 #   endif
11678     unixtime >>= 1;  secscale <<= 1;
11679     retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11680     if (!(retsts & 1)) {
11681       SETERRNO(EVMSERR, retsts);
11682       return -1;
11683     }
11684     retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11685     if (!(retsts & 1)) {
11686       SETERRNO(EVMSERR, retsts);
11687       return -1;
11688     }
11689   }
11690   else {
11691     /* Just get the current time in VMS format directly */
11692     retsts = sys$gettim(bintime);
11693     if (!(retsts & 1)) {
11694       SETERRNO(EVMSERR, retsts);
11695       return -1;
11696     }
11697   }
11698 
11699   myfab.fab$l_fna = vmsspec;
11700   myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11701   myfab.fab$l_nam = &mynam;
11702   mynam.nam$l_esa = esa;
11703   mynam.nam$b_ess = (unsigned char) sizeof esa;
11704   mynam.nam$l_rsa = rsa;
11705   mynam.nam$b_rss = (unsigned char) sizeof rsa;
11706   if (decc_efs_case_preserve)
11707       mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11708 
11709   /* Look for the file to be affected, letting RMS parse the file
11710    * specification for us as well.  I have set errno using only
11711    * values documented in the utime() man page for VMS POSIX.
11712    */
11713   retsts = sys$parse(&myfab,0,0);
11714   if (!(retsts & 1)) {
11715     set_vaxc_errno(retsts);
11716     if      (retsts == RMS$_PRV) set_errno(EACCES);
11717     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11718     else                         set_errno(EVMSERR);
11719     return -1;
11720   }
11721   retsts = sys$search(&myfab,0,0);
11722   if (!(retsts & 1)) {
11723     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
11724     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
11725     set_vaxc_errno(retsts);
11726     if      (retsts == RMS$_PRV) set_errno(EACCES);
11727     else if (retsts == RMS$_FNF) set_errno(ENOENT);
11728     else                         set_errno(EVMSERR);
11729     return -1;
11730   }
11731 
11732   devdsc.dsc$w_length = mynam.nam$b_dev;
11733   /* cast ok for read only parameter */
11734   devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11735 
11736   retsts = sys$assign(&devdsc,&chan,0,0);
11737   if (!(retsts & 1)) {
11738     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
11739     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
11740     set_vaxc_errno(retsts);
11741     if      (retsts == SS$_IVDEVNAM)   set_errno(ENOTDIR);
11742     else if (retsts == SS$_NOPRIV)     set_errno(EACCES);
11743     else if (retsts == SS$_NOSUCHDEV)  set_errno(ENOTDIR);
11744     else                               set_errno(EVMSERR);
11745     return -1;
11746   }
11747 
11748   fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11749   fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11750 
11751   memset((void *) &myfib, 0, sizeof myfib);
11752 #if defined(__DECC) || defined(__DECCXX)
11753   for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11754   for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11755   /* This prevents the revision time of the file being reset to the current
11756    * time as a result of our IO$_MODIFY $QIO. */
11757   myfib.fib$l_acctl = FIB$M_NORECORD;
11758 #else
11759   for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11760   for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11761   myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11762 #endif
11763   retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11764   mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
11765   myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
11766   _ckvmssts(sys$dassgn(chan));
11767   if (retsts & 1) retsts = iosb[0];
11768   if (!(retsts & 1)) {
11769     set_vaxc_errno(retsts);
11770     if (retsts == SS$_NOPRIV) set_errno(EACCES);
11771     else                      set_errno(EVMSERR);
11772     return -1;
11773   }
11774 
11775   return 0;
11776 
11777 #endif /* #if __CRTL_VER >= 70300000 */
11778 
11779 }  /* end of my_utime() */
11780 /*}}}*/
11781 
11782 /*
11783  * flex_stat, flex_lstat, flex_fstat
11784  * basic stat, but gets it right when asked to stat
11785  * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11786  */
11787 
11788 #ifndef _USE_STD_STAT
11789 /* encode_dev packs a VMS device name string into an integer to allow
11790  * simple comparisons. This can be used, for example, to check whether two
11791  * files are located on the same device, by comparing their encoded device
11792  * names. Even a string comparison would not do, because stat() reuses the
11793  * device name buffer for each call; so without encode_dev, it would be
11794  * necessary to save the buffer and use strcmp (this would mean a number of
11795  * changes to the standard Perl code, to say nothing of what a Perl script
11796  * would have to do.
11797  *
11798  * The device lock id, if it exists, should be unique (unless perhaps compared
11799  * with lock ids transferred from other nodes). We have a lock id if the disk is
11800  * mounted cluster-wide, which is when we tend to get long (host-qualified)
11801  * device names. Thus we use the lock id in preference, and only if that isn't
11802  * available, do we try to pack the device name into an integer (flagged by
11803  * the sign bit (LOCKID_MASK) being set).
11804  *
11805  * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11806  * name and its encoded form, but it seems very unlikely that we will find
11807  * two files on different disks that share the same encoded device names,
11808  * and even more remote that they will share the same file id (if the test
11809  * is to check for the same file).
11810  *
11811  * A better method might be to use sys$device_scan on the first call, and to
11812  * search for the device, returning an index into the cached array.
11813  * The number returned would be more intelligible.
11814  * This is probably not worth it, and anyway would take quite a bit longer
11815  * on the first call.
11816  */
11817 #define LOCKID_MASK 0x80000000     /* Use 0 to force device name use only */
11818 static mydev_t encode_dev (pTHX_ const char *dev)
11819 {
11820   int i;
11821   unsigned long int f;
11822   mydev_t enc;
11823   char c;
11824   const char *q;
11825 
11826   if (!dev || !dev[0]) return 0;
11827 
11828 #if LOCKID_MASK
11829   {
11830     struct dsc$descriptor_s dev_desc;
11831     unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11832 
11833     /* For cluster-mounted disks, the disk lock identifier is unique, so we
11834        can try that first. */
11835     dev_desc.dsc$w_length =  strlen (dev);
11836     dev_desc.dsc$b_dtype =   DSC$K_DTYPE_T;
11837     dev_desc.dsc$b_class =   DSC$K_CLASS_S;
11838     dev_desc.dsc$a_pointer = (char *) dev;  /* Read only parameter */
11839     status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11840     if (!$VMS_STATUS_SUCCESS(status)) {
11841       switch (status) {
11842         case SS$_NOSUCHDEV:
11843           SETERRNO(ENODEV, status);
11844           return 0;
11845         default:
11846           _ckvmssts(status);
11847       }
11848     }
11849     if (lockid) return (lockid & ~LOCKID_MASK);
11850   }
11851 #endif
11852 
11853   /* Otherwise we try to encode the device name */
11854   enc = 0;
11855   f = 1;
11856   i = 0;
11857   for (q = dev + strlen(dev); q--; q >= dev) {
11858     if (*q == ':')
11859 	break;
11860     if (isdigit (*q))
11861       c= (*q) - '0';
11862     else if (isalpha (toupper (*q)))
11863       c= toupper (*q) - 'A' + (char)10;
11864     else
11865       continue; /* Skip '$'s */
11866     i++;
11867     if (i>6) break;     /* 36^7 is too large to fit in an unsigned long int */
11868     if (i>1) f *= 36;
11869     enc += f * (unsigned long int) c;
11870   }
11871   return (enc | LOCKID_MASK);  /* May have already overflowed into bit 31 */
11872 
11873 }  /* end of encode_dev() */
11874 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11875 	device_no = encode_dev(aTHX_ devname)
11876 #else
11877 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11878 	device_no = new_dev_no
11879 #endif
11880 
11881 static int
11882 is_null_device(const char *name)
11883 {
11884   if (decc_bug_devnull != 0) {
11885     if (strncmp("/dev/null", name, 9) == 0)
11886       return 1;
11887   }
11888     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11889        The underscore prefix, controller letter, and unit number are
11890        independently optional; for our purposes, the colon punctuation
11891        is not.  The colon can be trailed by optional directory and/or
11892        filename, but two consecutive colons indicates a nodename rather
11893        than a device.  [pr]  */
11894   if (*name == '_') ++name;
11895   if (tolower(*name++) != 'n') return 0;
11896   if (tolower(*name++) != 'l') return 0;
11897   if (tolower(*name) == 'a') ++name;
11898   if (*name == '0') ++name;
11899   return (*name++ == ':') && (*name != ':');
11900 }
11901 
11902 static int
11903 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11904 
11905 #define flex_stat_int(a,b,c)		Perl_flex_stat_int(aTHX_ a,b,c)
11906 
11907 static I32
11908 Perl_cando_by_name_int
11909    (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11910 {
11911   char usrname[L_cuserid];
11912   struct dsc$descriptor_s usrdsc =
11913          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11914   char *vmsname = NULL, *fileified = NULL;
11915   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11916   unsigned short int retlen, trnlnm_iter_count;
11917   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11918   union prvdef curprv;
11919   struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11920          {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11921          {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11922   struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11923          {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11924          {0,0,0,0}};
11925   struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11926          {0,0,0,0}};
11927   struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11928   Stat_t st;
11929   static int profile_context = -1;
11930 
11931   if (!fname || !*fname) return FALSE;
11932 
11933   /* Make sure we expand logical names, since sys$check_access doesn't */
11934   fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11935   if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11936   if (!strpbrk(fname,"/]>:")) {
11937       my_strlcpy(fileified, fname, VMS_MAXRSS);
11938       trnlnm_iter_count = 0;
11939       while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11940         trnlnm_iter_count++;
11941         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11942       }
11943       fname = fileified;
11944   }
11945 
11946   vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11947   if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11948   if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11949     /* Don't know if already in VMS format, so make sure */
11950     if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11951       PerlMem_free(fileified);
11952       PerlMem_free(vmsname);
11953       return FALSE;
11954     }
11955   }
11956   else {
11957     my_strlcpy(vmsname, fname, VMS_MAXRSS);
11958   }
11959 
11960   /* sys$check_access needs a file spec, not a directory spec.
11961    * flex_stat now will handle a null thread context during startup.
11962    */
11963 
11964   retlen = namdsc.dsc$w_length = strlen(vmsname);
11965   if (vmsname[retlen-1] == ']'
11966       || vmsname[retlen-1] == '>'
11967       || vmsname[retlen-1] == ':'
11968       || (!flex_stat_int(vmsname, &st, 1) &&
11969           S_ISDIR(st.st_mode))) {
11970 
11971       if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11972         PerlMem_free(fileified);
11973         PerlMem_free(vmsname);
11974         return FALSE;
11975       }
11976       fname = fileified;
11977   }
11978   else {
11979       fname = vmsname;
11980   }
11981 
11982   retlen = namdsc.dsc$w_length = strlen(fname);
11983   namdsc.dsc$a_pointer = (char *)fname;
11984 
11985   switch (bit) {
11986     case S_IXUSR: case S_IXGRP: case S_IXOTH:
11987       access = ARM$M_EXECUTE;
11988       flags = CHP$M_READ;
11989       break;
11990     case S_IRUSR: case S_IRGRP: case S_IROTH:
11991       access = ARM$M_READ;
11992       flags = CHP$M_READ | CHP$M_USEREADALL;
11993       break;
11994     case S_IWUSR: case S_IWGRP: case S_IWOTH:
11995       access = ARM$M_WRITE;
11996       flags = CHP$M_READ | CHP$M_WRITE;
11997       break;
11998     case S_IDUSR: case S_IDGRP: case S_IDOTH:
11999       access = ARM$M_DELETE;
12000       flags = CHP$M_READ | CHP$M_WRITE;
12001       break;
12002     default:
12003       if (fileified != NULL)
12004 	PerlMem_free(fileified);
12005       if (vmsname != NULL)
12006 	PerlMem_free(vmsname);
12007       return FALSE;
12008   }
12009 
12010   /* Before we call $check_access, create a user profile with the current
12011    * process privs since otherwise it just uses the default privs from the
12012    * UAF and might give false positives or negatives.  This only works on
12013    * VMS versions v6.0 and later since that's when sys$create_user_profile
12014    * became available.
12015    */
12016 
12017   /* get current process privs and username */
12018   _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12019   _ckvmssts_noperl(iosb[0]);
12020 
12021   /* find out the space required for the profile */
12022   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12023                                     &usrprodsc.dsc$w_length,&profile_context));
12024 
12025   /* allocate space for the profile and get it filled in */
12026   usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
12027   if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12028   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12029                                     &usrprodsc.dsc$w_length,&profile_context));
12030 
12031   /* use the profile to check access to the file; free profile & analyze results */
12032   retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12033   PerlMem_free(usrprodsc.dsc$a_pointer);
12034   if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12035 
12036   if (retsts == SS$_NOPRIV      || retsts == SS$_NOSUCHOBJECT ||
12037       retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12038       retsts == RMS$_DIR        || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12039     set_vaxc_errno(retsts);
12040     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12041     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12042     else set_errno(ENOENT);
12043     if (fileified != NULL)
12044       PerlMem_free(fileified);
12045     if (vmsname != NULL)
12046       PerlMem_free(vmsname);
12047     return FALSE;
12048   }
12049   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12050     if (fileified != NULL)
12051       PerlMem_free(fileified);
12052     if (vmsname != NULL)
12053       PerlMem_free(vmsname);
12054     return TRUE;
12055   }
12056   _ckvmssts_noperl(retsts);
12057 
12058   if (fileified != NULL)
12059     PerlMem_free(fileified);
12060   if (vmsname != NULL)
12061     PerlMem_free(vmsname);
12062   return FALSE;  /* Should never get here */
12063 
12064 }
12065 
12066 /* Do the permissions allow some operation?  Assumes PL_statcache already set. */
12067 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12068  * subset of the applicable information.
12069  */
12070 bool
12071 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12072 {
12073   return cando_by_name_int
12074 	(bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12075 }  /* end of cando() */
12076 /*}}}*/
12077 
12078 
12079 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12080 I32
12081 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12082 {
12083    return cando_by_name_int(bit, effective, fname, 0);
12084 
12085 }  /* end of cando_by_name() */
12086 /*}}}*/
12087 
12088 
12089 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12090 int
12091 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12092 {
12093   dSAVE_ERRNO; /* fstat may set this even on success */
12094   if (!fstat(fd, &statbufp->crtl_stat)) {
12095     char *cptr;
12096     char *vms_filename;
12097     vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12098     if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12099 
12100     /* Save name for cando by name in VMS format */
12101     cptr = getname(fd, vms_filename, 1);
12102 
12103     /* This should not happen, but just in case */
12104     if (cptr == NULL) {
12105 	statbufp->st_devnam[0] = 0;
12106     }
12107     else {
12108 	/* Make sure that the saved name fits in 255 characters */
12109 	cptr = int_rmsexpand_vms
12110 		       (vms_filename,
12111 			statbufp->st_devnam,
12112 			0);
12113 	if (cptr == NULL)
12114 	    statbufp->st_devnam[0] = 0;
12115     }
12116     PerlMem_free(vms_filename);
12117 
12118     VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12119     VMS_DEVICE_ENCODE
12120 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12121 
12122 #   ifdef VMSISH_TIME
12123     if (VMSISH_TIME) {
12124       statbufp->st_mtime = _toloc(statbufp->st_mtime);
12125       statbufp->st_atime = _toloc(statbufp->st_atime);
12126       statbufp->st_ctime = _toloc(statbufp->st_ctime);
12127     }
12128 #   endif
12129     RESTORE_ERRNO;
12130     return 0;
12131   }
12132   return -1;
12133 
12134 }  /* end of flex_fstat() */
12135 /*}}}*/
12136 
12137 static int
12138 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12139 {
12140     char *temp_fspec = NULL;
12141     char *fileified = NULL;
12142     const char *save_spec;
12143     char *ret_spec;
12144     int retval = -1;
12145     char efs_hack = 0;
12146     char already_fileified = 0;
12147     dSAVEDERRNO;
12148 
12149     if (!fspec) {
12150         errno = EINVAL;
12151         return retval;
12152     }
12153 
12154     if (decc_bug_devnull != 0) {
12155       if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12156 	memset(statbufp,0,sizeof *statbufp);
12157         VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12158 	statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12159 	statbufp->st_uid = 0x00010001;
12160 	statbufp->st_gid = 0x0001;
12161 	time((time_t *)&statbufp->st_mtime);
12162 	statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12163 	return 0;
12164       }
12165     }
12166 
12167     SAVE_ERRNO;
12168 
12169 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12170   /*
12171    * If we are in POSIX filespec mode, accept the filename as is.
12172    */
12173   if (decc_posix_compliant_pathnames == 0) {
12174 #endif
12175 
12176     /* Try for a simple stat first.  If fspec contains a filename without
12177      * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12178      * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12179      * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12180      * not sea:[wine.dark]., if the latter exists.  If the intended target is
12181      * the file with null type, specify this by calling flex_stat() with
12182      * a '.' at the end of fspec.
12183      */
12184 
12185     if (lstat_flag == 0)
12186         retval = stat(fspec, &statbufp->crtl_stat);
12187     else
12188         retval = lstat(fspec, &statbufp->crtl_stat);
12189 
12190     if (!retval) {
12191         save_spec = fspec;
12192     }
12193     else {
12194         /* In the odd case where we have write but not read access
12195          * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12196          */
12197         fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12198         if (fileified == NULL)
12199               _ckvmssts_noperl(SS$_INSFMEM);
12200 
12201         ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12202         if (ret_spec != NULL) {
12203             if (lstat_flag == 0)
12204                 retval = stat(fileified, &statbufp->crtl_stat);
12205             else
12206                 retval = lstat(fileified, &statbufp->crtl_stat);
12207             save_spec = fileified;
12208             already_fileified = 1;
12209         }
12210     }
12211 
12212     if (retval && vms_bug_stat_filename) {
12213 
12214         temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12215         if (temp_fspec == NULL)
12216             _ckvmssts_noperl(SS$_INSFMEM);
12217 
12218         /* We should try again as a vmsified file specification. */
12219 
12220         ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12221         if (ret_spec != NULL) {
12222             if (lstat_flag == 0)
12223                 retval = stat(temp_fspec, &statbufp->crtl_stat);
12224             else
12225                 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12226             save_spec = temp_fspec;
12227         }
12228     }
12229 
12230     if (retval) {
12231         /* Last chance - allow multiple dots without EFS CHARSET */
12232         /* The CRTL stat() falls down hard on multi-dot filenames in unix
12233          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12234          * enable it if it isn't already.
12235          */
12236 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12237         if (!decc_efs_charset && (decc_efs_charset_index > 0))
12238             decc$feature_set_value(decc_efs_charset_index, 1, 1);
12239 #endif
12240         if (lstat_flag == 0)
12241 	    retval = stat(fspec, &statbufp->crtl_stat);
12242         else
12243 	    retval = lstat(fspec, &statbufp->crtl_stat);
12244         save_spec = fspec;
12245 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12246         if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12247             decc$feature_set_value(decc_efs_charset_index, 1, 0);
12248             efs_hack = 1;
12249         }
12250 #endif
12251     }
12252 
12253 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12254   } else {
12255     if (lstat_flag == 0)
12256       retval = stat(temp_fspec, &statbufp->crtl_stat);
12257     else
12258       retval = lstat(temp_fspec, &statbufp->crtl_stat);
12259       save_spec = temp_fspec;
12260   }
12261 #endif
12262 
12263 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12264   /* As you were... */
12265   if (!decc_efs_charset)
12266     decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12267 #endif
12268 
12269     if (!retval) {
12270       char *cptr;
12271       int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12272 
12273       /* If this is an lstat, do not follow the link */
12274       if (lstat_flag)
12275 	rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12276 
12277 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12278       /* If we used the efs_hack above, we must also use it here for */
12279       /* perl_cando to work */
12280       if (efs_hack && (decc_efs_charset_index > 0)) {
12281           decc$feature_set_value(decc_efs_charset_index, 1, 1);
12282       }
12283 #endif
12284 
12285       /* If we've got a directory, save a fileified, expanded version of it
12286        * in st_devnam.  If not a directory, just an expanded version.
12287        */
12288       if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12289           fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12290           if (fileified == NULL)
12291               _ckvmssts_noperl(SS$_INSFMEM);
12292 
12293           cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12294           if (cptr != NULL)
12295               save_spec = fileified;
12296       }
12297 
12298       cptr = int_rmsexpand(save_spec,
12299                            statbufp->st_devnam,
12300                            NULL,
12301                            rmsex_flags,
12302                            0,
12303                            0);
12304 
12305 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12306       if (efs_hack && (decc_efs_charset_index > 0)) {
12307           decc$feature_set_value(decc_efs_charset, 1, 0);
12308       }
12309 #endif
12310 
12311       /* Fix me: If this is NULL then stat found a file, and we could */
12312       /* not convert the specification to VMS - Should never happen */
12313       if (cptr == NULL)
12314 	statbufp->st_devnam[0] = 0;
12315 
12316       VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12317       VMS_DEVICE_ENCODE
12318 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12319 #     ifdef VMSISH_TIME
12320       if (VMSISH_TIME) {
12321         statbufp->st_mtime = _toloc(statbufp->st_mtime);
12322         statbufp->st_atime = _toloc(statbufp->st_atime);
12323         statbufp->st_ctime = _toloc(statbufp->st_ctime);
12324       }
12325 #     endif
12326     }
12327     /* If we were successful, leave errno where we found it */
12328     if (retval == 0) RESTORE_ERRNO;
12329     if (temp_fspec)
12330         PerlMem_free(temp_fspec);
12331     if (fileified)
12332         PerlMem_free(fileified);
12333     return retval;
12334 
12335 }  /* end of flex_stat_int() */
12336 
12337 
12338 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12339 int
12340 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12341 {
12342    return flex_stat_int(fspec, statbufp, 0);
12343 }
12344 /*}}}*/
12345 
12346 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12347 int
12348 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12349 {
12350    return flex_stat_int(fspec, statbufp, 1);
12351 }
12352 /*}}}*/
12353 
12354 
12355 /*{{{char *my_getlogin()*/
12356 /* VMS cuserid == Unix getlogin, except calling sequence */
12357 char *
12358 my_getlogin(void)
12359 {
12360     static char user[L_cuserid];
12361     return cuserid(user);
12362 }
12363 /*}}}*/
12364 
12365 
12366 /*  rmscopy - copy a file using VMS RMS routines
12367  *
12368  *  Copies contents and attributes of spec_in to spec_out, except owner
12369  *  and protection information.  Name and type of spec_in are used as
12370  *  defaults for spec_out.  The third parameter specifies whether rmscopy()
12371  *  should try to propagate timestamps from the input file to the output file.
12372  *  If it is less than 0, no timestamps are preserved.  If it is 0, then
12373  *  rmscopy() will behave similarly to the DCL COPY command: timestamps are
12374  *  propagated to the output file at creation iff the output file specification
12375  *  did not contain an explicit name or type, and the revision date is always
12376  *  updated at the end of the copy operation.  If it is greater than 0, then
12377  *  it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12378  *  other than the revision date should be propagated, and bit 1 indicates
12379  *  that the revision date should be propagated.
12380  *
12381  *  Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12382  *
12383  *  Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12384  *  Incorporates, with permission, some code from EZCOPY by Tim Adye
12385  *  <T.J.Adye@rl.ac.uk>.  Permission is given to distribute this code
12386  * as part of the Perl standard distribution under the terms of the
12387  * GNU General Public License or the Perl Artistic License.  Copies
12388  * of each may be found in the Perl standard distribution.
12389  */ /* FIXME */
12390 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12391 int
12392 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12393 {
12394     char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12395          *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12396     unsigned long int sts;
12397     int dna_len;
12398     struct FAB fab_in, fab_out;
12399     struct RAB rab_in, rab_out;
12400     rms_setup_nam(nam);
12401     rms_setup_nam(nam_out);
12402     struct XABDAT xabdat;
12403     struct XABFHC xabfhc;
12404     struct XABRDT xabrdt;
12405     struct XABSUM xabsum;
12406 
12407     vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12408     if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12409     vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12410     if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12411     if (!spec_in  || !*spec_in  || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12412         !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12413       PerlMem_free(vmsin);
12414       PerlMem_free(vmsout);
12415       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12416       return 0;
12417     }
12418 
12419     esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12420     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12421     esal = NULL;
12422 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12423     esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12424     if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12425 #endif
12426     fab_in = cc$rms_fab;
12427     rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12428     fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12429     fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12430     fab_in.fab$l_fop = FAB$M_SQO;
12431     rms_bind_fab_nam(fab_in, nam);
12432     fab_in.fab$l_xab = (void *) &xabdat;
12433 
12434     rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12435     if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12436     rsal = NULL;
12437 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12438     rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12439     if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12440 #endif
12441     rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12442     rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12443     rms_nam_esl(nam) = 0;
12444     rms_nam_rsl(nam) = 0;
12445     rms_nam_esll(nam) = 0;
12446     rms_nam_rsll(nam) = 0;
12447 #ifdef NAM$M_NO_SHORT_UPCASE
12448     if (decc_efs_case_preserve)
12449 	rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12450 #endif
12451 
12452     xabdat = cc$rms_xabdat;        /* To get creation date */
12453     xabdat.xab$l_nxt = (void *) &xabfhc;
12454 
12455     xabfhc = cc$rms_xabfhc;        /* To get record length */
12456     xabfhc.xab$l_nxt = (void *) &xabsum;
12457 
12458     xabsum = cc$rms_xabsum;        /* To get key and area information */
12459 
12460     if (!((sts = sys$open(&fab_in)) & 1)) {
12461       PerlMem_free(vmsin);
12462       PerlMem_free(vmsout);
12463       PerlMem_free(esa);
12464       if (esal != NULL)
12465 	PerlMem_free(esal);
12466       PerlMem_free(rsa);
12467       if (rsal != NULL)
12468 	PerlMem_free(rsal);
12469       set_vaxc_errno(sts);
12470       switch (sts) {
12471         case RMS$_FNF: case RMS$_DNF:
12472           set_errno(ENOENT); break;
12473         case RMS$_DIR:
12474           set_errno(ENOTDIR); break;
12475         case RMS$_DEV:
12476           set_errno(ENODEV); break;
12477         case RMS$_SYN:
12478           set_errno(EINVAL); break;
12479         case RMS$_PRV:
12480           set_errno(EACCES); break;
12481         default:
12482           set_errno(EVMSERR);
12483       }
12484       return 0;
12485     }
12486 
12487     nam_out = nam;
12488     fab_out = fab_in;
12489     fab_out.fab$w_ifi = 0;
12490     fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12491     fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12492     fab_out.fab$l_fop = FAB$M_SQO;
12493     rms_bind_fab_nam(fab_out, nam_out);
12494     rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12495     dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12496     rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12497     esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12498     if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12499     rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12500     if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12501     esal_out = NULL;
12502     rsal_out = NULL;
12503 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12504     esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12505     if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12506     rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12507     if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12508 #endif
12509     rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12510     rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12511 
12512     if (preserve_dates == 0) {  /* Act like DCL COPY */
12513       rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12514       fab_out.fab$l_xab = NULL;  /* Don't disturb data from input file */
12515       if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12516 	PerlMem_free(vmsin);
12517 	PerlMem_free(vmsout);
12518 	PerlMem_free(esa);
12519 	if (esal != NULL)
12520 	    PerlMem_free(esal);
12521 	PerlMem_free(rsa);
12522 	if (rsal != NULL)
12523 	    PerlMem_free(rsal);
12524 	PerlMem_free(esa_out);
12525 	if (esal_out != NULL)
12526 	    PerlMem_free(esal_out);
12527 	PerlMem_free(rsa_out);
12528 	if (rsal_out != NULL)
12529 	    PerlMem_free(rsal_out);
12530         set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12531         set_vaxc_errno(sts);
12532         return 0;
12533       }
12534       fab_out.fab$l_xab = (void *) &xabdat;
12535       if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12536 	preserve_dates = 1;
12537     }
12538     if (preserve_dates < 0)   /* Clear all bits; we'll use it as a */
12539       preserve_dates =0;      /* bitmask from this point forward   */
12540 
12541     if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12542     if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12543       PerlMem_free(vmsin);
12544       PerlMem_free(vmsout);
12545       PerlMem_free(esa);
12546       if (esal != NULL)
12547 	  PerlMem_free(esal);
12548       PerlMem_free(rsa);
12549       if (rsal != NULL)
12550 	  PerlMem_free(rsal);
12551       PerlMem_free(esa_out);
12552       if (esal_out != NULL)
12553 	  PerlMem_free(esal_out);
12554       PerlMem_free(rsa_out);
12555       if (rsal_out != NULL)
12556 	  PerlMem_free(rsal_out);
12557       set_vaxc_errno(sts);
12558       switch (sts) {
12559         case RMS$_DNF:
12560           set_errno(ENOENT); break;
12561         case RMS$_DIR:
12562           set_errno(ENOTDIR); break;
12563         case RMS$_DEV:
12564           set_errno(ENODEV); break;
12565         case RMS$_SYN:
12566           set_errno(EINVAL); break;
12567         case RMS$_PRV:
12568           set_errno(EACCES); break;
12569         default:
12570           set_errno(EVMSERR);
12571       }
12572       return 0;
12573     }
12574     fab_out.fab$l_fop |= FAB$M_DLT;  /* in case we have to bail out */
12575     if (preserve_dates & 2) {
12576       /* sys$close() will process xabrdt, not xabdat */
12577       xabrdt = cc$rms_xabrdt;
12578 #ifndef __GNUC__
12579       xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12580 #else
12581       /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12582        * is unsigned long[2], while DECC & VAXC use a struct */
12583       memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12584 #endif
12585       fab_out.fab$l_xab = (void *) &xabrdt;
12586     }
12587 
12588     ubf = (char *)PerlMem_malloc(32256);
12589     if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12590     rab_in = cc$rms_rab;
12591     rab_in.rab$l_fab = &fab_in;
12592     rab_in.rab$l_rop = RAB$M_BIO;
12593     rab_in.rab$l_ubf = ubf;
12594     rab_in.rab$w_usz = 32256;
12595     if (!((sts = sys$connect(&rab_in)) & 1)) {
12596       sys$close(&fab_in); sys$close(&fab_out);
12597       PerlMem_free(vmsin);
12598       PerlMem_free(vmsout);
12599       PerlMem_free(ubf);
12600       PerlMem_free(esa);
12601       if (esal != NULL)
12602 	  PerlMem_free(esal);
12603       PerlMem_free(rsa);
12604       if (rsal != NULL)
12605 	  PerlMem_free(rsal);
12606       PerlMem_free(esa_out);
12607       if (esal_out != NULL)
12608 	  PerlMem_free(esal_out);
12609       PerlMem_free(rsa_out);
12610       if (rsal_out != NULL)
12611 	  PerlMem_free(rsal_out);
12612       set_errno(EVMSERR); set_vaxc_errno(sts);
12613       return 0;
12614     }
12615 
12616     rab_out = cc$rms_rab;
12617     rab_out.rab$l_fab = &fab_out;
12618     rab_out.rab$l_rbf = ubf;
12619     if (!((sts = sys$connect(&rab_out)) & 1)) {
12620       sys$close(&fab_in); sys$close(&fab_out);
12621       PerlMem_free(vmsin);
12622       PerlMem_free(vmsout);
12623       PerlMem_free(ubf);
12624       PerlMem_free(esa);
12625       if (esal != NULL)
12626 	  PerlMem_free(esal);
12627       PerlMem_free(rsa);
12628       if (rsal != NULL)
12629 	  PerlMem_free(rsal);
12630       PerlMem_free(esa_out);
12631       if (esal_out != NULL)
12632 	  PerlMem_free(esal_out);
12633       PerlMem_free(rsa_out);
12634       if (rsal_out != NULL)
12635 	  PerlMem_free(rsal_out);
12636       set_errno(EVMSERR); set_vaxc_errno(sts);
12637       return 0;
12638     }
12639 
12640     while ((sts = sys$read(&rab_in))) {  /* always true  */
12641       if (sts == RMS$_EOF) break;
12642       rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12643       if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12644         sys$close(&fab_in); sys$close(&fab_out);
12645 	PerlMem_free(vmsin);
12646 	PerlMem_free(vmsout);
12647 	PerlMem_free(ubf);
12648 	PerlMem_free(esa);
12649 	if (esal != NULL)
12650 	    PerlMem_free(esal);
12651 	PerlMem_free(rsa);
12652 	if (rsal != NULL)
12653 	    PerlMem_free(rsal);
12654 	PerlMem_free(esa_out);
12655  	if (esal_out != NULL)
12656 	    PerlMem_free(esal_out);
12657 	PerlMem_free(rsa_out);
12658  	if (rsal_out != NULL)
12659 	    PerlMem_free(rsal_out);
12660         set_errno(EVMSERR); set_vaxc_errno(sts);
12661         return 0;
12662       }
12663     }
12664 
12665 
12666     fab_out.fab$l_fop &= ~FAB$M_DLT;  /* We got this far; keep the output */
12667     sys$close(&fab_in);  sys$close(&fab_out);
12668     sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12669 
12670     PerlMem_free(vmsin);
12671     PerlMem_free(vmsout);
12672     PerlMem_free(ubf);
12673     PerlMem_free(esa);
12674     if (esal != NULL)
12675 	PerlMem_free(esal);
12676     PerlMem_free(rsa);
12677     if (rsal != NULL)
12678 	PerlMem_free(rsal);
12679     PerlMem_free(esa_out);
12680     if (esal_out != NULL)
12681 	PerlMem_free(esal_out);
12682     PerlMem_free(rsa_out);
12683     if (rsal_out != NULL)
12684 	PerlMem_free(rsal_out);
12685 
12686     if (!(sts & 1)) {
12687       set_errno(EVMSERR); set_vaxc_errno(sts);
12688       return 0;
12689     }
12690 
12691     return 1;
12692 
12693 }  /* end of rmscopy() */
12694 /*}}}*/
12695 
12696 
12697 /***  The following glue provides 'hooks' to make some of the routines
12698  * from this file available from Perl.  These routines are sufficiently
12699  * basic, and are required sufficiently early in the build process,
12700  * that's it's nice to have them available to miniperl as well as the
12701  * full Perl, so they're set up here instead of in an extension.  The
12702  * Perl code which handles importation of these names into a given
12703  * package lives in [.VMS]Filespec.pm in @INC.
12704  */
12705 
12706 void
12707 rmsexpand_fromperl(pTHX_ CV *cv)
12708 {
12709   dXSARGS;
12710   char *fspec, *defspec = NULL, *rslt;
12711   STRLEN n_a;
12712   int fs_utf8, dfs_utf8;
12713 
12714   fs_utf8 = 0;
12715   dfs_utf8 = 0;
12716   if (!items || items > 2)
12717     Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12718   fspec = SvPV(ST(0),n_a);
12719   fs_utf8 = SvUTF8(ST(0));
12720   if (!fspec || !*fspec) XSRETURN_UNDEF;
12721   if (items == 2) {
12722     defspec = SvPV(ST(1),n_a);
12723     dfs_utf8 = SvUTF8(ST(1));
12724   }
12725   rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12726   ST(0) = sv_newmortal();
12727   if (rslt != NULL) {
12728     sv_usepvn(ST(0),rslt,strlen(rslt));
12729     if (fs_utf8) {
12730 	SvUTF8_on(ST(0));
12731     }
12732   }
12733   XSRETURN(1);
12734 }
12735 
12736 void
12737 vmsify_fromperl(pTHX_ CV *cv)
12738 {
12739   dXSARGS;
12740   char *vmsified;
12741   STRLEN n_a;
12742   int utf8_fl;
12743 
12744   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12745   utf8_fl = SvUTF8(ST(0));
12746   vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12747   ST(0) = sv_newmortal();
12748   if (vmsified != NULL) {
12749     sv_usepvn(ST(0),vmsified,strlen(vmsified));
12750     if (utf8_fl) {
12751 	SvUTF8_on(ST(0));
12752     }
12753   }
12754   XSRETURN(1);
12755 }
12756 
12757 void
12758 unixify_fromperl(pTHX_ CV *cv)
12759 {
12760   dXSARGS;
12761   char *unixified;
12762   STRLEN n_a;
12763   int utf8_fl;
12764 
12765   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12766   utf8_fl = SvUTF8(ST(0));
12767   unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12768   ST(0) = sv_newmortal();
12769   if (unixified != NULL) {
12770     sv_usepvn(ST(0),unixified,strlen(unixified));
12771     if (utf8_fl) {
12772 	SvUTF8_on(ST(0));
12773     }
12774   }
12775   XSRETURN(1);
12776 }
12777 
12778 void
12779 fileify_fromperl(pTHX_ CV *cv)
12780 {
12781   dXSARGS;
12782   char *fileified;
12783   STRLEN n_a;
12784   int utf8_fl;
12785 
12786   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12787   utf8_fl = SvUTF8(ST(0));
12788   fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12789   ST(0) = sv_newmortal();
12790   if (fileified != NULL) {
12791     sv_usepvn(ST(0),fileified,strlen(fileified));
12792     if (utf8_fl) {
12793 	SvUTF8_on(ST(0));
12794     }
12795   }
12796   XSRETURN(1);
12797 }
12798 
12799 void
12800 pathify_fromperl(pTHX_ CV *cv)
12801 {
12802   dXSARGS;
12803   char *pathified;
12804   STRLEN n_a;
12805   int utf8_fl;
12806 
12807   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12808   utf8_fl = SvUTF8(ST(0));
12809   pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12810   ST(0) = sv_newmortal();
12811   if (pathified != NULL) {
12812     sv_usepvn(ST(0),pathified,strlen(pathified));
12813     if (utf8_fl) {
12814 	SvUTF8_on(ST(0));
12815     }
12816   }
12817   XSRETURN(1);
12818 }
12819 
12820 void
12821 vmspath_fromperl(pTHX_ CV *cv)
12822 {
12823   dXSARGS;
12824   char *vmspath;
12825   STRLEN n_a;
12826   int utf8_fl;
12827 
12828   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12829   utf8_fl = SvUTF8(ST(0));
12830   vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12831   ST(0) = sv_newmortal();
12832   if (vmspath != NULL) {
12833     sv_usepvn(ST(0),vmspath,strlen(vmspath));
12834     if (utf8_fl) {
12835 	SvUTF8_on(ST(0));
12836     }
12837   }
12838   XSRETURN(1);
12839 }
12840 
12841 void
12842 unixpath_fromperl(pTHX_ CV *cv)
12843 {
12844   dXSARGS;
12845   char *unixpath;
12846   STRLEN n_a;
12847   int utf8_fl;
12848 
12849   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12850   utf8_fl = SvUTF8(ST(0));
12851   unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12852   ST(0) = sv_newmortal();
12853   if (unixpath != NULL) {
12854     sv_usepvn(ST(0),unixpath,strlen(unixpath));
12855     if (utf8_fl) {
12856 	SvUTF8_on(ST(0));
12857     }
12858   }
12859   XSRETURN(1);
12860 }
12861 
12862 void
12863 candelete_fromperl(pTHX_ CV *cv)
12864 {
12865   dXSARGS;
12866   char *fspec, *fsp;
12867   SV *mysv;
12868   IO *io;
12869   STRLEN n_a;
12870 
12871   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12872 
12873   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12874   Newx(fspec, VMS_MAXRSS, char);
12875   if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12876   if (isGV_with_GP(mysv)) {
12877     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12878       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12879       ST(0) = &PL_sv_no;
12880       Safefree(fspec);
12881       XSRETURN(1);
12882     }
12883     fsp = fspec;
12884   }
12885   else {
12886     if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12887       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12888       ST(0) = &PL_sv_no;
12889       Safefree(fspec);
12890       XSRETURN(1);
12891     }
12892   }
12893 
12894   ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12895   Safefree(fspec);
12896   XSRETURN(1);
12897 }
12898 
12899 void
12900 rmscopy_fromperl(pTHX_ CV *cv)
12901 {
12902   dXSARGS;
12903   char *inspec, *outspec, *inp, *outp;
12904   int date_flag;
12905   SV *mysv;
12906   IO *io;
12907   STRLEN n_a;
12908 
12909   if (items < 2 || items > 3)
12910     Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12911 
12912   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12913   Newx(inspec, VMS_MAXRSS, char);
12914   if (isGV_with_GP(mysv)) {
12915     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12916       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12917       ST(0) = sv_2mortal(newSViv(0));
12918       Safefree(inspec);
12919       XSRETURN(1);
12920     }
12921     inp = inspec;
12922   }
12923   else {
12924     if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12925       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12926       ST(0) = sv_2mortal(newSViv(0));
12927       Safefree(inspec);
12928       XSRETURN(1);
12929     }
12930   }
12931   mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12932   Newx(outspec, VMS_MAXRSS, char);
12933   if (isGV_with_GP(mysv)) {
12934     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12935       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12936       ST(0) = sv_2mortal(newSViv(0));
12937       Safefree(inspec);
12938       Safefree(outspec);
12939       XSRETURN(1);
12940     }
12941     outp = outspec;
12942   }
12943   else {
12944     if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12945       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12946       ST(0) = sv_2mortal(newSViv(0));
12947       Safefree(inspec);
12948       Safefree(outspec);
12949       XSRETURN(1);
12950     }
12951   }
12952   date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12953 
12954   ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12955   Safefree(inspec);
12956   Safefree(outspec);
12957   XSRETURN(1);
12958 }
12959 
12960 /* The mod2fname is limited to shorter filenames by design, so it should
12961  * not be modified to support longer EFS pathnames
12962  */
12963 void
12964 mod2fname(pTHX_ CV *cv)
12965 {
12966   dXSARGS;
12967   char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12968        workbuff[NAM$C_MAXRSS*1 + 1];
12969   SSize_t counter, num_entries;
12970   /* ODS-5 ups this, but we want to be consistent, so... */
12971   int max_name_len = 39;
12972   AV *in_array = (AV *)SvRV(ST(0));
12973 
12974   num_entries = av_tindex(in_array);
12975 
12976   /* All the names start with PL_. */
12977   strcpy(ultimate_name, "PL_");
12978 
12979   /* Clean up our working buffer */
12980   Zero(work_name, sizeof(work_name), char);
12981 
12982   /* Run through the entries and build up a working name */
12983   for(counter = 0; counter <= num_entries; counter++) {
12984     /* If it's not the first name then tack on a __ */
12985     if (counter) {
12986       my_strlcat(work_name, "__", sizeof(work_name));
12987     }
12988     my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12989   }
12990 
12991   /* Check to see if we actually have to bother...*/
12992   if (strlen(work_name) + 3 <= max_name_len) {
12993     my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12994   } else {
12995     /* It's too darned big, so we need to go strip. We use the same */
12996     /* algorithm as xsubpp does. First, strip out doubled __ */
12997     char *source, *dest, last;
12998     dest = workbuff;
12999     last = 0;
13000     for (source = work_name; *source; source++) {
13001       if (last == *source && last == '_') {
13002 	continue;
13003       }
13004       *dest++ = *source;
13005       last = *source;
13006     }
13007     /* Go put it back */
13008     my_strlcpy(work_name, workbuff, sizeof(work_name));
13009     /* Is it still too big? */
13010     if (strlen(work_name) + 3 > max_name_len) {
13011       /* Strip duplicate letters */
13012       last = 0;
13013       dest = workbuff;
13014       for (source = work_name; *source; source++) {
13015 	if (last == toupper(*source)) {
13016 	continue;
13017 	}
13018 	*dest++ = *source;
13019 	last = toupper(*source);
13020       }
13021       my_strlcpy(work_name, workbuff, sizeof(work_name));
13022     }
13023 
13024     /* Is it *still* too big? */
13025     if (strlen(work_name) + 3 > max_name_len) {
13026       /* Too bad, we truncate */
13027       work_name[max_name_len - 2] = 0;
13028     }
13029     my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
13030   }
13031 
13032   /* Okay, return it */
13033   ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13034   XSRETURN(1);
13035 }
13036 
13037 void
13038 hushexit_fromperl(pTHX_ CV *cv)
13039 {
13040     dXSARGS;
13041 
13042     if (items > 0) {
13043         VMSISH_HUSHED = SvTRUE(ST(0));
13044     }
13045     ST(0) = boolSV(VMSISH_HUSHED);
13046     XSRETURN(1);
13047 }
13048 
13049 
13050 PerlIO *
13051 Perl_vms_start_glob
13052    (pTHX_ SV *tmpglob,
13053     IO *io)
13054 {
13055     PerlIO *fp;
13056     struct vs_str_st *rslt;
13057     char *vmsspec;
13058     char *rstr;
13059     char *begin, *cp;
13060     $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13061     PerlIO *tmpfp;
13062     STRLEN i;
13063     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13064     struct dsc$descriptor_vs rsdsc;
13065     unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13066     unsigned long hasver = 0, isunix = 0;
13067     unsigned long int lff_flags = 0;
13068     int rms_sts;
13069     int vms_old_glob = 1;
13070 
13071     if (!SvOK(tmpglob)) {
13072         SETERRNO(ENOENT,RMS$_FNF);
13073         return NULL;
13074     }
13075 
13076     vms_old_glob = !decc_filename_unix_report;
13077 
13078 #ifdef VMS_LONGNAME_SUPPORT
13079     lff_flags = LIB$M_FIL_LONG_NAMES;
13080 #endif
13081     /* The Newx macro will not allow me to assign a smaller array
13082      * to the rslt pointer, so we will assign it to the begin char pointer
13083      * and then copy the value into the rslt pointer.
13084      */
13085     Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13086     rslt = (struct vs_str_st *)begin;
13087     rslt->length = 0;
13088     rstr = &rslt->str[0];
13089     rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13090     rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13091     rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13092     rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13093 
13094     Newx(vmsspec, VMS_MAXRSS, char);
13095 
13096 	/* We could find out if there's an explicit dev/dir or version
13097 	   by peeking into lib$find_file's internal context at
13098 	   ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13099 	   but that's unsupported, so I don't want to do it now and
13100 	   have it bite someone in the future. */
13101 	/* Fix-me: vms_split_path() is the only way to do this, the
13102 	   existing method will fail with many legal EFS or UNIX specifications
13103 	 */
13104 
13105     cp = SvPV(tmpglob,i);
13106 
13107     for (; i; i--) {
13108 	if (cp[i] == ';') hasver = 1;
13109 	if (cp[i] == '.') {
13110 	    if (sts) hasver = 1;
13111 	    else sts = 1;
13112 	}
13113 	if (cp[i] == '/') {
13114 	    hasdir = isunix = 1;
13115 	    break;
13116 	}
13117 	if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13118 	    hasdir = 1;
13119 	    break;
13120 	}
13121     }
13122 
13123     /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13124     if ((hasdir == 0) && decc_filename_unix_report) {
13125         isunix = 1;
13126     }
13127 
13128     if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13129 	char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13130 	int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13131 	int wildstar = 0;
13132 	int wildquery = 0;
13133 	int found = 0;
13134 	Stat_t st;
13135 	int stat_sts;
13136 	stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13137 	if (!stat_sts && S_ISDIR(st.st_mode)) {
13138             char * vms_dir;
13139             const char * fname;
13140             STRLEN fname_len;
13141 
13142             /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13143             /* path delimiter of ':>]', if so, then the old behavior has */
13144             /* obviously been specifically requested */
13145 
13146             fname = SvPVX_const(tmpglob);
13147             fname_len = strlen(fname);
13148             vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13149             if (vms_old_glob || (vms_dir != NULL)) {
13150                 wilddsc.dsc$a_pointer = tovmspath_utf8(
13151                                             SvPVX(tmpglob),vmsspec,NULL);
13152                 ok = (wilddsc.dsc$a_pointer != NULL);
13153                 /* maybe passed 'foo' rather than '[.foo]', thus not
13154                    detected above */
13155                 hasdir = 1;
13156             } else {
13157                 /* Operate just on the directory, the special stat/fstat for */
13158                 /* leaves the fileified  specification in the st_devnam */
13159                 /* member. */
13160                 wilddsc.dsc$a_pointer = st.st_devnam;
13161                 ok = 1;
13162             }
13163 	}
13164 	else {
13165 	    wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13166 	    ok = (wilddsc.dsc$a_pointer != NULL);
13167 	}
13168 	if (ok)
13169 	    wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13170 
13171 	/* If not extended character set, replace ? with % */
13172 	/* With extended character set, ? is a wildcard single character */
13173 	for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13174 	    if (*cp == '?') {
13175                 wildquery = 1;
13176                 if (!decc_efs_charset)
13177                     *cp = '%';
13178             } else if (*cp == '%') {
13179                 wildquery = 1;
13180             } else if (*cp == '*') {
13181                 wildstar = 1;
13182             }
13183 	}
13184 
13185         if (ok) {
13186             wv_sts = vms_split_path(
13187                 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13188                 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13189                 &wvs_spec, &wvs_len);
13190         } else {
13191             wn_spec = NULL;
13192             wn_len = 0;
13193             we_spec = NULL;
13194             we_len = 0;
13195         }
13196 
13197 	sts = SS$_NORMAL;
13198 	while (ok && $VMS_STATUS_SUCCESS(sts)) {
13199 	 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13200 	 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13201          int valid_find;
13202 
13203             valid_find = 0;
13204 	    sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13205 				&dfltdsc,NULL,&rms_sts,&lff_flags);
13206 	    if (!$VMS_STATUS_SUCCESS(sts))
13207 		break;
13208 
13209 	    /* with varying string, 1st word of buffer contains result length */
13210 	    rstr[rslt->length] = '\0';
13211 
13212 	     /* Find where all the components are */
13213 	     v_sts = vms_split_path
13214 		       (rstr,
13215 			&v_spec,
13216 			&v_len,
13217 			&r_spec,
13218 			&r_len,
13219 			&d_spec,
13220 			&d_len,
13221 			&n_spec,
13222 			&n_len,
13223 			&e_spec,
13224 			&e_len,
13225 			&vs_spec,
13226 			&vs_len);
13227 
13228 	    /* If no version on input, truncate the version on output */
13229 	    if (!hasver && (vs_len > 0)) {
13230 		*vs_spec = '\0';
13231 		vs_len = 0;
13232             }
13233 
13234             if (isunix) {
13235 
13236                 /* In Unix report mode, remove the ".dir;1" from the name */
13237                 /* if it is a real directory */
13238                 if (decc_filename_unix_report && decc_efs_charset) {
13239                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13240                         Stat_t statbuf;
13241                         int ret_sts;
13242 
13243                         ret_sts = flex_lstat(rstr, &statbuf);
13244                         if ((ret_sts == 0) &&
13245                             S_ISDIR(statbuf.st_mode)) {
13246                             e_len = 0;
13247                             e_spec[0] = 0;
13248                         }
13249                     }
13250                 }
13251 
13252 		/* No version & a null extension on UNIX handling */
13253 		if ((e_len == 1) && decc_readdir_dropdotnotype) {
13254 		    e_len = 0;
13255 		    *e_spec = '\0';
13256 		}
13257 	    }
13258 
13259 	    if (!decc_efs_case_preserve) {
13260 	        for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13261 	    }
13262 
13263             /* Find File treats a Null extension as return all extensions */
13264             /* This is contrary to Perl expectations */
13265 
13266             if (wildstar || wildquery || vms_old_glob) {
13267                 /* really need to see if the returned file name matched */
13268                 /* but for now will assume that it matches */
13269                 valid_find = 1;
13270             } else {
13271                 /* Exact Match requested */
13272                 /* How are directories handled? - like a file */
13273                 if ((e_len == we_len) && (n_len == wn_len)) {
13274                     int t1;
13275                     t1 = e_len;
13276                     if (t1 > 0)
13277                         t1 = strncmp(e_spec, we_spec, e_len);
13278                     if (t1 == 0) {
13279                        t1 = n_len;
13280                        if (t1 > 0)
13281                            t1 = strncmp(n_spec, we_spec, n_len);
13282                        if (t1 == 0)
13283                            valid_find = 1;
13284                     }
13285                 }
13286             }
13287 
13288             if (valid_find) {
13289 	        found++;
13290 
13291 	        if (hasdir) {
13292 		    if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13293 		    begin = rstr;
13294 	        }
13295 	        else {
13296 		    /* Start with the name */
13297 		    begin = n_spec;
13298 	        }
13299 	        strcat(begin,"\n");
13300 	        ok = (PerlIO_puts(tmpfp,begin) != EOF);
13301             }
13302 	}
13303 	if (cxt) (void)lib$find_file_end(&cxt);
13304 
13305 	if (!found) {
13306 	    /* Be POSIXish: return the input pattern when no matches */
13307 	    my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13308 	    strcat(rstr,"\n");
13309 	    ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13310 	}
13311 
13312 	if (ok && sts != RMS$_NMF &&
13313 	    sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13314 	if (!ok) {
13315 	    if (!(sts & 1)) {
13316 		SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13317 	    }
13318 	    PerlIO_close(tmpfp);
13319 	    fp = NULL;
13320 	}
13321 	else {
13322 	    PerlIO_rewind(tmpfp);
13323 	    IoTYPE(io) = IoTYPE_RDONLY;
13324 	    IoIFP(io) = fp = tmpfp;
13325 	    IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
13326 	}
13327     }
13328     Safefree(vmsspec);
13329     Safefree(rslt);
13330     return fp;
13331 }
13332 
13333 
13334 static char *
13335 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13336 		   int *utf8_fl);
13337 
13338 void
13339 unixrealpath_fromperl(pTHX_ CV *cv)
13340 {
13341     dXSARGS;
13342     char *fspec, *rslt_spec, *rslt;
13343     STRLEN n_a;
13344 
13345     if (!items || items != 1)
13346 	Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13347 
13348     fspec = SvPV(ST(0),n_a);
13349     if (!fspec || !*fspec) XSRETURN_UNDEF;
13350 
13351     Newx(rslt_spec, VMS_MAXRSS + 1, char);
13352     rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13353 
13354     ST(0) = sv_newmortal();
13355     if (rslt != NULL)
13356 	sv_usepvn(ST(0),rslt,strlen(rslt));
13357     else
13358 	Safefree(rslt_spec);
13359 	XSRETURN(1);
13360 }
13361 
13362 static char *
13363 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13364 		   int *utf8_fl);
13365 
13366 void
13367 vmsrealpath_fromperl(pTHX_ CV *cv)
13368 {
13369     dXSARGS;
13370     char *fspec, *rslt_spec, *rslt;
13371     STRLEN n_a;
13372 
13373     if (!items || items != 1)
13374 	Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13375 
13376     fspec = SvPV(ST(0),n_a);
13377     if (!fspec || !*fspec) XSRETURN_UNDEF;
13378 
13379     Newx(rslt_spec, VMS_MAXRSS + 1, char);
13380     rslt = do_vms_realname(fspec, rslt_spec, NULL);
13381 
13382     ST(0) = sv_newmortal();
13383     if (rslt != NULL)
13384 	sv_usepvn(ST(0),rslt,strlen(rslt));
13385     else
13386 	Safefree(rslt_spec);
13387 	XSRETURN(1);
13388 }
13389 
13390 #ifdef HAS_SYMLINK
13391 /*
13392  * A thin wrapper around decc$symlink to make sure we follow the
13393  * standard and do not create a symlink with a zero-length name,
13394  * and convert the target to Unix format, as the CRTL can't handle
13395  * targets in VMS format.
13396  */
13397 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13398 int
13399 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13400 {
13401     int sts;
13402     char * utarget;
13403 
13404     if (!link_name || !*link_name) {
13405       SETERRNO(ENOENT, SS$_NOSUCHFILE);
13406       return -1;
13407     }
13408 
13409     utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13410     /* An untranslatable filename should be passed through. */
13411     (void) int_tounixspec(contents, utarget, NULL);
13412     sts = symlink(utarget, link_name);
13413     PerlMem_free(utarget);
13414     return sts;
13415 }
13416 /*}}}*/
13417 
13418 #endif /* HAS_SYMLINK */
13419 
13420 int do_vms_case_tolerant(void);
13421 
13422 void
13423 case_tolerant_process_fromperl(pTHX_ CV *cv)
13424 {
13425   dXSARGS;
13426   ST(0) = boolSV(do_vms_case_tolerant());
13427   XSRETURN(1);
13428 }
13429 
13430 #ifdef USE_ITHREADS
13431 
13432 void
13433 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13434                           struct interp_intern *dst)
13435 {
13436     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13437 
13438     memcpy(dst,src,sizeof(struct interp_intern));
13439 }
13440 
13441 #endif
13442 
13443 void
13444 Perl_sys_intern_clear(pTHX)
13445 {
13446 }
13447 
13448 void
13449 Perl_sys_intern_init(pTHX)
13450 {
13451     unsigned int ix = RAND_MAX;
13452     double x;
13453 
13454     VMSISH_HUSHED = 0;
13455 
13456     MY_POSIX_EXIT = vms_posix_exit;
13457 
13458     x = (float)ix;
13459     MY_INV_RAND_MAX = 1./x;
13460 }
13461 
13462 void
13463 init_os_extras(void)
13464 {
13465   dTHX;
13466   char* file = __FILE__;
13467   if (decc_disable_to_vms_logname_translation) {
13468     no_translate_barewords = TRUE;
13469   } else {
13470     no_translate_barewords = FALSE;
13471   }
13472 
13473   newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13474   newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13475   newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13476   newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13477   newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13478   newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13479   newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13480   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13481   newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13482   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13483   newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13484   newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13485   newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13486   newXSproto("VMS::Filespec::case_tolerant_process",
13487       case_tolerant_process_fromperl,file,"");
13488 
13489   store_pipelocs(aTHX);         /* will redo any earlier attempts */
13490 
13491   return;
13492 }
13493 
13494 #if __CRTL_VER == 80200000
13495 /* This missed getting in to the DECC SDK for 8.2 */
13496 char *realpath(const char *file_name, char * resolved_name, ...);
13497 #endif
13498 
13499 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13500 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13501  * The perl fallback routine to provide realpath() is not as efficient
13502  * on OpenVMS.
13503  */
13504 
13505 #ifdef __cplusplus
13506 extern "C" {
13507 #endif
13508 
13509 /* Hack, use old stat() as fastest way of getting ino_t and device */
13510 int decc$stat(const char *name, void * statbuf);
13511 #if !defined(__VAX) && __CRTL_VER >= 80200000
13512 int decc$lstat(const char *name, void * statbuf);
13513 #else
13514 #define decc$lstat decc$stat
13515 #endif
13516 
13517 #ifdef __cplusplus
13518 }
13519 #endif
13520 
13521 
13522 /* Realpath is fragile.  In 8.3 it does not work if the feature
13523  * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13524  * links are implemented in RMS, not the CRTL. It also can fail if the
13525  * user does not have read/execute access to some of the directories.
13526  * So in order for Do What I Mean mode to work, if realpath() fails,
13527  * fall back to looking up the filename by the device name and FID.
13528  */
13529 
13530 int vms_fid_to_name(char * outname, int outlen,
13531                     const char * name, int lstat_flag, mode_t * mode)
13532 {
13533 #pragma message save
13534 #pragma message disable MISALGNDSTRCT
13535 #pragma message disable MISALGNDMEM
13536 #pragma member_alignment save
13537 #pragma nomember_alignment
13538 struct statbuf_t {
13539     char	   * st_dev;
13540     unsigned short st_ino[3];
13541     unsigned short old_st_mode;
13542     unsigned long  padl[30];  /* plenty of room */
13543 } statbuf;
13544 #pragma message restore
13545 #pragma member_alignment restore
13546 
13547     int sts;
13548     struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13549     struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13550     char *fileified;
13551     char *temp_fspec;
13552     char *ret_spec;
13553 
13554     /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13555      * unexpected answers
13556      */
13557 
13558     fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13559     if (fileified == NULL)
13560         _ckvmssts_noperl(SS$_INSFMEM);
13561 
13562     temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13563     if (temp_fspec == NULL)
13564         _ckvmssts_noperl(SS$_INSFMEM);
13565 
13566     sts = -1;
13567     /* First need to try as a directory */
13568     ret_spec = int_tovmspath(name, temp_fspec, NULL);
13569     if (ret_spec != NULL) {
13570         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13571         if (ret_spec != NULL) {
13572             if (lstat_flag == 0)
13573                 sts = decc$stat(fileified, &statbuf);
13574             else
13575                 sts = decc$lstat(fileified, &statbuf);
13576         }
13577     }
13578 
13579     /* Then as a VMS file spec */
13580     if (sts != 0) {
13581         ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13582         if (ret_spec != NULL) {
13583             if (lstat_flag == 0) {
13584                 sts = decc$stat(temp_fspec, &statbuf);
13585             } else {
13586                 sts = decc$lstat(temp_fspec, &statbuf);
13587             }
13588         }
13589     }
13590 
13591     if (sts) {
13592         /* Next try - allow multiple dots with out EFS CHARSET */
13593         /* The CRTL stat() falls down hard on multi-dot filenames in unix
13594          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13595          * enable it if it isn't already.
13596          */
13597 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13598         if (!decc_efs_charset && (decc_efs_charset_index > 0))
13599             decc$feature_set_value(decc_efs_charset_index, 1, 1);
13600 #endif
13601         ret_spec = int_tovmspath(name, temp_fspec, NULL);
13602         if (lstat_flag == 0) {
13603             sts = decc$stat(name, &statbuf);
13604         } else {
13605             sts = decc$lstat(name, &statbuf);
13606         }
13607 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13608         if (!decc_efs_charset && (decc_efs_charset_index > 0))
13609             decc$feature_set_value(decc_efs_charset_index, 1, 0);
13610 #endif
13611     }
13612 
13613 
13614     /* and then because the Perl Unix to VMS conversion is not perfect */
13615     /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13616     /* characters from filenames so we need to try it as-is */
13617     if (sts) {
13618         if (lstat_flag == 0) {
13619             sts = decc$stat(name, &statbuf);
13620         } else {
13621             sts = decc$lstat(name, &statbuf);
13622         }
13623     }
13624 
13625     if (sts == 0) {
13626         int vms_sts;
13627 
13628 	dvidsc.dsc$a_pointer=statbuf.st_dev;
13629         dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13630 
13631 	specdsc.dsc$a_pointer = outname;
13632 	specdsc.dsc$w_length = outlen-1;
13633 
13634         vms_sts = lib$fid_to_name
13635 	    (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13636         if ($VMS_STATUS_SUCCESS(vms_sts)) {
13637 	    outname[specdsc.dsc$w_length] = 0;
13638 
13639             /* Return the mode */
13640             if (mode) {
13641                 *mode = statbuf.old_st_mode;
13642             }
13643 	}
13644     }
13645     PerlMem_free(temp_fspec);
13646     PerlMem_free(fileified);
13647     return sts;
13648 }
13649 
13650 
13651 
13652 static char *
13653 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13654 		   int *utf8_fl)
13655 {
13656     char * rslt = NULL;
13657 
13658 #ifdef HAS_SYMLINK
13659     if (decc_posix_compliant_pathnames > 0 ) {
13660 	/* realpath currently only works if posix compliant pathnames are
13661 	 * enabled.  It may start working when they are not, but in that
13662 	 * case we still want the fallback behavior for backwards compatibility
13663 	 */
13664         rslt = realpath(filespec, outbuf);
13665     }
13666 #endif
13667 
13668     if (rslt == NULL) {
13669         char * vms_spec;
13670         char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13671         int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13672         mode_t my_mode;
13673 
13674 	/* Fall back to fid_to_name */
13675 
13676         Newx(vms_spec, VMS_MAXRSS + 1, char);
13677 
13678 	sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13679 	if (sts == 0) {
13680 
13681 
13682 	    /* Now need to trim the version off */
13683 	    sts = vms_split_path
13684 		  (vms_spec,
13685 		   &v_spec,
13686 		   &v_len,
13687 		   &r_spec,
13688 		   &r_len,
13689 		   &d_spec,
13690 		   &d_len,
13691 		   &n_spec,
13692 		   &n_len,
13693 		   &e_spec,
13694 		   &e_len,
13695 		   &vs_spec,
13696 		   &vs_len);
13697 
13698 
13699 		if (sts == 0) {
13700 	            int haslower = 0;
13701 	            const char *cp;
13702 
13703 	            /* Trim off the version */
13704 	            int file_len = v_len + r_len + d_len + n_len + e_len;
13705 	            vms_spec[file_len] = 0;
13706 
13707 	            /* Trim off the .DIR if this is a directory */
13708 	            if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13709                         if (S_ISDIR(my_mode)) {
13710                             e_len = 0;
13711                             e_spec[0] = 0;
13712                         }
13713 	            }
13714 
13715 	            /* Drop NULL extensions on UNIX file specification */
13716 		    if ((e_len == 1) && decc_readdir_dropdotnotype) {
13717 			e_len = 0;
13718 			e_spec[0] = '\0';
13719 		    }
13720 
13721 	            /* The result is expected to be in UNIX format */
13722 		    rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13723 
13724                     /* Downcase if input had any lower case letters and
13725 	             * case preservation is not in effect.
13726 	             */
13727 	            if (!decc_efs_case_preserve) {
13728 	                for (cp = filespec; *cp; cp++)
13729 	                    if (islower(*cp)) { haslower = 1; break; }
13730 
13731 	                if (haslower) __mystrtolower(rslt);
13732 	            }
13733 	        }
13734 	} else {
13735 
13736 	    /* Now for some hacks to deal with backwards and forward */
13737 	    /* compatibility */
13738 	    if (!decc_efs_charset) {
13739 
13740 		/* 1. ODS-2 mode wants to do a syntax only translation */
13741 		rslt = int_rmsexpand(filespec, outbuf,
13742 				    NULL, 0, NULL, utf8_fl);
13743 
13744 	    } else {
13745 		if (decc_filename_unix_report) {
13746 		    char * dir_name;
13747 		    char * vms_dir_name;
13748 		    char * file_name;
13749 
13750 		    /* 2. ODS-5 / UNIX report mode should return a failure */
13751 		    /*    if the parent directory also does not exist */
13752 		    /*    Otherwise, get the real path for the parent */
13753 		    /*    and add the child to it. */
13754 
13755 		    /* basename / dirname only available for VMS 7.0+ */
13756 		    /* So we may need to implement them as common routines */
13757 
13758 		    Newx(dir_name, VMS_MAXRSS + 1, char);
13759 		    Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13760 		    dir_name[0] = '\0';
13761 		    file_name = NULL;
13762 
13763 		    /* First try a VMS parse */
13764 		    sts = vms_split_path
13765 			  (filespec,
13766 			   &v_spec,
13767 			   &v_len,
13768 			   &r_spec,
13769 			   &r_len,
13770 			   &d_spec,
13771 			   &d_len,
13772 			   &n_spec,
13773 			   &n_len,
13774 			   &e_spec,
13775 			   &e_len,
13776 			   &vs_spec,
13777 			   &vs_len);
13778 
13779 		    if (sts == 0) {
13780 			/* This is VMS */
13781 
13782 			int dir_len = v_len + r_len + d_len + n_len;
13783 			if (dir_len > 0) {
13784 			   memcpy(dir_name, filespec, dir_len);
13785 			   dir_name[dir_len] = '\0';
13786 			   file_name = (char *)&filespec[dir_len + 1];
13787 			}
13788 		    } else {
13789 			/* This must be UNIX */
13790 			char * tchar;
13791 
13792 			tchar = strrchr(filespec, '/');
13793 
13794 			if (tchar != NULL) {
13795 			    int dir_len = tchar - filespec;
13796 			    memcpy(dir_name, filespec, dir_len);
13797 			    dir_name[dir_len] = '\0';
13798 			    file_name = (char *) &filespec[dir_len + 1];
13799 			}
13800 		    }
13801 
13802 		    /* Dir name is defaulted */
13803 		    if (dir_name[0] == 0) {
13804 			dir_name[0] = '.';
13805 			dir_name[1] = '\0';
13806 		    }
13807 
13808 		    /* Need realpath for the directory */
13809 		    sts = vms_fid_to_name(vms_dir_name,
13810 					  VMS_MAXRSS + 1,
13811 					  dir_name, 0, NULL);
13812 
13813 		    if (sts == 0) {
13814 		        /* Now need to pathify it. */
13815 		        char *tdir = int_pathify_dirspec(vms_dir_name,
13816 							 outbuf);
13817 
13818 			/* And now add the original filespec to it */
13819 			if (file_name != NULL) {
13820 			    my_strlcat(outbuf, file_name, VMS_MAXRSS);
13821 			}
13822 			return outbuf;
13823 		    }
13824 		    Safefree(vms_dir_name);
13825 		    Safefree(dir_name);
13826 		}
13827             }
13828         }
13829         Safefree(vms_spec);
13830     }
13831     return rslt;
13832 }
13833 
13834 static char *
13835 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13836 		   int *utf8_fl)
13837 {
13838     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13839     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13840 
13841     /* Fall back to fid_to_name */
13842 
13843     sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13844     if (sts != 0) {
13845 	return NULL;
13846     }
13847     else {
13848 
13849 
13850 	/* Now need to trim the version off */
13851 	sts = vms_split_path
13852 		  (outbuf,
13853 		   &v_spec,
13854 		   &v_len,
13855 		   &r_spec,
13856 		   &r_len,
13857 		   &d_spec,
13858 		   &d_len,
13859 		   &n_spec,
13860 		   &n_len,
13861 		   &e_spec,
13862 		   &e_len,
13863 		   &vs_spec,
13864 		   &vs_len);
13865 
13866 
13867 	if (sts == 0) {
13868 	    int haslower = 0;
13869 	    const char *cp;
13870 
13871 	    /* Trim off the version */
13872 	    int file_len = v_len + r_len + d_len + n_len + e_len;
13873 	    outbuf[file_len] = 0;
13874 
13875 	    /* Downcase if input had any lower case letters and
13876 	     * case preservation is not in effect.
13877 	     */
13878 	    if (!decc_efs_case_preserve) {
13879 	        for (cp = filespec; *cp; cp++)
13880 	            if (islower(*cp)) { haslower = 1; break; }
13881 
13882 	        if (haslower) __mystrtolower(outbuf);
13883 	    }
13884 	}
13885     }
13886     return outbuf;
13887 }
13888 
13889 
13890 /*}}}*/
13891 /* External entry points */
13892 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13893 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13894 
13895 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13896 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13897 
13898 /* case_tolerant */
13899 
13900 /*{{{int do_vms_case_tolerant(void)*/
13901 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13902  * controlled by a process setting.
13903  */
13904 int do_vms_case_tolerant(void)
13905 {
13906     return vms_process_case_tolerant;
13907 }
13908 /*}}}*/
13909 /* External entry points */
13910 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13911 int Perl_vms_case_tolerant(void)
13912 { return do_vms_case_tolerant(); }
13913 #else
13914 int Perl_vms_case_tolerant(void)
13915 { return vms_process_case_tolerant; }
13916 #endif
13917 
13918 
13919  /* Start of DECC RTL Feature handling */
13920 
13921 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13922 
13923 static int
13924 set_feature_default(const char *name, int value)
13925 {
13926     int status;
13927     int index;
13928     char val_str[10];
13929 
13930     /* If the feature has been explicitly disabled in the environment,
13931      * then don't enable it here.
13932      */
13933     if (value > 0) {
13934         status = simple_trnlnm(name, val_str, sizeof(val_str));
13935         if (status) {
13936             val_str[0] = _toupper(val_str[0]);
13937             if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13938 	        return 0;
13939         }
13940     }
13941 
13942     index = decc$feature_get_index(name);
13943 
13944     status = decc$feature_set_value(index, 1, value);
13945     if (index == -1 || (status == -1)) {
13946       return -1;
13947     }
13948 
13949     status = decc$feature_get_value(index, 1);
13950     if (status != value) {
13951       return -1;
13952     }
13953 
13954     /* Various things may check for an environment setting
13955      * rather than the feature directly, so set that too.
13956      */
13957     vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13958 
13959     return 0;
13960 }
13961 #endif
13962 
13963 
13964 /* C RTL Feature settings */
13965 
13966 #if defined(__DECC) || defined(__DECCXX)
13967 
13968 #ifdef __cplusplus
13969 extern "C" {
13970 #endif
13971 
13972 extern void
13973 vmsperl_set_features(void)
13974 {
13975     int status;
13976     int s;
13977     char val_str[10];
13978 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13979     const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13980     const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13981     unsigned long case_perm;
13982     unsigned long case_image;
13983 #endif
13984 
13985     /* Allow an exception to bring Perl into the VMS debugger */
13986     vms_debug_on_exception = 0;
13987     status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13988     if (status) {
13989        val_str[0] = _toupper(val_str[0]);
13990        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13991 	 vms_debug_on_exception = 1;
13992        else
13993 	 vms_debug_on_exception = 0;
13994     }
13995 
13996     /* Debug unix/vms file translation routines */
13997     vms_debug_fileify = 0;
13998     status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13999     if (status) {
14000 	val_str[0] = _toupper(val_str[0]);
14001         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14002 	    vms_debug_fileify = 1;
14003         else
14004 	    vms_debug_fileify = 0;
14005     }
14006 
14007 
14008     /* Historically PERL has been doing vmsify / stat differently than */
14009     /* the CRTL.  In particular, under some conditions the CRTL will   */
14010     /* remove some illegal characters like spaces from filenames       */
14011     /* resulting in some differences.  The stat()/lstat() wrapper has  */
14012     /* been reporting such file names as invalid and fails to stat them */
14013     /* fixing this bug so that stat()/lstat() accept these like the     */
14014     /* CRTL does will result in several tests failing.                  */
14015     /* This should really be fixed, but for now, set up a feature to    */
14016     /* enable it so that the impact can be studied.                     */
14017     vms_bug_stat_filename = 0;
14018     status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14019     if (status) {
14020 	val_str[0] = _toupper(val_str[0]);
14021         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14022 	    vms_bug_stat_filename = 1;
14023         else
14024 	    vms_bug_stat_filename = 0;
14025     }
14026 
14027 
14028     /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14029     vms_vtf7_filenames = 0;
14030     status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14031     if (status) {
14032        val_str[0] = _toupper(val_str[0]);
14033        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14034 	 vms_vtf7_filenames = 1;
14035        else
14036 	 vms_vtf7_filenames = 0;
14037     }
14038 
14039     /* unlink all versions on unlink() or rename() */
14040     vms_unlink_all_versions = 0;
14041     status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14042     if (status) {
14043        val_str[0] = _toupper(val_str[0]);
14044        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14045 	 vms_unlink_all_versions = 1;
14046        else
14047 	 vms_unlink_all_versions = 0;
14048     }
14049 
14050 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14051     /* Detect running under GNV Bash or other UNIX like shell */
14052     gnv_unix_shell = 0;
14053     status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14054     if (status) {
14055 	 gnv_unix_shell = 1;
14056 	 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14057 	 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14058 	 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14059 	 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14060 	 vms_unlink_all_versions = 1;
14061 	 vms_posix_exit = 1;
14062     }
14063     /* Some reasonable defaults that are not CRTL defaults */
14064     set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14065     set_feature_default("DECC$ARGV_PARSE_STYLE", 1);     /* Requires extended parse. */
14066     set_feature_default("DECC$EFS_CHARSET", 1);
14067 #endif
14068 
14069     /* hacks to see if known bugs are still present for testing */
14070 
14071     /* PCP mode requires creating /dev/null special device file */
14072     decc_bug_devnull = 0;
14073     status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14074     if (status) {
14075        val_str[0] = _toupper(val_str[0]);
14076        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14077           decc_bug_devnull = 1;
14078        else
14079 	  decc_bug_devnull = 0;
14080     }
14081 
14082 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14083     s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14084     if (s >= 0) {
14085 	decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14086 	if (decc_disable_to_vms_logname_translation < 0)
14087 	    decc_disable_to_vms_logname_translation = 0;
14088     }
14089 
14090     s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14091     if (s >= 0) {
14092 	decc_efs_case_preserve = decc$feature_get_value(s, 1);
14093 	if (decc_efs_case_preserve < 0)
14094 	    decc_efs_case_preserve = 0;
14095     }
14096 
14097     s = decc$feature_get_index("DECC$EFS_CHARSET");
14098     decc_efs_charset_index = s;
14099     if (s >= 0) {
14100 	decc_efs_charset = decc$feature_get_value(s, 1);
14101 	if (decc_efs_charset < 0)
14102 	    decc_efs_charset = 0;
14103     }
14104 
14105     s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14106     if (s >= 0) {
14107 	decc_filename_unix_report = decc$feature_get_value(s, 1);
14108 	if (decc_filename_unix_report > 0) {
14109 	    decc_filename_unix_report = 1;
14110 	    vms_posix_exit = 1;
14111 	}
14112 	else
14113 	    decc_filename_unix_report = 0;
14114     }
14115 
14116     s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14117     if (s >= 0) {
14118 	decc_filename_unix_only = decc$feature_get_value(s, 1);
14119 	if (decc_filename_unix_only > 0) {
14120 	    decc_filename_unix_only = 1;
14121 	}
14122 	else {
14123 	    decc_filename_unix_only = 0;
14124 	}
14125     }
14126 
14127     s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14128     if (s >= 0) {
14129 	decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14130 	if (decc_filename_unix_no_version < 0)
14131 	    decc_filename_unix_no_version = 0;
14132     }
14133 
14134     s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14135     if (s >= 0) {
14136 	decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14137 	if (decc_readdir_dropdotnotype < 0)
14138 	    decc_readdir_dropdotnotype = 0;
14139     }
14140 
14141 #if __CRTL_VER >= 80200000
14142     s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14143     if (s >= 0) {
14144 	decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14145 	if (decc_posix_compliant_pathnames < 0)
14146 	    decc_posix_compliant_pathnames = 0;
14147 	if (decc_posix_compliant_pathnames > 4)
14148 	    decc_posix_compliant_pathnames = 0;
14149     }
14150 
14151 #endif
14152 #else
14153     status = simple_trnlnm
14154 	("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14155     if (status) {
14156 	val_str[0] = _toupper(val_str[0]);
14157 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14158 	   decc_disable_to_vms_logname_translation = 1;
14159 	}
14160     }
14161 
14162 #ifndef __VAX
14163     status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14164     if (status) {
14165 	val_str[0] = _toupper(val_str[0]);
14166 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14167 	   decc_efs_case_preserve = 1;
14168 	}
14169     }
14170 #endif
14171 
14172     status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14173     if (status) {
14174 	val_str[0] = _toupper(val_str[0]);
14175 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14176 	   decc_filename_unix_report = 1;
14177 	}
14178     }
14179     status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14180     if (status) {
14181 	val_str[0] = _toupper(val_str[0]);
14182 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14183 	   decc_filename_unix_only = 1;
14184 	   decc_filename_unix_report = 1;
14185 	}
14186     }
14187     status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14188     if (status) {
14189 	val_str[0] = _toupper(val_str[0]);
14190 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14191 	   decc_filename_unix_no_version = 1;
14192 	}
14193     }
14194     status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14195     if (status) {
14196 	val_str[0] = _toupper(val_str[0]);
14197 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14198 	   decc_readdir_dropdotnotype = 1;
14199 	}
14200     }
14201 #endif
14202 
14203 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14204 
14205      /* Report true case tolerance */
14206     /*----------------------------*/
14207     status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14208     if (!$VMS_STATUS_SUCCESS(status))
14209 	case_perm = PPROP$K_CASE_BLIND;
14210     status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14211     if (!$VMS_STATUS_SUCCESS(status))
14212 	case_image = PPROP$K_CASE_BLIND;
14213     if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14214 	(case_image == PPROP$K_CASE_SENSITIVE))
14215 	vms_process_case_tolerant = 0;
14216 
14217 #endif
14218 
14219     /* USE POSIX/DCL Exit codes - Recommended, but needs to default to  */
14220     /* for strict backward compatibility */
14221     status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14222     if (status) {
14223        val_str[0] = _toupper(val_str[0]);
14224        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14225 	 vms_posix_exit = 1;
14226        else
14227 	 vms_posix_exit = 0;
14228     }
14229 }
14230 
14231 /* Use 32-bit pointers because that's what the image activator
14232  * assumes for the LIB$INITIALZE psect.
14233  */
14234 #if __INITIAL_POINTER_SIZE
14235 #pragma pointer_size save
14236 #pragma pointer_size 32
14237 #endif
14238 
14239 /* Create a reference to the LIB$INITIALIZE function. */
14240 extern void LIB$INITIALIZE(void);
14241 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14242 
14243 /* Create an array of pointers to the init functions in the special
14244  * LIB$INITIALIZE section. In our case, the array only has one entry.
14245  */
14246 #pragma extern_model save
14247 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14248 extern void (* const vmsperl_unused_global_2[])() =
14249 {
14250    vmsperl_set_features,
14251 };
14252 #pragma extern_model restore
14253 
14254 #if __INITIAL_POINTER_SIZE
14255 #pragma pointer_size restore
14256 #endif
14257 
14258 #ifdef __cplusplus
14259 }
14260 #endif
14261 
14262 #endif /* defined(__DECC) || defined(__DECCXX) */
14263 /*  End of vms.c */
14264