xref: /openbsd-src/gnu/usr.bin/perl/vms/vms.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
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 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
882 int
883 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
884   struct dsc$descriptor_s **tabvec, unsigned long int flags)
885 {
886     const char *cp1;
887     char uplnm[LNM$C_NAMLENGTH+1], *cp2;
888     unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
889     unsigned long int retsts, attr = LNM$M_CASE_BLIND;
890     int midx;
891     unsigned char acmode;
892     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
893                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
894     struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
895                                  {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
896                                  {0, 0, 0, 0}};
897     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
898 #if defined(PERL_IMPLICIT_CONTEXT)
899     pTHX = NULL;
900     if (PL_curinterp) {
901       aTHX = PERL_GET_INTERP;
902     } else {
903       aTHX = NULL;
904     }
905 #endif
906 
907     if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
908       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
909     }
910     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
911       *cp2 = _toupper(*cp1);
912       if (cp1 - lnm > LNM$C_NAMLENGTH) {
913         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
914         return 0;
915       }
916     }
917     lnmdsc.dsc$w_length = cp1 - lnm;
918     lnmdsc.dsc$a_pointer = uplnm;
919     uplnm[lnmdsc.dsc$w_length] = '\0';
920     secure = flags & PERL__TRNENV_SECURE;
921     acmode = secure ? PSL$C_EXEC : PSL$C_USER;
922     if (!tabvec || !*tabvec) tabvec = env_tables;
923 
924     for (curtab = 0; tabvec[curtab]; curtab++) {
925       if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
926         if (!ivenv && !secure) {
927           char *eq;
928           int i;
929           if (!environ) {
930             ivenv = 1;
931 #if defined(PERL_IMPLICIT_CONTEXT)
932             if (aTHX == NULL) {
933                 fprintf(stderr,
934                     "Can't read CRTL environ\n");
935             } else
936 #endif
937                 Perl_warn(aTHX_ "Can't read CRTL environ\n");
938             continue;
939           }
940           retsts = SS$_NOLOGNAM;
941           for (i = 0; environ[i]; i++) {
942             if ((eq = strchr(environ[i],'=')) &&
943                 lnmdsc.dsc$w_length == (eq - environ[i]) &&
944                 !strncmp(environ[i],uplnm,eq - environ[i])) {
945               eq++;
946               for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
947               if (!eqvlen) continue;
948               retsts = SS$_NORMAL;
949               break;
950             }
951           }
952           if (retsts != SS$_NOLOGNAM) break;
953         }
954       }
955       else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
956                !str$case_blind_compare(&tmpdsc,&clisym)) {
957         if (!ivsym && !secure) {
958           unsigned short int deflen = LNM$C_NAMLENGTH;
959           struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
960           /* dynamic dsc to accommodate possible long value */
961           _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
962           retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
963           if (retsts & 1) {
964             if (eqvlen > MAX_DCL_SYMBOL) {
965               set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
966               eqvlen = MAX_DCL_SYMBOL;
967 	      /* Special hack--we might be called before the interpreter's */
968 	      /* fully initialized, in which case either thr or PL_curcop */
969 	      /* might be bogus. We have to check, since ckWARN needs them */
970 	      /* both to be valid if running threaded */
971 #if defined(PERL_IMPLICIT_CONTEXT)
972               if (aTHX == NULL) {
973                   fprintf(stderr,
974                      "Value of CLI symbol \"%s\" too long",lnm);
975               } else
976 #endif
977 		if (ckWARN(WARN_MISC)) {
978 		  Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
979 		}
980             }
981             strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
982           }
983           _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
984           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
985           if (retsts == LIB$_NOSUCHSYM) continue;
986           break;
987         }
988       }
989       else if (!ivlnm) {
990         if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
991           midx = my_maxidx(lnm);
992           for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
993             lnmlst[1].bufadr = cp2;
994             eqvlen = 0;
995             retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
996             if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
997             if (retsts == SS$_NOLOGNAM) break;
998             /* PPFs have a prefix */
999             if (
1000 #if INTSIZE == 4
1001                  *((int *)uplnm) == *((int *)"SYS$")                    &&
1002 #endif
1003                  eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00        &&
1004                  ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT"))  ||
1005                    (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT"))   ||
1006                    (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR"))   ||
1007                    (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) )  ) {
1008               memmove(eqv,eqv+4,eqvlen-4);
1009               eqvlen -= 4;
1010             }
1011             cp2 += eqvlen;
1012             *cp2 = '\0';
1013           }
1014           if ((retsts == SS$_IVLOGNAM) ||
1015               (retsts == SS$_NOLOGNAM)) { continue; }
1016         }
1017         else {
1018           retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1019           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1020           if (retsts == SS$_NOLOGNAM) continue;
1021           eqv[eqvlen] = '\0';
1022         }
1023         eqvlen = strlen(eqv);
1024         break;
1025       }
1026     }
1027     if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1028     else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1029              retsts == SS$_IVLOGNAM   || retsts == SS$_IVLOGTAB   ||
1030              retsts == SS$_NOLOGNAM) {
1031       set_errno(EINVAL);  set_vaxc_errno(retsts);
1032     }
1033     else _ckvmssts_noperl(retsts);
1034     return 0;
1035 }  /* end of vmstrnenv */
1036 /*}}}*/
1037 
1038 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1039 /* Define as a function so we can access statics. */
1040 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1041 {
1042     int flags = 0;
1043 
1044 #if defined(PERL_IMPLICIT_CONTEXT)
1045     if (aTHX != NULL)
1046 #endif
1047 #ifdef SECURE_INTERNAL_GETENV
1048         flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1049                  PERL__TRNENV_SECURE : 0;
1050 #endif
1051 
1052     return vmstrnenv(lnm, eqv, idx, fildev, flags);
1053 }
1054 /*}}}*/
1055 
1056 /* my_getenv
1057  * Note: Uses Perl temp to store result so char * can be returned to
1058  * caller; this pointer will be invalidated at next Perl statement
1059  * transition.
1060  * We define this as a function rather than a macro in terms of my_getenv_len()
1061  * so that it'll work when PL_curinterp is undefined (and we therefore can't
1062  * allocate SVs).
1063  */
1064 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1065 char *
1066 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1067 {
1068     const char *cp1;
1069     static char *__my_getenv_eqv = NULL;
1070     char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1071     unsigned long int idx = 0;
1072     int success, secure, saverr, savvmserr;
1073     int midx, flags;
1074     SV *tmpsv;
1075 
1076     midx = my_maxidx(lnm) + 1;
1077 
1078     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1079       /* Set up a temporary buffer for the return value; Perl will
1080        * clean it up at the next statement transition */
1081       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1082       if (!tmpsv) return NULL;
1083       eqv = SvPVX(tmpsv);
1084     }
1085     else {
1086       /* Assume no interpreter ==> single thread */
1087       if (__my_getenv_eqv != NULL) {
1088         Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1089       }
1090       else {
1091         Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1092       }
1093       eqv = __my_getenv_eqv;
1094     }
1095 
1096     for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1097     if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1098       int len;
1099       getcwd(eqv,LNM$C_NAMLENGTH);
1100 
1101       len = strlen(eqv);
1102 
1103       /* Get rid of "000000/ in rooted filespecs */
1104       if (len > 7) {
1105         char * zeros;
1106 	zeros = strstr(eqv, "/000000/");
1107 	if (zeros != NULL) {
1108 	  int mlen;
1109 	  mlen = len - (zeros - eqv) - 7;
1110 	  memmove(zeros, &zeros[7], mlen);
1111 	  len = len - 7;
1112 	  eqv[len] = '\0';
1113 	}
1114       }
1115       return eqv;
1116     }
1117     else {
1118       /* Impose security constraints only if tainting */
1119       if (sys) {
1120         /* Impose security constraints only if tainting */
1121         secure = PL_curinterp ? TAINTING_get : will_taint;
1122         saverr = errno;  savvmserr = vaxc$errno;
1123       }
1124       else {
1125         secure = 0;
1126       }
1127 
1128       flags =
1129 #ifdef SECURE_INTERNAL_GETENV
1130               secure ? PERL__TRNENV_SECURE : 0
1131 #else
1132               0
1133 #endif
1134       ;
1135 
1136       /* For the getenv interface we combine all the equivalence names
1137        * of a search list logical into one value to acquire a maximum
1138        * value length of 255*128 (assuming %ENV is using logicals).
1139        */
1140       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1141 
1142       /* If the name contains a semicolon-delimited index, parse it
1143        * off and make sure we only retrieve the equivalence name for
1144        * that index.  */
1145       if ((cp2 = strchr(lnm,';')) != NULL) {
1146         my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1147         idx = strtoul(cp2+1,NULL,0);
1148         lnm = uplnm;
1149         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1150       }
1151 
1152       success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1153 
1154       /* Discard NOLOGNAM on internal calls since we're often looking
1155        * for an optional name, and this "error" often shows up as the
1156        * (bogus) exit status for a die() call later on.  */
1157       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1158       return success ? eqv : NULL;
1159     }
1160 
1161 }  /* end of my_getenv() */
1162 /*}}}*/
1163 
1164 
1165 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1166 char *
1167 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1168 {
1169     const char *cp1;
1170     char *buf, *cp2;
1171     unsigned long idx = 0;
1172     int midx, flags;
1173     static char *__my_getenv_len_eqv = NULL;
1174     int secure, saverr, savvmserr;
1175     SV *tmpsv;
1176 
1177     midx = my_maxidx(lnm) + 1;
1178 
1179     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1180       /* Set up a temporary buffer for the return value; Perl will
1181        * clean it up at the next statement transition */
1182       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1183       if (!tmpsv) return NULL;
1184       buf = SvPVX(tmpsv);
1185     }
1186     else {
1187       /* Assume no interpreter ==> single thread */
1188       if (__my_getenv_len_eqv != NULL) {
1189         Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1190       }
1191       else {
1192         Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1193       }
1194       buf = __my_getenv_len_eqv;
1195     }
1196 
1197     for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1198     if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1199     char * zeros;
1200 
1201       getcwd(buf,LNM$C_NAMLENGTH);
1202       *len = strlen(buf);
1203 
1204       /* Get rid of "000000/ in rooted filespecs */
1205       if (*len > 7) {
1206       zeros = strstr(buf, "/000000/");
1207       if (zeros != NULL) {
1208 	int mlen;
1209 	mlen = *len - (zeros - buf) - 7;
1210 	memmove(zeros, &zeros[7], mlen);
1211 	*len = *len - 7;
1212 	buf[*len] = '\0';
1213 	}
1214       }
1215       return buf;
1216     }
1217     else {
1218       if (sys) {
1219         /* Impose security constraints only if tainting */
1220         secure = PL_curinterp ? TAINTING_get : will_taint;
1221         saverr = errno;  savvmserr = vaxc$errno;
1222       }
1223       else {
1224         secure = 0;
1225       }
1226 
1227       flags =
1228 #ifdef SECURE_INTERNAL_GETENV
1229               secure ? PERL__TRNENV_SECURE : 0
1230 #else
1231               0
1232 #endif
1233       ;
1234 
1235       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1236 
1237       if ((cp2 = strchr(lnm,';')) != NULL) {
1238         my_strlcpy(buf, lnm, cp2 - lnm + 1);
1239         idx = strtoul(cp2+1,NULL,0);
1240         lnm = buf;
1241         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1242       }
1243 
1244       *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1245 
1246       /* Get rid of "000000/ in rooted filespecs */
1247       if (*len > 7) {
1248       char * zeros;
1249 	zeros = strstr(buf, "/000000/");
1250 	if (zeros != NULL) {
1251 	  int mlen;
1252 	  mlen = *len - (zeros - buf) - 7;
1253 	  memmove(zeros, &zeros[7], mlen);
1254 	  *len = *len - 7;
1255 	  buf[*len] = '\0';
1256 	}
1257       }
1258 
1259       /* Discard NOLOGNAM on internal calls since we're often looking
1260        * for an optional name, and this "error" often shows up as the
1261        * (bogus) exit status for a die() call later on.  */
1262       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1263       return *len ? buf : NULL;
1264     }
1265 
1266 }  /* end of my_getenv_len() */
1267 /*}}}*/
1268 
1269 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1270 
1271 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1272 
1273 /*{{{ void prime_env_iter() */
1274 void
1275 prime_env_iter(void)
1276 /* Fill the %ENV associative array with all logical names we can
1277  * find, in preparation for iterating over it.
1278  */
1279 {
1280   static int primed = 0;
1281   HV *seenhv = NULL, *envhv;
1282   SV *sv = NULL;
1283   char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1284   unsigned short int chan;
1285 #ifndef CLI$M_TRUSTED
1286 #  define CLI$M_TRUSTED 0x40  /* Missing from VAXC headers */
1287 #endif
1288   unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1289   unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1290   long int i;
1291   bool have_sym = FALSE, have_lnm = FALSE;
1292   struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1293   $DESCRIPTOR(cmddsc,cmd);    $DESCRIPTOR(nldsc,"_NLA0:");
1294   $DESCRIPTOR(clidsc,"DCL");  $DESCRIPTOR(clitabdsc,"DCLTABLES");
1295   $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1296   $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1297 #if defined(PERL_IMPLICIT_CONTEXT)
1298   pTHX;
1299 #endif
1300 #if defined(USE_ITHREADS)
1301   static perl_mutex primenv_mutex;
1302   MUTEX_INIT(&primenv_mutex);
1303 #endif
1304 
1305 #if defined(PERL_IMPLICIT_CONTEXT)
1306     /* We jump through these hoops because we can be called at */
1307     /* platform-specific initialization time, which is before anything is */
1308     /* set up--we can't even do a plain dTHX since that relies on the */
1309     /* interpreter structure to be initialized */
1310     if (PL_curinterp) {
1311       aTHX = PERL_GET_INTERP;
1312     } else {
1313       /* we never get here because the NULL pointer will cause the */
1314       /* several of the routines called by this routine to access violate */
1315 
1316       /* This routine is only called by hv.c/hv_iterinit which has a */
1317       /* context, so the real fix may be to pass it through instead of */
1318       /* the hoops above */
1319       aTHX = NULL;
1320     }
1321 #endif
1322 
1323   if (primed || !PL_envgv) return;
1324   MUTEX_LOCK(&primenv_mutex);
1325   if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1326   envhv = GvHVn(PL_envgv);
1327   /* Perform a dummy fetch as an lval to insure that the hash table is
1328    * set up.  Otherwise, the hv_store() will turn into a nullop. */
1329   (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1330 
1331   for (i = 0; env_tables[i]; i++) {
1332      if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1333          !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1334      if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1335   }
1336   if (have_sym || have_lnm) {
1337     long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1338     _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1339     _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1340     _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1341   }
1342 
1343   for (i--; i >= 0; i--) {
1344     if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1345       char *start;
1346       int j;
1347       for (j = 0; environ[j]; j++) {
1348         if (!(start = strchr(environ[j],'='))) {
1349           if (ckWARN(WARN_INTERNAL))
1350             Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1351         }
1352         else {
1353           start++;
1354           sv = newSVpv(start,0);
1355           SvTAINTED_on(sv);
1356           (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1357         }
1358       }
1359       continue;
1360     }
1361     else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1362              !str$case_blind_compare(&tmpdsc,&clisym)) {
1363       my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1364       cmddsc.dsc$w_length = 20;
1365       if (env_tables[i]->dsc$w_length == 12 &&
1366           (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1367           !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local  *", sizeof(cmd)-12);
1368       flags = defflags | CLI$M_NOLOGNAM;
1369     }
1370     else {
1371       my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1372       if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1373         my_strlcat(cmd," /Table=", sizeof(cmd));
1374         cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1375       }
1376       else cmddsc.dsc$w_length = 14;  /* N.B. We test this below */
1377       flags = defflags | CLI$M_NOCLISYM;
1378     }
1379 
1380     /* Create a new subprocess to execute each command, to exclude the
1381      * remote possibility that someone could subvert a mbx or file used
1382      * to write multiple commands to a single subprocess.
1383      */
1384     do {
1385       retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1386                          0,&riseandshine,0,0,&clidsc,&clitabdsc);
1387       flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1388       defflags &= ~CLI$M_TRUSTED;
1389     } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1390     _ckvmssts(retsts);
1391     if (!buf) Newx(buf,mbxbufsiz + 1,char);
1392     if (seenhv) SvREFCNT_dec(seenhv);
1393     seenhv = newHV();
1394     while (1) {
1395       char *cp1, *cp2, *key;
1396       unsigned long int sts, iosb[2], retlen, keylen;
1397       U32 hash;
1398 
1399       sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1400       if (sts & 1) sts = iosb[0] & 0xffff;
1401       if (sts == SS$_ENDOFFILE) {
1402         int wakect = 0;
1403         while (substs == 0) { sys$hiber(); wakect++;}
1404         if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
1405         _ckvmssts(substs);
1406         break;
1407       }
1408       _ckvmssts(sts);
1409       retlen = iosb[0] >> 16;
1410       if (!retlen) continue;  /* blank line */
1411       buf[retlen] = '\0';
1412       if (iosb[1] != subpid) {
1413         if (iosb[1]) {
1414           Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1415         }
1416         continue;
1417       }
1418       if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1419         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1420 
1421       for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1422       if (*cp1 == '(' || /* Logical name table name */
1423           *cp1 == '='    /* Next eqv of searchlist  */) continue;
1424       if (*cp1 == '"') cp1++;
1425       for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1426       key = cp1;  keylen = cp2 - cp1;
1427       if (keylen && hv_exists(seenhv,key,keylen)) continue;
1428       while (*cp2 && *cp2 != '=') cp2++;
1429       while (*cp2 && *cp2 == '=') cp2++;
1430       while (*cp2 && *cp2 == ' ') cp2++;
1431       if (*cp2 == '"') {  /* String translation; may embed "" */
1432         for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1433         cp2++;  cp1--; /* Skip "" surrounding translation */
1434       }
1435       else {  /* Numeric translation */
1436         for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1437         cp1--;  /* stop on last non-space char */
1438       }
1439       if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1440         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1441         continue;
1442       }
1443       PERL_HASH(hash,key,keylen);
1444 
1445       if (cp1 == cp2 && *cp2 == '.') {
1446         /* A single dot usually means an unprintable character, such as a null
1447          * to indicate a zero-length value.  Get the actual value to make sure.
1448          */
1449         char lnm[LNM$C_NAMLENGTH+1];
1450         char eqv[MAX_DCL_SYMBOL+1];
1451         int trnlen;
1452         strncpy(lnm, key, keylen);
1453         trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1454         sv = newSVpvn(eqv, strlen(eqv));
1455       }
1456       else {
1457         sv = newSVpvn(cp2,cp1 - cp2 + 1);
1458       }
1459 
1460       SvTAINTED_on(sv);
1461       hv_store(envhv,key,keylen,sv,hash);
1462       hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1463     }
1464     if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1465       /* get the PPFs for this process, not the subprocess */
1466       const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1467       char eqv[LNM$C_NAMLENGTH+1];
1468       int trnlen, i;
1469       for (i = 0; ppfs[i]; i++) {
1470         trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1471         sv = newSVpv(eqv,trnlen);
1472         SvTAINTED_on(sv);
1473         hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1474       }
1475     }
1476   }
1477   primed = 1;
1478   if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1479   if (buf) Safefree(buf);
1480   if (seenhv) SvREFCNT_dec(seenhv);
1481   MUTEX_UNLOCK(&primenv_mutex);
1482   return;
1483 
1484 }  /* end of prime_env_iter */
1485 /*}}}*/
1486 
1487 
1488 /*{{{ int  vmssetenv(const char *lnm, const char *eqv)*/
1489 /* Define or delete an element in the same "environment" as
1490  * vmstrnenv().  If an element is to be deleted, it's removed from
1491  * the first place it's found.  If it's to be set, it's set in the
1492  * place designated by the first element of the table vector.
1493  * Like setenv() returns 0 for success, non-zero on error.
1494  */
1495 int
1496 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1497 {
1498     const char *cp1;
1499     char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1500     unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1501     int nseg = 0, j;
1502     unsigned long int retsts, usermode = PSL$C_USER;
1503     struct itmlst_3 *ile, *ilist;
1504     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1505                             eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1506                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1507     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1508     $DESCRIPTOR(local,"_LOCAL");
1509 
1510     if (!lnm) {
1511         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1512         return SS$_IVLOGNAM;
1513     }
1514 
1515     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1516       *cp2 = _toupper(*cp1);
1517       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1518         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1519         return SS$_IVLOGNAM;
1520       }
1521     }
1522     lnmdsc.dsc$w_length = cp1 - lnm;
1523     if (!tabvec || !*tabvec) tabvec = env_tables;
1524 
1525     if (!eqv) {  /* we're deleting n element */
1526       for (curtab = 0; tabvec[curtab]; curtab++) {
1527         if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1528         int i;
1529           for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1530             if ((cp1 = strchr(environ[i],'=')) &&
1531                 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1532                 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1533 #ifdef HAS_SETENV
1534               return setenv(lnm,"",1) ? vaxc$errno : 0;
1535             }
1536           }
1537           ivenv = 1; retsts = SS$_NOLOGNAM;
1538 #else
1539               if (ckWARN(WARN_INTERNAL))
1540                 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1541               ivenv = 1; retsts = SS$_NOSUCHPGM;
1542               break;
1543             }
1544           }
1545 #endif
1546         }
1547         else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1548                  !str$case_blind_compare(&tmpdsc,&clisym)) {
1549           unsigned int symtype;
1550           if (tabvec[curtab]->dsc$w_length == 12 &&
1551               (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1552               !str$case_blind_compare(&tmpdsc,&local))
1553             symtype = LIB$K_CLI_LOCAL_SYM;
1554           else symtype = LIB$K_CLI_GLOBAL_SYM;
1555           retsts = lib$delete_symbol(&lnmdsc,&symtype);
1556           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1557           if (retsts == LIB$_NOSUCHSYM) continue;
1558           break;
1559         }
1560         else if (!ivlnm) {
1561           retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1562           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1563           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1564           retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1565           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1566         }
1567       }
1568     }
1569     else {  /* we're defining a value */
1570       if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1571 #ifdef HAS_SETENV
1572         return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1573 #else
1574         if (ckWARN(WARN_INTERNAL))
1575           Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1576         retsts = SS$_NOSUCHPGM;
1577 #endif
1578       }
1579       else {
1580         eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1581         eqvdsc.dsc$w_length  = strlen(eqv);
1582         if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1583             !str$case_blind_compare(&tmpdsc,&clisym)) {
1584           unsigned int symtype;
1585           if (tabvec[0]->dsc$w_length == 12 &&
1586               (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1587                !str$case_blind_compare(&tmpdsc,&local))
1588             symtype = LIB$K_CLI_LOCAL_SYM;
1589           else symtype = LIB$K_CLI_GLOBAL_SYM;
1590           retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1591         }
1592         else {
1593           if (!*eqv) eqvdsc.dsc$w_length = 1;
1594 	  if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1595 
1596             nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1597             if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1598 	      Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1599                           lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1600               eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1601               nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1602 	    }
1603 
1604             Newx(ilist,nseg+1,struct itmlst_3);
1605             ile = ilist;
1606             if (!ile) {
1607 	      set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1608               return SS$_INSFMEM;
1609 	    }
1610             memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1611 
1612             for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1613               ile->itmcode = LNM$_STRING;
1614               ile->bufadr = c;
1615               if ((j+1) == nseg) {
1616                 ile->buflen = strlen(c);
1617                 /* in case we are truncating one that's too long */
1618                 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1619               }
1620               else {
1621                 ile->buflen = LNM$C_NAMLENGTH;
1622               }
1623             }
1624 
1625             retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1626             Safefree (ilist);
1627 	  }
1628           else {
1629             retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1630 	  }
1631         }
1632       }
1633     }
1634     if (!(retsts & 1)) {
1635       switch (retsts) {
1636         case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1637         case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1638           set_errno(EVMSERR); break;
1639         case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1640         case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1641           set_errno(EINVAL); break;
1642         case SS$_NOPRIV:
1643           set_errno(EACCES); break;
1644         default:
1645           _ckvmssts(retsts);
1646           set_errno(EVMSERR);
1647        }
1648        set_vaxc_errno(retsts);
1649        return (int) retsts || 44; /* retsts should never be 0, but just in case */
1650     }
1651     else {
1652       /* We reset error values on success because Perl does an hv_fetch()
1653        * before each hv_store(), and if the thing we're setting didn't
1654        * previously exist, we've got a leftover error message.  (Of course,
1655        * this fails in the face of
1656        *    $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1657        * in that the error reported in $! isn't spurious,
1658        * but it's right more often than not.)
1659        */
1660       set_errno(0); set_vaxc_errno(retsts);
1661       return 0;
1662     }
1663 
1664 }  /* end of vmssetenv() */
1665 /*}}}*/
1666 
1667 /*{{{ void  my_setenv(const char *lnm, const char *eqv)*/
1668 /* This has to be a function since there's a prototype for it in proto.h */
1669 void
1670 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1671 {
1672     if (lnm && *lnm) {
1673       int len = strlen(lnm);
1674       if  (len == 7) {
1675         char uplnm[8];
1676         int i;
1677         for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1678         if (!strcmp(uplnm,"DEFAULT")) {
1679           if (eqv && *eqv) my_chdir(eqv);
1680           return;
1681         }
1682     }
1683   }
1684   (void) vmssetenv(lnm,eqv,NULL);
1685 }
1686 /*}}}*/
1687 
1688 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1689 /*  vmssetuserlnm
1690  *  sets a user-mode logical in the process logical name table
1691  *  used for redirection of sys$error
1692  */
1693 void
1694 Perl_vmssetuserlnm(const char *name, const char *eqv)
1695 {
1696     $DESCRIPTOR(d_tab, "LNM$PROCESS");
1697     struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1698     unsigned long int iss, attr = LNM$M_CONFINE;
1699     unsigned char acmode = PSL$C_USER;
1700     struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1701                                  {0, 0, 0, 0}};
1702     d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1703     d_name.dsc$w_length = strlen(name);
1704 
1705     lnmlst[0].buflen = strlen(eqv);
1706     lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1707 
1708     iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1709     if (!(iss&1)) lib$signal(iss);
1710 }
1711 /*}}}*/
1712 
1713 
1714 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1715 /* my_crypt - VMS password hashing
1716  * my_crypt() provides an interface compatible with the Unix crypt()
1717  * C library function, and uses sys$hash_password() to perform VMS
1718  * password hashing.  The quadword hashed password value is returned
1719  * as a NUL-terminated 8 character string.  my_crypt() does not change
1720  * the case of its string arguments; in order to match the behavior
1721  * of LOGINOUT et al., alphabetic characters in both arguments must
1722  *  be upcased by the caller.
1723  *
1724  * - fix me to call ACM services when available
1725  */
1726 char *
1727 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1728 {
1729 #   ifndef UAI$C_PREFERRED_ALGORITHM
1730 #     define UAI$C_PREFERRED_ALGORITHM 127
1731 #   endif
1732     unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1733     unsigned short int salt = 0;
1734     unsigned long int sts;
1735     struct const_dsc {
1736         unsigned short int dsc$w_length;
1737         unsigned char      dsc$b_type;
1738         unsigned char      dsc$b_class;
1739         const char *       dsc$a_pointer;
1740     }  usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1741        txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1742     struct itmlst_3 uailst[3] = {
1743         { sizeof alg,  UAI$_ENCRYPT, &alg, 0},
1744         { sizeof salt, UAI$_SALT,    &salt, 0},
1745         { 0,           0,            NULL,  NULL}};
1746     static char hash[9];
1747 
1748     usrdsc.dsc$w_length = strlen(usrname);
1749     usrdsc.dsc$a_pointer = usrname;
1750     if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1751       switch (sts) {
1752         case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1753           set_errno(EACCES);
1754           break;
1755         case RMS$_RNF:
1756           set_errno(ESRCH);  /* There isn't a Unix no-such-user error */
1757           break;
1758         default:
1759           set_errno(EVMSERR);
1760       }
1761       set_vaxc_errno(sts);
1762       if (sts != RMS$_RNF) return NULL;
1763     }
1764 
1765     txtdsc.dsc$w_length = strlen(textpasswd);
1766     txtdsc.dsc$a_pointer = textpasswd;
1767     if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1768       set_errno(EVMSERR);  set_vaxc_errno(sts);  return NULL;
1769     }
1770 
1771     return (char *) hash;
1772 
1773 }  /* end of my_crypt() */
1774 /*}}}*/
1775 
1776 
1777 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1778 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1779 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1780 
1781 /* fixup barenames that are directories for internal use.
1782  * There have been problems with the consistent handling of UNIX
1783  * style directory names when routines are presented with a name that
1784  * has no directory delimiters at all.  So this routine will eventually
1785  * fix the issue.
1786  */
1787 static char * fixup_bare_dirnames(const char * name)
1788 {
1789   if (decc_disable_to_vms_logname_translation) {
1790 /* fix me */
1791   }
1792   return NULL;
1793 }
1794 
1795 /* 8.3, remove() is now broken on symbolic links */
1796 static int rms_erase(const char * vmsname);
1797 
1798 
1799 /* mp_do_kill_file
1800  * A little hack to get around a bug in some implementation of remove()
1801  * that do not know how to delete a directory
1802  *
1803  * Delete any file to which user has control access, regardless of whether
1804  * delete access is explicitly allowed.
1805  * Limitations: User must have write access to parent directory.
1806  *              Does not block signals or ASTs; if interrupted in midstream
1807  *              may leave file with an altered ACL.
1808  * HANDLE WITH CARE!
1809  */
1810 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1811 static int
1812 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1813 {
1814     char *vmsname;
1815     char *rslt;
1816     unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1817     unsigned long int cxt = 0, aclsts, fndsts;
1818     int rmsts = -1;
1819     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1820     struct myacedef {
1821       unsigned char myace$b_length;
1822       unsigned char myace$b_type;
1823       unsigned short int myace$w_flags;
1824       unsigned long int myace$l_access;
1825       unsigned long int myace$l_ident;
1826     } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1827                  ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1828       oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1829      struct itmlst_3
1830        findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1831                      {sizeof oldace, ACL$C_READACE,   &oldace, 0},{0,0,0,0}},
1832        addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1833        dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1834        lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1835        ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1836 
1837     /* Expand the input spec using RMS, since the CRTL remove() and
1838      * system services won't do this by themselves, so we may miss
1839      * a file "hiding" behind a logical name or search list. */
1840     vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1841     if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1842 
1843     rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1844     if (rslt == NULL) {
1845         PerlMem_free(vmsname);
1846 	return -1;
1847       }
1848 
1849     /* Erase the file */
1850     rmsts = rms_erase(vmsname);
1851 
1852     /* Did it succeed */
1853     if ($VMS_STATUS_SUCCESS(rmsts)) {
1854 	PerlMem_free(vmsname);
1855 	return 0;
1856       }
1857 
1858     /* If not, can changing protections help? */
1859     if (rmsts != RMS$_PRV) {
1860       set_vaxc_errno(rmsts);
1861       PerlMem_free(vmsname);
1862       return -1;
1863     }
1864 
1865     /* No, so we get our own UIC to use as a rights identifier,
1866      * and the insert an ACE at the head of the ACL which allows us
1867      * to delete the file.
1868      */
1869     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1870     fildsc.dsc$w_length = strlen(vmsname);
1871     fildsc.dsc$a_pointer = vmsname;
1872     cxt = 0;
1873     newace.myace$l_ident = oldace.myace$l_ident;
1874     rmsts = -1;
1875     if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1876       switch (aclsts) {
1877         case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1878           set_errno(ENOENT); break;
1879         case RMS$_DIR:
1880           set_errno(ENOTDIR); break;
1881         case RMS$_DEV:
1882           set_errno(ENODEV); break;
1883         case RMS$_SYN: case SS$_INVFILFOROP:
1884           set_errno(EINVAL); break;
1885         case RMS$_PRV:
1886           set_errno(EACCES); break;
1887         default:
1888           _ckvmssts_noperl(aclsts);
1889       }
1890       set_vaxc_errno(aclsts);
1891       PerlMem_free(vmsname);
1892       return -1;
1893     }
1894     /* Grab any existing ACEs with this identifier in case we fail */
1895     aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1896     if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1897                     || fndsts == SS$_NOMOREACE ) {
1898       /* Add the new ACE . . . */
1899       if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1900         goto yourroom;
1901 
1902       rmsts = rms_erase(vmsname);
1903       if ($VMS_STATUS_SUCCESS(rmsts)) {
1904 	rmsts = 0;
1905 	}
1906 	else {
1907 	rmsts = -1;
1908         /* We blew it - dir with files in it, no write priv for
1909          * parent directory, etc.  Put things back the way they were. */
1910         if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1911           goto yourroom;
1912         if (fndsts & 1) {
1913           addlst[0].bufadr = &oldace;
1914           if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1915             goto yourroom;
1916         }
1917       }
1918     }
1919 
1920     yourroom:
1921     fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1922     /* We just deleted it, so of course it's not there.  Some versions of
1923      * VMS seem to return success on the unlock operation anyhow (after all
1924      * the unlock is successful), but others don't.
1925      */
1926     if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1927     if (aclsts & 1) aclsts = fndsts;
1928     if (!(aclsts & 1)) {
1929       set_errno(EVMSERR);
1930       set_vaxc_errno(aclsts);
1931     }
1932 
1933     PerlMem_free(vmsname);
1934     return rmsts;
1935 
1936 }  /* end of kill_file() */
1937 /*}}}*/
1938 
1939 
1940 /*{{{int do_rmdir(char *name)*/
1941 int
1942 Perl_do_rmdir(pTHX_ const char *name)
1943 {
1944     char * dirfile;
1945     int retval;
1946     Stat_t st;
1947 
1948     /* lstat returns a VMS fileified specification of the name */
1949     /* that is looked up, and also lets verifies that this is a directory */
1950 
1951     retval = flex_lstat(name, &st);
1952     if (retval != 0) {
1953         char * ret_spec;
1954 
1955         /* Due to a historical feature, flex_stat/lstat can not see some */
1956         /* Unix format file names that the rest of the CRTL can see */
1957         /* Fixing that feature will cause some perl tests to fail */
1958         /* So try this one more time. */
1959 
1960         retval = lstat(name, &st.crtl_stat);
1961         if (retval != 0)
1962             return -1;
1963 
1964         /* force it to a file spec for the kill file to work. */
1965         ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1966         if (ret_spec == NULL) {
1967             errno = EIO;
1968             return -1;
1969         }
1970     }
1971 
1972     if (!S_ISDIR(st.st_mode)) {
1973 	errno = ENOTDIR;
1974 	retval = -1;
1975     }
1976     else {
1977         dirfile = st.st_devnam;
1978 
1979         /* It may be possible for flex_stat to find a file and vmsify() to */
1980         /* fail with ODS-2 specifications.  mp_do_kill_file can not deal */
1981         /* with that case, so fail it */
1982         if (dirfile[0] == 0) {
1983             errno = EIO;
1984             return -1;
1985         }
1986 
1987 	retval = mp_do_kill_file(aTHX_ dirfile, 1);
1988     }
1989 
1990     return retval;
1991 
1992 }  /* end of do_rmdir */
1993 /*}}}*/
1994 
1995 /* kill_file
1996  * Delete any file to which user has control access, regardless of whether
1997  * delete access is explicitly allowed.
1998  * Limitations: User must have write access to parent directory.
1999  *              Does not block signals or ASTs; if interrupted in midstream
2000  *              may leave file with an altered ACL.
2001  * HANDLE WITH CARE!
2002  */
2003 /*{{{int kill_file(char *name)*/
2004 int
2005 Perl_kill_file(pTHX_ const char *name)
2006 {
2007     char * vmsfile;
2008     Stat_t st;
2009     int rmsts;
2010 
2011     /* Convert the filename to VMS format and see if it is a directory */
2012     /* flex_lstat returns a vmsified file specification */
2013     rmsts = flex_lstat(name, &st);
2014     if (rmsts != 0) {
2015 
2016         /* Due to a historical feature, flex_stat/lstat can not see some */
2017         /* Unix format file names that the rest of the CRTL can see when */
2018         /* ODS-2 file specifications are in use. */
2019         /* Fixing that feature will cause some perl tests to fail */
2020         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2021         st.st_mode = 0;
2022         vmsfile = (char *) name; /* cast ok */
2023 
2024     } else {
2025         vmsfile = st.st_devnam;
2026         if (vmsfile[0] == 0) {
2027             /* It may be possible for flex_stat to find a file and vmsify() */
2028             /* to fail with ODS-2 specifications.  mp_do_kill_file can not */
2029             /* deal with that case, so fail it */
2030             errno = EIO;
2031             return -1;
2032         }
2033     }
2034 
2035     /* Remove() is allowed to delete directories, according to the X/Open
2036      * specifications.
2037      * This may need special handling to work with the ACL hacks.
2038      */
2039     if (S_ISDIR(st.st_mode)) {
2040         rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2041         return rmsts;
2042     }
2043 
2044     rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2045 
2046     /* Need to delete all versions ? */
2047     if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2048         int i = 0;
2049 
2050         /* Just use lstat() here as do not need st_dev */
2051         /* and we know that the file is in VMS format or that */
2052         /* because of a historical bug, flex_stat can not see the file */
2053         while (lstat(vmsfile, (stat_t *)&st) == 0) {
2054             rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2055             if (rmsts != 0)
2056                 break;
2057             i++;
2058 
2059             /* Make sure that we do not loop forever */
2060             if (i > 32767) {
2061                 errno = EIO;
2062                 rmsts = -1;
2063                 break;
2064             }
2065         }
2066     }
2067 
2068     return rmsts;
2069 
2070 }  /* end of kill_file() */
2071 /*}}}*/
2072 
2073 
2074 /*{{{int my_mkdir(char *,Mode_t)*/
2075 int
2076 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2077 {
2078   STRLEN dirlen = strlen(dir);
2079 
2080   /* zero length string sometimes gives ACCVIO */
2081   if (dirlen == 0) return -1;
2082 
2083   /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2084    * null file name/type.  However, it's commonplace under Unix,
2085    * so we'll allow it for a gain in portability.
2086    */
2087   if (dir[dirlen-1] == '/') {
2088     char *newdir = savepvn(dir,dirlen-1);
2089     int ret = mkdir(newdir,mode);
2090     Safefree(newdir);
2091     return ret;
2092   }
2093   else return mkdir(dir,mode);
2094 }  /* end of my_mkdir */
2095 /*}}}*/
2096 
2097 /*{{{int my_chdir(char *)*/
2098 int
2099 Perl_my_chdir(pTHX_ const char *dir)
2100 {
2101   STRLEN dirlen = strlen(dir);
2102   const char *dir1 = dir;
2103 
2104   /* zero length string sometimes gives ACCVIO */
2105   if (dirlen == 0) {
2106     SETERRNO(EINVAL, SS$_BADPARAM);
2107     return -1;
2108   }
2109 
2110   /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2111    * This does not work if DECC$EFS_CHARSET is active.  Hack it here
2112    * so that existing scripts do not need to be changed.
2113    */
2114   while ((dirlen > 0) && (*dir1 == ' ')) {
2115     dir1++;
2116     dirlen--;
2117   }
2118 
2119   /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2120    * that implies
2121    * null file name/type.  However, it's commonplace under Unix,
2122    * so we'll allow it for a gain in portability.
2123    *
2124    *  '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2125    */
2126   if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2127       char *newdir;
2128       int ret;
2129       newdir = (char *)PerlMem_malloc(dirlen);
2130       if (newdir ==NULL)
2131           _ckvmssts_noperl(SS$_INSFMEM);
2132       memcpy(newdir, dir1, dirlen-1);
2133       newdir[dirlen-1] = '\0';
2134       ret = chdir(newdir);
2135       PerlMem_free(newdir);
2136       return ret;
2137   }
2138   else return chdir(dir1);
2139 }  /* end of my_chdir */
2140 /*}}}*/
2141 
2142 
2143 /*{{{int my_chmod(char *, mode_t)*/
2144 int
2145 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2146 {
2147   Stat_t st;
2148   int ret = -1;
2149   char * changefile;
2150   STRLEN speclen = strlen(file_spec);
2151 
2152   /* zero length string sometimes gives ACCVIO */
2153   if (speclen == 0) return -1;
2154 
2155   /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2156    * that implies null file name/type.  However, it's commonplace under Unix,
2157    * so we'll allow it for a gain in portability.
2158    *
2159    * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2160    * in VMS file.dir notation.
2161    */
2162   changefile = (char *) file_spec; /* cast ok */
2163   ret = flex_lstat(file_spec, &st);
2164   if (ret != 0) {
2165 
2166         /* Due to a historical feature, flex_stat/lstat can not see some */
2167         /* Unix format file names that the rest of the CRTL can see when */
2168         /* ODS-2 file specifications are in use. */
2169         /* Fixing that feature will cause some perl tests to fail */
2170         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2171         st.st_mode = 0;
2172 
2173   } else {
2174       /* It may be possible to get here with nothing in st_devname */
2175       /* chmod still may work though */
2176       if (st.st_devnam[0] != 0) {
2177           changefile = st.st_devnam;
2178       }
2179   }
2180   ret = chmod(changefile, mode);
2181   return ret;
2182 }  /* end of my_chmod */
2183 /*}}}*/
2184 
2185 
2186 /*{{{FILE *my_tmpfile()*/
2187 FILE *
2188 my_tmpfile(void)
2189 {
2190   FILE *fp;
2191   char *cp;
2192 
2193   if ((fp = tmpfile())) return fp;
2194 
2195   cp = (char *)PerlMem_malloc(L_tmpnam+24);
2196   if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2197 
2198   if (decc_filename_unix_only == 0)
2199     strcpy(cp,"Sys$Scratch:");
2200   else
2201     strcpy(cp,"/tmp/");
2202   tmpnam(cp+strlen(cp));
2203   strcat(cp,".Perltmp");
2204   fp = fopen(cp,"w+","fop=dlt");
2205   PerlMem_free(cp);
2206   return fp;
2207 }
2208 /*}}}*/
2209 
2210 
2211 /*
2212  * The C RTL's sigaction fails to check for invalid signal numbers so we
2213  * help it out a bit.  The docs are correct, but the actual routine doesn't
2214  * do what the docs say it will.
2215  */
2216 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2217 int
2218 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2219                    struct sigaction* oact)
2220 {
2221   if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2222 	SETERRNO(EINVAL, SS$_INVARG);
2223 	return -1;
2224   }
2225   return sigaction(sig, act, oact);
2226 }
2227 /*}}}*/
2228 
2229 #ifdef KILL_BY_SIGPRC
2230 #include <errnodef.h>
2231 
2232 /* We implement our own kill() using the undocumented system service
2233    sys$sigprc for one of two reasons:
2234 
2235    1.) If the kill() in an older CRTL uses sys$forcex, causing the
2236    target process to do a sys$exit, which usually can't be handled
2237    gracefully...certainly not by Perl and the %SIG{} mechanism.
2238 
2239    2.) If the kill() in the CRTL can't be called from a signal
2240    handler without disappearing into the ether, i.e., the signal
2241    it purportedly sends is never trapped. Still true as of VMS 7.3.
2242 
2243    sys$sigprc has the same parameters as sys$forcex, but throws an exception
2244    in the target process rather than calling sys$exit.
2245 
2246    Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2247    on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2248    provide.  On VMS 7.0+ this is taken care of by doing sys$sigprc
2249    with condition codes C$_SIG0+nsig*8, catching the exception on the
2250    target process and resignaling with appropriate arguments.
2251 
2252    But we don't have that VMS 7.0+ exception handler, so if you
2253    Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS.  Oh well.
2254 
2255    Also note that SIGTERM is listed in the docs as being "unimplemented",
2256    yet always seems to be signaled with a VMS condition code of 4 (and
2257    correctly handled for that code).  So we hardwire it in.
2258 
2259    Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2260    number to see if it's valid.  So Perl_my_kill(pid,0) returns -1 rather
2261    than signalling with an unrecognized (and unhandled by CRTL) code.
2262 */
2263 
2264 #define _MY_SIG_MAX 28
2265 
2266 static unsigned int
2267 Perl_sig_to_vmscondition_int(int sig)
2268 {
2269     static unsigned int sig_code[_MY_SIG_MAX+1] =
2270     {
2271         0,                  /*  0 ZERO     */
2272         SS$_HANGUP,         /*  1 SIGHUP   */
2273         SS$_CONTROLC,       /*  2 SIGINT   */
2274         SS$_CONTROLY,       /*  3 SIGQUIT  */
2275         SS$_RADRMOD,        /*  4 SIGILL   */
2276         SS$_BREAK,          /*  5 SIGTRAP  */
2277         SS$_OPCCUS,         /*  6 SIGABRT  */
2278         SS$_COMPAT,         /*  7 SIGEMT   */
2279 #ifdef __VAX
2280         SS$_FLTOVF,         /*  8 SIGFPE VAX */
2281 #else
2282         SS$_HPARITH,        /*  8 SIGFPE AXP */
2283 #endif
2284         SS$_ABORT,          /*  9 SIGKILL  */
2285         SS$_ACCVIO,         /* 10 SIGBUS   */
2286         SS$_ACCVIO,         /* 11 SIGSEGV  */
2287         SS$_BADPARAM,       /* 12 SIGSYS   */
2288         SS$_NOMBX,          /* 13 SIGPIPE  */
2289         SS$_ASTFLT,         /* 14 SIGALRM  */
2290         4,                  /* 15 SIGTERM  */
2291         0,                  /* 16 SIGUSR1  */
2292         0,                  /* 17 SIGUSR2  */
2293         0,                  /* 18 */
2294         0,                  /* 19 */
2295         0,                  /* 20 SIGCHLD  */
2296         0,                  /* 21 SIGCONT  */
2297         0,                  /* 22 SIGSTOP  */
2298         0,                  /* 23 SIGTSTP  */
2299         0,                  /* 24 SIGTTIN  */
2300         0,                  /* 25 SIGTTOU  */
2301         0,                  /* 26 */
2302         0,                  /* 27 */
2303         0                   /* 28 SIGWINCH  */
2304     };
2305 
2306     static int initted = 0;
2307     if (!initted) {
2308         initted = 1;
2309         sig_code[16] = C$_SIGUSR1;
2310         sig_code[17] = C$_SIGUSR2;
2311         sig_code[20] = C$_SIGCHLD;
2312 #if __CRTL_VER >= 70300000
2313         sig_code[28] = C$_SIGWINCH;
2314 #endif
2315     }
2316 
2317     if (sig < _SIG_MIN) return 0;
2318     if (sig > _MY_SIG_MAX) return 0;
2319     return sig_code[sig];
2320 }
2321 
2322 unsigned int
2323 Perl_sig_to_vmscondition(int sig)
2324 {
2325 #ifdef SS$_DEBUG
2326     if (vms_debug_on_exception != 0)
2327 	lib$signal(SS$_DEBUG);
2328 #endif
2329     return Perl_sig_to_vmscondition_int(sig);
2330 }
2331 
2332 
2333 #define sys$sigprc SYS$SIGPRC
2334 #ifdef __cplusplus
2335 extern "C" {
2336 #endif
2337 int sys$sigprc(unsigned int *pidadr,
2338                struct dsc$descriptor_s *prcname,
2339                unsigned int code);
2340 #ifdef __cplusplus
2341 }
2342 #endif
2343 
2344 int
2345 Perl_my_kill(int pid, int sig)
2346 {
2347     int iss;
2348     unsigned int code;
2349 
2350      /* sig 0 means validate the PID */
2351     /*------------------------------*/
2352     if (sig == 0) {
2353 	const unsigned long int jpicode = JPI$_PID;
2354 	pid_t ret_pid;
2355 	int status;
2356         status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2357 	if ($VMS_STATUS_SUCCESS(status))
2358 	   return 0;
2359 	switch (status) {
2360         case SS$_NOSUCHNODE:
2361         case SS$_UNREACHABLE:
2362 	case SS$_NONEXPR:
2363 	   errno = ESRCH;
2364 	   break;
2365 	case SS$_NOPRIV:
2366 	   errno = EPERM;
2367 	   break;
2368 	default:
2369 	   errno = EVMSERR;
2370 	}
2371 	vaxc$errno=status;
2372 	return -1;
2373     }
2374 
2375     code = Perl_sig_to_vmscondition_int(sig);
2376 
2377     if (!code) {
2378 	SETERRNO(EINVAL, SS$_BADPARAM);
2379         return -1;
2380     }
2381 
2382     /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2383      * signals are to be sent to multiple processes.
2384      *  pid = 0 - all processes in group except ones that the system exempts
2385      *  pid = -1 - all processes except ones that the system exempts
2386      *  pid = -n - all processes in group (abs(n)) except ...
2387      * For now, just report as not supported.
2388      */
2389 
2390     if (pid <= 0) {
2391 	SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2392         return -1;
2393     }
2394 
2395     iss = sys$sigprc((unsigned int *)&pid,0,code);
2396     if (iss&1) return 0;
2397 
2398     switch (iss) {
2399       case SS$_NOPRIV:
2400         set_errno(EPERM);  break;
2401       case SS$_NONEXPR:
2402       case SS$_NOSUCHNODE:
2403       case SS$_UNREACHABLE:
2404         set_errno(ESRCH);  break;
2405       case SS$_INSFMEM:
2406         set_errno(ENOMEM); break;
2407       default:
2408         _ckvmssts_noperl(iss);
2409         set_errno(EVMSERR);
2410     }
2411     set_vaxc_errno(iss);
2412 
2413     return -1;
2414 }
2415 #endif
2416 
2417 /* Routine to convert a VMS status code to a UNIX status code.
2418 ** More tricky than it appears because of conflicting conventions with
2419 ** existing code.
2420 **
2421 ** VMS status codes are a bit mask, with the least significant bit set for
2422 ** success.
2423 **
2424 ** Special UNIX status of EVMSERR indicates that no translation is currently
2425 ** available, and programs should check the VMS status code.
2426 **
2427 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2428 ** decoding.
2429 */
2430 
2431 #ifndef C_FACILITY_NO
2432 #define C_FACILITY_NO 0x350000
2433 #endif
2434 #ifndef DCL_IVVERB
2435 #define DCL_IVVERB 0x38090
2436 #endif
2437 
2438 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2439 {
2440 int facility;
2441 int fac_sp;
2442 int msg_no;
2443 int msg_status;
2444 int unix_status;
2445 
2446   /* Assume the best or the worst */
2447   if (vms_status & STS$M_SUCCESS)
2448     unix_status = 0;
2449   else
2450     unix_status = EVMSERR;
2451 
2452   msg_status = vms_status & ~STS$M_CONTROL;
2453 
2454   facility = vms_status & STS$M_FAC_NO;
2455   fac_sp = vms_status & STS$M_FAC_SP;
2456   msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2457 
2458   if (((facility == 0) || (fac_sp == 0))  && (child_flag == 0)) {
2459     switch(msg_no) {
2460     case SS$_NORMAL:
2461 	unix_status = 0;
2462 	break;
2463     case SS$_ACCVIO:
2464 	unix_status = EFAULT;
2465 	break;
2466     case SS$_DEVOFFLINE:
2467 	unix_status = EBUSY;
2468 	break;
2469     case SS$_CLEARED:
2470 	unix_status = ENOTCONN;
2471 	break;
2472     case SS$_IVCHAN:
2473     case SS$_IVLOGNAM:
2474     case SS$_BADPARAM:
2475     case SS$_IVLOGTAB:
2476     case SS$_NOLOGNAM:
2477     case SS$_NOLOGTAB:
2478     case SS$_INVFILFOROP:
2479     case SS$_INVARG:
2480     case SS$_NOSUCHID:
2481     case SS$_IVIDENT:
2482 	unix_status = EINVAL;
2483 	break;
2484     case SS$_UNSUPPORTED:
2485 	unix_status = ENOTSUP;
2486 	break;
2487     case SS$_FILACCERR:
2488     case SS$_NOGRPPRV:
2489     case SS$_NOSYSPRV:
2490 	unix_status = EACCES;
2491 	break;
2492     case SS$_DEVICEFULL:
2493 	unix_status = ENOSPC;
2494 	break;
2495     case SS$_NOSUCHDEV:
2496 	unix_status = ENODEV;
2497 	break;
2498     case SS$_NOSUCHFILE:
2499     case SS$_NOSUCHOBJECT:
2500 	unix_status = ENOENT;
2501 	break;
2502     case SS$_ABORT:				    /* Fatal case */
2503     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2504     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2505 	unix_status = EINTR;
2506 	break;
2507     case SS$_BUFFEROVF:
2508 	unix_status = E2BIG;
2509 	break;
2510     case SS$_INSFMEM:
2511 	unix_status = ENOMEM;
2512 	break;
2513     case SS$_NOPRIV:
2514 	unix_status = EPERM;
2515 	break;
2516     case SS$_NOSUCHNODE:
2517     case SS$_UNREACHABLE:
2518 	unix_status = ESRCH;
2519 	break;
2520     case SS$_NONEXPR:
2521 	unix_status = ECHILD;
2522 	break;
2523     default:
2524 	if ((facility == 0) && (msg_no < 8)) {
2525 	  /* These are not real VMS status codes so assume that they are
2526           ** already UNIX status codes
2527 	  */
2528 	  unix_status = msg_no;
2529 	  break;
2530 	}
2531     }
2532   }
2533   else {
2534     /* Translate a POSIX exit code to a UNIX exit code */
2535     if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000))  {
2536 	unix_status = (msg_no & 0x07F8) >> 3;
2537     }
2538     else {
2539 
2540 	 /* Documented traditional behavior for handling VMS child exits */
2541 	/*--------------------------------------------------------------*/
2542 	if (child_flag != 0) {
2543 
2544 	     /* Success / Informational return 0 */
2545 	    /*----------------------------------*/
2546 	    if (msg_no & STS$K_SUCCESS)
2547 		return 0;
2548 
2549 	     /* Warning returns 1 */
2550 	    /*-------------------*/
2551 	    if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2552 	    	return 1;
2553 
2554 	     /* Everything else pass through the severity bits */
2555 	    /*------------------------------------------------*/
2556 	    return (msg_no & STS$M_SEVERITY);
2557 	}
2558 
2559 	 /* Normal VMS status to ERRNO mapping attempt */
2560 	/*--------------------------------------------*/
2561 	switch(msg_status) {
2562 	/* case RMS$_EOF: */ /* End of File */
2563 	case RMS$_FNF:	/* File Not Found */
2564 	case RMS$_DNF:	/* Dir Not Found */
2565 		unix_status = ENOENT;
2566 		break;
2567 	case RMS$_RNF:	/* Record Not Found */
2568 		unix_status = ESRCH;
2569 		break;
2570 	case RMS$_DIR:
2571 		unix_status = ENOTDIR;
2572 		break;
2573 	case RMS$_DEV:
2574 		unix_status = ENODEV;
2575 		break;
2576 	case RMS$_IFI:
2577 	case RMS$_FAC:
2578 	case RMS$_ISI:
2579 		unix_status = EBADF;
2580 		break;
2581 	case RMS$_FEX:
2582 		unix_status = EEXIST;
2583 		break;
2584 	case RMS$_SYN:
2585 	case RMS$_FNM:
2586 	case LIB$_INVSTRDES:
2587 	case LIB$_INVARG:
2588 	case LIB$_NOSUCHSYM:
2589 	case LIB$_INVSYMNAM:
2590 	case DCL_IVVERB:
2591 		unix_status = EINVAL;
2592 		break;
2593 	case CLI$_BUFOVF:
2594 	case RMS$_RTB:
2595 	case CLI$_TKNOVF:
2596 	case CLI$_RSLOVF:
2597 		unix_status = E2BIG;
2598 		break;
2599 	case RMS$_PRV:	/* No privilege */
2600 	case RMS$_ACC:	/* ACP file access failed */
2601 	case RMS$_WLK:	/* Device write locked */
2602 		unix_status = EACCES;
2603 		break;
2604 	case RMS$_MKD:  /* Failed to mark for delete */
2605 		unix_status = EPERM;
2606 		break;
2607 	/* case RMS$_NMF: */  /* No more files */
2608 	}
2609     }
2610   }
2611 
2612   return unix_status;
2613 }
2614 
2615 /* Try to guess at what VMS error status should go with a UNIX errno
2616  * value.  This is hard to do as there could be many possible VMS
2617  * error statuses that caused the errno value to be set.
2618  */
2619 
2620 int Perl_unix_status_to_vms(int unix_status)
2621 {
2622 int test_unix_status;
2623 
2624      /* Trivial cases first */
2625     /*---------------------*/
2626     if (unix_status == EVMSERR)
2627 	return vaxc$errno;
2628 
2629      /* Is vaxc$errno sane? */
2630     /*---------------------*/
2631     test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2632     if (test_unix_status == unix_status)
2633 	return vaxc$errno;
2634 
2635      /* If way out of range, must be VMS code already */
2636     /*-----------------------------------------------*/
2637     if (unix_status > EVMSERR)
2638 	return unix_status;
2639 
2640      /* If out of range, punt */
2641     /*-----------------------*/
2642     if (unix_status > __ERRNO_MAX)
2643 	return SS$_ABORT;
2644 
2645 
2646      /* Ok, now we have to do it the hard way. */
2647     /*----------------------------------------*/
2648     switch(unix_status) {
2649     case 0:	return SS$_NORMAL;
2650     case EPERM: return SS$_NOPRIV;
2651     case ENOENT: return SS$_NOSUCHOBJECT;
2652     case ESRCH: return SS$_UNREACHABLE;
2653     case EINTR: return SS$_ABORT;
2654     /* case EIO: */
2655     /* case ENXIO:  */
2656     case E2BIG: return SS$_BUFFEROVF;
2657     /* case ENOEXEC */
2658     case EBADF: return RMS$_IFI;
2659     case ECHILD: return SS$_NONEXPR;
2660     /* case EAGAIN */
2661     case ENOMEM: return SS$_INSFMEM;
2662     case EACCES: return SS$_FILACCERR;
2663     case EFAULT: return SS$_ACCVIO;
2664     /* case ENOTBLK */
2665     case EBUSY: return SS$_DEVOFFLINE;
2666     case EEXIST: return RMS$_FEX;
2667     /* case EXDEV */
2668     case ENODEV: return SS$_NOSUCHDEV;
2669     case ENOTDIR: return RMS$_DIR;
2670     /* case EISDIR */
2671     case EINVAL: return SS$_INVARG;
2672     /* case ENFILE */
2673     /* case EMFILE */
2674     /* case ENOTTY */
2675     /* case ETXTBSY */
2676     /* case EFBIG */
2677     case ENOSPC: return SS$_DEVICEFULL;
2678     case ESPIPE: return LIB$_INVARG;
2679     /* case EROFS: */
2680     /* case EMLINK: */
2681     /* case EPIPE: */
2682     /* case EDOM */
2683     case ERANGE: return LIB$_INVARG;
2684     /* case EWOULDBLOCK */
2685     /* case EINPROGRESS */
2686     /* case EALREADY */
2687     /* case ENOTSOCK */
2688     /* case EDESTADDRREQ */
2689     /* case EMSGSIZE */
2690     /* case EPROTOTYPE */
2691     /* case ENOPROTOOPT */
2692     /* case EPROTONOSUPPORT */
2693     /* case ESOCKTNOSUPPORT */
2694     /* case EOPNOTSUPP */
2695     /* case EPFNOSUPPORT */
2696     /* case EAFNOSUPPORT */
2697     /* case EADDRINUSE */
2698     /* case EADDRNOTAVAIL */
2699     /* case ENETDOWN */
2700     /* case ENETUNREACH */
2701     /* case ENETRESET */
2702     /* case ECONNABORTED */
2703     /* case ECONNRESET */
2704     /* case ENOBUFS */
2705     /* case EISCONN */
2706     case ENOTCONN: return SS$_CLEARED;
2707     /* case ESHUTDOWN */
2708     /* case ETOOMANYREFS */
2709     /* case ETIMEDOUT */
2710     /* case ECONNREFUSED */
2711     /* case ELOOP */
2712     /* case ENAMETOOLONG */
2713     /* case EHOSTDOWN */
2714     /* case EHOSTUNREACH */
2715     /* case ENOTEMPTY */
2716     /* case EPROCLIM */
2717     /* case EUSERS  */
2718     /* case EDQUOT  */
2719     /* case ENOMSG  */
2720     /* case EIDRM */
2721     /* case EALIGN */
2722     /* case ESTALE */
2723     /* case EREMOTE */
2724     /* case ENOLCK */
2725     /* case ENOSYS */
2726     /* case EFTYPE */
2727     /* case ECANCELED */
2728     /* case EFAIL */
2729     /* case EINPROG */
2730     case ENOTSUP:
2731 	return SS$_UNSUPPORTED;
2732     /* case EDEADLK */
2733     /* case ENWAIT */
2734     /* case EILSEQ */
2735     /* case EBADCAT */
2736     /* case EBADMSG */
2737     /* case EABANDONED */
2738     default:
2739 	return SS$_ABORT; /* punt */
2740     }
2741 }
2742 
2743 
2744 /* default piping mailbox size */
2745 #ifdef __VAX
2746 #  define PERL_BUFSIZ        512
2747 #else
2748 #  define PERL_BUFSIZ        8192
2749 #endif
2750 
2751 
2752 static void
2753 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2754 {
2755   unsigned long int mbxbufsiz;
2756   static unsigned long int syssize = 0;
2757   unsigned long int dviitm = DVI$_DEVNAM;
2758   char csize[LNM$C_NAMLENGTH+1];
2759   int sts;
2760 
2761   if (!syssize) {
2762     unsigned long syiitm = SYI$_MAXBUF;
2763     /*
2764      * Get the SYSGEN parameter MAXBUF
2765      *
2766      * If the logical 'PERL_MBX_SIZE' is defined
2767      * use the value of the logical instead of PERL_BUFSIZ, but
2768      * keep the size between 128 and MAXBUF.
2769      *
2770      */
2771     _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2772   }
2773 
2774   if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2775       mbxbufsiz = atoi(csize);
2776   } else {
2777       mbxbufsiz = PERL_BUFSIZ;
2778   }
2779   if (mbxbufsiz < 128) mbxbufsiz = 128;
2780   if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2781 
2782   _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2783 
2784   sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2785   _ckvmssts_noperl(sts);
2786   namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2787 
2788 }  /* end of create_mbx() */
2789 
2790 
2791 /*{{{  my_popen and my_pclose*/
2792 
2793 typedef struct _iosb           IOSB;
2794 typedef struct _iosb*         pIOSB;
2795 typedef struct _pipe           Pipe;
2796 typedef struct _pipe*         pPipe;
2797 typedef struct pipe_details    Info;
2798 typedef struct pipe_details*  pInfo;
2799 typedef struct _srqp            RQE;
2800 typedef struct _srqp*          pRQE;
2801 typedef struct _tochildbuf      CBuf;
2802 typedef struct _tochildbuf*    pCBuf;
2803 
2804 struct _iosb {
2805     unsigned short status;
2806     unsigned short count;
2807     unsigned long  dvispec;
2808 };
2809 
2810 #pragma member_alignment save
2811 #pragma nomember_alignment quadword
2812 struct _srqp {          /* VMS self-relative queue entry */
2813     unsigned long qptr[2];
2814 };
2815 #pragma member_alignment restore
2816 static RQE  RQE_ZERO = {0,0};
2817 
2818 struct _tochildbuf {
2819     RQE             q;
2820     int             eof;
2821     unsigned short  size;
2822     char            *buf;
2823 };
2824 
2825 struct _pipe {
2826     RQE            free;
2827     RQE            wait;
2828     int            fd_out;
2829     unsigned short chan_in;
2830     unsigned short chan_out;
2831     char          *buf;
2832     unsigned int   bufsize;
2833     IOSB           iosb;
2834     IOSB           iosb2;
2835     int           *pipe_done;
2836     int            retry;
2837     int            type;
2838     int            shut_on_empty;
2839     int            need_wake;
2840     pPipe         *home;
2841     pInfo          info;
2842     pCBuf          curr;
2843     pCBuf          curr2;
2844 #if defined(PERL_IMPLICIT_CONTEXT)
2845     void	    *thx;	    /* Either a thread or an interpreter */
2846                                     /* pointer, depending on how we're built */
2847 #endif
2848 };
2849 
2850 
2851 struct pipe_details
2852 {
2853     pInfo           next;
2854     PerlIO *fp;  /* file pointer to pipe mailbox */
2855     int useFILE; /* using stdio, not perlio */
2856     int pid;   /* PID of subprocess */
2857     int mode;  /* == 'r' if pipe open for reading */
2858     int done;  /* subprocess has completed */
2859     int waiting; /* waiting for completion/closure */
2860     int             closing;        /* my_pclose is closing this pipe */
2861     unsigned long   completion;     /* termination status of subprocess */
2862     pPipe           in;             /* pipe in to sub */
2863     pPipe           out;            /* pipe out of sub */
2864     pPipe           err;            /* pipe of sub's sys$error */
2865     int             in_done;        /* true when in pipe finished */
2866     int             out_done;
2867     int             err_done;
2868     unsigned short  xchan;	    /* channel to debug xterm */
2869     unsigned short  xchan_valid;    /* channel is assigned */
2870 };
2871 
2872 struct exit_control_block
2873 {
2874     struct exit_control_block *flink;
2875     unsigned long int (*exit_routine)(void);
2876     unsigned long int arg_count;
2877     unsigned long int *status_address;
2878     unsigned long int exit_status;
2879 };
2880 
2881 typedef struct _closed_pipes    Xpipe;
2882 typedef struct _closed_pipes*  pXpipe;
2883 
2884 struct _closed_pipes {
2885     int             pid;            /* PID of subprocess */
2886     unsigned long   completion;     /* termination status of subprocess */
2887 };
2888 #define NKEEPCLOSED 50
2889 static Xpipe closed_list[NKEEPCLOSED];
2890 static int   closed_index = 0;
2891 static int   closed_num = 0;
2892 
2893 #define RETRY_DELAY     "0 ::0.20"
2894 #define MAX_RETRY              50
2895 
2896 static int pipe_ef = 0;          /* first call to safe_popen inits these*/
2897 static unsigned long mypid;
2898 static unsigned long delaytime[2];
2899 
2900 static pInfo open_pipes = NULL;
2901 static $DESCRIPTOR(nl_desc, "NL:");
2902 
2903 #define PIPE_COMPLETION_WAIT    30  /* seconds, for EOF/FORCEX wait */
2904 
2905 
2906 
2907 static unsigned long int
2908 pipe_exit_routine(void)
2909 {
2910     pInfo info;
2911     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2912     int sts, did_stuff, j;
2913 
2914    /*
2915     * Flush any pending i/o, but since we are in process run-down, be
2916     * careful about referencing PerlIO structures that may already have
2917     * been deallocated.  We may not even have an interpreter anymore.
2918     */
2919     info = open_pipes;
2920     while (info) {
2921         if (info->fp) {
2922 #if defined(PERL_IMPLICIT_CONTEXT)
2923            /* We need to use the Perl context of the thread that created */
2924            /* the pipe. */
2925            pTHX;
2926            if (info->err)
2927                aTHX = info->err->thx;
2928            else if (info->out)
2929                aTHX = info->out->thx;
2930            else if (info->in)
2931                aTHX = info->in->thx;
2932 #endif
2933            if (!info->useFILE
2934 #if defined(USE_ITHREADS)
2935              && my_perl
2936 #endif
2937 #ifdef USE_PERLIO
2938              && PL_perlio_fd_refcnt
2939 #endif
2940               )
2941                PerlIO_flush(info->fp);
2942            else
2943                fflush((FILE *)info->fp);
2944         }
2945         info = info->next;
2946     }
2947 
2948     /*
2949      next we try sending an EOF...ignore if doesn't work, make sure we
2950      don't hang
2951     */
2952     did_stuff = 0;
2953     info = open_pipes;
2954 
2955     while (info) {
2956       _ckvmssts_noperl(sys$setast(0));
2957       if (info->in && !info->in->shut_on_empty) {
2958         _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2959                                  0, 0, 0, 0, 0, 0));
2960         info->waiting = 1;
2961         did_stuff = 1;
2962       }
2963       _ckvmssts_noperl(sys$setast(1));
2964       info = info->next;
2965     }
2966 
2967     /* wait for EOF to have effect, up to ~ 30 sec [default] */
2968 
2969     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2970         int nwait = 0;
2971 
2972         info = open_pipes;
2973         while (info) {
2974           _ckvmssts_noperl(sys$setast(0));
2975           if (info->waiting && info->done)
2976                 info->waiting = 0;
2977           nwait += info->waiting;
2978           _ckvmssts_noperl(sys$setast(1));
2979           info = info->next;
2980         }
2981         if (!nwait) break;
2982         sleep(1);
2983     }
2984 
2985     did_stuff = 0;
2986     info = open_pipes;
2987     while (info) {
2988       _ckvmssts_noperl(sys$setast(0));
2989       if (!info->done) { /* Tap them gently on the shoulder . . .*/
2990         sts = sys$forcex(&info->pid,0,&abort);
2991         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2992         did_stuff = 1;
2993       }
2994       _ckvmssts_noperl(sys$setast(1));
2995       info = info->next;
2996     }
2997 
2998     /* again, wait for effect */
2999 
3000     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3001         int nwait = 0;
3002 
3003         info = open_pipes;
3004         while (info) {
3005           _ckvmssts_noperl(sys$setast(0));
3006           if (info->waiting && info->done)
3007                 info->waiting = 0;
3008           nwait += info->waiting;
3009           _ckvmssts_noperl(sys$setast(1));
3010           info = info->next;
3011         }
3012         if (!nwait) break;
3013         sleep(1);
3014     }
3015 
3016     info = open_pipes;
3017     while (info) {
3018       _ckvmssts_noperl(sys$setast(0));
3019       if (!info->done) {  /* We tried to be nice . . . */
3020         sts = sys$delprc(&info->pid,0);
3021         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3022         info->done = 1;  /* sys$delprc is as done as we're going to get. */
3023       }
3024       _ckvmssts_noperl(sys$setast(1));
3025       info = info->next;
3026     }
3027 
3028     while(open_pipes) {
3029 
3030 #if defined(PERL_IMPLICIT_CONTEXT)
3031       /* We need to use the Perl context of the thread that created */
3032       /* the pipe. */
3033       pTHX;
3034       if (open_pipes->err)
3035           aTHX = open_pipes->err->thx;
3036       else if (open_pipes->out)
3037           aTHX = open_pipes->out->thx;
3038       else if (open_pipes->in)
3039           aTHX = open_pipes->in->thx;
3040 #endif
3041       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3042       else if (!(sts & 1)) retsts = sts;
3043     }
3044     return retsts;
3045 }
3046 
3047 static struct exit_control_block pipe_exitblock =
3048        {(struct exit_control_block *) 0,
3049         pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3050 
3051 static void pipe_mbxtofd_ast(pPipe p);
3052 static void pipe_tochild1_ast(pPipe p);
3053 static void pipe_tochild2_ast(pPipe p);
3054 
3055 static void
3056 popen_completion_ast(pInfo info)
3057 {
3058   pInfo i = open_pipes;
3059   int iss;
3060 
3061   info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3062   closed_list[closed_index].pid = info->pid;
3063   closed_list[closed_index].completion = info->completion;
3064   closed_index++;
3065   if (closed_index == NKEEPCLOSED)
3066     closed_index = 0;
3067   closed_num++;
3068 
3069   while (i) {
3070     if (i == info) break;
3071     i = i->next;
3072   }
3073   if (!i) return;       /* unlinked, probably freed too */
3074 
3075   info->done = TRUE;
3076 
3077 /*
3078     Writing to subprocess ...
3079             if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3080 
3081             chan_out may be waiting for "done" flag, or hung waiting
3082             for i/o completion to child...cancel the i/o.  This will
3083             put it into "snarf mode" (done but no EOF yet) that discards
3084             input.
3085 
3086     Output from subprocess (stdout, stderr) needs to be flushed and
3087     shut down.   We try sending an EOF, but if the mbx is full the pipe
3088     routine should still catch the "shut_on_empty" flag, telling it to
3089     use immediate-style reads so that "mbx empty" -> EOF.
3090 
3091 
3092 */
3093   if (info->in && !info->in_done) {               /* only for mode=w */
3094         if (info->in->shut_on_empty && info->in->need_wake) {
3095             info->in->need_wake = FALSE;
3096             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3097         } else {
3098             _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3099         }
3100   }
3101 
3102   if (info->out && !info->out_done) {             /* were we also piping output? */
3103       info->out->shut_on_empty = TRUE;
3104       iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3105       if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3106       _ckvmssts_noperl(iss);
3107   }
3108 
3109   if (info->err && !info->err_done) {        /* we were piping stderr */
3110         info->err->shut_on_empty = TRUE;
3111         iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3112         if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3113         _ckvmssts_noperl(iss);
3114   }
3115   _ckvmssts_noperl(sys$setef(pipe_ef));
3116 
3117 }
3118 
3119 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3120 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3121 static void pipe_infromchild_ast(pPipe p);
3122 
3123 /*
3124     I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3125     inside an AST routine without worrying about reentrancy and which Perl
3126     memory allocator is being used.
3127 
3128     We read data and queue up the buffers, then spit them out one at a
3129     time to the output mailbox when the output mailbox is ready for one.
3130 
3131 */
3132 #define INITIAL_TOCHILDQUEUE  2
3133 
3134 static pPipe
3135 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3136 {
3137     pPipe p;
3138     pCBuf b;
3139     char mbx1[64], mbx2[64];
3140     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3141                                       DSC$K_CLASS_S, mbx1},
3142                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3143                                       DSC$K_CLASS_S, mbx2};
3144     unsigned int dviitm = DVI$_DEVBUFSIZ;
3145     int j, n;
3146 
3147     n = sizeof(Pipe);
3148     _ckvmssts_noperl(lib$get_vm(&n, &p));
3149 
3150     create_mbx(&p->chan_in , &d_mbx1);
3151     create_mbx(&p->chan_out, &d_mbx2);
3152     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3153 
3154     p->buf           = 0;
3155     p->shut_on_empty = FALSE;
3156     p->need_wake     = FALSE;
3157     p->type          = 0;
3158     p->retry         = 0;
3159     p->iosb.status   = SS$_NORMAL;
3160     p->iosb2.status  = SS$_NORMAL;
3161     p->free          = RQE_ZERO;
3162     p->wait          = RQE_ZERO;
3163     p->curr          = 0;
3164     p->curr2         = 0;
3165     p->info          = 0;
3166 #ifdef PERL_IMPLICIT_CONTEXT
3167     p->thx	     = aTHX;
3168 #endif
3169 
3170     n = sizeof(CBuf) + p->bufsize;
3171 
3172     for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3173         _ckvmssts_noperl(lib$get_vm(&n, &b));
3174         b->buf = (char *) b + sizeof(CBuf);
3175         _ckvmssts_noperl(lib$insqhi(b, &p->free));
3176     }
3177 
3178     pipe_tochild2_ast(p);
3179     pipe_tochild1_ast(p);
3180     strcpy(wmbx, mbx1);
3181     strcpy(rmbx, mbx2);
3182     return p;
3183 }
3184 
3185 /*  reads the MBX Perl is writing, and queues */
3186 
3187 static void
3188 pipe_tochild1_ast(pPipe p)
3189 {
3190     pCBuf b = p->curr;
3191     int iss = p->iosb.status;
3192     int eof = (iss == SS$_ENDOFFILE);
3193     int sts;
3194 #ifdef PERL_IMPLICIT_CONTEXT
3195     pTHX = p->thx;
3196 #endif
3197 
3198     if (p->retry) {
3199         if (eof) {
3200             p->shut_on_empty = TRUE;
3201             b->eof     = TRUE;
3202             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3203         } else  {
3204             _ckvmssts_noperl(iss);
3205         }
3206 
3207         b->eof  = eof;
3208         b->size = p->iosb.count;
3209         _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3210         if (p->need_wake) {
3211             p->need_wake = FALSE;
3212             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3213         }
3214     } else {
3215         p->retry = 1;   /* initial call */
3216     }
3217 
3218     if (eof) {                  /* flush the free queue, return when done */
3219         int n = sizeof(CBuf) + p->bufsize;
3220         while (1) {
3221             iss = lib$remqti(&p->free, &b);
3222             if (iss == LIB$_QUEWASEMP) return;
3223             _ckvmssts_noperl(iss);
3224             _ckvmssts_noperl(lib$free_vm(&n, &b));
3225         }
3226     }
3227 
3228     iss = lib$remqti(&p->free, &b);
3229     if (iss == LIB$_QUEWASEMP) {
3230         int n = sizeof(CBuf) + p->bufsize;
3231         _ckvmssts_noperl(lib$get_vm(&n, &b));
3232         b->buf = (char *) b + sizeof(CBuf);
3233     } else {
3234        _ckvmssts_noperl(iss);
3235     }
3236 
3237     p->curr = b;
3238     iss = sys$qio(0,p->chan_in,
3239              IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3240              &p->iosb,
3241              pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3242     if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3243     _ckvmssts_noperl(iss);
3244 }
3245 
3246 
3247 /* writes queued buffers to output, waits for each to complete before
3248    doing the next */
3249 
3250 static void
3251 pipe_tochild2_ast(pPipe p)
3252 {
3253     pCBuf b = p->curr2;
3254     int iss = p->iosb2.status;
3255     int n = sizeof(CBuf) + p->bufsize;
3256     int done = (p->info && p->info->done) ||
3257               iss == SS$_CANCEL || iss == SS$_ABORT;
3258 #if defined(PERL_IMPLICIT_CONTEXT)
3259     pTHX = p->thx;
3260 #endif
3261 
3262     do {
3263         if (p->type) {         /* type=1 has old buffer, dispose */
3264             if (p->shut_on_empty) {
3265                 _ckvmssts_noperl(lib$free_vm(&n, &b));
3266             } else {
3267                 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3268             }
3269             p->type = 0;
3270         }
3271 
3272         iss = lib$remqti(&p->wait, &b);
3273         if (iss == LIB$_QUEWASEMP) {
3274             if (p->shut_on_empty) {
3275                 if (done) {
3276                     _ckvmssts_noperl(sys$dassgn(p->chan_out));
3277                     *p->pipe_done = TRUE;
3278                     _ckvmssts_noperl(sys$setef(pipe_ef));
3279                 } else {
3280                     _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3281                         &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3282                 }
3283                 return;
3284             }
3285             p->need_wake = TRUE;
3286             return;
3287         }
3288         _ckvmssts_noperl(iss);
3289         p->type = 1;
3290     } while (done);
3291 
3292 
3293     p->curr2 = b;
3294     if (b->eof) {
3295         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3296             &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3297     } else {
3298         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3299             &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3300     }
3301 
3302     return;
3303 
3304 }
3305 
3306 
3307 static pPipe
3308 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3309 {
3310     pPipe p;
3311     char mbx1[64], mbx2[64];
3312     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3313                                       DSC$K_CLASS_S, mbx1},
3314                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3315                                       DSC$K_CLASS_S, mbx2};
3316     unsigned int dviitm = DVI$_DEVBUFSIZ;
3317 
3318     int n = sizeof(Pipe);
3319     _ckvmssts_noperl(lib$get_vm(&n, &p));
3320     create_mbx(&p->chan_in , &d_mbx1);
3321     create_mbx(&p->chan_out, &d_mbx2);
3322 
3323     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3324     n = p->bufsize * sizeof(char);
3325     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3326     p->shut_on_empty = FALSE;
3327     p->info   = 0;
3328     p->type   = 0;
3329     p->iosb.status = SS$_NORMAL;
3330 #if defined(PERL_IMPLICIT_CONTEXT)
3331     p->thx = aTHX;
3332 #endif
3333     pipe_infromchild_ast(p);
3334 
3335     strcpy(wmbx, mbx1);
3336     strcpy(rmbx, mbx2);
3337     return p;
3338 }
3339 
3340 static void
3341 pipe_infromchild_ast(pPipe p)
3342 {
3343     int iss = p->iosb.status;
3344     int eof = (iss == SS$_ENDOFFILE);
3345     int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3346     int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3347 #if defined(PERL_IMPLICIT_CONTEXT)
3348     pTHX = p->thx;
3349 #endif
3350 
3351     if (p->info && p->info->closing && p->chan_out)  {           /* output shutdown */
3352         _ckvmssts_noperl(sys$dassgn(p->chan_out));
3353         p->chan_out = 0;
3354     }
3355 
3356     /* read completed:
3357             input shutdown if EOF from self (done or shut_on_empty)
3358             output shutdown if closing flag set (my_pclose)
3359             send data/eof from child or eof from self
3360             otherwise, re-read (snarf of data from child)
3361     */
3362 
3363     if (p->type == 1) {
3364         p->type = 0;
3365         if (myeof && p->chan_in) {                  /* input shutdown */
3366             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3367             p->chan_in = 0;
3368         }
3369 
3370         if (p->chan_out) {
3371             if (myeof || kideof) {      /* pass EOF to parent */
3372                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3373                                          pipe_infromchild_ast, p,
3374                                          0, 0, 0, 0, 0, 0));
3375                 return;
3376             } else if (eof) {       /* eat EOF --- fall through to read*/
3377 
3378             } else {                /* transmit data */
3379                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3380                                          pipe_infromchild_ast,p,
3381                                          p->buf, p->iosb.count, 0, 0, 0, 0));
3382                 return;
3383             }
3384         }
3385     }
3386 
3387     /*  everything shut? flag as done */
3388 
3389     if (!p->chan_in && !p->chan_out) {
3390         *p->pipe_done = TRUE;
3391         _ckvmssts_noperl(sys$setef(pipe_ef));
3392         return;
3393     }
3394 
3395     /* write completed (or read, if snarfing from child)
3396             if still have input active,
3397                queue read...immediate mode if shut_on_empty so we get EOF if empty
3398             otherwise,
3399                check if Perl reading, generate EOFs as needed
3400     */
3401 
3402     if (p->type == 0) {
3403         p->type = 1;
3404         if (p->chan_in) {
3405             iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3406                           pipe_infromchild_ast,p,
3407                           p->buf, p->bufsize, 0, 0, 0, 0);
3408             if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3409             _ckvmssts_noperl(iss);
3410         } else {           /* send EOFs for extra reads */
3411             p->iosb.status = SS$_ENDOFFILE;
3412             p->iosb.dvispec = 0;
3413             _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3414                                      0, 0, 0,
3415                                      pipe_infromchild_ast, p, 0, 0, 0, 0));
3416         }
3417     }
3418 }
3419 
3420 static pPipe
3421 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3422 {
3423     pPipe p;
3424     char mbx[64];
3425     unsigned long dviitm = DVI$_DEVBUFSIZ;
3426     struct stat s;
3427     struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3428                                       DSC$K_CLASS_S, mbx};
3429     int n = sizeof(Pipe);
3430 
3431     /* things like terminals and mbx's don't need this filter */
3432     if (fd && fstat(fd,&s) == 0) {
3433         unsigned long devchar;
3434 	char device[65];
3435 	unsigned short dev_len;
3436 	struct dsc$descriptor_s d_dev;
3437 	char * cptr;
3438 	struct item_list_3 items[3];
3439 	int status;
3440 	unsigned short dvi_iosb[4];
3441 
3442 	cptr = getname(fd, out, 1);
3443 	if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3444 	d_dev.dsc$a_pointer = out;
3445 	d_dev.dsc$w_length = strlen(out);
3446 	d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3447 	d_dev.dsc$b_class = DSC$K_CLASS_S;
3448 
3449 	items[0].len = 4;
3450 	items[0].code = DVI$_DEVCHAR;
3451 	items[0].bufadr = &devchar;
3452 	items[0].retadr = NULL;
3453 	items[1].len = 64;
3454 	items[1].code = DVI$_FULLDEVNAM;
3455 	items[1].bufadr = device;
3456 	items[1].retadr = &dev_len;
3457 	items[2].len = 0;
3458 	items[2].code = 0;
3459 
3460 	status = sys$getdviw
3461 	        (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3462 	_ckvmssts_noperl(status);
3463 	if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3464 	    device[dev_len] = 0;
3465 
3466 	    if (!(devchar & DEV$M_DIR)) {
3467 		strcpy(out, device);
3468 		return 0;
3469 	    }
3470 	}
3471     }
3472 
3473     _ckvmssts_noperl(lib$get_vm(&n, &p));
3474     p->fd_out = dup(fd);
3475     create_mbx(&p->chan_in, &d_mbx);
3476     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3477     n = (p->bufsize+1) * sizeof(char);
3478     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3479     p->shut_on_empty = FALSE;
3480     p->retry = 0;
3481     p->info  = 0;
3482     strcpy(out, mbx);
3483 
3484     _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3485                              pipe_mbxtofd_ast, p,
3486                              p->buf, p->bufsize, 0, 0, 0, 0));
3487 
3488     return p;
3489 }
3490 
3491 static void
3492 pipe_mbxtofd_ast(pPipe p)
3493 {
3494     int iss = p->iosb.status;
3495     int done = p->info->done;
3496     int iss2;
3497     int eof = (iss == SS$_ENDOFFILE);
3498     int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3499     int err = !(iss&1) && !eof;
3500 #if defined(PERL_IMPLICIT_CONTEXT)
3501     pTHX = p->thx;
3502 #endif
3503 
3504     if (done && myeof) {               /* end piping */
3505         close(p->fd_out);
3506         sys$dassgn(p->chan_in);
3507         *p->pipe_done = TRUE;
3508         _ckvmssts_noperl(sys$setef(pipe_ef));
3509         return;
3510     }
3511 
3512     if (!err && !eof) {             /* good data to send to file */
3513         p->buf[p->iosb.count] = '\n';
3514         iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3515         if (iss2 < 0) {
3516             p->retry++;
3517             if (p->retry < MAX_RETRY) {
3518                 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3519                 return;
3520             }
3521         }
3522         p->retry = 0;
3523     } else if (err) {
3524         _ckvmssts_noperl(iss);
3525     }
3526 
3527 
3528     iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3529           pipe_mbxtofd_ast, p,
3530           p->buf, p->bufsize, 0, 0, 0, 0);
3531     if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3532     _ckvmssts_noperl(iss);
3533 }
3534 
3535 
3536 typedef struct _pipeloc     PLOC;
3537 typedef struct _pipeloc*   pPLOC;
3538 
3539 struct _pipeloc {
3540     pPLOC   next;
3541     char    dir[NAM$C_MAXRSS+1];
3542 };
3543 static pPLOC  head_PLOC = 0;
3544 
3545 void
3546 free_pipelocs(pTHX_ void *head)
3547 {
3548     pPLOC p, pnext;
3549     pPLOC *pHead = (pPLOC *)head;
3550 
3551     p = *pHead;
3552     while (p) {
3553         pnext = p->next;
3554         PerlMem_free(p);
3555         p = pnext;
3556     }
3557     *pHead = 0;
3558 }
3559 
3560 static void
3561 store_pipelocs(pTHX)
3562 {
3563     int    i;
3564     pPLOC  p;
3565     AV    *av = 0;
3566     SV    *dirsv;
3567     char  *dir, *x;
3568     char  *unixdir;
3569     char  temp[NAM$C_MAXRSS+1];
3570     STRLEN n_a;
3571 
3572     if (head_PLOC)
3573         free_pipelocs(aTHX_ &head_PLOC);
3574 
3575 /*  the . directory from @INC comes last */
3576 
3577     p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3578     if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3579     p->next = head_PLOC;
3580     head_PLOC = p;
3581     strcpy(p->dir,"./");
3582 
3583 /*  get the directory from $^X */
3584 
3585     unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3586     if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3587 
3588 #ifdef PERL_IMPLICIT_CONTEXT
3589     if (aTHX && PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3590 #else
3591     if (PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3592 #endif
3593         my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3594         x = strrchr(temp,']');
3595 	if (x == NULL) {
3596 	x = strrchr(temp,'>');
3597 	  if (x == NULL) {
3598 	    /* It could be a UNIX path */
3599 	    x = strrchr(temp,'/');
3600 	  }
3601 	}
3602 	if (x)
3603 	  x[1] = '\0';
3604 	else {
3605 	  /* Got a bare name, so use default directory */
3606 	  temp[0] = '.';
3607 	  temp[1] = '\0';
3608 	}
3609 
3610         if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3611             p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3612 	    if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3613             p->next = head_PLOC;
3614             head_PLOC = p;
3615             my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3616 	}
3617     }
3618 
3619 /*  reverse order of @INC entries, skip "." since entered above */
3620 
3621 #ifdef PERL_IMPLICIT_CONTEXT
3622     if (aTHX)
3623 #endif
3624     if (PL_incgv) av = GvAVn(PL_incgv);
3625 
3626     for (i = 0; av && i <= AvFILL(av); i++) {
3627         dirsv = *av_fetch(av,i,TRUE);
3628 
3629         if (SvROK(dirsv)) continue;
3630         dir = SvPVx(dirsv,n_a);
3631         if (strcmp(dir,".") == 0) continue;
3632         if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3633             continue;
3634 
3635         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3636         p->next = head_PLOC;
3637         head_PLOC = p;
3638         my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3639     }
3640 
3641 /* most likely spot (ARCHLIB) put first in the list */
3642 
3643 #ifdef ARCHLIB_EXP
3644     if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3645         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3646 	if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3647         p->next = head_PLOC;
3648         head_PLOC = p;
3649         my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3650     }
3651 #endif
3652     PerlMem_free(unixdir);
3653 }
3654 
3655 static I32
3656 Perl_cando_by_name_int
3657    (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3658 #if !defined(PERL_IMPLICIT_CONTEXT)
3659 #define cando_by_name_int		Perl_cando_by_name_int
3660 #else
3661 #define cando_by_name_int(a,b,c,d)	Perl_cando_by_name_int(aTHX_ a,b,c,d)
3662 #endif
3663 
3664 static char *
3665 find_vmspipe(pTHX)
3666 {
3667     static int   vmspipe_file_status = 0;
3668     static char  vmspipe_file[NAM$C_MAXRSS+1];
3669 
3670     /* already found? Check and use ... need read+execute permission */
3671 
3672     if (vmspipe_file_status == 1) {
3673         if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3674          && cando_by_name_int
3675 	   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3676             return vmspipe_file;
3677         }
3678         vmspipe_file_status = 0;
3679     }
3680 
3681     /* scan through stored @INC, $^X */
3682 
3683     if (vmspipe_file_status == 0) {
3684         char file[NAM$C_MAXRSS+1];
3685         pPLOC  p = head_PLOC;
3686 
3687         while (p) {
3688 	    char * exp_res;
3689 	    int dirlen;
3690 	    dirlen = my_strlcpy(file, p->dir, sizeof(file));
3691             my_strlcat(file, "vmspipe.com", sizeof(file));
3692             p = p->next;
3693 
3694             exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3695             if (!exp_res) continue;
3696 
3697             if (cando_by_name_int
3698 		(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3699              && cando_by_name_int
3700 		   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3701                 vmspipe_file_status = 1;
3702                 return vmspipe_file;
3703             }
3704         }
3705         vmspipe_file_status = -1;   /* failed, use tempfiles */
3706     }
3707 
3708     return 0;
3709 }
3710 
3711 static FILE *
3712 vmspipe_tempfile(pTHX)
3713 {
3714     char file[NAM$C_MAXRSS+1];
3715     FILE *fp;
3716     static int index = 0;
3717     Stat_t s0, s1;
3718     int cmp_result;
3719 
3720     /* create a tempfile */
3721 
3722     /* we can't go from   W, shr=get to  R, shr=get without
3723        an intermediate vulnerable state, so don't bother trying...
3724 
3725        and lib$spawn doesn't shr=put, so have to close the write
3726 
3727        So... match up the creation date/time and the FID to
3728        make sure we're dealing with the same file
3729 
3730     */
3731 
3732     index++;
3733     if (!decc_filename_unix_only) {
3734       sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3735       fp = fopen(file,"w");
3736       if (!fp) {
3737         sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3738         fp = fopen(file,"w");
3739         if (!fp) {
3740             sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3741             fp = fopen(file,"w");
3742 	}
3743       }
3744      }
3745      else {
3746       sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3747       fp = fopen(file,"w");
3748       if (!fp) {
3749 	sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3750 	fp = fopen(file,"w");
3751 	if (!fp) {
3752 	  sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3753 	  fp = fopen(file,"w");
3754 	}
3755       }
3756     }
3757     if (!fp) return 0;  /* we're hosed */
3758 
3759     fprintf(fp,"$! 'f$verify(0)'\n");
3760     fprintf(fp,"$!  ---  protect against nonstandard definitions ---\n");
3761     fprintf(fp,"$ perl_cfile  = f$environment(\"procedure\")\n");
3762     fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3763     fprintf(fp,"$ perl_on     = \"set noon\"\n");
3764     fprintf(fp,"$ perl_exit   = \"exit\"\n");
3765     fprintf(fp,"$ perl_del    = \"delete\"\n");
3766     fprintf(fp,"$ pif         = \"if\"\n");
3767     fprintf(fp,"$!  --- define i/o redirection (sys$output set by lib$spawn)\n");
3768     fprintf(fp,"$ pif perl_popen_in  .nes. \"\" then perl_define/user/name_attributes=confine sys$input  'perl_popen_in'\n");
3769     fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error  'perl_popen_err'\n");
3770     fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define      sys$output 'perl_popen_out'\n");
3771     fprintf(fp,"$!  --- build command line to get max possible length\n");
3772     fprintf(fp,"$c=perl_popen_cmd0\n");
3773     fprintf(fp,"$c=c+perl_popen_cmd1\n");
3774     fprintf(fp,"$c=c+perl_popen_cmd2\n");
3775     fprintf(fp,"$x=perl_popen_cmd3\n");
3776     fprintf(fp,"$c=c+x\n");
3777     fprintf(fp,"$ perl_on\n");
3778     fprintf(fp,"$ 'c'\n");
3779     fprintf(fp,"$ perl_status = $STATUS\n");
3780     fprintf(fp,"$ perl_del  'perl_cfile'\n");
3781     fprintf(fp,"$ perl_exit 'perl_status'\n");
3782     fsync(fileno(fp));
3783 
3784     fgetname(fp, file, 1);
3785     fstat(fileno(fp), &s0.crtl_stat);
3786     fclose(fp);
3787 
3788     if (decc_filename_unix_only)
3789 	int_tounixspec(file, file, NULL);
3790     fp = fopen(file,"r","shr=get");
3791     if (!fp) return 0;
3792     fstat(fileno(fp), &s1.crtl_stat);
3793 
3794     cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3795     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
3796         fclose(fp);
3797         return 0;
3798     }
3799 
3800     return fp;
3801 }
3802 
3803 
3804 static int vms_is_syscommand_xterm(void)
3805 {
3806     const static struct dsc$descriptor_s syscommand_dsc =
3807       { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3808 
3809     const static struct dsc$descriptor_s decwdisplay_dsc =
3810       { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3811 
3812     struct item_list_3 items[2];
3813     unsigned short dvi_iosb[4];
3814     unsigned long devchar;
3815     unsigned long devclass;
3816     int status;
3817 
3818     /* Very simple check to guess if sys$command is a decterm? */
3819     /* First see if the DECW$DISPLAY: device exists */
3820     items[0].len = 4;
3821     items[0].code = DVI$_DEVCHAR;
3822     items[0].bufadr = &devchar;
3823     items[0].retadr = NULL;
3824     items[1].len = 0;
3825     items[1].code = 0;
3826 
3827     status = sys$getdviw
3828 	(NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3829 
3830     if ($VMS_STATUS_SUCCESS(status)) {
3831         status = dvi_iosb[0];
3832     }
3833 
3834     if (!$VMS_STATUS_SUCCESS(status)) {
3835         SETERRNO(EVMSERR, status);
3836 	return -1;
3837     }
3838 
3839     /* If it does, then for now assume that we are on a workstation */
3840     /* Now verify that SYS$COMMAND is a terminal */
3841     /* for creating the debugger DECTerm */
3842 
3843     items[0].len = 4;
3844     items[0].code = DVI$_DEVCLASS;
3845     items[0].bufadr = &devclass;
3846     items[0].retadr = NULL;
3847     items[1].len = 0;
3848     items[1].code = 0;
3849 
3850     status = sys$getdviw
3851 	(NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3852 
3853     if ($VMS_STATUS_SUCCESS(status)) {
3854         status = dvi_iosb[0];
3855     }
3856 
3857     if (!$VMS_STATUS_SUCCESS(status)) {
3858         SETERRNO(EVMSERR, status);
3859 	return -1;
3860     }
3861     else {
3862 	if (devclass == DC$_TERM) {
3863 	    return 0;
3864 	}
3865     }
3866     return -1;
3867 }
3868 
3869 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3870 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3871 {
3872     int status;
3873     int ret_stat;
3874     char * ret_char;
3875     char device_name[65];
3876     unsigned short device_name_len;
3877     struct dsc$descriptor_s customization_dsc;
3878     struct dsc$descriptor_s device_name_dsc;
3879     const char * cptr;
3880     char customization[200];
3881     char title[40];
3882     pInfo info = NULL;
3883     char mbx1[64];
3884     unsigned short p_chan;
3885     int n;
3886     unsigned short iosb[4];
3887     const char * cust_str =
3888         "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3889     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3890                                           DSC$K_CLASS_S, mbx1};
3891 
3892      /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3893     /*---------------------------------------*/
3894     VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3895 
3896 
3897     /* Make sure that this is from the Perl debugger */
3898     ret_char = strstr(cmd," xterm ");
3899     if (ret_char == NULL)
3900 	return NULL;
3901     cptr = ret_char + 7;
3902     ret_char = strstr(cmd,"tty");
3903     if (ret_char == NULL)
3904 	return NULL;
3905     ret_char = strstr(cmd,"sleep");
3906     if (ret_char == NULL)
3907 	return NULL;
3908 
3909     if (decw_term_port == 0) {
3910 	$DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3911 	$DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3912 	$DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3913 
3914        status = lib$find_image_symbol
3915 			       (&filename1_dsc,
3916 				&decw_term_port_dsc,
3917 				(void *)&decw_term_port,
3918 				NULL,
3919 				0);
3920 
3921 	/* Try again with the other image name */
3922 	if (!$VMS_STATUS_SUCCESS(status)) {
3923 
3924            status = lib$find_image_symbol
3925 			       (&filename2_dsc,
3926 				&decw_term_port_dsc,
3927 				(void *)&decw_term_port,
3928 				NULL,
3929 				0);
3930 
3931 	}
3932 
3933     }
3934 
3935 
3936     /* No decw$term_port, give it up */
3937     if (!$VMS_STATUS_SUCCESS(status))
3938 	return NULL;
3939 
3940     /* Are we on a workstation? */
3941     /* to do: capture the rows / columns and pass their properties */
3942     ret_stat = vms_is_syscommand_xterm();
3943     if (ret_stat < 0)
3944 	return NULL;
3945 
3946     /* Make the title: */
3947     ret_char = strstr(cptr,"-title");
3948     if (ret_char != NULL) {
3949 	while ((*cptr != 0) && (*cptr != '\"')) {
3950 	    cptr++;
3951 	}
3952 	if (*cptr == '\"')
3953 	    cptr++;
3954 	n = 0;
3955 	while ((*cptr != 0) && (*cptr != '\"')) {
3956 	    title[n] = *cptr;
3957 	    n++;
3958 	    if (n == 39) {
3959 		title[39] = 0;
3960 		break;
3961 	    }
3962 	    cptr++;
3963 	}
3964 	title[n] = 0;
3965     }
3966     else {
3967 	    /* Default title */
3968 	    strcpy(title,"Perl Debug DECTerm");
3969     }
3970     sprintf(customization, cust_str, title);
3971 
3972     customization_dsc.dsc$a_pointer = customization;
3973     customization_dsc.dsc$w_length = strlen(customization);
3974     customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3975     customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3976 
3977     device_name_dsc.dsc$a_pointer = device_name;
3978     device_name_dsc.dsc$w_length = sizeof device_name -1;
3979     device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3980     device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3981 
3982     device_name_len = 0;
3983 
3984     /* Try to create the window */
3985      status = (*decw_term_port)
3986        (NULL,
3987 	NULL,
3988 	&customization_dsc,
3989 	&device_name_dsc,
3990 	&device_name_len,
3991 	NULL,
3992 	NULL,
3993 	NULL);
3994     if (!$VMS_STATUS_SUCCESS(status)) {
3995         SETERRNO(EVMSERR, status);
3996 	return NULL;
3997     }
3998 
3999     device_name[device_name_len] = '\0';
4000 
4001     /* Need to set this up to look like a pipe for cleanup */
4002     n = sizeof(Info);
4003     status = lib$get_vm(&n, &info);
4004     if (!$VMS_STATUS_SUCCESS(status)) {
4005         SETERRNO(ENOMEM, status);
4006         return NULL;
4007     }
4008 
4009     info->mode = *mode;
4010     info->done = FALSE;
4011     info->completion = 0;
4012     info->closing    = FALSE;
4013     info->in         = 0;
4014     info->out        = 0;
4015     info->err        = 0;
4016     info->fp         = NULL;
4017     info->useFILE    = 0;
4018     info->waiting    = 0;
4019     info->in_done    = TRUE;
4020     info->out_done   = TRUE;
4021     info->err_done   = TRUE;
4022 
4023     /* Assign a channel on this so that it will persist, and not login */
4024     /* We stash this channel in the info structure for reference. */
4025     /* The created xterm self destructs when the last channel is removed */
4026     /* and it appears that perl5db.pl (perl debugger) does this routinely */
4027     /* So leave this assigned. */
4028     device_name_dsc.dsc$w_length = device_name_len;
4029     status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4030     if (!$VMS_STATUS_SUCCESS(status)) {
4031         SETERRNO(EVMSERR, status);
4032 	return NULL;
4033     }
4034     info->xchan_valid = 1;
4035 
4036     /* Now create a mailbox to be read by the application */
4037 
4038     create_mbx(&p_chan, &d_mbx1);
4039 
4040     /* write the name of the created terminal to the mailbox */
4041     status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4042             iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4043 
4044     if (!$VMS_STATUS_SUCCESS(status)) {
4045         SETERRNO(EVMSERR, status);
4046 	return NULL;
4047     }
4048 
4049     info->fp  = PerlIO_open(mbx1, mode);
4050 
4051     /* Done with this channel */
4052     sys$dassgn(p_chan);
4053 
4054     /* If any errors, then clean up */
4055     if (!info->fp) {
4056        	n = sizeof(Info);
4057 	_ckvmssts_noperl(lib$free_vm(&n, &info));
4058 	return NULL;
4059         }
4060 
4061     /* All done */
4062     return info->fp;
4063 }
4064 
4065 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4066 
4067 static PerlIO *
4068 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4069 {
4070     static int handler_set_up = FALSE;
4071     PerlIO * ret_fp;
4072     unsigned long int sts, flags = CLI$M_NOWAIT;
4073     /* The use of a GLOBAL table (as was done previously) rendered
4074      * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4075      * environment.  Hence we've switched to LOCAL symbol table.
4076      */
4077     unsigned int table = LIB$K_CLI_LOCAL_SYM;
4078     int j, wait = 0, n;
4079     char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4080     char *in, *out, *err, mbx[512];
4081     FILE *tpipe = 0;
4082     char tfilebuf[NAM$C_MAXRSS+1];
4083     pInfo info = NULL;
4084     char cmd_sym_name[20];
4085     struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4086                                       DSC$K_CLASS_S, symbol};
4087     struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4088                                       DSC$K_CLASS_S, 0};
4089     struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4090                                       DSC$K_CLASS_S, cmd_sym_name};
4091     struct dsc$descriptor_s *vmscmd;
4092     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4093     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4094     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4095 
4096     /* Check here for Xterm create request.  This means looking for
4097      * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4098      *  is possible to create an xterm.
4099      */
4100     if (*in_mode == 'r') {
4101         PerlIO * xterm_fd;
4102 
4103 #if defined(PERL_IMPLICIT_CONTEXT)
4104         /* Can not fork an xterm with a NULL context */
4105         /* This probably could never happen */
4106         xterm_fd = NULL;
4107         if (aTHX != NULL)
4108 #endif
4109 	xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4110 	if (xterm_fd != NULL)
4111 	    return xterm_fd;
4112     }
4113 
4114     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static vmspipe file */
4115 
4116     /* once-per-program initialization...
4117        note that the SETAST calls and the dual test of pipe_ef
4118        makes sure that only the FIRST thread through here does
4119        the initialization...all other threads wait until it's
4120        done.
4121 
4122        Yeah, uglier than a pthread call, it's got all the stuff inline
4123        rather than in a separate routine.
4124     */
4125 
4126     if (!pipe_ef) {
4127         _ckvmssts_noperl(sys$setast(0));
4128         if (!pipe_ef) {
4129             unsigned long int pidcode = JPI$_PID;
4130             $DESCRIPTOR(d_delay, RETRY_DELAY);
4131             _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4132             _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4133             _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4134         }
4135         if (!handler_set_up) {
4136           _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4137           handler_set_up = TRUE;
4138         }
4139         _ckvmssts_noperl(sys$setast(1));
4140     }
4141 
4142     /* see if we can find a VMSPIPE.COM */
4143 
4144     tfilebuf[0] = '@';
4145     vmspipe = find_vmspipe(aTHX);
4146     if (vmspipe) {
4147         vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4148     } else {        /* uh, oh...we're in tempfile hell */
4149         tpipe = vmspipe_tempfile(aTHX);
4150         if (!tpipe) {       /* a fish popular in Boston */
4151             if (ckWARN(WARN_PIPE)) {
4152                 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4153             }
4154         return NULL;
4155         }
4156         fgetname(tpipe,tfilebuf+1,1);
4157         vmspipedsc.dsc$w_length  = strlen(tfilebuf);
4158     }
4159     vmspipedsc.dsc$a_pointer = tfilebuf;
4160 
4161     sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4162     if (!(sts & 1)) {
4163       switch (sts) {
4164         case RMS$_FNF:  case RMS$_DNF:
4165           set_errno(ENOENT); break;
4166         case RMS$_DIR:
4167           set_errno(ENOTDIR); break;
4168         case RMS$_DEV:
4169           set_errno(ENODEV); break;
4170         case RMS$_PRV:
4171           set_errno(EACCES); break;
4172         case RMS$_SYN:
4173           set_errno(EINVAL); break;
4174         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4175           set_errno(E2BIG); break;
4176         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4177           _ckvmssts_noperl(sts); /* fall through */
4178         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4179           set_errno(EVMSERR);
4180       }
4181       set_vaxc_errno(sts);
4182       if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4183         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4184       }
4185       *psts = sts;
4186       return NULL;
4187     }
4188     n = sizeof(Info);
4189     _ckvmssts_noperl(lib$get_vm(&n, &info));
4190 
4191     my_strlcpy(mode, in_mode, sizeof(mode));
4192     info->mode = *mode;
4193     info->done = FALSE;
4194     info->completion = 0;
4195     info->closing    = FALSE;
4196     info->in         = 0;
4197     info->out        = 0;
4198     info->err        = 0;
4199     info->fp         = NULL;
4200     info->useFILE    = 0;
4201     info->waiting    = 0;
4202     info->in_done    = TRUE;
4203     info->out_done   = TRUE;
4204     info->err_done   = TRUE;
4205     info->xchan      = 0;
4206     info->xchan_valid = 0;
4207 
4208     in = (char *)PerlMem_malloc(VMS_MAXRSS);
4209     if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4210     out = (char *)PerlMem_malloc(VMS_MAXRSS);
4211     if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4212     err = (char *)PerlMem_malloc(VMS_MAXRSS);
4213     if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4214 
4215     in[0] = out[0] = err[0] = '\0';
4216 
4217     if ((p = strchr(mode,'F')) != NULL) {   /* F -> use FILE* */
4218         info->useFILE = 1;
4219         strcpy(p,p+1);
4220     }
4221     if ((p = strchr(mode,'W')) != NULL) {   /* W -> wait for completion */
4222         wait = 1;
4223         strcpy(p,p+1);
4224     }
4225 
4226     if (*mode == 'r') {             /* piping from subroutine */
4227 
4228         info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4229         if (info->out) {
4230             info->out->pipe_done = &info->out_done;
4231             info->out_done = FALSE;
4232             info->out->info = info;
4233         }
4234         if (!info->useFILE) {
4235 	    info->fp  = PerlIO_open(mbx, mode);
4236         } else {
4237             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4238             vmssetuserlnm("SYS$INPUT", mbx);
4239         }
4240 
4241         if (!info->fp && info->out) {
4242             sys$cancel(info->out->chan_out);
4243 
4244             while (!info->out_done) {
4245                 int done;
4246                 _ckvmssts_noperl(sys$setast(0));
4247                 done = info->out_done;
4248                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4249                 _ckvmssts_noperl(sys$setast(1));
4250                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4251             }
4252 
4253             if (info->out->buf) {
4254                 n = info->out->bufsize * sizeof(char);
4255                 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4256             }
4257             n = sizeof(Pipe);
4258             _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4259             n = sizeof(Info);
4260             _ckvmssts_noperl(lib$free_vm(&n, &info));
4261             *psts = RMS$_FNF;
4262             return NULL;
4263         }
4264 
4265         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4266         if (info->err) {
4267             info->err->pipe_done = &info->err_done;
4268             info->err_done = FALSE;
4269             info->err->info = info;
4270         }
4271 
4272     } else if (*mode == 'w') {      /* piping to subroutine */
4273 
4274         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4275         if (info->out) {
4276             info->out->pipe_done = &info->out_done;
4277             info->out_done = FALSE;
4278             info->out->info = info;
4279         }
4280 
4281         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4282         if (info->err) {
4283             info->err->pipe_done = &info->err_done;
4284             info->err_done = FALSE;
4285             info->err->info = info;
4286         }
4287 
4288         info->in = pipe_tochild_setup(aTHX_ in,mbx);
4289         if (!info->useFILE) {
4290 	    info->fp  = PerlIO_open(mbx, mode);
4291         } else {
4292             info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4293             vmssetuserlnm("SYS$OUTPUT", mbx);
4294         }
4295 
4296         if (info->in) {
4297             info->in->pipe_done = &info->in_done;
4298             info->in_done = FALSE;
4299             info->in->info = info;
4300         }
4301 
4302         /* error cleanup */
4303         if (!info->fp && info->in) {
4304             info->done = TRUE;
4305             _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4306                                       0, 0, 0, 0, 0, 0, 0, 0));
4307 
4308             while (!info->in_done) {
4309                 int done;
4310                 _ckvmssts_noperl(sys$setast(0));
4311                 done = info->in_done;
4312                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4313                 _ckvmssts_noperl(sys$setast(1));
4314                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4315             }
4316 
4317             if (info->in->buf) {
4318                 n = info->in->bufsize * sizeof(char);
4319                 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4320             }
4321             n = sizeof(Pipe);
4322             _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4323             n = sizeof(Info);
4324             _ckvmssts_noperl(lib$free_vm(&n, &info));
4325             *psts = RMS$_FNF;
4326             return NULL;
4327         }
4328 
4329 
4330     } else if (*mode == 'n') {       /* separate subprocess, no Perl i/o */
4331         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4332         if (info->out) {
4333             info->out->pipe_done = &info->out_done;
4334             info->out_done = FALSE;
4335             info->out->info = info;
4336         }
4337 
4338         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4339         if (info->err) {
4340             info->err->pipe_done = &info->err_done;
4341             info->err_done = FALSE;
4342             info->err->info = info;
4343         }
4344     }
4345 
4346     d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4347     _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4348 
4349     d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4350     _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4351 
4352     d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4353     _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4354 
4355     /* Done with the names for the pipes */
4356     PerlMem_free(err);
4357     PerlMem_free(out);
4358     PerlMem_free(in);
4359 
4360     p = vmscmd->dsc$a_pointer;
4361     while (*p == ' ' || *p == '\t') p++;        /* remove leading whitespace */
4362     if (*p == '$') p++;                         /* remove leading $ */
4363     while (*p == ' ' || *p == '\t') p++;
4364 
4365     for (j = 0; j < 4; j++) {
4366         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4367         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4368 
4369     d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4370     _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4371 
4372         if (strlen(p) > MAX_DCL_SYMBOL) {
4373             p += MAX_DCL_SYMBOL;
4374         } else {
4375             p += strlen(p);
4376         }
4377     }
4378     _ckvmssts_noperl(sys$setast(0));
4379     info->next=open_pipes;  /* prepend to list */
4380     open_pipes=info;
4381     _ckvmssts_noperl(sys$setast(1));
4382     /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4383      * and SYS$COMMAND.  vmspipe.com will redefine SYS$INPUT, but we'll still
4384      * have SYS$COMMAND if we need it.
4385      */
4386     _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4387                       0, &info->pid, &info->completion,
4388                       0, popen_completion_ast,info,0,0,0));
4389 
4390     /* if we were using a tempfile, close it now */
4391 
4392     if (tpipe) fclose(tpipe);
4393 
4394     /* once the subprocess is spawned, it has copied the symbols and
4395        we can get rid of ours */
4396 
4397     for (j = 0; j < 4; j++) {
4398         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4399         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4400     _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4401     }
4402     _ckvmssts_noperl(lib$delete_symbol(&d_sym_in,  &table));
4403     _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4404     _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4405     vms_execfree(vmscmd);
4406 
4407 #ifdef PERL_IMPLICIT_CONTEXT
4408     if (aTHX)
4409 #endif
4410     PL_forkprocess = info->pid;
4411 
4412     ret_fp = info->fp;
4413     if (wait) {
4414          dSAVEDERRNO;
4415          int done = 0;
4416          while (!done) {
4417              _ckvmssts_noperl(sys$setast(0));
4418              done = info->done;
4419              if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4420              _ckvmssts_noperl(sys$setast(1));
4421              if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4422          }
4423         *psts = info->completion;
4424 /* Caller thinks it is open and tries to close it. */
4425 /* This causes some problems, as it changes the error status */
4426 /*        my_pclose(info->fp); */
4427 
4428          /* If we did not have a file pointer open, then we have to */
4429          /* clean up here or eventually we will run out of something */
4430          SAVE_ERRNO;
4431          if (info->fp == NULL) {
4432              my_pclose_pinfo(aTHX_ info);
4433          }
4434          RESTORE_ERRNO;
4435 
4436     } else {
4437         *psts = info->pid;
4438     }
4439     return ret_fp;
4440 }  /* end of safe_popen */
4441 
4442 
4443 /*{{{  PerlIO *my_popen(char *cmd, char *mode)*/
4444 PerlIO *
4445 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4446 {
4447     int sts;
4448     TAINT_ENV();
4449     TAINT_PROPER("popen");
4450     PERL_FLUSHALL_FOR_CHILD;
4451     return safe_popen(aTHX_ cmd,mode,&sts);
4452 }
4453 
4454 /*}}}*/
4455 
4456 
4457 /* Routine to close and cleanup a pipe info structure */
4458 
4459 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4460 
4461     unsigned long int retsts;
4462     int done, n;
4463     pInfo next, last;
4464 
4465     /* If we were writing to a subprocess, insure that someone reading from
4466      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
4467      * produce an EOF record in the mailbox.
4468      *
4469      *  well, at least sometimes it *does*, so we have to watch out for
4470      *  the first EOF closing the pipe (and DASSGN'ing the channel)...
4471      */
4472      if (info->fp) {
4473         if (!info->useFILE
4474 #if defined(USE_ITHREADS)
4475           && my_perl
4476 #endif
4477 #ifdef USE_PERLIO
4478           && PL_perlio_fd_refcnt
4479 #endif
4480            )
4481             PerlIO_flush(info->fp);
4482         else
4483             fflush((FILE *)info->fp);
4484     }
4485 
4486     _ckvmssts(sys$setast(0));
4487      info->closing = TRUE;
4488      done = info->done && info->in_done && info->out_done && info->err_done;
4489      /* hanging on write to Perl's input? cancel it */
4490      if (info->mode == 'r' && info->out && !info->out_done) {
4491         if (info->out->chan_out) {
4492             _ckvmssts(sys$cancel(info->out->chan_out));
4493             if (!info->out->chan_in) {   /* EOF generation, need AST */
4494                 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4495             }
4496         }
4497      }
4498      if (info->in && !info->in_done && !info->in->shut_on_empty)  /* EOF if hasn't had one yet */
4499          _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4500                            0, 0, 0, 0, 0, 0));
4501     _ckvmssts(sys$setast(1));
4502     if (info->fp) {
4503      if (!info->useFILE
4504 #if defined(USE_ITHREADS)
4505          && my_perl
4506 #endif
4507 #ifdef USE_PERLIO
4508          && PL_perlio_fd_refcnt
4509 #endif
4510         )
4511         PerlIO_close(info->fp);
4512      else
4513         fclose((FILE *)info->fp);
4514     }
4515      /*
4516         we have to wait until subprocess completes, but ALSO wait until all
4517         the i/o completes...otherwise we'll be freeing the "info" structure
4518         that the i/o ASTs could still be using...
4519      */
4520 
4521      while (!done) {
4522          _ckvmssts(sys$setast(0));
4523          done = info->done && info->in_done && info->out_done && info->err_done;
4524          if (!done) _ckvmssts(sys$clref(pipe_ef));
4525          _ckvmssts(sys$setast(1));
4526          if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4527      }
4528      retsts = info->completion;
4529 
4530     /* remove from list of open pipes */
4531     _ckvmssts(sys$setast(0));
4532     last = NULL;
4533     for (next = open_pipes; next != NULL; last = next, next = next->next) {
4534         if (next == info)
4535             break;
4536     }
4537 
4538     if (last)
4539         last->next = info->next;
4540     else
4541         open_pipes = info->next;
4542     _ckvmssts(sys$setast(1));
4543 
4544     /* free buffers and structures */
4545 
4546     if (info->in) {
4547         if (info->in->buf) {
4548             n = info->in->bufsize * sizeof(char);
4549             _ckvmssts(lib$free_vm(&n, &info->in->buf));
4550         }
4551         n = sizeof(Pipe);
4552         _ckvmssts(lib$free_vm(&n, &info->in));
4553     }
4554     if (info->out) {
4555         if (info->out->buf) {
4556             n = info->out->bufsize * sizeof(char);
4557             _ckvmssts(lib$free_vm(&n, &info->out->buf));
4558         }
4559         n = sizeof(Pipe);
4560         _ckvmssts(lib$free_vm(&n, &info->out));
4561     }
4562     if (info->err) {
4563         if (info->err->buf) {
4564             n = info->err->bufsize * sizeof(char);
4565             _ckvmssts(lib$free_vm(&n, &info->err->buf));
4566         }
4567         n = sizeof(Pipe);
4568         _ckvmssts(lib$free_vm(&n, &info->err));
4569     }
4570     n = sizeof(Info);
4571     _ckvmssts(lib$free_vm(&n, &info));
4572 
4573     return retsts;
4574 }
4575 
4576 
4577 /*{{{  I32 my_pclose(PerlIO *fp)*/
4578 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4579 {
4580     pInfo info, last = NULL;
4581     I32 ret_status;
4582 
4583     /* Fixme - need ast and mutex protection here */
4584     for (info = open_pipes; info != NULL; last = info, info = info->next)
4585         if (info->fp == fp) break;
4586 
4587     if (info == NULL) {  /* no such pipe open */
4588       set_errno(ECHILD); /* quoth POSIX */
4589       set_vaxc_errno(SS$_NONEXPR);
4590       return -1;
4591     }
4592 
4593     ret_status = my_pclose_pinfo(aTHX_ info);
4594 
4595     return ret_status;
4596 
4597 }  /* end of my_pclose() */
4598 
4599 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4600   /* Roll our own prototype because we want this regardless of whether
4601    * _VMS_WAIT is defined.
4602    */
4603 
4604 #ifdef __cplusplus
4605 extern "C" {
4606 #endif
4607   __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4608 #ifdef __cplusplus
4609 }
4610 #endif
4611 
4612 #endif
4613 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4614    created with popen(); otherwise partially emulate waitpid() unless
4615    we have a suitable one from the CRTL that came with VMS 7.2 and later.
4616    Also check processes not considered by the CRTL waitpid().
4617  */
4618 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4619 Pid_t
4620 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4621 {
4622     pInfo info;
4623     int done;
4624     int sts;
4625     int j;
4626 
4627     if (statusp) *statusp = 0;
4628 
4629     for (info = open_pipes; info != NULL; info = info->next)
4630         if (info->pid == pid) break;
4631 
4632     if (info != NULL) {  /* we know about this child */
4633       while (!info->done) {
4634           _ckvmssts(sys$setast(0));
4635           done = info->done;
4636           if (!done) _ckvmssts(sys$clref(pipe_ef));
4637           _ckvmssts(sys$setast(1));
4638           if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4639       }
4640 
4641       if (statusp) *statusp = info->completion;
4642       return pid;
4643     }
4644 
4645     /* child that already terminated? */
4646 
4647     for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4648         if (closed_list[j].pid == pid) {
4649             if (statusp) *statusp = closed_list[j].completion;
4650             return pid;
4651         }
4652     }
4653 
4654     /* fall through if this child is not one of our own pipe children */
4655 
4656 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4657 
4658       /* waitpid() became available in the CRTL as of VMS 7.0, but only
4659        * in 7.2 did we get a version that fills in the VMS completion
4660        * status as Perl has always tried to do.
4661        */
4662 
4663       sts = __vms_waitpid( pid, statusp, flags );
4664 
4665       if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4666          return sts;
4667 
4668       /* If the real waitpid tells us the child does not exist, we
4669        * fall through here to implement waiting for a child that
4670        * was created by some means other than exec() (say, spawned
4671        * from DCL) or to wait for a process that is not a subprocess
4672        * of the current process.
4673        */
4674 
4675 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4676 
4677     {
4678       $DESCRIPTOR(intdsc,"0 00:00:01");
4679       unsigned long int ownercode = JPI$_OWNER, ownerpid;
4680       unsigned long int pidcode = JPI$_PID, mypid;
4681       unsigned long int interval[2];
4682       unsigned int jpi_iosb[2];
4683       struct itmlst_3 jpilist[2] = {
4684           {sizeof(ownerpid),        JPI$_OWNER, &ownerpid,        0},
4685           {                      0,         0,                 0, 0}
4686       };
4687 
4688       if (pid <= 0) {
4689         /* Sorry folks, we don't presently implement rooting around for
4690            the first child we can find, and we definitely don't want to
4691            pass a pid of -1 to $getjpi, where it is a wildcard operation.
4692          */
4693         set_errno(ENOTSUP);
4694         return -1;
4695       }
4696 
4697       /* Get the owner of the child so I can warn if it's not mine. If the
4698        * process doesn't exist or I don't have the privs to look at it,
4699        * I can go home early.
4700        */
4701       sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4702       if (sts & 1) sts = jpi_iosb[0];
4703       if (!(sts & 1)) {
4704         switch (sts) {
4705             case SS$_NONEXPR:
4706                 set_errno(ECHILD);
4707                 break;
4708             case SS$_NOPRIV:
4709                 set_errno(EACCES);
4710                 break;
4711             default:
4712                 _ckvmssts(sts);
4713         }
4714         set_vaxc_errno(sts);
4715         return -1;
4716       }
4717 
4718       if (ckWARN(WARN_EXEC)) {
4719         /* remind folks they are asking for non-standard waitpid behavior */
4720         _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4721         if (ownerpid != mypid)
4722           Perl_warner(aTHX_ packWARN(WARN_EXEC),
4723                       "waitpid: process %x is not a child of process %x",
4724                       pid,mypid);
4725       }
4726 
4727       /* simply check on it once a second until it's not there anymore. */
4728 
4729       _ckvmssts(sys$bintim(&intdsc,interval));
4730       while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4731             _ckvmssts(sys$schdwk(0,0,interval,0));
4732             _ckvmssts(sys$hiber());
4733       }
4734       if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4735 
4736       _ckvmssts(sts);
4737       return pid;
4738     }
4739 }  /* end of waitpid() */
4740 /*}}}*/
4741 /*}}}*/
4742 /*}}}*/
4743 
4744 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4745 char *
4746 my_gconvert(double val, int ndig, int trail, char *buf)
4747 {
4748   static char __gcvtbuf[DBL_DIG+1];
4749   char *loc;
4750 
4751   loc = buf ? buf : __gcvtbuf;
4752 
4753   if (val) {
4754     if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4755     return gcvt(val,ndig,loc);
4756   }
4757   else {
4758     loc[0] = '0'; loc[1] = '\0';
4759     return loc;
4760   }
4761 
4762 }
4763 /*}}}*/
4764 
4765 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4766 static int rms_free_search_context(struct FAB * fab)
4767 {
4768 struct NAM * nam;
4769 
4770     nam = fab->fab$l_nam;
4771     nam->nam$b_nop |= NAM$M_SYNCHK;
4772     nam->nam$l_rlf = NULL;
4773     fab->fab$b_dns = 0;
4774     return sys$parse(fab, NULL, NULL);
4775 }
4776 
4777 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4778 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4779 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4780 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4781 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4782 #define rms_nam_esll(nam) nam.nam$b_esl
4783 #define rms_nam_esl(nam) nam.nam$b_esl
4784 #define rms_nam_name(nam) nam.nam$l_name
4785 #define rms_nam_namel(nam) nam.nam$l_name
4786 #define rms_nam_type(nam) nam.nam$l_type
4787 #define rms_nam_typel(nam) nam.nam$l_type
4788 #define rms_nam_ver(nam) nam.nam$l_ver
4789 #define rms_nam_verl(nam) nam.nam$l_ver
4790 #define rms_nam_rsll(nam) nam.nam$b_rsl
4791 #define rms_nam_rsl(nam) nam.nam$b_rsl
4792 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4793 #define rms_set_fna(fab, nam, name, size) \
4794 	{ fab.fab$b_fns = size; fab.fab$l_fna = name; }
4795 #define rms_get_fna(fab, nam) fab.fab$l_fna
4796 #define rms_set_dna(fab, nam, name, size) \
4797 	{ fab.fab$b_dns = size; fab.fab$l_dna = name; }
4798 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4799 #define rms_set_esa(nam, name, size) \
4800 	{ nam.nam$b_ess = size; nam.nam$l_esa = name; }
4801 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4802 	{ nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4803 #define rms_set_rsa(nam, name, size) \
4804 	{ nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4805 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4806 	{ nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4807 #define rms_nam_name_type_l_size(nam) \
4808 	(nam.nam$b_name + nam.nam$b_type)
4809 #else
4810 static int rms_free_search_context(struct FAB * fab)
4811 {
4812 struct NAML * nam;
4813 
4814     nam = fab->fab$l_naml;
4815     nam->naml$b_nop |= NAM$M_SYNCHK;
4816     nam->naml$l_rlf = NULL;
4817     nam->naml$l_long_defname_size = 0;
4818 
4819     fab->fab$b_dns = 0;
4820     return sys$parse(fab, NULL, NULL);
4821 }
4822 
4823 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4824 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4825 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4826 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4827 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4828 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4829 #define rms_nam_esl(nam) nam.naml$b_esl
4830 #define rms_nam_name(nam) nam.naml$l_name
4831 #define rms_nam_namel(nam) nam.naml$l_long_name
4832 #define rms_nam_type(nam) nam.naml$l_type
4833 #define rms_nam_typel(nam) nam.naml$l_long_type
4834 #define rms_nam_ver(nam) nam.naml$l_ver
4835 #define rms_nam_verl(nam) nam.naml$l_long_ver
4836 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4837 #define rms_nam_rsl(nam) nam.naml$b_rsl
4838 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4839 #define rms_set_fna(fab, nam, name, size) \
4840 	{ fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4841 	nam.naml$l_long_filename_size = size; \
4842 	nam.naml$l_long_filename = name;}
4843 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4844 #define rms_set_dna(fab, nam, name, size) \
4845 	{ fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4846 	nam.naml$l_long_defname_size = size; \
4847 	nam.naml$l_long_defname = name; }
4848 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4849 #define rms_set_esa(nam, name, size) \
4850 	{ nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4851 	nam.naml$l_long_expand_alloc = size; \
4852 	nam.naml$l_long_expand = name; }
4853 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4854 	{ nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4855 	nam.naml$l_long_expand = l_name; \
4856 	nam.naml$l_long_expand_alloc = l_size; }
4857 #define rms_set_rsa(nam, name, size) \
4858 	{ nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4859 	nam.naml$l_long_result = name; \
4860 	nam.naml$l_long_result_alloc = size; }
4861 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4862 	{ nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4863 	nam.naml$l_long_result = l_name; \
4864 	nam.naml$l_long_result_alloc = l_size; }
4865 #define rms_nam_name_type_l_size(nam) \
4866 	(nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4867 #endif
4868 
4869 
4870 /* rms_erase
4871  * The CRTL for 8.3 and later can create symbolic links in any mode,
4872  * however in 8.3 the unlink/remove/delete routines will only properly handle
4873  * them if one of the PCP modes is active.
4874  */
4875 static int rms_erase(const char * vmsname)
4876 {
4877   int status;
4878   struct FAB myfab = cc$rms_fab;
4879   rms_setup_nam(mynam);
4880 
4881   rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4882   rms_bind_fab_nam(myfab, mynam);
4883 
4884 #ifdef NAML$M_OPEN_SPECIAL
4885   rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4886 #endif
4887 
4888   status = sys$erase(&myfab, 0, 0);
4889 
4890   return status;
4891 }
4892 
4893 
4894 static int
4895 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4896 		    const struct dsc$descriptor_s * vms_dst_dsc,
4897 		    unsigned long flags)
4898 {
4899     /*  VMS and UNIX handle file permissions differently and the
4900      * the same ACL trick may be needed for renaming files,
4901      * especially if they are directories.
4902      */
4903 
4904    /* todo: get kill_file and rename to share common code */
4905    /* I can not find online documentation for $change_acl
4906     * it appears to be replaced by $set_security some time ago */
4907 
4908 const unsigned int access_mode = 0;
4909 $DESCRIPTOR(obj_file_dsc,"FILE");
4910 char *vmsname;
4911 char *rslt;
4912 unsigned long int jpicode = JPI$_UIC;
4913 int aclsts, fndsts, rnsts = -1;
4914 unsigned int ctx = 0;
4915 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4916 struct dsc$descriptor_s * clean_dsc;
4917 
4918 struct myacedef {
4919     unsigned char myace$b_length;
4920     unsigned char myace$b_type;
4921     unsigned short int myace$w_flags;
4922     unsigned long int myace$l_access;
4923     unsigned long int myace$l_ident;
4924 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4925 	     ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4926 	     0},
4927 	     oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4928 
4929 struct item_list_3
4930 	findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4931 		      {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4932 		      {0,0,0,0}},
4933 	addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4934 	dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4935 		     {0,0,0,0}};
4936 
4937 
4938     /* Expand the input spec using RMS, since we do not want to put
4939      * ACLs on the target of a symbolic link */
4940     vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4941     if (vmsname == NULL)
4942 	return SS$_INSFMEM;
4943 
4944     rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4945 			vmsname,
4946 			PERL_RMSEXPAND_M_SYMLINK);
4947     if (rslt == NULL) {
4948 	PerlMem_free(vmsname);
4949 	return SS$_INSFMEM;
4950     }
4951 
4952     /* So we get our own UIC to use as a rights identifier,
4953      * and the insert an ACE at the head of the ACL which allows us
4954      * to delete the file.
4955      */
4956     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4957 
4958     fildsc.dsc$w_length = strlen(vmsname);
4959     fildsc.dsc$a_pointer = vmsname;
4960     ctx = 0;
4961     newace.myace$l_ident = oldace.myace$l_ident;
4962     rnsts = SS$_ABORT;
4963 
4964     /* Grab any existing ACEs with this identifier in case we fail */
4965     clean_dsc = &fildsc;
4966     aclsts = fndsts = sys$get_security(&obj_file_dsc,
4967 			       &fildsc,
4968 			       NULL,
4969 			       OSS$M_WLOCK,
4970 			       findlst,
4971 			       &ctx,
4972 			       &access_mode);
4973 
4974     if ($VMS_STATUS_SUCCESS(fndsts)  || (fndsts == SS$_ACLEMPTY)) {
4975 	/* Add the new ACE . . . */
4976 
4977 	/* if the sys$get_security succeeded, then ctx is valid, and the
4978 	 * object/file descriptors will be ignored.  But otherwise they
4979 	 * are needed
4980 	 */
4981 	aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4982 				  OSS$M_RELCTX, addlst, &ctx, &access_mode);
4983 	if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4984 	    set_errno(EVMSERR);
4985 	    set_vaxc_errno(aclsts);
4986 	    PerlMem_free(vmsname);
4987 	    return aclsts;
4988 	}
4989 
4990 	rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
4991 				NULL, NULL,
4992 				&flags,
4993 				NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4994 
4995 	if ($VMS_STATUS_SUCCESS(rnsts)) {
4996 	    clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
4997 	}
4998 
4999 	/* Put things back the way they were. */
5000 	ctx = 0;
5001 	aclsts = sys$get_security(&obj_file_dsc,
5002 				  clean_dsc,
5003 				  NULL,
5004 				  OSS$M_WLOCK,
5005 				  findlst,
5006 				  &ctx,
5007 				  &access_mode);
5008 
5009 	if ($VMS_STATUS_SUCCESS(aclsts)) {
5010 	int sec_flags;
5011 
5012 	    sec_flags = 0;
5013 	    if (!$VMS_STATUS_SUCCESS(fndsts))
5014 		sec_flags = OSS$M_RELCTX;
5015 
5016 	    /* Get rid of the new ACE */
5017 	    aclsts = sys$set_security(NULL, NULL, NULL,
5018 				  sec_flags, dellst, &ctx, &access_mode);
5019 
5020 	    /* If there was an old ACE, put it back */
5021 	    if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5022 		addlst[0].bufadr = &oldace;
5023 		aclsts = sys$set_security(NULL, NULL, NULL,
5024 				      OSS$M_RELCTX, addlst, &ctx, &access_mode);
5025 		if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5026 		    set_errno(EVMSERR);
5027 		    set_vaxc_errno(aclsts);
5028 		    rnsts = aclsts;
5029 		}
5030 	    } else {
5031 	    int aclsts2;
5032 
5033 		/* Try to clear the lock on the ACL list */
5034 		aclsts2 = sys$set_security(NULL, NULL, NULL,
5035 				      OSS$M_RELCTX, NULL, &ctx, &access_mode);
5036 
5037 		/* Rename errors are most important */
5038 		if (!$VMS_STATUS_SUCCESS(rnsts))
5039 		    aclsts = rnsts;
5040 		set_errno(EVMSERR);
5041 		set_vaxc_errno(aclsts);
5042 		rnsts = aclsts;
5043 	    }
5044 	}
5045 	else {
5046 	    if (aclsts != SS$_ACLEMPTY)
5047 		rnsts = aclsts;
5048 	}
5049     }
5050     else
5051 	rnsts = fndsts;
5052 
5053     PerlMem_free(vmsname);
5054     return rnsts;
5055 }
5056 
5057 
5058 /*{{{int rename(const char *, const char * */
5059 /* Not exactly what X/Open says to do, but doing it absolutely right
5060  * and efficiently would require a lot more work.  This should be close
5061  * enough to pass all but the most strict X/Open compliance test.
5062  */
5063 int
5064 Perl_rename(pTHX_ const char *src, const char * dst)
5065 {
5066 int retval;
5067 int pre_delete = 0;
5068 int src_sts;
5069 int dst_sts;
5070 Stat_t src_st;
5071 Stat_t dst_st;
5072 
5073     /* Validate the source file */
5074     src_sts = flex_lstat(src, &src_st);
5075     if (src_sts != 0) {
5076 
5077 	/* No source file or other problem */
5078 	return src_sts;
5079     }
5080     if (src_st.st_devnam[0] == 0)  {
5081         /* This may be possible so fail if it is seen. */
5082         errno = EIO;
5083         return -1;
5084     }
5085 
5086     dst_sts = flex_lstat(dst, &dst_st);
5087     if (dst_sts == 0) {
5088 
5089 	if (dst_st.st_dev != src_st.st_dev) {
5090 	    /* Must be on the same device */
5091 	    errno = EXDEV;
5092 	    return -1;
5093 	}
5094 
5095 	/* VMS_INO_T_COMPARE is true if the inodes are different
5096 	 * to match the output of memcmp
5097 	 */
5098 
5099 	if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5100 	    /* That was easy, the files are the same! */
5101 	    return 0;
5102 	}
5103 
5104 	if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5105 	    /* If source is a directory, so must be dest */
5106 		errno = EISDIR;
5107 		return -1;
5108 	}
5109 
5110     }
5111 
5112 
5113     if ((dst_sts == 0) &&
5114 	(vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5115 
5116 	/* We have issues here if vms_unlink_all_versions is set
5117 	 * If the destination exists, and is not a directory, then
5118 	 * we must delete in advance.
5119 	 *
5120 	 * If the src is a directory, then we must always pre-delete
5121 	 * the destination.
5122 	 *
5123 	 * If we successfully delete the dst in advance, and the rename fails
5124 	 * X/Open requires that errno be EIO.
5125 	 *
5126 	 */
5127 
5128 	if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5129 	    int d_sts;
5130 	    d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5131 	                             S_ISDIR(dst_st.st_mode));
5132 
5133            /* Need to delete all versions ? */
5134            if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5135                 int i = 0;
5136 
5137                 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5138                     d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5139                     if (d_sts != 0)
5140                         break;
5141                     i++;
5142 
5143                     /* Make sure that we do not loop forever */
5144                     if (i > 32767) {
5145                         errno = EIO;
5146                         d_sts = -1;
5147                         break;
5148                     }
5149                 }
5150            }
5151 
5152 	    if (d_sts != 0)
5153 		return d_sts;
5154 
5155 	    /* We killed the destination, so only errno now is EIO */
5156 	    pre_delete = 1;
5157 	}
5158     }
5159 
5160     /* Originally the idea was to call the CRTL rename() and only
5161      * try the lib$rename_file if it failed.
5162      * It turns out that there are too many variants in what the
5163      * the CRTL rename might do, so only use lib$rename_file
5164      */
5165     retval = -1;
5166 
5167     {
5168 	/* Is the source and dest both in VMS format */
5169 	/* if the source is a directory, then need to fileify */
5170 	/*  and dest must be a directory or non-existent. */
5171 
5172 	char * vms_dst;
5173 	int sts;
5174 	char * ret_str;
5175 	unsigned long flags;
5176 	struct dsc$descriptor_s old_file_dsc;
5177 	struct dsc$descriptor_s new_file_dsc;
5178 
5179 	/* We need to modify the src and dst depending
5180 	 * on if one or more of them are directories.
5181 	 */
5182 
5183 	vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5184 	if (vms_dst == NULL)
5185 	    _ckvmssts_noperl(SS$_INSFMEM);
5186 
5187 	if (S_ISDIR(src_st.st_mode)) {
5188 	char * ret_str;
5189 	char * vms_dir_file;
5190 
5191 	    vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5192 	    if (vms_dir_file == NULL)
5193 		_ckvmssts_noperl(SS$_INSFMEM);
5194 
5195 	    /* If the dest is a directory, we must remove it */
5196 	    if (dst_sts == 0) {
5197 		int d_sts;
5198 		d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5199 		if (d_sts != 0) {
5200 		    PerlMem_free(vms_dst);
5201 		    errno = EIO;
5202 		    return d_sts;
5203 		}
5204 
5205 		pre_delete = 1;
5206 	    }
5207 
5208 	   /* The dest must be a VMS file specification */
5209 	   ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5210 	   if (ret_str == NULL) {
5211 		PerlMem_free(vms_dst);
5212 		errno = EIO;
5213 		return -1;
5214 	   }
5215 
5216 	    /* The source must be a file specification */
5217 	    ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5218 	    if (ret_str == NULL) {
5219 		PerlMem_free(vms_dst);
5220 		PerlMem_free(vms_dir_file);
5221 		errno = EIO;
5222 		return -1;
5223 	    }
5224 	    PerlMem_free(vms_dst);
5225 	    vms_dst = vms_dir_file;
5226 
5227 	} else {
5228 	    /* File to file or file to new dir */
5229 
5230 	    if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5231 		/* VMS pathify a dir target */
5232 		ret_str = int_tovmspath(dst, vms_dst, NULL);
5233 		if (ret_str == NULL) {
5234 		    PerlMem_free(vms_dst);
5235 		    errno = EIO;
5236 		    return -1;
5237 		}
5238 	    } else {
5239                 char * v_spec, * r_spec, * d_spec, * n_spec;
5240                 char * e_spec, * vs_spec;
5241                 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5242 
5243 		/* fileify a target VMS file specification */
5244 		ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5245 		if (ret_str == NULL) {
5246 		    PerlMem_free(vms_dst);
5247 		    errno = EIO;
5248 		    return -1;
5249 		}
5250 
5251 		sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5252                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5253                              &e_len, &vs_spec, &vs_len);
5254 		if (sts == 0) {
5255 		     if (e_len == 0) {
5256 		         /* Get rid of the version */
5257 		         if (vs_len != 0) {
5258 		             *vs_spec = '\0';
5259 		         }
5260 		         /* Need to specify a '.' so that the extension */
5261 		         /* is not inherited */
5262 		         strcat(vms_dst,".");
5263 		     }
5264 		}
5265 	    }
5266 	}
5267 
5268 	old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5269 	old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5270 	old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5271 	old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5272 
5273 	new_file_dsc.dsc$a_pointer = vms_dst;
5274 	new_file_dsc.dsc$w_length = strlen(vms_dst);
5275 	new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5276 	new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5277 
5278 	flags = 0;
5279 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5280 	flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5281 #endif
5282 
5283 	sts = lib$rename_file(&old_file_dsc,
5284 			      &new_file_dsc,
5285 			      NULL, NULL,
5286 			      &flags,
5287 			      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5288 	if (!$VMS_STATUS_SUCCESS(sts)) {
5289 
5290 	   /* We could have failed because VMS style permissions do not
5291 	    * permit renames that UNIX will allow.  Just like the hack
5292 	    * in for kill_file.
5293 	    */
5294 	   sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5295 	}
5296 
5297 	PerlMem_free(vms_dst);
5298 	if (!$VMS_STATUS_SUCCESS(sts)) {
5299 	    errno = EIO;
5300 	    return -1;
5301 	}
5302 	retval = 0;
5303     }
5304 
5305     if (vms_unlink_all_versions) {
5306 	/* Now get rid of any previous versions of the source file that
5307 	 * might still exist
5308 	 */
5309 	int i = 0;
5310 	dSAVEDERRNO;
5311 	SAVE_ERRNO;
5312 	src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5313 	                           S_ISDIR(src_st.st_mode));
5314 	while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5315 	     src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5316 	                               S_ISDIR(src_st.st_mode));
5317 	     if (src_sts != 0)
5318 	         break;
5319 	     i++;
5320 
5321 	     /* Make sure that we do not loop forever */
5322 	     if (i > 32767) {
5323 	         src_sts = -1;
5324 	         break;
5325 	     }
5326 	}
5327 	RESTORE_ERRNO;
5328     }
5329 
5330     /* We deleted the destination, so must force the error to be EIO */
5331     if ((retval != 0) && (pre_delete != 0))
5332 	errno = EIO;
5333 
5334     return retval;
5335 }
5336 /*}}}*/
5337 
5338 
5339 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5340 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5341  * to expand file specification.  Allows for a single default file
5342  * specification and a simple mask of options.  If outbuf is non-NULL,
5343  * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5344  * the resultant file specification is placed.  If outbuf is NULL, the
5345  * resultant file specification is placed into a static buffer.
5346  * The third argument, if non-NULL, is taken to be a default file
5347  * specification string.  The fourth argument is unused at present.
5348  * rmesexpand() returns the address of the resultant string if
5349  * successful, and NULL on error.
5350  *
5351  * New functionality for previously unused opts value:
5352  *  PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5353  *  PERL_RMSEXPAND_M_LONG - Want output in long formst
5354  *  PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5355  *  PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5356  */
5357 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5358 
5359 static char *
5360 int_rmsexpand
5361    (const char *filespec,
5362     char *outbuf,
5363     const char *defspec,
5364     unsigned opts,
5365     int * fs_utf8,
5366     int * dfs_utf8)
5367 {
5368   char * ret_spec;
5369   const char * in_spec;
5370   char * spec_buf;
5371   const char * def_spec;
5372   char * vmsfspec, *vmsdefspec;
5373   char * esa;
5374   char * esal = NULL;
5375   char * outbufl;
5376   struct FAB myfab = cc$rms_fab;
5377   rms_setup_nam(mynam);
5378   STRLEN speclen;
5379   unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5380   int sts;
5381 
5382   /* temp hack until UTF8 is actually implemented */
5383   if (fs_utf8 != NULL)
5384     *fs_utf8 = 0;
5385 
5386   if (!filespec || !*filespec) {
5387     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5388     return NULL;
5389   }
5390 
5391   vmsfspec = NULL;
5392   vmsdefspec = NULL;
5393   outbufl = NULL;
5394 
5395   in_spec = filespec;
5396   isunix = 0;
5397   if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5398       char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5399       int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5400 
5401       /* If this is a UNIX file spec, convert it to VMS */
5402       sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5403                            &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5404                            &e_len, &vs_spec, &vs_len);
5405       if (sts != 0) {
5406           isunix = 1;
5407           char * ret_spec;
5408 
5409           vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5410           if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5411           ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5412           if (ret_spec == NULL) {
5413               PerlMem_free(vmsfspec);
5414               return NULL;
5415           }
5416           in_spec = (const char *)vmsfspec;
5417 
5418           /* Unless we are forcing to VMS format, a UNIX input means
5419            * UNIX output, and that requires long names to be used
5420            */
5421           if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5422 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5423               opts |= PERL_RMSEXPAND_M_LONG;
5424 #else
5425               NOOP;
5426 #endif
5427           else
5428               isunix = 0;
5429       }
5430 
5431   }
5432 
5433   rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5434   rms_bind_fab_nam(myfab, mynam);
5435 
5436   /* Process the default file specification if present */
5437   def_spec = defspec;
5438   if (defspec && *defspec) {
5439     int t_isunix;
5440     t_isunix = is_unix_filespec(defspec);
5441     if (t_isunix) {
5442       vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5443       if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5444       ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5445 
5446       if (ret_spec == NULL) {
5447           /* Clean up and bail */
5448           PerlMem_free(vmsdefspec);
5449           if (vmsfspec != NULL)
5450               PerlMem_free(vmsfspec);
5451               return NULL;
5452           }
5453           def_spec = (const char *)vmsdefspec;
5454       }
5455       rms_set_dna(myfab, mynam,
5456                   (char *)def_spec, strlen(def_spec)); /* cast ok */
5457   }
5458 
5459   /* Now we need the expansion buffers */
5460   esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5461   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5462 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5463   esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5464   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5465 #endif
5466   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5467 
5468   /* If a NAML block is used RMS always writes to the long and short
5469    * addresses unless you suppress the short name.
5470    */
5471 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5472   outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5473   if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5474 #endif
5475    rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5476 
5477 #ifdef NAM$M_NO_SHORT_UPCASE
5478   if (decc_efs_case_preserve)
5479     rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5480 #endif
5481 
5482    /* We may not want to follow symbolic links */
5483 #ifdef NAML$M_OPEN_SPECIAL
5484   if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5485     rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5486 #endif
5487 
5488   /* First attempt to parse as an existing file */
5489   retsts = sys$parse(&myfab,0,0);
5490   if (!(retsts & STS$K_SUCCESS)) {
5491 
5492     /* Could not find the file, try as syntax only if error is not fatal */
5493     rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5494     if (retsts == RMS$_DNF ||
5495         retsts == RMS$_DIR ||
5496         retsts == RMS$_DEV ||
5497         retsts == RMS$_PRV) {
5498       retsts = sys$parse(&myfab,0,0);
5499       if (retsts & STS$K_SUCCESS) goto int_expanded;
5500     }
5501 
5502      /* Still could not parse the file specification */
5503     /*----------------------------------------------*/
5504     sts = rms_free_search_context(&myfab); /* Free search context */
5505     if (vmsdefspec != NULL)
5506 	PerlMem_free(vmsdefspec);
5507     if (vmsfspec != NULL)
5508 	PerlMem_free(vmsfspec);
5509     if (outbufl != NULL)
5510 	PerlMem_free(outbufl);
5511     PerlMem_free(esa);
5512     if (esal != NULL)
5513 	PerlMem_free(esal);
5514     set_vaxc_errno(retsts);
5515     if      (retsts == RMS$_PRV) set_errno(EACCES);
5516     else if (retsts == RMS$_DEV) set_errno(ENODEV);
5517     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5518     else                         set_errno(EVMSERR);
5519     return NULL;
5520   }
5521   retsts = sys$search(&myfab,0,0);
5522   if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5523     sts = rms_free_search_context(&myfab); /* Free search context */
5524     if (vmsdefspec != NULL)
5525 	PerlMem_free(vmsdefspec);
5526     if (vmsfspec != NULL)
5527 	PerlMem_free(vmsfspec);
5528     if (outbufl != NULL)
5529 	PerlMem_free(outbufl);
5530     PerlMem_free(esa);
5531     if (esal != NULL)
5532 	PerlMem_free(esal);
5533     set_vaxc_errno(retsts);
5534     if      (retsts == RMS$_PRV) set_errno(EACCES);
5535     else                         set_errno(EVMSERR);
5536     return NULL;
5537   }
5538 
5539   /* If the input filespec contained any lowercase characters,
5540    * downcase the result for compatibility with Unix-minded code. */
5541 int_expanded:
5542   if (!decc_efs_case_preserve) {
5543     char * tbuf;
5544     for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5545       if (islower(*tbuf)) { haslower = 1; break; }
5546   }
5547 
5548    /* Is a long or a short name expected */
5549   /*------------------------------------*/
5550   spec_buf = NULL;
5551 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5552   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5553     if (rms_nam_rsll(mynam)) {
5554 	spec_buf = outbufl;
5555 	speclen = rms_nam_rsll(mynam);
5556     }
5557     else {
5558 	spec_buf = esal; /* Not esa */
5559 	speclen = rms_nam_esll(mynam);
5560     }
5561   }
5562   else {
5563 #endif
5564     if (rms_nam_rsl(mynam)) {
5565 	spec_buf = outbuf;
5566 	speclen = rms_nam_rsl(mynam);
5567     }
5568     else {
5569 	spec_buf = esa; /* Not esal */
5570 	speclen = rms_nam_esl(mynam);
5571     }
5572 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5573   }
5574 #endif
5575   spec_buf[speclen] = '\0';
5576 
5577   /* Trim off null fields added by $PARSE
5578    * If type > 1 char, must have been specified in original or default spec
5579    * (not true for version; $SEARCH may have added version of existing file).
5580    */
5581   trimver  = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5582   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5583     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5584              ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5585   }
5586   else {
5587     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5588              ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5589   }
5590   if (trimver || trimtype) {
5591     if (defspec && *defspec) {
5592       char *defesal = NULL;
5593       char *defesa = NULL;
5594       defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5595       if (defesa != NULL) {
5596         struct FAB deffab = cc$rms_fab;
5597 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5598         defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5599         if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5600 #endif
5601 	rms_setup_nam(defnam);
5602 
5603 	rms_bind_fab_nam(deffab, defnam);
5604 
5605 	/* Cast ok */
5606 	rms_set_fna
5607 	    (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5608 
5609 	/* RMS needs the esa/esal as a work area if wildcards are involved */
5610 	rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5611 
5612 	rms_clear_nam_nop(defnam);
5613 	rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5614 #ifdef NAM$M_NO_SHORT_UPCASE
5615 	if (decc_efs_case_preserve)
5616 	  rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5617 #endif
5618 #ifdef NAML$M_OPEN_SPECIAL
5619 	if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5620 	  rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5621 #endif
5622 	if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5623 	  if (trimver) {
5624 	     trimver  = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5625 	  }
5626 	  if (trimtype) {
5627 	    trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5628 	  }
5629 	}
5630 	if (defesal != NULL)
5631 	    PerlMem_free(defesal);
5632 	PerlMem_free(defesa);
5633       } else {
5634           _ckvmssts_noperl(SS$_INSFMEM);
5635       }
5636     }
5637     if (trimver) {
5638       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5639 	if (*(rms_nam_verl(mynam)) != '\"')
5640 	  speclen = rms_nam_verl(mynam) - spec_buf;
5641       }
5642       else {
5643 	if (*(rms_nam_ver(mynam)) != '\"')
5644 	  speclen = rms_nam_ver(mynam) - spec_buf;
5645       }
5646     }
5647     if (trimtype) {
5648       /* If we didn't already trim version, copy down */
5649       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5650 	if (speclen > rms_nam_verl(mynam) - spec_buf)
5651 	  memmove
5652 	   (rms_nam_typel(mynam),
5653 	    rms_nam_verl(mynam),
5654 	    speclen - (rms_nam_verl(mynam) - spec_buf));
5655 	  speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5656       }
5657       else {
5658 	if (speclen > rms_nam_ver(mynam) - spec_buf)
5659 	  memmove
5660 	   (rms_nam_type(mynam),
5661 	    rms_nam_ver(mynam),
5662 	    speclen - (rms_nam_ver(mynam) - spec_buf));
5663 	  speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5664       }
5665     }
5666   }
5667 
5668    /* Done with these copies of the input files */
5669   /*-------------------------------------------*/
5670   if (vmsfspec != NULL)
5671 	PerlMem_free(vmsfspec);
5672   if (vmsdefspec != NULL)
5673 	PerlMem_free(vmsdefspec);
5674 
5675   /* If we just had a directory spec on input, $PARSE "helpfully"
5676    * adds an empty name and type for us */
5677 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5678   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5679     if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5680 	rms_nam_verl(mynam)  == rms_nam_typel(mynam) + 1 &&
5681 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5682       speclen = rms_nam_namel(mynam) - spec_buf;
5683   }
5684   else
5685 #endif
5686   {
5687     if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5688 	rms_nam_ver(mynam)  == rms_nam_ver(mynam) + 1 &&
5689 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5690       speclen = rms_nam_name(mynam) - spec_buf;
5691   }
5692 
5693   /* Posix format specifications must have matching quotes */
5694   if (speclen < (VMS_MAXRSS - 1)) {
5695     if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5696       if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5697         spec_buf[speclen] = '\"';
5698         speclen++;
5699       }
5700     }
5701   }
5702   spec_buf[speclen] = '\0';
5703   if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5704 
5705   /* Have we been working with an expanded, but not resultant, spec? */
5706   /* Also, convert back to Unix syntax if necessary. */
5707   {
5708   int rsl;
5709 
5710 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5711     if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5712       rsl = rms_nam_rsll(mynam);
5713     } else
5714 #endif
5715     {
5716       rsl = rms_nam_rsl(mynam);
5717     }
5718     if (!rsl) {
5719       /* rsl is not present, it means that spec_buf is either */
5720       /* esa or esal, and needs to be copied to outbuf */
5721       /* convert to Unix if desired */
5722       if (isunix) {
5723         ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5724       } else {
5725         /* VMS file specs are not in UTF-8 */
5726         if (fs_utf8 != NULL)
5727             *fs_utf8 = 0;
5728         my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5729         ret_spec = outbuf;
5730       }
5731     }
5732     else {
5733       /* Now spec_buf is either outbuf or outbufl */
5734       /* We need the result into outbuf */
5735       if (isunix) {
5736            /* If we need this in UNIX, then we need another buffer */
5737            /* to keep things in order */
5738            char * src;
5739            char * new_src = NULL;
5740            if (spec_buf == outbuf) {
5741                new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5742                my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5743            } else {
5744                src = spec_buf;
5745            }
5746            ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5747            if (new_src) {
5748                PerlMem_free(new_src);
5749            }
5750       } else {
5751            /* VMS file specs are not in UTF-8 */
5752            if (fs_utf8 != NULL)
5753                *fs_utf8 = 0;
5754 
5755            /* Copy the buffer if needed */
5756            if (outbuf != spec_buf)
5757                my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5758            ret_spec = outbuf;
5759       }
5760     }
5761   }
5762 
5763   /* Need to clean up the search context */
5764   rms_set_rsal(mynam, NULL, 0, NULL, 0);
5765   sts = rms_free_search_context(&myfab); /* Free search context */
5766 
5767   /* Clean up the extra buffers */
5768   if (esal != NULL)
5769       PerlMem_free(esal);
5770   PerlMem_free(esa);
5771   if (outbufl != NULL)
5772      PerlMem_free(outbufl);
5773 
5774   /* Return the result */
5775   return ret_spec;
5776 }
5777 
5778 /* Common simple case - Expand an already VMS spec */
5779 static char *
5780 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5781     opts |= PERL_RMSEXPAND_M_VMS_IN;
5782     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5783 }
5784 
5785 /* Common simple case - Expand to a VMS spec */
5786 static char *
5787 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5788     opts |= PERL_RMSEXPAND_M_VMS;
5789     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5790 }
5791 
5792 
5793 /* Entry point used by perl routines */
5794 static char *
5795 mp_do_rmsexpand
5796    (pTHX_ const char *filespec,
5797     char *outbuf,
5798     int ts,
5799     const char *defspec,
5800     unsigned opts,
5801     int * fs_utf8,
5802     int * dfs_utf8)
5803 {
5804     static char __rmsexpand_retbuf[VMS_MAXRSS];
5805     char * expanded, *ret_spec, *ret_buf;
5806 
5807     expanded = NULL;
5808     ret_buf = outbuf;
5809     if (ret_buf == NULL) {
5810         if (ts) {
5811             Newx(expanded, VMS_MAXRSS, char);
5812             if (expanded == NULL)
5813                 _ckvmssts(SS$_INSFMEM);
5814             ret_buf = expanded;
5815         } else {
5816             ret_buf = __rmsexpand_retbuf;
5817         }
5818     }
5819 
5820 
5821     ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5822                              opts, fs_utf8,  dfs_utf8);
5823 
5824     if (ret_spec == NULL) {
5825        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5826        if (expanded)
5827            Safefree(expanded);
5828     }
5829 
5830     return ret_spec;
5831 }
5832 /*}}}*/
5833 /* External entry points */
5834 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5835 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5836 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5837 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5838 char *Perl_rmsexpand_utf8
5839   (pTHX_ const char *spec, char *buf, const char *def,
5840    unsigned opt, int * fs_utf8, int * dfs_utf8)
5841 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5842 char *Perl_rmsexpand_utf8_ts
5843   (pTHX_ const char *spec, char *buf, const char *def,
5844    unsigned opt, int * fs_utf8, int * dfs_utf8)
5845 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5846 
5847 
5848 /*
5849 ** The following routines are provided to make life easier when
5850 ** converting among VMS-style and Unix-style directory specifications.
5851 ** All will take input specifications in either VMS or Unix syntax. On
5852 ** failure, all return NULL.  If successful, the routines listed below
5853 ** return a pointer to a buffer containing the appropriately
5854 ** reformatted spec (and, therefore, subsequent calls to that routine
5855 ** will clobber the result), while the routines of the same names with
5856 ** a _ts suffix appended will return a pointer to a mallocd string
5857 ** containing the appropriately reformatted spec.
5858 ** In all cases, only explicit syntax is altered; no check is made that
5859 ** the resulting string is valid or that the directory in question
5860 ** actually exists.
5861 **
5862 **   fileify_dirspec() - convert a directory spec into the name of the
5863 **     directory file (i.e. what you can stat() to see if it's a dir).
5864 **     The style (VMS or Unix) of the result is the same as the style
5865 **     of the parameter passed in.
5866 **   pathify_dirspec() - convert a directory spec into a path (i.e.
5867 **     what you prepend to a filename to indicate what directory it's in).
5868 **     The style (VMS or Unix) of the result is the same as the style
5869 **     of the parameter passed in.
5870 **   tounixpath() - convert a directory spec into a Unix-style path.
5871 **   tovmspath() - convert a directory spec into a VMS-style path.
5872 **   tounixspec() - convert any file spec into a Unix-style file spec.
5873 **   tovmsspec() - convert any file spec into a VMS-style spec.
5874 **   xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5875 **
5876 ** Copyright 1996 by Charles Bailey  <bailey@newman.upenn.edu>
5877 ** Permission is given to distribute this code as part of the Perl
5878 ** standard distribution under the terms of the GNU General Public
5879 ** License or the Perl Artistic License.  Copies of each may be
5880 ** found in the Perl standard distribution.
5881  */
5882 
5883 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5884 static char *
5885 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5886 {
5887     unsigned long int dirlen, retlen, hasfilename = 0;
5888     char *cp1, *cp2, *lastdir;
5889     char *trndir, *vmsdir;
5890     unsigned short int trnlnm_iter_count;
5891     int sts;
5892     if (utf8_fl != NULL)
5893 	*utf8_fl = 0;
5894 
5895     if (!dir || !*dir) {
5896       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5897     }
5898     dirlen = strlen(dir);
5899     while (dirlen && dir[dirlen-1] == '/') --dirlen;
5900     if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5901       if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5902         dir = "/sys$disk";
5903         dirlen = 9;
5904       }
5905       else
5906 	dirlen = 1;
5907     }
5908     if (dirlen > (VMS_MAXRSS - 1)) {
5909       set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5910       return NULL;
5911     }
5912     trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5913     if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5914     if (!strpbrk(dir+1,"/]>:")  &&
5915 	(!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5916       strcpy(trndir,*dir == '/' ? dir + 1: dir);
5917       trnlnm_iter_count = 0;
5918       while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5919         trnlnm_iter_count++;
5920         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5921       }
5922       dirlen = strlen(trndir);
5923     }
5924     else {
5925       memcpy(trndir, dir, dirlen);
5926       trndir[dirlen] = '\0';
5927     }
5928 
5929     /* At this point we are done with *dir and use *trndir which is a
5930      * copy that can be modified.  *dir must not be modified.
5931      */
5932 
5933     /* If we were handed a rooted logical name or spec, treat it like a
5934      * simple directory, so that
5935      *    $ Define myroot dev:[dir.]
5936      *    ... do_fileify_dirspec("myroot",buf,1) ...
5937      * does something useful.
5938      */
5939     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5940       trndir[--dirlen] = '\0';
5941       trndir[dirlen-1] = ']';
5942     }
5943     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5944       trndir[--dirlen] = '\0';
5945       trndir[dirlen-1] = '>';
5946     }
5947 
5948     if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5949       /* If we've got an explicit filename, we can just shuffle the string. */
5950       if (*(cp1+1)) hasfilename = 1;
5951       /* Similarly, we can just back up a level if we've got multiple levels
5952          of explicit directories in a VMS spec which ends with directories. */
5953       else {
5954         for (cp2 = cp1; cp2 > trndir; cp2--) {
5955 	  if (*cp2 == '.') {
5956 	    if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5957 /* fix-me, can not scan EFS file specs backward like this */
5958               *cp2 = *cp1; *cp1 = '\0';
5959               hasfilename = 1;
5960 	      break;
5961 	    }
5962           }
5963           if (*cp2 == '[' || *cp2 == '<') break;
5964         }
5965       }
5966     }
5967 
5968     vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5969     if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5970     cp1 = strpbrk(trndir,"]:>");
5971     if (hasfilename || !cp1) { /* filename present or not VMS */
5972 
5973       if (trndir[0] == '.') {
5974         if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5975 	  PerlMem_free(trndir);
5976 	  PerlMem_free(vmsdir);
5977           return int_fileify_dirspec("[]", buf, NULL);
5978 	}
5979         else if (trndir[1] == '.' &&
5980                (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5981 	  PerlMem_free(trndir);
5982 	  PerlMem_free(vmsdir);
5983           return int_fileify_dirspec("[-]", buf, NULL);
5984 	}
5985       }
5986       if (dirlen && trndir[dirlen-1] == '/') {    /* path ends with '/'; just add .dir;1 */
5987         dirlen -= 1;                 /* to last element */
5988         lastdir = strrchr(trndir,'/');
5989       }
5990       else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5991         /* If we have "/." or "/..", VMSify it and let the VMS code
5992          * below expand it, rather than repeating the code to handle
5993          * relative components of a filespec here */
5994         do {
5995           if (*(cp1+2) == '.') cp1++;
5996           if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5997 	    char * ret_chr;
5998             if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
5999 		PerlMem_free(trndir);
6000 		PerlMem_free(vmsdir);
6001 		return NULL;
6002 	    }
6003             if (strchr(vmsdir,'/') != NULL) {
6004               /* If int_tovmsspec() returned it, it must have VMS syntax
6005                * delimiters in it, so it's a mixed VMS/Unix spec.  We take
6006                * the time to check this here only so we avoid a recursion
6007                * loop; otherwise, gigo.
6008                */
6009 	      PerlMem_free(trndir);
6010 	      PerlMem_free(vmsdir);
6011               set_errno(EINVAL);  set_vaxc_errno(RMS$_SYN);
6012 	      return NULL;
6013             }
6014             if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6015 		PerlMem_free(trndir);
6016 		PerlMem_free(vmsdir);
6017 		return NULL;
6018 	    }
6019 	    ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6020 	    PerlMem_free(trndir);
6021 	    PerlMem_free(vmsdir);
6022             return ret_chr;
6023           }
6024           cp1++;
6025         } while ((cp1 = strstr(cp1,"/.")) != NULL);
6026         lastdir = strrchr(trndir,'/');
6027       }
6028       else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6029 	char * ret_chr;
6030         /* Ditto for specs that end in an MFD -- let the VMS code
6031          * figure out whether it's a real device or a rooted logical. */
6032 
6033         /* This should not happen any more.  Allowing the fake /000000
6034          * in a UNIX pathname causes all sorts of problems when trying
6035          * to run in UNIX emulation.  So the VMS to UNIX conversions
6036          * now remove the fake /000000 directories.
6037          */
6038 
6039         trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6040         if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6041 	    PerlMem_free(trndir);
6042 	    PerlMem_free(vmsdir);
6043 	    return NULL;
6044 	}
6045         if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6046 	    PerlMem_free(trndir);
6047 	    PerlMem_free(vmsdir);
6048 	    return NULL;
6049 	}
6050 	ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6051 	PerlMem_free(trndir);
6052 	PerlMem_free(vmsdir);
6053         return ret_chr;
6054       }
6055       else {
6056 
6057         if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6058              !(lastdir = cp1 = strrchr(trndir,']')) &&
6059              !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6060 
6061         cp2 = strrchr(cp1,'.');
6062         if (cp2) {
6063             int e_len, vs_len = 0;
6064             int is_dir = 0;
6065             char * cp3;
6066             cp3 = strchr(cp2,';');
6067             e_len = strlen(cp2);
6068             if (cp3) {
6069                 vs_len = strlen(cp3);
6070                 e_len = e_len - vs_len;
6071             }
6072             is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6073             if (!is_dir) {
6074                 if (!decc_efs_charset) {
6075                     /* If this is not EFS, then not a directory */
6076                     PerlMem_free(trndir);
6077                     PerlMem_free(vmsdir);
6078                     set_errno(ENOTDIR);
6079                     set_vaxc_errno(RMS$_DIR);
6080                     return NULL;
6081                 }
6082             } else {
6083                 /* Ok, here we have an issue, technically if a .dir shows */
6084                 /* from inside a directory, then we should treat it as */
6085                 /* xxx^.dir.dir.  But we do not have that context at this */
6086                 /* point unless this is totally restructured, so we remove */
6087                 /* The .dir for now, and fix this better later */
6088                 dirlen = cp2 - trndir;
6089             }
6090             if (decc_efs_charset && !strchr(trndir,'/')) {
6091                 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6092                 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6093 
6094                 for (; cp4 > cp1; cp4--) {
6095                     if (*cp4 == '.') {
6096                         if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6097                             memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6098                             *cp4 = '^';
6099                             dirlen++;
6100 	                }
6101                     }
6102                 }
6103             }
6104         }
6105 
6106       }
6107 
6108       retlen = dirlen + 6;
6109       memcpy(buf, trndir, dirlen);
6110       buf[dirlen] = '\0';
6111 
6112       /* We've picked up everything up to the directory file name.
6113          Now just add the type and version, and we're set. */
6114       if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6115           strcat(buf,".dir;1");
6116       else
6117           strcat(buf,".DIR;1");
6118       PerlMem_free(trndir);
6119       PerlMem_free(vmsdir);
6120       return buf;
6121     }
6122     else {  /* VMS-style directory spec */
6123 
6124       char *esa, *esal, term, *cp;
6125       char *my_esa;
6126       int my_esa_len;
6127       unsigned long int cmplen, haslower = 0;
6128       struct FAB dirfab = cc$rms_fab;
6129       rms_setup_nam(savnam);
6130       rms_setup_nam(dirnam);
6131 
6132       esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6133       if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6134       esal = NULL;
6135 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6136       esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6137       if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6138 #endif
6139       rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6140       rms_bind_fab_nam(dirfab, dirnam);
6141       rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6142       rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6143 #ifdef NAM$M_NO_SHORT_UPCASE
6144       if (decc_efs_case_preserve)
6145 	rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6146 #endif
6147 
6148       for (cp = trndir; *cp; cp++)
6149         if (islower(*cp)) { haslower = 1; break; }
6150       if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6151         if ((dirfab.fab$l_sts == RMS$_DIR) ||
6152             (dirfab.fab$l_sts == RMS$_DNF) ||
6153             (dirfab.fab$l_sts == RMS$_PRV)) {
6154             rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6155             sts = sys$parse(&dirfab);
6156         }
6157         if (!sts) {
6158 	  PerlMem_free(esa);
6159 	  if (esal != NULL)
6160 	      PerlMem_free(esal);
6161 	  PerlMem_free(trndir);
6162 	  PerlMem_free(vmsdir);
6163           set_errno(EVMSERR);
6164           set_vaxc_errno(dirfab.fab$l_sts);
6165           return NULL;
6166         }
6167       }
6168       else {
6169         savnam = dirnam;
6170 	/* Does the file really exist? */
6171         if (sys$search(&dirfab)& STS$K_SUCCESS) {
6172           /* Yes; fake the fnb bits so we'll check type below */
6173           rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6174         }
6175         else { /* No; just work with potential name */
6176           if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6177           else {
6178 	    int fab_sts;
6179 	    fab_sts = dirfab.fab$l_sts;
6180 	    sts = rms_free_search_context(&dirfab);
6181 	    PerlMem_free(esa);
6182 	    if (esal != NULL)
6183 		PerlMem_free(esal);
6184 	    PerlMem_free(trndir);
6185 	    PerlMem_free(vmsdir);
6186             set_errno(EVMSERR);  set_vaxc_errno(fab_sts);
6187             return NULL;
6188           }
6189         }
6190       }
6191 
6192       /* Make sure we are using the right buffer */
6193 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6194       if (esal != NULL) {
6195 	my_esa = esal;
6196 	my_esa_len = rms_nam_esll(dirnam);
6197       } else {
6198 #endif
6199 	my_esa = esa;
6200         my_esa_len = rms_nam_esl(dirnam);
6201 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6202       }
6203 #endif
6204       my_esa[my_esa_len] = '\0';
6205       if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6206         cp1 = strchr(my_esa,']');
6207         if (!cp1) cp1 = strchr(my_esa,'>');
6208         if (cp1) {  /* Should always be true */
6209           my_esa_len -= cp1 - my_esa - 1;
6210           memmove(my_esa, cp1 + 1, my_esa_len);
6211         }
6212       }
6213       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) {  /* Was type specified? */
6214         /* Yep; check version while we're at it, if it's there. */
6215         cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6216         if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6217           /* Something other than .DIR[;1].  Bzzt. */
6218 	  sts = rms_free_search_context(&dirfab);
6219 	  PerlMem_free(esa);
6220 	  if (esal != NULL)
6221 	     PerlMem_free(esal);
6222 	  PerlMem_free(trndir);
6223 	  PerlMem_free(vmsdir);
6224           set_errno(ENOTDIR);
6225           set_vaxc_errno(RMS$_DIR);
6226           return NULL;
6227         }
6228       }
6229 
6230       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6231         /* They provided at least the name; we added the type, if necessary, */
6232         my_strlcpy(buf, my_esa, VMS_MAXRSS);
6233 	sts = rms_free_search_context(&dirfab);
6234 	PerlMem_free(trndir);
6235 	PerlMem_free(esa);
6236 	if (esal != NULL)
6237 	    PerlMem_free(esal);
6238 	PerlMem_free(vmsdir);
6239         return buf;
6240       }
6241       if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6242         for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6243         *cp1 = '\0';
6244         my_esa_len -= 9;
6245       }
6246       if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6247       if (cp1 == NULL) { /* should never happen */
6248 	sts = rms_free_search_context(&dirfab);
6249 	PerlMem_free(trndir);
6250 	PerlMem_free(esa);
6251 	if (esal != NULL)
6252 	    PerlMem_free(esal);
6253 	PerlMem_free(vmsdir);
6254         return NULL;
6255       }
6256       term = *cp1;
6257       *cp1 = '\0';
6258       retlen = strlen(my_esa);
6259       cp1 = strrchr(my_esa,'.');
6260       /* ODS-5 directory specifications can have extra "." in them. */
6261       /* Fix-me, can not scan EFS file specifications backwards */
6262       while (cp1 != NULL) {
6263         if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6264 	  break;
6265 	else {
6266 	   cp1--;
6267 	   while ((cp1 > my_esa) && (*cp1 != '.'))
6268 	     cp1--;
6269 	}
6270 	if (cp1 == my_esa)
6271 	  cp1 = NULL;
6272       }
6273 
6274       if ((cp1) != NULL) {
6275         /* There's more than one directory in the path.  Just roll back. */
6276         *cp1 = term;
6277         my_strlcpy(buf, my_esa, VMS_MAXRSS);
6278       }
6279       else {
6280         if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6281           /* Go back and expand rooted logical name */
6282           rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6283 #ifdef NAM$M_NO_SHORT_UPCASE
6284 	  if (decc_efs_case_preserve)
6285 	    rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6286 #endif
6287           if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6288 	    sts = rms_free_search_context(&dirfab);
6289 	    PerlMem_free(esa);
6290 	    if (esal != NULL)
6291 		PerlMem_free(esal);
6292 	    PerlMem_free(trndir);
6293 	    PerlMem_free(vmsdir);
6294             set_errno(EVMSERR);
6295             set_vaxc_errno(dirfab.fab$l_sts);
6296             return NULL;
6297           }
6298 
6299 	  /* This changes the length of the string of course */
6300 	  if (esal != NULL) {
6301 	      my_esa_len = rms_nam_esll(dirnam);
6302 	  } else {
6303 	      my_esa_len = rms_nam_esl(dirnam);
6304 	  }
6305 
6306           retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6307           cp1 = strstr(my_esa,"][");
6308           if (!cp1) cp1 = strstr(my_esa,"]<");
6309           dirlen = cp1 - my_esa;
6310           memcpy(buf, my_esa, dirlen);
6311           if (!strncmp(cp1+2,"000000]",7)) {
6312             buf[dirlen-1] = '\0';
6313 	    /* fix-me Not full ODS-5, just extra dots in directories for now */
6314 	    cp1 = buf + dirlen - 1;
6315 	    while (cp1 > buf)
6316 	    {
6317 	      if (*cp1 == '[')
6318 		break;
6319 	      if (*cp1 == '.') {
6320 		if (*(cp1-1) != '^')
6321 		  break;
6322 	      }
6323 	      cp1--;
6324 	    }
6325             if (*cp1 == '.') *cp1 = ']';
6326             else {
6327               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6328               memmove(cp1+1,"000000]",7);
6329             }
6330           }
6331           else {
6332             memmove(buf+dirlen, cp1+2, retlen-dirlen);
6333             buf[retlen] = '\0';
6334             /* Convert last '.' to ']' */
6335             cp1 = buf+retlen-1;
6336 	    while (*cp != '[') {
6337 	      cp1--;
6338 	      if (*cp1 == '.') {
6339 		/* Do not trip on extra dots in ODS-5 directories */
6340 		if ((cp1 == buf) || (*(cp1-1) != '^'))
6341 		break;
6342 	      }
6343 	    }
6344             if (*cp1 == '.') *cp1 = ']';
6345             else {
6346               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6347               memmove(cp1+1,"000000]",7);
6348             }
6349           }
6350         }
6351         else {  /* This is a top-level dir.  Add the MFD to the path. */
6352           cp1 = my_esa;
6353           cp2 = buf;
6354           while ((*cp1 != ':')  && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6355           strcpy(cp2,":[000000]");
6356           cp1 += 2;
6357           strcpy(cp2+9,cp1);
6358         }
6359       }
6360       sts = rms_free_search_context(&dirfab);
6361       /* We've set up the string up through the filename.  Add the
6362          type and version, and we're done. */
6363       strcat(buf,".DIR;1");
6364 
6365       /* $PARSE may have upcased filespec, so convert output to lower
6366        * case if input contained any lowercase characters. */
6367       if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6368       PerlMem_free(trndir);
6369       PerlMem_free(esa);
6370       if (esal != NULL)
6371 	PerlMem_free(esal);
6372       PerlMem_free(vmsdir);
6373       return buf;
6374     }
6375 }  /* end of int_fileify_dirspec() */
6376 
6377 
6378 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6379 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6380 {
6381     static char __fileify_retbuf[VMS_MAXRSS];
6382     char * fileified, *ret_spec, *ret_buf;
6383 
6384     fileified = NULL;
6385     ret_buf = buf;
6386     if (ret_buf == NULL) {
6387         if (ts) {
6388             Newx(fileified, VMS_MAXRSS, char);
6389             if (fileified == NULL)
6390                 _ckvmssts(SS$_INSFMEM);
6391             ret_buf = fileified;
6392         } else {
6393             ret_buf = __fileify_retbuf;
6394         }
6395     }
6396 
6397     ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6398 
6399     if (ret_spec == NULL) {
6400        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6401        if (fileified)
6402            Safefree(fileified);
6403     }
6404 
6405     return ret_spec;
6406 }  /* end of do_fileify_dirspec() */
6407 /*}}}*/
6408 
6409 /* External entry points */
6410 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6411 { return do_fileify_dirspec(dir,buf,0,NULL); }
6412 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6413 { return do_fileify_dirspec(dir,buf,1,NULL); }
6414 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6415 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6416 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6417 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6418 
6419 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6420     char * v_spec, int v_len, char * r_spec, int r_len,
6421     char * d_spec, int d_len, char * n_spec, int n_len,
6422     char * e_spec, int e_len, char * vs_spec, int vs_len) {
6423 
6424     /* VMS specification - Try to do this the simple way */
6425     if ((v_len + r_len > 0) || (d_len > 0)) {
6426         int is_dir;
6427 
6428         /* No name or extension component, already a directory */
6429         if ((n_len + e_len + vs_len) == 0) {
6430             strcpy(buf, dir);
6431             return buf;
6432         }
6433 
6434         /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6435         /* This results from catfile() being used instead of catdir() */
6436         /* So even though it should not work, we need to allow it */
6437 
6438         /* If this is .DIR;1 then do a simple conversion */
6439         is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6440         if (is_dir || (e_len == 0) && (d_len > 0)) {
6441              int len;
6442              len = v_len + r_len + d_len - 1;
6443              char dclose = d_spec[d_len - 1];
6444              memcpy(buf, dir, len);
6445              buf[len] = '.';
6446              len++;
6447              memcpy(&buf[len], n_spec, n_len);
6448              len += n_len;
6449              buf[len] = dclose;
6450              buf[len + 1] = '\0';
6451              return buf;
6452         }
6453 
6454 #ifdef HAS_SYMLINK
6455         else if (d_len > 0) {
6456             /* In the olden days, a directory needed to have a .DIR */
6457             /* extension to be a valid directory, but now it could  */
6458             /* be a symbolic link */
6459             int len;
6460             len = v_len + r_len + d_len - 1;
6461             char dclose = d_spec[d_len - 1];
6462             memcpy(buf, dir, len);
6463             buf[len] = '.';
6464             len++;
6465             memcpy(&buf[len], n_spec, n_len);
6466             len += n_len;
6467             if (e_len > 0) {
6468                 if (decc_efs_charset) {
6469                     if (e_len == 4
6470                         && (toupper(e_spec[1]) == 'D')
6471                         && (toupper(e_spec[2]) == 'I')
6472                         && (toupper(e_spec[3]) == 'R')) {
6473 
6474                         /* Corner case: directory spec with invalid version.
6475                          * Valid would have followed is_dir path above.
6476                          */
6477                         SETERRNO(ENOTDIR, RMS$_DIR);
6478                         return NULL;
6479                     }
6480                     else {
6481                         buf[len] = '^';
6482                         len++;
6483                         memcpy(&buf[len], e_spec, e_len);
6484                         len += e_len;
6485                     }
6486                 }
6487                 else {
6488                     SETERRNO(ENOTDIR, RMS$_DIR);
6489                     return NULL;
6490                 }
6491             }
6492             buf[len] = dclose;
6493             buf[len + 1] = '\0';
6494             return buf;
6495         }
6496 #else
6497         else {
6498             set_vaxc_errno(RMS$_DIR);
6499             set_errno(ENOTDIR);
6500             return NULL;
6501         }
6502 #endif
6503     }
6504     set_vaxc_errno(RMS$_DIR);
6505     set_errno(ENOTDIR);
6506     return NULL;
6507 }
6508 
6509 
6510 /* Internal routine to make sure or convert a directory to be in a */
6511 /* path specification.  No utf8 flag because it is not changed or used */
6512 static char *int_pathify_dirspec(const char *dir, char *buf)
6513 {
6514     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6515     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6516     char * exp_spec, *ret_spec;
6517     char * trndir;
6518     unsigned short int trnlnm_iter_count;
6519     STRLEN trnlen;
6520     int need_to_lower;
6521 
6522     if (vms_debug_fileify) {
6523         if (dir == NULL)
6524             fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6525         else
6526             fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6527     }
6528 
6529     /* We may need to lower case the result if we translated  */
6530     /* a logical name or got the current working directory */
6531     need_to_lower = 0;
6532 
6533     if (!dir || !*dir) {
6534       set_errno(EINVAL);
6535       set_vaxc_errno(SS$_BADPARAM);
6536       return NULL;
6537     }
6538 
6539     trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6540     if (trndir == NULL)
6541         _ckvmssts_noperl(SS$_INSFMEM);
6542 
6543     /* If no directory specified use the current default */
6544     if (*dir)
6545         my_strlcpy(trndir, dir, VMS_MAXRSS);
6546     else {
6547         getcwd(trndir, VMS_MAXRSS - 1);
6548         need_to_lower = 1;
6549     }
6550 
6551     /* now deal with bare names that could be logical names */
6552     trnlnm_iter_count = 0;
6553     while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6554            && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6555         trnlnm_iter_count++;
6556         need_to_lower = 1;
6557         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6558             break;
6559         trnlen = strlen(trndir);
6560 
6561         /* Trap simple rooted lnms, and return lnm:[000000] */
6562         if (!strcmp(trndir+trnlen-2,".]")) {
6563             my_strlcpy(buf, dir, VMS_MAXRSS);
6564             strcat(buf, ":[000000]");
6565             PerlMem_free(trndir);
6566 
6567             if (vms_debug_fileify) {
6568                 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6569             }
6570             return buf;
6571         }
6572     }
6573 
6574     /* At this point we do not work with *dir, but the copy in  *trndir */
6575 
6576     if (need_to_lower && !decc_efs_case_preserve) {
6577         /* Legacy mode, lower case the returned value */
6578         __mystrtolower(trndir);
6579     }
6580 
6581 
6582     /* Some special cases, '..', '.' */
6583     sts = 0;
6584     if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6585        /* Force UNIX filespec */
6586        sts = 1;
6587 
6588     } else {
6589         /* Is this Unix or VMS format? */
6590         sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6591                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6592                              &e_len, &vs_spec, &vs_len);
6593         if (sts == 0) {
6594 
6595             /* Just a filename? */
6596             if ((v_len + r_len + d_len) == 0) {
6597 
6598                 /* Now we have a problem, this could be Unix or VMS */
6599                 /* We have to guess.  .DIR usually means VMS */
6600 
6601                 /* In UNIX report mode, the .DIR extension is removed */
6602                 /* if one shows up, it is for a non-directory or a directory */
6603                 /* in EFS charset mode */
6604 
6605                 /* So if we are in Unix report mode, assume that this */
6606                 /* is a relative Unix directory specification */
6607 
6608                 sts = 1;
6609                 if (!decc_filename_unix_report && decc_efs_charset) {
6610                     int is_dir;
6611                     is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6612 
6613                     if (is_dir) {
6614                         /* Traditional mode, assume .DIR is directory */
6615                         buf[0] = '[';
6616                         buf[1] = '.';
6617                         memcpy(&buf[2], n_spec, n_len);
6618                         buf[n_len + 2] = ']';
6619                         buf[n_len + 3] = '\0';
6620                         PerlMem_free(trndir);
6621                         if (vms_debug_fileify) {
6622                             fprintf(stderr,
6623                                     "int_pathify_dirspec: buf = %s\n",
6624                                     buf);
6625                         }
6626                         return buf;
6627                     }
6628                 }
6629             }
6630         }
6631     }
6632     if (sts == 0) {
6633         ret_spec = int_pathify_dirspec_simple(trndir, buf,
6634             v_spec, v_len, r_spec, r_len,
6635             d_spec, d_len, n_spec, n_len,
6636             e_spec, e_len, vs_spec, vs_len);
6637 
6638         if (ret_spec != NULL) {
6639             PerlMem_free(trndir);
6640             if (vms_debug_fileify) {
6641                 fprintf(stderr,
6642                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6643             }
6644             return ret_spec;
6645         }
6646 
6647         /* Simple way did not work, which means that a logical name */
6648         /* was present for the directory specification.             */
6649         /* Need to use an rmsexpand variant to decode it completely */
6650         exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6651         if (exp_spec == NULL)
6652             _ckvmssts_noperl(SS$_INSFMEM);
6653 
6654         ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6655         if (ret_spec != NULL) {
6656             sts = vms_split_path(exp_spec, &v_spec, &v_len,
6657                                  &r_spec, &r_len, &d_spec, &d_len,
6658                                  &n_spec, &n_len, &e_spec,
6659                                  &e_len, &vs_spec, &vs_len);
6660             if (sts == 0) {
6661                 ret_spec = int_pathify_dirspec_simple(
6662                     exp_spec, buf, v_spec, v_len, r_spec, r_len,
6663                     d_spec, d_len, n_spec, n_len,
6664                     e_spec, e_len, vs_spec, vs_len);
6665 
6666                 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6667                     /* Legacy mode, lower case the returned value */
6668                     __mystrtolower(ret_spec);
6669                 }
6670             } else {
6671                 set_vaxc_errno(RMS$_DIR);
6672                 set_errno(ENOTDIR);
6673                 ret_spec = NULL;
6674             }
6675         }
6676         PerlMem_free(exp_spec);
6677         PerlMem_free(trndir);
6678         if (vms_debug_fileify) {
6679             if (ret_spec == NULL)
6680                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6681             else
6682                 fprintf(stderr,
6683                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6684         }
6685         return ret_spec;
6686 
6687     } else {
6688         /* Unix specification, Could be trivial conversion, */
6689         /* but have to deal with trailing '.dir' or extra '.' */
6690 
6691         char * lastdot;
6692         char * lastslash;
6693         int is_dir;
6694         STRLEN dir_len = strlen(trndir);
6695 
6696         lastslash = strrchr(trndir, '/');
6697         if (lastslash == NULL)
6698             lastslash = trndir;
6699         else
6700             lastslash++;
6701 
6702         lastdot = NULL;
6703 
6704         /* '..' or '.' are valid directory components */
6705         is_dir = 0;
6706         if (lastslash[0] == '.') {
6707             if (lastslash[1] == '\0') {
6708                is_dir = 1;
6709             } else if (lastslash[1] == '.') {
6710                 if (lastslash[2] == '\0') {
6711                     is_dir = 1;
6712                 } else {
6713                     /* And finally allow '...' */
6714                     if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6715                         is_dir = 1;
6716                     }
6717                 }
6718             }
6719         }
6720 
6721         if (!is_dir) {
6722            lastdot = strrchr(lastslash, '.');
6723         }
6724         if (lastdot != NULL) {
6725             STRLEN e_len;
6726              /* '.dir' is discarded, and any other '.' is invalid */
6727             e_len = strlen(lastdot);
6728 
6729             is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6730 
6731             if (is_dir) {
6732                 dir_len = dir_len - 4;
6733             }
6734         }
6735 
6736         my_strlcpy(buf, trndir, VMS_MAXRSS);
6737         if (buf[dir_len - 1] != '/') {
6738             buf[dir_len] = '/';
6739             buf[dir_len + 1] = '\0';
6740         }
6741 
6742         /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6743         if (!decc_efs_charset) {
6744              int dir_start = 0;
6745              char * str = buf;
6746              if (str[0] == '.') {
6747                  char * dots = str;
6748                  int cnt = 1;
6749                  while ((dots[cnt] == '.') && (cnt < 3))
6750                      cnt++;
6751                  if (cnt <= 3) {
6752                      if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6753                          dir_start = 1;
6754                          str += cnt;
6755                      }
6756                  }
6757              }
6758              for (; *str; ++str) {
6759                  while (*str == '/') {
6760                      dir_start = 1;
6761                      *str++;
6762                  }
6763                  if (dir_start) {
6764 
6765                      /* Have to skip up to three dots which could be */
6766                      /* directories, 3 dots being a VMS extension for Perl */
6767                      char * dots = str;
6768                      int cnt = 0;
6769                      while ((dots[cnt] == '.') && (cnt < 3)) {
6770                          cnt++;
6771                      }
6772                      if (dots[cnt] == '\0')
6773                          break;
6774                      if ((cnt > 1) && (dots[cnt] != '/')) {
6775                          dir_start = 0;
6776                      } else {
6777                          str += cnt;
6778                      }
6779 
6780                      /* too many dots? */
6781                      if ((cnt == 0) || (cnt > 3)) {
6782                          dir_start = 0;
6783                      }
6784                  }
6785                  if (!dir_start && (*str == '.')) {
6786                      *str = '_';
6787                  }
6788              }
6789         }
6790         PerlMem_free(trndir);
6791         ret_spec = buf;
6792         if (vms_debug_fileify) {
6793             if (ret_spec == NULL)
6794                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6795             else
6796                 fprintf(stderr,
6797                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6798         }
6799         return ret_spec;
6800     }
6801 }
6802 
6803 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6804 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6805 {
6806     static char __pathify_retbuf[VMS_MAXRSS];
6807     char * pathified, *ret_spec, *ret_buf;
6808 
6809     pathified = NULL;
6810     ret_buf = buf;
6811     if (ret_buf == NULL) {
6812         if (ts) {
6813             Newx(pathified, VMS_MAXRSS, char);
6814             if (pathified == NULL)
6815                 _ckvmssts(SS$_INSFMEM);
6816             ret_buf = pathified;
6817         } else {
6818             ret_buf = __pathify_retbuf;
6819         }
6820     }
6821 
6822     ret_spec = int_pathify_dirspec(dir, ret_buf);
6823 
6824     if (ret_spec == NULL) {
6825        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6826        if (pathified)
6827            Safefree(pathified);
6828     }
6829 
6830     return ret_spec;
6831 
6832 }  /* end of do_pathify_dirspec() */
6833 
6834 
6835 /* External entry points */
6836 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6837 { return do_pathify_dirspec(dir,buf,0,NULL); }
6838 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6839 { return do_pathify_dirspec(dir,buf,1,NULL); }
6840 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6841 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6842 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6843 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6844 
6845 /* Internal tounixspec routine that does not use a thread context */
6846 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6847 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6848 {
6849   char *dirend, *cp1, *cp3, *tmp;
6850   const char *cp2;
6851   int dirlen;
6852   unsigned short int trnlnm_iter_count;
6853   int cmp_rslt, outchars_added;
6854   if (utf8_fl != NULL)
6855     *utf8_fl = 0;
6856 
6857   if (vms_debug_fileify) {
6858       if (spec == NULL)
6859           fprintf(stderr, "int_tounixspec: spec = NULL\n");
6860       else
6861           fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6862   }
6863 
6864 
6865   if (spec == NULL) {
6866       set_errno(EINVAL);
6867       set_vaxc_errno(SS$_BADPARAM);
6868       return NULL;
6869   }
6870   if (strlen(spec) > (VMS_MAXRSS-1)) {
6871       set_errno(E2BIG);
6872       set_vaxc_errno(SS$_BUFFEROVF);
6873       return NULL;
6874   }
6875 
6876   /* New VMS specific format needs translation
6877    * glob passes filenames with trailing '\n' and expects this preserved.
6878    */
6879   if (decc_posix_compliant_pathnames) {
6880     if (strncmp(spec, "\"^UP^", 5) == 0) {
6881       char * uspec;
6882       char *tunix;
6883       int tunix_len;
6884       int nl_flag;
6885 
6886       tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6887       if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6888       tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6889       nl_flag = 0;
6890       if (tunix[tunix_len - 1] == '\n') {
6891 	tunix[tunix_len - 1] = '\"';
6892 	tunix[tunix_len] = '\0';
6893 	tunix_len--;
6894 	nl_flag = 1;
6895       }
6896       uspec = decc$translate_vms(tunix);
6897       PerlMem_free(tunix);
6898       if ((int)uspec > 0) {
6899 	my_strlcpy(rslt, uspec, VMS_MAXRSS);
6900 	if (nl_flag) {
6901 	  strcat(rslt,"\n");
6902 	}
6903 	else {
6904 	  /* If we can not translate it, makemaker wants as-is */
6905 	  my_strlcpy(rslt, spec, VMS_MAXRSS);
6906 	}
6907 	return rslt;
6908       }
6909     }
6910   }
6911 
6912   cmp_rslt = 0; /* Presume VMS */
6913   cp1 = strchr(spec, '/');
6914   if (cp1 == NULL)
6915     cmp_rslt = 0;
6916 
6917     /* Look for EFS ^/ */
6918     if (decc_efs_charset) {
6919       while (cp1 != NULL) {
6920 	cp2 = cp1 - 1;
6921 	if (*cp2 != '^') {
6922 	  /* Found illegal VMS, assume UNIX */
6923 	  cmp_rslt = 1;
6924 	  break;
6925 	}
6926       cp1++;
6927       cp1 = strchr(cp1, '/');
6928     }
6929   }
6930 
6931   /* Look for "." and ".." */
6932   if (decc_filename_unix_report) {
6933     if (spec[0] == '.') {
6934       if ((spec[1] == '\0') || (spec[1] == '\n')) {
6935 	cmp_rslt = 1;
6936       }
6937       else {
6938 	if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6939 	  cmp_rslt = 1;
6940 	}
6941       }
6942     }
6943   }
6944 
6945   cp1 = rslt;
6946   cp2 = spec;
6947 
6948   /* This is already UNIX or at least nothing VMS understands,
6949    * so all we can reasonably do is unescape extended chars.
6950    */
6951   if (cmp_rslt) {
6952     while (*cp2) {
6953         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6954         cp1 += outchars_added;
6955     }
6956     *cp1 = '\0';
6957     if (vms_debug_fileify) {
6958         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6959     }
6960     return rslt;
6961   }
6962 
6963   dirend = strrchr(spec,']');
6964   if (dirend == NULL) dirend = strrchr(spec,'>');
6965   if (dirend == NULL) dirend = strchr(spec,':');
6966   if (dirend == NULL) {
6967     while (*cp2) {
6968         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6969         cp1 += outchars_added;
6970     }
6971     *cp1 = '\0';
6972     if (vms_debug_fileify) {
6973         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6974     }
6975     return rslt;
6976   }
6977 
6978   /* Special case 1 - sys$posix_root = / */
6979   if (!decc_disable_posix_root) {
6980     if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6981       *cp1 = '/';
6982       cp1++;
6983       cp2 = cp2 + 15;
6984       }
6985   }
6986 
6987   /* Special case 2 - Convert NLA0: to /dev/null */
6988   cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6989   if (cmp_rslt == 0) {
6990     strcpy(rslt, "/dev/null");
6991     cp1 = cp1 + 9;
6992     cp2 = cp2 + 5;
6993     if (spec[6] != '\0') {
6994       cp1[9] = '/';
6995       cp1++;
6996       cp2++;
6997     }
6998   }
6999 
7000    /* Also handle special case "SYS$SCRATCH:" */
7001   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7002   tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7003   if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7004   if (cmp_rslt == 0) {
7005   int islnm;
7006 
7007     islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7008     if (!islnm) {
7009       strcpy(rslt, "/tmp");
7010       cp1 = cp1 + 4;
7011       cp2 = cp2 + 12;
7012       if (spec[12] != '\0') {
7013 	cp1[4] = '/';
7014 	cp1++;
7015 	cp2++;
7016       }
7017     }
7018   }
7019 
7020   if (*cp2 != '[' && *cp2 != '<') {
7021     *(cp1++) = '/';
7022   }
7023   else {  /* the VMS spec begins with directories */
7024     cp2++;
7025     if (*cp2 == ']' || *cp2 == '>') {
7026       *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7027       PerlMem_free(tmp);
7028       return rslt;
7029     }
7030     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7031       if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7032 	PerlMem_free(tmp);
7033         if (vms_debug_fileify) {
7034             fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7035         }
7036         return NULL;
7037       }
7038       trnlnm_iter_count = 0;
7039       do {
7040         cp3 = tmp;
7041         while (*cp3 != ':' && *cp3) cp3++;
7042         *(cp3++) = '\0';
7043         if (strchr(cp3,']') != NULL) break;
7044         trnlnm_iter_count++;
7045         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7046       } while (vmstrnenv(tmp,tmp,0,fildev,0));
7047       cp1 = rslt;
7048       cp3 = tmp;
7049       *(cp1++) = '/';
7050       while (*cp3) {
7051         *(cp1++) = *(cp3++);
7052         if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7053 	    PerlMem_free(tmp);
7054             set_errno(ENAMETOOLONG);
7055             set_vaxc_errno(SS$_BUFFEROVF);
7056             if (vms_debug_fileify) {
7057                 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7058             }
7059 	    return NULL; /* No room */
7060 	}
7061       }
7062       *(cp1++) = '/';
7063     }
7064     if ((*cp2 == '^')) {
7065         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7066         cp1 += outchars_added;
7067     }
7068     else if ( *cp2 == '.') {
7069       if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7070         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7071         cp2 += 3;
7072       }
7073       else cp2++;
7074     }
7075   }
7076   PerlMem_free(tmp);
7077   for (; cp2 <= dirend; cp2++) {
7078     if ((*cp2 == '^')) {
7079 	/* EFS file escape, pass the next character as is */
7080 	/* Fix me: HEX encoding for Unicode not implemented */
7081 	*(cp1++) = *(++cp2);
7082         /* An escaped dot stays as is -- don't convert to slash */
7083         if (*cp2 == '.') cp2++;
7084     }
7085     if (*cp2 == ':') {
7086       *(cp1++) = '/';
7087       if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7088     }
7089     else if (*cp2 == ']' || *cp2 == '>') {
7090       if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7091     }
7092     else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7093       *(cp1++) = '/';
7094       if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7095         while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7096                *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7097         if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7098             *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7099       }
7100       else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7101         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7102         cp2 += 2;
7103       }
7104     }
7105     else if (*cp2 == '-') {
7106       if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7107         while (*cp2 == '-') {
7108           cp2++;
7109           *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7110         }
7111         if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7112                                                          /* filespecs like */
7113           set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);   /* [fred.--foo.bar] */
7114           if (vms_debug_fileify) {
7115               fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7116           }
7117           return NULL;
7118         }
7119       }
7120       else *(cp1++) = *cp2;
7121     }
7122     else *(cp1++) = *cp2;
7123   }
7124   /* Translate the rest of the filename. */
7125   while (*cp2) {
7126       int dot_seen = 0;
7127       switch(*cp2) {
7128       /* Fixme - for compatibility with the CRTL we should be removing */
7129       /* spaces from the file specifications, but this may show that */
7130       /* some tests that were appearing to pass are not really passing */
7131       case '%':
7132           cp2++;
7133           *(cp1++) = '?';
7134           break;
7135       case '^':
7136           cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7137           cp1 += outchars_added;
7138           break;
7139       case ';':
7140           if (decc_filename_unix_no_version) {
7141               /* Easy, drop the version */
7142               while (*cp2)
7143                   cp2++;
7144               break;
7145           } else {
7146               /* Punt - passing the version as a dot will probably */
7147               /* break perl in weird ways, but so did passing */
7148               /* through the ; as a version.  Follow the CRTL and */
7149               /* hope for the best. */
7150               cp2++;
7151               *(cp1++) = '.';
7152           }
7153           break;
7154       case '.':
7155           if (dot_seen) {
7156               /* We will need to fix this properly later */
7157               /* As Perl may be installed on an ODS-5 volume, but not */
7158               /* have the EFS_CHARSET enabled, it still may encounter */
7159               /* filenames with extra dots in them, and a precedent got */
7160               /* set which allowed them to work, that we will uphold here */
7161               /* If extra dots are present in a name and no ^ is on them */
7162               /* VMS assumes that the first one is the extension delimiter */
7163               /* the rest have an implied ^. */
7164 
7165               /* this is also a conflict as the . is also a version */
7166               /* delimiter in VMS, */
7167 
7168               *(cp1++) = *(cp2++);
7169               break;
7170           }
7171           dot_seen = 1;
7172           /* This is an extension */
7173           if (decc_readdir_dropdotnotype) {
7174               cp2++;
7175               if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7176                   /* Drop the dot for the extension */
7177                   break;
7178               } else {
7179                   *(cp1++) = '.';
7180               }
7181               break;
7182           }
7183       default:
7184           *(cp1++) = *(cp2++);
7185       }
7186   }
7187   *cp1 = '\0';
7188 
7189   /* This still leaves /000000/ when working with a
7190    * VMS device root or concealed root.
7191    */
7192   {
7193   int ulen;
7194   char * zeros;
7195 
7196       ulen = strlen(rslt);
7197 
7198       /* Get rid of "000000/ in rooted filespecs */
7199       if (ulen > 7) {
7200 	zeros = strstr(rslt, "/000000/");
7201 	if (zeros != NULL) {
7202 	  int mlen;
7203 	  mlen = ulen - (zeros - rslt) - 7;
7204 	  memmove(zeros, &zeros[7], mlen);
7205 	  ulen = ulen - 7;
7206 	  rslt[ulen] = '\0';
7207 	}
7208       }
7209   }
7210 
7211   if (vms_debug_fileify) {
7212       fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7213   }
7214   return rslt;
7215 
7216 }  /* end of int_tounixspec() */
7217 
7218 
7219 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7220 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7221 {
7222     static char __tounixspec_retbuf[VMS_MAXRSS];
7223     char * unixspec, *ret_spec, *ret_buf;
7224 
7225     unixspec = NULL;
7226     ret_buf = buf;
7227     if (ret_buf == NULL) {
7228         if (ts) {
7229             Newx(unixspec, VMS_MAXRSS, char);
7230             if (unixspec == NULL)
7231                 _ckvmssts(SS$_INSFMEM);
7232             ret_buf = unixspec;
7233         } else {
7234             ret_buf = __tounixspec_retbuf;
7235         }
7236     }
7237 
7238     ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7239 
7240     if (ret_spec == NULL) {
7241        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7242        if (unixspec)
7243            Safefree(unixspec);
7244     }
7245 
7246     return ret_spec;
7247 
7248 }  /* end of do_tounixspec() */
7249 /*}}}*/
7250 /* External entry points */
7251 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7252   { return do_tounixspec(spec,buf,0, NULL); }
7253 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7254   { return do_tounixspec(spec,buf,1, NULL); }
7255 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7256   { return do_tounixspec(spec,buf,0, utf8_fl); }
7257 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7258   { return do_tounixspec(spec,buf,1, utf8_fl); }
7259 
7260 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7261 
7262 /*
7263  This procedure is used to identify if a path is based in either
7264  the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7265  it returns the OpenVMS format directory for it.
7266 
7267  It is expecting specifications of only '/' or '/xxxx/'
7268 
7269  If a posix root does not exist, or 'xxxx' is not a directory
7270  in the posix root, it returns a failure.
7271 
7272  FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7273 
7274  It is used only internally by posix_to_vmsspec_hardway().
7275  */
7276 
7277 static int posix_root_to_vms
7278   (char *vmspath, int vmspath_len,
7279    const char *unixpath,
7280    const int * utf8_fl)
7281 {
7282 int sts;
7283 struct FAB myfab = cc$rms_fab;
7284 rms_setup_nam(mynam);
7285 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7286 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7287 char * esa, * esal, * rsa, * rsal;
7288 int dir_flag;
7289 int unixlen;
7290 
7291     dir_flag = 0;
7292     vmspath[0] = '\0';
7293     unixlen = strlen(unixpath);
7294     if (unixlen == 0) {
7295       return RMS$_FNF;
7296     }
7297 
7298 #if __CRTL_VER >= 80200000
7299   /* If not a posix spec already, convert it */
7300   if (decc_posix_compliant_pathnames) {
7301     if (strncmp(unixpath,"\"^UP^",5) != 0) {
7302       sprintf(vmspath,"\"^UP^%s\"",unixpath);
7303     }
7304     else {
7305       /* This is already a VMS specification, no conversion */
7306       unixlen--;
7307       my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7308     }
7309   }
7310   else
7311 #endif
7312   {
7313   int path_len;
7314   int i,j;
7315 
7316      /* Check to see if this is under the POSIX root */
7317      if (decc_disable_posix_root) {
7318 	return RMS$_FNF;
7319      }
7320 
7321      /* Skip leading / */
7322      if (unixpath[0] == '/') {
7323 	unixpath++;
7324 	unixlen--;
7325      }
7326 
7327 
7328      strcpy(vmspath,"SYS$POSIX_ROOT:");
7329 
7330      /* If this is only the / , or blank, then... */
7331      if (unixpath[0] == '\0') {
7332 	/* by definition, this is the answer */
7333 	return SS$_NORMAL;
7334      }
7335 
7336      /* Need to look up a directory */
7337      vmspath[15] = '[';
7338      vmspath[16] = '\0';
7339 
7340      /* Copy and add '^' escape characters as needed */
7341      j = 16;
7342      i = 0;
7343      while (unixpath[i] != 0) {
7344      int k;
7345 
7346 	j += copy_expand_unix_filename_escape
7347 	    (&vmspath[j], &unixpath[i], &k, utf8_fl);
7348 	i += k;
7349      }
7350 
7351      path_len = strlen(vmspath);
7352      if (vmspath[path_len - 1] == '/')
7353 	path_len--;
7354      vmspath[path_len] = ']';
7355      path_len++;
7356      vmspath[path_len] = '\0';
7357 
7358   }
7359   vmspath[vmspath_len] = 0;
7360   if (unixpath[unixlen - 1] == '/')
7361   dir_flag = 1;
7362   esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7363   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7364   esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7365   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7366   rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7367   if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7368   rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7369   if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7370   rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7371   rms_bind_fab_nam(myfab, mynam);
7372   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7373   rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7374   if (decc_efs_case_preserve)
7375     mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7376 #ifdef NAML$M_OPEN_SPECIAL
7377   mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7378 #endif
7379 
7380   /* Set up the remaining naml fields */
7381   sts = sys$parse(&myfab);
7382 
7383   /* It failed! Try again as a UNIX filespec */
7384   if (!(sts & 1)) {
7385     PerlMem_free(esal);
7386     PerlMem_free(esa);
7387     PerlMem_free(rsal);
7388     PerlMem_free(rsa);
7389     return sts;
7390   }
7391 
7392    /* get the Device ID and the FID */
7393    sts = sys$search(&myfab);
7394 
7395    /* These are no longer needed */
7396    PerlMem_free(esa);
7397    PerlMem_free(rsal);
7398    PerlMem_free(rsa);
7399 
7400    /* on any failure, returned the POSIX ^UP^ filespec */
7401    if (!(sts & 1)) {
7402       PerlMem_free(esal);
7403       return sts;
7404    }
7405    specdsc.dsc$a_pointer = vmspath;
7406    specdsc.dsc$w_length = vmspath_len;
7407 
7408    dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7409    dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7410    sts = lib$fid_to_name
7411       (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7412 
7413   /* on any failure, returned the POSIX ^UP^ filespec */
7414   if (!(sts & 1)) {
7415      /* This can happen if user does not have permission to read directories */
7416      if (strncmp(unixpath,"\"^UP^",5) != 0)
7417        sprintf(vmspath,"\"^UP^%s\"",unixpath);
7418      else
7419        my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7420   }
7421   else {
7422     vmspath[specdsc.dsc$w_length] = 0;
7423 
7424     /* Are we expecting a directory? */
7425     if (dir_flag != 0) {
7426     int i;
7427     char *eptr;
7428 
7429       eptr = NULL;
7430 
7431       i = specdsc.dsc$w_length - 1;
7432       while (i > 0) {
7433       int zercnt;
7434 	zercnt = 0;
7435 	/* Version must be '1' */
7436 	if (vmspath[i--] != '1')
7437 	  break;
7438 	/* Version delimiter is one of ".;" */
7439 	if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7440 	  break;
7441 	i--;
7442 	if (vmspath[i--] != 'R')
7443 	  break;
7444 	if (vmspath[i--] != 'I')
7445 	  break;
7446 	if (vmspath[i--] != 'D')
7447 	  break;
7448 	if (vmspath[i--] != '.')
7449 	  break;
7450 	eptr = &vmspath[i+1];
7451  	while (i > 0) {
7452 	  if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7453 	    if (vmspath[i-1] != '^') {
7454 	      if (zercnt != 6) {
7455   		*eptr = vmspath[i];
7456 		eptr[1] = '\0';
7457 		vmspath[i] = '.';
7458   		break;
7459 	      }
7460 	      else {
7461  		/* Get rid of 6 imaginary zero directory filename */
7462   		vmspath[i+1] = '\0';
7463  	      }
7464 	    }
7465 	  }
7466 	  if (vmspath[i] == '0')
7467 	    zercnt++;
7468 	  else
7469 	    zercnt = 10;
7470 	  i--;
7471 	}
7472 	break;
7473       }
7474     }
7475   }
7476   PerlMem_free(esal);
7477   return sts;
7478 }
7479 
7480 /* /dev/mumble needs to be handled special.
7481    /dev/null becomes NLA0:, And there is the potential for other stuff
7482    like /dev/tty which may need to be mapped to something.
7483 */
7484 
7485 static int
7486 slash_dev_special_to_vms
7487    (const char * unixptr,
7488     char * vmspath,
7489     int vmspath_len)
7490 {
7491 char * nextslash;
7492 int len;
7493 int cmp;
7494 
7495     unixptr += 4;
7496     nextslash = strchr(unixptr, '/');
7497     len = strlen(unixptr);
7498     if (nextslash != NULL)
7499 	len = nextslash - unixptr;
7500     cmp = strncmp("null", unixptr, 5);
7501     if (cmp == 0) {
7502 	if (vmspath_len >= 6) {
7503 	    strcpy(vmspath, "_NLA0:");
7504 	    return SS$_NORMAL;
7505 	}
7506     }
7507     return 0;
7508 }
7509 
7510 
7511 /* The built in routines do not understand perl's special needs, so
7512     doing a manual conversion from UNIX to VMS
7513 
7514     If the utf8_fl is not null and points to a non-zero value, then
7515     treat 8 bit characters as UTF-8.
7516 
7517     The sequence starting with '$(' and ending with ')' will be passed
7518     through with out interpretation instead of being escaped.
7519 
7520   */
7521 static int posix_to_vmsspec_hardway
7522   (char *vmspath, int vmspath_len,
7523    const char *unixpath,
7524    int dir_flag,
7525    int * utf8_fl) {
7526 
7527 char *esa;
7528 const char *unixptr;
7529 const char *unixend;
7530 char *vmsptr;
7531 const char *lastslash;
7532 const char *lastdot;
7533 int unixlen;
7534 int vmslen;
7535 int dir_start;
7536 int dir_dot;
7537 int quoted;
7538 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7539 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7540 
7541   if (utf8_fl != NULL)
7542     *utf8_fl = 0;
7543 
7544   unixptr = unixpath;
7545   dir_dot = 0;
7546 
7547   /* Ignore leading "/" characters */
7548   while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7549     unixptr++;
7550   }
7551   unixlen = strlen(unixptr);
7552 
7553   /* Do nothing with blank paths */
7554   if (unixlen == 0) {
7555     vmspath[0] = '\0';
7556     return SS$_NORMAL;
7557   }
7558 
7559   quoted = 0;
7560   /* This could have a "^UP^ on the front */
7561   if (strncmp(unixptr,"\"^UP^",5) == 0) {
7562     quoted = 1;
7563     unixptr+= 5;
7564     unixlen-= 5;
7565   }
7566 
7567   lastslash = strrchr(unixptr,'/');
7568   lastdot = strrchr(unixptr,'.');
7569   unixend = strrchr(unixptr,'\"');
7570   if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7571     unixend = unixptr + unixlen;
7572   }
7573 
7574   /* last dot is last dot or past end of string */
7575   if (lastdot == NULL)
7576     lastdot = unixptr + unixlen;
7577 
7578   /* if no directories, set last slash to beginning of string */
7579   if (lastslash == NULL) {
7580     lastslash = unixptr;
7581   }
7582   else {
7583     /* Watch out for trailing "." after last slash, still a directory */
7584     if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7585       lastslash = unixptr + unixlen;
7586     }
7587 
7588     /* Watch out for trailing ".." after last slash, still a directory */
7589     if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7590       lastslash = unixptr + unixlen;
7591     }
7592 
7593     /* dots in directories are aways escaped */
7594     if (lastdot < lastslash)
7595       lastdot = unixptr + unixlen;
7596   }
7597 
7598   /* if (unixptr < lastslash) then we are in a directory */
7599 
7600   dir_start = 0;
7601 
7602   vmsptr = vmspath;
7603   vmslen = 0;
7604 
7605   /* Start with the UNIX path */
7606   if (*unixptr != '/') {
7607     /* relative paths */
7608 
7609     /* If allowing logical names on relative pathnames, then handle here */
7610     if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7611 	!decc_posix_compliant_pathnames) {
7612     char * nextslash;
7613     int seg_len;
7614     char * trn;
7615     int islnm;
7616 
7617 	/* Find the next slash */
7618 	nextslash = strchr(unixptr,'/');
7619 
7620 	esa = (char *)PerlMem_malloc(vmspath_len);
7621 	if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7622 
7623 	trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7624 	if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7625 
7626 	if (nextslash != NULL) {
7627 
7628 	    seg_len = nextslash - unixptr;
7629 	    memcpy(esa, unixptr, seg_len);
7630 	    esa[seg_len] = 0;
7631 	}
7632 	else {
7633 	    seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7634 	}
7635 	/* trnlnm(section) */
7636 	islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7637 
7638 	if (islnm) {
7639 	    /* Now fix up the directory */
7640 
7641 	    /* Split up the path to find the components */
7642 	    sts = vms_split_path
7643 		  (trn,
7644 		   &v_spec,
7645 		   &v_len,
7646 		   &r_spec,
7647 		   &r_len,
7648 		   &d_spec,
7649 		   &d_len,
7650 		   &n_spec,
7651 		   &n_len,
7652 		   &e_spec,
7653 		   &e_len,
7654 		   &vs_spec,
7655 		   &vs_len);
7656 
7657 	    while (sts == 0) {
7658 	    int cmp;
7659 
7660 		/* A logical name must be a directory  or the full
7661 		   specification.  It is only a full specification if
7662 		   it is the only component */
7663 		if ((unixptr[seg_len] == '\0') ||
7664 		    (unixptr[seg_len+1] == '\0')) {
7665 
7666 		    /* Is a directory being required? */
7667 		    if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7668 			/* Not a logical name */
7669 			break;
7670 		    }
7671 
7672 
7673 		    if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7674 			/* This must be a directory */
7675 			if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7676 			    vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7677 			    vmsptr[vmslen] = ':';
7678 			    vmslen++;
7679 			    vmsptr[vmslen] = '\0';
7680 			    return SS$_NORMAL;
7681 			}
7682 		    }
7683 
7684 		}
7685 
7686 
7687 		/* must be dev/directory - ignore version */
7688 		if ((n_len + e_len) != 0)
7689 		    break;
7690 
7691 		/* transfer the volume */
7692 		if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7693 		    memcpy(vmsptr, v_spec, v_len);
7694 		    vmsptr += v_len;
7695 		    vmsptr[0] = '\0';
7696 		    vmslen += v_len;
7697 		}
7698 
7699 		/* unroot the rooted directory */
7700 		if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7701 		    r_spec[0] = '[';
7702 		    r_spec[r_len - 1] = ']';
7703 
7704 		    /* This should not be there, but nothing is perfect */
7705 		    if (r_len > 9) {
7706 			cmp = strcmp(&r_spec[1], "000000.");
7707 			if (cmp == 0) {
7708 			    r_spec += 7;
7709 			    r_spec[7] = '[';
7710 			    r_len -= 7;
7711 			    if (r_len == 2)
7712 				r_len = 0;
7713 			}
7714 		    }
7715 		    if (r_len > 0) {
7716 			memcpy(vmsptr, r_spec, r_len);
7717 			vmsptr += r_len;
7718 			vmslen += r_len;
7719 			vmsptr[0] = '\0';
7720 		    }
7721 		}
7722 		/* Bring over the directory. */
7723 		if ((d_len > 0) &&
7724 		    ((d_len + vmslen) < vmspath_len)) {
7725 		    d_spec[0] = '[';
7726 		    d_spec[d_len - 1] = ']';
7727 		    if (d_len > 9) {
7728 			cmp = strcmp(&d_spec[1], "000000.");
7729 			if (cmp == 0) {
7730 			    d_spec += 7;
7731 			    d_spec[7] = '[';
7732 			    d_len -= 7;
7733 			    if (d_len == 2)
7734 				d_len = 0;
7735 			}
7736 		    }
7737 
7738 		    if (r_len > 0) {
7739 			/* Remove the redundant root */
7740 			if (r_len > 0) {
7741 			    /* remove the ][ */
7742 			    vmsptr--;
7743 			    vmslen--;
7744 			    d_spec++;
7745 			    d_len--;
7746 			}
7747 			memcpy(vmsptr, d_spec, d_len);
7748 			    vmsptr += d_len;
7749 			    vmslen += d_len;
7750 			    vmsptr[0] = '\0';
7751 		    }
7752 		}
7753 		break;
7754 	    }
7755 	}
7756 
7757 	PerlMem_free(esa);
7758 	PerlMem_free(trn);
7759     }
7760 
7761     if (lastslash > unixptr) {
7762     int dotdir_seen;
7763 
7764       /* skip leading ./ */
7765       dotdir_seen = 0;
7766       while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7767 	dotdir_seen = 1;
7768 	unixptr++;
7769 	unixptr++;
7770       }
7771 
7772       /* Are we still in a directory? */
7773       if (unixptr <= lastslash) {
7774  	*vmsptr++ = '[';
7775  	vmslen = 1;
7776  	dir_start = 1;
7777 
7778  	/* if not backing up, then it is relative forward. */
7779  	if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7780  	      ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7781  	  *vmsptr++ = '.';
7782  	  vmslen++;
7783  	  dir_dot = 1;
7784  	  }
7785        }
7786        else {
7787 	 if (dotdir_seen) {
7788 	   /* Perl wants an empty directory here to tell the difference
7789 	    * between a DCL command and a filename
7790 	    */
7791 	  *vmsptr++ = '[';
7792 	  *vmsptr++ = ']';
7793 	  vmslen = 2;
7794  	}
7795       }
7796     }
7797     else {
7798       /* Handle two special files . and .. */
7799       if (unixptr[0] == '.') {
7800         if (&unixptr[1] == unixend) {
7801 	  *vmsptr++ = '[';
7802 	  *vmsptr++ = ']';
7803 	  vmslen += 2;
7804 	  *vmsptr++ = '\0';
7805 	  return SS$_NORMAL;
7806 	}
7807         if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7808 	  *vmsptr++ = '[';
7809 	  *vmsptr++ = '-';
7810 	  *vmsptr++ = ']';
7811 	  vmslen += 3;
7812 	  *vmsptr++ = '\0';
7813 	  return SS$_NORMAL;
7814 	}
7815       }
7816     }
7817   }
7818   else {	/* Absolute PATH handling */
7819   int sts;
7820   char * nextslash;
7821   int seg_len;
7822     /* Need to find out where root is */
7823 
7824     /* In theory, this procedure should never get an absolute POSIX pathname
7825      * that can not be found on the POSIX root.
7826      * In practice, that can not be relied on, and things will show up
7827      * here that are a VMS device name or concealed logical name instead.
7828      * So to make things work, this procedure must be tolerant.
7829      */
7830     esa = (char *)PerlMem_malloc(vmspath_len);
7831     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7832 
7833     sts = SS$_NORMAL;
7834     nextslash = strchr(&unixptr[1],'/');
7835     seg_len = 0;
7836     if (nextslash != NULL) {
7837       int cmp;
7838       seg_len = nextslash - &unixptr[1];
7839       my_strlcpy(vmspath, unixptr, seg_len + 2);
7840       cmp = 1;
7841       if (seg_len == 3) {
7842 	cmp = strncmp(vmspath, "dev", 4);
7843 	if (cmp == 0) {
7844 	    sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7845 	    if (sts == SS$_NORMAL)
7846 		return SS$_NORMAL;
7847 	}
7848       }
7849       sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7850     }
7851 
7852     if ($VMS_STATUS_SUCCESS(sts)) {
7853       /* This is verified to be a real path */
7854 
7855       sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7856       if ($VMS_STATUS_SUCCESS(sts)) {
7857 	vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7858 	vmsptr = vmspath + vmslen;
7859 	unixptr++;
7860 	if (unixptr < lastslash) {
7861 	char * rptr;
7862 	  vmsptr--;
7863 	  *vmsptr++ = '.';
7864 	  dir_start = 1;
7865 	  dir_dot = 1;
7866 	  if (vmslen > 7) {
7867 	  int cmp;
7868 	    rptr = vmsptr - 7;
7869 	    cmp = strcmp(rptr,"000000.");
7870 	    if (cmp == 0) {
7871 	      vmslen -= 7;
7872 	      vmsptr -= 7;
7873 	      vmsptr[1] = '\0';
7874 	    } /* removing 6 zeros */
7875 	  } /* vmslen < 7, no 6 zeros possible */
7876 	} /* Not in a directory */
7877       } /* Posix root found */
7878       else {
7879 	/* No posix root, fall back to default directory */
7880 	strcpy(vmspath, "SYS$DISK:[");
7881 	vmsptr = &vmspath[10];
7882 	vmslen = 10;
7883 	if (unixptr > lastslash) {
7884 	   *vmsptr = ']';
7885 	   vmsptr++;
7886 	   vmslen++;
7887 	}
7888 	else {
7889 	   dir_start = 1;
7890 	}
7891       }
7892     } /* end of verified real path handling */
7893     else {
7894     int add_6zero;
7895     int islnm;
7896 
7897       /* Ok, we have a device or a concealed root that is not in POSIX
7898        * or we have garbage.  Make the best of it.
7899        */
7900 
7901       /* Posix to VMS destroyed this, so copy it again */
7902       my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7903       vmslen = strlen(vmspath); /* We know we're truncating. */
7904       vmsptr = &vmsptr[vmslen];
7905       islnm = 0;
7906 
7907       /* Now do we need to add the fake 6 zero directory to it? */
7908       add_6zero = 1;
7909       if ((*lastslash == '/') && (nextslash < lastslash)) {
7910 	/* No there is another directory */
7911 	add_6zero = 0;
7912       }
7913       else {
7914       int trnend;
7915       int cmp;
7916 
7917 	/* now we have foo:bar or foo:[000000]bar to decide from */
7918 	islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7919 
7920         if (!islnm && !decc_posix_compliant_pathnames) {
7921 
7922 	    cmp = strncmp("bin", vmspath, 4);
7923 	    if (cmp == 0) {
7924 	        /* bin => SYS$SYSTEM: */
7925 		islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7926 	    }
7927 	    else {
7928 	        /* tmp => SYS$SCRATCH: */
7929 	        cmp = strncmp("tmp", vmspath, 4);
7930 		if (cmp == 0) {
7931 		    islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7932 		}
7933 	    }
7934 	}
7935 
7936         trnend = islnm ? islnm - 1 : 0;
7937 
7938 	/* if this was a logical name, ']' or '>' must be present */
7939 	/* if not a logical name, then assume a device and hope. */
7940 	islnm =  trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7941 
7942 	/* if log name and trailing '.' then rooted - treat as device */
7943 	add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7944 
7945 	/* Fix me, if not a logical name, a device lookup should be
7946          * done to see if the device is file structured.  If the device
7947          * is not file structured, the 6 zeros should not be put on.
7948          *
7949          * As it is, perl is occasionally looking for dev:[000000]tty.
7950 	 * which looks a little strange.
7951 	 *
7952 	 * Not that easy to detect as "/dev" may be file structured with
7953 	 * special device files.
7954          */
7955 
7956 	if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
7957 	    (&nextslash[1] == unixend)) {
7958 	  /* No real directory present */
7959 	  add_6zero = 1;
7960 	}
7961       }
7962 
7963       /* Put the device delimiter on */
7964       *vmsptr++ = ':';
7965       vmslen++;
7966       unixptr = nextslash;
7967       unixptr++;
7968 
7969       /* Start directory if needed */
7970       if (!islnm || add_6zero) {
7971 	*vmsptr++ = '[';
7972 	vmslen++;
7973 	dir_start = 1;
7974       }
7975 
7976       /* add fake 000000] if needed */
7977       if (add_6zero) {
7978 	*vmsptr++ = '0';
7979 	*vmsptr++ = '0';
7980 	*vmsptr++ = '0';
7981 	*vmsptr++ = '0';
7982 	*vmsptr++ = '0';
7983 	*vmsptr++ = '0';
7984 	*vmsptr++ = ']';
7985 	vmslen += 7;
7986 	dir_start = 0;
7987       }
7988 
7989     } /* non-POSIX translation */
7990     PerlMem_free(esa);
7991   } /* End of relative/absolute path handling */
7992 
7993   while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7994   int dash_flag;
7995   int in_cnt;
7996   int out_cnt;
7997 
7998     dash_flag = 0;
7999 
8000     if (dir_start != 0) {
8001 
8002       /* First characters in a directory are handled special */
8003       while ((*unixptr == '/') ||
8004 	     ((*unixptr == '.') &&
8005 	      ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8006 		(&unixptr[1]==unixend)))) {
8007       int loop_flag;
8008 
8009 	loop_flag = 0;
8010 
8011         /* Skip redundant / in specification */
8012         while ((*unixptr == '/') && (dir_start != 0)) {
8013 	  loop_flag = 1;
8014 	  unixptr++;
8015 	  if (unixptr == lastslash)
8016 	    break;
8017 	}
8018 	if (unixptr == lastslash)
8019 	  break;
8020 
8021         /* Skip redundant ./ characters */
8022 	while ((*unixptr == '.') &&
8023 	       ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8024 	  loop_flag = 1;
8025 	  unixptr++;
8026 	  if (unixptr == lastslash)
8027 	    break;
8028 	  if (*unixptr == '/')
8029 	    unixptr++;
8030 	}
8031 	if (unixptr == lastslash)
8032 	  break;
8033 
8034 	/* Skip redundant ../ characters */
8035 	while ((*unixptr == '.') && (unixptr[1] == '.') &&
8036 	     ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8037 	  /* Set the backing up flag */
8038 	  loop_flag = 1;
8039 	  dir_dot = 0;
8040 	  dash_flag = 1;
8041 	  *vmsptr++ = '-';
8042 	  vmslen++;
8043 	  unixptr++; /* first . */
8044 	  unixptr++; /* second . */
8045 	  if (unixptr == lastslash)
8046 	    break;
8047 	  if (*unixptr == '/') /* The slash */
8048 	    unixptr++;
8049 	}
8050 	if (unixptr == lastslash)
8051 	  break;
8052 
8053 	/* To do: Perl expects /.../ to be translated to [...] on VMS */
8054   	/* Not needed when VMS is pretending to be UNIX. */
8055 
8056 	/* Is this loop stuck because of too many dots? */
8057 	if (loop_flag == 0) {
8058 	  /* Exit the loop and pass the rest through */
8059 	  break;
8060 	}
8061       }
8062 
8063       /* Are we done with directories yet? */
8064       if (unixptr >= lastslash) {
8065 
8066 	/* Watch out for trailing dots */
8067 	if (dir_dot != 0) {
8068 	    vmslen --;
8069 	    vmsptr--;
8070 	}
8071 	*vmsptr++ = ']';
8072 	vmslen++;
8073 	dash_flag = 0;
8074 	dir_start = 0;
8075 	if (*unixptr == '/')
8076 	  unixptr++;
8077       }
8078       else {
8079 	/* Have we stopped backing up? */
8080 	if (dash_flag) {
8081 	  *vmsptr++ = '.';
8082 	  vmslen++;
8083 	  dash_flag = 0;
8084 	  /* dir_start continues to be = 1 */
8085 	}
8086 	if (*unixptr == '-') {
8087 	  *vmsptr++ = '^';
8088 	  *vmsptr++ = *unixptr++;
8089 	  vmslen += 2;
8090 	  dir_start = 0;
8091 
8092 	  /* Now are we done with directories yet? */
8093 	  if (unixptr >= lastslash) {
8094 
8095 	    /* Watch out for trailing dots */
8096 	    if (dir_dot != 0) {
8097 	      vmslen --;
8098 	      vmsptr--;
8099 	    }
8100 
8101 	    *vmsptr++ = ']';
8102 	    vmslen++;
8103 	    dash_flag = 0;
8104 	    dir_start = 0;
8105 	  }
8106 	}
8107       }
8108     }
8109 
8110     /* All done? */
8111     if (unixptr >= unixend)
8112       break;
8113 
8114     /* Normal characters - More EFS work probably needed */
8115     dir_start = 0;
8116     dir_dot = 0;
8117 
8118     switch(*unixptr) {
8119     case '/':
8120 	/* remove multiple / */
8121 	while (unixptr[1] == '/') {
8122 	   unixptr++;
8123 	}
8124 	if (unixptr == lastslash) {
8125 	  /* Watch out for trailing dots */
8126 	  if (dir_dot != 0) {
8127 	    vmslen --;
8128 	    vmsptr--;
8129 	  }
8130 	  *vmsptr++ = ']';
8131 	}
8132 	else {
8133 	  dir_start = 1;
8134 	  *vmsptr++ = '.';
8135 	  dir_dot = 1;
8136 
8137 	  /* To do: Perl expects /.../ to be translated to [...] on VMS */
8138  	  /* Not needed when VMS is pretending to be UNIX. */
8139 
8140 	}
8141 	dash_flag = 0;
8142 	if (unixptr != unixend)
8143 	  unixptr++;
8144 	vmslen++;
8145 	break;
8146     case '.':
8147 	if ((unixptr < lastdot) || (unixptr < lastslash) ||
8148 	    (&unixptr[1] == unixend)) {
8149 	  *vmsptr++ = '^';
8150 	  *vmsptr++ = '.';
8151 	  vmslen += 2;
8152 	  unixptr++;
8153 
8154 	  /* trailing dot ==> '^..' on VMS */
8155 	  if (unixptr == unixend) {
8156 	    *vmsptr++ = '.';
8157 	    vmslen++;
8158 	    unixptr++;
8159 	  }
8160 	  break;
8161 	}
8162 
8163 	*vmsptr++ = *unixptr++;
8164 	vmslen ++;
8165 	break;
8166     case '"':
8167 	if (quoted && (&unixptr[1] == unixend)) {
8168 	    unixptr++;
8169 	    break;
8170 	}
8171 	in_cnt = copy_expand_unix_filename_escape
8172 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8173 	vmsptr += out_cnt;
8174 	unixptr += in_cnt;
8175 	break;
8176     case '~':
8177     case ';':
8178     case '\\':
8179     case '?':
8180     case ' ':
8181     default:
8182 	in_cnt = copy_expand_unix_filename_escape
8183 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8184 	vmsptr += out_cnt;
8185 	unixptr += in_cnt;
8186 	break;
8187     }
8188   }
8189 
8190   /* Make sure directory is closed */
8191   if (unixptr == lastslash) {
8192     char *vmsptr2;
8193     vmsptr2 = vmsptr - 1;
8194 
8195     if (*vmsptr2 != ']') {
8196       *vmsptr2--;
8197 
8198       /* directories do not end in a dot bracket */
8199       if (*vmsptr2 == '.') {
8200 	vmsptr2--;
8201 
8202 	/* ^. is allowed */
8203         if (*vmsptr2 != '^') {
8204 	  vmsptr--; /* back up over the dot */
8205  	}
8206       }
8207       *vmsptr++ = ']';
8208     }
8209   }
8210   else {
8211     char *vmsptr2;
8212     /* Add a trailing dot if a file with no extension */
8213     vmsptr2 = vmsptr - 1;
8214     if ((vmslen > 1) &&
8215 	(*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8216 	(*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8217 	*vmsptr++ = '.';
8218         vmslen++;
8219     }
8220   }
8221 
8222   *vmsptr = '\0';
8223   return SS$_NORMAL;
8224 }
8225 #endif
8226 
8227  /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8228 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8229 {
8230 char * result;
8231 int utf8_flag;
8232 
8233    /* If a UTF8 flag is being passed, honor it */
8234    utf8_flag = 0;
8235    if (utf8_fl != NULL) {
8236      utf8_flag = *utf8_fl;
8237     *utf8_fl = 0;
8238    }
8239 
8240    if (utf8_flag) {
8241      /* If there is a possibility of UTF8, then if any UTF8 characters
8242         are present, then they must be converted to VTF-7
8243       */
8244      result = strcpy(rslt, path); /* FIX-ME */
8245    }
8246    else
8247      result = strcpy(rslt, path);
8248 
8249    return result;
8250 }
8251 
8252 /* A convenience macro for copying dots in filenames and escaping
8253  * them when they haven't already been escaped, with guards to
8254  * avoid checking before the start of the buffer or advancing
8255  * beyond the end of it (allowing room for the NUL terminator).
8256  */
8257 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8258     if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8259           || ((vmsefsdot) == (vmsefsbuf))) \
8260          && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8261        ) { \
8262         *((vmsefsdot)++) = '^'; \
8263     } \
8264     if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8265         *((vmsefsdot)++) = '.'; \
8266 } STMT_END
8267 
8268 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8269 static char *int_tovmsspec
8270    (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8271   char *dirend;
8272   char *lastdot;
8273   char *cp1;
8274   const char *cp2;
8275   unsigned long int infront = 0, hasdir = 1;
8276   int rslt_len;
8277   int no_type_seen;
8278   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8279   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8280 
8281   if (vms_debug_fileify) {
8282       if (path == NULL)
8283           fprintf(stderr, "int_tovmsspec: path = NULL\n");
8284       else
8285           fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8286   }
8287 
8288   if (path == NULL) {
8289       /* If we fail, we should be setting errno */
8290       set_errno(EINVAL);
8291       set_vaxc_errno(SS$_BADPARAM);
8292       return NULL;
8293   }
8294   rslt_len = VMS_MAXRSS-1;
8295 
8296   /* '.' and '..' are "[]" and "[-]" for a quick check */
8297   if (path[0] == '.') {
8298     if (path[1] == '\0') {
8299       strcpy(rslt,"[]");
8300       if (utf8_flag != NULL)
8301 	*utf8_flag = 0;
8302       return rslt;
8303     }
8304     else {
8305       if (path[1] == '.' && path[2] == '\0') {
8306 	strcpy(rslt,"[-]");
8307 	if (utf8_flag != NULL)
8308 	   *utf8_flag = 0;
8309 	return rslt;
8310       }
8311     }
8312   }
8313 
8314    /* Posix specifications are now a native VMS format */
8315   /*--------------------------------------------------*/
8316 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8317   if (decc_posix_compliant_pathnames) {
8318     if (strncmp(path,"\"^UP^",5) == 0) {
8319       posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8320       return rslt;
8321     }
8322   }
8323 #endif
8324 
8325   /* This is really the only way to see if this is already in VMS format */
8326   sts = vms_split_path
8327        (path,
8328 	&v_spec,
8329 	&v_len,
8330 	&r_spec,
8331 	&r_len,
8332 	&d_spec,
8333 	&d_len,
8334 	&n_spec,
8335 	&n_len,
8336 	&e_spec,
8337 	&e_len,
8338 	&vs_spec,
8339 	&vs_len);
8340   if (sts == 0) {
8341     /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8342        replacement, because the above parse just took care of most of
8343        what is needed to do vmspath when the specification is already
8344        in VMS format.
8345 
8346        And if it is not already, it is easier to do the conversion as
8347        part of this routine than to call this routine and then work on
8348        the result.
8349      */
8350 
8351     /* If VMS punctuation was found, it is already VMS format */
8352     if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8353       if (utf8_flag != NULL)
8354 	*utf8_flag = 0;
8355       my_strlcpy(rslt, path, VMS_MAXRSS);
8356       if (vms_debug_fileify) {
8357           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8358       }
8359       return rslt;
8360     }
8361     /* Now, what to do with trailing "." cases where there is no
8362        extension?  If this is a UNIX specification, and EFS characters
8363        are enabled, then the trailing "." should be converted to a "^.".
8364        But if this was already a VMS specification, then it should be
8365        left alone.
8366 
8367        So in the case of ambiguity, leave the specification alone.
8368      */
8369 
8370 
8371     /* If there is a possibility of UTF8, then if any UTF8 characters
8372         are present, then they must be converted to VTF-7
8373      */
8374     if (utf8_flag != NULL)
8375       *utf8_flag = 0;
8376     my_strlcpy(rslt, path, VMS_MAXRSS);
8377     if (vms_debug_fileify) {
8378         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8379     }
8380     return rslt;
8381   }
8382 
8383   dirend = strrchr(path,'/');
8384 
8385   if (dirend == NULL) {
8386      /* If we get here with no Unix directory delimiters, then this is an
8387       * ambiguous file specification, such as a Unix glob specification, a
8388       * shell or make macro, or a filespec that would be valid except for
8389       * unescaped extended characters.  The safest thing if it's a macro
8390       * is to pass it through as-is.
8391       */
8392       if (strstr(path, "$(")) {
8393           my_strlcpy(rslt, path, VMS_MAXRSS);
8394           if (vms_debug_fileify) {
8395               fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8396           }
8397           return rslt;
8398       }
8399       hasdir = 0;
8400   }
8401   else if (*(dirend+1) == '.') {  /* do we have trailing "/." or "/.." or "/..."? */
8402     if (!*(dirend+2)) dirend +=2;
8403     if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8404     if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8405   }
8406 
8407   cp1 = rslt;
8408   cp2 = path;
8409   lastdot = strrchr(cp2,'.');
8410   if (*cp2 == '/') {
8411     char *trndev;
8412     int islnm, rooted;
8413     STRLEN trnend;
8414 
8415     while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8416     if (!*(cp2+1)) {
8417       if (decc_disable_posix_root) {
8418 	strcpy(rslt,"sys$disk:[000000]");
8419       }
8420       else {
8421 	strcpy(rslt,"sys$posix_root:[000000]");
8422       }
8423       if (utf8_flag != NULL)
8424 	*utf8_flag = 0;
8425       if (vms_debug_fileify) {
8426           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8427       }
8428       return rslt;
8429     }
8430     while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8431     *cp1 = '\0';
8432     trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8433     if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8434     islnm =  simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8435 
8436      /* DECC special handling */
8437     if (!islnm) {
8438       if (strcmp(rslt,"bin") == 0) {
8439 	strcpy(rslt,"sys$system");
8440 	cp1 = rslt + 10;
8441 	*cp1 = 0;
8442 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8443       }
8444       else if (strcmp(rslt,"tmp") == 0) {
8445 	strcpy(rslt,"sys$scratch");
8446 	cp1 = rslt + 11;
8447 	*cp1 = 0;
8448 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8449       }
8450       else if (!decc_disable_posix_root) {
8451         strcpy(rslt, "sys$posix_root");
8452 	cp1 = rslt + 14;
8453 	*cp1 = 0;
8454 	cp2 = path;
8455         while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8456 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8457       }
8458       else if (strcmp(rslt,"dev") == 0) {
8459 	if (strncmp(cp2,"/null", 5) == 0) {
8460 	  if ((cp2[5] == 0) || (cp2[5] == '/')) {
8461 	    strcpy(rslt,"NLA0");
8462 	    cp1 = rslt + 4;
8463 	    *cp1 = 0;
8464 	    cp2 = cp2 + 5;
8465 	    islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8466 	  }
8467 	}
8468       }
8469     }
8470 
8471     trnend = islnm ? strlen(trndev) - 1 : 0;
8472     islnm =  trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8473     rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8474     /* If the first element of the path is a logical name, determine
8475      * whether it has to be translated so we can add more directories. */
8476     if (!islnm || rooted) {
8477       *(cp1++) = ':';
8478       *(cp1++) = '[';
8479       if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8480       else cp2++;
8481     }
8482     else {
8483       if (cp2 != dirend) {
8484         my_strlcpy(rslt, trndev, VMS_MAXRSS);
8485         cp1 = rslt + trnend;
8486 	if (*cp2 != 0) {
8487           *(cp1++) = '.';
8488           cp2++;
8489         }
8490       }
8491       else {
8492 	if (decc_disable_posix_root) {
8493 	  *(cp1++) = ':';
8494 	  hasdir = 0;
8495 	}
8496       }
8497     }
8498     PerlMem_free(trndev);
8499   }
8500   else if (hasdir) {
8501     *(cp1++) = '[';
8502     if (*cp2 == '.') {
8503       if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8504         cp2 += 2;         /* skip over "./" - it's redundant */
8505         *(cp1++) = '.';   /* but it does indicate a relative dirspec */
8506       }
8507       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8508         *(cp1++) = '-';                                 /* "../" --> "-" */
8509         cp2 += 3;
8510       }
8511       else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8512                (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8513         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8514         if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8515         cp2 += 4;
8516       }
8517       else if ((cp2 != lastdot) || (lastdot < dirend)) {
8518 	/* Escape the extra dots in EFS file specifications */
8519 	*(cp1++) = '^';
8520       }
8521       if (cp2 > dirend) cp2 = dirend;
8522     }
8523     else *(cp1++) = '.';
8524   }
8525   else {
8526     *(cp1++) = *cp2;
8527   }
8528   for (; cp2 < dirend; cp2++) {
8529     if (*cp2 == '/') {
8530       if (*(cp2-1) == '/') continue;
8531       if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8532       infront = 0;
8533     }
8534     else if (!infront && *cp2 == '.') {
8535       if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8536       else if (*(cp2+1) == '/') cp2++;   /* skip over "./" - it's redundant */
8537       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8538         if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8539         else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8540         else {
8541           *(cp1++) = '-';
8542         }
8543         cp2 += 2;
8544         if (cp2 == dirend) break;
8545       }
8546       else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8547                 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8548         if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8549         *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8550         if (!*(cp2+3)) {
8551           *(cp1++) = '.';  /* Simulate trailing '/' */
8552           cp2 += 2;  /* for loop will incr this to == dirend */
8553         }
8554         else cp2 += 3;  /* Trailing '/' was there, so skip it, too */
8555       }
8556       else {
8557         if (decc_efs_charset == 0) {
8558 	  if (cp1 > rslt && *(cp1-1) == '^')
8559 	    cp1--;         /* remove the escape, if any */
8560 	  *(cp1++) = '_';  /* fix up syntax - '.' in name not allowed */
8561 	}
8562 	else {
8563 	  VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8564 	}
8565       }
8566     }
8567     else {
8568       if (!infront && cp1 > rslt && *(cp1-1) == '-')  *(cp1++) = '.';
8569       if (*cp2 == '.') {
8570         if (decc_efs_charset == 0) {
8571 	  if (cp1 > rslt && *(cp1-1) == '^')
8572 	    cp1--;         /* remove the escape, if any */
8573 	  *(cp1++) = '_';
8574 	}
8575 	else {
8576 	  VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8577 	}
8578       }
8579       else                  *(cp1++) =  *cp2;
8580       infront = 1;
8581     }
8582   }
8583   if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8584   if (hasdir) *(cp1++) = ']';
8585   if (*cp2) cp2++;  /* check in case we ended with trailing '..' */
8586   /* fixme for ODS5 */
8587   no_type_seen = 0;
8588   if (cp2 > lastdot)
8589     no_type_seen = 1;
8590   while (*cp2) {
8591     switch(*cp2) {
8592     case '?':
8593         if (decc_efs_charset == 0)
8594 	  *(cp1++) = '%';
8595 	else
8596 	  *(cp1++) = '?';
8597 	cp2++;
8598     case ' ':
8599 	if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */
8600 	    *(cp1)++ = '^';
8601 	*(cp1)++ = '_';
8602 	cp2++;
8603 	break;
8604     case '.':
8605 	if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8606 	    decc_readdir_dropdotnotype) {
8607 	  VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8608 	  cp2++;
8609 
8610 	  /* trailing dot ==> '^..' on VMS */
8611 	  if (*cp2 == '\0') {
8612 	    *(cp1++) = '.';
8613 	    no_type_seen = 0;
8614 	  }
8615 	}
8616 	else {
8617 	  *(cp1++) = *(cp2++);
8618 	  no_type_seen = 0;
8619 	}
8620 	break;
8621     case '$':
8622 	 /* This could be a macro to be passed through */
8623 	*(cp1++) = *(cp2++);
8624 	if (*cp2 == '(') {
8625 	const char * save_cp2;
8626 	char * save_cp1;
8627 	int is_macro;
8628 
8629 	    /* paranoid check */
8630 	    save_cp2 = cp2;
8631 	    save_cp1 = cp1;
8632 	    is_macro = 0;
8633 
8634 	    /* Test through */
8635 	    *(cp1++) = *(cp2++);
8636 	    if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8637 		*(cp1++) = *(cp2++);
8638 		while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8639 		    *(cp1++) = *(cp2++);
8640 		}
8641 		if (*cp2 == ')') {
8642 		    *(cp1++) = *(cp2++);
8643 		    is_macro = 1;
8644 		}
8645 	    }
8646 	    if (is_macro == 0) {
8647 		/* Not really a macro - never mind */
8648 		cp2 = save_cp2;
8649 		cp1 = save_cp1;
8650 	    }
8651 	}
8652 	break;
8653     case '\"':
8654     case '~':
8655     case '`':
8656     case '!':
8657     case '#':
8658     case '%':
8659     case '^':
8660         /* Don't escape again if following character is
8661          * already something we escape.
8662          */
8663         if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8664 	    *(cp1++) = *(cp2++);
8665 	    break;
8666         }
8667         /* But otherwise fall through and escape it. */
8668     case '&':
8669     case '(':
8670     case ')':
8671     case '=':
8672     case '+':
8673     case '\'':
8674     case '@':
8675     case '[':
8676     case ']':
8677     case '{':
8678     case '}':
8679     case ':':
8680     case '\\':
8681     case '|':
8682     case '<':
8683     case '>':
8684 	if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */
8685 	    *(cp1++) = '^';
8686 	*(cp1++) = *(cp2++);
8687 	break;
8688     case ';':
8689 	/* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8690 	 * which is wrong.  UNIX notation should be ".dir." unless
8691 	 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8692 	 * changing this behavior could break more things at this time.
8693 	 * efs character set effectively does not allow "." to be a version
8694 	 * delimiter as a further complication about changing this.
8695 	 */
8696 	if (decc_filename_unix_report != 0) {
8697 	  *(cp1++) = '^';
8698 	}
8699 	*(cp1++) = *(cp2++);
8700 	break;
8701     default:
8702 	*(cp1++) = *(cp2++);
8703     }
8704   }
8705   if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8706   char *lcp1;
8707     lcp1 = cp1;
8708     lcp1--;
8709      /* Fix me for "^]", but that requires making sure that you do
8710       * not back up past the start of the filename
8711       */
8712     if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8713       *cp1++ = '.';
8714   }
8715   *cp1 = '\0';
8716 
8717   if (utf8_flag != NULL)
8718     *utf8_flag = 0;
8719   if (vms_debug_fileify) {
8720       fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8721   }
8722   return rslt;
8723 
8724 }  /* end of int_tovmsspec() */
8725 
8726 
8727 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8728 static char *mp_do_tovmsspec
8729    (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8730   static char __tovmsspec_retbuf[VMS_MAXRSS];
8731     char * vmsspec, *ret_spec, *ret_buf;
8732 
8733     vmsspec = NULL;
8734     ret_buf = buf;
8735     if (ret_buf == NULL) {
8736         if (ts) {
8737             Newx(vmsspec, VMS_MAXRSS, char);
8738             if (vmsspec == NULL)
8739                 _ckvmssts(SS$_INSFMEM);
8740             ret_buf = vmsspec;
8741         } else {
8742             ret_buf = __tovmsspec_retbuf;
8743         }
8744     }
8745 
8746     ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8747 
8748     if (ret_spec == NULL) {
8749        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8750        if (vmsspec)
8751            Safefree(vmsspec);
8752     }
8753 
8754     return ret_spec;
8755 
8756 }  /* end of mp_do_tovmsspec() */
8757 /*}}}*/
8758 /* External entry points */
8759 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8760   { return do_tovmsspec(path,buf,0,NULL); }
8761 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8762   { return do_tovmsspec(path,buf,1,NULL); }
8763 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8764   { return do_tovmsspec(path,buf,0,utf8_fl); }
8765 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8766   { return do_tovmsspec(path,buf,1,utf8_fl); }
8767 
8768 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8769 /* Internal routine for use with out an explicit context present */
8770 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8771 
8772     char * ret_spec, *pathified;
8773 
8774     if (path == NULL)
8775         return NULL;
8776 
8777     pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8778     if (pathified == NULL)
8779         _ckvmssts_noperl(SS$_INSFMEM);
8780 
8781     ret_spec = int_pathify_dirspec(path, pathified);
8782 
8783     if (ret_spec == NULL) {
8784         PerlMem_free(pathified);
8785         return NULL;
8786     }
8787 
8788     ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8789 
8790     PerlMem_free(pathified);
8791     return ret_spec;
8792 
8793 }
8794 
8795 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8796 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8797   static char __tovmspath_retbuf[VMS_MAXRSS];
8798   int vmslen;
8799   char *pathified, *vmsified, *cp;
8800 
8801   if (path == NULL) return NULL;
8802   pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8803   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8804   if (int_pathify_dirspec(path, pathified) == NULL) {
8805     PerlMem_free(pathified);
8806     return NULL;
8807   }
8808 
8809   vmsified = NULL;
8810   if (buf == NULL)
8811      Newx(vmsified, VMS_MAXRSS, char);
8812   if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8813     PerlMem_free(pathified);
8814     if (vmsified) Safefree(vmsified);
8815     return NULL;
8816   }
8817   PerlMem_free(pathified);
8818   if (buf) {
8819     return buf;
8820   }
8821   else if (ts) {
8822     vmslen = strlen(vmsified);
8823     Newx(cp,vmslen+1,char);
8824     memcpy(cp,vmsified,vmslen);
8825     cp[vmslen] = '\0';
8826     Safefree(vmsified);
8827     return cp;
8828   }
8829   else {
8830     my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8831     Safefree(vmsified);
8832     return __tovmspath_retbuf;
8833   }
8834 
8835 }  /* end of do_tovmspath() */
8836 /*}}}*/
8837 /* External entry points */
8838 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8839   { return do_tovmspath(path,buf,0, NULL); }
8840 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8841   { return do_tovmspath(path,buf,1, NULL); }
8842 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8843   { return do_tovmspath(path,buf,0,utf8_fl); }
8844 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8845   { return do_tovmspath(path,buf,1,utf8_fl); }
8846 
8847 
8848 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8849 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8850   static char __tounixpath_retbuf[VMS_MAXRSS];
8851   int unixlen;
8852   char *pathified, *unixified, *cp;
8853 
8854   if (path == NULL) return NULL;
8855   pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8856   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8857   if (int_pathify_dirspec(path, pathified) == NULL) {
8858     PerlMem_free(pathified);
8859     return NULL;
8860   }
8861 
8862   unixified = NULL;
8863   if (buf == NULL) {
8864       Newx(unixified, VMS_MAXRSS, char);
8865   }
8866   if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8867     PerlMem_free(pathified);
8868     if (unixified) Safefree(unixified);
8869     return NULL;
8870   }
8871   PerlMem_free(pathified);
8872   if (buf) {
8873     return buf;
8874   }
8875   else if (ts) {
8876     unixlen = strlen(unixified);
8877     Newx(cp,unixlen+1,char);
8878     memcpy(cp,unixified,unixlen);
8879     cp[unixlen] = '\0';
8880     Safefree(unixified);
8881     return cp;
8882   }
8883   else {
8884     my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8885     Safefree(unixified);
8886     return __tounixpath_retbuf;
8887   }
8888 
8889 }  /* end of do_tounixpath() */
8890 /*}}}*/
8891 /* External entry points */
8892 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8893   { return do_tounixpath(path,buf,0,NULL); }
8894 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8895   { return do_tounixpath(path,buf,1,NULL); }
8896 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8897   { return do_tounixpath(path,buf,0,utf8_fl); }
8898 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8899   { return do_tounixpath(path,buf,1,utf8_fl); }
8900 
8901 /*
8902  * @(#)argproc.c 2.2 94/08/16	Mark Pizzolato (mark AT infocomm DOT com)
8903  *
8904  *****************************************************************************
8905  *                                                                           *
8906  *  Copyright (C) 1989-1994, 2007 by                                         *
8907  *  Mark Pizzolato - INFO COMM, Danville, California  (510) 837-5600         *
8908  *                                                                           *
8909  *  Permission is hereby granted for the reproduction of this software       *
8910  *  on condition that this copyright notice is included in source            *
8911  *  distributions of the software.  The code may be modified and             *
8912  *  distributed under the same terms as Perl itself.                         *
8913  *                                                                           *
8914  *  27-Aug-1994 Modified for inclusion in perl5                              *
8915  *              by Charles Bailey  (bailey AT newman DOT upenn DOT edu)      *
8916  *****************************************************************************
8917  */
8918 
8919 /*
8920  * getredirection() is intended to aid in porting C programs
8921  * to VMS (Vax-11 C).  The native VMS environment does not support
8922  * '>' and '<' I/O redirection, or command line wild card expansion,
8923  * or a command line pipe mechanism using the '|' AND background
8924  * command execution '&'.  All of these capabilities are provided to any
8925  * C program which calls this procedure as the first thing in the
8926  * main program.
8927  * The piping mechanism will probably work with almost any 'filter' type
8928  * of program.  With suitable modification, it may useful for other
8929  * portability problems as well.
8930  *
8931  * Author:  Mark Pizzolato	(mark AT infocomm DOT com)
8932  */
8933 struct list_item
8934     {
8935     struct list_item *next;
8936     char *value;
8937     };
8938 
8939 static void add_item(struct list_item **head,
8940 		     struct list_item **tail,
8941 		     char *value,
8942 		     int *count);
8943 
8944 static void mp_expand_wild_cards(pTHX_ char *item,
8945 				struct list_item **head,
8946 				struct list_item **tail,
8947 				int *count);
8948 
8949 static int background_process(pTHX_ int argc, char **argv);
8950 
8951 static void pipe_and_fork(pTHX_ char **cmargv);
8952 
8953 /*{{{ void getredirection(int *ac, char ***av)*/
8954 static void
8955 mp_getredirection(pTHX_ int *ac, char ***av)
8956 /*
8957  * Process vms redirection arg's.  Exit if any error is seen.
8958  * If getredirection() processes an argument, it is erased
8959  * from the vector.  getredirection() returns a new argc and argv value.
8960  * In the event that a background command is requested (by a trailing "&"),
8961  * this routine creates a background subprocess, and simply exits the program.
8962  *
8963  * Warning: do not try to simplify the code for vms.  The code
8964  * presupposes that getredirection() is called before any data is
8965  * read from stdin or written to stdout.
8966  *
8967  * Normal usage is as follows:
8968  *
8969  *	main(argc, argv)
8970  *	int		argc;
8971  *    	char		*argv[];
8972  *	{
8973  *		getredirection(&argc, &argv);
8974  *	}
8975  */
8976 {
8977     int			argc = *ac;	/* Argument Count	  */
8978     char		**argv = *av;	/* Argument Vector	  */
8979     char		*ap;   		/* Argument pointer	  */
8980     int	       		j;		/* argv[] index		  */
8981     int			item_count = 0;	/* Count of Items in List */
8982     struct list_item 	*list_head = 0;	/* First Item in List	    */
8983     struct list_item	*list_tail;	/* Last Item in List	    */
8984     char 		*in = NULL;	/* Input File Name	    */
8985     char 		*out = NULL;	/* Output File Name	    */
8986     char 		*outmode = "w";	/* Mode to Open Output File */
8987     char 		*err = NULL;	/* Error File Name	    */
8988     char 		*errmode = "w";	/* Mode to Open Error File  */
8989     int			cmargc = 0;    	/* Piped Command Arg Count  */
8990     char		**cmargv = NULL;/* Piped Command Arg Vector */
8991 
8992     /*
8993      * First handle the case where the last thing on the line ends with
8994      * a '&'.  This indicates the desire for the command to be run in a
8995      * subprocess, so we satisfy that desire.
8996      */
8997     ap = argv[argc-1];
8998     if (0 == strcmp("&", ap))
8999        exit(background_process(aTHX_ --argc, argv));
9000     if (*ap && '&' == ap[strlen(ap)-1])
9001 	{
9002 	ap[strlen(ap)-1] = '\0';
9003        exit(background_process(aTHX_ argc, argv));
9004 	}
9005     /*
9006      * Now we handle the general redirection cases that involve '>', '>>',
9007      * '<', and pipes '|'.
9008      */
9009     for (j = 0; j < argc; ++j)
9010 	{
9011 	if (0 == strcmp("<", argv[j]))
9012 	    {
9013 	    if (j+1 >= argc)
9014 		{
9015 		fprintf(stderr,"No input file after < on command line");
9016 		exit(LIB$_WRONUMARG);
9017 		}
9018 	    in = argv[++j];
9019 	    continue;
9020 	    }
9021 	if ('<' == *(ap = argv[j]))
9022 	    {
9023 	    in = 1 + ap;
9024 	    continue;
9025 	    }
9026 	if (0 == strcmp(">", ap))
9027 	    {
9028 	    if (j+1 >= argc)
9029 		{
9030 		fprintf(stderr,"No output file after > on command line");
9031 		exit(LIB$_WRONUMARG);
9032 		}
9033 	    out = argv[++j];
9034 	    continue;
9035 	    }
9036 	if ('>' == *ap)
9037 	    {
9038 	    if ('>' == ap[1])
9039 		{
9040 		outmode = "a";
9041 		if ('\0' == ap[2])
9042 		    out = argv[++j];
9043 		else
9044 		    out = 2 + ap;
9045 		}
9046 	    else
9047 		out = 1 + ap;
9048 	    if (j >= argc)
9049 		{
9050 		fprintf(stderr,"No output file after > or >> on command line");
9051 		exit(LIB$_WRONUMARG);
9052 		}
9053 	    continue;
9054 	    }
9055 	if (('2' == *ap) && ('>' == ap[1]))
9056 	    {
9057 	    if ('>' == ap[2])
9058 		{
9059 		errmode = "a";
9060 		if ('\0' == ap[3])
9061 		    err = argv[++j];
9062 		else
9063 		    err = 3 + ap;
9064 		}
9065 	    else
9066 		if ('\0' == ap[2])
9067 		    err = argv[++j];
9068 		else
9069 		    err = 2 + ap;
9070 	    if (j >= argc)
9071 		{
9072 		fprintf(stderr,"No output file after 2> or 2>> on command line");
9073 		exit(LIB$_WRONUMARG);
9074 		}
9075 	    continue;
9076 	    }
9077 	if (0 == strcmp("|", argv[j]))
9078 	    {
9079 	    if (j+1 >= argc)
9080 		{
9081 		fprintf(stderr,"No command into which to pipe on command line");
9082 		exit(LIB$_WRONUMARG);
9083 		}
9084 	    cmargc = argc-(j+1);
9085 	    cmargv = &argv[j+1];
9086 	    argc = j;
9087 	    continue;
9088 	    }
9089 	if ('|' == *(ap = argv[j]))
9090 	    {
9091 	    ++argv[j];
9092 	    cmargc = argc-j;
9093 	    cmargv = &argv[j];
9094 	    argc = j;
9095 	    continue;
9096 	    }
9097 	expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9098 	}
9099     /*
9100      * Allocate and fill in the new argument vector, Some Unix's terminate
9101      * the list with an extra null pointer.
9102      */
9103     argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9104     if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9105     *av = argv;
9106     for (j = 0; j < item_count; ++j, list_head = list_head->next)
9107 	argv[j] = list_head->value;
9108     *ac = item_count;
9109     if (cmargv != NULL)
9110 	{
9111 	if (out != NULL)
9112 	    {
9113 	    fprintf(stderr,"'|' and '>' may not both be specified on command line");
9114 	    exit(LIB$_INVARGORD);
9115 	    }
9116 	pipe_and_fork(aTHX_ cmargv);
9117 	}
9118 
9119     /* Check for input from a pipe (mailbox) */
9120 
9121     if (in == NULL && 1 == isapipe(0))
9122 	{
9123 	char mbxname[L_tmpnam];
9124 	long int bufsize;
9125 	long int dvi_item = DVI$_DEVBUFSIZ;
9126 	$DESCRIPTOR(mbxnam, "");
9127 	$DESCRIPTOR(mbxdevnam, "");
9128 
9129 	/* Input from a pipe, reopen it in binary mode to disable	*/
9130 	/* carriage control processing.	 				*/
9131 
9132 	fgetname(stdin, mbxname, 1);
9133 	mbxnam.dsc$a_pointer = mbxname;
9134 	mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9135 	lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9136 	mbxdevnam.dsc$a_pointer = mbxname;
9137 	mbxdevnam.dsc$w_length = sizeof(mbxname);
9138 	dvi_item = DVI$_DEVNAM;
9139 	lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9140 	mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9141 	set_errno(0);
9142 	set_vaxc_errno(1);
9143 	freopen(mbxname, "rb", stdin);
9144 	if (errno != 0)
9145 	    {
9146 	    fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9147 	    exit(vaxc$errno);
9148 	    }
9149 	}
9150     if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9151 	{
9152 	fprintf(stderr,"Can't open input file %s as stdin",in);
9153 	exit(vaxc$errno);
9154 	}
9155     if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9156 	{
9157 	fprintf(stderr,"Can't open output file %s as stdout",out);
9158 	exit(vaxc$errno);
9159 	}
9160 	if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9161 
9162     if (err != NULL) {
9163         if (strcmp(err,"&1") == 0) {
9164             dup2(fileno(stdout), fileno(stderr));
9165             vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9166         } else {
9167 	FILE *tmperr;
9168 	if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9169 	    {
9170 	    fprintf(stderr,"Can't open error file %s as stderr",err);
9171 	    exit(vaxc$errno);
9172 	    }
9173 	    fclose(tmperr);
9174            if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9175 		{
9176 		exit(vaxc$errno);
9177 		}
9178 	    vmssetuserlnm("SYS$ERROR", err);
9179 	}
9180         }
9181 #ifdef ARGPROC_DEBUG
9182     PerlIO_printf(Perl_debug_log, "Arglist:\n");
9183     for (j = 0; j < *ac;  ++j)
9184 	PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9185 #endif
9186    /* Clear errors we may have hit expanding wildcards, so they don't
9187       show up in Perl's $! later */
9188    set_errno(0); set_vaxc_errno(1);
9189 }  /* end of getredirection() */
9190 /*}}}*/
9191 
9192 static void add_item(struct list_item **head,
9193 		     struct list_item **tail,
9194 		     char *value,
9195 		     int *count)
9196 {
9197     if (*head == 0)
9198 	{
9199 	*head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9200 	if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9201 	*tail = *head;
9202 	}
9203     else {
9204 	(*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9205 	if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9206 	*tail = (*tail)->next;
9207 	}
9208     (*tail)->value = value;
9209     ++(*count);
9210 }
9211 
9212 static void mp_expand_wild_cards(pTHX_ char *item,
9213 			      struct list_item **head,
9214 			      struct list_item **tail,
9215 			      int *count)
9216 {
9217 int expcount = 0;
9218 unsigned long int context = 0;
9219 int isunix = 0;
9220 int item_len = 0;
9221 char *had_version;
9222 char *had_device;
9223 int had_directory;
9224 char *devdir,*cp;
9225 char *vmsspec;
9226 $DESCRIPTOR(filespec, "");
9227 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9228 $DESCRIPTOR(resultspec, "");
9229 unsigned long int lff_flags = 0;
9230 int sts;
9231 int rms_sts;
9232 
9233 #ifdef VMS_LONGNAME_SUPPORT
9234     lff_flags = LIB$M_FIL_LONG_NAMES;
9235 #endif
9236 
9237     for (cp = item; *cp; cp++) {
9238 	if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9239 	if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9240     }
9241     if (!*cp || isspace(*cp))
9242 	{
9243 	add_item(head, tail, item, count);
9244 	return;
9245 	}
9246     else
9247         {
9248      /* "double quoted" wild card expressions pass as is */
9249      /* From DCL that means using e.g.:                  */
9250      /* perl program """perl.*"""                        */
9251      item_len = strlen(item);
9252      if ( '"' == *item && '"' == item[item_len-1] )
9253        {
9254        item++;
9255        item[item_len-2] = '\0';
9256        add_item(head, tail, item, count);
9257        return;
9258        }
9259      }
9260     resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9261     resultspec.dsc$b_class = DSC$K_CLASS_D;
9262     resultspec.dsc$a_pointer = NULL;
9263     vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9264     if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9265     if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9266       filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9267     if (!isunix || !filespec.dsc$a_pointer)
9268       filespec.dsc$a_pointer = item;
9269     filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9270     /*
9271      * Only return version specs, if the caller specified a version
9272      */
9273     had_version = strchr(item, ';');
9274     /*
9275      * Only return device and directory specs, if the caller specified either.
9276      */
9277     had_device = strchr(item, ':');
9278     had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9279 
9280     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9281 				 (&filespec, &resultspec, &context,
9282     				  &defaultspec, 0, &rms_sts, &lff_flags)))
9283 	{
9284 	char *string;
9285 	char *c;
9286 
9287 	string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9288         if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9289 	my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9290 	if (NULL == had_version)
9291 	    *(strrchr(string, ';')) = '\0';
9292 	if ((!had_directory) && (had_device == NULL))
9293 	    {
9294 	    if (NULL == (devdir = strrchr(string, ']')))
9295 		devdir = strrchr(string, '>');
9296 	    my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9297 	    }
9298 	/*
9299 	 * Be consistent with what the C RTL has already done to the rest of
9300 	 * the argv items and lowercase all of these names.
9301 	 */
9302 	if (!decc_efs_case_preserve) {
9303 	    for (c = string; *c; ++c)
9304 	    if (isupper(*c))
9305 		*c = tolower(*c);
9306 	}
9307 	if (isunix) trim_unixpath(string,item,1);
9308 	add_item(head, tail, string, count);
9309 	++expcount;
9310     }
9311     PerlMem_free(vmsspec);
9312     if (sts != RMS$_NMF)
9313 	{
9314 	set_vaxc_errno(sts);
9315 	switch (sts)
9316 	    {
9317 	    case RMS$_FNF: case RMS$_DNF:
9318 		set_errno(ENOENT); break;
9319 	    case RMS$_DIR:
9320 		set_errno(ENOTDIR); break;
9321 	    case RMS$_DEV:
9322 		set_errno(ENODEV); break;
9323 	    case RMS$_FNM: case RMS$_SYN:
9324 		set_errno(EINVAL); break;
9325 	    case RMS$_PRV:
9326 		set_errno(EACCES); break;
9327 	    default:
9328 		_ckvmssts_noperl(sts);
9329 	    }
9330 	}
9331     if (expcount == 0)
9332 	add_item(head, tail, item, count);
9333     _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9334     _ckvmssts_noperl(lib$find_file_end(&context));
9335 }
9336 
9337 static int child_st[2];/* Event Flag set when child process completes	*/
9338 
9339 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox		*/
9340 
9341 static unsigned long int exit_handler(void)
9342 {
9343 short iosb[4];
9344 
9345     if (0 == child_st[0])
9346 	{
9347 #ifdef ARGPROC_DEBUG
9348 	PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9349 #endif
9350 	fflush(stdout);	    /* Have to flush pipe for binary data to	*/
9351 			    /* terminate properly -- <tp@mccall.com>	*/
9352 	sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9353 	sys$dassgn(child_chan);
9354 	fclose(stdout);
9355 	sys$synch(0, child_st);
9356 	}
9357     return(1);
9358 }
9359 
9360 static void sig_child(int chan)
9361 {
9362 #ifdef ARGPROC_DEBUG
9363     PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9364 #endif
9365     if (child_st[0] == 0)
9366 	child_st[0] = 1;
9367 }
9368 
9369 static struct exit_control_block exit_block =
9370     {
9371     0,
9372     exit_handler,
9373     1,
9374     &exit_block.exit_status,
9375     0
9376     };
9377 
9378 static void
9379 pipe_and_fork(pTHX_ char **cmargv)
9380 {
9381     PerlIO *fp;
9382     struct dsc$descriptor_s *vmscmd;
9383     char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9384     int sts, j, l, ismcr, quote, tquote = 0;
9385 
9386     sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9387     vms_execfree(vmscmd);
9388 
9389     j = l = 0;
9390     p = subcmd;
9391     q = cmargv[0];
9392     ismcr = q && toupper(*q) == 'M'     && toupper(*(q+1)) == 'C'
9393               && toupper(*(q+2)) == 'R' && !*(q+3);
9394 
9395     while (q && l < MAX_DCL_LINE_LENGTH) {
9396         if (!*q) {
9397             if (j > 0 && quote) {
9398                 *p++ = '"';
9399                 l++;
9400             }
9401             q = cmargv[++j];
9402             if (q) {
9403                 if (ismcr && j > 1) quote = 1;
9404                 tquote =  (strchr(q,' ')) != NULL || *q == '\0';
9405                 *p++ = ' ';
9406                 l++;
9407                 if (quote || tquote) {
9408                     *p++ = '"';
9409                     l++;
9410                 }
9411 	    }
9412         } else {
9413             if ((quote||tquote) && *q == '"') {
9414                 *p++ = '"';
9415                 l++;
9416 	    }
9417             *p++ = *q++;
9418             l++;
9419         }
9420     }
9421     *p = '\0';
9422 
9423     fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9424     if (fp == NULL) {
9425         PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9426     }
9427 }
9428 
9429 static int background_process(pTHX_ int argc, char **argv)
9430 {
9431 char command[MAX_DCL_SYMBOL + 1] = "$";
9432 $DESCRIPTOR(value, "");
9433 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9434 static $DESCRIPTOR(null, "NLA0:");
9435 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9436 char pidstring[80];
9437 $DESCRIPTOR(pidstr, "");
9438 int pid;
9439 unsigned long int flags = 17, one = 1, retsts;
9440 int len;
9441 
9442     len = my_strlcat(command, argv[0], sizeof(command));
9443     while (--argc && (len < MAX_DCL_SYMBOL))
9444 	{
9445 	my_strlcat(command, " \"", sizeof(command));
9446 	my_strlcat(command, *(++argv), sizeof(command));
9447 	len = my_strlcat(command, "\"", sizeof(command));
9448 	}
9449     value.dsc$a_pointer = command;
9450     value.dsc$w_length = strlen(value.dsc$a_pointer);
9451     _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9452     retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9453     if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9454 	_ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9455     }
9456     else {
9457 	_ckvmssts_noperl(retsts);
9458     }
9459 #ifdef ARGPROC_DEBUG
9460     PerlIO_printf(Perl_debug_log, "%s\n", command);
9461 #endif
9462     sprintf(pidstring, "%08X", pid);
9463     PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9464     pidstr.dsc$a_pointer = pidstring;
9465     pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9466     lib$set_symbol(&pidsymbol, &pidstr);
9467     return(SS$_NORMAL);
9468 }
9469 /*}}}*/
9470 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9471 
9472 
9473 /* OS-specific initialization at image activation (not thread startup) */
9474 /* Older VAXC header files lack these constants */
9475 #ifndef JPI$_RIGHTS_SIZE
9476 #  define JPI$_RIGHTS_SIZE 817
9477 #endif
9478 #ifndef KGB$M_SUBSYSTEM
9479 #  define KGB$M_SUBSYSTEM 0x8
9480 #endif
9481 
9482 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9483 
9484 /*{{{void vms_image_init(int *, char ***)*/
9485 void
9486 vms_image_init(int *argcp, char ***argvp)
9487 {
9488   int status;
9489   char eqv[LNM$C_NAMLENGTH+1] = "";
9490   unsigned int len, tabct = 8, tabidx = 0;
9491   unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9492   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9493   unsigned short int dummy, rlen;
9494   struct dsc$descriptor_s **tabvec;
9495 #if defined(PERL_IMPLICIT_CONTEXT)
9496   pTHX = NULL;
9497 #endif
9498   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
9499                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
9500                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9501                                  {          0,                0,    0,      0} };
9502 
9503 #ifdef KILL_BY_SIGPRC
9504     Perl_csighandler_init();
9505 #endif
9506 
9507 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9508     /* This was moved from the pre-image init handler because on threaded */
9509     /* Perl it was always returning 0 for the default value. */
9510     status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9511     if (status > 0) {
9512         int s;
9513 	s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9514 	if (s > 0) {
9515             int initial;
9516 	    initial = decc$feature_get_value(s, 4);
9517 	    if (initial > 0) {
9518                 /* initial is: 0 if nothing has set the feature */
9519                 /*            -1 if initialized to default */
9520                 /*             1 if set by logical name */
9521                 /*             2 if set by decc$feature_set_value */
9522 		decc_disable_posix_root = decc$feature_get_value(s, 1);
9523 
9524                 /* If the value is not valid, force the feature off */
9525 		if (decc_disable_posix_root < 0) {
9526 		    decc$feature_set_value(s, 1, 1);
9527 		    decc_disable_posix_root = 1;
9528 		}
9529 	    }
9530 	    else {
9531 		/* Nothing has asked for it explicitly, so use our own default. */
9532 		decc_disable_posix_root = 1;
9533 		decc$feature_set_value(s, 1, 1);
9534 	    }
9535 	}
9536     }
9537 #endif
9538 
9539   _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9540   _ckvmssts_noperl(iosb[0]);
9541   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9542     if (iprv[i]) {           /* Running image installed with privs? */
9543       _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
9544       will_taint = TRUE;
9545       break;
9546     }
9547   }
9548   /* Rights identifiers might trigger tainting as well. */
9549   if (!will_taint && (rlen || rsz)) {
9550     while (rlen < rsz) {
9551       /* We didn't get all the identifiers on the first pass.  Allocate a
9552        * buffer much larger than $GETJPI wants (rsz is size in bytes that
9553        * were needed to hold all identifiers at time of last call; we'll
9554        * allocate that many unsigned long ints), and go back and get 'em.
9555        * If it gave us less than it wanted to despite ample buffer space,
9556        * something's broken.  Is your system missing a system identifier?
9557        */
9558       if (rsz <= jpilist[1].buflen) {
9559          /* Perl_croak accvios when used this early in startup. */
9560          fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9561                          rsz, (unsigned long) jpilist[1].buflen,
9562                          "Check your rights database for corruption.\n");
9563          exit(SS$_ABORT);
9564       }
9565       if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9566       jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9567       if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9568       jpilist[1].buflen = rsz * sizeof(unsigned long int);
9569       _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9570       _ckvmssts_noperl(iosb[0]);
9571     }
9572     mask = (unsigned long int *)jpilist[1].bufadr;
9573     /* Check attribute flags for each identifier (2nd longword); protected
9574      * subsystem identifiers trigger tainting.
9575      */
9576     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9577       if (mask[i] & KGB$M_SUBSYSTEM) {
9578         will_taint = TRUE;
9579         break;
9580       }
9581     }
9582     if (mask != rlst) PerlMem_free(mask);
9583   }
9584 
9585   /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9586    * logical, some versions of the CRTL will add a phanthom /000000/
9587    * directory.  This needs to be removed.
9588    */
9589   if (decc_filename_unix_report) {
9590   char * zeros;
9591   int ulen;
9592     ulen = strlen(argvp[0][0]);
9593     if (ulen > 7) {
9594       zeros = strstr(argvp[0][0], "/000000/");
9595       if (zeros != NULL) {
9596 	int mlen;
9597 	mlen = ulen - (zeros - argvp[0][0]) - 7;
9598 	memmove(zeros, &zeros[7], mlen);
9599 	ulen = ulen - 7;
9600 	argvp[0][0][ulen] = '\0';
9601       }
9602     }
9603     /* It also may have a trailing dot that needs to be removed otherwise
9604      * it will be converted to VMS mode incorrectly.
9605      */
9606     ulen--;
9607     if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9608       argvp[0][0][ulen] = '\0';
9609   }
9610 
9611   /* We need to use this hack to tell Perl it should run with tainting,
9612    * since its tainting flag may be part of the PL_curinterp struct, which
9613    * hasn't been allocated when vms_image_init() is called.
9614    */
9615   if (will_taint) {
9616     char **newargv, **oldargv;
9617     oldargv = *argvp;
9618     newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9619     if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9620     newargv[0] = oldargv[0];
9621     newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9622     if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9623     strcpy(newargv[1], "-T");
9624     Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9625     (*argcp)++;
9626     newargv[*argcp] = NULL;
9627     /* We orphan the old argv, since we don't know where it's come from,
9628      * so we don't know how to free it.
9629      */
9630     *argvp = newargv;
9631   }
9632   else {  /* Did user explicitly request tainting? */
9633     int i;
9634     char *cp, **av = *argvp;
9635     for (i = 1; i < *argcp; i++) {
9636       if (*av[i] != '-') break;
9637       for (cp = av[i]+1; *cp; cp++) {
9638         if (*cp == 'T') { will_taint = 1; break; }
9639         else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9640                   strchr("DFIiMmx",*cp)) break;
9641       }
9642       if (will_taint) break;
9643     }
9644   }
9645 
9646   for (tabidx = 0;
9647        len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9648        tabidx++) {
9649     if (!tabidx) {
9650       tabvec = (struct dsc$descriptor_s **)
9651 	    PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9652       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9653     }
9654     else if (tabidx >= tabct) {
9655       tabct += 8;
9656       tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9657       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9658     }
9659     tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9660     if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9661     tabvec[tabidx]->dsc$w_length  = len;
9662     tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
9663     tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_S;
9664     tabvec[tabidx]->dsc$a_pointer = PerlMem_malloc(len + 1);
9665     if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9666     my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9667   }
9668   if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9669 
9670   getredirection(argcp,argvp);
9671 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9672   {
9673 # include <reentrancy.h>
9674   decc$set_reentrancy(C$C_MULTITHREAD);
9675   }
9676 #endif
9677   return;
9678 }
9679 /*}}}*/
9680 
9681 
9682 /* trim_unixpath()
9683  * Trim Unix-style prefix off filespec, so it looks like what a shell
9684  * glob expansion would return (i.e. from specified prefix on, not
9685  * full path).  Note that returned filespec is Unix-style, regardless
9686  * of whether input filespec was VMS-style or Unix-style.
9687  *
9688  * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9689  * determine prefix (both may be in VMS or Unix syntax).  opts is a bit
9690  * vector of options; at present, only bit 0 is used, and if set tells
9691  * trim unixpath to try the current default directory as a prefix when
9692  * presented with a possibly ambiguous ... wildcard.
9693  *
9694  * Returns !=0 on success, with trimmed filespec replacing contents of
9695  * fspec, and 0 on failure, with contents of fpsec unchanged.
9696  */
9697 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9698 int
9699 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9700 {
9701   char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9702   int tmplen, reslen = 0, dirs = 0;
9703 
9704   if (!wildspec || !fspec) return 0;
9705 
9706   unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9707   if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9708   tplate = unixwild;
9709   if (strpbrk(wildspec,"]>:") != NULL) {
9710     if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9711         PerlMem_free(unixwild);
9712 	return 0;
9713     }
9714   }
9715   else {
9716     my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9717   }
9718   unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9719   if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9720   if (strpbrk(fspec,"]>:") != NULL) {
9721     if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9722         PerlMem_free(unixwild);
9723         PerlMem_free(unixified);
9724 	return 0;
9725     }
9726     else base = unixified;
9727     /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9728      * check to see that final result fits into (isn't longer than) fspec */
9729     reslen = strlen(fspec);
9730   }
9731   else base = fspec;
9732 
9733   /* No prefix or absolute path on wildcard, so nothing to remove */
9734   if (!*tplate || *tplate == '/') {
9735     PerlMem_free(unixwild);
9736     if (base == fspec) {
9737         PerlMem_free(unixified);
9738 	return 1;
9739     }
9740     tmplen = strlen(unixified);
9741     if (tmplen > reslen) {
9742         PerlMem_free(unixified);
9743 	return 0;  /* not enough space */
9744     }
9745     /* Copy unixified resultant, including trailing NUL */
9746     memmove(fspec,unixified,tmplen+1);
9747     PerlMem_free(unixified);
9748     return 1;
9749   }
9750 
9751   for (end = base; *end; end++) ;  /* Find end of resultant filespec */
9752   if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9753     for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9754     for (cp1 = end ;cp1 >= base; cp1--)
9755       if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9756         { cp1++; break; }
9757     if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9758     PerlMem_free(unixified);
9759     PerlMem_free(unixwild);
9760     return 1;
9761   }
9762   else {
9763     char *tpl, *lcres;
9764     char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9765     int ells = 1, totells, segdirs, match;
9766     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9767                             resdsc =  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9768 
9769     while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9770     totells = ells;
9771     for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9772     tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9773     if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9774     if (ellipsis == tplate && opts & 1) {
9775       /* Template begins with an ellipsis.  Since we can't tell how many
9776        * directory names at the front of the resultant to keep for an
9777        * arbitrary starting point, we arbitrarily choose the current
9778        * default directory as a starting point.  If it's there as a prefix,
9779        * clip it off.  If not, fall through and act as if the leading
9780        * ellipsis weren't there (i.e. return shortest possible path that
9781        * could match template).
9782        */
9783       if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9784 	  PerlMem_free(tpl);
9785 	  PerlMem_free(unixified);
9786 	  PerlMem_free(unixwild);
9787 	  return 0;
9788       }
9789       if (!decc_efs_case_preserve) {
9790  	for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9791 	  if (_tolower(*cp1) != _tolower(*cp2)) break;
9792       }
9793       segdirs = dirs - totells;  /* Min # of dirs we must have left */
9794       for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9795       if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9796         memmove(fspec,cp2+1,end - cp2);
9797 	PerlMem_free(tpl);
9798 	PerlMem_free(unixified);
9799 	PerlMem_free(unixwild);
9800         return 1;
9801       }
9802     }
9803     /* First off, back up over constant elements at end of path */
9804     if (dirs) {
9805       for (front = end ; front >= base; front--)
9806          if (*front == '/' && !dirs--) { front++; break; }
9807     }
9808     lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9809     if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9810     for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9811          cp1++,cp2++) {
9812 	    if (!decc_efs_case_preserve) {
9813 		*cp2 = _tolower(*cp1);  /* Make lc copy for match */
9814 	    }
9815 	    else {
9816 		*cp2 = *cp1;
9817 	    }
9818     }
9819     if (cp1 != '\0') {
9820 	PerlMem_free(tpl);
9821 	PerlMem_free(unixified);
9822 	PerlMem_free(unixwild);
9823 	PerlMem_free(lcres);
9824 	return 0;  /* Path too long. */
9825     }
9826     lcend = cp2;
9827     *cp2 = '\0';  /* Pick up with memcpy later */
9828     lcfront = lcres + (front - base);
9829     /* Now skip over each ellipsis and try to match the path in front of it. */
9830     while (ells--) {
9831       for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9832         if (*(cp1)   == '.' && *(cp1+1) == '.' &&
9833             *(cp1+2) == '.' && *(cp1+3) == '/'    ) break;
9834       if (cp1 < tplate) break; /* template started with an ellipsis */
9835       if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9836         ellipsis = cp1; continue;
9837       }
9838       wilddsc.dsc$a_pointer = tpl;
9839       wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9840       nextell = cp1;
9841       for (segdirs = 0, cp2 = tpl;
9842            cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9843            cp1++, cp2++) {
9844          if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9845          else {
9846 	    if (!decc_efs_case_preserve) {
9847 	      *cp2 = _tolower(*cp1);  /* else lowercase for match */
9848 	    }
9849 	    else {
9850 	      *cp2 = *cp1;  /* else preserve case for match */
9851 	    }
9852 	 }
9853          if (*cp2 == '/') segdirs++;
9854       }
9855       if (cp1 != ellipsis - 1) {
9856 	  PerlMem_free(tpl);
9857 	  PerlMem_free(unixified);
9858 	  PerlMem_free(unixwild);
9859 	  PerlMem_free(lcres);
9860 	  return 0; /* Path too long */
9861       }
9862       /* Back up at least as many dirs as in template before matching */
9863       for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9864         if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9865       for (match = 0; cp1 > lcres;) {
9866         resdsc.dsc$a_pointer = cp1;
9867         if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9868           match++;
9869           if (match == 1) lcfront = cp1;
9870         }
9871         for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9872       }
9873       if (!match) {
9874 	PerlMem_free(tpl);
9875 	PerlMem_free(unixified);
9876 	PerlMem_free(unixwild);
9877 	PerlMem_free(lcres);
9878 	return 0;  /* Can't find prefix ??? */
9879       }
9880       if (match > 1 && opts & 1) {
9881         /* This ... wildcard could cover more than one set of dirs (i.e.
9882          * a set of similar dir names is repeated).  If the template
9883          * contains more than 1 ..., upstream elements could resolve the
9884          * ambiguity, but it's not worth a full backtracking setup here.
9885          * As a quick heuristic, clip off the current default directory
9886          * if it's present to find the trimmed spec, else use the
9887          * shortest string that this ... could cover.
9888          */
9889         char def[NAM$C_MAXRSS+1], *st;
9890 
9891         if (getcwd(def, sizeof def,0) == NULL) {
9892 	    PerlMem_free(unixified);
9893 	    PerlMem_free(unixwild);
9894 	    PerlMem_free(lcres);
9895 	    PerlMem_free(tpl);
9896 	    return 0;
9897 	}
9898 	if (!decc_efs_case_preserve) {
9899 	  for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9900 	    if (_tolower(*cp1) != _tolower(*cp2)) break;
9901 	}
9902         segdirs = dirs - totells;  /* Min # of dirs we must have left */
9903         for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9904         if (*cp1 == '\0' && *cp2 == '/') {
9905           memmove(fspec,cp2+1,end - cp2);
9906 	  PerlMem_free(tpl);
9907 	  PerlMem_free(unixified);
9908 	  PerlMem_free(unixwild);
9909 	  PerlMem_free(lcres);
9910           return 1;
9911         }
9912         /* Nope -- stick with lcfront from above and keep going. */
9913       }
9914     }
9915     memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9916     PerlMem_free(tpl);
9917     PerlMem_free(unixified);
9918     PerlMem_free(unixwild);
9919     PerlMem_free(lcres);
9920     return 1;
9921   }
9922 
9923 }  /* end of trim_unixpath() */
9924 /*}}}*/
9925 
9926 
9927 /*
9928  *  VMS readdir() routines.
9929  *  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9930  *
9931  *  21-Jul-1994  Charles Bailey  bailey@newman.upenn.edu
9932  *  Minor modifications to original routines.
9933  */
9934 
9935 /* readdir may have been redefined by reentr.h, so make sure we get
9936  * the local version for what we do here.
9937  */
9938 #ifdef readdir
9939 # undef readdir
9940 #endif
9941 #if !defined(PERL_IMPLICIT_CONTEXT)
9942 # define readdir Perl_readdir
9943 #else
9944 # define readdir(a) Perl_readdir(aTHX_ a)
9945 #endif
9946 
9947     /* Number of elements in vms_versions array */
9948 #define VERSIZE(e)	(sizeof e->vms_versions / sizeof e->vms_versions[0])
9949 
9950 /*
9951  *  Open a directory, return a handle for later use.
9952  */
9953 /*{{{ DIR *opendir(char*name) */
9954 DIR *
9955 Perl_opendir(pTHX_ const char *name)
9956 {
9957     DIR *dd;
9958     char *dir;
9959     Stat_t sb;
9960 
9961     Newx(dir, VMS_MAXRSS, char);
9962     if (int_tovmspath(name, dir, NULL) == NULL) {
9963       Safefree(dir);
9964       return NULL;
9965     }
9966     /* Check access before stat; otherwise stat does not
9967      * accurately report whether it's a directory.
9968      */
9969     if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9970       /* cando_by_name has already set errno */
9971       Safefree(dir);
9972       return NULL;
9973     }
9974     if (flex_stat(dir,&sb) == -1) return NULL;
9975     if (!S_ISDIR(sb.st_mode)) {
9976       Safefree(dir);
9977       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
9978       return NULL;
9979     }
9980     /* Get memory for the handle, and the pattern. */
9981     Newx(dd,1,DIR);
9982     Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9983 
9984     /* Fill in the fields; mainly playing with the descriptor. */
9985     sprintf(dd->pattern, "%s*.*",dir);
9986     Safefree(dir);
9987     dd->context = 0;
9988     dd->count = 0;
9989     dd->flags = 0;
9990     /* By saying we want the result of readdir() in unix format, we are really
9991      * saying we want all the escapes removed, translating characters that
9992      * must be escaped in a VMS-format name to their unescaped form, which is
9993      * presumably allowed in a Unix-format name.
9994      */
9995     dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
9996     dd->pat.dsc$a_pointer = dd->pattern;
9997     dd->pat.dsc$w_length = strlen(dd->pattern);
9998     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9999     dd->pat.dsc$b_class = DSC$K_CLASS_S;
10000 #if defined(USE_ITHREADS)
10001     Newx(dd->mutex,1,perl_mutex);
10002     MUTEX_INIT( (perl_mutex *) dd->mutex );
10003 #else
10004     dd->mutex = NULL;
10005 #endif
10006 
10007     return dd;
10008 }  /* end of opendir() */
10009 /*}}}*/
10010 
10011 /*
10012  *  Set the flag to indicate we want versions or not.
10013  */
10014 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10015 void
10016 vmsreaddirversions(DIR *dd, int flag)
10017 {
10018     if (flag)
10019 	dd->flags |= PERL_VMSDIR_M_VERSIONS;
10020     else
10021 	dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10022 }
10023 /*}}}*/
10024 
10025 /*
10026  *  Free up an opened directory.
10027  */
10028 /*{{{ void closedir(DIR *dd)*/
10029 void
10030 Perl_closedir(DIR *dd)
10031 {
10032     int sts;
10033 
10034     sts = lib$find_file_end(&dd->context);
10035     Safefree(dd->pattern);
10036 #if defined(USE_ITHREADS)
10037     MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10038     Safefree(dd->mutex);
10039 #endif
10040     Safefree(dd);
10041 }
10042 /*}}}*/
10043 
10044 /*
10045  *  Collect all the version numbers for the current file.
10046  */
10047 static void
10048 collectversions(pTHX_ DIR *dd)
10049 {
10050     struct dsc$descriptor_s	pat;
10051     struct dsc$descriptor_s	res;
10052     struct dirent *e;
10053     char *p, *text, *buff;
10054     int i;
10055     unsigned long context, tmpsts;
10056 
10057     /* Convenient shorthand. */
10058     e = &dd->entry;
10059 
10060     /* Add the version wildcard, ignoring the "*.*" put on before */
10061     i = strlen(dd->pattern);
10062     Newx(text,i + e->d_namlen + 3,char);
10063     my_strlcpy(text, dd->pattern, i + 1);
10064     sprintf(&text[i - 3], "%s;*", e->d_name);
10065 
10066     /* Set up the pattern descriptor. */
10067     pat.dsc$a_pointer = text;
10068     pat.dsc$w_length = i + e->d_namlen - 1;
10069     pat.dsc$b_dtype = DSC$K_DTYPE_T;
10070     pat.dsc$b_class = DSC$K_CLASS_S;
10071 
10072     /* Set up result descriptor. */
10073     Newx(buff, VMS_MAXRSS, char);
10074     res.dsc$a_pointer = buff;
10075     res.dsc$w_length = VMS_MAXRSS - 1;
10076     res.dsc$b_dtype = DSC$K_DTYPE_T;
10077     res.dsc$b_class = DSC$K_CLASS_S;
10078 
10079     /* Read files, collecting versions. */
10080     for (context = 0, e->vms_verscount = 0;
10081          e->vms_verscount < VERSIZE(e);
10082          e->vms_verscount++) {
10083 	unsigned long rsts;
10084 	unsigned long flags = 0;
10085 
10086 #ifdef VMS_LONGNAME_SUPPORT
10087 	flags = LIB$M_FIL_LONG_NAMES;
10088 #endif
10089 	tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10090 	if (tmpsts == RMS$_NMF || context == 0) break;
10091 	_ckvmssts(tmpsts);
10092 	buff[VMS_MAXRSS - 1] = '\0';
10093 	if ((p = strchr(buff, ';')))
10094 	    e->vms_versions[e->vms_verscount] = atoi(p + 1);
10095 	else
10096 	    e->vms_versions[e->vms_verscount] = -1;
10097     }
10098 
10099     _ckvmssts(lib$find_file_end(&context));
10100     Safefree(text);
10101     Safefree(buff);
10102 
10103 }  /* end of collectversions() */
10104 
10105 /*
10106  *  Read the next entry from the directory.
10107  */
10108 /*{{{ struct dirent *readdir(DIR *dd)*/
10109 struct dirent *
10110 Perl_readdir(pTHX_ DIR *dd)
10111 {
10112     struct dsc$descriptor_s	res;
10113     char *p, *buff;
10114     unsigned long int tmpsts;
10115     unsigned long rsts;
10116     unsigned long flags = 0;
10117     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10118     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10119 
10120     /* Set up result descriptor, and get next file. */
10121     Newx(buff, VMS_MAXRSS, char);
10122     res.dsc$a_pointer = buff;
10123     res.dsc$w_length = VMS_MAXRSS - 1;
10124     res.dsc$b_dtype = DSC$K_DTYPE_T;
10125     res.dsc$b_class = DSC$K_CLASS_S;
10126 
10127 #ifdef VMS_LONGNAME_SUPPORT
10128     flags = LIB$M_FIL_LONG_NAMES;
10129 #endif
10130 
10131     tmpsts = lib$find_file
10132 	(&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10133     if (dd->context == 0)
10134         tmpsts = RMS$_NMF;  /* None left. (should be set, but make sure) */
10135 
10136     if (!(tmpsts & 1)) {
10137       switch (tmpsts) {
10138         case RMS$_NMF:
10139           break;  /* no more files considered success */
10140         case RMS$_PRV:
10141           SETERRNO(EACCES, tmpsts); break;
10142         case RMS$_DEV:
10143           SETERRNO(ENODEV, tmpsts); break;
10144         case RMS$_DIR:
10145           SETERRNO(ENOTDIR, tmpsts); break;
10146         case RMS$_FNF: case RMS$_DNF:
10147           SETERRNO(ENOENT, tmpsts); break;
10148         default:
10149           SETERRNO(EVMSERR, tmpsts);
10150       }
10151       Safefree(buff);
10152       return NULL;
10153     }
10154     dd->count++;
10155     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10156     buff[res.dsc$w_length] = '\0';
10157     p = buff + res.dsc$w_length;
10158     while (--p >= buff) if (!isspace(*p)) break;
10159     *p = '\0';
10160     if (!decc_efs_case_preserve) {
10161       for (p = buff; *p; p++) *p = _tolower(*p);
10162     }
10163 
10164     /* Skip any directory component and just copy the name. */
10165     sts = vms_split_path
10166        (buff,
10167 	&v_spec,
10168 	&v_len,
10169 	&r_spec,
10170 	&r_len,
10171 	&d_spec,
10172 	&d_len,
10173 	&n_spec,
10174 	&n_len,
10175 	&e_spec,
10176 	&e_len,
10177 	&vs_spec,
10178 	&vs_len);
10179 
10180     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10181 
10182         /* In Unix report mode, remove the ".dir;1" from the name */
10183         /* if it is a real directory. */
10184         if (decc_filename_unix_report && decc_efs_charset) {
10185             if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10186                 Stat_t statbuf;
10187                 int ret_sts;
10188 
10189                 ret_sts = flex_lstat(buff, &statbuf);
10190                 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10191                     e_len = 0;
10192                     e_spec[0] = 0;
10193                 }
10194             }
10195         }
10196 
10197         /* Drop NULL extensions on UNIX file specification */
10198 	if ((e_len == 1) && decc_readdir_dropdotnotype) {
10199 	    e_len = 0;
10200 	    e_spec[0] = '\0';
10201         }
10202     }
10203 
10204     memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10205     dd->entry.d_name[n_len + e_len] = '\0';
10206     dd->entry.d_namlen = n_len + e_len;
10207 
10208     /* Convert the filename to UNIX format if needed */
10209     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10210 
10211 	/* Translate the encoded characters. */
10212 	/* Fixme: Unicode handling could result in embedded 0 characters */
10213 	if (strchr(dd->entry.d_name, '^') != NULL) {
10214 	    char new_name[256];
10215 	    char * q;
10216 	    p = dd->entry.d_name;
10217 	    q = new_name;
10218 	    while (*p != 0) {
10219 		int inchars_read, outchars_added;
10220 		inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10221 		p += inchars_read;
10222 		q += outchars_added;
10223 		/* fix-me */
10224 		/* if outchars_added > 1, then this is a wide file specification */
10225 		/* Wide file specifications need to be passed in Perl */
10226 		/* counted strings apparently with a Unicode flag */
10227 	    }
10228 	    *q = 0;
10229 	    dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10230 	}
10231     }
10232 
10233     dd->entry.vms_verscount = 0;
10234     if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10235     Safefree(buff);
10236     return &dd->entry;
10237 
10238 }  /* end of readdir() */
10239 /*}}}*/
10240 
10241 /*
10242  *  Read the next entry from the directory -- thread-safe version.
10243  */
10244 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10245 int
10246 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10247 {
10248     int retval;
10249 
10250     MUTEX_LOCK( (perl_mutex *) dd->mutex );
10251 
10252     entry = readdir(dd);
10253     *result = entry;
10254     retval = ( *result == NULL ? errno : 0 );
10255 
10256     MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10257 
10258     return retval;
10259 
10260 }  /* end of readdir_r() */
10261 /*}}}*/
10262 
10263 /*
10264  *  Return something that can be used in a seekdir later.
10265  */
10266 /*{{{ long telldir(DIR *dd)*/
10267 long
10268 Perl_telldir(DIR *dd)
10269 {
10270     return dd->count;
10271 }
10272 /*}}}*/
10273 
10274 /*
10275  *  Return to a spot where we used to be.  Brute force.
10276  */
10277 /*{{{ void seekdir(DIR *dd,long count)*/
10278 void
10279 Perl_seekdir(pTHX_ DIR *dd, long count)
10280 {
10281     int old_flags;
10282 
10283     /* If we haven't done anything yet... */
10284     if (dd->count == 0)
10285 	return;
10286 
10287     /* Remember some state, and clear it. */
10288     old_flags = dd->flags;
10289     dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10290     _ckvmssts(lib$find_file_end(&dd->context));
10291     dd->context = 0;
10292 
10293     /* The increment is in readdir(). */
10294     for (dd->count = 0; dd->count < count; )
10295 	readdir(dd);
10296 
10297     dd->flags = old_flags;
10298 
10299 }  /* end of seekdir() */
10300 /*}}}*/
10301 
10302 /* VMS subprocess management
10303  *
10304  * my_vfork() - just a vfork(), after setting a flag to record that
10305  * the current script is trying a Unix-style fork/exec.
10306  *
10307  * vms_do_aexec() and vms_do_exec() are called in response to the
10308  * perl 'exec' function.  If this follows a vfork call, then they
10309  * call out the regular perl routines in doio.c which do an
10310  * execvp (for those who really want to try this under VMS).
10311  * Otherwise, they do exactly what the perl docs say exec should
10312  * do - terminate the current script and invoke a new command
10313  * (See below for notes on command syntax.)
10314  *
10315  * do_aspawn() and do_spawn() implement the VMS side of the perl
10316  * 'system' function.
10317  *
10318  * Note on command arguments to perl 'exec' and 'system': When handled
10319  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10320  * are concatenated to form a DCL command string.  If the first non-numeric
10321  * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10322  * the command string is handed off to DCL directly.  Otherwise,
10323  * the first token of the command is taken as the filespec of an image
10324  * to run.  The filespec is expanded using a default type of '.EXE' and
10325  * the process defaults for device, directory, etc., and if found, the resultant
10326  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10327  * the command string as parameters.  This is perhaps a bit complicated,
10328  * but I hope it will form a happy medium between what VMS folks expect
10329  * from lib$spawn and what Unix folks expect from exec.
10330  */
10331 
10332 static int vfork_called;
10333 
10334 /*{{{int my_vfork(void)*/
10335 int
10336 my_vfork(void)
10337 {
10338   vfork_called++;
10339   return vfork();
10340 }
10341 /*}}}*/
10342 
10343 
10344 static void
10345 vms_execfree(struct dsc$descriptor_s *vmscmd)
10346 {
10347   if (vmscmd) {
10348       if (vmscmd->dsc$a_pointer) {
10349           PerlMem_free(vmscmd->dsc$a_pointer);
10350       }
10351       PerlMem_free(vmscmd);
10352   }
10353 }
10354 
10355 static char *
10356 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10357 {
10358   char *junk, *tmps = NULL;
10359   size_t cmdlen = 0;
10360   size_t rlen;
10361   SV **idx;
10362   STRLEN n_a;
10363 
10364   idx = mark;
10365   if (really) {
10366     tmps = SvPV(really,rlen);
10367     if (*tmps) {
10368       cmdlen += rlen + 1;
10369       idx++;
10370     }
10371   }
10372 
10373   for (idx++; idx <= sp; idx++) {
10374     if (*idx) {
10375       junk = SvPVx(*idx,rlen);
10376       cmdlen += rlen ? rlen + 1 : 0;
10377     }
10378   }
10379   Newx(PL_Cmd, cmdlen+1, char);
10380 
10381   if (tmps && *tmps) {
10382     my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10383     mark++;
10384   }
10385   else *PL_Cmd = '\0';
10386   while (++mark <= sp) {
10387     if (*mark) {
10388       char *s = SvPVx(*mark,n_a);
10389       if (!*s) continue;
10390       if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10391       my_strlcat(PL_Cmd, s, cmdlen+1);
10392     }
10393   }
10394   return PL_Cmd;
10395 
10396 }  /* end of setup_argstr() */
10397 
10398 
10399 static unsigned long int
10400 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10401                    struct dsc$descriptor_s **pvmscmd)
10402 {
10403   char * vmsspec;
10404   char * resspec;
10405   char image_name[NAM$C_MAXRSS+1];
10406   char image_argv[NAM$C_MAXRSS+1];
10407   $DESCRIPTOR(defdsc,".EXE");
10408   $DESCRIPTOR(defdsc2,".");
10409   struct dsc$descriptor_s resdsc;
10410   struct dsc$descriptor_s *vmscmd;
10411   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10412   unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10413   char *s, *rest, *cp, *wordbreak;
10414   char * cmd;
10415   int cmdlen;
10416   int isdcl;
10417 
10418   vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10419   if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10420 
10421   /* vmsspec is a DCL command buffer, not just a filename */
10422   vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10423   if (vmsspec == NULL)
10424       _ckvmssts_noperl(SS$_INSFMEM);
10425 
10426   resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10427   if (resspec == NULL)
10428       _ckvmssts_noperl(SS$_INSFMEM);
10429 
10430   /* Make a copy for modification */
10431   cmdlen = strlen(incmd);
10432   cmd = (char *)PerlMem_malloc(cmdlen+1);
10433   if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10434   my_strlcpy(cmd, incmd, cmdlen + 1);
10435   image_name[0] = 0;
10436   image_argv[0] = 0;
10437 
10438   resdsc.dsc$a_pointer = resspec;
10439   resdsc.dsc$b_dtype  = DSC$K_DTYPE_T;
10440   resdsc.dsc$b_class  = DSC$K_CLASS_S;
10441   resdsc.dsc$w_length = VMS_MAXRSS - 1;
10442 
10443   vmscmd->dsc$a_pointer = NULL;
10444   vmscmd->dsc$b_dtype  = DSC$K_DTYPE_T;
10445   vmscmd->dsc$b_class  = DSC$K_CLASS_S;
10446   vmscmd->dsc$w_length = 0;
10447   if (pvmscmd) *pvmscmd = vmscmd;
10448 
10449   if (suggest_quote) *suggest_quote = 0;
10450 
10451   if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10452     PerlMem_free(cmd);
10453     PerlMem_free(vmsspec);
10454     PerlMem_free(resspec);
10455     return CLI$_BUFOVF;                /* continuation lines currently unsupported */
10456   }
10457 
10458   s = cmd;
10459 
10460   while (*s && isspace(*s)) s++;
10461 
10462   if (*s == '@' || *s == '$') {
10463     vmsspec[0] = *s;  rest = s + 1;
10464     for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10465   }
10466   else { cp = vmsspec; rest = s; }
10467 
10468   /* If the first word is quoted, then we need to unquote it and
10469    * escape spaces within it.  We'll expand into the resspec buffer,
10470    * then copy back into the cmd buffer, expanding the latter if
10471    * necessary.
10472    */
10473   if (*rest == '"') {
10474     char *cp2;
10475     char *r = rest;
10476     bool in_quote = 0;
10477     int clen = cmdlen;
10478     int soff = s - cmd;
10479 
10480     for (cp2 = resspec;
10481          *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10482          rest++) {
10483 
10484       if (*rest == ' ') {    /* Escape ' ' to '^_'. */
10485         *cp2 = '^';
10486         *(++cp2) = '_';
10487         cp2++;
10488         clen++;
10489       }
10490       else if (*rest == '"') {
10491         clen--;
10492         if (in_quote) {     /* Must be closing quote. */
10493           rest++;
10494           break;
10495         }
10496         in_quote = 1;
10497       }
10498       else {
10499         *cp2 = *rest;
10500         cp2++;
10501       }
10502     }
10503     *cp2 = '\0';
10504 
10505     /* Expand the command buffer if necessary. */
10506     if (clen > cmdlen) {
10507       cmd = (char *)PerlMem_realloc(cmd, clen);
10508       if (cmd == NULL)
10509         _ckvmssts_noperl(SS$_INSFMEM);
10510       /* Where we are may have changed, so recompute offsets */
10511       r = cmd + (r - s - soff);
10512       rest = cmd + (rest - s - soff);
10513       s = cmd + soff;
10514     }
10515 
10516     /* Shift the non-verb portion of the command (if any) up or
10517      * down as necessary.
10518      */
10519     if (*rest)
10520       memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10521 
10522     /* Copy the unquoted and escaped command verb into place. */
10523     memcpy(r, resspec, cp2 - resspec);
10524     cmd[clen] = '\0';
10525     cmdlen = clen;
10526     rest = r;         /* Rewind for subsequent operations. */
10527   }
10528 
10529   if (*rest == '.' || *rest == '/') {
10530     char *cp2;
10531     for (cp2 = resspec;
10532          *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10533          rest++, cp2++) *cp2 = *rest;
10534     *cp2 = '\0';
10535     if (int_tovmsspec(resspec, cp, 0, NULL)) {
10536       s = vmsspec;
10537 
10538       /* When a UNIX spec with no file type is translated to VMS, */
10539       /* A trailing '.' is appended under ODS-5 rules.            */
10540       /* Here we do not want that trailing "." as it prevents     */
10541       /* Looking for a implied ".exe" type. */
10542       if (decc_efs_charset) {
10543           int i;
10544           i = strlen(vmsspec);
10545           if (vmsspec[i-1] == '.') {
10546               vmsspec[i-1] = '\0';
10547           }
10548       }
10549 
10550       if (*rest) {
10551         for (cp2 = vmsspec + strlen(vmsspec);
10552              *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10553              rest++, cp2++) *cp2 = *rest;
10554         *cp2 = '\0';
10555       }
10556     }
10557   }
10558   /* Intuit whether verb (first word of cmd) is a DCL command:
10559    *   - if first nonspace char is '@', it's a DCL indirection
10560    * otherwise
10561    *   - if verb contains a filespec separator, it's not a DCL command
10562    *   - if it doesn't, caller tells us whether to default to a DCL
10563    *     command, or to a local image unless told it's DCL (by leading '$')
10564    */
10565   if (*s == '@') {
10566       isdcl = 1;
10567       if (suggest_quote) *suggest_quote = 1;
10568   } else {
10569     char *filespec = strpbrk(s,":<[.;");
10570     rest = wordbreak = strpbrk(s," \"\t/");
10571     if (!wordbreak) wordbreak = s + strlen(s);
10572     if (*s == '$') check_img = 0;
10573     if (filespec && (filespec < wordbreak)) isdcl = 0;
10574     else isdcl = !check_img;
10575   }
10576 
10577   if (!isdcl) {
10578     int rsts;
10579     imgdsc.dsc$a_pointer = s;
10580     imgdsc.dsc$w_length = wordbreak - s;
10581     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10582     if (!(retsts&1)) {
10583         _ckvmssts_noperl(lib$find_file_end(&cxt));
10584         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10585       if (!(retsts & 1) && *s == '$') {
10586         _ckvmssts_noperl(lib$find_file_end(&cxt));
10587 	imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10588 	retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10589 	if (!(retsts&1)) {
10590 	  _ckvmssts_noperl(lib$find_file_end(&cxt));
10591           retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10592         }
10593       }
10594     }
10595     _ckvmssts_noperl(lib$find_file_end(&cxt));
10596 
10597     if (retsts & 1) {
10598       FILE *fp;
10599       s = resspec;
10600       while (*s && !isspace(*s)) s++;
10601       *s = '\0';
10602 
10603       /* check that it's really not DCL with no file extension */
10604       fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10605       if (fp) {
10606         char b[256] = {0,0,0,0};
10607         read(fileno(fp), b, 256);
10608         isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10609 	if (isdcl) {
10610 	  int shebang_len;
10611 
10612 	  /* Check for script */
10613 	  shebang_len = 0;
10614 	  if ((b[0] == '#') && (b[1] == '!'))
10615 	     shebang_len = 2;
10616 #ifdef ALTERNATE_SHEBANG
10617 	  else {
10618 	    shebang_len = strlen(ALTERNATE_SHEBANG);
10619 	    if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10620 	      char * perlstr;
10621 		perlstr = strstr("perl",b);
10622 		if (perlstr == NULL)
10623 		  shebang_len = 0;
10624 	    }
10625 	    else
10626 	      shebang_len = 0;
10627 	  }
10628 #endif
10629 
10630 	  if (shebang_len > 0) {
10631 	  int i;
10632 	  int j;
10633 	  char tmpspec[NAM$C_MAXRSS + 1];
10634 
10635 	    i = shebang_len;
10636 	     /* Image is following after white space */
10637 	    /*--------------------------------------*/
10638 	    while (isprint(b[i]) && isspace(b[i]))
10639 		i++;
10640 
10641 	    j = 0;
10642 	    while (isprint(b[i]) && !isspace(b[i])) {
10643 		tmpspec[j++] = b[i++];
10644 		if (j >= NAM$C_MAXRSS)
10645 		   break;
10646 	    }
10647 	    tmpspec[j] = '\0';
10648 
10649 	     /* There may be some default parameters to the image */
10650 	    /*---------------------------------------------------*/
10651 	    j = 0;
10652 	    while (isprint(b[i])) {
10653 		image_argv[j++] = b[i++];
10654 		if (j >= NAM$C_MAXRSS)
10655 		   break;
10656 	    }
10657 	    while ((j > 0) && !isprint(image_argv[j-1]))
10658 		j--;
10659 	    image_argv[j] = 0;
10660 
10661 	    /* It will need to be converted to VMS format and validated */
10662 	    if (tmpspec[0] != '\0') {
10663 	      char * iname;
10664 
10665 	       /* Try to find the exact program requested to be run */
10666 	      /*---------------------------------------------------*/
10667 	      iname = int_rmsexpand
10668 		 (tmpspec, image_name, ".exe",
10669 		  PERL_RMSEXPAND_M_VMS, NULL, NULL);
10670 	      if (iname != NULL) {
10671 		if (cando_by_name_int
10672 			(S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10673 		  /* MCR prefix needed */
10674 		  isdcl = 0;
10675 		}
10676 		else {
10677 		   /* Try again with a null type */
10678 		  /*----------------------------*/
10679 		  iname = int_rmsexpand
10680 		    (tmpspec, image_name, ".",
10681 		     PERL_RMSEXPAND_M_VMS, NULL, NULL);
10682 		  if (iname != NULL) {
10683 		    if (cando_by_name_int
10684 			 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10685 		      /* MCR prefix needed */
10686 		      isdcl = 0;
10687 		    }
10688 		  }
10689 		}
10690 
10691 		 /* Did we find the image to run the script? */
10692 		/*------------------------------------------*/
10693 		if (isdcl) {
10694 		  char *tchr;
10695 
10696 		   /* Assume DCL or foreign command exists */
10697 		  /*--------------------------------------*/
10698 		  tchr = strrchr(tmpspec, '/');
10699 		  if (tchr != NULL) {
10700 		    tchr++;
10701 		  }
10702 		  else {
10703 		    tchr = tmpspec;
10704 		  }
10705 		  my_strlcpy(image_name, tchr, sizeof(image_name));
10706 		}
10707 	      }
10708 	    }
10709 	  }
10710 	}
10711         fclose(fp);
10712       }
10713       if (check_img && isdcl) {
10714           PerlMem_free(cmd);
10715           PerlMem_free(resspec);
10716           PerlMem_free(vmsspec);
10717           return RMS$_FNF;
10718       }
10719 
10720       if (cando_by_name(S_IXUSR,0,resspec)) {
10721         vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10722 	if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10723         if (!isdcl) {
10724             my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10725 	    if (image_name[0] != 0) {
10726 		my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10727 		my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10728 	    }
10729 	} else if (image_name[0] != 0) {
10730 	    my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10731 	    my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10732         } else {
10733             my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10734         }
10735         if (suggest_quote) *suggest_quote = 1;
10736 
10737 	/* If there is an image name, use original command */
10738 	if (image_name[0] == 0)
10739 	    my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10740 	else {
10741 	    rest = cmd;
10742 	    while (*rest && isspace(*rest)) rest++;
10743 	}
10744 
10745 	if (image_argv[0] != 0) {
10746 	  my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10747 	  my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10748 	}
10749         if (rest) {
10750 	   int rest_len;
10751 	   int vmscmd_len;
10752 
10753 	   rest_len = strlen(rest);
10754 	   vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10755 	   if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10756 	      my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10757 	   else
10758 	     retsts = CLI$_BUFOVF;
10759 	}
10760         vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10761         PerlMem_free(cmd);
10762         PerlMem_free(vmsspec);
10763         PerlMem_free(resspec);
10764         return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10765       }
10766       else
10767 	retsts = RMS$_PRV;
10768     }
10769   }
10770   /* It's either a DCL command or we couldn't find a suitable image */
10771   vmscmd->dsc$w_length = strlen(cmd);
10772 
10773   vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10774   my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10775 
10776   PerlMem_free(cmd);
10777   PerlMem_free(resspec);
10778   PerlMem_free(vmsspec);
10779 
10780   /* check if it's a symbol (for quoting purposes) */
10781   if (suggest_quote && !*suggest_quote) {
10782     int iss;
10783     char equiv[LNM$C_NAMLENGTH];
10784     struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10785     eqvdsc.dsc$a_pointer = equiv;
10786 
10787     iss = lib$get_symbol(vmscmd,&eqvdsc);
10788     if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10789   }
10790   if (!(retsts & 1)) {
10791     /* just hand off status values likely to be due to user error */
10792     if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10793         retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10794        (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10795     else { _ckvmssts_noperl(retsts); }
10796   }
10797 
10798   return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10799 
10800 }  /* end of setup_cmddsc() */
10801 
10802 
10803 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10804 bool
10805 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10806 {
10807 bool exec_sts;
10808 char * cmd;
10809 
10810   if (sp > mark) {
10811     if (vfork_called) {           /* this follows a vfork - act Unixish */
10812       vfork_called--;
10813       if (vfork_called < 0) {
10814         Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10815         vfork_called = 0;
10816       }
10817       else return do_aexec(really,mark,sp);
10818     }
10819                                            /* no vfork - act VMSish */
10820     cmd = setup_argstr(aTHX_ really,mark,sp);
10821     exec_sts = vms_do_exec(cmd);
10822     Safefree(cmd);  /* Clean up from setup_argstr() */
10823     return exec_sts;
10824   }
10825 
10826   return FALSE;
10827 }  /* end of vms_do_aexec() */
10828 /*}}}*/
10829 
10830 /* {{{bool vms_do_exec(char *cmd) */
10831 bool
10832 Perl_vms_do_exec(pTHX_ const char *cmd)
10833 {
10834   struct dsc$descriptor_s *vmscmd;
10835 
10836   if (vfork_called) {             /* this follows a vfork - act Unixish */
10837     vfork_called--;
10838     if (vfork_called < 0) {
10839       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10840       vfork_called = 0;
10841     }
10842     else return do_exec(cmd);
10843   }
10844 
10845   {                               /* no vfork - act VMSish */
10846     unsigned long int retsts;
10847 
10848     TAINT_ENV();
10849     TAINT_PROPER("exec");
10850     if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10851       retsts = lib$do_command(vmscmd);
10852 
10853     switch (retsts) {
10854       case RMS$_FNF: case RMS$_DNF:
10855         set_errno(ENOENT); break;
10856       case RMS$_DIR:
10857         set_errno(ENOTDIR); break;
10858       case RMS$_DEV:
10859         set_errno(ENODEV); break;
10860       case RMS$_PRV:
10861         set_errno(EACCES); break;
10862       case RMS$_SYN:
10863         set_errno(EINVAL); break;
10864       case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10865         set_errno(E2BIG); break;
10866       case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10867         _ckvmssts_noperl(retsts); /* fall through */
10868       default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10869         set_errno(EVMSERR);
10870     }
10871     set_vaxc_errno(retsts);
10872     if (ckWARN(WARN_EXEC)) {
10873       Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10874              vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10875     }
10876     vms_execfree(vmscmd);
10877   }
10878 
10879   return FALSE;
10880 
10881 }  /* end of vms_do_exec() */
10882 /*}}}*/
10883 
10884 int do_spawn2(pTHX_ const char *, int);
10885 
10886 int
10887 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10888 {
10889 unsigned long int sts;
10890 char * cmd;
10891 int flags = 0;
10892 
10893   if (sp > mark) {
10894 
10895     /* We'll copy the (undocumented?) Win32 behavior and allow a
10896      * numeric first argument.  But the only value we'll support
10897      * through do_aspawn is a value of 1, which means spawn without
10898      * waiting for completion -- other values are ignored.
10899      */
10900     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10901 	++mark;
10902 	flags = SvIVx(*mark);
10903     }
10904 
10905     if (flags && flags == 1)     /* the Win32 P_NOWAIT value */
10906         flags = CLI$M_NOWAIT;
10907     else
10908         flags = 0;
10909 
10910     cmd = setup_argstr(aTHX_ really, mark, sp);
10911     sts = do_spawn2(aTHX_ cmd, flags);
10912     /* pp_sys will clean up cmd */
10913     return sts;
10914   }
10915   return SS$_ABORT;
10916 }  /* end of do_aspawn() */
10917 /*}}}*/
10918 
10919 
10920 /* {{{int do_spawn(char* cmd) */
10921 int
10922 Perl_do_spawn(pTHX_ char* cmd)
10923 {
10924     PERL_ARGS_ASSERT_DO_SPAWN;
10925 
10926     return do_spawn2(aTHX_ cmd, 0);
10927 }
10928 /*}}}*/
10929 
10930 /* {{{int do_spawn_nowait(char* cmd) */
10931 int
10932 Perl_do_spawn_nowait(pTHX_ char* cmd)
10933 {
10934     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10935 
10936     return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10937 }
10938 /*}}}*/
10939 
10940 /* {{{int do_spawn2(char *cmd) */
10941 int
10942 do_spawn2(pTHX_ const char *cmd, int flags)
10943 {
10944   unsigned long int sts, substs;
10945 
10946   /* The caller of this routine expects to Safefree(PL_Cmd) */
10947   Newx(PL_Cmd,10,char);
10948 
10949   TAINT_ENV();
10950   TAINT_PROPER("spawn");
10951   if (!cmd || !*cmd) {
10952     sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10953     if (!(sts & 1)) {
10954       switch (sts) {
10955         case RMS$_FNF:  case RMS$_DNF:
10956           set_errno(ENOENT); break;
10957         case RMS$_DIR:
10958           set_errno(ENOTDIR); break;
10959         case RMS$_DEV:
10960           set_errno(ENODEV); break;
10961         case RMS$_PRV:
10962           set_errno(EACCES); break;
10963         case RMS$_SYN:
10964           set_errno(EINVAL); break;
10965         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10966           set_errno(E2BIG); break;
10967         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10968           _ckvmssts_noperl(sts); /* fall through */
10969         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10970           set_errno(EVMSERR);
10971       }
10972       set_vaxc_errno(sts);
10973       if (ckWARN(WARN_EXEC)) {
10974         Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10975 		    Strerror(errno));
10976       }
10977     }
10978     sts = substs;
10979   }
10980   else {
10981     char mode[3];
10982     PerlIO * fp;
10983     if (flags & CLI$M_NOWAIT)
10984         strcpy(mode, "n");
10985     else
10986         strcpy(mode, "nW");
10987 
10988     fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10989     if (fp != NULL)
10990       my_pclose(fp);
10991     /* sts will be the pid in the nowait case */
10992   }
10993   return sts;
10994 }  /* end of do_spawn2() */
10995 /*}}}*/
10996 
10997 
10998 static unsigned int *sockflags, sockflagsize;
10999 
11000 /*
11001  * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11002  * routines found in some versions of the CRTL can't deal with sockets.
11003  * We don't shim the other file open routines since a socket isn't
11004  * likely to be opened by a name.
11005  */
11006 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11007 FILE *my_fdopen(int fd, const char *mode)
11008 {
11009   FILE *fp = fdopen(fd, mode);
11010 
11011   if (fp) {
11012     unsigned int fdoff = fd / sizeof(unsigned int);
11013     Stat_t sbuf; /* native stat; we don't need flex_stat */
11014     if (!sockflagsize || fdoff > sockflagsize) {
11015       if (sockflags) Renew(     sockflags,fdoff+2,unsigned int);
11016       else           Newx  (sockflags,fdoff+2,unsigned int);
11017       memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11018       sockflagsize = fdoff + 2;
11019     }
11020     if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11021       sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11022   }
11023   return fp;
11024 
11025 }
11026 /*}}}*/
11027 
11028 
11029 /*
11030  * Clear the corresponding bit when the (possibly) socket stream is closed.
11031  * There still a small hole: we miss an implicit close which might occur
11032  * via freopen().  >> Todo
11033  */
11034 /*{{{ int my_fclose(FILE *fp)*/
11035 int my_fclose(FILE *fp) {
11036   if (fp) {
11037     unsigned int fd = fileno(fp);
11038     unsigned int fdoff = fd / sizeof(unsigned int);
11039 
11040     if (sockflagsize && fdoff < sockflagsize)
11041       sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11042   }
11043   return fclose(fp);
11044 }
11045 /*}}}*/
11046 
11047 
11048 /*
11049  * A simple fwrite replacement which outputs itmsz*nitm chars without
11050  * introducing record boundaries every itmsz chars.
11051  * We are using fputs, which depends on a terminating null.  We may
11052  * well be writing binary data, so we need to accommodate not only
11053  * data with nulls sprinkled in the middle but also data with no null
11054  * byte at the end.
11055  */
11056 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11057 int
11058 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11059 {
11060   char *cp, *end, *cpd;
11061   char *data;
11062   unsigned int fd = fileno(dest);
11063   unsigned int fdoff = fd / sizeof(unsigned int);
11064   int retval;
11065   int bufsize = itmsz * nitm + 1;
11066 
11067   if (fdoff < sockflagsize &&
11068       (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11069     if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11070     return nitm;
11071   }
11072 
11073   _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11074   memcpy( data, src, itmsz*nitm );
11075   data[itmsz*nitm] = '\0';
11076 
11077   end = data + itmsz * nitm;
11078   retval = (int) nitm; /* on success return # items written */
11079 
11080   cpd = data;
11081   while (cpd <= end) {
11082     for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11083     if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11084     if (cp < end)
11085       if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11086     cpd = cp + 1;
11087   }
11088 
11089   if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11090   return retval;
11091 
11092 }  /* end of my_fwrite() */
11093 /*}}}*/
11094 
11095 /*{{{ int my_flush(FILE *fp)*/
11096 int
11097 Perl_my_flush(pTHX_ FILE *fp)
11098 {
11099     int res;
11100     if ((res = fflush(fp)) == 0 && fp) {
11101 #ifdef VMS_DO_SOCKETS
11102 	Stat_t s;
11103 	if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11104 #endif
11105 	    res = fsync(fileno(fp));
11106     }
11107 /*
11108  * If the flush succeeded but set end-of-file, we need to clear
11109  * the error because our caller may check ferror().  BTW, this
11110  * probably means we just flushed an empty file.
11111  */
11112     if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11113 
11114     return res;
11115 }
11116 /*}}}*/
11117 
11118 /* fgetname() is not returning the correct file specifications when
11119  * decc_filename_unix_report mode is active.  So we have to have it
11120  * aways return filenames in VMS mode and convert it ourselves.
11121  */
11122 
11123 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11124 char *
11125 Perl_my_fgetname(FILE *fp, char * buf) {
11126     char * retname;
11127     char * vms_name;
11128 
11129     retname = fgetname(fp, buf, 1);
11130 
11131     /* If we are in VMS mode, then we are done */
11132     if (!decc_filename_unix_report || (retname == NULL)) {
11133        return retname;
11134     }
11135 
11136     /* Convert this to Unix format */
11137     vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11138     my_strlcpy(vms_name, retname, VMS_MAXRSS);
11139     retname = int_tounixspec(vms_name, buf, NULL);
11140     PerlMem_free(vms_name);
11141 
11142     return retname;
11143 }
11144 /*}}}*/
11145 
11146 /*
11147  * Here are replacements for the following Unix routines in the VMS environment:
11148  *      getpwuid    Get information for a particular UIC or UID
11149  *      getpwnam    Get information for a named user
11150  *      getpwent    Get information for each user in the rights database
11151  *      setpwent    Reset search to the start of the rights database
11152  *      endpwent    Finish searching for users in the rights database
11153  *
11154  * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11155  * (defined in pwd.h), which contains the following fields:-
11156  *      struct passwd {
11157  *              char        *pw_name;    Username (in lower case)
11158  *              char        *pw_passwd;  Hashed password
11159  *              unsigned int pw_uid;     UIC
11160  *              unsigned int pw_gid;     UIC group  number
11161  *              char        *pw_unixdir; Default device/directory (VMS-style)
11162  *              char        *pw_gecos;   Owner name
11163  *              char        *pw_dir;     Default device/directory (Unix-style)
11164  *              char        *pw_shell;   Default CLI name (eg. DCL)
11165  *      };
11166  * If the specified user does not exist, getpwuid and getpwnam return NULL.
11167  *
11168  * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11169  * not the UIC member number (eg. what's returned by getuid()),
11170  * getpwuid() can accept either as input (if uid is specified, the caller's
11171  * UIC group is used), though it won't recognise gid=0.
11172  *
11173  * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11174  * information about other users in your group or in other groups, respectively.
11175  * If the required privilege is not available, then these routines fill only
11176  * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11177  * string).
11178  *
11179  * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11180  */
11181 
11182 /* sizes of various UAF record fields */
11183 #define UAI$S_USERNAME 12
11184 #define UAI$S_IDENT    31
11185 #define UAI$S_OWNER    31
11186 #define UAI$S_DEFDEV   31
11187 #define UAI$S_DEFDIR   63
11188 #define UAI$S_DEFCLI   31
11189 #define UAI$S_PWD       8
11190 
11191 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT  && \
11192                         (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11193                         (uic).uic$v_group  != UIC$K_WILD_GROUP)
11194 
11195 static char __empty[]= "";
11196 static struct passwd __passwd_empty=
11197     {(char *) __empty, (char *) __empty, 0, 0,
11198      (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11199 static int contxt= 0;
11200 static struct passwd __pwdcache;
11201 static char __pw_namecache[UAI$S_IDENT+1];
11202 
11203 /*
11204  * This routine does most of the work extracting the user information.
11205  */
11206 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11207 {
11208     static struct {
11209         unsigned char length;
11210         char pw_gecos[UAI$S_OWNER+1];
11211     } owner;
11212     static union uicdef uic;
11213     static struct {
11214         unsigned char length;
11215         char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11216     } defdev;
11217     static struct {
11218         unsigned char length;
11219         char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11220     } defdir;
11221     static struct {
11222         unsigned char length;
11223         char pw_shell[UAI$S_DEFCLI+1];
11224     } defcli;
11225     static char pw_passwd[UAI$S_PWD+1];
11226 
11227     static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11228     struct dsc$descriptor_s name_desc;
11229     unsigned long int sts;
11230 
11231     static struct itmlst_3 itmlst[]= {
11232         {UAI$S_OWNER+1,    UAI$_OWNER,  &owner,    &lowner},
11233         {sizeof(uic),      UAI$_UIC,    &uic,      &luic},
11234         {UAI$S_DEFDEV+1,   UAI$_DEFDEV, &defdev,   &ldefdev},
11235         {UAI$S_DEFDIR+1,   UAI$_DEFDIR, &defdir,   &ldefdir},
11236         {UAI$S_DEFCLI+1,   UAI$_DEFCLI, &defcli,   &ldefcli},
11237         {UAI$S_PWD,        UAI$_PWD,    pw_passwd, &lpwd},
11238         {0,                0,           NULL,    NULL}};
11239 
11240     name_desc.dsc$w_length=  strlen(name);
11241     name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11242     name_desc.dsc$b_class=   DSC$K_CLASS_S;
11243     name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11244 
11245 /*  Note that sys$getuai returns many fields as counted strings. */
11246     sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11247     if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11248       set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11249     }
11250     else { _ckvmssts(sts); }
11251     if (!(sts & 1)) return 0;  /* out here in case _ckvmssts() doesn't abort */
11252 
11253     if ((int) owner.length  < lowner)  lowner=  (int) owner.length;
11254     if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11255     if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11256     if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11257     memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11258     owner.pw_gecos[lowner]=            '\0';
11259     defdev.pw_dir[ldefdev+ldefdir]= '\0';
11260     defcli.pw_shell[ldefcli]=          '\0';
11261     if (valid_uic(uic)) {
11262         pwd->pw_uid= uic.uic$l_uic;
11263         pwd->pw_gid= uic.uic$v_group;
11264     }
11265     else
11266       Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11267     pwd->pw_passwd=  pw_passwd;
11268     pwd->pw_gecos=   owner.pw_gecos;
11269     pwd->pw_dir=     defdev.pw_dir;
11270     pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11271     pwd->pw_shell=   defcli.pw_shell;
11272     if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11273         int ldir;
11274         ldir= strlen(pwd->pw_unixdir) - 1;
11275         if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11276     }
11277     else
11278         my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11279     if (!decc_efs_case_preserve)
11280         __mystrtolower(pwd->pw_unixdir);
11281     return 1;
11282 }
11283 
11284 /*
11285  * Get information for a named user.
11286 */
11287 /*{{{struct passwd *getpwnam(char *name)*/
11288 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11289 {
11290     struct dsc$descriptor_s name_desc;
11291     union uicdef uic;
11292     unsigned long int sts;
11293 
11294     __pwdcache = __passwd_empty;
11295     if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11296       /* We still may be able to determine pw_uid and pw_gid */
11297       name_desc.dsc$w_length=  strlen(name);
11298       name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11299       name_desc.dsc$b_class=   DSC$K_CLASS_S;
11300       name_desc.dsc$a_pointer= (char *) name;
11301       if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11302         __pwdcache.pw_uid= uic.uic$l_uic;
11303         __pwdcache.pw_gid= uic.uic$v_group;
11304       }
11305       else {
11306         if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11307           set_vaxc_errno(sts);
11308           set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11309           return NULL;
11310         }
11311         else { _ckvmssts(sts); }
11312       }
11313     }
11314     my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11315     __pwdcache.pw_name= __pw_namecache;
11316     return &__pwdcache;
11317 }  /* end of my_getpwnam() */
11318 /*}}}*/
11319 
11320 /*
11321  * Get information for a particular UIC or UID.
11322  * Called by my_getpwent with uid=-1 to list all users.
11323 */
11324 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11325 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11326 {
11327     const $DESCRIPTOR(name_desc,__pw_namecache);
11328     unsigned short lname;
11329     union uicdef uic;
11330     unsigned long int status;
11331 
11332     if (uid == (unsigned int) -1) {
11333       do {
11334         status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11335         if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11336           set_vaxc_errno(status);
11337           set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11338           my_endpwent();
11339           return NULL;
11340         }
11341         else { _ckvmssts(status); }
11342       } while (!valid_uic (uic));
11343     }
11344     else {
11345       uic.uic$l_uic= uid;
11346       if (!uic.uic$v_group)
11347         uic.uic$v_group= PerlProc_getgid();
11348       if (valid_uic(uic))
11349         status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11350       else status = SS$_IVIDENT;
11351       if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11352           status == RMS$_PRV) {
11353         set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11354         return NULL;
11355       }
11356       else { _ckvmssts(status); }
11357     }
11358     __pw_namecache[lname]= '\0';
11359     __mystrtolower(__pw_namecache);
11360 
11361     __pwdcache = __passwd_empty;
11362     __pwdcache.pw_name = __pw_namecache;
11363 
11364 /*  Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11365     The identifier's value is usually the UIC, but it doesn't have to be,
11366     so if we can, we let fillpasswd update this. */
11367     __pwdcache.pw_uid =  uic.uic$l_uic;
11368     __pwdcache.pw_gid =  uic.uic$v_group;
11369 
11370     fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11371     return &__pwdcache;
11372 
11373 }  /* end of my_getpwuid() */
11374 /*}}}*/
11375 
11376 /*
11377  * Get information for next user.
11378 */
11379 /*{{{struct passwd *my_getpwent()*/
11380 struct passwd *Perl_my_getpwent(pTHX)
11381 {
11382     return (my_getpwuid((unsigned int) -1));
11383 }
11384 /*}}}*/
11385 
11386 /*
11387  * Finish searching rights database for users.
11388 */
11389 /*{{{void my_endpwent()*/
11390 void Perl_my_endpwent(pTHX)
11391 {
11392     if (contxt) {
11393       _ckvmssts(sys$finish_rdb(&contxt));
11394       contxt= 0;
11395     }
11396 }
11397 /*}}}*/
11398 
11399 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11400  * my_utime(), and flex_stat(), all of which operate on UTC unless
11401  * VMSISH_TIMES is true.
11402  */
11403 /* method used to handle UTC conversions:
11404  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
11405  */
11406 static int gmtime_emulation_type;
11407 /* number of secs to add to UTC POSIX-style time to get local time */
11408 static long int utc_offset_secs;
11409 
11410 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11411  * in vmsish.h.  #undef them here so we can call the CRTL routines
11412  * directly.
11413  */
11414 #undef gmtime
11415 #undef localtime
11416 #undef time
11417 
11418 
11419 static time_t toutc_dst(time_t loc) {
11420   struct tm *rsltmp;
11421 
11422   if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11423   loc -= utc_offset_secs;
11424   if (rsltmp->tm_isdst) loc -= 3600;
11425   return loc;
11426 }
11427 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11428        ((gmtime_emulation_type || my_time(NULL)), \
11429        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11430        ((secs) - utc_offset_secs))))
11431 
11432 static time_t toloc_dst(time_t utc) {
11433   struct tm *rsltmp;
11434 
11435   utc += utc_offset_secs;
11436   if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11437   if (rsltmp->tm_isdst) utc += 3600;
11438   return utc;
11439 }
11440 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11441        ((gmtime_emulation_type || my_time(NULL)), \
11442        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11443        ((secs) + utc_offset_secs))))
11444 
11445 /* my_time(), my_localtime(), my_gmtime()
11446  * By default traffic in UTC time values, using CRTL gmtime() or
11447  * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11448  * Note: We need to use these functions even when the CRTL has working
11449  * UTC support, since they also handle C<use vmsish qw(times);>
11450  *
11451  * Contributed by Chuck Lane  <lane@duphy4.physics.drexel.edu>
11452  * Modified by Charles Bailey <bailey@newman.upenn.edu>
11453  */
11454 
11455 /*{{{time_t my_time(time_t *timep)*/
11456 time_t Perl_my_time(pTHX_ time_t *timep)
11457 {
11458   time_t when;
11459   struct tm *tm_p;
11460 
11461   if (gmtime_emulation_type == 0) {
11462     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
11463                               /* results of calls to gmtime() and localtime() */
11464                               /* for same &base */
11465 
11466     gmtime_emulation_type++;
11467     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11468       char off[LNM$C_NAMLENGTH+1];;
11469 
11470       gmtime_emulation_type++;
11471       if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11472         gmtime_emulation_type++;
11473         utc_offset_secs = 0;
11474         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11475       }
11476       else { utc_offset_secs = atol(off); }
11477     }
11478     else { /* We've got a working gmtime() */
11479       struct tm gmt, local;
11480 
11481       gmt = *tm_p;
11482       tm_p = localtime(&base);
11483       local = *tm_p;
11484       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
11485       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11486       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
11487       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
11488     }
11489   }
11490 
11491   when = time(NULL);
11492 # ifdef VMSISH_TIME
11493   if (VMSISH_TIME) when = _toloc(when);
11494 # endif
11495   if (timep != NULL) *timep = when;
11496   return when;
11497 
11498 }  /* end of my_time() */
11499 /*}}}*/
11500 
11501 
11502 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11503 struct tm *
11504 Perl_my_gmtime(pTHX_ const time_t *timep)
11505 {
11506   time_t when;
11507   struct tm *rsltmp;
11508 
11509   if (timep == NULL) {
11510     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11511     return NULL;
11512   }
11513   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
11514 
11515   when = *timep;
11516 # ifdef VMSISH_TIME
11517   if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11518 #  endif
11519   return gmtime(&when);
11520 }  /* end of my_gmtime() */
11521 /*}}}*/
11522 
11523 
11524 /*{{{struct tm *my_localtime(const time_t *timep)*/
11525 struct tm *
11526 Perl_my_localtime(pTHX_ const time_t *timep)
11527 {
11528   time_t when;
11529 
11530   if (timep == NULL) {
11531     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11532     return NULL;
11533   }
11534   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
11535   if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11536 
11537   when = *timep;
11538 # ifdef VMSISH_TIME
11539   if (VMSISH_TIME) when = _toutc(when);
11540 # endif
11541   /* CRTL localtime() wants UTC as input, does tz correction itself */
11542   return localtime(&when);
11543 } /*  end of my_localtime() */
11544 /*}}}*/
11545 
11546 /* Reset definitions for later calls */
11547 #define gmtime(t)    my_gmtime(t)
11548 #define localtime(t) my_localtime(t)
11549 #define time(t)      my_time(t)
11550 
11551 
11552 /* my_utime - update modification/access time of a file
11553  *
11554  * VMS 7.3 and later implementation
11555  * Only the UTC translation is home-grown. The rest is handled by the
11556  * CRTL utime(), which will take into account the relevant feature
11557  * logicals and ODS-5 volume characteristics for true access times.
11558  *
11559  * pre VMS 7.3 implementation:
11560  * The calling sequence is identical to POSIX utime(), but under
11561  * VMS with ODS-2, only the modification time is changed; ODS-2 does
11562  * not maintain access times.  Restrictions differ from the POSIX
11563  * definition in that the time can be changed as long as the
11564  * caller has permission to execute the necessary IO$_MODIFY $QIO;
11565  * no separate checks are made to insure that the caller is the
11566  * owner of the file or has special privs enabled.
11567  * Code here is based on Joe Meadows' FILE utility.
11568  *
11569  */
11570 
11571 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11572  *              to VMS epoch  (01-JAN-1858 00:00:00.00)
11573  * in 100 ns intervals.
11574  */
11575 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11576 
11577 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11578 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11579 {
11580 #if __CRTL_VER >= 70300000
11581   struct utimbuf utc_utimes, *utc_utimesp;
11582 
11583   if (utimes != NULL) {
11584     utc_utimes.actime = utimes->actime;
11585     utc_utimes.modtime = utimes->modtime;
11586 # ifdef VMSISH_TIME
11587     /* If input was local; convert to UTC for sys svc */
11588     if (VMSISH_TIME) {
11589       utc_utimes.actime = _toutc(utimes->actime);
11590       utc_utimes.modtime = _toutc(utimes->modtime);
11591     }
11592 # endif
11593     utc_utimesp = &utc_utimes;
11594   }
11595   else {
11596     utc_utimesp = NULL;
11597   }
11598 
11599   return utime(file, utc_utimesp);
11600 
11601 #else /* __CRTL_VER < 70300000 */
11602 
11603   int i;
11604   int sts;
11605   long int bintime[2], len = 2, lowbit, unixtime,
11606            secscale = 10000000; /* seconds --> 100 ns intervals */
11607   unsigned long int chan, iosb[2], retsts;
11608   char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11609   struct FAB myfab = cc$rms_fab;
11610   struct NAM mynam = cc$rms_nam;
11611 #if defined (__DECC) && defined (__VAX)
11612   /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11613    * at least through VMS V6.1, which causes a type-conversion warning.
11614    */
11615 #  pragma message save
11616 #  pragma message disable cvtdiftypes
11617 #endif
11618   struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11619   struct fibdef myfib;
11620 #if defined (__DECC) && defined (__VAX)
11621   /* This should be right after the declaration of myatr, but due
11622    * to a bug in VAX DEC C, this takes effect a statement early.
11623    */
11624 #  pragma message restore
11625 #endif
11626   /* cast ok for read only parameter */
11627   struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11628                         devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11629                         fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11630 
11631   if (file == NULL || *file == '\0') {
11632     SETERRNO(ENOENT, LIB$_INVARG);
11633     return -1;
11634   }
11635 
11636   /* Convert to VMS format ensuring that it will fit in 255 characters */
11637   if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11638       SETERRNO(ENOENT, LIB$_INVARG);
11639       return -1;
11640   }
11641   if (utimes != NULL) {
11642     /* Convert Unix time    (seconds since 01-JAN-1970 00:00:00.00)
11643      * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11644      * Since time_t is unsigned long int, and lib$emul takes a signed long int
11645      * as input, we force the sign bit to be clear by shifting unixtime right
11646      * one bit, then multiplying by an extra factor of 2 in lib$emul().
11647      */
11648     lowbit = (utimes->modtime & 1) ? secscale : 0;
11649     unixtime = (long int) utimes->modtime;
11650 #   ifdef VMSISH_TIME
11651     /* If input was UTC; convert to local for sys svc */
11652     if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11653 #   endif
11654     unixtime >>= 1;  secscale <<= 1;
11655     retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11656     if (!(retsts & 1)) {
11657       SETERRNO(EVMSERR, retsts);
11658       return -1;
11659     }
11660     retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11661     if (!(retsts & 1)) {
11662       SETERRNO(EVMSERR, retsts);
11663       return -1;
11664     }
11665   }
11666   else {
11667     /* Just get the current time in VMS format directly */
11668     retsts = sys$gettim(bintime);
11669     if (!(retsts & 1)) {
11670       SETERRNO(EVMSERR, retsts);
11671       return -1;
11672     }
11673   }
11674 
11675   myfab.fab$l_fna = vmsspec;
11676   myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11677   myfab.fab$l_nam = &mynam;
11678   mynam.nam$l_esa = esa;
11679   mynam.nam$b_ess = (unsigned char) sizeof esa;
11680   mynam.nam$l_rsa = rsa;
11681   mynam.nam$b_rss = (unsigned char) sizeof rsa;
11682   if (decc_efs_case_preserve)
11683       mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11684 
11685   /* Look for the file to be affected, letting RMS parse the file
11686    * specification for us as well.  I have set errno using only
11687    * values documented in the utime() man page for VMS POSIX.
11688    */
11689   retsts = sys$parse(&myfab,0,0);
11690   if (!(retsts & 1)) {
11691     set_vaxc_errno(retsts);
11692     if      (retsts == RMS$_PRV) set_errno(EACCES);
11693     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11694     else                         set_errno(EVMSERR);
11695     return -1;
11696   }
11697   retsts = sys$search(&myfab,0,0);
11698   if (!(retsts & 1)) {
11699     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
11700     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
11701     set_vaxc_errno(retsts);
11702     if      (retsts == RMS$_PRV) set_errno(EACCES);
11703     else if (retsts == RMS$_FNF) set_errno(ENOENT);
11704     else                         set_errno(EVMSERR);
11705     return -1;
11706   }
11707 
11708   devdsc.dsc$w_length = mynam.nam$b_dev;
11709   /* cast ok for read only parameter */
11710   devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11711 
11712   retsts = sys$assign(&devdsc,&chan,0,0);
11713   if (!(retsts & 1)) {
11714     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
11715     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
11716     set_vaxc_errno(retsts);
11717     if      (retsts == SS$_IVDEVNAM)   set_errno(ENOTDIR);
11718     else if (retsts == SS$_NOPRIV)     set_errno(EACCES);
11719     else if (retsts == SS$_NOSUCHDEV)  set_errno(ENOTDIR);
11720     else                               set_errno(EVMSERR);
11721     return -1;
11722   }
11723 
11724   fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11725   fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11726 
11727   memset((void *) &myfib, 0, sizeof myfib);
11728 #if defined(__DECC) || defined(__DECCXX)
11729   for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11730   for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11731   /* This prevents the revision time of the file being reset to the current
11732    * time as a result of our IO$_MODIFY $QIO. */
11733   myfib.fib$l_acctl = FIB$M_NORECORD;
11734 #else
11735   for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11736   for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11737   myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11738 #endif
11739   retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11740   mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
11741   myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
11742   _ckvmssts(sys$dassgn(chan));
11743   if (retsts & 1) retsts = iosb[0];
11744   if (!(retsts & 1)) {
11745     set_vaxc_errno(retsts);
11746     if (retsts == SS$_NOPRIV) set_errno(EACCES);
11747     else                      set_errno(EVMSERR);
11748     return -1;
11749   }
11750 
11751   return 0;
11752 
11753 #endif /* #if __CRTL_VER >= 70300000 */
11754 
11755 }  /* end of my_utime() */
11756 /*}}}*/
11757 
11758 /*
11759  * flex_stat, flex_lstat, flex_fstat
11760  * basic stat, but gets it right when asked to stat
11761  * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11762  */
11763 
11764 #ifndef _USE_STD_STAT
11765 /* encode_dev packs a VMS device name string into an integer to allow
11766  * simple comparisons. This can be used, for example, to check whether two
11767  * files are located on the same device, by comparing their encoded device
11768  * names. Even a string comparison would not do, because stat() reuses the
11769  * device name buffer for each call; so without encode_dev, it would be
11770  * necessary to save the buffer and use strcmp (this would mean a number of
11771  * changes to the standard Perl code, to say nothing of what a Perl script
11772  * would have to do.
11773  *
11774  * The device lock id, if it exists, should be unique (unless perhaps compared
11775  * with lock ids transferred from other nodes). We have a lock id if the disk is
11776  * mounted cluster-wide, which is when we tend to get long (host-qualified)
11777  * device names. Thus we use the lock id in preference, and only if that isn't
11778  * available, do we try to pack the device name into an integer (flagged by
11779  * the sign bit (LOCKID_MASK) being set).
11780  *
11781  * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11782  * name and its encoded form, but it seems very unlikely that we will find
11783  * two files on different disks that share the same encoded device names,
11784  * and even more remote that they will share the same file id (if the test
11785  * is to check for the same file).
11786  *
11787  * A better method might be to use sys$device_scan on the first call, and to
11788  * search for the device, returning an index into the cached array.
11789  * The number returned would be more intelligible.
11790  * This is probably not worth it, and anyway would take quite a bit longer
11791  * on the first call.
11792  */
11793 #define LOCKID_MASK 0x80000000     /* Use 0 to force device name use only */
11794 static mydev_t encode_dev (pTHX_ const char *dev)
11795 {
11796   int i;
11797   unsigned long int f;
11798   mydev_t enc;
11799   char c;
11800   const char *q;
11801 
11802   if (!dev || !dev[0]) return 0;
11803 
11804 #if LOCKID_MASK
11805   {
11806     struct dsc$descriptor_s dev_desc;
11807     unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11808 
11809     /* For cluster-mounted disks, the disk lock identifier is unique, so we
11810        can try that first. */
11811     dev_desc.dsc$w_length =  strlen (dev);
11812     dev_desc.dsc$b_dtype =   DSC$K_DTYPE_T;
11813     dev_desc.dsc$b_class =   DSC$K_CLASS_S;
11814     dev_desc.dsc$a_pointer = (char *) dev;  /* Read only parameter */
11815     status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11816     if (!$VMS_STATUS_SUCCESS(status)) {
11817       switch (status) {
11818         case SS$_NOSUCHDEV:
11819           SETERRNO(ENODEV, status);
11820           return 0;
11821         default:
11822           _ckvmssts(status);
11823       }
11824     }
11825     if (lockid) return (lockid & ~LOCKID_MASK);
11826   }
11827 #endif
11828 
11829   /* Otherwise we try to encode the device name */
11830   enc = 0;
11831   f = 1;
11832   i = 0;
11833   for (q = dev + strlen(dev); q--; q >= dev) {
11834     if (*q == ':')
11835 	break;
11836     if (isdigit (*q))
11837       c= (*q) - '0';
11838     else if (isalpha (toupper (*q)))
11839       c= toupper (*q) - 'A' + (char)10;
11840     else
11841       continue; /* Skip '$'s */
11842     i++;
11843     if (i>6) break;     /* 36^7 is too large to fit in an unsigned long int */
11844     if (i>1) f *= 36;
11845     enc += f * (unsigned long int) c;
11846   }
11847   return (enc | LOCKID_MASK);  /* May have already overflowed into bit 31 */
11848 
11849 }  /* end of encode_dev() */
11850 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11851 	device_no = encode_dev(aTHX_ devname)
11852 #else
11853 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11854 	device_no = new_dev_no
11855 #endif
11856 
11857 static int
11858 is_null_device(const char *name)
11859 {
11860   if (decc_bug_devnull != 0) {
11861     if (strncmp("/dev/null", name, 9) == 0)
11862       return 1;
11863   }
11864     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11865        The underscore prefix, controller letter, and unit number are
11866        independently optional; for our purposes, the colon punctuation
11867        is not.  The colon can be trailed by optional directory and/or
11868        filename, but two consecutive colons indicates a nodename rather
11869        than a device.  [pr]  */
11870   if (*name == '_') ++name;
11871   if (tolower(*name++) != 'n') return 0;
11872   if (tolower(*name++) != 'l') return 0;
11873   if (tolower(*name) == 'a') ++name;
11874   if (*name == '0') ++name;
11875   return (*name++ == ':') && (*name != ':');
11876 }
11877 
11878 static int
11879 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11880 
11881 #define flex_stat_int(a,b,c)		Perl_flex_stat_int(aTHX_ a,b,c)
11882 
11883 static I32
11884 Perl_cando_by_name_int
11885    (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11886 {
11887   char usrname[L_cuserid];
11888   struct dsc$descriptor_s usrdsc =
11889          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11890   char *vmsname = NULL, *fileified = NULL;
11891   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11892   unsigned short int retlen, trnlnm_iter_count;
11893   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11894   union prvdef curprv;
11895   struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11896          {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11897          {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11898   struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11899          {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11900          {0,0,0,0}};
11901   struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11902          {0,0,0,0}};
11903   struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11904   Stat_t st;
11905   static int profile_context = -1;
11906 
11907   if (!fname || !*fname) return FALSE;
11908 
11909   /* Make sure we expand logical names, since sys$check_access doesn't */
11910   fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11911   if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11912   if (!strpbrk(fname,"/]>:")) {
11913       my_strlcpy(fileified, fname, VMS_MAXRSS);
11914       trnlnm_iter_count = 0;
11915       while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11916         trnlnm_iter_count++;
11917         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11918       }
11919       fname = fileified;
11920   }
11921 
11922   vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11923   if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11924   if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11925     /* Don't know if already in VMS format, so make sure */
11926     if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11927       PerlMem_free(fileified);
11928       PerlMem_free(vmsname);
11929       return FALSE;
11930     }
11931   }
11932   else {
11933     my_strlcpy(vmsname, fname, VMS_MAXRSS);
11934   }
11935 
11936   /* sys$check_access needs a file spec, not a directory spec.
11937    * flex_stat now will handle a null thread context during startup.
11938    */
11939 
11940   retlen = namdsc.dsc$w_length = strlen(vmsname);
11941   if (vmsname[retlen-1] == ']'
11942       || vmsname[retlen-1] == '>'
11943       || vmsname[retlen-1] == ':'
11944       || (!flex_stat_int(vmsname, &st, 1) &&
11945           S_ISDIR(st.st_mode))) {
11946 
11947       if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11948         PerlMem_free(fileified);
11949         PerlMem_free(vmsname);
11950         return FALSE;
11951       }
11952       fname = fileified;
11953   }
11954   else {
11955       fname = vmsname;
11956   }
11957 
11958   retlen = namdsc.dsc$w_length = strlen(fname);
11959   namdsc.dsc$a_pointer = (char *)fname;
11960 
11961   switch (bit) {
11962     case S_IXUSR: case S_IXGRP: case S_IXOTH:
11963       access = ARM$M_EXECUTE;
11964       flags = CHP$M_READ;
11965       break;
11966     case S_IRUSR: case S_IRGRP: case S_IROTH:
11967       access = ARM$M_READ;
11968       flags = CHP$M_READ | CHP$M_USEREADALL;
11969       break;
11970     case S_IWUSR: case S_IWGRP: case S_IWOTH:
11971       access = ARM$M_WRITE;
11972       flags = CHP$M_READ | CHP$M_WRITE;
11973       break;
11974     case S_IDUSR: case S_IDGRP: case S_IDOTH:
11975       access = ARM$M_DELETE;
11976       flags = CHP$M_READ | CHP$M_WRITE;
11977       break;
11978     default:
11979       if (fileified != NULL)
11980 	PerlMem_free(fileified);
11981       if (vmsname != NULL)
11982 	PerlMem_free(vmsname);
11983       return FALSE;
11984   }
11985 
11986   /* Before we call $check_access, create a user profile with the current
11987    * process privs since otherwise it just uses the default privs from the
11988    * UAF and might give false positives or negatives.  This only works on
11989    * VMS versions v6.0 and later since that's when sys$create_user_profile
11990    * became available.
11991    */
11992 
11993   /* get current process privs and username */
11994   _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11995   _ckvmssts_noperl(iosb[0]);
11996 
11997   /* find out the space required for the profile */
11998   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11999                                     &usrprodsc.dsc$w_length,&profile_context));
12000 
12001   /* allocate space for the profile and get it filled in */
12002   usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
12003   if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12004   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12005                                     &usrprodsc.dsc$w_length,&profile_context));
12006 
12007   /* use the profile to check access to the file; free profile & analyze results */
12008   retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12009   PerlMem_free(usrprodsc.dsc$a_pointer);
12010   if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12011 
12012   if (retsts == SS$_NOPRIV      || retsts == SS$_NOSUCHOBJECT ||
12013       retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12014       retsts == RMS$_DIR        || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12015     set_vaxc_errno(retsts);
12016     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12017     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12018     else set_errno(ENOENT);
12019     if (fileified != NULL)
12020       PerlMem_free(fileified);
12021     if (vmsname != NULL)
12022       PerlMem_free(vmsname);
12023     return FALSE;
12024   }
12025   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12026     if (fileified != NULL)
12027       PerlMem_free(fileified);
12028     if (vmsname != NULL)
12029       PerlMem_free(vmsname);
12030     return TRUE;
12031   }
12032   _ckvmssts_noperl(retsts);
12033 
12034   if (fileified != NULL)
12035     PerlMem_free(fileified);
12036   if (vmsname != NULL)
12037     PerlMem_free(vmsname);
12038   return FALSE;  /* Should never get here */
12039 
12040 }
12041 
12042 /* Do the permissions allow some operation?  Assumes PL_statcache already set. */
12043 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12044  * subset of the applicable information.
12045  */
12046 bool
12047 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12048 {
12049   return cando_by_name_int
12050 	(bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12051 }  /* end of cando() */
12052 /*}}}*/
12053 
12054 
12055 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12056 I32
12057 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12058 {
12059    return cando_by_name_int(bit, effective, fname, 0);
12060 
12061 }  /* end of cando_by_name() */
12062 /*}}}*/
12063 
12064 
12065 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12066 int
12067 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12068 {
12069   dSAVE_ERRNO; /* fstat may set this even on success */
12070   if (!fstat(fd, &statbufp->crtl_stat)) {
12071     char *cptr;
12072     char *vms_filename;
12073     vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12074     if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12075 
12076     /* Save name for cando by name in VMS format */
12077     cptr = getname(fd, vms_filename, 1);
12078 
12079     /* This should not happen, but just in case */
12080     if (cptr == NULL) {
12081 	statbufp->st_devnam[0] = 0;
12082     }
12083     else {
12084 	/* Make sure that the saved name fits in 255 characters */
12085 	cptr = int_rmsexpand_vms
12086 		       (vms_filename,
12087 			statbufp->st_devnam,
12088 			0);
12089 	if (cptr == NULL)
12090 	    statbufp->st_devnam[0] = 0;
12091     }
12092     PerlMem_free(vms_filename);
12093 
12094     VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12095     VMS_DEVICE_ENCODE
12096 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12097 
12098 #   ifdef VMSISH_TIME
12099     if (VMSISH_TIME) {
12100       statbufp->st_mtime = _toloc(statbufp->st_mtime);
12101       statbufp->st_atime = _toloc(statbufp->st_atime);
12102       statbufp->st_ctime = _toloc(statbufp->st_ctime);
12103     }
12104 #   endif
12105     RESTORE_ERRNO;
12106     return 0;
12107   }
12108   return -1;
12109 
12110 }  /* end of flex_fstat() */
12111 /*}}}*/
12112 
12113 static int
12114 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12115 {
12116     char *temp_fspec = NULL;
12117     char *fileified = NULL;
12118     const char *save_spec;
12119     char *ret_spec;
12120     int retval = -1;
12121     char efs_hack = 0;
12122     char already_fileified = 0;
12123     dSAVEDERRNO;
12124 
12125     if (!fspec) {
12126         errno = EINVAL;
12127         return retval;
12128     }
12129 
12130     if (decc_bug_devnull != 0) {
12131       if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12132 	memset(statbufp,0,sizeof *statbufp);
12133         VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12134 	statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12135 	statbufp->st_uid = 0x00010001;
12136 	statbufp->st_gid = 0x0001;
12137 	time((time_t *)&statbufp->st_mtime);
12138 	statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12139 	return 0;
12140       }
12141     }
12142 
12143     SAVE_ERRNO;
12144 
12145 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12146   /*
12147    * If we are in POSIX filespec mode, accept the filename as is.
12148    */
12149   if (decc_posix_compliant_pathnames == 0) {
12150 #endif
12151 
12152     /* Try for a simple stat first.  If fspec contains a filename without
12153      * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12154      * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12155      * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12156      * not sea:[wine.dark]., if the latter exists.  If the intended target is
12157      * the file with null type, specify this by calling flex_stat() with
12158      * a '.' at the end of fspec.
12159      */
12160 
12161     if (lstat_flag == 0)
12162         retval = stat(fspec, &statbufp->crtl_stat);
12163     else
12164         retval = lstat(fspec, &statbufp->crtl_stat);
12165 
12166     if (!retval) {
12167         save_spec = fspec;
12168     }
12169     else {
12170         /* In the odd case where we have write but not read access
12171          * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12172          */
12173         fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12174         if (fileified == NULL)
12175               _ckvmssts_noperl(SS$_INSFMEM);
12176 
12177         ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12178         if (ret_spec != NULL) {
12179             if (lstat_flag == 0)
12180                 retval = stat(fileified, &statbufp->crtl_stat);
12181             else
12182                 retval = lstat(fileified, &statbufp->crtl_stat);
12183             save_spec = fileified;
12184             already_fileified = 1;
12185         }
12186     }
12187 
12188     if (retval && vms_bug_stat_filename) {
12189 
12190         temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12191         if (temp_fspec == NULL)
12192             _ckvmssts_noperl(SS$_INSFMEM);
12193 
12194         /* We should try again as a vmsified file specification. */
12195 
12196         ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12197         if (ret_spec != NULL) {
12198             if (lstat_flag == 0)
12199                 retval = stat(temp_fspec, &statbufp->crtl_stat);
12200             else
12201                 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12202             save_spec = temp_fspec;
12203         }
12204     }
12205 
12206     if (retval) {
12207         /* Last chance - allow multiple dots without EFS CHARSET */
12208         /* The CRTL stat() falls down hard on multi-dot filenames in unix
12209          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12210          * enable it if it isn't already.
12211          */
12212 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12213         if (!decc_efs_charset && (decc_efs_charset_index > 0))
12214             decc$feature_set_value(decc_efs_charset_index, 1, 1);
12215 #endif
12216         if (lstat_flag == 0)
12217 	    retval = stat(fspec, &statbufp->crtl_stat);
12218         else
12219 	    retval = lstat(fspec, &statbufp->crtl_stat);
12220         save_spec = fspec;
12221 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12222         if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12223             decc$feature_set_value(decc_efs_charset_index, 1, 0);
12224             efs_hack = 1;
12225         }
12226 #endif
12227     }
12228 
12229 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12230   } else {
12231     if (lstat_flag == 0)
12232       retval = stat(temp_fspec, &statbufp->crtl_stat);
12233     else
12234       retval = lstat(temp_fspec, &statbufp->crtl_stat);
12235       save_spec = temp_fspec;
12236   }
12237 #endif
12238 
12239 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12240   /* As you were... */
12241   if (!decc_efs_charset)
12242     decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12243 #endif
12244 
12245     if (!retval) {
12246       char *cptr;
12247       int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12248 
12249       /* If this is an lstat, do not follow the link */
12250       if (lstat_flag)
12251 	rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12252 
12253 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12254       /* If we used the efs_hack above, we must also use it here for */
12255       /* perl_cando to work */
12256       if (efs_hack && (decc_efs_charset_index > 0)) {
12257           decc$feature_set_value(decc_efs_charset_index, 1, 1);
12258       }
12259 #endif
12260 
12261       /* If we've got a directory, save a fileified, expanded version of it
12262        * in st_devnam.  If not a directory, just an expanded version.
12263        */
12264       if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12265           fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12266           if (fileified == NULL)
12267               _ckvmssts_noperl(SS$_INSFMEM);
12268 
12269           cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12270           if (cptr != NULL)
12271               save_spec = fileified;
12272       }
12273 
12274       cptr = int_rmsexpand(save_spec,
12275                            statbufp->st_devnam,
12276                            NULL,
12277                            rmsex_flags,
12278                            0,
12279                            0);
12280 
12281 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12282       if (efs_hack && (decc_efs_charset_index > 0)) {
12283           decc$feature_set_value(decc_efs_charset, 1, 0);
12284       }
12285 #endif
12286 
12287       /* Fix me: If this is NULL then stat found a file, and we could */
12288       /* not convert the specification to VMS - Should never happen */
12289       if (cptr == NULL)
12290 	statbufp->st_devnam[0] = 0;
12291 
12292       VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12293       VMS_DEVICE_ENCODE
12294 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12295 #     ifdef VMSISH_TIME
12296       if (VMSISH_TIME) {
12297         statbufp->st_mtime = _toloc(statbufp->st_mtime);
12298         statbufp->st_atime = _toloc(statbufp->st_atime);
12299         statbufp->st_ctime = _toloc(statbufp->st_ctime);
12300       }
12301 #     endif
12302     }
12303     /* If we were successful, leave errno where we found it */
12304     if (retval == 0) RESTORE_ERRNO;
12305     if (temp_fspec)
12306         PerlMem_free(temp_fspec);
12307     if (fileified)
12308         PerlMem_free(fileified);
12309     return retval;
12310 
12311 }  /* end of flex_stat_int() */
12312 
12313 
12314 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12315 int
12316 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12317 {
12318    return flex_stat_int(fspec, statbufp, 0);
12319 }
12320 /*}}}*/
12321 
12322 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12323 int
12324 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12325 {
12326    return flex_stat_int(fspec, statbufp, 1);
12327 }
12328 /*}}}*/
12329 
12330 
12331 /*{{{char *my_getlogin()*/
12332 /* VMS cuserid == Unix getlogin, except calling sequence */
12333 char *
12334 my_getlogin(void)
12335 {
12336     static char user[L_cuserid];
12337     return cuserid(user);
12338 }
12339 /*}}}*/
12340 
12341 
12342 /*  rmscopy - copy a file using VMS RMS routines
12343  *
12344  *  Copies contents and attributes of spec_in to spec_out, except owner
12345  *  and protection information.  Name and type of spec_in are used as
12346  *  defaults for spec_out.  The third parameter specifies whether rmscopy()
12347  *  should try to propagate timestamps from the input file to the output file.
12348  *  If it is less than 0, no timestamps are preserved.  If it is 0, then
12349  *  rmscopy() will behave similarly to the DCL COPY command: timestamps are
12350  *  propagated to the output file at creation iff the output file specification
12351  *  did not contain an explicit name or type, and the revision date is always
12352  *  updated at the end of the copy operation.  If it is greater than 0, then
12353  *  it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12354  *  other than the revision date should be propagated, and bit 1 indicates
12355  *  that the revision date should be propagated.
12356  *
12357  *  Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12358  *
12359  *  Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12360  *  Incorporates, with permission, some code from EZCOPY by Tim Adye
12361  *  <T.J.Adye@rl.ac.uk>.  Permission is given to distribute this code
12362  * as part of the Perl standard distribution under the terms of the
12363  * GNU General Public License or the Perl Artistic License.  Copies
12364  * of each may be found in the Perl standard distribution.
12365  */ /* FIXME */
12366 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12367 int
12368 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12369 {
12370     char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12371          *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12372     unsigned long int sts;
12373     int dna_len;
12374     struct FAB fab_in, fab_out;
12375     struct RAB rab_in, rab_out;
12376     rms_setup_nam(nam);
12377     rms_setup_nam(nam_out);
12378     struct XABDAT xabdat;
12379     struct XABFHC xabfhc;
12380     struct XABRDT xabrdt;
12381     struct XABSUM xabsum;
12382 
12383     vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12384     if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12385     vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12386     if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12387     if (!spec_in  || !*spec_in  || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12388         !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12389       PerlMem_free(vmsin);
12390       PerlMem_free(vmsout);
12391       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12392       return 0;
12393     }
12394 
12395     esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12396     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12397     esal = NULL;
12398 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12399     esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12400     if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12401 #endif
12402     fab_in = cc$rms_fab;
12403     rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12404     fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12405     fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12406     fab_in.fab$l_fop = FAB$M_SQO;
12407     rms_bind_fab_nam(fab_in, nam);
12408     fab_in.fab$l_xab = (void *) &xabdat;
12409 
12410     rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12411     if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12412     rsal = NULL;
12413 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12414     rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12415     if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12416 #endif
12417     rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12418     rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12419     rms_nam_esl(nam) = 0;
12420     rms_nam_rsl(nam) = 0;
12421     rms_nam_esll(nam) = 0;
12422     rms_nam_rsll(nam) = 0;
12423 #ifdef NAM$M_NO_SHORT_UPCASE
12424     if (decc_efs_case_preserve)
12425 	rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12426 #endif
12427 
12428     xabdat = cc$rms_xabdat;        /* To get creation date */
12429     xabdat.xab$l_nxt = (void *) &xabfhc;
12430 
12431     xabfhc = cc$rms_xabfhc;        /* To get record length */
12432     xabfhc.xab$l_nxt = (void *) &xabsum;
12433 
12434     xabsum = cc$rms_xabsum;        /* To get key and area information */
12435 
12436     if (!((sts = sys$open(&fab_in)) & 1)) {
12437       PerlMem_free(vmsin);
12438       PerlMem_free(vmsout);
12439       PerlMem_free(esa);
12440       if (esal != NULL)
12441 	PerlMem_free(esal);
12442       PerlMem_free(rsa);
12443       if (rsal != NULL)
12444 	PerlMem_free(rsal);
12445       set_vaxc_errno(sts);
12446       switch (sts) {
12447         case RMS$_FNF: case RMS$_DNF:
12448           set_errno(ENOENT); break;
12449         case RMS$_DIR:
12450           set_errno(ENOTDIR); break;
12451         case RMS$_DEV:
12452           set_errno(ENODEV); break;
12453         case RMS$_SYN:
12454           set_errno(EINVAL); break;
12455         case RMS$_PRV:
12456           set_errno(EACCES); break;
12457         default:
12458           set_errno(EVMSERR);
12459       }
12460       return 0;
12461     }
12462 
12463     nam_out = nam;
12464     fab_out = fab_in;
12465     fab_out.fab$w_ifi = 0;
12466     fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12467     fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12468     fab_out.fab$l_fop = FAB$M_SQO;
12469     rms_bind_fab_nam(fab_out, nam_out);
12470     rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12471     dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12472     rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12473     esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12474     if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12475     rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12476     if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12477     esal_out = NULL;
12478     rsal_out = NULL;
12479 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12480     esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12481     if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12482     rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12483     if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12484 #endif
12485     rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12486     rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12487 
12488     if (preserve_dates == 0) {  /* Act like DCL COPY */
12489       rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12490       fab_out.fab$l_xab = NULL;  /* Don't disturb data from input file */
12491       if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12492 	PerlMem_free(vmsin);
12493 	PerlMem_free(vmsout);
12494 	PerlMem_free(esa);
12495 	if (esal != NULL)
12496 	    PerlMem_free(esal);
12497 	PerlMem_free(rsa);
12498 	if (rsal != NULL)
12499 	    PerlMem_free(rsal);
12500 	PerlMem_free(esa_out);
12501 	if (esal_out != NULL)
12502 	    PerlMem_free(esal_out);
12503 	PerlMem_free(rsa_out);
12504 	if (rsal_out != NULL)
12505 	    PerlMem_free(rsal_out);
12506         set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12507         set_vaxc_errno(sts);
12508         return 0;
12509       }
12510       fab_out.fab$l_xab = (void *) &xabdat;
12511       if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12512 	preserve_dates = 1;
12513     }
12514     if (preserve_dates < 0)   /* Clear all bits; we'll use it as a */
12515       preserve_dates =0;      /* bitmask from this point forward   */
12516 
12517     if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12518     if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12519       PerlMem_free(vmsin);
12520       PerlMem_free(vmsout);
12521       PerlMem_free(esa);
12522       if (esal != NULL)
12523 	  PerlMem_free(esal);
12524       PerlMem_free(rsa);
12525       if (rsal != NULL)
12526 	  PerlMem_free(rsal);
12527       PerlMem_free(esa_out);
12528       if (esal_out != NULL)
12529 	  PerlMem_free(esal_out);
12530       PerlMem_free(rsa_out);
12531       if (rsal_out != NULL)
12532 	  PerlMem_free(rsal_out);
12533       set_vaxc_errno(sts);
12534       switch (sts) {
12535         case RMS$_DNF:
12536           set_errno(ENOENT); break;
12537         case RMS$_DIR:
12538           set_errno(ENOTDIR); break;
12539         case RMS$_DEV:
12540           set_errno(ENODEV); break;
12541         case RMS$_SYN:
12542           set_errno(EINVAL); break;
12543         case RMS$_PRV:
12544           set_errno(EACCES); break;
12545         default:
12546           set_errno(EVMSERR);
12547       }
12548       return 0;
12549     }
12550     fab_out.fab$l_fop |= FAB$M_DLT;  /* in case we have to bail out */
12551     if (preserve_dates & 2) {
12552       /* sys$close() will process xabrdt, not xabdat */
12553       xabrdt = cc$rms_xabrdt;
12554 #ifndef __GNUC__
12555       xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12556 #else
12557       /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12558        * is unsigned long[2], while DECC & VAXC use a struct */
12559       memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12560 #endif
12561       fab_out.fab$l_xab = (void *) &xabrdt;
12562     }
12563 
12564     ubf = (char *)PerlMem_malloc(32256);
12565     if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12566     rab_in = cc$rms_rab;
12567     rab_in.rab$l_fab = &fab_in;
12568     rab_in.rab$l_rop = RAB$M_BIO;
12569     rab_in.rab$l_ubf = ubf;
12570     rab_in.rab$w_usz = 32256;
12571     if (!((sts = sys$connect(&rab_in)) & 1)) {
12572       sys$close(&fab_in); sys$close(&fab_out);
12573       PerlMem_free(vmsin);
12574       PerlMem_free(vmsout);
12575       PerlMem_free(ubf);
12576       PerlMem_free(esa);
12577       if (esal != NULL)
12578 	  PerlMem_free(esal);
12579       PerlMem_free(rsa);
12580       if (rsal != NULL)
12581 	  PerlMem_free(rsal);
12582       PerlMem_free(esa_out);
12583       if (esal_out != NULL)
12584 	  PerlMem_free(esal_out);
12585       PerlMem_free(rsa_out);
12586       if (rsal_out != NULL)
12587 	  PerlMem_free(rsal_out);
12588       set_errno(EVMSERR); set_vaxc_errno(sts);
12589       return 0;
12590     }
12591 
12592     rab_out = cc$rms_rab;
12593     rab_out.rab$l_fab = &fab_out;
12594     rab_out.rab$l_rbf = ubf;
12595     if (!((sts = sys$connect(&rab_out)) & 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     while ((sts = sys$read(&rab_in))) {  /* always true  */
12617       if (sts == RMS$_EOF) break;
12618       rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12619       if (!(sts & 1) || !((sts = sys$write(&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 
12641 
12642     fab_out.fab$l_fop &= ~FAB$M_DLT;  /* We got this far; keep the output */
12643     sys$close(&fab_in);  sys$close(&fab_out);
12644     sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12645 
12646     PerlMem_free(vmsin);
12647     PerlMem_free(vmsout);
12648     PerlMem_free(ubf);
12649     PerlMem_free(esa);
12650     if (esal != NULL)
12651 	PerlMem_free(esal);
12652     PerlMem_free(rsa);
12653     if (rsal != NULL)
12654 	PerlMem_free(rsal);
12655     PerlMem_free(esa_out);
12656     if (esal_out != NULL)
12657 	PerlMem_free(esal_out);
12658     PerlMem_free(rsa_out);
12659     if (rsal_out != NULL)
12660 	PerlMem_free(rsal_out);
12661 
12662     if (!(sts & 1)) {
12663       set_errno(EVMSERR); set_vaxc_errno(sts);
12664       return 0;
12665     }
12666 
12667     return 1;
12668 
12669 }  /* end of rmscopy() */
12670 /*}}}*/
12671 
12672 
12673 /***  The following glue provides 'hooks' to make some of the routines
12674  * from this file available from Perl.  These routines are sufficiently
12675  * basic, and are required sufficiently early in the build process,
12676  * that's it's nice to have them available to miniperl as well as the
12677  * full Perl, so they're set up here instead of in an extension.  The
12678  * Perl code which handles importation of these names into a given
12679  * package lives in [.VMS]Filespec.pm in @INC.
12680  */
12681 
12682 void
12683 rmsexpand_fromperl(pTHX_ CV *cv)
12684 {
12685   dXSARGS;
12686   char *fspec, *defspec = NULL, *rslt;
12687   STRLEN n_a;
12688   int fs_utf8, dfs_utf8;
12689 
12690   fs_utf8 = 0;
12691   dfs_utf8 = 0;
12692   if (!items || items > 2)
12693     Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12694   fspec = SvPV(ST(0),n_a);
12695   fs_utf8 = SvUTF8(ST(0));
12696   if (!fspec || !*fspec) XSRETURN_UNDEF;
12697   if (items == 2) {
12698     defspec = SvPV(ST(1),n_a);
12699     dfs_utf8 = SvUTF8(ST(1));
12700   }
12701   rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12702   ST(0) = sv_newmortal();
12703   if (rslt != NULL) {
12704     sv_usepvn(ST(0),rslt,strlen(rslt));
12705     if (fs_utf8) {
12706 	SvUTF8_on(ST(0));
12707     }
12708   }
12709   XSRETURN(1);
12710 }
12711 
12712 void
12713 vmsify_fromperl(pTHX_ CV *cv)
12714 {
12715   dXSARGS;
12716   char *vmsified;
12717   STRLEN n_a;
12718   int utf8_fl;
12719 
12720   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12721   utf8_fl = SvUTF8(ST(0));
12722   vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12723   ST(0) = sv_newmortal();
12724   if (vmsified != NULL) {
12725     sv_usepvn(ST(0),vmsified,strlen(vmsified));
12726     if (utf8_fl) {
12727 	SvUTF8_on(ST(0));
12728     }
12729   }
12730   XSRETURN(1);
12731 }
12732 
12733 void
12734 unixify_fromperl(pTHX_ CV *cv)
12735 {
12736   dXSARGS;
12737   char *unixified;
12738   STRLEN n_a;
12739   int utf8_fl;
12740 
12741   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12742   utf8_fl = SvUTF8(ST(0));
12743   unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12744   ST(0) = sv_newmortal();
12745   if (unixified != NULL) {
12746     sv_usepvn(ST(0),unixified,strlen(unixified));
12747     if (utf8_fl) {
12748 	SvUTF8_on(ST(0));
12749     }
12750   }
12751   XSRETURN(1);
12752 }
12753 
12754 void
12755 fileify_fromperl(pTHX_ CV *cv)
12756 {
12757   dXSARGS;
12758   char *fileified;
12759   STRLEN n_a;
12760   int utf8_fl;
12761 
12762   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12763   utf8_fl = SvUTF8(ST(0));
12764   fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12765   ST(0) = sv_newmortal();
12766   if (fileified != NULL) {
12767     sv_usepvn(ST(0),fileified,strlen(fileified));
12768     if (utf8_fl) {
12769 	SvUTF8_on(ST(0));
12770     }
12771   }
12772   XSRETURN(1);
12773 }
12774 
12775 void
12776 pathify_fromperl(pTHX_ CV *cv)
12777 {
12778   dXSARGS;
12779   char *pathified;
12780   STRLEN n_a;
12781   int utf8_fl;
12782 
12783   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12784   utf8_fl = SvUTF8(ST(0));
12785   pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12786   ST(0) = sv_newmortal();
12787   if (pathified != NULL) {
12788     sv_usepvn(ST(0),pathified,strlen(pathified));
12789     if (utf8_fl) {
12790 	SvUTF8_on(ST(0));
12791     }
12792   }
12793   XSRETURN(1);
12794 }
12795 
12796 void
12797 vmspath_fromperl(pTHX_ CV *cv)
12798 {
12799   dXSARGS;
12800   char *vmspath;
12801   STRLEN n_a;
12802   int utf8_fl;
12803 
12804   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12805   utf8_fl = SvUTF8(ST(0));
12806   vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12807   ST(0) = sv_newmortal();
12808   if (vmspath != NULL) {
12809     sv_usepvn(ST(0),vmspath,strlen(vmspath));
12810     if (utf8_fl) {
12811 	SvUTF8_on(ST(0));
12812     }
12813   }
12814   XSRETURN(1);
12815 }
12816 
12817 void
12818 unixpath_fromperl(pTHX_ CV *cv)
12819 {
12820   dXSARGS;
12821   char *unixpath;
12822   STRLEN n_a;
12823   int utf8_fl;
12824 
12825   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12826   utf8_fl = SvUTF8(ST(0));
12827   unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12828   ST(0) = sv_newmortal();
12829   if (unixpath != NULL) {
12830     sv_usepvn(ST(0),unixpath,strlen(unixpath));
12831     if (utf8_fl) {
12832 	SvUTF8_on(ST(0));
12833     }
12834   }
12835   XSRETURN(1);
12836 }
12837 
12838 void
12839 candelete_fromperl(pTHX_ CV *cv)
12840 {
12841   dXSARGS;
12842   char *fspec, *fsp;
12843   SV *mysv;
12844   IO *io;
12845   STRLEN n_a;
12846 
12847   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12848 
12849   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12850   Newx(fspec, VMS_MAXRSS, char);
12851   if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12852   if (isGV_with_GP(mysv)) {
12853     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12854       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12855       ST(0) = &PL_sv_no;
12856       Safefree(fspec);
12857       XSRETURN(1);
12858     }
12859     fsp = fspec;
12860   }
12861   else {
12862     if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12863       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12864       ST(0) = &PL_sv_no;
12865       Safefree(fspec);
12866       XSRETURN(1);
12867     }
12868   }
12869 
12870   ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12871   Safefree(fspec);
12872   XSRETURN(1);
12873 }
12874 
12875 void
12876 rmscopy_fromperl(pTHX_ CV *cv)
12877 {
12878   dXSARGS;
12879   char *inspec, *outspec, *inp, *outp;
12880   int date_flag;
12881   SV *mysv;
12882   IO *io;
12883   STRLEN n_a;
12884 
12885   if (items < 2 || items > 3)
12886     Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12887 
12888   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12889   Newx(inspec, VMS_MAXRSS, char);
12890   if (isGV_with_GP(mysv)) {
12891     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12892       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12893       ST(0) = sv_2mortal(newSViv(0));
12894       Safefree(inspec);
12895       XSRETURN(1);
12896     }
12897     inp = inspec;
12898   }
12899   else {
12900     if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12901       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12902       ST(0) = sv_2mortal(newSViv(0));
12903       Safefree(inspec);
12904       XSRETURN(1);
12905     }
12906   }
12907   mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12908   Newx(outspec, VMS_MAXRSS, char);
12909   if (isGV_with_GP(mysv)) {
12910     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12911       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12912       ST(0) = sv_2mortal(newSViv(0));
12913       Safefree(inspec);
12914       Safefree(outspec);
12915       XSRETURN(1);
12916     }
12917     outp = outspec;
12918   }
12919   else {
12920     if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12921       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12922       ST(0) = sv_2mortal(newSViv(0));
12923       Safefree(inspec);
12924       Safefree(outspec);
12925       XSRETURN(1);
12926     }
12927   }
12928   date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12929 
12930   ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12931   Safefree(inspec);
12932   Safefree(outspec);
12933   XSRETURN(1);
12934 }
12935 
12936 /* The mod2fname is limited to shorter filenames by design, so it should
12937  * not be modified to support longer EFS pathnames
12938  */
12939 void
12940 mod2fname(pTHX_ CV *cv)
12941 {
12942   dXSARGS;
12943   char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12944        workbuff[NAM$C_MAXRSS*1 + 1];
12945   int counter, num_entries;
12946   /* ODS-5 ups this, but we want to be consistent, so... */
12947   int max_name_len = 39;
12948   AV *in_array = (AV *)SvRV(ST(0));
12949 
12950   num_entries = av_len(in_array);
12951 
12952   /* All the names start with PL_. */
12953   strcpy(ultimate_name, "PL_");
12954 
12955   /* Clean up our working buffer */
12956   Zero(work_name, sizeof(work_name), char);
12957 
12958   /* Run through the entries and build up a working name */
12959   for(counter = 0; counter <= num_entries; counter++) {
12960     /* If it's not the first name then tack on a __ */
12961     if (counter) {
12962       my_strlcat(work_name, "__", sizeof(work_name));
12963     }
12964     my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12965   }
12966 
12967   /* Check to see if we actually have to bother...*/
12968   if (strlen(work_name) + 3 <= max_name_len) {
12969     my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12970   } else {
12971     /* It's too darned big, so we need to go strip. We use the same */
12972     /* algorithm as xsubpp does. First, strip out doubled __ */
12973     char *source, *dest, last;
12974     dest = workbuff;
12975     last = 0;
12976     for (source = work_name; *source; source++) {
12977       if (last == *source && last == '_') {
12978 	continue;
12979       }
12980       *dest++ = *source;
12981       last = *source;
12982     }
12983     /* Go put it back */
12984     my_strlcpy(work_name, workbuff, sizeof(work_name));
12985     /* Is it still too big? */
12986     if (strlen(work_name) + 3 > max_name_len) {
12987       /* Strip duplicate letters */
12988       last = 0;
12989       dest = workbuff;
12990       for (source = work_name; *source; source++) {
12991 	if (last == toupper(*source)) {
12992 	continue;
12993 	}
12994 	*dest++ = *source;
12995 	last = toupper(*source);
12996       }
12997       my_strlcpy(work_name, workbuff, sizeof(work_name));
12998     }
12999 
13000     /* Is it *still* too big? */
13001     if (strlen(work_name) + 3 > max_name_len) {
13002       /* Too bad, we truncate */
13003       work_name[max_name_len - 2] = 0;
13004     }
13005     my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
13006   }
13007 
13008   /* Okay, return it */
13009   ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13010   XSRETURN(1);
13011 }
13012 
13013 void
13014 hushexit_fromperl(pTHX_ CV *cv)
13015 {
13016     dXSARGS;
13017 
13018     if (items > 0) {
13019         VMSISH_HUSHED = SvTRUE(ST(0));
13020     }
13021     ST(0) = boolSV(VMSISH_HUSHED);
13022     XSRETURN(1);
13023 }
13024 
13025 
13026 PerlIO *
13027 Perl_vms_start_glob
13028    (pTHX_ SV *tmpglob,
13029     IO *io)
13030 {
13031     PerlIO *fp;
13032     struct vs_str_st *rslt;
13033     char *vmsspec;
13034     char *rstr;
13035     char *begin, *cp;
13036     $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13037     PerlIO *tmpfp;
13038     STRLEN i;
13039     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13040     struct dsc$descriptor_vs rsdsc;
13041     unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13042     unsigned long hasver = 0, isunix = 0;
13043     unsigned long int lff_flags = 0;
13044     int rms_sts;
13045     int vms_old_glob = 1;
13046 
13047     if (!SvOK(tmpglob)) {
13048         SETERRNO(ENOENT,RMS$_FNF);
13049         return NULL;
13050     }
13051 
13052     vms_old_glob = !decc_filename_unix_report;
13053 
13054 #ifdef VMS_LONGNAME_SUPPORT
13055     lff_flags = LIB$M_FIL_LONG_NAMES;
13056 #endif
13057     /* The Newx macro will not allow me to assign a smaller array
13058      * to the rslt pointer, so we will assign it to the begin char pointer
13059      * and then copy the value into the rslt pointer.
13060      */
13061     Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13062     rslt = (struct vs_str_st *)begin;
13063     rslt->length = 0;
13064     rstr = &rslt->str[0];
13065     rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13066     rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13067     rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13068     rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13069 
13070     Newx(vmsspec, VMS_MAXRSS, char);
13071 
13072 	/* We could find out if there's an explicit dev/dir or version
13073 	   by peeking into lib$find_file's internal context at
13074 	   ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13075 	   but that's unsupported, so I don't want to do it now and
13076 	   have it bite someone in the future. */
13077 	/* Fix-me: vms_split_path() is the only way to do this, the
13078 	   existing method will fail with many legal EFS or UNIX specifications
13079 	 */
13080 
13081     cp = SvPV(tmpglob,i);
13082 
13083     for (; i; i--) {
13084 	if (cp[i] == ';') hasver = 1;
13085 	if (cp[i] == '.') {
13086 	    if (sts) hasver = 1;
13087 	    else sts = 1;
13088 	}
13089 	if (cp[i] == '/') {
13090 	    hasdir = isunix = 1;
13091 	    break;
13092 	}
13093 	if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13094 	    hasdir = 1;
13095 	    break;
13096 	}
13097     }
13098 
13099     /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13100     if ((hasdir == 0) && decc_filename_unix_report) {
13101         isunix = 1;
13102     }
13103 
13104     if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13105 	char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13106 	int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13107 	int wildstar = 0;
13108 	int wildquery = 0;
13109 	int found = 0;
13110 	Stat_t st;
13111 	int stat_sts;
13112 	stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13113 	if (!stat_sts && S_ISDIR(st.st_mode)) {
13114             char * vms_dir;
13115             const char * fname;
13116             STRLEN fname_len;
13117 
13118             /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13119             /* path delimiter of ':>]', if so, then the old behavior has */
13120             /* obviously been specifically requested */
13121 
13122             fname = SvPVX_const(tmpglob);
13123             fname_len = strlen(fname);
13124             vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13125             if (vms_old_glob || (vms_dir != NULL)) {
13126                 wilddsc.dsc$a_pointer = tovmspath_utf8(
13127                                             SvPVX(tmpglob),vmsspec,NULL);
13128                 ok = (wilddsc.dsc$a_pointer != NULL);
13129                 /* maybe passed 'foo' rather than '[.foo]', thus not
13130                    detected above */
13131                 hasdir = 1;
13132             } else {
13133                 /* Operate just on the directory, the special stat/fstat for */
13134                 /* leaves the fileified  specification in the st_devnam */
13135                 /* member. */
13136                 wilddsc.dsc$a_pointer = st.st_devnam;
13137                 ok = 1;
13138             }
13139 	}
13140 	else {
13141 	    wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13142 	    ok = (wilddsc.dsc$a_pointer != NULL);
13143 	}
13144 	if (ok)
13145 	    wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13146 
13147 	/* If not extended character set, replace ? with % */
13148 	/* With extended character set, ? is a wildcard single character */
13149 	for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13150 	    if (*cp == '?') {
13151                 wildquery = 1;
13152                 if (!decc_efs_charset)
13153                     *cp = '%';
13154             } else if (*cp == '%') {
13155                 wildquery = 1;
13156             } else if (*cp == '*') {
13157                 wildstar = 1;
13158             }
13159 	}
13160 
13161         if (ok) {
13162             wv_sts = vms_split_path(
13163                 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13164                 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13165                 &wvs_spec, &wvs_len);
13166         } else {
13167             wn_spec = NULL;
13168             wn_len = 0;
13169             we_spec = NULL;
13170             we_len = 0;
13171         }
13172 
13173 	sts = SS$_NORMAL;
13174 	while (ok && $VMS_STATUS_SUCCESS(sts)) {
13175 	 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13176 	 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13177          int valid_find;
13178 
13179             valid_find = 0;
13180 	    sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13181 				&dfltdsc,NULL,&rms_sts,&lff_flags);
13182 	    if (!$VMS_STATUS_SUCCESS(sts))
13183 		break;
13184 
13185 	    /* with varying string, 1st word of buffer contains result length */
13186 	    rstr[rslt->length] = '\0';
13187 
13188 	     /* Find where all the components are */
13189 	     v_sts = vms_split_path
13190 		       (rstr,
13191 			&v_spec,
13192 			&v_len,
13193 			&r_spec,
13194 			&r_len,
13195 			&d_spec,
13196 			&d_len,
13197 			&n_spec,
13198 			&n_len,
13199 			&e_spec,
13200 			&e_len,
13201 			&vs_spec,
13202 			&vs_len);
13203 
13204 	    /* If no version on input, truncate the version on output */
13205 	    if (!hasver && (vs_len > 0)) {
13206 		*vs_spec = '\0';
13207 		vs_len = 0;
13208             }
13209 
13210             if (isunix) {
13211 
13212                 /* In Unix report mode, remove the ".dir;1" from the name */
13213                 /* if it is a real directory */
13214                 if (decc_filename_unix_report && decc_efs_charset) {
13215                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13216                         Stat_t statbuf;
13217                         int ret_sts;
13218 
13219                         ret_sts = flex_lstat(rstr, &statbuf);
13220                         if ((ret_sts == 0) &&
13221                             S_ISDIR(statbuf.st_mode)) {
13222                             e_len = 0;
13223                             e_spec[0] = 0;
13224                         }
13225                     }
13226                 }
13227 
13228 		/* No version & a null extension on UNIX handling */
13229 		if ((e_len == 1) && decc_readdir_dropdotnotype) {
13230 		    e_len = 0;
13231 		    *e_spec = '\0';
13232 		}
13233 	    }
13234 
13235 	    if (!decc_efs_case_preserve) {
13236 	        for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13237 	    }
13238 
13239             /* Find File treats a Null extension as return all extensions */
13240             /* This is contrary to Perl expectations */
13241 
13242             if (wildstar || wildquery || vms_old_glob) {
13243                 /* really need to see if the returned file name matched */
13244                 /* but for now will assume that it matches */
13245                 valid_find = 1;
13246             } else {
13247                 /* Exact Match requested */
13248                 /* How are directories handled? - like a file */
13249                 if ((e_len == we_len) && (n_len == wn_len)) {
13250                     int t1;
13251                     t1 = e_len;
13252                     if (t1 > 0)
13253                         t1 = strncmp(e_spec, we_spec, e_len);
13254                     if (t1 == 0) {
13255                        t1 = n_len;
13256                        if (t1 > 0)
13257                            t1 = strncmp(n_spec, we_spec, n_len);
13258                        if (t1 == 0)
13259                            valid_find = 1;
13260                     }
13261                 }
13262             }
13263 
13264             if (valid_find) {
13265 	        found++;
13266 
13267 	        if (hasdir) {
13268 		    if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13269 		    begin = rstr;
13270 	        }
13271 	        else {
13272 		    /* Start with the name */
13273 		    begin = n_spec;
13274 	        }
13275 	        strcat(begin,"\n");
13276 	        ok = (PerlIO_puts(tmpfp,begin) != EOF);
13277             }
13278 	}
13279 	if (cxt) (void)lib$find_file_end(&cxt);
13280 
13281 	if (!found) {
13282 	    /* Be POSIXish: return the input pattern when no matches */
13283 	    my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13284 	    strcat(rstr,"\n");
13285 	    ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13286 	}
13287 
13288 	if (ok && sts != RMS$_NMF &&
13289 	    sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13290 	if (!ok) {
13291 	    if (!(sts & 1)) {
13292 		SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13293 	    }
13294 	    PerlIO_close(tmpfp);
13295 	    fp = NULL;
13296 	}
13297 	else {
13298 	    PerlIO_rewind(tmpfp);
13299 	    IoTYPE(io) = IoTYPE_RDONLY;
13300 	    IoIFP(io) = fp = tmpfp;
13301 	    IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
13302 	}
13303     }
13304     Safefree(vmsspec);
13305     Safefree(rslt);
13306     return fp;
13307 }
13308 
13309 
13310 static char *
13311 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13312 		   int *utf8_fl);
13313 
13314 void
13315 unixrealpath_fromperl(pTHX_ CV *cv)
13316 {
13317     dXSARGS;
13318     char *fspec, *rslt_spec, *rslt;
13319     STRLEN n_a;
13320 
13321     if (!items || items != 1)
13322 	Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13323 
13324     fspec = SvPV(ST(0),n_a);
13325     if (!fspec || !*fspec) XSRETURN_UNDEF;
13326 
13327     Newx(rslt_spec, VMS_MAXRSS + 1, char);
13328     rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13329 
13330     ST(0) = sv_newmortal();
13331     if (rslt != NULL)
13332 	sv_usepvn(ST(0),rslt,strlen(rslt));
13333     else
13334 	Safefree(rslt_spec);
13335 	XSRETURN(1);
13336 }
13337 
13338 static char *
13339 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13340 		   int *utf8_fl);
13341 
13342 void
13343 vmsrealpath_fromperl(pTHX_ CV *cv)
13344 {
13345     dXSARGS;
13346     char *fspec, *rslt_spec, *rslt;
13347     STRLEN n_a;
13348 
13349     if (!items || items != 1)
13350 	Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13351 
13352     fspec = SvPV(ST(0),n_a);
13353     if (!fspec || !*fspec) XSRETURN_UNDEF;
13354 
13355     Newx(rslt_spec, VMS_MAXRSS + 1, char);
13356     rslt = do_vms_realname(fspec, rslt_spec, NULL);
13357 
13358     ST(0) = sv_newmortal();
13359     if (rslt != NULL)
13360 	sv_usepvn(ST(0),rslt,strlen(rslt));
13361     else
13362 	Safefree(rslt_spec);
13363 	XSRETURN(1);
13364 }
13365 
13366 #ifdef HAS_SYMLINK
13367 /*
13368  * A thin wrapper around decc$symlink to make sure we follow the
13369  * standard and do not create a symlink with a zero-length name,
13370  * and convert the target to Unix format, as the CRTL can't handle
13371  * targets in VMS format.
13372  */
13373 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13374 int
13375 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13376 {
13377     int sts;
13378     char * utarget;
13379 
13380     if (!link_name || !*link_name) {
13381       SETERRNO(ENOENT, SS$_NOSUCHFILE);
13382       return -1;
13383     }
13384 
13385     utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13386     /* An untranslatable filename should be passed through. */
13387     (void) int_tounixspec(contents, utarget, NULL);
13388     sts = symlink(utarget, link_name);
13389     PerlMem_free(utarget);
13390     return sts;
13391 }
13392 /*}}}*/
13393 
13394 #endif /* HAS_SYMLINK */
13395 
13396 int do_vms_case_tolerant(void);
13397 
13398 void
13399 case_tolerant_process_fromperl(pTHX_ CV *cv)
13400 {
13401   dXSARGS;
13402   ST(0) = boolSV(do_vms_case_tolerant());
13403   XSRETURN(1);
13404 }
13405 
13406 #ifdef USE_ITHREADS
13407 
13408 void
13409 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13410                           struct interp_intern *dst)
13411 {
13412     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13413 
13414     memcpy(dst,src,sizeof(struct interp_intern));
13415 }
13416 
13417 #endif
13418 
13419 void
13420 Perl_sys_intern_clear(pTHX)
13421 {
13422 }
13423 
13424 void
13425 Perl_sys_intern_init(pTHX)
13426 {
13427     unsigned int ix = RAND_MAX;
13428     double x;
13429 
13430     VMSISH_HUSHED = 0;
13431 
13432     MY_POSIX_EXIT = vms_posix_exit;
13433 
13434     x = (float)ix;
13435     MY_INV_RAND_MAX = 1./x;
13436 }
13437 
13438 void
13439 init_os_extras(void)
13440 {
13441   dTHX;
13442   char* file = __FILE__;
13443   if (decc_disable_to_vms_logname_translation) {
13444     no_translate_barewords = TRUE;
13445   } else {
13446     no_translate_barewords = FALSE;
13447   }
13448 
13449   newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13450   newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13451   newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13452   newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13453   newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13454   newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13455   newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13456   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13457   newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13458   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13459   newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13460   newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13461   newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13462   newXSproto("VMS::Filespec::case_tolerant_process",
13463       case_tolerant_process_fromperl,file,"");
13464 
13465   store_pipelocs(aTHX);         /* will redo any earlier attempts */
13466 
13467   return;
13468 }
13469 
13470 #if __CRTL_VER == 80200000
13471 /* This missed getting in to the DECC SDK for 8.2 */
13472 char *realpath(const char *file_name, char * resolved_name, ...);
13473 #endif
13474 
13475 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13476 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13477  * The perl fallback routine to provide realpath() is not as efficient
13478  * on OpenVMS.
13479  */
13480 
13481 #ifdef __cplusplus
13482 extern "C" {
13483 #endif
13484 
13485 /* Hack, use old stat() as fastest way of getting ino_t and device */
13486 int decc$stat(const char *name, void * statbuf);
13487 #if !defined(__VAX) && __CRTL_VER >= 80200000
13488 int decc$lstat(const char *name, void * statbuf);
13489 #else
13490 #define decc$lstat decc$stat
13491 #endif
13492 
13493 #ifdef __cplusplus
13494 }
13495 #endif
13496 
13497 
13498 /* Realpath is fragile.  In 8.3 it does not work if the feature
13499  * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13500  * links are implemented in RMS, not the CRTL. It also can fail if the
13501  * user does not have read/execute access to some of the directories.
13502  * So in order for Do What I Mean mode to work, if realpath() fails,
13503  * fall back to looking up the filename by the device name and FID.
13504  */
13505 
13506 int vms_fid_to_name(char * outname, int outlen,
13507                     const char * name, int lstat_flag, mode_t * mode)
13508 {
13509 #pragma message save
13510 #pragma message disable MISALGNDSTRCT
13511 #pragma message disable MISALGNDMEM
13512 #pragma member_alignment save
13513 #pragma nomember_alignment
13514 struct statbuf_t {
13515     char	   * st_dev;
13516     unsigned short st_ino[3];
13517     unsigned short old_st_mode;
13518     unsigned long  padl[30];  /* plenty of room */
13519 } statbuf;
13520 #pragma message restore
13521 #pragma member_alignment restore
13522 
13523     int sts;
13524     struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13525     struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13526     char *fileified;
13527     char *temp_fspec;
13528     char *ret_spec;
13529 
13530     /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13531      * unexpected answers
13532      */
13533 
13534     fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13535     if (fileified == NULL)
13536         _ckvmssts_noperl(SS$_INSFMEM);
13537 
13538     temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13539     if (temp_fspec == NULL)
13540         _ckvmssts_noperl(SS$_INSFMEM);
13541 
13542     sts = -1;
13543     /* First need to try as a directory */
13544     ret_spec = int_tovmspath(name, temp_fspec, NULL);
13545     if (ret_spec != NULL) {
13546         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13547         if (ret_spec != NULL) {
13548             if (lstat_flag == 0)
13549                 sts = decc$stat(fileified, &statbuf);
13550             else
13551                 sts = decc$lstat(fileified, &statbuf);
13552         }
13553     }
13554 
13555     /* Then as a VMS file spec */
13556     if (sts != 0) {
13557         ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13558         if (ret_spec != NULL) {
13559             if (lstat_flag == 0) {
13560                 sts = decc$stat(temp_fspec, &statbuf);
13561             } else {
13562                 sts = decc$lstat(temp_fspec, &statbuf);
13563             }
13564         }
13565     }
13566 
13567     if (sts) {
13568         /* Next try - allow multiple dots with out EFS CHARSET */
13569         /* The CRTL stat() falls down hard on multi-dot filenames in unix
13570          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13571          * enable it if it isn't already.
13572          */
13573 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13574         if (!decc_efs_charset && (decc_efs_charset_index > 0))
13575             decc$feature_set_value(decc_efs_charset_index, 1, 1);
13576 #endif
13577         ret_spec = int_tovmspath(name, temp_fspec, NULL);
13578         if (lstat_flag == 0) {
13579             sts = decc$stat(name, &statbuf);
13580         } else {
13581             sts = decc$lstat(name, &statbuf);
13582         }
13583 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13584         if (!decc_efs_charset && (decc_efs_charset_index > 0))
13585             decc$feature_set_value(decc_efs_charset_index, 1, 0);
13586 #endif
13587     }
13588 
13589 
13590     /* and then because the Perl Unix to VMS conversion is not perfect */
13591     /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13592     /* characters from filenames so we need to try it as-is */
13593     if (sts) {
13594         if (lstat_flag == 0) {
13595             sts = decc$stat(name, &statbuf);
13596         } else {
13597             sts = decc$lstat(name, &statbuf);
13598         }
13599     }
13600 
13601     if (sts == 0) {
13602         int vms_sts;
13603 
13604 	dvidsc.dsc$a_pointer=statbuf.st_dev;
13605         dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13606 
13607 	specdsc.dsc$a_pointer = outname;
13608 	specdsc.dsc$w_length = outlen-1;
13609 
13610         vms_sts = lib$fid_to_name
13611 	    (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13612         if ($VMS_STATUS_SUCCESS(vms_sts)) {
13613 	    outname[specdsc.dsc$w_length] = 0;
13614 
13615             /* Return the mode */
13616             if (mode) {
13617                 *mode = statbuf.old_st_mode;
13618             }
13619 	}
13620     }
13621     PerlMem_free(temp_fspec);
13622     PerlMem_free(fileified);
13623     return sts;
13624 }
13625 
13626 
13627 
13628 static char *
13629 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13630 		   int *utf8_fl)
13631 {
13632     char * rslt = NULL;
13633 
13634 #ifdef HAS_SYMLINK
13635     if (decc_posix_compliant_pathnames > 0 ) {
13636 	/* realpath currently only works if posix compliant pathnames are
13637 	 * enabled.  It may start working when they are not, but in that
13638 	 * case we still want the fallback behavior for backwards compatibility
13639 	 */
13640         rslt = realpath(filespec, outbuf);
13641     }
13642 #endif
13643 
13644     if (rslt == NULL) {
13645         char * vms_spec;
13646         char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13647         int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13648         mode_t my_mode;
13649 
13650 	/* Fall back to fid_to_name */
13651 
13652         Newx(vms_spec, VMS_MAXRSS + 1, char);
13653 
13654 	sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13655 	if (sts == 0) {
13656 
13657 
13658 	    /* Now need to trim the version off */
13659 	    sts = vms_split_path
13660 		  (vms_spec,
13661 		   &v_spec,
13662 		   &v_len,
13663 		   &r_spec,
13664 		   &r_len,
13665 		   &d_spec,
13666 		   &d_len,
13667 		   &n_spec,
13668 		   &n_len,
13669 		   &e_spec,
13670 		   &e_len,
13671 		   &vs_spec,
13672 		   &vs_len);
13673 
13674 
13675 		if (sts == 0) {
13676 	            int haslower = 0;
13677 	            const char *cp;
13678 
13679 	            /* Trim off the version */
13680 	            int file_len = v_len + r_len + d_len + n_len + e_len;
13681 	            vms_spec[file_len] = 0;
13682 
13683 	            /* Trim off the .DIR if this is a directory */
13684 	            if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13685                         if (S_ISDIR(my_mode)) {
13686                             e_len = 0;
13687                             e_spec[0] = 0;
13688                         }
13689 	            }
13690 
13691 	            /* Drop NULL extensions on UNIX file specification */
13692 		    if ((e_len == 1) && decc_readdir_dropdotnotype) {
13693 			e_len = 0;
13694 			e_spec[0] = '\0';
13695 		    }
13696 
13697 	            /* The result is expected to be in UNIX format */
13698 		    rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13699 
13700                     /* Downcase if input had any lower case letters and
13701 	             * case preservation is not in effect.
13702 	             */
13703 	            if (!decc_efs_case_preserve) {
13704 	                for (cp = filespec; *cp; cp++)
13705 	                    if (islower(*cp)) { haslower = 1; break; }
13706 
13707 	                if (haslower) __mystrtolower(rslt);
13708 	            }
13709 	        }
13710 	} else {
13711 
13712 	    /* Now for some hacks to deal with backwards and forward */
13713 	    /* compatibility */
13714 	    if (!decc_efs_charset) {
13715 
13716 		/* 1. ODS-2 mode wants to do a syntax only translation */
13717 		rslt = int_rmsexpand(filespec, outbuf,
13718 				    NULL, 0, NULL, utf8_fl);
13719 
13720 	    } else {
13721 		if (decc_filename_unix_report) {
13722 		    char * dir_name;
13723 		    char * vms_dir_name;
13724 		    char * file_name;
13725 
13726 		    /* 2. ODS-5 / UNIX report mode should return a failure */
13727 		    /*    if the parent directory also does not exist */
13728 		    /*    Otherwise, get the real path for the parent */
13729 		    /*    and add the child to it. */
13730 
13731 		    /* basename / dirname only available for VMS 7.0+ */
13732 		    /* So we may need to implement them as common routines */
13733 
13734 		    Newx(dir_name, VMS_MAXRSS + 1, char);
13735 		    Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13736 		    dir_name[0] = '\0';
13737 		    file_name = NULL;
13738 
13739 		    /* First try a VMS parse */
13740 		    sts = vms_split_path
13741 			  (filespec,
13742 			   &v_spec,
13743 			   &v_len,
13744 			   &r_spec,
13745 			   &r_len,
13746 			   &d_spec,
13747 			   &d_len,
13748 			   &n_spec,
13749 			   &n_len,
13750 			   &e_spec,
13751 			   &e_len,
13752 			   &vs_spec,
13753 			   &vs_len);
13754 
13755 		    if (sts == 0) {
13756 			/* This is VMS */
13757 
13758 			int dir_len = v_len + r_len + d_len + n_len;
13759 			if (dir_len > 0) {
13760 			   memcpy(dir_name, filespec, dir_len);
13761 			   dir_name[dir_len] = '\0';
13762 			   file_name = (char *)&filespec[dir_len + 1];
13763 			}
13764 		    } else {
13765 			/* This must be UNIX */
13766 			char * tchar;
13767 
13768 			tchar = strrchr(filespec, '/');
13769 
13770 			if (tchar != NULL) {
13771 			    int dir_len = tchar - filespec;
13772 			    memcpy(dir_name, filespec, dir_len);
13773 			    dir_name[dir_len] = '\0';
13774 			    file_name = (char *) &filespec[dir_len + 1];
13775 			}
13776 		    }
13777 
13778 		    /* Dir name is defaulted */
13779 		    if (dir_name[0] == 0) {
13780 			dir_name[0] = '.';
13781 			dir_name[1] = '\0';
13782 		    }
13783 
13784 		    /* Need realpath for the directory */
13785 		    sts = vms_fid_to_name(vms_dir_name,
13786 					  VMS_MAXRSS + 1,
13787 					  dir_name, 0, NULL);
13788 
13789 		    if (sts == 0) {
13790 		        /* Now need to pathify it. */
13791 		        char *tdir = int_pathify_dirspec(vms_dir_name,
13792 							 outbuf);
13793 
13794 			/* And now add the original filespec to it */
13795 			if (file_name != NULL) {
13796 			    my_strlcat(outbuf, file_name, VMS_MAXRSS);
13797 			}
13798 			return outbuf;
13799 		    }
13800 		    Safefree(vms_dir_name);
13801 		    Safefree(dir_name);
13802 		}
13803             }
13804         }
13805         Safefree(vms_spec);
13806     }
13807     return rslt;
13808 }
13809 
13810 static char *
13811 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13812 		   int *utf8_fl)
13813 {
13814     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13815     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13816 
13817     /* Fall back to fid_to_name */
13818 
13819     sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13820     if (sts != 0) {
13821 	return NULL;
13822     }
13823     else {
13824 
13825 
13826 	/* Now need to trim the version off */
13827 	sts = vms_split_path
13828 		  (outbuf,
13829 		   &v_spec,
13830 		   &v_len,
13831 		   &r_spec,
13832 		   &r_len,
13833 		   &d_spec,
13834 		   &d_len,
13835 		   &n_spec,
13836 		   &n_len,
13837 		   &e_spec,
13838 		   &e_len,
13839 		   &vs_spec,
13840 		   &vs_len);
13841 
13842 
13843 	if (sts == 0) {
13844 	    int haslower = 0;
13845 	    const char *cp;
13846 
13847 	    /* Trim off the version */
13848 	    int file_len = v_len + r_len + d_len + n_len + e_len;
13849 	    outbuf[file_len] = 0;
13850 
13851 	    /* Downcase if input had any lower case letters and
13852 	     * case preservation is not in effect.
13853 	     */
13854 	    if (!decc_efs_case_preserve) {
13855 	        for (cp = filespec; *cp; cp++)
13856 	            if (islower(*cp)) { haslower = 1; break; }
13857 
13858 	        if (haslower) __mystrtolower(outbuf);
13859 	    }
13860 	}
13861     }
13862     return outbuf;
13863 }
13864 
13865 
13866 /*}}}*/
13867 /* External entry points */
13868 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13869 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13870 
13871 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13872 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13873 
13874 /* case_tolerant */
13875 
13876 /*{{{int do_vms_case_tolerant(void)*/
13877 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13878  * controlled by a process setting.
13879  */
13880 int do_vms_case_tolerant(void)
13881 {
13882     return vms_process_case_tolerant;
13883 }
13884 /*}}}*/
13885 /* External entry points */
13886 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13887 int Perl_vms_case_tolerant(void)
13888 { return do_vms_case_tolerant(); }
13889 #else
13890 int Perl_vms_case_tolerant(void)
13891 { return vms_process_case_tolerant; }
13892 #endif
13893 
13894 
13895  /* Start of DECC RTL Feature handling */
13896 
13897 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13898 
13899 static int
13900 set_feature_default(const char *name, int value)
13901 {
13902     int status;
13903     int index;
13904     char val_str[10];
13905 
13906     /* If the feature has been explicitly disabled in the environment,
13907      * then don't enable it here.
13908      */
13909     if (value > 0) {
13910         status = simple_trnlnm(name, val_str, sizeof(val_str));
13911         if ($VMS_STATUS_SUCCESS(status)) {
13912             val_str[0] = _toupper(val_str[0]);
13913             if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13914 	        return 0;
13915         }
13916     }
13917 
13918     index = decc$feature_get_index(name);
13919 
13920     status = decc$feature_set_value(index, 1, value);
13921     if (index == -1 || (status == -1)) {
13922       return -1;
13923     }
13924 
13925     status = decc$feature_get_value(index, 1);
13926     if (status != value) {
13927       return -1;
13928     }
13929 
13930     /* Various things may check for an environment setting
13931      * rather than the feature directly, so set that too.
13932      */
13933     vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13934 
13935     return 0;
13936 }
13937 #endif
13938 
13939 
13940 /* C RTL Feature settings */
13941 
13942 #if defined(__DECC) || defined(__DECCXX)
13943 
13944 #ifdef __cplusplus
13945 extern "C" {
13946 #endif
13947 
13948 extern void
13949 vmsperl_set_features(void)
13950 {
13951     int status;
13952     int s;
13953     char val_str[10];
13954 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13955     const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13956     const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13957     unsigned long case_perm;
13958     unsigned long case_image;
13959 #endif
13960 
13961     /* Allow an exception to bring Perl into the VMS debugger */
13962     vms_debug_on_exception = 0;
13963     status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13964     if ($VMS_STATUS_SUCCESS(status)) {
13965        val_str[0] = _toupper(val_str[0]);
13966        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13967 	 vms_debug_on_exception = 1;
13968        else
13969 	 vms_debug_on_exception = 0;
13970     }
13971 
13972     /* Debug unix/vms file translation routines */
13973     vms_debug_fileify = 0;
13974     status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13975     if ($VMS_STATUS_SUCCESS(status)) {
13976 	val_str[0] = _toupper(val_str[0]);
13977         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13978 	    vms_debug_fileify = 1;
13979         else
13980 	    vms_debug_fileify = 0;
13981     }
13982 
13983 
13984     /* Historically PERL has been doing vmsify / stat differently than */
13985     /* the CRTL.  In particular, under some conditions the CRTL will   */
13986     /* remove some illegal characters like spaces from filenames       */
13987     /* resulting in some differences.  The stat()/lstat() wrapper has  */
13988     /* been reporting such file names as invalid and fails to stat them */
13989     /* fixing this bug so that stat()/lstat() accept these like the     */
13990     /* CRTL does will result in several tests failing.                  */
13991     /* This should really be fixed, but for now, set up a feature to    */
13992     /* enable it so that the impact can be studied.                     */
13993     vms_bug_stat_filename = 0;
13994     status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13995     if ($VMS_STATUS_SUCCESS(status)) {
13996 	val_str[0] = _toupper(val_str[0]);
13997         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13998 	    vms_bug_stat_filename = 1;
13999         else
14000 	    vms_bug_stat_filename = 0;
14001     }
14002 
14003 
14004     /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14005     vms_vtf7_filenames = 0;
14006     status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14007     if ($VMS_STATUS_SUCCESS(status)) {
14008        val_str[0] = _toupper(val_str[0]);
14009        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14010 	 vms_vtf7_filenames = 1;
14011        else
14012 	 vms_vtf7_filenames = 0;
14013     }
14014 
14015     /* unlink all versions on unlink() or rename() */
14016     vms_unlink_all_versions = 0;
14017     status = simple_trnlnm
14018 	("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14019     if ($VMS_STATUS_SUCCESS(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_unlink_all_versions = 1;
14023        else
14024 	 vms_unlink_all_versions = 0;
14025     }
14026 
14027 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14028     /* Detect running under GNV Bash or other UNIX like shell */
14029     gnv_unix_shell = 0;
14030     status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14031     if ($VMS_STATUS_SUCCESS(status)) {
14032 	 gnv_unix_shell = 1;
14033 	 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14034 	 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14035 	 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14036 	 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14037 	 vms_unlink_all_versions = 1;
14038 	 vms_posix_exit = 1;
14039     }
14040     /* Some reasonable defaults that are not CRTL defaults */
14041     set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14042     set_feature_default("DECC$ARGV_PARSE_STYLE", 1);     /* Requires extended parse. */
14043     set_feature_default("DECC$EFS_CHARSET", 1);
14044 #endif
14045 
14046     /* hacks to see if known bugs are still present for testing */
14047 
14048     /* PCP mode requires creating /dev/null special device file */
14049     decc_bug_devnull = 0;
14050     status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14051     if ($VMS_STATUS_SUCCESS(status)) {
14052        val_str[0] = _toupper(val_str[0]);
14053        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14054           decc_bug_devnull = 1;
14055        else
14056 	  decc_bug_devnull = 0;
14057     }
14058 
14059 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14060     s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14061     if (s >= 0) {
14062 	decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14063 	if (decc_disable_to_vms_logname_translation < 0)
14064 	    decc_disable_to_vms_logname_translation = 0;
14065     }
14066 
14067     s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14068     if (s >= 0) {
14069 	decc_efs_case_preserve = decc$feature_get_value(s, 1);
14070 	if (decc_efs_case_preserve < 0)
14071 	    decc_efs_case_preserve = 0;
14072     }
14073 
14074     s = decc$feature_get_index("DECC$EFS_CHARSET");
14075     decc_efs_charset_index = s;
14076     if (s >= 0) {
14077 	decc_efs_charset = decc$feature_get_value(s, 1);
14078 	if (decc_efs_charset < 0)
14079 	    decc_efs_charset = 0;
14080     }
14081 
14082     s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14083     if (s >= 0) {
14084 	decc_filename_unix_report = decc$feature_get_value(s, 1);
14085 	if (decc_filename_unix_report > 0) {
14086 	    decc_filename_unix_report = 1;
14087 	    vms_posix_exit = 1;
14088 	}
14089 	else
14090 	    decc_filename_unix_report = 0;
14091     }
14092 
14093     s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14094     if (s >= 0) {
14095 	decc_filename_unix_only = decc$feature_get_value(s, 1);
14096 	if (decc_filename_unix_only > 0) {
14097 	    decc_filename_unix_only = 1;
14098 	}
14099 	else {
14100 	    decc_filename_unix_only = 0;
14101 	}
14102     }
14103 
14104     s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14105     if (s >= 0) {
14106 	decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14107 	if (decc_filename_unix_no_version < 0)
14108 	    decc_filename_unix_no_version = 0;
14109     }
14110 
14111     s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14112     if (s >= 0) {
14113 	decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14114 	if (decc_readdir_dropdotnotype < 0)
14115 	    decc_readdir_dropdotnotype = 0;
14116     }
14117 
14118 #if __CRTL_VER >= 80200000
14119     s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14120     if (s >= 0) {
14121 	decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14122 	if (decc_posix_compliant_pathnames < 0)
14123 	    decc_posix_compliant_pathnames = 0;
14124 	if (decc_posix_compliant_pathnames > 4)
14125 	    decc_posix_compliant_pathnames = 0;
14126     }
14127 
14128 #endif
14129 #else
14130     status = simple_trnlnm
14131 	("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14132     if ($VMS_STATUS_SUCCESS(status)) {
14133 	val_str[0] = _toupper(val_str[0]);
14134 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14135 	   decc_disable_to_vms_logname_translation = 1;
14136 	}
14137     }
14138 
14139 #ifndef __VAX
14140     status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14141     if ($VMS_STATUS_SUCCESS(status)) {
14142 	val_str[0] = _toupper(val_str[0]);
14143 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14144 	   decc_efs_case_preserve = 1;
14145 	}
14146     }
14147 #endif
14148 
14149     status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14150     if ($VMS_STATUS_SUCCESS(status)) {
14151 	val_str[0] = _toupper(val_str[0]);
14152 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14153 	   decc_filename_unix_report = 1;
14154 	}
14155     }
14156     status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14157     if ($VMS_STATUS_SUCCESS(status)) {
14158 	val_str[0] = _toupper(val_str[0]);
14159 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14160 	   decc_filename_unix_only = 1;
14161 	   decc_filename_unix_report = 1;
14162 	}
14163     }
14164     status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14165     if ($VMS_STATUS_SUCCESS(status)) {
14166 	val_str[0] = _toupper(val_str[0]);
14167 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14168 	   decc_filename_unix_no_version = 1;
14169 	}
14170     }
14171     status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14172     if ($VMS_STATUS_SUCCESS(status)) {
14173 	val_str[0] = _toupper(val_str[0]);
14174 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14175 	   decc_readdir_dropdotnotype = 1;
14176 	}
14177     }
14178 #endif
14179 
14180 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14181 
14182      /* Report true case tolerance */
14183     /*----------------------------*/
14184     status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14185     if (!$VMS_STATUS_SUCCESS(status))
14186 	case_perm = PPROP$K_CASE_BLIND;
14187     status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14188     if (!$VMS_STATUS_SUCCESS(status))
14189 	case_image = PPROP$K_CASE_BLIND;
14190     if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14191 	(case_image == PPROP$K_CASE_SENSITIVE))
14192 	vms_process_case_tolerant = 0;
14193 
14194 #endif
14195 
14196     /* USE POSIX/DCL Exit codes - Recommended, but needs to default to  */
14197     /* for strict backward compatibility */
14198     status = simple_trnlnm
14199 	("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14200     if ($VMS_STATUS_SUCCESS(status)) {
14201        val_str[0] = _toupper(val_str[0]);
14202        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14203 	 vms_posix_exit = 1;
14204        else
14205 	 vms_posix_exit = 0;
14206     }
14207 }
14208 
14209 /* Use 32-bit pointers because that's what the image activator
14210  * assumes for the LIB$INITIALZE psect.
14211  */
14212 #if __INITIAL_POINTER_SIZE
14213 #pragma pointer_size save
14214 #pragma pointer_size 32
14215 #endif
14216 
14217 /* Create a reference to the LIB$INITIALIZE function. */
14218 extern void LIB$INITIALIZE(void);
14219 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14220 
14221 /* Create an array of pointers to the init functions in the special
14222  * LIB$INITIALIZE section. In our case, the array only has one entry.
14223  */
14224 #pragma extern_model save
14225 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14226 extern void (* const vmsperl_unused_global_2[])() =
14227 {
14228    vmsperl_set_features,
14229 };
14230 #pragma extern_model restore
14231 
14232 #if __INITIAL_POINTER_SIZE
14233 #pragma pointer_size restore
14234 #endif
14235 
14236 #ifdef __cplusplus
14237 }
14238 #endif
14239 
14240 #endif /* defined(__DECC) || defined(__DECCXX) */
14241 /*  End of vms.c */
14242