xref: /openbsd/gnu/usr.bin/perl/vms/vms.c (revision 898184e3)
1 /*    vms.c
2  *
3  *    VMS-specific routines for perl5
4  *
5  *    Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6  *    2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
7  *
8  *    You may distribute under the terms of either the GNU General Public
9  *    License or the Artistic License, as specified in the README file.
10  *
11  *    Please see Changes*.* or the Perl Repository Browser for revision history.
12  */
13 
14 /*
15  *   Yet small as was their hunted band
16  *   still fell and fearless was each hand,
17  *   and strong deeds they wrought yet oft,
18  *   and loved the woods, whose ways more soft
19  *   them seemed than thralls of that black throne
20  *   to live and languish in halls of stone.
21  *        "The Lay of Leithian", Canto II, lines 135-40
22  *
23  *     [p.162 of _The Lays of Beleriand_]
24  */
25 
26 #include <acedef.h>
27 #include <acldef.h>
28 #include <armdef.h>
29 #include <atrdef.h>
30 #include <chpdef.h>
31 #include <clidef.h>
32 #include <climsgdef.h>
33 #include <dcdef.h>
34 #include <descrip.h>
35 #include <devdef.h>
36 #include <dvidef.h>
37 #include <fibdef.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 <msgdef.h>
48 #include <ossdef.h>
49 #if __CRTL_VER >= 70301000 && !defined(__VAX)
50 #include <ppropdef.h>
51 #endif
52 #include <prvdef.h>
53 #include <psldef.h>
54 #include <rms.h>
55 #include <shrdef.h>
56 #include <ssdef.h>
57 #include <starlet.h>
58 #include <strdef.h>
59 #include <str$routines.h>
60 #include <syidef.h>
61 #include <uaidef.h>
62 #include <uicdef.h>
63 #include <stsdef.h>
64 #include <rmsdef.h>
65 #include <smgdef.h>
66 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
67 #include <efndef.h>
68 #define NO_EFN EFN$C_ENF
69 #else
70 #define NO_EFN 0;
71 #endif
72 
73 #if  __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
74 int   decc$feature_get_index(const char *name);
75 char* decc$feature_get_name(int index);
76 int   decc$feature_get_value(int index, int mode);
77 int   decc$feature_set_value(int index, int mode, int value);
78 #else
79 #include <unixlib.h>
80 #endif
81 
82 #pragma member_alignment save
83 #pragma nomember_alignment longword
84 struct item_list_3 {
85 	unsigned short len;
86 	unsigned short code;
87 	void * bufadr;
88 	unsigned short * retadr;
89 };
90 #pragma member_alignment restore
91 
92 /* More specific prototype than in starlet_c.h makes programming errors
93    more visible.
94  */
95 #ifdef sys$getdviw
96 #undef sys$getdviw
97 int sys$getdviw
98        (unsigned long efn,
99 	unsigned short chan,
100 	const struct dsc$descriptor_s * devnam,
101 	const struct item_list_3 * itmlst,
102 	void * iosb,
103 	void * (astadr)(unsigned long),
104 	void * astprm,
105 	void * nullarg);
106 #endif
107 
108 #ifdef sys$get_security
109 #undef sys$get_security
110 int sys$get_security
111        (const struct dsc$descriptor_s * clsnam,
112 	const struct dsc$descriptor_s * objnam,
113 	const unsigned int *objhan,
114 	unsigned int flags,
115 	const struct item_list_3 * itmlst,
116 	unsigned int * contxt,
117 	const unsigned int * acmode);
118 #endif
119 
120 #ifdef sys$set_security
121 #undef sys$set_security
122 int sys$set_security
123        (const struct dsc$descriptor_s * clsnam,
124 	const struct dsc$descriptor_s * objnam,
125 	const unsigned int *objhan,
126 	unsigned int flags,
127 	const struct item_list_3 * itmlst,
128 	unsigned int * contxt,
129 	const unsigned int * acmode);
130 #endif
131 
132 #ifdef lib$find_image_symbol
133 #undef lib$find_image_symbol
134 int lib$find_image_symbol
135        (const struct dsc$descriptor_s * imgname,
136 	const struct dsc$descriptor_s * symname,
137 	void * symval,
138 	const struct dsc$descriptor_s * defspec,
139 	unsigned long flag);
140 #endif
141 
142 #ifdef lib$rename_file
143 #undef lib$rename_file
144 int lib$rename_file
145        (const struct dsc$descriptor_s * old_file_dsc,
146 	const struct dsc$descriptor_s * new_file_dsc,
147 	const struct dsc$descriptor_s * default_file_dsc,
148 	const struct dsc$descriptor_s * related_file_dsc,
149 	const unsigned long * flags,
150 	void * (success)(const struct dsc$descriptor_s * old_dsc,
151 			 const struct dsc$descriptor_s * new_dsc,
152 			 const void *),
153 	void * (error)(const struct dsc$descriptor_s * old_dsc,
154 		       const struct dsc$descriptor_s * new_dsc,
155 		       const int * rms_sts,
156 		       const int * rms_stv,
157 		       const int * error_src,
158 		       const void * usr_arg),
159 	int (confirm)(const struct dsc$descriptor_s * old_dsc,
160 		      const struct dsc$descriptor_s * new_dsc,
161 		      const void * old_fab,
162 		      const void * usr_arg),
163 	void * user_arg,
164 	struct dsc$descriptor_s * old_result_name_dsc,
165 	struct dsc$descriptor_s * new_result_name_dsc,
166 	unsigned long * file_scan_context);
167 #endif
168 
169 #if __CRTL_VER >= 70300000 && !defined(__VAX)
170 
171 static int set_feature_default(const char *name, int value)
172 {
173     int status;
174     int index;
175 
176     index = decc$feature_get_index(name);
177 
178     status = decc$feature_set_value(index, 1, value);
179     if (index == -1 || (status == -1)) {
180       return -1;
181     }
182 
183     status = decc$feature_get_value(index, 1);
184     if (status != value) {
185       return -1;
186     }
187 
188 return 0;
189 }
190 #endif
191 
192 /* Older versions of ssdef.h don't have these */
193 #ifndef SS$_INVFILFOROP
194 #  define SS$_INVFILFOROP 3930
195 #endif
196 #ifndef SS$_NOSUCHOBJECT
197 #  define SS$_NOSUCHOBJECT 2696
198 #endif
199 
200 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
201 #define PERLIO_NOT_STDIO 0
202 
203 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
204  * code below needs to get to the underlying CRTL routines. */
205 #define DONT_MASK_RTL_CALLS
206 #include "EXTERN.h"
207 #include "perl.h"
208 #include "XSUB.h"
209 /* Anticipating future expansion in lexical warnings . . . */
210 #ifndef WARN_INTERNAL
211 #  define WARN_INTERNAL WARN_MISC
212 #endif
213 
214 #ifdef VMS_LONGNAME_SUPPORT
215 #include <libfildef.h>
216 #endif
217 
218 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
219 #  define RTL_USES_UTC 1
220 #endif
221 
222 #if !defined(__VAX) && __CRTL_VER >= 80200000
223 #ifdef lstat
224 #undef lstat
225 #endif
226 #else
227 #ifdef lstat
228 #undef lstat
229 #endif
230 #define lstat(_x, _y) stat(_x, _y)
231 #endif
232 
233 /* Routine to create a decterm for use with the Perl debugger */
234 /* No headers, this information was found in the Programming Concepts Manual */
235 
236 static int (*decw_term_port)
237    (const struct dsc$descriptor_s * display,
238     const struct dsc$descriptor_s * setup_file,
239     const struct dsc$descriptor_s * customization,
240     struct dsc$descriptor_s * result_device_name,
241     unsigned short * result_device_name_length,
242     void * controller,
243     void * char_buffer,
244     void * char_change_buffer) = 0;
245 
246 /* gcc's header files don't #define direct access macros
247  * corresponding to VAXC's variant structs */
248 #ifdef __GNUC__
249 #  define uic$v_format uic$r_uic_form.uic$v_format
250 #  define uic$v_group uic$r_uic_form.uic$v_group
251 #  define uic$v_member uic$r_uic_form.uic$v_member
252 #  define prv$v_bypass  prv$r_prvdef_bits0.prv$v_bypass
253 #  define prv$v_grpprv  prv$r_prvdef_bits0.prv$v_grpprv
254 #  define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
255 #  define prv$v_sysprv  prv$r_prvdef_bits0.prv$v_sysprv
256 #endif
257 
258 #if defined(NEED_AN_H_ERRNO)
259 dEXT int h_errno;
260 #endif
261 
262 #ifdef __DECC
263 #pragma message disable pragma
264 #pragma member_alignment save
265 #pragma nomember_alignment longword
266 #pragma message save
267 #pragma message disable misalgndmem
268 #endif
269 struct itmlst_3 {
270   unsigned short int buflen;
271   unsigned short int itmcode;
272   void *bufadr;
273   unsigned short int *retlen;
274 };
275 
276 struct filescan_itmlst_2 {
277     unsigned short length;
278     unsigned short itmcode;
279     char * component;
280 };
281 
282 struct vs_str_st {
283     unsigned short length;
284     char str[65536];
285 };
286 
287 #ifdef __DECC
288 #pragma message restore
289 #pragma member_alignment restore
290 #endif
291 
292 #define do_fileify_dirspec(a,b,c,d)	mp_do_fileify_dirspec(aTHX_ a,b,c,d)
293 #define do_pathify_dirspec(a,b,c,d)	mp_do_pathify_dirspec(aTHX_ a,b,c,d)
294 #define do_tovmsspec(a,b,c,d)		mp_do_tovmsspec(aTHX_ a,b,c,0,d)
295 #define do_tovmspath(a,b,c,d)		mp_do_tovmspath(aTHX_ a,b,c,d)
296 #define do_rmsexpand(a,b,c,d,e,f,g)	mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
297 #define do_vms_realpath(a,b,c)		mp_do_vms_realpath(aTHX_ a,b,c)
298 #define do_vms_realname(a,b,c)		mp_do_vms_realname(aTHX_ a,b,c)
299 #define do_tounixspec(a,b,c,d)		mp_do_tounixspec(aTHX_ a,b,c,d)
300 #define do_tounixpath(a,b,c,d)		mp_do_tounixpath(aTHX_ a,b,c,d)
301 #define do_vms_case_tolerant(a)		mp_do_vms_case_tolerant(a)
302 #define expand_wild_cards(a,b,c,d)	mp_expand_wild_cards(aTHX_ a,b,c,d)
303 #define getredirection(a,b)		mp_getredirection(aTHX_ a,b)
304 
305 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
306 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
307 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
308 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
309 
310 static char *  int_rmsexpand_vms(
311     const char * filespec, char * outbuf, unsigned opts);
312 static char * int_rmsexpand_tovms(
313     const char * filespec, char * outbuf, unsigned opts);
314 static char *int_tovmsspec
315    (const char *path, char *buf, int dir_flag, int * utf8_flag);
316 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
317 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
318 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
319 
320 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
321 #define PERL_LNM_MAX_ALLOWED_INDEX 127
322 
323 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
324  * depending on the facility.  SHOW LOGICAL does 10, so we'll imitate that for
325  * the Perl facility.
326  */
327 #define PERL_LNM_MAX_ITER 10
328 
329   /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
330 #if __CRTL_VER >= 70302000 && !defined(__VAX)
331 #define MAX_DCL_SYMBOL		(8192)
332 #define MAX_DCL_LINE_LENGTH	(4096 - 4)
333 #else
334 #define MAX_DCL_SYMBOL		(1024)
335 #define MAX_DCL_LINE_LENGTH	(1024 - 4)
336 #endif
337 
338 static char *__mystrtolower(char *str)
339 {
340   if (str) for (; *str; ++str) *str= tolower(*str);
341   return str;
342 }
343 
344 static struct dsc$descriptor_s fildevdsc =
345   { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
346 static struct dsc$descriptor_s crtlenvdsc =
347   { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
348 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
349 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
350 static struct dsc$descriptor_s **env_tables = defenv;
351 static bool will_taint = FALSE;  /* tainting active, but no PL_curinterp yet */
352 
353 /* True if we shouldn't treat barewords as logicals during directory */
354 /* munching */
355 static int no_translate_barewords;
356 
357 #ifndef RTL_USES_UTC
358 static int tz_updated = 1;
359 #endif
360 
361 /* DECC Features that may need to affect how Perl interprets
362  * displays filename information
363  */
364 static int decc_disable_to_vms_logname_translation = 1;
365 static int decc_disable_posix_root = 1;
366 int decc_efs_case_preserve = 0;
367 static int decc_efs_charset = 0;
368 static int decc_efs_charset_index = -1;
369 static int decc_filename_unix_no_version = 0;
370 static int decc_filename_unix_only = 0;
371 int decc_filename_unix_report = 0;
372 int decc_posix_compliant_pathnames = 0;
373 int decc_readdir_dropdotnotype = 0;
374 static int vms_process_case_tolerant = 1;
375 int vms_vtf7_filenames = 0;
376 int gnv_unix_shell = 0;
377 static int vms_unlink_all_versions = 0;
378 static int vms_posix_exit = 0;
379 
380 /* bug workarounds if needed */
381 int decc_bug_devnull = 1;
382 int decc_dir_barename = 0;
383 int vms_bug_stat_filename = 0;
384 
385 static int vms_debug_on_exception = 0;
386 static int vms_debug_fileify = 0;
387 
388 /* Simple logical name translation */
389 static int simple_trnlnm
390    (const char * logname,
391     char * value,
392     int value_len)
393 {
394     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
395     const unsigned long attr = LNM$M_CASE_BLIND;
396     struct dsc$descriptor_s name_dsc;
397     int status;
398     unsigned short result;
399     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
400                                 {0, 0, 0, 0}};
401 
402     name_dsc.dsc$w_length = strlen(logname);
403     name_dsc.dsc$a_pointer = (char *)logname;
404     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
405     name_dsc.dsc$b_class = DSC$K_CLASS_S;
406 
407     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
408 
409     if ($VMS_STATUS_SUCCESS(status)) {
410 
411 	 /* Null terminate and return the string */
412 	/*--------------------------------------*/
413 	value[result] = 0;
414         return result;
415     }
416 
417     return 0;
418 }
419 
420 
421 /* Is this a UNIX file specification?
422  *   No longer a simple check with EFS file specs
423  *   For now, not a full check, but need to
424  *   handle POSIX ^UP^ specifications
425  *   Fixing to handle ^/ cases would require
426  *   changes to many other conversion routines.
427  */
428 
429 static int is_unix_filespec(const char *path)
430 {
431 int ret_val;
432 const char * pch1;
433 
434     ret_val = 0;
435     if (strncmp(path,"\"^UP^",5) != 0) {
436 	pch1 = strchr(path, '/');
437 	if (pch1 != NULL)
438 	    ret_val = 1;
439 	else {
440 
441 	    /* If the user wants UNIX files, "." needs to be treated as in UNIX */
442 	    if (decc_filename_unix_report || decc_filename_unix_only) {
443 	    if (strcmp(path,".") == 0)
444 		ret_val = 1;
445 	    }
446 	}
447     }
448     return ret_val;
449 }
450 
451 /* This routine converts a UCS-2 character to be VTF-7 encoded.
452  */
453 
454 static void ucs2_to_vtf7
455    (char *outspec,
456     unsigned long ucs2_char,
457     int * output_cnt)
458 {
459 unsigned char * ucs_ptr;
460 int hex;
461 
462     ucs_ptr = (unsigned char *)&ucs2_char;
463 
464     outspec[0] = '^';
465     outspec[1] = 'U';
466     hex = (ucs_ptr[1] >> 4) & 0xf;
467     if (hex < 0xA)
468 	outspec[2] = hex + '0';
469     else
470 	outspec[2] = (hex - 9) + 'A';
471     hex = ucs_ptr[1] & 0xF;
472     if (hex < 0xA)
473 	outspec[3] = hex + '0';
474     else {
475 	outspec[3] = (hex - 9) + 'A';
476     }
477     hex = (ucs_ptr[0] >> 4) & 0xf;
478     if (hex < 0xA)
479 	outspec[4] = hex + '0';
480     else
481 	outspec[4] = (hex - 9) + 'A';
482     hex = ucs_ptr[1] & 0xF;
483     if (hex < 0xA)
484 	outspec[5] = hex + '0';
485     else {
486 	outspec[5] = (hex - 9) + 'A';
487     }
488     *output_cnt = 6;
489 }
490 
491 
492 /* This handles the conversion of a UNIX extended character set to a ^
493  * escaped VMS character.
494  * in a UNIX file specification.
495  *
496  * The output count variable contains the number of characters added
497  * to the output string.
498  *
499  * The return value is the number of characters read from the input string
500  */
501 static int copy_expand_unix_filename_escape
502   (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
503 {
504 int count;
505 int scnt;
506 int utf8_flag;
507 
508     utf8_flag = 0;
509     if (utf8_fl)
510       utf8_flag = *utf8_fl;
511 
512     count = 0;
513     *output_cnt = 0;
514     if (*inspec >= 0x80) {
515 	if (utf8_fl && vms_vtf7_filenames) {
516 	unsigned long ucs_char;
517 
518 	    ucs_char = 0;
519 
520 	    if ((*inspec & 0xE0) == 0xC0) {
521 		/* 2 byte Unicode */
522 		ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
523 		if (ucs_char >= 0x80) {
524 		    ucs2_to_vtf7(outspec, ucs_char, output_cnt);
525 		    return 2;
526 		}
527 	    } else if ((*inspec & 0xF0) == 0xE0) {
528 		/* 3 byte Unicode */
529 		ucs_char = ((inspec[0] & 0xF) << 12) +
530 		   ((inspec[1] & 0x3f) << 6) +
531 		   (inspec[2] & 0x3f);
532 		if (ucs_char >= 0x800) {
533 		    ucs2_to_vtf7(outspec, ucs_char, output_cnt);
534 		    return 3;
535 		}
536 
537 #if 0 /* I do not see longer sequences supported by OpenVMS */
538       /* Maybe some one can fix this later */
539 	    } else if ((*inspec & 0xF8) == 0xF0) {
540 		/* 4 byte Unicode */
541 		/* UCS-4 to UCS-2 */
542 	    } else if ((*inspec & 0xFC) == 0xF8) {
543 		/* 5 byte Unicode */
544 		/* UCS-4 to UCS-2 */
545 	    } else if ((*inspec & 0xFE) == 0xFC) {
546 		/* 6 byte Unicode */
547 		/* UCS-4 to UCS-2 */
548 #endif
549 	    }
550 	}
551 
552 	/* High bit set, but not a Unicode character! */
553 
554 	/* Non printing DECMCS or ISO Latin-1 character? */
555 	if (*inspec <= 0x9F) {
556 	int hex;
557 	    outspec[0] = '^';
558 	    outspec++;
559 	    hex = (*inspec >> 4) & 0xF;
560 	    if (hex < 0xA)
561 		outspec[1] = hex + '0';
562 	    else {
563 		outspec[1] = (hex - 9) + 'A';
564 	    }
565 	    hex = *inspec & 0xF;
566 	    if (hex < 0xA)
567 		outspec[2] = hex + '0';
568 	    else {
569 		outspec[2] = (hex - 9) + 'A';
570 	    }
571 	    *output_cnt = 3;
572 	    return 1;
573 	} else if (*inspec == 0xA0) {
574 	    outspec[0] = '^';
575 	    outspec[1] = 'A';
576 	    outspec[2] = '0';
577 	    *output_cnt = 3;
578 	    return 1;
579 	} else if (*inspec == 0xFF) {
580 	    outspec[0] = '^';
581 	    outspec[1] = 'F';
582 	    outspec[2] = 'F';
583 	    *output_cnt = 3;
584 	    return 1;
585 	}
586 	*outspec = *inspec;
587 	*output_cnt = 1;
588 	return 1;
589     }
590 
591     /* Is this a macro that needs to be passed through?
592      * Macros start with $( and an alpha character, followed
593      * by a string of alpha numeric characters ending with a )
594      * If this does not match, then encode it as ODS-5.
595      */
596     if ((inspec[0] == '$') && (inspec[1] == '(')) {
597     int tcnt;
598 
599 	if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
600 	    tcnt = 3;
601 	    outspec[0] = inspec[0];
602 	    outspec[1] = inspec[1];
603 	    outspec[2] = inspec[2];
604 
605 	    while(isalnum(inspec[tcnt]) ||
606 		  (inspec[2] == '.') || (inspec[2] == '_')) {
607 		outspec[tcnt] = inspec[tcnt];
608 		tcnt++;
609 	    }
610 	    if (inspec[tcnt] == ')') {
611 		outspec[tcnt] = inspec[tcnt];
612 		tcnt++;
613 		*output_cnt = tcnt;
614 		return tcnt;
615 	    }
616 	}
617     }
618 
619     switch (*inspec) {
620     case 0x7f:
621 	outspec[0] = '^';
622 	outspec[1] = '7';
623 	outspec[2] = 'F';
624 	*output_cnt = 3;
625 	return 1;
626 	break;
627     case '?':
628 	if (decc_efs_charset == 0)
629 	  outspec[0] = '%';
630 	else
631 	  outspec[0] = '?';
632 	*output_cnt = 1;
633 	return 1;
634 	break;
635     case '.':
636     case '~':
637     case '!':
638     case '#':
639     case '&':
640     case '\'':
641     case '`':
642     case '(':
643     case ')':
644     case '+':
645     case '@':
646     case '{':
647     case '}':
648     case ',':
649     case ';':
650     case '[':
651     case ']':
652     case '%':
653     case '^':
654     case '\\':
655         /* Don't escape again if following character is
656          * already something we escape.
657          */
658         if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
659 	    *outspec = *inspec;
660 	    *output_cnt = 1;
661 	    return 1;
662 	    break;
663         }
664         /* But otherwise fall through and escape it. */
665     case '=':
666 	/* Assume that this is to be escaped */
667 	outspec[0] = '^';
668 	outspec[1] = *inspec;
669 	*output_cnt = 2;
670 	return 1;
671 	break;
672     case ' ': /* space */
673 	/* Assume that this is to be escaped */
674 	outspec[0] = '^';
675 	outspec[1] = '_';
676 	*output_cnt = 2;
677 	return 1;
678 	break;
679     default:
680 	*outspec = *inspec;
681 	*output_cnt = 1;
682 	return 1;
683 	break;
684     }
685 }
686 
687 
688 /* This handles the expansion of a '^' prefix to the proper character
689  * in a UNIX file specification.
690  *
691  * The output count variable contains the number of characters added
692  * to the output string.
693  *
694  * The return value is the number of characters read from the input
695  * string
696  */
697 static int copy_expand_vms_filename_escape
698   (char *outspec, const char *inspec, int *output_cnt)
699 {
700 int count;
701 int scnt;
702 
703     count = 0;
704     *output_cnt = 0;
705     if (*inspec == '^') {
706 	inspec++;
707 	switch (*inspec) {
708         /* Spaces and non-trailing dots should just be passed through,
709          * but eat the escape character.
710          */
711 	case '.':
712 	    *outspec = *inspec;
713 	    count += 2;
714 	    (*output_cnt)++;
715 	    break;
716 	case '_': /* space */
717 	    *outspec = ' ';
718 	    count += 2;
719 	    (*output_cnt)++;
720 	    break;
721 	case '^':
722             /* Hmm.  Better leave the escape escaped. */
723             outspec[0] = '^';
724             outspec[1] = '^';
725 	    count += 2;
726 	    (*output_cnt) += 2;
727 	    break;
728 	case 'U': /* Unicode - FIX-ME this is wrong. */
729 	    inspec++;
730 	    count++;
731 	    scnt = strspn(inspec, "0123456789ABCDEFabcdef");
732 	    if (scnt == 4) {
733 		unsigned int c1, c2;
734 		scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
735 		outspec[0] == c1 & 0xff;
736 		outspec[1] == c2 & 0xff;
737 		if (scnt > 1) {
738 		    (*output_cnt) += 2;
739 		    count += 4;
740 		}
741 	    }
742 	    else {
743 		/* Error - do best we can to continue */
744 		*outspec = 'U';
745 		outspec++;
746 		(*output_cnt++);
747 		*outspec = *inspec;
748 		count++;
749 		(*output_cnt++);
750 	    }
751 	    break;
752 	default:
753 	    scnt = strspn(inspec, "0123456789ABCDEFabcdef");
754 	    if (scnt == 2) {
755 		/* Hex encoded */
756 		unsigned int c1;
757 		scnt = sscanf(inspec, "%2x", &c1);
758 		outspec[0] = c1 & 0xff;
759 		if (scnt > 0) {
760 		    (*output_cnt++);
761 		    count += 2;
762 	        }
763 	    }
764 	    else {
765 		*outspec = *inspec;
766 		count++;
767 		(*output_cnt++);
768 	    }
769 	}
770     }
771     else {
772 	*outspec = *inspec;
773 	count++;
774 	(*output_cnt)++;
775     }
776     return count;
777 }
778 
779 #ifdef sys$filescan
780 #undef sys$filescan
781 int sys$filescan
782    (const struct dsc$descriptor_s * srcstr,
783     struct filescan_itmlst_2 * valuelist,
784     unsigned long * fldflags,
785     struct dsc$descriptor_s *auxout,
786     unsigned short * retlen);
787 #endif
788 
789 /* vms_split_path - Verify that the input file specification is a
790  * VMS format file specification, and provide pointers to the components of
791  * it.  With EFS format filenames, this is virtually the only way to
792  * parse a VMS path specification into components.
793  *
794  * If the sum of the components do not add up to the length of the
795  * string, then the passed file specification is probably a UNIX style
796  * path.
797  */
798 static int vms_split_path
799    (const char * path,
800     char * * volume,
801     int * vol_len,
802     char * * root,
803     int * root_len,
804     char * * dir,
805     int * dir_len,
806     char * * name,
807     int * name_len,
808     char * * ext,
809     int * ext_len,
810     char * * version,
811     int * ver_len)
812 {
813 struct dsc$descriptor path_desc;
814 int status;
815 unsigned long flags;
816 int ret_stat;
817 struct filescan_itmlst_2 item_list[9];
818 const int filespec = 0;
819 const int nodespec = 1;
820 const int devspec = 2;
821 const int rootspec = 3;
822 const int dirspec = 4;
823 const int namespec = 5;
824 const int typespec = 6;
825 const int verspec = 7;
826 
827     /* Assume the worst for an easy exit */
828     ret_stat = -1;
829     *volume = NULL;
830     *vol_len = 0;
831     *root = NULL;
832     *root_len = 0;
833     *dir = NULL;
834     *dir_len;
835     *name = NULL;
836     *name_len = 0;
837     *ext = NULL;
838     *ext_len = 0;
839     *version = NULL;
840     *ver_len = 0;
841 
842     path_desc.dsc$a_pointer = (char *)path; /* cast ok */
843     path_desc.dsc$w_length = strlen(path);
844     path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
845     path_desc.dsc$b_class = DSC$K_CLASS_S;
846 
847     /* Get the total length, if it is shorter than the string passed
848      * then this was probably not a VMS formatted file specification
849      */
850     item_list[filespec].itmcode = FSCN$_FILESPEC;
851     item_list[filespec].length = 0;
852     item_list[filespec].component = NULL;
853 
854     /* If the node is present, then it gets considered as part of the
855      * volume name to hopefully make things simple.
856      */
857     item_list[nodespec].itmcode = FSCN$_NODE;
858     item_list[nodespec].length = 0;
859     item_list[nodespec].component = NULL;
860 
861     item_list[devspec].itmcode = FSCN$_DEVICE;
862     item_list[devspec].length = 0;
863     item_list[devspec].component = NULL;
864 
865     /* root is a special case,  adding it to either the directory or
866      * the device components will probalby complicate things for the
867      * callers of this routine, so leave it separate.
868      */
869     item_list[rootspec].itmcode = FSCN$_ROOT;
870     item_list[rootspec].length = 0;
871     item_list[rootspec].component = NULL;
872 
873     item_list[dirspec].itmcode = FSCN$_DIRECTORY;
874     item_list[dirspec].length = 0;
875     item_list[dirspec].component = NULL;
876 
877     item_list[namespec].itmcode = FSCN$_NAME;
878     item_list[namespec].length = 0;
879     item_list[namespec].component = NULL;
880 
881     item_list[typespec].itmcode = FSCN$_TYPE;
882     item_list[typespec].length = 0;
883     item_list[typespec].component = NULL;
884 
885     item_list[verspec].itmcode = FSCN$_VERSION;
886     item_list[verspec].length = 0;
887     item_list[verspec].component = NULL;
888 
889     item_list[8].itmcode = 0;
890     item_list[8].length = 0;
891     item_list[8].component = NULL;
892 
893     status = sys$filescan
894        ((const struct dsc$descriptor_s *)&path_desc, item_list,
895 	&flags, NULL, NULL);
896     _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
897 
898     /* If we parsed it successfully these two lengths should be the same */
899     if (path_desc.dsc$w_length != item_list[filespec].length)
900 	return ret_stat;
901 
902     /* If we got here, then it is a VMS file specification */
903     ret_stat = 0;
904 
905     /* set the volume name */
906     if (item_list[nodespec].length > 0) {
907 	*volume = item_list[nodespec].component;
908 	*vol_len = item_list[nodespec].length + item_list[devspec].length;
909     }
910     else {
911 	*volume = item_list[devspec].component;
912 	*vol_len = item_list[devspec].length;
913     }
914 
915     *root = item_list[rootspec].component;
916     *root_len = item_list[rootspec].length;
917 
918     *dir = item_list[dirspec].component;
919     *dir_len = item_list[dirspec].length;
920 
921     /* Now fun with versions and EFS file specifications
922      * The parser can not tell the difference when a "." is a version
923      * delimiter or a part of the file specification.
924      */
925     if ((decc_efs_charset) &&
926 	(item_list[verspec].length > 0) &&
927 	(item_list[verspec].component[0] == '.')) {
928 	*name = item_list[namespec].component;
929 	*name_len = item_list[namespec].length + item_list[typespec].length;
930 	*ext = item_list[verspec].component;
931 	*ext_len = item_list[verspec].length;
932 	*version = NULL;
933 	*ver_len = 0;
934     }
935     else {
936 	*name = item_list[namespec].component;
937 	*name_len = item_list[namespec].length;
938 	*ext = item_list[typespec].component;
939 	*ext_len = item_list[typespec].length;
940 	*version = item_list[verspec].component;
941 	*ver_len = item_list[verspec].length;
942     }
943     return ret_stat;
944 }
945 
946 /* Routine to determine if the file specification ends with .dir */
947 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
948 
949     /* e_len must be 4, and version must be <= 2 characters */
950     if (e_len != 4 || vs_len > 2)
951         return 0;
952 
953     /* If a version number is present, it needs to be one */
954     if ((vs_len == 2) && (vs_spec[1] != '1'))
955         return 0;
956 
957     /* Look for the DIR on the extension */
958     if (vms_process_case_tolerant) {
959         if ((toupper(e_spec[1]) == 'D') &&
960             (toupper(e_spec[2]) == 'I') &&
961             (toupper(e_spec[3]) == 'R')) {
962             return 1;
963         }
964     } else {
965         /* Directory extensions are supposed to be in upper case only */
966         /* I would not be surprised if this rule can not be enforced */
967         /* if and when someone fully debugs the case sensitive mode */
968         if ((e_spec[1] == 'D') &&
969             (e_spec[2] == 'I') &&
970             (e_spec[3] == 'R')) {
971             return 1;
972         }
973     }
974     return 0;
975 }
976 
977 
978 /* my_maxidx
979  * Routine to retrieve the maximum equivalence index for an input
980  * logical name.  Some calls to this routine have no knowledge if
981  * the variable is a logical or not.  So on error we return a max
982  * index of zero.
983  */
984 /*{{{int my_maxidx(const char *lnm) */
985 static int
986 my_maxidx(const char *lnm)
987 {
988     int status;
989     int midx;
990     int attr = LNM$M_CASE_BLIND;
991     struct dsc$descriptor lnmdsc;
992     struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
993                                 {0, 0, 0, 0}};
994 
995     lnmdsc.dsc$w_length = strlen(lnm);
996     lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
997     lnmdsc.dsc$b_class = DSC$K_CLASS_S;
998     lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
999 
1000     status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
1001     if ((status & 1) == 0)
1002        midx = 0;
1003 
1004     return (midx);
1005 }
1006 /*}}}*/
1007 
1008 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
1009 int
1010 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
1011   struct dsc$descriptor_s **tabvec, unsigned long int flags)
1012 {
1013     const char *cp1;
1014     char uplnm[LNM$C_NAMLENGTH+1], *cp2;
1015     unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
1016     unsigned long int retsts, attr = LNM$M_CASE_BLIND;
1017     int midx;
1018     unsigned char acmode;
1019     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1020                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1021     struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
1022                                  {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
1023                                  {0, 0, 0, 0}};
1024     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1025 #if defined(PERL_IMPLICIT_CONTEXT)
1026     pTHX = NULL;
1027     if (PL_curinterp) {
1028       aTHX = PERL_GET_INTERP;
1029     } else {
1030       aTHX = NULL;
1031     }
1032 #endif
1033 
1034     if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
1035       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
1036     }
1037     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1038       *cp2 = _toupper(*cp1);
1039       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1040         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1041         return 0;
1042       }
1043     }
1044     lnmdsc.dsc$w_length = cp1 - lnm;
1045     lnmdsc.dsc$a_pointer = uplnm;
1046     uplnm[lnmdsc.dsc$w_length] = '\0';
1047     secure = flags & PERL__TRNENV_SECURE;
1048     acmode = secure ? PSL$C_EXEC : PSL$C_USER;
1049     if (!tabvec || !*tabvec) tabvec = env_tables;
1050 
1051     for (curtab = 0; tabvec[curtab]; curtab++) {
1052       if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1053         if (!ivenv && !secure) {
1054           char *eq, *end;
1055           int i;
1056           if (!environ) {
1057             ivenv = 1;
1058 #if defined(PERL_IMPLICIT_CONTEXT)
1059             if (aTHX == NULL) {
1060                 fprintf(stderr,
1061                     "Can't read CRTL environ\n");
1062             } else
1063 #endif
1064                 Perl_warn(aTHX_ "Can't read CRTL environ\n");
1065             continue;
1066           }
1067           retsts = SS$_NOLOGNAM;
1068           for (i = 0; environ[i]; i++) {
1069             if ((eq = strchr(environ[i],'=')) &&
1070                 lnmdsc.dsc$w_length == (eq - environ[i]) &&
1071                 !strncmp(environ[i],uplnm,eq - environ[i])) {
1072               eq++;
1073               for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
1074               if (!eqvlen) continue;
1075               retsts = SS$_NORMAL;
1076               break;
1077             }
1078           }
1079           if (retsts != SS$_NOLOGNAM) break;
1080         }
1081       }
1082       else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1083                !str$case_blind_compare(&tmpdsc,&clisym)) {
1084         if (!ivsym && !secure) {
1085           unsigned short int deflen = LNM$C_NAMLENGTH;
1086           struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1087           /* dynamic dsc to accomodate possible long value */
1088           _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
1089           retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1090           if (retsts & 1) {
1091             if (eqvlen > MAX_DCL_SYMBOL) {
1092               set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1093               eqvlen = MAX_DCL_SYMBOL;
1094 	      /* Special hack--we might be called before the interpreter's */
1095 	      /* fully initialized, in which case either thr or PL_curcop */
1096 	      /* might be bogus. We have to check, since ckWARN needs them */
1097 	      /* both to be valid if running threaded */
1098 #if defined(PERL_IMPLICIT_CONTEXT)
1099               if (aTHX == NULL) {
1100                   fprintf(stderr,
1101                      "Value of CLI symbol \"%s\" too long",lnm);
1102               } else
1103 #endif
1104 		if (ckWARN(WARN_MISC)) {
1105 		  Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1106 		}
1107             }
1108             strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1109           }
1110           _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1111           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1112           if (retsts == LIB$_NOSUCHSYM) continue;
1113           break;
1114         }
1115       }
1116       else if (!ivlnm) {
1117         if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1118           midx = my_maxidx(lnm);
1119           for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1120             lnmlst[1].bufadr = cp2;
1121             eqvlen = 0;
1122             retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1123             if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1124             if (retsts == SS$_NOLOGNAM) break;
1125             /* PPFs have a prefix */
1126             if (
1127 #if INTSIZE == 4
1128                  *((int *)uplnm) == *((int *)"SYS$")                    &&
1129 #endif
1130                  eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00        &&
1131                  ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT"))  ||
1132                    (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT"))   ||
1133                    (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR"))   ||
1134                    (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) )  ) {
1135               memmove(eqv,eqv+4,eqvlen-4);
1136               eqvlen -= 4;
1137             }
1138             cp2 += eqvlen;
1139             *cp2 = '\0';
1140           }
1141           if ((retsts == SS$_IVLOGNAM) ||
1142               (retsts == SS$_NOLOGNAM)) { continue; }
1143         }
1144         else {
1145           retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1146           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1147           if (retsts == SS$_NOLOGNAM) continue;
1148           eqv[eqvlen] = '\0';
1149         }
1150         eqvlen = strlen(eqv);
1151         break;
1152       }
1153     }
1154     if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1155     else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1156              retsts == SS$_IVLOGNAM   || retsts == SS$_IVLOGTAB   ||
1157              retsts == SS$_NOLOGNAM) {
1158       set_errno(EINVAL);  set_vaxc_errno(retsts);
1159     }
1160     else _ckvmssts_noperl(retsts);
1161     return 0;
1162 }  /* end of vmstrnenv */
1163 /*}}}*/
1164 
1165 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1166 /* Define as a function so we can access statics. */
1167 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1168 {
1169     int flags = 0;
1170 
1171 #if defined(PERL_IMPLICIT_CONTEXT)
1172     if (aTHX != NULL)
1173 #endif
1174 #ifdef SECURE_INTERNAL_GETENV
1175         flags = (PL_curinterp ? PL_tainting : will_taint) ?
1176                  PERL__TRNENV_SECURE : 0;
1177 #endif
1178 
1179     return vmstrnenv(lnm, eqv, idx, fildev, flags);
1180 }
1181 /*}}}*/
1182 
1183 /* my_getenv
1184  * Note: Uses Perl temp to store result so char * can be returned to
1185  * caller; this pointer will be invalidated at next Perl statement
1186  * transition.
1187  * We define this as a function rather than a macro in terms of my_getenv_len()
1188  * so that it'll work when PL_curinterp is undefined (and we therefore can't
1189  * allocate SVs).
1190  */
1191 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1192 char *
1193 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1194 {
1195     const char *cp1;
1196     static char *__my_getenv_eqv = NULL;
1197     char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1198     unsigned long int idx = 0;
1199     int trnsuccess, success, secure, saverr, savvmserr;
1200     int midx, flags;
1201     SV *tmpsv;
1202 
1203     midx = my_maxidx(lnm) + 1;
1204 
1205     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1206       /* Set up a temporary buffer for the return value; Perl will
1207        * clean it up at the next statement transition */
1208       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1209       if (!tmpsv) return NULL;
1210       eqv = SvPVX(tmpsv);
1211     }
1212     else {
1213       /* Assume no interpreter ==> single thread */
1214       if (__my_getenv_eqv != NULL) {
1215         Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1216       }
1217       else {
1218         Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1219       }
1220       eqv = __my_getenv_eqv;
1221     }
1222 
1223     for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1224     if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1225       int len;
1226       getcwd(eqv,LNM$C_NAMLENGTH);
1227 
1228       len = strlen(eqv);
1229 
1230       /* Get rid of "000000/ in rooted filespecs */
1231       if (len > 7) {
1232         char * zeros;
1233 	zeros = strstr(eqv, "/000000/");
1234 	if (zeros != NULL) {
1235 	  int mlen;
1236 	  mlen = len - (zeros - eqv) - 7;
1237 	  memmove(zeros, &zeros[7], mlen);
1238 	  len = len - 7;
1239 	  eqv[len] = '\0';
1240 	}
1241       }
1242       return eqv;
1243     }
1244     else {
1245       /* Impose security constraints only if tainting */
1246       if (sys) {
1247         /* Impose security constraints only if tainting */
1248         secure = PL_curinterp ? PL_tainting : will_taint;
1249         saverr = errno;  savvmserr = vaxc$errno;
1250       }
1251       else {
1252         secure = 0;
1253       }
1254 
1255       flags =
1256 #ifdef SECURE_INTERNAL_GETENV
1257               secure ? PERL__TRNENV_SECURE : 0
1258 #else
1259               0
1260 #endif
1261       ;
1262 
1263       /* For the getenv interface we combine all the equivalence names
1264        * of a search list logical into one value to acquire a maximum
1265        * value length of 255*128 (assuming %ENV is using logicals).
1266        */
1267       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1268 
1269       /* If the name contains a semicolon-delimited index, parse it
1270        * off and make sure we only retrieve the equivalence name for
1271        * that index.  */
1272       if ((cp2 = strchr(lnm,';')) != NULL) {
1273         strcpy(uplnm,lnm);
1274         uplnm[cp2-lnm] = '\0';
1275         idx = strtoul(cp2+1,NULL,0);
1276         lnm = uplnm;
1277         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1278       }
1279 
1280       success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1281 
1282       /* Discard NOLOGNAM on internal calls since we're often looking
1283        * for an optional name, and this "error" often shows up as the
1284        * (bogus) exit status for a die() call later on.  */
1285       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1286       return success ? eqv : NULL;
1287     }
1288 
1289 }  /* end of my_getenv() */
1290 /*}}}*/
1291 
1292 
1293 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1294 char *
1295 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1296 {
1297     const char *cp1;
1298     char *buf, *cp2;
1299     unsigned long idx = 0;
1300     int midx, flags;
1301     static char *__my_getenv_len_eqv = NULL;
1302     int secure, saverr, savvmserr;
1303     SV *tmpsv;
1304 
1305     midx = my_maxidx(lnm) + 1;
1306 
1307     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1308       /* Set up a temporary buffer for the return value; Perl will
1309        * clean it up at the next statement transition */
1310       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1311       if (!tmpsv) return NULL;
1312       buf = SvPVX(tmpsv);
1313     }
1314     else {
1315       /* Assume no interpreter ==> single thread */
1316       if (__my_getenv_len_eqv != NULL) {
1317         Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1318       }
1319       else {
1320         Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1321       }
1322       buf = __my_getenv_len_eqv;
1323     }
1324 
1325     for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1326     if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1327     char * zeros;
1328 
1329       getcwd(buf,LNM$C_NAMLENGTH);
1330       *len = strlen(buf);
1331 
1332       /* Get rid of "000000/ in rooted filespecs */
1333       if (*len > 7) {
1334       zeros = strstr(buf, "/000000/");
1335       if (zeros != NULL) {
1336 	int mlen;
1337 	mlen = *len - (zeros - buf) - 7;
1338 	memmove(zeros, &zeros[7], mlen);
1339 	*len = *len - 7;
1340 	buf[*len] = '\0';
1341 	}
1342       }
1343       return buf;
1344     }
1345     else {
1346       if (sys) {
1347         /* Impose security constraints only if tainting */
1348         secure = PL_curinterp ? PL_tainting : will_taint;
1349         saverr = errno;  savvmserr = vaxc$errno;
1350       }
1351       else {
1352         secure = 0;
1353       }
1354 
1355       flags =
1356 #ifdef SECURE_INTERNAL_GETENV
1357               secure ? PERL__TRNENV_SECURE : 0
1358 #else
1359               0
1360 #endif
1361       ;
1362 
1363       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1364 
1365       if ((cp2 = strchr(lnm,';')) != NULL) {
1366         strcpy(buf,lnm);
1367         buf[cp2-lnm] = '\0';
1368         idx = strtoul(cp2+1,NULL,0);
1369         lnm = buf;
1370         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1371       }
1372 
1373       *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1374 
1375       /* Get rid of "000000/ in rooted filespecs */
1376       if (*len > 7) {
1377       char * zeros;
1378 	zeros = strstr(buf, "/000000/");
1379 	if (zeros != NULL) {
1380 	  int mlen;
1381 	  mlen = *len - (zeros - buf) - 7;
1382 	  memmove(zeros, &zeros[7], mlen);
1383 	  *len = *len - 7;
1384 	  buf[*len] = '\0';
1385 	}
1386       }
1387 
1388       /* Discard NOLOGNAM on internal calls since we're often looking
1389        * for an optional name, and this "error" often shows up as the
1390        * (bogus) exit status for a die() call later on.  */
1391       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1392       return *len ? buf : NULL;
1393     }
1394 
1395 }  /* end of my_getenv_len() */
1396 /*}}}*/
1397 
1398 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1399 
1400 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1401 
1402 /*{{{ void prime_env_iter() */
1403 void
1404 prime_env_iter(void)
1405 /* Fill the %ENV associative array with all logical names we can
1406  * find, in preparation for iterating over it.
1407  */
1408 {
1409   static int primed = 0;
1410   HV *seenhv = NULL, *envhv;
1411   SV *sv = NULL;
1412   char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1413   unsigned short int chan;
1414 #ifndef CLI$M_TRUSTED
1415 #  define CLI$M_TRUSTED 0x40  /* Missing from VAXC headers */
1416 #endif
1417   unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1418   unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1419   long int i;
1420   bool have_sym = FALSE, have_lnm = FALSE;
1421   struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1422   $DESCRIPTOR(cmddsc,cmd);    $DESCRIPTOR(nldsc,"_NLA0:");
1423   $DESCRIPTOR(clidsc,"DCL");  $DESCRIPTOR(clitabdsc,"DCLTABLES");
1424   $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1425   $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1426 #if defined(PERL_IMPLICIT_CONTEXT)
1427   pTHX;
1428 #endif
1429 #if defined(USE_ITHREADS)
1430   static perl_mutex primenv_mutex;
1431   MUTEX_INIT(&primenv_mutex);
1432 #endif
1433 
1434 #if defined(PERL_IMPLICIT_CONTEXT)
1435     /* We jump through these hoops because we can be called at */
1436     /* platform-specific initialization time, which is before anything is */
1437     /* set up--we can't even do a plain dTHX since that relies on the */
1438     /* interpreter structure to be initialized */
1439     if (PL_curinterp) {
1440       aTHX = PERL_GET_INTERP;
1441     } else {
1442       /* we never get here because the NULL pointer will cause the */
1443       /* several of the routines called by this routine to access violate */
1444 
1445       /* This routine is only called by hv.c/hv_iterinit which has a */
1446       /* context, so the real fix may be to pass it through instead of */
1447       /* the hoops above */
1448       aTHX = NULL;
1449     }
1450 #endif
1451 
1452   if (primed || !PL_envgv) return;
1453   MUTEX_LOCK(&primenv_mutex);
1454   if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1455   envhv = GvHVn(PL_envgv);
1456   /* Perform a dummy fetch as an lval to insure that the hash table is
1457    * set up.  Otherwise, the hv_store() will turn into a nullop. */
1458   (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1459 
1460   for (i = 0; env_tables[i]; i++) {
1461      if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1462          !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1463      if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1464   }
1465   if (have_sym || have_lnm) {
1466     long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1467     _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1468     _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1469     _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1470   }
1471 
1472   for (i--; i >= 0; i--) {
1473     if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1474       char *start;
1475       int j;
1476       for (j = 0; environ[j]; j++) {
1477         if (!(start = strchr(environ[j],'='))) {
1478           if (ckWARN(WARN_INTERNAL))
1479             Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1480         }
1481         else {
1482           start++;
1483           sv = newSVpv(start,0);
1484           SvTAINTED_on(sv);
1485           (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1486         }
1487       }
1488       continue;
1489     }
1490     else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1491              !str$case_blind_compare(&tmpdsc,&clisym)) {
1492       strcpy(cmd,"Show Symbol/Global *");
1493       cmddsc.dsc$w_length = 20;
1494       if (env_tables[i]->dsc$w_length == 12 &&
1495           (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1496           !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local  *");
1497       flags = defflags | CLI$M_NOLOGNAM;
1498     }
1499     else {
1500       strcpy(cmd,"Show Logical *");
1501       if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1502         strcat(cmd," /Table=");
1503         strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1504         cmddsc.dsc$w_length = strlen(cmd);
1505       }
1506       else cmddsc.dsc$w_length = 14;  /* N.B. We test this below */
1507       flags = defflags | CLI$M_NOCLISYM;
1508     }
1509 
1510     /* Create a new subprocess to execute each command, to exclude the
1511      * remote possibility that someone could subvert a mbx or file used
1512      * to write multiple commands to a single subprocess.
1513      */
1514     do {
1515       retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1516                          0,&riseandshine,0,0,&clidsc,&clitabdsc);
1517       flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1518       defflags &= ~CLI$M_TRUSTED;
1519     } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1520     _ckvmssts(retsts);
1521     if (!buf) Newx(buf,mbxbufsiz + 1,char);
1522     if (seenhv) SvREFCNT_dec(seenhv);
1523     seenhv = newHV();
1524     while (1) {
1525       char *cp1, *cp2, *key;
1526       unsigned long int sts, iosb[2], retlen, keylen;
1527       register U32 hash;
1528 
1529       sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1530       if (sts & 1) sts = iosb[0] & 0xffff;
1531       if (sts == SS$_ENDOFFILE) {
1532         int wakect = 0;
1533         while (substs == 0) { sys$hiber(); wakect++;}
1534         if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
1535         _ckvmssts(substs);
1536         break;
1537       }
1538       _ckvmssts(sts);
1539       retlen = iosb[0] >> 16;
1540       if (!retlen) continue;  /* blank line */
1541       buf[retlen] = '\0';
1542       if (iosb[1] != subpid) {
1543         if (iosb[1]) {
1544           Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1545         }
1546         continue;
1547       }
1548       if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1549         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1550 
1551       for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1552       if (*cp1 == '(' || /* Logical name table name */
1553           *cp1 == '='    /* Next eqv of searchlist  */) continue;
1554       if (*cp1 == '"') cp1++;
1555       for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1556       key = cp1;  keylen = cp2 - cp1;
1557       if (keylen && hv_exists(seenhv,key,keylen)) continue;
1558       while (*cp2 && *cp2 != '=') cp2++;
1559       while (*cp2 && *cp2 == '=') cp2++;
1560       while (*cp2 && *cp2 == ' ') cp2++;
1561       if (*cp2 == '"') {  /* String translation; may embed "" */
1562         for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1563         cp2++;  cp1--; /* Skip "" surrounding translation */
1564       }
1565       else {  /* Numeric translation */
1566         for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1567         cp1--;  /* stop on last non-space char */
1568       }
1569       if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1570         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1571         continue;
1572       }
1573       PERL_HASH(hash,key,keylen);
1574 
1575       if (cp1 == cp2 && *cp2 == '.') {
1576         /* A single dot usually means an unprintable character, such as a null
1577          * to indicate a zero-length value.  Get the actual value to make sure.
1578          */
1579         char lnm[LNM$C_NAMLENGTH+1];
1580         char eqv[MAX_DCL_SYMBOL+1];
1581         int trnlen;
1582         strncpy(lnm, key, keylen);
1583         trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1584         sv = newSVpvn(eqv, strlen(eqv));
1585       }
1586       else {
1587         sv = newSVpvn(cp2,cp1 - cp2 + 1);
1588       }
1589 
1590       SvTAINTED_on(sv);
1591       hv_store(envhv,key,keylen,sv,hash);
1592       hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1593     }
1594     if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1595       /* get the PPFs for this process, not the subprocess */
1596       const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1597       char eqv[LNM$C_NAMLENGTH+1];
1598       int trnlen, i;
1599       for (i = 0; ppfs[i]; i++) {
1600         trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1601         sv = newSVpv(eqv,trnlen);
1602         SvTAINTED_on(sv);
1603         hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1604       }
1605     }
1606   }
1607   primed = 1;
1608   if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1609   if (buf) Safefree(buf);
1610   if (seenhv) SvREFCNT_dec(seenhv);
1611   MUTEX_UNLOCK(&primenv_mutex);
1612   return;
1613 
1614 }  /* end of prime_env_iter */
1615 /*}}}*/
1616 
1617 
1618 /*{{{ int  vmssetenv(const char *lnm, const char *eqv)*/
1619 /* Define or delete an element in the same "environment" as
1620  * vmstrnenv().  If an element is to be deleted, it's removed from
1621  * the first place it's found.  If it's to be set, it's set in the
1622  * place designated by the first element of the table vector.
1623  * Like setenv() returns 0 for success, non-zero on error.
1624  */
1625 int
1626 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1627 {
1628     const char *cp1;
1629     char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1630     unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1631     int nseg = 0, j;
1632     unsigned long int retsts, usermode = PSL$C_USER;
1633     struct itmlst_3 *ile, *ilist;
1634     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1635                             eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1636                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1637     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1638     $DESCRIPTOR(local,"_LOCAL");
1639 
1640     if (!lnm) {
1641         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1642         return SS$_IVLOGNAM;
1643     }
1644 
1645     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1646       *cp2 = _toupper(*cp1);
1647       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1648         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1649         return SS$_IVLOGNAM;
1650       }
1651     }
1652     lnmdsc.dsc$w_length = cp1 - lnm;
1653     if (!tabvec || !*tabvec) tabvec = env_tables;
1654 
1655     if (!eqv) {  /* we're deleting n element */
1656       for (curtab = 0; tabvec[curtab]; curtab++) {
1657         if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1658         int i;
1659           for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1660             if ((cp1 = strchr(environ[i],'=')) &&
1661                 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1662                 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1663 #ifdef HAS_SETENV
1664               return setenv(lnm,"",1) ? vaxc$errno : 0;
1665             }
1666           }
1667           ivenv = 1; retsts = SS$_NOLOGNAM;
1668 #else
1669               if (ckWARN(WARN_INTERNAL))
1670                 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1671               ivenv = 1; retsts = SS$_NOSUCHPGM;
1672               break;
1673             }
1674           }
1675 #endif
1676         }
1677         else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1678                  !str$case_blind_compare(&tmpdsc,&clisym)) {
1679           unsigned int symtype;
1680           if (tabvec[curtab]->dsc$w_length == 12 &&
1681               (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1682               !str$case_blind_compare(&tmpdsc,&local))
1683             symtype = LIB$K_CLI_LOCAL_SYM;
1684           else symtype = LIB$K_CLI_GLOBAL_SYM;
1685           retsts = lib$delete_symbol(&lnmdsc,&symtype);
1686           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1687           if (retsts == LIB$_NOSUCHSYM) continue;
1688           break;
1689         }
1690         else if (!ivlnm) {
1691           retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1692           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1693           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1694           retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1695           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1696         }
1697       }
1698     }
1699     else {  /* we're defining a value */
1700       if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1701 #ifdef HAS_SETENV
1702         return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1703 #else
1704         if (ckWARN(WARN_INTERNAL))
1705           Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1706         retsts = SS$_NOSUCHPGM;
1707 #endif
1708       }
1709       else {
1710         eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1711         eqvdsc.dsc$w_length  = strlen(eqv);
1712         if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1713             !str$case_blind_compare(&tmpdsc,&clisym)) {
1714           unsigned int symtype;
1715           if (tabvec[0]->dsc$w_length == 12 &&
1716               (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1717                !str$case_blind_compare(&tmpdsc,&local))
1718             symtype = LIB$K_CLI_LOCAL_SYM;
1719           else symtype = LIB$K_CLI_GLOBAL_SYM;
1720           retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1721         }
1722         else {
1723           if (!*eqv) eqvdsc.dsc$w_length = 1;
1724 	  if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1725 
1726             nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1727             if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1728 	      Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1729                           lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1730               eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1731               nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1732 	    }
1733 
1734             Newx(ilist,nseg+1,struct itmlst_3);
1735             ile = ilist;
1736             if (!ile) {
1737 	      set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1738               return SS$_INSFMEM;
1739 	    }
1740             memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1741 
1742             for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1743               ile->itmcode = LNM$_STRING;
1744               ile->bufadr = c;
1745               if ((j+1) == nseg) {
1746                 ile->buflen = strlen(c);
1747                 /* in case we are truncating one that's too long */
1748                 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1749               }
1750               else {
1751                 ile->buflen = LNM$C_NAMLENGTH;
1752               }
1753             }
1754 
1755             retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1756             Safefree (ilist);
1757 	  }
1758           else {
1759             retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1760 	  }
1761         }
1762       }
1763     }
1764     if (!(retsts & 1)) {
1765       switch (retsts) {
1766         case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1767         case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1768           set_errno(EVMSERR); break;
1769         case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1770         case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1771           set_errno(EINVAL); break;
1772         case SS$_NOPRIV:
1773           set_errno(EACCES); break;
1774         default:
1775           _ckvmssts(retsts);
1776           set_errno(EVMSERR);
1777        }
1778        set_vaxc_errno(retsts);
1779        return (int) retsts || 44; /* retsts should never be 0, but just in case */
1780     }
1781     else {
1782       /* We reset error values on success because Perl does an hv_fetch()
1783        * before each hv_store(), and if the thing we're setting didn't
1784        * previously exist, we've got a leftover error message.  (Of course,
1785        * this fails in the face of
1786        *    $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1787        * in that the error reported in $! isn't spurious,
1788        * but it's right more often than not.)
1789        */
1790       set_errno(0); set_vaxc_errno(retsts);
1791       return 0;
1792     }
1793 
1794 }  /* end of vmssetenv() */
1795 /*}}}*/
1796 
1797 /*{{{ void  my_setenv(const char *lnm, const char *eqv)*/
1798 /* This has to be a function since there's a prototype for it in proto.h */
1799 void
1800 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1801 {
1802     if (lnm && *lnm) {
1803       int len = strlen(lnm);
1804       if  (len == 7) {
1805         char uplnm[8];
1806         int i;
1807         for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1808         if (!strcmp(uplnm,"DEFAULT")) {
1809           if (eqv && *eqv) my_chdir(eqv);
1810           return;
1811         }
1812     }
1813 #ifndef RTL_USES_UTC
1814     if (len == 6 || len == 2) {
1815       char uplnm[7];
1816       int i;
1817       for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1818       uplnm[len] = '\0';
1819       if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1820       if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1821     }
1822 #endif
1823   }
1824   (void) vmssetenv(lnm,eqv,NULL);
1825 }
1826 /*}}}*/
1827 
1828 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1829 /*  vmssetuserlnm
1830  *  sets a user-mode logical in the process logical name table
1831  *  used for redirection of sys$error
1832  *
1833  *  Fix-me: The pTHX is not needed for this routine, however doio.c
1834  *          is calling it with one instead of using a macro.
1835  *          A macro needs to be added to vmsish.h and doio.c updated to use it.
1836  *
1837  */
1838 void
1839 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1840 {
1841     $DESCRIPTOR(d_tab, "LNM$PROCESS");
1842     struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1843     unsigned long int iss, attr = LNM$M_CONFINE;
1844     unsigned char acmode = PSL$C_USER;
1845     struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1846                                  {0, 0, 0, 0}};
1847     d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1848     d_name.dsc$w_length = strlen(name);
1849 
1850     lnmlst[0].buflen = strlen(eqv);
1851     lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1852 
1853     iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1854     if (!(iss&1)) lib$signal(iss);
1855 }
1856 /*}}}*/
1857 
1858 
1859 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1860 /* my_crypt - VMS password hashing
1861  * my_crypt() provides an interface compatible with the Unix crypt()
1862  * C library function, and uses sys$hash_password() to perform VMS
1863  * password hashing.  The quadword hashed password value is returned
1864  * as a NUL-terminated 8 character string.  my_crypt() does not change
1865  * the case of its string arguments; in order to match the behavior
1866  * of LOGINOUT et al., alphabetic characters in both arguments must
1867  *  be upcased by the caller.
1868  *
1869  * - fix me to call ACM services when available
1870  */
1871 char *
1872 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1873 {
1874 #   ifndef UAI$C_PREFERRED_ALGORITHM
1875 #     define UAI$C_PREFERRED_ALGORITHM 127
1876 #   endif
1877     unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1878     unsigned short int salt = 0;
1879     unsigned long int sts;
1880     struct const_dsc {
1881         unsigned short int dsc$w_length;
1882         unsigned char      dsc$b_type;
1883         unsigned char      dsc$b_class;
1884         const char *       dsc$a_pointer;
1885     }  usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1886        txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1887     struct itmlst_3 uailst[3] = {
1888         { sizeof alg,  UAI$_ENCRYPT, &alg, 0},
1889         { sizeof salt, UAI$_SALT,    &salt, 0},
1890         { 0,           0,            NULL,  NULL}};
1891     static char hash[9];
1892 
1893     usrdsc.dsc$w_length = strlen(usrname);
1894     usrdsc.dsc$a_pointer = usrname;
1895     if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1896       switch (sts) {
1897         case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1898           set_errno(EACCES);
1899           break;
1900         case RMS$_RNF:
1901           set_errno(ESRCH);  /* There isn't a Unix no-such-user error */
1902           break;
1903         default:
1904           set_errno(EVMSERR);
1905       }
1906       set_vaxc_errno(sts);
1907       if (sts != RMS$_RNF) return NULL;
1908     }
1909 
1910     txtdsc.dsc$w_length = strlen(textpasswd);
1911     txtdsc.dsc$a_pointer = textpasswd;
1912     if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1913       set_errno(EVMSERR);  set_vaxc_errno(sts);  return NULL;
1914     }
1915 
1916     return (char *) hash;
1917 
1918 }  /* end of my_crypt() */
1919 /*}}}*/
1920 
1921 
1922 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1923 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1924 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1925 
1926 /* fixup barenames that are directories for internal use.
1927  * There have been problems with the consistent handling of UNIX
1928  * style directory names when routines are presented with a name that
1929  * has no directory delimitors at all.  So this routine will eventually
1930  * fix the issue.
1931  */
1932 static char * fixup_bare_dirnames(const char * name)
1933 {
1934   if (decc_disable_to_vms_logname_translation) {
1935 /* fix me */
1936   }
1937   return NULL;
1938 }
1939 
1940 /* 8.3, remove() is now broken on symbolic links */
1941 static int rms_erase(const char * vmsname);
1942 
1943 
1944 /* mp_do_kill_file
1945  * A little hack to get around a bug in some implemenation of remove()
1946  * that do not know how to delete a directory
1947  *
1948  * Delete any file to which user has control access, regardless of whether
1949  * delete access is explicitly allowed.
1950  * Limitations: User must have write access to parent directory.
1951  *              Does not block signals or ASTs; if interrupted in midstream
1952  *              may leave file with an altered ACL.
1953  * HANDLE WITH CARE!
1954  */
1955 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1956 static int
1957 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1958 {
1959     char *vmsname;
1960     char *rslt;
1961     unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1962     unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1963     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1964     struct myacedef {
1965       unsigned char myace$b_length;
1966       unsigned char myace$b_type;
1967       unsigned short int myace$w_flags;
1968       unsigned long int myace$l_access;
1969       unsigned long int myace$l_ident;
1970     } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1971                  ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1972       oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1973      struct itmlst_3
1974        findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1975                      {sizeof oldace, ACL$C_READACE,   &oldace, 0},{0,0,0,0}},
1976        addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1977        dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1978        lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1979        ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1980 
1981     /* Expand the input spec using RMS, since the CRTL remove() and
1982      * system services won't do this by themselves, so we may miss
1983      * a file "hiding" behind a logical name or search list. */
1984     vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1985     if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1986 
1987     rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1988     if (rslt == NULL) {
1989         PerlMem_free(vmsname);
1990 	return -1;
1991       }
1992 
1993     /* Erase the file */
1994     rmsts = rms_erase(vmsname);
1995 
1996     /* Did it succeed */
1997     if ($VMS_STATUS_SUCCESS(rmsts)) {
1998 	PerlMem_free(vmsname);
1999 	return 0;
2000       }
2001 
2002     /* If not, can changing protections help? */
2003     if (rmsts != RMS$_PRV) {
2004       set_vaxc_errno(rmsts);
2005       PerlMem_free(vmsname);
2006       return -1;
2007     }
2008 
2009     /* No, so we get our own UIC to use as a rights identifier,
2010      * and the insert an ACE at the head of the ACL which allows us
2011      * to delete the file.
2012      */
2013     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
2014     fildsc.dsc$w_length = strlen(vmsname);
2015     fildsc.dsc$a_pointer = vmsname;
2016     cxt = 0;
2017     newace.myace$l_ident = oldace.myace$l_ident;
2018     rmsts = -1;
2019     if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
2020       switch (aclsts) {
2021         case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
2022           set_errno(ENOENT); break;
2023         case RMS$_DIR:
2024           set_errno(ENOTDIR); break;
2025         case RMS$_DEV:
2026           set_errno(ENODEV); break;
2027         case RMS$_SYN: case SS$_INVFILFOROP:
2028           set_errno(EINVAL); break;
2029         case RMS$_PRV:
2030           set_errno(EACCES); break;
2031         default:
2032           _ckvmssts_noperl(aclsts);
2033       }
2034       set_vaxc_errno(aclsts);
2035       PerlMem_free(vmsname);
2036       return -1;
2037     }
2038     /* Grab any existing ACEs with this identifier in case we fail */
2039     aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2040     if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2041                     || fndsts == SS$_NOMOREACE ) {
2042       /* Add the new ACE . . . */
2043       if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2044         goto yourroom;
2045 
2046       rmsts = rms_erase(vmsname);
2047       if ($VMS_STATUS_SUCCESS(rmsts)) {
2048 	rmsts = 0;
2049 	}
2050 	else {
2051 	rmsts = -1;
2052         /* We blew it - dir with files in it, no write priv for
2053          * parent directory, etc.  Put things back the way they were. */
2054         if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2055           goto yourroom;
2056         if (fndsts & 1) {
2057           addlst[0].bufadr = &oldace;
2058           if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2059             goto yourroom;
2060         }
2061       }
2062     }
2063 
2064     yourroom:
2065     fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2066     /* We just deleted it, so of course it's not there.  Some versions of
2067      * VMS seem to return success on the unlock operation anyhow (after all
2068      * the unlock is successful), but others don't.
2069      */
2070     if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2071     if (aclsts & 1) aclsts = fndsts;
2072     if (!(aclsts & 1)) {
2073       set_errno(EVMSERR);
2074       set_vaxc_errno(aclsts);
2075     }
2076 
2077     PerlMem_free(vmsname);
2078     return rmsts;
2079 
2080 }  /* end of kill_file() */
2081 /*}}}*/
2082 
2083 
2084 /*{{{int do_rmdir(char *name)*/
2085 int
2086 Perl_do_rmdir(pTHX_ const char *name)
2087 {
2088     char * dirfile;
2089     int retval;
2090     Stat_t st;
2091 
2092     /* lstat returns a VMS fileified specification of the name */
2093     /* that is looked up, and also lets verifies that this is a directory */
2094 
2095     retval = flex_lstat(name, &st);
2096     if (retval != 0) {
2097         char * ret_spec;
2098 
2099         /* Due to a historical feature, flex_stat/lstat can not see some */
2100         /* Unix format file names that the rest of the CRTL can see */
2101         /* Fixing that feature will cause some perl tests to fail */
2102         /* So try this one more time. */
2103 
2104         retval = lstat(name, &st.crtl_stat);
2105         if (retval != 0)
2106             return -1;
2107 
2108         /* force it to a file spec for the kill file to work. */
2109         ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
2110         if (ret_spec == NULL) {
2111             errno = EIO;
2112             return -1;
2113         }
2114     }
2115 
2116     if (!S_ISDIR(st.st_mode)) {
2117 	errno = ENOTDIR;
2118 	retval = -1;
2119     }
2120     else {
2121         dirfile = st.st_devnam;
2122 
2123         /* It may be possible for flex_stat to find a file and vmsify() to */
2124         /* fail with ODS-2 specifications.  mp_do_kill_file can not deal */
2125         /* with that case, so fail it */
2126         if (dirfile[0] == 0) {
2127             errno = EIO;
2128             return -1;
2129         }
2130 
2131 	retval = mp_do_kill_file(aTHX_ dirfile, 1);
2132     }
2133 
2134     return retval;
2135 
2136 }  /* end of do_rmdir */
2137 /*}}}*/
2138 
2139 /* kill_file
2140  * Delete any file to which user has control access, regardless of whether
2141  * delete access is explicitly allowed.
2142  * Limitations: User must have write access to parent directory.
2143  *              Does not block signals or ASTs; if interrupted in midstream
2144  *              may leave file with an altered ACL.
2145  * HANDLE WITH CARE!
2146  */
2147 /*{{{int kill_file(char *name)*/
2148 int
2149 Perl_kill_file(pTHX_ const char *name)
2150 {
2151     char * vmsfile;
2152     Stat_t st;
2153     int rmsts;
2154 
2155     /* Convert the filename to VMS format and see if it is a directory */
2156     /* flex_lstat returns a vmsified file specification */
2157     rmsts = flex_lstat(name, &st);
2158     if (rmsts != 0) {
2159 
2160         /* Due to a historical feature, flex_stat/lstat can not see some */
2161         /* Unix format file names that the rest of the CRTL can see when */
2162         /* ODS-2 file specifications are in use. */
2163         /* Fixing that feature will cause some perl tests to fail */
2164         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2165         st.st_mode = 0;
2166         vmsfile = (char *) name; /* cast ok */
2167 
2168     } else {
2169         vmsfile = st.st_devnam;
2170         if (vmsfile[0] == 0) {
2171             /* It may be possible for flex_stat to find a file and vmsify() */
2172             /* to fail with ODS-2 specifications.  mp_do_kill_file can not */
2173             /* deal with that case, so fail it */
2174             errno = EIO;
2175             return -1;
2176         }
2177     }
2178 
2179     /* Remove() is allowed to delete directories, according to the X/Open
2180      * specifications.
2181      * This may need special handling to work with the ACL hacks.
2182      */
2183     if (S_ISDIR(st.st_mode)) {
2184         rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2185         return rmsts;
2186     }
2187 
2188     rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2189 
2190     /* Need to delete all versions ? */
2191     if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2192         int i = 0;
2193 
2194         /* Just use lstat() here as do not need st_dev */
2195         /* and we know that the file is in VMS format or that */
2196         /* because of a historical bug, flex_stat can not see the file */
2197         while (lstat(vmsfile, (stat_t *)&st) == 0) {
2198             rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2199             if (rmsts != 0)
2200                 break;
2201             i++;
2202 
2203             /* Make sure that we do not loop forever */
2204             if (i > 32767) {
2205                 errno = EIO;
2206                 rmsts = -1;
2207                 break;
2208             }
2209         }
2210     }
2211 
2212     return rmsts;
2213 
2214 }  /* end of kill_file() */
2215 /*}}}*/
2216 
2217 
2218 /*{{{int my_mkdir(char *,Mode_t)*/
2219 int
2220 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2221 {
2222   STRLEN dirlen = strlen(dir);
2223 
2224   /* zero length string sometimes gives ACCVIO */
2225   if (dirlen == 0) return -1;
2226 
2227   /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2228    * null file name/type.  However, it's commonplace under Unix,
2229    * so we'll allow it for a gain in portability.
2230    */
2231   if (dir[dirlen-1] == '/') {
2232     char *newdir = savepvn(dir,dirlen-1);
2233     int ret = mkdir(newdir,mode);
2234     Safefree(newdir);
2235     return ret;
2236   }
2237   else return mkdir(dir,mode);
2238 }  /* end of my_mkdir */
2239 /*}}}*/
2240 
2241 /*{{{int my_chdir(char *)*/
2242 int
2243 Perl_my_chdir(pTHX_ const char *dir)
2244 {
2245   STRLEN dirlen = strlen(dir);
2246 
2247   /* zero length string sometimes gives ACCVIO */
2248   if (dirlen == 0) return -1;
2249   const char *dir1;
2250 
2251   /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2252    * This does not work if DECC$EFS_CHARSET is active.  Hack it here
2253    * so that existing scripts do not need to be changed.
2254    */
2255   dir1 = dir;
2256   while ((dirlen > 0) && (*dir1 == ' ')) {
2257     dir1++;
2258     dirlen--;
2259   }
2260 
2261   /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2262    * that implies
2263    * null file name/type.  However, it's commonplace under Unix,
2264    * so we'll allow it for a gain in portability.
2265    *
2266    *  '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2267    */
2268   if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2269       char *newdir;
2270       int ret;
2271       newdir = PerlMem_malloc(dirlen);
2272       if (newdir ==NULL)
2273           _ckvmssts_noperl(SS$_INSFMEM);
2274       strncpy(newdir, dir1, dirlen-1);
2275       newdir[dirlen-1] = '\0';
2276       ret = chdir(newdir);
2277       PerlMem_free(newdir);
2278       return ret;
2279   }
2280   else return chdir(dir1);
2281 }  /* end of my_chdir */
2282 /*}}}*/
2283 
2284 
2285 /*{{{int my_chmod(char *, mode_t)*/
2286 int
2287 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2288 {
2289   Stat_t st;
2290   int ret = -1;
2291   char * changefile;
2292   STRLEN speclen = strlen(file_spec);
2293 
2294   /* zero length string sometimes gives ACCVIO */
2295   if (speclen == 0) return -1;
2296 
2297   /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2298    * that implies null file name/type.  However, it's commonplace under Unix,
2299    * so we'll allow it for a gain in portability.
2300    *
2301    * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2302    * in VMS file.dir notation.
2303    */
2304   changefile = (char *) file_spec; /* cast ok */
2305   ret = flex_lstat(file_spec, &st);
2306   if (ret != 0) {
2307 
2308         /* Due to a historical feature, flex_stat/lstat can not see some */
2309         /* Unix format file names that the rest of the CRTL can see when */
2310         /* ODS-2 file specifications are in use. */
2311         /* Fixing that feature will cause some perl tests to fail */
2312         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2313         st.st_mode = 0;
2314 
2315   } else {
2316       /* It may be possible to get here with nothing in st_devname */
2317       /* chmod still may work though */
2318       if (st.st_devnam[0] != 0) {
2319           changefile = st.st_devnam;
2320       }
2321   }
2322   ret = chmod(changefile, mode);
2323   return ret;
2324 }  /* end of my_chmod */
2325 /*}}}*/
2326 
2327 
2328 /*{{{FILE *my_tmpfile()*/
2329 FILE *
2330 my_tmpfile(void)
2331 {
2332   FILE *fp;
2333   char *cp;
2334 
2335   if ((fp = tmpfile())) return fp;
2336 
2337   cp = PerlMem_malloc(L_tmpnam+24);
2338   if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2339 
2340   if (decc_filename_unix_only == 0)
2341     strcpy(cp,"Sys$Scratch:");
2342   else
2343     strcpy(cp,"/tmp/");
2344   tmpnam(cp+strlen(cp));
2345   strcat(cp,".Perltmp");
2346   fp = fopen(cp,"w+","fop=dlt");
2347   PerlMem_free(cp);
2348   return fp;
2349 }
2350 /*}}}*/
2351 
2352 
2353 #ifndef HOMEGROWN_POSIX_SIGNALS
2354 /*
2355  * The C RTL's sigaction fails to check for invalid signal numbers so we
2356  * help it out a bit.  The docs are correct, but the actual routine doesn't
2357  * do what the docs say it will.
2358  */
2359 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2360 int
2361 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2362                    struct sigaction* oact)
2363 {
2364   if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2365 	SETERRNO(EINVAL, SS$_INVARG);
2366 	return -1;
2367   }
2368   return sigaction(sig, act, oact);
2369 }
2370 /*}}}*/
2371 #endif
2372 
2373 #ifdef KILL_BY_SIGPRC
2374 #include <errnodef.h>
2375 
2376 /* We implement our own kill() using the undocumented system service
2377    sys$sigprc for one of two reasons:
2378 
2379    1.) If the kill() in an older CRTL uses sys$forcex, causing the
2380    target process to do a sys$exit, which usually can't be handled
2381    gracefully...certainly not by Perl and the %SIG{} mechanism.
2382 
2383    2.) If the kill() in the CRTL can't be called from a signal
2384    handler without disappearing into the ether, i.e., the signal
2385    it purportedly sends is never trapped. Still true as of VMS 7.3.
2386 
2387    sys$sigprc has the same parameters as sys$forcex, but throws an exception
2388    in the target process rather than calling sys$exit.
2389 
2390    Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2391    on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2392    provide.  On VMS 7.0+ this is taken care of by doing sys$sigprc
2393    with condition codes C$_SIG0+nsig*8, catching the exception on the
2394    target process and resignaling with appropriate arguments.
2395 
2396    But we don't have that VMS 7.0+ exception handler, so if you
2397    Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS.  Oh well.
2398 
2399    Also note that SIGTERM is listed in the docs as being "unimplemented",
2400    yet always seems to be signaled with a VMS condition code of 4 (and
2401    correctly handled for that code).  So we hardwire it in.
2402 
2403    Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2404    number to see if it's valid.  So Perl_my_kill(pid,0) returns -1 rather
2405    than signalling with an unrecognized (and unhandled by CRTL) code.
2406 */
2407 
2408 #define _MY_SIG_MAX 28
2409 
2410 static unsigned int
2411 Perl_sig_to_vmscondition_int(int sig)
2412 {
2413     static unsigned int sig_code[_MY_SIG_MAX+1] =
2414     {
2415         0,                  /*  0 ZERO     */
2416         SS$_HANGUP,         /*  1 SIGHUP   */
2417         SS$_CONTROLC,       /*  2 SIGINT   */
2418         SS$_CONTROLY,       /*  3 SIGQUIT  */
2419         SS$_RADRMOD,        /*  4 SIGILL   */
2420         SS$_BREAK,          /*  5 SIGTRAP  */
2421         SS$_OPCCUS,         /*  6 SIGABRT  */
2422         SS$_COMPAT,         /*  7 SIGEMT   */
2423 #ifdef __VAX
2424         SS$_FLTOVF,         /*  8 SIGFPE VAX */
2425 #else
2426         SS$_HPARITH,        /*  8 SIGFPE AXP */
2427 #endif
2428         SS$_ABORT,          /*  9 SIGKILL  */
2429         SS$_ACCVIO,         /* 10 SIGBUS   */
2430         SS$_ACCVIO,         /* 11 SIGSEGV  */
2431         SS$_BADPARAM,       /* 12 SIGSYS   */
2432         SS$_NOMBX,          /* 13 SIGPIPE  */
2433         SS$_ASTFLT,         /* 14 SIGALRM  */
2434         4,                  /* 15 SIGTERM  */
2435         0,                  /* 16 SIGUSR1  */
2436         0,                  /* 17 SIGUSR2  */
2437         0,                  /* 18 */
2438         0,                  /* 19 */
2439         0,                  /* 20 SIGCHLD  */
2440         0,                  /* 21 SIGCONT  */
2441         0,                  /* 22 SIGSTOP  */
2442         0,                  /* 23 SIGTSTP  */
2443         0,                  /* 24 SIGTTIN  */
2444         0,                  /* 25 SIGTTOU  */
2445         0,                  /* 26 */
2446         0,                  /* 27 */
2447         0                   /* 28 SIGWINCH  */
2448     };
2449 
2450 #if __VMS_VER >= 60200000
2451     static int initted = 0;
2452     if (!initted) {
2453         initted = 1;
2454         sig_code[16] = C$_SIGUSR1;
2455         sig_code[17] = C$_SIGUSR2;
2456 #if __CRTL_VER >= 70000000
2457         sig_code[20] = C$_SIGCHLD;
2458 #endif
2459 #if __CRTL_VER >= 70300000
2460         sig_code[28] = C$_SIGWINCH;
2461 #endif
2462     }
2463 #endif
2464 
2465     if (sig < _SIG_MIN) return 0;
2466     if (sig > _MY_SIG_MAX) return 0;
2467     return sig_code[sig];
2468 }
2469 
2470 unsigned int
2471 Perl_sig_to_vmscondition(int sig)
2472 {
2473 #ifdef SS$_DEBUG
2474     if (vms_debug_on_exception != 0)
2475 	lib$signal(SS$_DEBUG);
2476 #endif
2477     return Perl_sig_to_vmscondition_int(sig);
2478 }
2479 
2480 
2481 int
2482 Perl_my_kill(int pid, int sig)
2483 {
2484     dTHX;
2485     int iss;
2486     unsigned int code;
2487     int sys$sigprc(unsigned int *pidadr,
2488                      struct dsc$descriptor_s *prcname,
2489                      unsigned int code);
2490 
2491      /* sig 0 means validate the PID */
2492     /*------------------------------*/
2493     if (sig == 0) {
2494 	const unsigned long int jpicode = JPI$_PID;
2495 	pid_t ret_pid;
2496 	int status;
2497         status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2498 	if ($VMS_STATUS_SUCCESS(status))
2499 	   return 0;
2500 	switch (status) {
2501         case SS$_NOSUCHNODE:
2502         case SS$_UNREACHABLE:
2503 	case SS$_NONEXPR:
2504 	   errno = ESRCH;
2505 	   break;
2506 	case SS$_NOPRIV:
2507 	   errno = EPERM;
2508 	   break;
2509 	default:
2510 	   errno = EVMSERR;
2511 	}
2512 	vaxc$errno=status;
2513 	return -1;
2514     }
2515 
2516     code = Perl_sig_to_vmscondition_int(sig);
2517 
2518     if (!code) {
2519 	SETERRNO(EINVAL, SS$_BADPARAM);
2520         return -1;
2521     }
2522 
2523     /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2524      * signals are to be sent to multiple processes.
2525      *  pid = 0 - all processes in group except ones that the system exempts
2526      *  pid = -1 - all processes except ones that the system exempts
2527      *  pid = -n - all processes in group (abs(n)) except ...
2528      * For now, just report as not supported.
2529      */
2530 
2531     if (pid <= 0) {
2532 	SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2533         return -1;
2534     }
2535 
2536     iss = sys$sigprc((unsigned int *)&pid,0,code);
2537     if (iss&1) return 0;
2538 
2539     switch (iss) {
2540       case SS$_NOPRIV:
2541         set_errno(EPERM);  break;
2542       case SS$_NONEXPR:
2543       case SS$_NOSUCHNODE:
2544       case SS$_UNREACHABLE:
2545         set_errno(ESRCH);  break;
2546       case SS$_INSFMEM:
2547         set_errno(ENOMEM); break;
2548       default:
2549         _ckvmssts_noperl(iss);
2550         set_errno(EVMSERR);
2551     }
2552     set_vaxc_errno(iss);
2553 
2554     return -1;
2555 }
2556 #endif
2557 
2558 /* Routine to convert a VMS status code to a UNIX status code.
2559 ** More tricky than it appears because of conflicting conventions with
2560 ** existing code.
2561 **
2562 ** VMS status codes are a bit mask, with the least significant bit set for
2563 ** success.
2564 **
2565 ** Special UNIX status of EVMSERR indicates that no translation is currently
2566 ** available, and programs should check the VMS status code.
2567 **
2568 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2569 ** decoding.
2570 */
2571 
2572 #ifndef C_FACILITY_NO
2573 #define C_FACILITY_NO 0x350000
2574 #endif
2575 #ifndef DCL_IVVERB
2576 #define DCL_IVVERB 0x38090
2577 #endif
2578 
2579 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2580 {
2581 int facility;
2582 int fac_sp;
2583 int msg_no;
2584 int msg_status;
2585 int unix_status;
2586 
2587   /* Assume the best or the worst */
2588   if (vms_status & STS$M_SUCCESS)
2589     unix_status = 0;
2590   else
2591     unix_status = EVMSERR;
2592 
2593   msg_status = vms_status & ~STS$M_CONTROL;
2594 
2595   facility = vms_status & STS$M_FAC_NO;
2596   fac_sp = vms_status & STS$M_FAC_SP;
2597   msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2598 
2599   if (((facility == 0) || (fac_sp == 0))  && (child_flag == 0)) {
2600     switch(msg_no) {
2601     case SS$_NORMAL:
2602 	unix_status = 0;
2603 	break;
2604     case SS$_ACCVIO:
2605 	unix_status = EFAULT;
2606 	break;
2607     case SS$_DEVOFFLINE:
2608 	unix_status = EBUSY;
2609 	break;
2610     case SS$_CLEARED:
2611 	unix_status = ENOTCONN;
2612 	break;
2613     case SS$_IVCHAN:
2614     case SS$_IVLOGNAM:
2615     case SS$_BADPARAM:
2616     case SS$_IVLOGTAB:
2617     case SS$_NOLOGNAM:
2618     case SS$_NOLOGTAB:
2619     case SS$_INVFILFOROP:
2620     case SS$_INVARG:
2621     case SS$_NOSUCHID:
2622     case SS$_IVIDENT:
2623 	unix_status = EINVAL;
2624 	break;
2625     case SS$_UNSUPPORTED:
2626 	unix_status = ENOTSUP;
2627 	break;
2628     case SS$_FILACCERR:
2629     case SS$_NOGRPPRV:
2630     case SS$_NOSYSPRV:
2631 	unix_status = EACCES;
2632 	break;
2633     case SS$_DEVICEFULL:
2634 	unix_status = ENOSPC;
2635 	break;
2636     case SS$_NOSUCHDEV:
2637 	unix_status = ENODEV;
2638 	break;
2639     case SS$_NOSUCHFILE:
2640     case SS$_NOSUCHOBJECT:
2641 	unix_status = ENOENT;
2642 	break;
2643     case SS$_ABORT:				    /* Fatal case */
2644     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2645     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2646 	unix_status = EINTR;
2647 	break;
2648     case SS$_BUFFEROVF:
2649 	unix_status = E2BIG;
2650 	break;
2651     case SS$_INSFMEM:
2652 	unix_status = ENOMEM;
2653 	break;
2654     case SS$_NOPRIV:
2655 	unix_status = EPERM;
2656 	break;
2657     case SS$_NOSUCHNODE:
2658     case SS$_UNREACHABLE:
2659 	unix_status = ESRCH;
2660 	break;
2661     case SS$_NONEXPR:
2662 	unix_status = ECHILD;
2663 	break;
2664     default:
2665 	if ((facility == 0) && (msg_no < 8)) {
2666 	  /* These are not real VMS status codes so assume that they are
2667           ** already UNIX status codes
2668 	  */
2669 	  unix_status = msg_no;
2670 	  break;
2671 	}
2672     }
2673   }
2674   else {
2675     /* Translate a POSIX exit code to a UNIX exit code */
2676     if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000))  {
2677 	unix_status = (msg_no & 0x07F8) >> 3;
2678     }
2679     else {
2680 
2681 	 /* Documented traditional behavior for handling VMS child exits */
2682 	/*--------------------------------------------------------------*/
2683 	if (child_flag != 0) {
2684 
2685 	     /* Success / Informational return 0 */
2686 	    /*----------------------------------*/
2687 	    if (msg_no & STS$K_SUCCESS)
2688 		return 0;
2689 
2690 	     /* Warning returns 1 */
2691 	    /*-------------------*/
2692 	    if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2693 	    	return 1;
2694 
2695 	     /* Everything else pass through the severity bits */
2696 	    /*------------------------------------------------*/
2697 	    return (msg_no & STS$M_SEVERITY);
2698 	}
2699 
2700 	 /* Normal VMS status to ERRNO mapping attempt */
2701 	/*--------------------------------------------*/
2702 	switch(msg_status) {
2703 	/* case RMS$_EOF: */ /* End of File */
2704 	case RMS$_FNF:	/* File Not Found */
2705 	case RMS$_DNF:	/* Dir Not Found */
2706 		unix_status = ENOENT;
2707 		break;
2708 	case RMS$_RNF:	/* Record Not Found */
2709 		unix_status = ESRCH;
2710 		break;
2711 	case RMS$_DIR:
2712 		unix_status = ENOTDIR;
2713 		break;
2714 	case RMS$_DEV:
2715 		unix_status = ENODEV;
2716 		break;
2717 	case RMS$_IFI:
2718 	case RMS$_FAC:
2719 	case RMS$_ISI:
2720 		unix_status = EBADF;
2721 		break;
2722 	case RMS$_FEX:
2723 		unix_status = EEXIST;
2724 		break;
2725 	case RMS$_SYN:
2726 	case RMS$_FNM:
2727 	case LIB$_INVSTRDES:
2728 	case LIB$_INVARG:
2729 	case LIB$_NOSUCHSYM:
2730 	case LIB$_INVSYMNAM:
2731 	case DCL_IVVERB:
2732 		unix_status = EINVAL;
2733 		break;
2734 	case CLI$_BUFOVF:
2735 	case RMS$_RTB:
2736 	case CLI$_TKNOVF:
2737 	case CLI$_RSLOVF:
2738 		unix_status = E2BIG;
2739 		break;
2740 	case RMS$_PRV:	/* No privilege */
2741 	case RMS$_ACC:	/* ACP file access failed */
2742 	case RMS$_WLK:	/* Device write locked */
2743 		unix_status = EACCES;
2744 		break;
2745 	case RMS$_MKD:  /* Failed to mark for delete */
2746 		unix_status = EPERM;
2747 		break;
2748 	/* case RMS$_NMF: */  /* No more files */
2749 	}
2750     }
2751   }
2752 
2753   return unix_status;
2754 }
2755 
2756 /* Try to guess at what VMS error status should go with a UNIX errno
2757  * value.  This is hard to do as there could be many possible VMS
2758  * error statuses that caused the errno value to be set.
2759  */
2760 
2761 int Perl_unix_status_to_vms(int unix_status)
2762 {
2763 int test_unix_status;
2764 
2765      /* Trivial cases first */
2766     /*---------------------*/
2767     if (unix_status == EVMSERR)
2768 	return vaxc$errno;
2769 
2770      /* Is vaxc$errno sane? */
2771     /*---------------------*/
2772     test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2773     if (test_unix_status == unix_status)
2774 	return vaxc$errno;
2775 
2776      /* If way out of range, must be VMS code already */
2777     /*-----------------------------------------------*/
2778     if (unix_status > EVMSERR)
2779 	return unix_status;
2780 
2781      /* If out of range, punt */
2782     /*-----------------------*/
2783     if (unix_status > __ERRNO_MAX)
2784 	return SS$_ABORT;
2785 
2786 
2787      /* Ok, now we have to do it the hard way. */
2788     /*----------------------------------------*/
2789     switch(unix_status) {
2790     case 0:	return SS$_NORMAL;
2791     case EPERM: return SS$_NOPRIV;
2792     case ENOENT: return SS$_NOSUCHOBJECT;
2793     case ESRCH: return SS$_UNREACHABLE;
2794     case EINTR: return SS$_ABORT;
2795     /* case EIO: */
2796     /* case ENXIO:  */
2797     case E2BIG: return SS$_BUFFEROVF;
2798     /* case ENOEXEC */
2799     case EBADF: return RMS$_IFI;
2800     case ECHILD: return SS$_NONEXPR;
2801     /* case EAGAIN */
2802     case ENOMEM: return SS$_INSFMEM;
2803     case EACCES: return SS$_FILACCERR;
2804     case EFAULT: return SS$_ACCVIO;
2805     /* case ENOTBLK */
2806     case EBUSY: return SS$_DEVOFFLINE;
2807     case EEXIST: return RMS$_FEX;
2808     /* case EXDEV */
2809     case ENODEV: return SS$_NOSUCHDEV;
2810     case ENOTDIR: return RMS$_DIR;
2811     /* case EISDIR */
2812     case EINVAL: return SS$_INVARG;
2813     /* case ENFILE */
2814     /* case EMFILE */
2815     /* case ENOTTY */
2816     /* case ETXTBSY */
2817     /* case EFBIG */
2818     case ENOSPC: return SS$_DEVICEFULL;
2819     case ESPIPE: return LIB$_INVARG;
2820     /* case EROFS: */
2821     /* case EMLINK: */
2822     /* case EPIPE: */
2823     /* case EDOM */
2824     case ERANGE: return LIB$_INVARG;
2825     /* case EWOULDBLOCK */
2826     /* case EINPROGRESS */
2827     /* case EALREADY */
2828     /* case ENOTSOCK */
2829     /* case EDESTADDRREQ */
2830     /* case EMSGSIZE */
2831     /* case EPROTOTYPE */
2832     /* case ENOPROTOOPT */
2833     /* case EPROTONOSUPPORT */
2834     /* case ESOCKTNOSUPPORT */
2835     /* case EOPNOTSUPP */
2836     /* case EPFNOSUPPORT */
2837     /* case EAFNOSUPPORT */
2838     /* case EADDRINUSE */
2839     /* case EADDRNOTAVAIL */
2840     /* case ENETDOWN */
2841     /* case ENETUNREACH */
2842     /* case ENETRESET */
2843     /* case ECONNABORTED */
2844     /* case ECONNRESET */
2845     /* case ENOBUFS */
2846     /* case EISCONN */
2847     case ENOTCONN: return SS$_CLEARED;
2848     /* case ESHUTDOWN */
2849     /* case ETOOMANYREFS */
2850     /* case ETIMEDOUT */
2851     /* case ECONNREFUSED */
2852     /* case ELOOP */
2853     /* case ENAMETOOLONG */
2854     /* case EHOSTDOWN */
2855     /* case EHOSTUNREACH */
2856     /* case ENOTEMPTY */
2857     /* case EPROCLIM */
2858     /* case EUSERS  */
2859     /* case EDQUOT  */
2860     /* case ENOMSG  */
2861     /* case EIDRM */
2862     /* case EALIGN */
2863     /* case ESTALE */
2864     /* case EREMOTE */
2865     /* case ENOLCK */
2866     /* case ENOSYS */
2867     /* case EFTYPE */
2868     /* case ECANCELED */
2869     /* case EFAIL */
2870     /* case EINPROG */
2871     case ENOTSUP:
2872 	return SS$_UNSUPPORTED;
2873     /* case EDEADLK */
2874     /* case ENWAIT */
2875     /* case EILSEQ */
2876     /* case EBADCAT */
2877     /* case EBADMSG */
2878     /* case EABANDONED */
2879     default:
2880 	return SS$_ABORT; /* punt */
2881     }
2882 
2883   return SS$_ABORT; /* Should not get here */
2884 }
2885 
2886 
2887 /* default piping mailbox size */
2888 #ifdef __VAX
2889 #  define PERL_BUFSIZ        512
2890 #else
2891 #  define PERL_BUFSIZ        8192
2892 #endif
2893 
2894 
2895 static void
2896 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2897 {
2898   unsigned long int mbxbufsiz;
2899   static unsigned long int syssize = 0;
2900   unsigned long int dviitm = DVI$_DEVNAM;
2901   char csize[LNM$C_NAMLENGTH+1];
2902   int sts;
2903 
2904   if (!syssize) {
2905     unsigned long syiitm = SYI$_MAXBUF;
2906     /*
2907      * Get the SYSGEN parameter MAXBUF
2908      *
2909      * If the logical 'PERL_MBX_SIZE' is defined
2910      * use the value of the logical instead of PERL_BUFSIZ, but
2911      * keep the size between 128 and MAXBUF.
2912      *
2913      */
2914     _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2915   }
2916 
2917   if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2918       mbxbufsiz = atoi(csize);
2919   } else {
2920       mbxbufsiz = PERL_BUFSIZ;
2921   }
2922   if (mbxbufsiz < 128) mbxbufsiz = 128;
2923   if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2924 
2925   _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2926 
2927   sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2928   _ckvmssts_noperl(sts);
2929   namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2930 
2931 }  /* end of create_mbx() */
2932 
2933 
2934 /*{{{  my_popen and my_pclose*/
2935 
2936 typedef struct _iosb           IOSB;
2937 typedef struct _iosb*         pIOSB;
2938 typedef struct _pipe           Pipe;
2939 typedef struct _pipe*         pPipe;
2940 typedef struct pipe_details    Info;
2941 typedef struct pipe_details*  pInfo;
2942 typedef struct _srqp            RQE;
2943 typedef struct _srqp*          pRQE;
2944 typedef struct _tochildbuf      CBuf;
2945 typedef struct _tochildbuf*    pCBuf;
2946 
2947 struct _iosb {
2948     unsigned short status;
2949     unsigned short count;
2950     unsigned long  dvispec;
2951 };
2952 
2953 #pragma member_alignment save
2954 #pragma nomember_alignment quadword
2955 struct _srqp {          /* VMS self-relative queue entry */
2956     unsigned long qptr[2];
2957 };
2958 #pragma member_alignment restore
2959 static RQE  RQE_ZERO = {0,0};
2960 
2961 struct _tochildbuf {
2962     RQE             q;
2963     int             eof;
2964     unsigned short  size;
2965     char            *buf;
2966 };
2967 
2968 struct _pipe {
2969     RQE            free;
2970     RQE            wait;
2971     int            fd_out;
2972     unsigned short chan_in;
2973     unsigned short chan_out;
2974     char          *buf;
2975     unsigned int   bufsize;
2976     IOSB           iosb;
2977     IOSB           iosb2;
2978     int           *pipe_done;
2979     int            retry;
2980     int            type;
2981     int            shut_on_empty;
2982     int            need_wake;
2983     pPipe         *home;
2984     pInfo          info;
2985     pCBuf          curr;
2986     pCBuf          curr2;
2987 #if defined(PERL_IMPLICIT_CONTEXT)
2988     void	    *thx;	    /* Either a thread or an interpreter */
2989                                     /* pointer, depending on how we're built */
2990 #endif
2991 };
2992 
2993 
2994 struct pipe_details
2995 {
2996     pInfo           next;
2997     PerlIO *fp;  /* file pointer to pipe mailbox */
2998     int useFILE; /* using stdio, not perlio */
2999     int pid;   /* PID of subprocess */
3000     int mode;  /* == 'r' if pipe open for reading */
3001     int done;  /* subprocess has completed */
3002     int waiting; /* waiting for completion/closure */
3003     int             closing;        /* my_pclose is closing this pipe */
3004     unsigned long   completion;     /* termination status of subprocess */
3005     pPipe           in;             /* pipe in to sub */
3006     pPipe           out;            /* pipe out of sub */
3007     pPipe           err;            /* pipe of sub's sys$error */
3008     int             in_done;        /* true when in pipe finished */
3009     int             out_done;
3010     int             err_done;
3011     unsigned short  xchan;	    /* channel to debug xterm */
3012     unsigned short  xchan_valid;    /* channel is assigned */
3013 };
3014 
3015 struct exit_control_block
3016 {
3017     struct exit_control_block *flink;
3018     unsigned long int	(*exit_routine)();
3019     unsigned long int arg_count;
3020     unsigned long int *status_address;
3021     unsigned long int exit_status;
3022 };
3023 
3024 typedef struct _closed_pipes    Xpipe;
3025 typedef struct _closed_pipes*  pXpipe;
3026 
3027 struct _closed_pipes {
3028     int             pid;            /* PID of subprocess */
3029     unsigned long   completion;     /* termination status of subprocess */
3030 };
3031 #define NKEEPCLOSED 50
3032 static Xpipe closed_list[NKEEPCLOSED];
3033 static int   closed_index = 0;
3034 static int   closed_num = 0;
3035 
3036 #define RETRY_DELAY     "0 ::0.20"
3037 #define MAX_RETRY              50
3038 
3039 static int pipe_ef = 0;          /* first call to safe_popen inits these*/
3040 static unsigned long mypid;
3041 static unsigned long delaytime[2];
3042 
3043 static pInfo open_pipes = NULL;
3044 static $DESCRIPTOR(nl_desc, "NL:");
3045 
3046 #define PIPE_COMPLETION_WAIT    30  /* seconds, for EOF/FORCEX wait */
3047 
3048 
3049 
3050 static unsigned long int
3051 pipe_exit_routine()
3052 {
3053     pInfo info;
3054     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3055     int sts, did_stuff, need_eof, j;
3056 
3057    /*
3058     * Flush any pending i/o, but since we are in process run-down, be
3059     * careful about referencing PerlIO structures that may already have
3060     * been deallocated.  We may not even have an interpreter anymore.
3061     */
3062     info = open_pipes;
3063     while (info) {
3064         if (info->fp) {
3065 #if defined(PERL_IMPLICIT_CONTEXT)
3066            /* We need to use the Perl context of the thread that created */
3067            /* the pipe. */
3068            pTHX;
3069            if (info->err)
3070                aTHX = info->err->thx;
3071            else if (info->out)
3072                aTHX = info->out->thx;
3073            else if (info->in)
3074                aTHX = info->in->thx;
3075 #endif
3076            if (!info->useFILE
3077 #if defined(USE_ITHREADS)
3078              && my_perl
3079 #endif
3080 #ifdef USE_PERLIO
3081              && PL_perlio_fd_refcnt
3082 #endif
3083               )
3084                PerlIO_flush(info->fp);
3085            else
3086                fflush((FILE *)info->fp);
3087         }
3088         info = info->next;
3089     }
3090 
3091     /*
3092      next we try sending an EOF...ignore if doesn't work, make sure we
3093      don't hang
3094     */
3095     did_stuff = 0;
3096     info = open_pipes;
3097 
3098     while (info) {
3099       int need_eof;
3100       _ckvmssts_noperl(sys$setast(0));
3101       if (info->in && !info->in->shut_on_empty) {
3102         _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3103                                  0, 0, 0, 0, 0, 0));
3104         info->waiting = 1;
3105         did_stuff = 1;
3106       }
3107       _ckvmssts_noperl(sys$setast(1));
3108       info = info->next;
3109     }
3110 
3111     /* wait for EOF to have effect, up to ~ 30 sec [default] */
3112 
3113     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3114         int nwait = 0;
3115 
3116         info = open_pipes;
3117         while (info) {
3118           _ckvmssts_noperl(sys$setast(0));
3119           if (info->waiting && info->done)
3120                 info->waiting = 0;
3121           nwait += info->waiting;
3122           _ckvmssts_noperl(sys$setast(1));
3123           info = info->next;
3124         }
3125         if (!nwait) break;
3126         sleep(1);
3127     }
3128 
3129     did_stuff = 0;
3130     info = open_pipes;
3131     while (info) {
3132       _ckvmssts_noperl(sys$setast(0));
3133       if (!info->done) { /* Tap them gently on the shoulder . . .*/
3134         sts = sys$forcex(&info->pid,0,&abort);
3135         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3136         did_stuff = 1;
3137       }
3138       _ckvmssts_noperl(sys$setast(1));
3139       info = info->next;
3140     }
3141 
3142     /* again, wait for effect */
3143 
3144     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3145         int nwait = 0;
3146 
3147         info = open_pipes;
3148         while (info) {
3149           _ckvmssts_noperl(sys$setast(0));
3150           if (info->waiting && info->done)
3151                 info->waiting = 0;
3152           nwait += info->waiting;
3153           _ckvmssts_noperl(sys$setast(1));
3154           info = info->next;
3155         }
3156         if (!nwait) break;
3157         sleep(1);
3158     }
3159 
3160     info = open_pipes;
3161     while (info) {
3162       _ckvmssts_noperl(sys$setast(0));
3163       if (!info->done) {  /* We tried to be nice . . . */
3164         sts = sys$delprc(&info->pid,0);
3165         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3166         info->done = 1;  /* sys$delprc is as done as we're going to get. */
3167       }
3168       _ckvmssts_noperl(sys$setast(1));
3169       info = info->next;
3170     }
3171 
3172     while(open_pipes) {
3173 
3174 #if defined(PERL_IMPLICIT_CONTEXT)
3175       /* We need to use the Perl context of the thread that created */
3176       /* the pipe. */
3177       pTHX;
3178       if (open_pipes->err)
3179           aTHX = open_pipes->err->thx;
3180       else if (open_pipes->out)
3181           aTHX = open_pipes->out->thx;
3182       else if (open_pipes->in)
3183           aTHX = open_pipes->in->thx;
3184 #endif
3185       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3186       else if (!(sts & 1)) retsts = sts;
3187     }
3188     return retsts;
3189 }
3190 
3191 static struct exit_control_block pipe_exitblock =
3192        {(struct exit_control_block *) 0,
3193         pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3194 
3195 static void pipe_mbxtofd_ast(pPipe p);
3196 static void pipe_tochild1_ast(pPipe p);
3197 static void pipe_tochild2_ast(pPipe p);
3198 
3199 static void
3200 popen_completion_ast(pInfo info)
3201 {
3202   pInfo i = open_pipes;
3203   int iss;
3204   int sts;
3205   pXpipe x;
3206 
3207   info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3208   closed_list[closed_index].pid = info->pid;
3209   closed_list[closed_index].completion = info->completion;
3210   closed_index++;
3211   if (closed_index == NKEEPCLOSED)
3212     closed_index = 0;
3213   closed_num++;
3214 
3215   while (i) {
3216     if (i == info) break;
3217     i = i->next;
3218   }
3219   if (!i) return;       /* unlinked, probably freed too */
3220 
3221   info->done = TRUE;
3222 
3223 /*
3224     Writing to subprocess ...
3225             if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3226 
3227             chan_out may be waiting for "done" flag, or hung waiting
3228             for i/o completion to child...cancel the i/o.  This will
3229             put it into "snarf mode" (done but no EOF yet) that discards
3230             input.
3231 
3232     Output from subprocess (stdout, stderr) needs to be flushed and
3233     shut down.   We try sending an EOF, but if the mbx is full the pipe
3234     routine should still catch the "shut_on_empty" flag, telling it to
3235     use immediate-style reads so that "mbx empty" -> EOF.
3236 
3237 
3238 */
3239   if (info->in && !info->in_done) {               /* only for mode=w */
3240         if (info->in->shut_on_empty && info->in->need_wake) {
3241             info->in->need_wake = FALSE;
3242             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3243         } else {
3244             _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3245         }
3246   }
3247 
3248   if (info->out && !info->out_done) {             /* were we also piping output? */
3249       info->out->shut_on_empty = TRUE;
3250       iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3251       if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3252       _ckvmssts_noperl(iss);
3253   }
3254 
3255   if (info->err && !info->err_done) {        /* we were piping stderr */
3256         info->err->shut_on_empty = TRUE;
3257         iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3258         if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3259         _ckvmssts_noperl(iss);
3260   }
3261   _ckvmssts_noperl(sys$setef(pipe_ef));
3262 
3263 }
3264 
3265 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3266 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3267 
3268 /*
3269     we actually differ from vmstrnenv since we use this to
3270     get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3271     are pointing to the same thing
3272 */
3273 
3274 static unsigned short
3275 popen_translate(pTHX_ char *logical, char *result)
3276 {
3277     int iss;
3278     $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3279     $DESCRIPTOR(d_log,"");
3280     struct _il3 {
3281         unsigned short length;
3282         unsigned short code;
3283         char *         buffer_addr;
3284         unsigned short *retlenaddr;
3285     } itmlst[2];
3286     unsigned short l, ifi;
3287 
3288     d_log.dsc$a_pointer = logical;
3289     d_log.dsc$w_length  = strlen(logical);
3290 
3291     itmlst[0].code = LNM$_STRING;
3292     itmlst[0].length = 255;
3293     itmlst[0].buffer_addr = result;
3294     itmlst[0].retlenaddr = &l;
3295 
3296     itmlst[1].code = 0;
3297     itmlst[1].length = 0;
3298     itmlst[1].buffer_addr = 0;
3299     itmlst[1].retlenaddr = 0;
3300 
3301     iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3302     if (iss == SS$_NOLOGNAM) {
3303         iss = SS$_NORMAL;
3304         l = 0;
3305     }
3306     if (!(iss&1)) lib$signal(iss);
3307     result[l] = '\0';
3308 /*
3309     logicals for PPFs have a 4 byte prefix  ESC+NUL+(RMS IFI)
3310     strip it off and return the ifi, if any
3311 */
3312     ifi  = 0;
3313     if (result[0] == 0x1b && result[1] == 0x00) {
3314         memmove(&ifi,result+2,2);
3315         strcpy(result,result+4);
3316     }
3317     return ifi;     /* this is the RMS internal file id */
3318 }
3319 
3320 static void pipe_infromchild_ast(pPipe p);
3321 
3322 /*
3323     I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3324     inside an AST routine without worrying about reentrancy and which Perl
3325     memory allocator is being used.
3326 
3327     We read data and queue up the buffers, then spit them out one at a
3328     time to the output mailbox when the output mailbox is ready for one.
3329 
3330 */
3331 #define INITIAL_TOCHILDQUEUE  2
3332 
3333 static pPipe
3334 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3335 {
3336     pPipe p;
3337     pCBuf b;
3338     char mbx1[64], mbx2[64];
3339     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3340                                       DSC$K_CLASS_S, mbx1},
3341                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3342                                       DSC$K_CLASS_S, mbx2};
3343     unsigned int dviitm = DVI$_DEVBUFSIZ;
3344     int j, n;
3345 
3346     n = sizeof(Pipe);
3347     _ckvmssts_noperl(lib$get_vm(&n, &p));
3348 
3349     create_mbx(&p->chan_in , &d_mbx1);
3350     create_mbx(&p->chan_out, &d_mbx2);
3351     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3352 
3353     p->buf           = 0;
3354     p->shut_on_empty = FALSE;
3355     p->need_wake     = FALSE;
3356     p->type          = 0;
3357     p->retry         = 0;
3358     p->iosb.status   = SS$_NORMAL;
3359     p->iosb2.status  = SS$_NORMAL;
3360     p->free          = RQE_ZERO;
3361     p->wait          = RQE_ZERO;
3362     p->curr          = 0;
3363     p->curr2         = 0;
3364     p->info          = 0;
3365 #ifdef PERL_IMPLICIT_CONTEXT
3366     p->thx	     = aTHX;
3367 #endif
3368 
3369     n = sizeof(CBuf) + p->bufsize;
3370 
3371     for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3372         _ckvmssts_noperl(lib$get_vm(&n, &b));
3373         b->buf = (char *) b + sizeof(CBuf);
3374         _ckvmssts_noperl(lib$insqhi(b, &p->free));
3375     }
3376 
3377     pipe_tochild2_ast(p);
3378     pipe_tochild1_ast(p);
3379     strcpy(wmbx, mbx1);
3380     strcpy(rmbx, mbx2);
3381     return p;
3382 }
3383 
3384 /*  reads the MBX Perl is writing, and queues */
3385 
3386 static void
3387 pipe_tochild1_ast(pPipe p)
3388 {
3389     pCBuf b = p->curr;
3390     int iss = p->iosb.status;
3391     int eof = (iss == SS$_ENDOFFILE);
3392     int sts;
3393 #ifdef PERL_IMPLICIT_CONTEXT
3394     pTHX = p->thx;
3395 #endif
3396 
3397     if (p->retry) {
3398         if (eof) {
3399             p->shut_on_empty = TRUE;
3400             b->eof     = TRUE;
3401             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3402         } else  {
3403             _ckvmssts_noperl(iss);
3404         }
3405 
3406         b->eof  = eof;
3407         b->size = p->iosb.count;
3408         _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3409         if (p->need_wake) {
3410             p->need_wake = FALSE;
3411             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3412         }
3413     } else {
3414         p->retry = 1;   /* initial call */
3415     }
3416 
3417     if (eof) {                  /* flush the free queue, return when done */
3418         int n = sizeof(CBuf) + p->bufsize;
3419         while (1) {
3420             iss = lib$remqti(&p->free, &b);
3421             if (iss == LIB$_QUEWASEMP) return;
3422             _ckvmssts_noperl(iss);
3423             _ckvmssts_noperl(lib$free_vm(&n, &b));
3424         }
3425     }
3426 
3427     iss = lib$remqti(&p->free, &b);
3428     if (iss == LIB$_QUEWASEMP) {
3429         int n = sizeof(CBuf) + p->bufsize;
3430         _ckvmssts_noperl(lib$get_vm(&n, &b));
3431         b->buf = (char *) b + sizeof(CBuf);
3432     } else {
3433        _ckvmssts_noperl(iss);
3434     }
3435 
3436     p->curr = b;
3437     iss = sys$qio(0,p->chan_in,
3438              IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3439              &p->iosb,
3440              pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3441     if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3442     _ckvmssts_noperl(iss);
3443 }
3444 
3445 
3446 /* writes queued buffers to output, waits for each to complete before
3447    doing the next */
3448 
3449 static void
3450 pipe_tochild2_ast(pPipe p)
3451 {
3452     pCBuf b = p->curr2;
3453     int iss = p->iosb2.status;
3454     int n = sizeof(CBuf) + p->bufsize;
3455     int done = (p->info && p->info->done) ||
3456               iss == SS$_CANCEL || iss == SS$_ABORT;
3457 #if defined(PERL_IMPLICIT_CONTEXT)
3458     pTHX = p->thx;
3459 #endif
3460 
3461     do {
3462         if (p->type) {         /* type=1 has old buffer, dispose */
3463             if (p->shut_on_empty) {
3464                 _ckvmssts_noperl(lib$free_vm(&n, &b));
3465             } else {
3466                 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3467             }
3468             p->type = 0;
3469         }
3470 
3471         iss = lib$remqti(&p->wait, &b);
3472         if (iss == LIB$_QUEWASEMP) {
3473             if (p->shut_on_empty) {
3474                 if (done) {
3475                     _ckvmssts_noperl(sys$dassgn(p->chan_out));
3476                     *p->pipe_done = TRUE;
3477                     _ckvmssts_noperl(sys$setef(pipe_ef));
3478                 } else {
3479                     _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3480                         &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3481                 }
3482                 return;
3483             }
3484             p->need_wake = TRUE;
3485             return;
3486         }
3487         _ckvmssts_noperl(iss);
3488         p->type = 1;
3489     } while (done);
3490 
3491 
3492     p->curr2 = b;
3493     if (b->eof) {
3494         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3495             &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3496     } else {
3497         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3498             &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3499     }
3500 
3501     return;
3502 
3503 }
3504 
3505 
3506 static pPipe
3507 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3508 {
3509     pPipe p;
3510     char mbx1[64], mbx2[64];
3511     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3512                                       DSC$K_CLASS_S, mbx1},
3513                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3514                                       DSC$K_CLASS_S, mbx2};
3515     unsigned int dviitm = DVI$_DEVBUFSIZ;
3516 
3517     int n = sizeof(Pipe);
3518     _ckvmssts_noperl(lib$get_vm(&n, &p));
3519     create_mbx(&p->chan_in , &d_mbx1);
3520     create_mbx(&p->chan_out, &d_mbx2);
3521 
3522     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3523     n = p->bufsize * sizeof(char);
3524     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3525     p->shut_on_empty = FALSE;
3526     p->info   = 0;
3527     p->type   = 0;
3528     p->iosb.status = SS$_NORMAL;
3529 #if defined(PERL_IMPLICIT_CONTEXT)
3530     p->thx = aTHX;
3531 #endif
3532     pipe_infromchild_ast(p);
3533 
3534     strcpy(wmbx, mbx1);
3535     strcpy(rmbx, mbx2);
3536     return p;
3537 }
3538 
3539 static void
3540 pipe_infromchild_ast(pPipe p)
3541 {
3542     int iss = p->iosb.status;
3543     int eof = (iss == SS$_ENDOFFILE);
3544     int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3545     int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3546 #if defined(PERL_IMPLICIT_CONTEXT)
3547     pTHX = p->thx;
3548 #endif
3549 
3550     if (p->info && p->info->closing && p->chan_out)  {           /* output shutdown */
3551         _ckvmssts_noperl(sys$dassgn(p->chan_out));
3552         p->chan_out = 0;
3553     }
3554 
3555     /* read completed:
3556             input shutdown if EOF from self (done or shut_on_empty)
3557             output shutdown if closing flag set (my_pclose)
3558             send data/eof from child or eof from self
3559             otherwise, re-read (snarf of data from child)
3560     */
3561 
3562     if (p->type == 1) {
3563         p->type = 0;
3564         if (myeof && p->chan_in) {                  /* input shutdown */
3565             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3566             p->chan_in = 0;
3567         }
3568 
3569         if (p->chan_out) {
3570             if (myeof || kideof) {      /* pass EOF to parent */
3571                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3572                                          pipe_infromchild_ast, p,
3573                                          0, 0, 0, 0, 0, 0));
3574                 return;
3575             } else if (eof) {       /* eat EOF --- fall through to read*/
3576 
3577             } else {                /* transmit data */
3578                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3579                                          pipe_infromchild_ast,p,
3580                                          p->buf, p->iosb.count, 0, 0, 0, 0));
3581                 return;
3582             }
3583         }
3584     }
3585 
3586     /*  everything shut? flag as done */
3587 
3588     if (!p->chan_in && !p->chan_out) {
3589         *p->pipe_done = TRUE;
3590         _ckvmssts_noperl(sys$setef(pipe_ef));
3591         return;
3592     }
3593 
3594     /* write completed (or read, if snarfing from child)
3595             if still have input active,
3596                queue read...immediate mode if shut_on_empty so we get EOF if empty
3597             otherwise,
3598                check if Perl reading, generate EOFs as needed
3599     */
3600 
3601     if (p->type == 0) {
3602         p->type = 1;
3603         if (p->chan_in) {
3604             iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3605                           pipe_infromchild_ast,p,
3606                           p->buf, p->bufsize, 0, 0, 0, 0);
3607             if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3608             _ckvmssts_noperl(iss);
3609         } else {           /* send EOFs for extra reads */
3610             p->iosb.status = SS$_ENDOFFILE;
3611             p->iosb.dvispec = 0;
3612             _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3613                                      0, 0, 0,
3614                                      pipe_infromchild_ast, p, 0, 0, 0, 0));
3615         }
3616     }
3617 }
3618 
3619 static pPipe
3620 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3621 {
3622     pPipe p;
3623     char mbx[64];
3624     unsigned long dviitm = DVI$_DEVBUFSIZ;
3625     struct stat s;
3626     struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3627                                       DSC$K_CLASS_S, mbx};
3628     int n = sizeof(Pipe);
3629 
3630     /* things like terminals and mbx's don't need this filter */
3631     if (fd && fstat(fd,&s) == 0) {
3632         unsigned long dviitm = DVI$_DEVCHAR, devchar;
3633 	char device[65];
3634 	unsigned short dev_len;
3635 	struct dsc$descriptor_s d_dev;
3636 	char * cptr;
3637 	struct item_list_3 items[3];
3638 	int status;
3639 	unsigned short dvi_iosb[4];
3640 
3641 	cptr = getname(fd, out, 1);
3642 	if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3643 	d_dev.dsc$a_pointer = out;
3644 	d_dev.dsc$w_length = strlen(out);
3645 	d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3646 	d_dev.dsc$b_class = DSC$K_CLASS_S;
3647 
3648 	items[0].len = 4;
3649 	items[0].code = DVI$_DEVCHAR;
3650 	items[0].bufadr = &devchar;
3651 	items[0].retadr = NULL;
3652 	items[1].len = 64;
3653 	items[1].code = DVI$_FULLDEVNAM;
3654 	items[1].bufadr = device;
3655 	items[1].retadr = &dev_len;
3656 	items[2].len = 0;
3657 	items[2].code = 0;
3658 
3659 	status = sys$getdviw
3660 	        (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3661 	_ckvmssts_noperl(status);
3662 	if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3663 	    device[dev_len] = 0;
3664 
3665 	    if (!(devchar & DEV$M_DIR)) {
3666 		strcpy(out, device);
3667 		return 0;
3668 	    }
3669 	}
3670     }
3671 
3672     _ckvmssts_noperl(lib$get_vm(&n, &p));
3673     p->fd_out = dup(fd);
3674     create_mbx(&p->chan_in, &d_mbx);
3675     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3676     n = (p->bufsize+1) * sizeof(char);
3677     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3678     p->shut_on_empty = FALSE;
3679     p->retry = 0;
3680     p->info  = 0;
3681     strcpy(out, mbx);
3682 
3683     _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3684                              pipe_mbxtofd_ast, p,
3685                              p->buf, p->bufsize, 0, 0, 0, 0));
3686 
3687     return p;
3688 }
3689 
3690 static void
3691 pipe_mbxtofd_ast(pPipe p)
3692 {
3693     int iss = p->iosb.status;
3694     int done = p->info->done;
3695     int iss2;
3696     int eof = (iss == SS$_ENDOFFILE);
3697     int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3698     int err = !(iss&1) && !eof;
3699 #if defined(PERL_IMPLICIT_CONTEXT)
3700     pTHX = p->thx;
3701 #endif
3702 
3703     if (done && myeof) {               /* end piping */
3704         close(p->fd_out);
3705         sys$dassgn(p->chan_in);
3706         *p->pipe_done = TRUE;
3707         _ckvmssts_noperl(sys$setef(pipe_ef));
3708         return;
3709     }
3710 
3711     if (!err && !eof) {             /* good data to send to file */
3712         p->buf[p->iosb.count] = '\n';
3713         iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3714         if (iss2 < 0) {
3715             p->retry++;
3716             if (p->retry < MAX_RETRY) {
3717                 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3718                 return;
3719             }
3720         }
3721         p->retry = 0;
3722     } else if (err) {
3723         _ckvmssts_noperl(iss);
3724     }
3725 
3726 
3727     iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3728           pipe_mbxtofd_ast, p,
3729           p->buf, p->bufsize, 0, 0, 0, 0);
3730     if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3731     _ckvmssts_noperl(iss);
3732 }
3733 
3734 
3735 typedef struct _pipeloc     PLOC;
3736 typedef struct _pipeloc*   pPLOC;
3737 
3738 struct _pipeloc {
3739     pPLOC   next;
3740     char    dir[NAM$C_MAXRSS+1];
3741 };
3742 static pPLOC  head_PLOC = 0;
3743 
3744 void
3745 free_pipelocs(pTHX_ void *head)
3746 {
3747     pPLOC p, pnext;
3748     pPLOC *pHead = (pPLOC *)head;
3749 
3750     p = *pHead;
3751     while (p) {
3752         pnext = p->next;
3753         PerlMem_free(p);
3754         p = pnext;
3755     }
3756     *pHead = 0;
3757 }
3758 
3759 static void
3760 store_pipelocs(pTHX)
3761 {
3762     int    i;
3763     pPLOC  p;
3764     AV    *av = 0;
3765     SV    *dirsv;
3766     GV    *gv;
3767     char  *dir, *x;
3768     char  *unixdir;
3769     char  temp[NAM$C_MAXRSS+1];
3770     STRLEN n_a;
3771 
3772     if (head_PLOC)
3773         free_pipelocs(aTHX_ &head_PLOC);
3774 
3775 /*  the . directory from @INC comes last */
3776 
3777     p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3778     if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3779     p->next = head_PLOC;
3780     head_PLOC = p;
3781     strcpy(p->dir,"./");
3782 
3783 /*  get the directory from $^X */
3784 
3785     unixdir = PerlMem_malloc(VMS_MAXRSS);
3786     if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3787 
3788 #ifdef PERL_IMPLICIT_CONTEXT
3789     if (aTHX && PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3790 #else
3791     if (PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3792 #endif
3793         strcpy(temp, PL_origargv[0]);
3794         x = strrchr(temp,']');
3795 	if (x == NULL) {
3796 	x = strrchr(temp,'>');
3797 	  if (x == NULL) {
3798 	    /* It could be a UNIX path */
3799 	    x = strrchr(temp,'/');
3800 	  }
3801 	}
3802 	if (x)
3803 	  x[1] = '\0';
3804 	else {
3805 	  /* Got a bare name, so use default directory */
3806 	  temp[0] = '.';
3807 	  temp[1] = '\0';
3808 	}
3809 
3810         if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3811             p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3812 	    if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3813             p->next = head_PLOC;
3814             head_PLOC = p;
3815             strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3816             p->dir[NAM$C_MAXRSS] = '\0';
3817 	}
3818     }
3819 
3820 /*  reverse order of @INC entries, skip "." since entered above */
3821 
3822 #ifdef PERL_IMPLICIT_CONTEXT
3823     if (aTHX)
3824 #endif
3825     if (PL_incgv) av = GvAVn(PL_incgv);
3826 
3827     for (i = 0; av && i <= AvFILL(av); i++) {
3828         dirsv = *av_fetch(av,i,TRUE);
3829 
3830         if (SvROK(dirsv)) continue;
3831         dir = SvPVx(dirsv,n_a);
3832         if (strcmp(dir,".") == 0) continue;
3833         if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3834             continue;
3835 
3836         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3837         p->next = head_PLOC;
3838         head_PLOC = p;
3839         strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3840         p->dir[NAM$C_MAXRSS] = '\0';
3841     }
3842 
3843 /* most likely spot (ARCHLIB) put first in the list */
3844 
3845 #ifdef ARCHLIB_EXP
3846     if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3847         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3848 	if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3849         p->next = head_PLOC;
3850         head_PLOC = p;
3851         strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3852         p->dir[NAM$C_MAXRSS] = '\0';
3853     }
3854 #endif
3855     PerlMem_free(unixdir);
3856 }
3857 
3858 static I32
3859 Perl_cando_by_name_int
3860    (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3861 #if !defined(PERL_IMPLICIT_CONTEXT)
3862 #define cando_by_name_int		Perl_cando_by_name_int
3863 #else
3864 #define cando_by_name_int(a,b,c,d)	Perl_cando_by_name_int(aTHX_ a,b,c,d)
3865 #endif
3866 
3867 static char *
3868 find_vmspipe(pTHX)
3869 {
3870     static int   vmspipe_file_status = 0;
3871     static char  vmspipe_file[NAM$C_MAXRSS+1];
3872 
3873     /* already found? Check and use ... need read+execute permission */
3874 
3875     if (vmspipe_file_status == 1) {
3876         if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3877          && cando_by_name_int
3878 	   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3879             return vmspipe_file;
3880         }
3881         vmspipe_file_status = 0;
3882     }
3883 
3884     /* scan through stored @INC, $^X */
3885 
3886     if (vmspipe_file_status == 0) {
3887         char file[NAM$C_MAXRSS+1];
3888         pPLOC  p = head_PLOC;
3889 
3890         while (p) {
3891 	    char * exp_res;
3892 	    int dirlen;
3893             strcpy(file, p->dir);
3894 	    dirlen = strlen(file);
3895             strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3896             file[NAM$C_MAXRSS] = '\0';
3897             p = p->next;
3898 
3899             exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3900             if (!exp_res) continue;
3901 
3902             if (cando_by_name_int
3903 		(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3904              && cando_by_name_int
3905 		   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3906                 vmspipe_file_status = 1;
3907                 return vmspipe_file;
3908             }
3909         }
3910         vmspipe_file_status = -1;   /* failed, use tempfiles */
3911     }
3912 
3913     return 0;
3914 }
3915 
3916 static FILE *
3917 vmspipe_tempfile(pTHX)
3918 {
3919     char file[NAM$C_MAXRSS+1];
3920     FILE *fp;
3921     static int index = 0;
3922     Stat_t s0, s1;
3923     int cmp_result;
3924 
3925     /* create a tempfile */
3926 
3927     /* we can't go from   W, shr=get to  R, shr=get without
3928        an intermediate vulnerable state, so don't bother trying...
3929 
3930        and lib$spawn doesn't shr=put, so have to close the write
3931 
3932        So... match up the creation date/time and the FID to
3933        make sure we're dealing with the same file
3934 
3935     */
3936 
3937     index++;
3938     if (!decc_filename_unix_only) {
3939       sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3940       fp = fopen(file,"w");
3941       if (!fp) {
3942         sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3943         fp = fopen(file,"w");
3944         if (!fp) {
3945             sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3946             fp = fopen(file,"w");
3947 	}
3948       }
3949      }
3950      else {
3951       sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3952       fp = fopen(file,"w");
3953       if (!fp) {
3954 	sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3955 	fp = fopen(file,"w");
3956 	if (!fp) {
3957 	  sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3958 	  fp = fopen(file,"w");
3959 	}
3960       }
3961     }
3962     if (!fp) return 0;  /* we're hosed */
3963 
3964     fprintf(fp,"$! 'f$verify(0)'\n");
3965     fprintf(fp,"$!  ---  protect against nonstandard definitions ---\n");
3966     fprintf(fp,"$ perl_cfile  = f$environment(\"procedure\")\n");
3967     fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3968     fprintf(fp,"$ perl_on     = \"set noon\"\n");
3969     fprintf(fp,"$ perl_exit   = \"exit\"\n");
3970     fprintf(fp,"$ perl_del    = \"delete\"\n");
3971     fprintf(fp,"$ pif         = \"if\"\n");
3972     fprintf(fp,"$!  --- define i/o redirection (sys$output set by lib$spawn)\n");
3973     fprintf(fp,"$ pif perl_popen_in  .nes. \"\" then perl_define/user/name_attributes=confine sys$input  'perl_popen_in'\n");
3974     fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error  'perl_popen_err'\n");
3975     fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define      sys$output 'perl_popen_out'\n");
3976     fprintf(fp,"$!  --- build command line to get max possible length\n");
3977     fprintf(fp,"$c=perl_popen_cmd0\n");
3978     fprintf(fp,"$c=c+perl_popen_cmd1\n");
3979     fprintf(fp,"$c=c+perl_popen_cmd2\n");
3980     fprintf(fp,"$x=perl_popen_cmd3\n");
3981     fprintf(fp,"$c=c+x\n");
3982     fprintf(fp,"$ perl_on\n");
3983     fprintf(fp,"$ 'c'\n");
3984     fprintf(fp,"$ perl_status = $STATUS\n");
3985     fprintf(fp,"$ perl_del  'perl_cfile'\n");
3986     fprintf(fp,"$ perl_exit 'perl_status'\n");
3987     fsync(fileno(fp));
3988 
3989     fgetname(fp, file, 1);
3990     fstat(fileno(fp), &s0.crtl_stat);
3991     fclose(fp);
3992 
3993     if (decc_filename_unix_only)
3994 	int_tounixspec(file, file, NULL);
3995     fp = fopen(file,"r","shr=get");
3996     if (!fp) return 0;
3997     fstat(fileno(fp), &s1.crtl_stat);
3998 
3999     cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
4000     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
4001         fclose(fp);
4002         return 0;
4003     }
4004 
4005     return fp;
4006 }
4007 
4008 
4009 static int vms_is_syscommand_xterm(void)
4010 {
4011     const static struct dsc$descriptor_s syscommand_dsc =
4012       { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
4013 
4014     const static struct dsc$descriptor_s decwdisplay_dsc =
4015       { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
4016 
4017     struct item_list_3 items[2];
4018     unsigned short dvi_iosb[4];
4019     unsigned long devchar;
4020     unsigned long devclass;
4021     int status;
4022 
4023     /* Very simple check to guess if sys$command is a decterm? */
4024     /* First see if the DECW$DISPLAY: device exists */
4025     items[0].len = 4;
4026     items[0].code = DVI$_DEVCHAR;
4027     items[0].bufadr = &devchar;
4028     items[0].retadr = NULL;
4029     items[1].len = 0;
4030     items[1].code = 0;
4031 
4032     status = sys$getdviw
4033 	(NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
4034 
4035     if ($VMS_STATUS_SUCCESS(status)) {
4036         status = dvi_iosb[0];
4037     }
4038 
4039     if (!$VMS_STATUS_SUCCESS(status)) {
4040         SETERRNO(EVMSERR, status);
4041 	return -1;
4042     }
4043 
4044     /* If it does, then for now assume that we are on a workstation */
4045     /* Now verify that SYS$COMMAND is a terminal */
4046     /* for creating the debugger DECTerm */
4047 
4048     items[0].len = 4;
4049     items[0].code = DVI$_DEVCLASS;
4050     items[0].bufadr = &devclass;
4051     items[0].retadr = NULL;
4052     items[1].len = 0;
4053     items[1].code = 0;
4054 
4055     status = sys$getdviw
4056 	(NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
4057 
4058     if ($VMS_STATUS_SUCCESS(status)) {
4059         status = dvi_iosb[0];
4060     }
4061 
4062     if (!$VMS_STATUS_SUCCESS(status)) {
4063         SETERRNO(EVMSERR, status);
4064 	return -1;
4065     }
4066     else {
4067 	if (devclass == DC$_TERM) {
4068 	    return 0;
4069 	}
4070     }
4071     return -1;
4072 }
4073 
4074 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
4075 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
4076 {
4077     int status;
4078     int ret_stat;
4079     char * ret_char;
4080     char device_name[65];
4081     unsigned short device_name_len;
4082     struct dsc$descriptor_s customization_dsc;
4083     struct dsc$descriptor_s device_name_dsc;
4084     const char * cptr;
4085     char * tptr;
4086     char customization[200];
4087     char title[40];
4088     pInfo info = NULL;
4089     char mbx1[64];
4090     unsigned short p_chan;
4091     int n;
4092     unsigned short iosb[4];
4093     struct item_list_3 items[2];
4094     const char * cust_str =
4095         "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4096     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4097                                           DSC$K_CLASS_S, mbx1};
4098 
4099      /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4100     /*---------------------------------------*/
4101     VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4102 
4103 
4104     /* Make sure that this is from the Perl debugger */
4105     ret_char = strstr(cmd," xterm ");
4106     if (ret_char == NULL)
4107 	return NULL;
4108     cptr = ret_char + 7;
4109     ret_char = strstr(cmd,"tty");
4110     if (ret_char == NULL)
4111 	return NULL;
4112     ret_char = strstr(cmd,"sleep");
4113     if (ret_char == NULL)
4114 	return NULL;
4115 
4116     if (decw_term_port == 0) {
4117 	$DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4118 	$DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4119 	$DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4120 
4121        status = lib$find_image_symbol
4122 			       (&filename1_dsc,
4123 				&decw_term_port_dsc,
4124 				(void *)&decw_term_port,
4125 				NULL,
4126 				0);
4127 
4128 	/* Try again with the other image name */
4129 	if (!$VMS_STATUS_SUCCESS(status)) {
4130 
4131            status = lib$find_image_symbol
4132 			       (&filename2_dsc,
4133 				&decw_term_port_dsc,
4134 				(void *)&decw_term_port,
4135 				NULL,
4136 				0);
4137 
4138 	}
4139 
4140     }
4141 
4142 
4143     /* No decw$term_port, give it up */
4144     if (!$VMS_STATUS_SUCCESS(status))
4145 	return NULL;
4146 
4147     /* Are we on a workstation? */
4148     /* to do: capture the rows / columns and pass their properties */
4149     ret_stat = vms_is_syscommand_xterm();
4150     if (ret_stat < 0)
4151 	return NULL;
4152 
4153     /* Make the title: */
4154     ret_char = strstr(cptr,"-title");
4155     if (ret_char != NULL) {
4156 	while ((*cptr != 0) && (*cptr != '\"')) {
4157 	    cptr++;
4158 	}
4159 	if (*cptr == '\"')
4160 	    cptr++;
4161 	n = 0;
4162 	while ((*cptr != 0) && (*cptr != '\"')) {
4163 	    title[n] = *cptr;
4164 	    n++;
4165 	    if (n == 39) {
4166 		title[39] == 0;
4167 		break;
4168 	    }
4169 	    cptr++;
4170 	}
4171 	title[n] = 0;
4172     }
4173     else {
4174 	    /* Default title */
4175 	    strcpy(title,"Perl Debug DECTerm");
4176     }
4177     sprintf(customization, cust_str, title);
4178 
4179     customization_dsc.dsc$a_pointer = customization;
4180     customization_dsc.dsc$w_length = strlen(customization);
4181     customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4182     customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4183 
4184     device_name_dsc.dsc$a_pointer = device_name;
4185     device_name_dsc.dsc$w_length = sizeof device_name -1;
4186     device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4187     device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4188 
4189     device_name_len = 0;
4190 
4191     /* Try to create the window */
4192      status = (*decw_term_port)
4193        (NULL,
4194 	NULL,
4195 	&customization_dsc,
4196 	&device_name_dsc,
4197 	&device_name_len,
4198 	NULL,
4199 	NULL,
4200 	NULL);
4201     if (!$VMS_STATUS_SUCCESS(status)) {
4202         SETERRNO(EVMSERR, status);
4203 	return NULL;
4204     }
4205 
4206     device_name[device_name_len] = '\0';
4207 
4208     /* Need to set this up to look like a pipe for cleanup */
4209     n = sizeof(Info);
4210     status = lib$get_vm(&n, &info);
4211     if (!$VMS_STATUS_SUCCESS(status)) {
4212         SETERRNO(ENOMEM, status);
4213         return NULL;
4214     }
4215 
4216     info->mode = *mode;
4217     info->done = FALSE;
4218     info->completion = 0;
4219     info->closing    = FALSE;
4220     info->in         = 0;
4221     info->out        = 0;
4222     info->err        = 0;
4223     info->fp         = NULL;
4224     info->useFILE    = 0;
4225     info->waiting    = 0;
4226     info->in_done    = TRUE;
4227     info->out_done   = TRUE;
4228     info->err_done   = TRUE;
4229 
4230     /* Assign a channel on this so that it will persist, and not login */
4231     /* We stash this channel in the info structure for reference. */
4232     /* The created xterm self destructs when the last channel is removed */
4233     /* and it appears that perl5db.pl (perl debugger) does this routinely */
4234     /* So leave this assigned. */
4235     device_name_dsc.dsc$w_length = device_name_len;
4236     status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4237     if (!$VMS_STATUS_SUCCESS(status)) {
4238         SETERRNO(EVMSERR, status);
4239 	return NULL;
4240     }
4241     info->xchan_valid = 1;
4242 
4243     /* Now create a mailbox to be read by the application */
4244 
4245     create_mbx(&p_chan, &d_mbx1);
4246 
4247     /* write the name of the created terminal to the mailbox */
4248     status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4249             iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4250 
4251     if (!$VMS_STATUS_SUCCESS(status)) {
4252         SETERRNO(EVMSERR, status);
4253 	return NULL;
4254     }
4255 
4256     info->fp  = PerlIO_open(mbx1, mode);
4257 
4258     /* Done with this channel */
4259     sys$dassgn(p_chan);
4260 
4261     /* If any errors, then clean up */
4262     if (!info->fp) {
4263        	n = sizeof(Info);
4264 	_ckvmssts_noperl(lib$free_vm(&n, &info));
4265 	return NULL;
4266         }
4267 
4268     /* All done */
4269     return info->fp;
4270 }
4271 
4272 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4273 
4274 static PerlIO *
4275 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4276 {
4277     static int handler_set_up = FALSE;
4278     PerlIO * ret_fp;
4279     unsigned long int sts, flags = CLI$M_NOWAIT;
4280     /* The use of a GLOBAL table (as was done previously) rendered
4281      * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4282      * environment.  Hence we've switched to LOCAL symbol table.
4283      */
4284     unsigned int table = LIB$K_CLI_LOCAL_SYM;
4285     int j, wait = 0, n;
4286     char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4287     char *in, *out, *err, mbx[512];
4288     FILE *tpipe = 0;
4289     char tfilebuf[NAM$C_MAXRSS+1];
4290     pInfo info = NULL;
4291     char cmd_sym_name[20];
4292     struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4293                                       DSC$K_CLASS_S, symbol};
4294     struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4295                                       DSC$K_CLASS_S, 0};
4296     struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4297                                       DSC$K_CLASS_S, cmd_sym_name};
4298     struct dsc$descriptor_s *vmscmd;
4299     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4300     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4301     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4302 
4303     /* Check here for Xterm create request.  This means looking for
4304      * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4305      *  is possible to create an xterm.
4306      */
4307     if (*in_mode == 'r') {
4308         PerlIO * xterm_fd;
4309 
4310 #if defined(PERL_IMPLICIT_CONTEXT)
4311         /* Can not fork an xterm with a NULL context */
4312         /* This probably could never happen */
4313         xterm_fd = NULL;
4314         if (aTHX != NULL)
4315 #endif
4316 	xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4317 	if (xterm_fd != NULL)
4318 	    return xterm_fd;
4319     }
4320 
4321     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static vmspipe file */
4322 
4323     /* once-per-program initialization...
4324        note that the SETAST calls and the dual test of pipe_ef
4325        makes sure that only the FIRST thread through here does
4326        the initialization...all other threads wait until it's
4327        done.
4328 
4329        Yeah, uglier than a pthread call, it's got all the stuff inline
4330        rather than in a separate routine.
4331     */
4332 
4333     if (!pipe_ef) {
4334         _ckvmssts_noperl(sys$setast(0));
4335         if (!pipe_ef) {
4336             unsigned long int pidcode = JPI$_PID;
4337             $DESCRIPTOR(d_delay, RETRY_DELAY);
4338             _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4339             _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4340             _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4341         }
4342         if (!handler_set_up) {
4343           _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4344           handler_set_up = TRUE;
4345         }
4346         _ckvmssts_noperl(sys$setast(1));
4347     }
4348 
4349     /* see if we can find a VMSPIPE.COM */
4350 
4351     tfilebuf[0] = '@';
4352     vmspipe = find_vmspipe(aTHX);
4353     if (vmspipe) {
4354         strcpy(tfilebuf+1,vmspipe);
4355     } else {        /* uh, oh...we're in tempfile hell */
4356         tpipe = vmspipe_tempfile(aTHX);
4357         if (!tpipe) {       /* a fish popular in Boston */
4358             if (ckWARN(WARN_PIPE)) {
4359                 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4360             }
4361         return NULL;
4362         }
4363         fgetname(tpipe,tfilebuf+1,1);
4364     }
4365     vmspipedsc.dsc$a_pointer = tfilebuf;
4366     vmspipedsc.dsc$w_length  = strlen(tfilebuf);
4367 
4368     sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4369     if (!(sts & 1)) {
4370       switch (sts) {
4371         case RMS$_FNF:  case RMS$_DNF:
4372           set_errno(ENOENT); break;
4373         case RMS$_DIR:
4374           set_errno(ENOTDIR); break;
4375         case RMS$_DEV:
4376           set_errno(ENODEV); break;
4377         case RMS$_PRV:
4378           set_errno(EACCES); break;
4379         case RMS$_SYN:
4380           set_errno(EINVAL); break;
4381         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4382           set_errno(E2BIG); break;
4383         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4384           _ckvmssts_noperl(sts); /* fall through */
4385         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4386           set_errno(EVMSERR);
4387       }
4388       set_vaxc_errno(sts);
4389       if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4390         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4391       }
4392       *psts = sts;
4393       return NULL;
4394     }
4395     n = sizeof(Info);
4396     _ckvmssts_noperl(lib$get_vm(&n, &info));
4397 
4398     strcpy(mode,in_mode);
4399     info->mode = *mode;
4400     info->done = FALSE;
4401     info->completion = 0;
4402     info->closing    = FALSE;
4403     info->in         = 0;
4404     info->out        = 0;
4405     info->err        = 0;
4406     info->fp         = NULL;
4407     info->useFILE    = 0;
4408     info->waiting    = 0;
4409     info->in_done    = TRUE;
4410     info->out_done   = TRUE;
4411     info->err_done   = TRUE;
4412     info->xchan      = 0;
4413     info->xchan_valid = 0;
4414 
4415     in = PerlMem_malloc(VMS_MAXRSS);
4416     if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4417     out = PerlMem_malloc(VMS_MAXRSS);
4418     if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4419     err = PerlMem_malloc(VMS_MAXRSS);
4420     if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4421 
4422     in[0] = out[0] = err[0] = '\0';
4423 
4424     if ((p = strchr(mode,'F')) != NULL) {   /* F -> use FILE* */
4425         info->useFILE = 1;
4426         strcpy(p,p+1);
4427     }
4428     if ((p = strchr(mode,'W')) != NULL) {   /* W -> wait for completion */
4429         wait = 1;
4430         strcpy(p,p+1);
4431     }
4432 
4433     if (*mode == 'r') {             /* piping from subroutine */
4434 
4435         info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4436         if (info->out) {
4437             info->out->pipe_done = &info->out_done;
4438             info->out_done = FALSE;
4439             info->out->info = info;
4440         }
4441         if (!info->useFILE) {
4442 	    info->fp  = PerlIO_open(mbx, mode);
4443         } else {
4444             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4445             Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4446         }
4447 
4448         if (!info->fp && info->out) {
4449             sys$cancel(info->out->chan_out);
4450 
4451             while (!info->out_done) {
4452                 int done;
4453                 _ckvmssts_noperl(sys$setast(0));
4454                 done = info->out_done;
4455                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4456                 _ckvmssts_noperl(sys$setast(1));
4457                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4458             }
4459 
4460             if (info->out->buf) {
4461                 n = info->out->bufsize * sizeof(char);
4462                 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4463             }
4464             n = sizeof(Pipe);
4465             _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4466             n = sizeof(Info);
4467             _ckvmssts_noperl(lib$free_vm(&n, &info));
4468             *psts = RMS$_FNF;
4469             return NULL;
4470         }
4471 
4472         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4473         if (info->err) {
4474             info->err->pipe_done = &info->err_done;
4475             info->err_done = FALSE;
4476             info->err->info = info;
4477         }
4478 
4479     } else if (*mode == 'w') {      /* piping to subroutine */
4480 
4481         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4482         if (info->out) {
4483             info->out->pipe_done = &info->out_done;
4484             info->out_done = FALSE;
4485             info->out->info = info;
4486         }
4487 
4488         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4489         if (info->err) {
4490             info->err->pipe_done = &info->err_done;
4491             info->err_done = FALSE;
4492             info->err->info = info;
4493         }
4494 
4495         info->in = pipe_tochild_setup(aTHX_ in,mbx);
4496         if (!info->useFILE) {
4497 	    info->fp  = PerlIO_open(mbx, mode);
4498         } else {
4499             info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4500             Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4501         }
4502 
4503         if (info->in) {
4504             info->in->pipe_done = &info->in_done;
4505             info->in_done = FALSE;
4506             info->in->info = info;
4507         }
4508 
4509         /* error cleanup */
4510         if (!info->fp && info->in) {
4511             info->done = TRUE;
4512             _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4513                                       0, 0, 0, 0, 0, 0, 0, 0));
4514 
4515             while (!info->in_done) {
4516                 int done;
4517                 _ckvmssts_noperl(sys$setast(0));
4518                 done = info->in_done;
4519                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4520                 _ckvmssts_noperl(sys$setast(1));
4521                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4522             }
4523 
4524             if (info->in->buf) {
4525                 n = info->in->bufsize * sizeof(char);
4526                 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4527             }
4528             n = sizeof(Pipe);
4529             _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4530             n = sizeof(Info);
4531             _ckvmssts_noperl(lib$free_vm(&n, &info));
4532             *psts = RMS$_FNF;
4533             return NULL;
4534         }
4535 
4536 
4537     } else if (*mode == 'n') {       /* separate subprocess, no Perl i/o */
4538         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4539         if (info->out) {
4540             info->out->pipe_done = &info->out_done;
4541             info->out_done = FALSE;
4542             info->out->info = info;
4543         }
4544 
4545         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4546         if (info->err) {
4547             info->err->pipe_done = &info->err_done;
4548             info->err_done = FALSE;
4549             info->err->info = info;
4550         }
4551     }
4552 
4553     symbol[MAX_DCL_SYMBOL] = '\0';
4554 
4555     strncpy(symbol, in, MAX_DCL_SYMBOL);
4556     d_symbol.dsc$w_length = strlen(symbol);
4557     _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4558 
4559     strncpy(symbol, err, MAX_DCL_SYMBOL);
4560     d_symbol.dsc$w_length = strlen(symbol);
4561     _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4562 
4563     strncpy(symbol, out, MAX_DCL_SYMBOL);
4564     d_symbol.dsc$w_length = strlen(symbol);
4565     _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4566 
4567     /* Done with the names for the pipes */
4568     PerlMem_free(err);
4569     PerlMem_free(out);
4570     PerlMem_free(in);
4571 
4572     p = vmscmd->dsc$a_pointer;
4573     while (*p == ' ' || *p == '\t') p++;        /* remove leading whitespace */
4574     if (*p == '$') p++;                         /* remove leading $ */
4575     while (*p == ' ' || *p == '\t') p++;
4576 
4577     for (j = 0; j < 4; j++) {
4578         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4579         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4580 
4581     strncpy(symbol, p, MAX_DCL_SYMBOL);
4582     d_symbol.dsc$w_length = strlen(symbol);
4583     _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4584 
4585         if (strlen(p) > MAX_DCL_SYMBOL) {
4586             p += MAX_DCL_SYMBOL;
4587         } else {
4588             p += strlen(p);
4589         }
4590     }
4591     _ckvmssts_noperl(sys$setast(0));
4592     info->next=open_pipes;  /* prepend to list */
4593     open_pipes=info;
4594     _ckvmssts_noperl(sys$setast(1));
4595     /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4596      * and SYS$COMMAND.  vmspipe.com will redefine SYS$INPUT, but we'll still
4597      * have SYS$COMMAND if we need it.
4598      */
4599     _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4600                       0, &info->pid, &info->completion,
4601                       0, popen_completion_ast,info,0,0,0));
4602 
4603     /* if we were using a tempfile, close it now */
4604 
4605     if (tpipe) fclose(tpipe);
4606 
4607     /* once the subprocess is spawned, it has copied the symbols and
4608        we can get rid of ours */
4609 
4610     for (j = 0; j < 4; j++) {
4611         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4612         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4613     _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4614     }
4615     _ckvmssts_noperl(lib$delete_symbol(&d_sym_in,  &table));
4616     _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4617     _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4618     vms_execfree(vmscmd);
4619 
4620 #ifdef PERL_IMPLICIT_CONTEXT
4621     if (aTHX)
4622 #endif
4623     PL_forkprocess = info->pid;
4624 
4625     ret_fp = info->fp;
4626     if (wait) {
4627          dSAVEDERRNO;
4628          int done = 0;
4629          while (!done) {
4630              _ckvmssts_noperl(sys$setast(0));
4631              done = info->done;
4632              if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4633              _ckvmssts_noperl(sys$setast(1));
4634              if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4635          }
4636         *psts = info->completion;
4637 /* Caller thinks it is open and tries to close it. */
4638 /* This causes some problems, as it changes the error status */
4639 /*        my_pclose(info->fp); */
4640 
4641          /* If we did not have a file pointer open, then we have to */
4642          /* clean up here or eventually we will run out of something */
4643          SAVE_ERRNO;
4644          if (info->fp == NULL) {
4645              my_pclose_pinfo(aTHX_ info);
4646          }
4647          RESTORE_ERRNO;
4648 
4649     } else {
4650         *psts = info->pid;
4651     }
4652     return ret_fp;
4653 }  /* end of safe_popen */
4654 
4655 
4656 /*{{{  PerlIO *my_popen(char *cmd, char *mode)*/
4657 PerlIO *
4658 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4659 {
4660     int sts;
4661     TAINT_ENV();
4662     TAINT_PROPER("popen");
4663     PERL_FLUSHALL_FOR_CHILD;
4664     return safe_popen(aTHX_ cmd,mode,&sts);
4665 }
4666 
4667 /*}}}*/
4668 
4669 
4670 /* Routine to close and cleanup a pipe info structure */
4671 
4672 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4673 
4674     unsigned long int retsts;
4675     int done, iss, n;
4676     int status;
4677     pInfo next, last;
4678 
4679     /* If we were writing to a subprocess, insure that someone reading from
4680      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
4681      * produce an EOF record in the mailbox.
4682      *
4683      *  well, at least sometimes it *does*, so we have to watch out for
4684      *  the first EOF closing the pipe (and DASSGN'ing the channel)...
4685      */
4686      if (info->fp) {
4687         if (!info->useFILE
4688 #if defined(USE_ITHREADS)
4689           && my_perl
4690 #endif
4691 #ifdef USE_PERLIO
4692           && PL_perlio_fd_refcnt
4693 #endif
4694            )
4695             PerlIO_flush(info->fp);
4696         else
4697             fflush((FILE *)info->fp);
4698     }
4699 
4700     _ckvmssts(sys$setast(0));
4701      info->closing = TRUE;
4702      done = info->done && info->in_done && info->out_done && info->err_done;
4703      /* hanging on write to Perl's input? cancel it */
4704      if (info->mode == 'r' && info->out && !info->out_done) {
4705         if (info->out->chan_out) {
4706             _ckvmssts(sys$cancel(info->out->chan_out));
4707             if (!info->out->chan_in) {   /* EOF generation, need AST */
4708                 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4709             }
4710         }
4711      }
4712      if (info->in && !info->in_done && !info->in->shut_on_empty)  /* EOF if hasn't had one yet */
4713          _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4714                            0, 0, 0, 0, 0, 0));
4715     _ckvmssts(sys$setast(1));
4716     if (info->fp) {
4717      if (!info->useFILE
4718 #if defined(USE_ITHREADS)
4719          && my_perl
4720 #endif
4721 #ifdef USE_PERLIO
4722          && PL_perlio_fd_refcnt
4723 #endif
4724         )
4725         PerlIO_close(info->fp);
4726      else
4727         fclose((FILE *)info->fp);
4728     }
4729      /*
4730         we have to wait until subprocess completes, but ALSO wait until all
4731         the i/o completes...otherwise we'll be freeing the "info" structure
4732         that the i/o ASTs could still be using...
4733      */
4734 
4735      while (!done) {
4736          _ckvmssts(sys$setast(0));
4737          done = info->done && info->in_done && info->out_done && info->err_done;
4738          if (!done) _ckvmssts(sys$clref(pipe_ef));
4739          _ckvmssts(sys$setast(1));
4740          if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4741      }
4742      retsts = info->completion;
4743 
4744     /* remove from list of open pipes */
4745     _ckvmssts(sys$setast(0));
4746     last = NULL;
4747     for (next = open_pipes; next != NULL; last = next, next = next->next) {
4748         if (next == info)
4749             break;
4750     }
4751 
4752     if (last)
4753         last->next = info->next;
4754     else
4755         open_pipes = info->next;
4756     _ckvmssts(sys$setast(1));
4757 
4758     /* free buffers and structures */
4759 
4760     if (info->in) {
4761         if (info->in->buf) {
4762             n = info->in->bufsize * sizeof(char);
4763             _ckvmssts(lib$free_vm(&n, &info->in->buf));
4764         }
4765         n = sizeof(Pipe);
4766         _ckvmssts(lib$free_vm(&n, &info->in));
4767     }
4768     if (info->out) {
4769         if (info->out->buf) {
4770             n = info->out->bufsize * sizeof(char);
4771             _ckvmssts(lib$free_vm(&n, &info->out->buf));
4772         }
4773         n = sizeof(Pipe);
4774         _ckvmssts(lib$free_vm(&n, &info->out));
4775     }
4776     if (info->err) {
4777         if (info->err->buf) {
4778             n = info->err->bufsize * sizeof(char);
4779             _ckvmssts(lib$free_vm(&n, &info->err->buf));
4780         }
4781         n = sizeof(Pipe);
4782         _ckvmssts(lib$free_vm(&n, &info->err));
4783     }
4784     n = sizeof(Info);
4785     _ckvmssts(lib$free_vm(&n, &info));
4786 
4787     return retsts;
4788 }
4789 
4790 
4791 /*{{{  I32 my_pclose(PerlIO *fp)*/
4792 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4793 {
4794     pInfo info, last = NULL;
4795     I32 ret_status;
4796 
4797     /* Fixme - need ast and mutex protection here */
4798     for (info = open_pipes; info != NULL; last = info, info = info->next)
4799         if (info->fp == fp) break;
4800 
4801     if (info == NULL) {  /* no such pipe open */
4802       set_errno(ECHILD); /* quoth POSIX */
4803       set_vaxc_errno(SS$_NONEXPR);
4804       return -1;
4805     }
4806 
4807     ret_status = my_pclose_pinfo(aTHX_ info);
4808 
4809     return ret_status;
4810 
4811 }  /* end of my_pclose() */
4812 
4813 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4814   /* Roll our own prototype because we want this regardless of whether
4815    * _VMS_WAIT is defined.
4816    */
4817   __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4818 #endif
4819 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4820    created with popen(); otherwise partially emulate waitpid() unless
4821    we have a suitable one from the CRTL that came with VMS 7.2 and later.
4822    Also check processes not considered by the CRTL waitpid().
4823  */
4824 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4825 Pid_t
4826 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4827 {
4828     pInfo info;
4829     int done;
4830     int sts;
4831     int j;
4832 
4833     if (statusp) *statusp = 0;
4834 
4835     for (info = open_pipes; info != NULL; info = info->next)
4836         if (info->pid == pid) break;
4837 
4838     if (info != NULL) {  /* we know about this child */
4839       while (!info->done) {
4840           _ckvmssts(sys$setast(0));
4841           done = info->done;
4842           if (!done) _ckvmssts(sys$clref(pipe_ef));
4843           _ckvmssts(sys$setast(1));
4844           if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4845       }
4846 
4847       if (statusp) *statusp = info->completion;
4848       return pid;
4849     }
4850 
4851     /* child that already terminated? */
4852 
4853     for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4854         if (closed_list[j].pid == pid) {
4855             if (statusp) *statusp = closed_list[j].completion;
4856             return pid;
4857         }
4858     }
4859 
4860     /* fall through if this child is not one of our own pipe children */
4861 
4862 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4863 
4864       /* waitpid() became available in the CRTL as of VMS 7.0, but only
4865        * in 7.2 did we get a version that fills in the VMS completion
4866        * status as Perl has always tried to do.
4867        */
4868 
4869       sts = __vms_waitpid( pid, statusp, flags );
4870 
4871       if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4872          return sts;
4873 
4874       /* If the real waitpid tells us the child does not exist, we
4875        * fall through here to implement waiting for a child that
4876        * was created by some means other than exec() (say, spawned
4877        * from DCL) or to wait for a process that is not a subprocess
4878        * of the current process.
4879        */
4880 
4881 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4882 
4883     {
4884       $DESCRIPTOR(intdsc,"0 00:00:01");
4885       unsigned long int ownercode = JPI$_OWNER, ownerpid;
4886       unsigned long int pidcode = JPI$_PID, mypid;
4887       unsigned long int interval[2];
4888       unsigned int jpi_iosb[2];
4889       struct itmlst_3 jpilist[2] = {
4890           {sizeof(ownerpid),        JPI$_OWNER, &ownerpid,        0},
4891           {                      0,         0,                 0, 0}
4892       };
4893 
4894       if (pid <= 0) {
4895         /* Sorry folks, we don't presently implement rooting around for
4896            the first child we can find, and we definitely don't want to
4897            pass a pid of -1 to $getjpi, where it is a wildcard operation.
4898          */
4899         set_errno(ENOTSUP);
4900         return -1;
4901       }
4902 
4903       /* Get the owner of the child so I can warn if it's not mine. If the
4904        * process doesn't exist or I don't have the privs to look at it,
4905        * I can go home early.
4906        */
4907       sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4908       if (sts & 1) sts = jpi_iosb[0];
4909       if (!(sts & 1)) {
4910         switch (sts) {
4911             case SS$_NONEXPR:
4912                 set_errno(ECHILD);
4913                 break;
4914             case SS$_NOPRIV:
4915                 set_errno(EACCES);
4916                 break;
4917             default:
4918                 _ckvmssts(sts);
4919         }
4920         set_vaxc_errno(sts);
4921         return -1;
4922       }
4923 
4924       if (ckWARN(WARN_EXEC)) {
4925         /* remind folks they are asking for non-standard waitpid behavior */
4926         _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4927         if (ownerpid != mypid)
4928           Perl_warner(aTHX_ packWARN(WARN_EXEC),
4929                       "waitpid: process %x is not a child of process %x",
4930                       pid,mypid);
4931       }
4932 
4933       /* simply check on it once a second until it's not there anymore. */
4934 
4935       _ckvmssts(sys$bintim(&intdsc,interval));
4936       while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4937             _ckvmssts(sys$schdwk(0,0,interval,0));
4938             _ckvmssts(sys$hiber());
4939       }
4940       if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4941 
4942       _ckvmssts(sts);
4943       return pid;
4944     }
4945 }  /* end of waitpid() */
4946 /*}}}*/
4947 /*}}}*/
4948 /*}}}*/
4949 
4950 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4951 char *
4952 my_gconvert(double val, int ndig, int trail, char *buf)
4953 {
4954   static char __gcvtbuf[DBL_DIG+1];
4955   char *loc;
4956 
4957   loc = buf ? buf : __gcvtbuf;
4958 
4959 #ifndef __DECC  /* VAXCRTL gcvt uses E format for numbers < 1 */
4960   if (val < 1) {
4961     sprintf(loc,"%.*g",ndig,val);
4962     return loc;
4963   }
4964 #endif
4965 
4966   if (val) {
4967     if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4968     return gcvt(val,ndig,loc);
4969   }
4970   else {
4971     loc[0] = '0'; loc[1] = '\0';
4972     return loc;
4973   }
4974 
4975 }
4976 /*}}}*/
4977 
4978 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4979 static int rms_free_search_context(struct FAB * fab)
4980 {
4981 struct NAM * nam;
4982 
4983     nam = fab->fab$l_nam;
4984     nam->nam$b_nop |= NAM$M_SYNCHK;
4985     nam->nam$l_rlf = NULL;
4986     fab->fab$b_dns = 0;
4987     return sys$parse(fab, NULL, NULL);
4988 }
4989 
4990 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4991 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4992 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4993 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4994 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4995 #define rms_nam_esll(nam) nam.nam$b_esl
4996 #define rms_nam_esl(nam) nam.nam$b_esl
4997 #define rms_nam_name(nam) nam.nam$l_name
4998 #define rms_nam_namel(nam) nam.nam$l_name
4999 #define rms_nam_type(nam) nam.nam$l_type
5000 #define rms_nam_typel(nam) nam.nam$l_type
5001 #define rms_nam_ver(nam) nam.nam$l_ver
5002 #define rms_nam_verl(nam) nam.nam$l_ver
5003 #define rms_nam_rsll(nam) nam.nam$b_rsl
5004 #define rms_nam_rsl(nam) nam.nam$b_rsl
5005 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
5006 #define rms_set_fna(fab, nam, name, size) \
5007 	{ fab.fab$b_fns = size; fab.fab$l_fna = name; }
5008 #define rms_get_fna(fab, nam) fab.fab$l_fna
5009 #define rms_set_dna(fab, nam, name, size) \
5010 	{ fab.fab$b_dns = size; fab.fab$l_dna = name; }
5011 #define rms_nam_dns(fab, nam) fab.fab$b_dns
5012 #define rms_set_esa(nam, name, size) \
5013 	{ nam.nam$b_ess = size; nam.nam$l_esa = name; }
5014 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5015 	{ nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
5016 #define rms_set_rsa(nam, name, size) \
5017 	{ nam.nam$l_rsa = name; nam.nam$b_rss = size; }
5018 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5019 	{ nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
5020 #define rms_nam_name_type_l_size(nam) \
5021 	(nam.nam$b_name + nam.nam$b_type)
5022 #else
5023 static int rms_free_search_context(struct FAB * fab)
5024 {
5025 struct NAML * nam;
5026 
5027     nam = fab->fab$l_naml;
5028     nam->naml$b_nop |= NAM$M_SYNCHK;
5029     nam->naml$l_rlf = NULL;
5030     nam->naml$l_long_defname_size = 0;
5031 
5032     fab->fab$b_dns = 0;
5033     return sys$parse(fab, NULL, NULL);
5034 }
5035 
5036 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
5037 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
5038 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
5039 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
5040 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
5041 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
5042 #define rms_nam_esl(nam) nam.naml$b_esl
5043 #define rms_nam_name(nam) nam.naml$l_name
5044 #define rms_nam_namel(nam) nam.naml$l_long_name
5045 #define rms_nam_type(nam) nam.naml$l_type
5046 #define rms_nam_typel(nam) nam.naml$l_long_type
5047 #define rms_nam_ver(nam) nam.naml$l_ver
5048 #define rms_nam_verl(nam) nam.naml$l_long_ver
5049 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
5050 #define rms_nam_rsl(nam) nam.naml$b_rsl
5051 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
5052 #define rms_set_fna(fab, nam, name, size) \
5053 	{ fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
5054 	nam.naml$l_long_filename_size = size; \
5055 	nam.naml$l_long_filename = name;}
5056 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
5057 #define rms_set_dna(fab, nam, name, size) \
5058 	{ fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
5059 	nam.naml$l_long_defname_size = size; \
5060 	nam.naml$l_long_defname = name; }
5061 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
5062 #define rms_set_esa(nam, name, size) \
5063 	{ nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
5064 	nam.naml$l_long_expand_alloc = size; \
5065 	nam.naml$l_long_expand = name; }
5066 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5067 	{ nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
5068 	nam.naml$l_long_expand = l_name; \
5069 	nam.naml$l_long_expand_alloc = l_size; }
5070 #define rms_set_rsa(nam, name, size) \
5071 	{ nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
5072 	nam.naml$l_long_result = name; \
5073 	nam.naml$l_long_result_alloc = size; }
5074 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5075 	{ nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
5076 	nam.naml$l_long_result = l_name; \
5077 	nam.naml$l_long_result_alloc = l_size; }
5078 #define rms_nam_name_type_l_size(nam) \
5079 	(nam.naml$l_long_name_size + nam.naml$l_long_type_size)
5080 #endif
5081 
5082 
5083 /* rms_erase
5084  * The CRTL for 8.3 and later can create symbolic links in any mode,
5085  * however in 8.3 the unlink/remove/delete routines will only properly handle
5086  * them if one of the PCP modes is active.
5087  */
5088 static int rms_erase(const char * vmsname)
5089 {
5090   int status;
5091   struct FAB myfab = cc$rms_fab;
5092   rms_setup_nam(mynam);
5093 
5094   rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
5095   rms_bind_fab_nam(myfab, mynam);
5096 
5097 #ifdef NAML$M_OPEN_SPECIAL
5098   rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5099 #endif
5100 
5101   status = sys$erase(&myfab, 0, 0);
5102 
5103   return status;
5104 }
5105 
5106 
5107 static int
5108 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5109 		    const struct dsc$descriptor_s * vms_dst_dsc,
5110 		    unsigned long flags)
5111 {
5112     /*  VMS and UNIX handle file permissions differently and the
5113      * the same ACL trick may be needed for renaming files,
5114      * especially if they are directories.
5115      */
5116 
5117    /* todo: get kill_file and rename to share common code */
5118    /* I can not find online documentation for $change_acl
5119     * it appears to be replaced by $set_security some time ago */
5120 
5121 const unsigned int access_mode = 0;
5122 $DESCRIPTOR(obj_file_dsc,"FILE");
5123 char *vmsname;
5124 char *rslt;
5125 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5126 int aclsts, fndsts, rnsts = -1;
5127 unsigned int ctx = 0;
5128 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5129 struct dsc$descriptor_s * clean_dsc;
5130 
5131 struct myacedef {
5132     unsigned char myace$b_length;
5133     unsigned char myace$b_type;
5134     unsigned short int myace$w_flags;
5135     unsigned long int myace$l_access;
5136     unsigned long int myace$l_ident;
5137 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5138 	     ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5139 	     0},
5140 	     oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5141 
5142 struct item_list_3
5143 	findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5144 		      {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5145 		      {0,0,0,0}},
5146 	addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5147 	dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5148 		     {0,0,0,0}};
5149 
5150 
5151     /* Expand the input spec using RMS, since we do not want to put
5152      * ACLs on the target of a symbolic link */
5153     vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5154     if (vmsname == NULL)
5155 	return SS$_INSFMEM;
5156 
5157     rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5158 			vmsname,
5159 			PERL_RMSEXPAND_M_SYMLINK);
5160     if (rslt == NULL) {
5161 	PerlMem_free(vmsname);
5162 	return SS$_INSFMEM;
5163     }
5164 
5165     /* So we get our own UIC to use as a rights identifier,
5166      * and the insert an ACE at the head of the ACL which allows us
5167      * to delete the file.
5168      */
5169     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5170 
5171     fildsc.dsc$w_length = strlen(vmsname);
5172     fildsc.dsc$a_pointer = vmsname;
5173     ctx = 0;
5174     newace.myace$l_ident = oldace.myace$l_ident;
5175     rnsts = SS$_ABORT;
5176 
5177     /* Grab any existing ACEs with this identifier in case we fail */
5178     clean_dsc = &fildsc;
5179     aclsts = fndsts = sys$get_security(&obj_file_dsc,
5180 			       &fildsc,
5181 			       NULL,
5182 			       OSS$M_WLOCK,
5183 			       findlst,
5184 			       &ctx,
5185 			       &access_mode);
5186 
5187     if ($VMS_STATUS_SUCCESS(fndsts)  || (fndsts == SS$_ACLEMPTY)) {
5188 	/* Add the new ACE . . . */
5189 
5190 	/* if the sys$get_security succeeded, then ctx is valid, and the
5191 	 * object/file descriptors will be ignored.  But otherwise they
5192 	 * are needed
5193 	 */
5194 	aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5195 				  OSS$M_RELCTX, addlst, &ctx, &access_mode);
5196 	if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5197 	    set_errno(EVMSERR);
5198 	    set_vaxc_errno(aclsts);
5199 	    PerlMem_free(vmsname);
5200 	    return aclsts;
5201 	}
5202 
5203 	rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5204 				NULL, NULL,
5205 				&flags,
5206 				NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5207 
5208 	if ($VMS_STATUS_SUCCESS(rnsts)) {
5209 	    clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5210 	}
5211 
5212 	/* Put things back the way they were. */
5213 	ctx = 0;
5214 	aclsts = sys$get_security(&obj_file_dsc,
5215 				  clean_dsc,
5216 				  NULL,
5217 				  OSS$M_WLOCK,
5218 				  findlst,
5219 				  &ctx,
5220 				  &access_mode);
5221 
5222 	if ($VMS_STATUS_SUCCESS(aclsts)) {
5223 	int sec_flags;
5224 
5225 	    sec_flags = 0;
5226 	    if (!$VMS_STATUS_SUCCESS(fndsts))
5227 		sec_flags = OSS$M_RELCTX;
5228 
5229 	    /* Get rid of the new ACE */
5230 	    aclsts = sys$set_security(NULL, NULL, NULL,
5231 				  sec_flags, dellst, &ctx, &access_mode);
5232 
5233 	    /* If there was an old ACE, put it back */
5234 	    if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5235 		addlst[0].bufadr = &oldace;
5236 		aclsts = sys$set_security(NULL, NULL, NULL,
5237 				      OSS$M_RELCTX, addlst, &ctx, &access_mode);
5238 		if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5239 		    set_errno(EVMSERR);
5240 		    set_vaxc_errno(aclsts);
5241 		    rnsts = aclsts;
5242 		}
5243 	    } else {
5244 	    int aclsts2;
5245 
5246 		/* Try to clear the lock on the ACL list */
5247 		aclsts2 = sys$set_security(NULL, NULL, NULL,
5248 				      OSS$M_RELCTX, NULL, &ctx, &access_mode);
5249 
5250 		/* Rename errors are most important */
5251 		if (!$VMS_STATUS_SUCCESS(rnsts))
5252 		    aclsts = rnsts;
5253 		set_errno(EVMSERR);
5254 		set_vaxc_errno(aclsts);
5255 		rnsts = aclsts;
5256 	    }
5257 	}
5258 	else {
5259 	    if (aclsts != SS$_ACLEMPTY)
5260 		rnsts = aclsts;
5261 	}
5262     }
5263     else
5264 	rnsts = fndsts;
5265 
5266     PerlMem_free(vmsname);
5267     return rnsts;
5268 }
5269 
5270 
5271 /*{{{int rename(const char *, const char * */
5272 /* Not exactly what X/Open says to do, but doing it absolutely right
5273  * and efficiently would require a lot more work.  This should be close
5274  * enough to pass all but the most strict X/Open compliance test.
5275  */
5276 int
5277 Perl_rename(pTHX_ const char *src, const char * dst)
5278 {
5279 int retval;
5280 int pre_delete = 0;
5281 int src_sts;
5282 int dst_sts;
5283 Stat_t src_st;
5284 Stat_t dst_st;
5285 
5286     /* Validate the source file */
5287     src_sts = flex_lstat(src, &src_st);
5288     if (src_sts != 0) {
5289 
5290 	/* No source file or other problem */
5291 	return src_sts;
5292     }
5293     if (src_st.st_devnam[0] == 0)  {
5294         /* This may be possible so fail if it is seen. */
5295         errno = EIO;
5296         return -1;
5297     }
5298 
5299     dst_sts = flex_lstat(dst, &dst_st);
5300     if (dst_sts == 0) {
5301 
5302 	if (dst_st.st_dev != src_st.st_dev) {
5303 	    /* Must be on the same device */
5304 	    errno = EXDEV;
5305 	    return -1;
5306 	}
5307 
5308 	/* VMS_INO_T_COMPARE is true if the inodes are different
5309 	 * to match the output of memcmp
5310 	 */
5311 
5312 	if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5313 	    /* That was easy, the files are the same! */
5314 	    return 0;
5315 	}
5316 
5317 	if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5318 	    /* If source is a directory, so must be dest */
5319 		errno = EISDIR;
5320 		return -1;
5321 	}
5322 
5323     }
5324 
5325 
5326     if ((dst_sts == 0) &&
5327 	(vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5328 
5329 	/* We have issues here if vms_unlink_all_versions is set
5330 	 * If the destination exists, and is not a directory, then
5331 	 * we must delete in advance.
5332 	 *
5333 	 * If the src is a directory, then we must always pre-delete
5334 	 * the destination.
5335 	 *
5336 	 * If we successfully delete the dst in advance, and the rename fails
5337 	 * X/Open requires that errno be EIO.
5338 	 *
5339 	 */
5340 
5341 	if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5342 	    int d_sts;
5343 	    d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5344 	                             S_ISDIR(dst_st.st_mode));
5345 
5346            /* Need to delete all versions ? */
5347            if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5348                 int i = 0;
5349 
5350                 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5351                     d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5352                     if (d_sts != 0)
5353                         break;
5354                     i++;
5355 
5356                     /* Make sure that we do not loop forever */
5357                     if (i > 32767) {
5358                         errno = EIO;
5359                         d_sts = -1;
5360                         break;
5361                     }
5362                 }
5363            }
5364 
5365 	    if (d_sts != 0)
5366 		return d_sts;
5367 
5368 	    /* We killed the destination, so only errno now is EIO */
5369 	    pre_delete = 1;
5370 	}
5371     }
5372 
5373     /* Originally the idea was to call the CRTL rename() and only
5374      * try the lib$rename_file if it failed.
5375      * It turns out that there are too many variants in what the
5376      * the CRTL rename might do, so only use lib$rename_file
5377      */
5378     retval = -1;
5379 
5380     {
5381 	/* Is the source and dest both in VMS format */
5382 	/* if the source is a directory, then need to fileify */
5383 	/*  and dest must be a directory or non-existant. */
5384 
5385 	char * vms_dst;
5386 	int sts;
5387 	char * ret_str;
5388 	unsigned long flags;
5389 	struct dsc$descriptor_s old_file_dsc;
5390 	struct dsc$descriptor_s new_file_dsc;
5391 
5392 	/* We need to modify the src and dst depending
5393 	 * on if one or more of them are directories.
5394 	 */
5395 
5396 	vms_dst = PerlMem_malloc(VMS_MAXRSS);
5397 	if (vms_dst == NULL)
5398 	    _ckvmssts_noperl(SS$_INSFMEM);
5399 
5400 	if (S_ISDIR(src_st.st_mode)) {
5401 	char * ret_str;
5402 	char * vms_dir_file;
5403 
5404 	    vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5405 	    if (vms_dir_file == NULL)
5406 		_ckvmssts_noperl(SS$_INSFMEM);
5407 
5408 	    /* If the dest is a directory, we must remove it
5409 	    if (dst_sts == 0) {
5410 		int d_sts;
5411 		d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5412 		if (d_sts != 0) {
5413 		    PerlMem_free(vms_dst);
5414 		    errno = EIO;
5415 		    return sts;
5416 		}
5417 
5418 		pre_delete = 1;
5419 	    }
5420 
5421 	   /* The dest must be a VMS file specification */
5422 	   ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5423 	   if (ret_str == NULL) {
5424 		PerlMem_free(vms_dst);
5425 		errno = EIO;
5426 		return -1;
5427 	   }
5428 
5429 	    /* The source must be a file specification */
5430 	    ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5431 	    if (ret_str == NULL) {
5432 		PerlMem_free(vms_dst);
5433 		PerlMem_free(vms_dir_file);
5434 		errno = EIO;
5435 		return -1;
5436 	    }
5437 	    PerlMem_free(vms_dst);
5438 	    vms_dst = vms_dir_file;
5439 
5440 	} else {
5441 	    /* File to file or file to new dir */
5442 
5443 	    if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5444 		/* VMS pathify a dir target */
5445 		ret_str = int_tovmspath(dst, vms_dst, NULL);
5446 		if (ret_str == NULL) {
5447 		    PerlMem_free(vms_dst);
5448 		    errno = EIO;
5449 		    return -1;
5450 		}
5451 	    } else {
5452                 char * v_spec, * r_spec, * d_spec, * n_spec;
5453                 char * e_spec, * vs_spec;
5454                 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5455 
5456 		/* fileify a target VMS file specification */
5457 		ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5458 		if (ret_str == NULL) {
5459 		    PerlMem_free(vms_dst);
5460 		    errno = EIO;
5461 		    return -1;
5462 		}
5463 
5464 		sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5465                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5466                              &e_len, &vs_spec, &vs_len);
5467 		if (sts == 0) {
5468 		     if (e_len == 0) {
5469 		         /* Get rid of the version */
5470 		         if (vs_len != 0) {
5471 		             *vs_spec = '\0';
5472 		         }
5473 		         /* Need to specify a '.' so that the extension */
5474 		         /* is not inherited */
5475 		         strcat(vms_dst,".");
5476 		     }
5477 		}
5478 	    }
5479 	}
5480 
5481 	old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5482 	old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5483 	old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5484 	old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5485 
5486 	new_file_dsc.dsc$a_pointer = vms_dst;
5487 	new_file_dsc.dsc$w_length = strlen(vms_dst);
5488 	new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5489 	new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5490 
5491 	flags = 0;
5492 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5493 	flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5494 #endif
5495 
5496 	sts = lib$rename_file(&old_file_dsc,
5497 			      &new_file_dsc,
5498 			      NULL, NULL,
5499 			      &flags,
5500 			      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5501 	if (!$VMS_STATUS_SUCCESS(sts)) {
5502 
5503 	   /* We could have failed because VMS style permissions do not
5504 	    * permit renames that UNIX will allow.  Just like the hack
5505 	    * in for kill_file.
5506 	    */
5507 	   sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5508 	}
5509 
5510 	PerlMem_free(vms_dst);
5511 	if (!$VMS_STATUS_SUCCESS(sts)) {
5512 	    errno = EIO;
5513 	    return -1;
5514 	}
5515 	retval = 0;
5516     }
5517 
5518     if (vms_unlink_all_versions) {
5519 	/* Now get rid of any previous versions of the source file that
5520 	 * might still exist
5521 	 */
5522 	int i = 0;
5523 	dSAVEDERRNO;
5524 	SAVE_ERRNO;
5525 	src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5526 	                           S_ISDIR(src_st.st_mode));
5527 	while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5528 	     src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5529 	                               S_ISDIR(src_st.st_mode));
5530 	     if (src_sts != 0)
5531 	         break;
5532 	     i++;
5533 
5534 	     /* Make sure that we do not loop forever */
5535 	     if (i > 32767) {
5536 	         src_sts = -1;
5537 	         break;
5538 	     }
5539 	}
5540 	RESTORE_ERRNO;
5541     }
5542 
5543     /* We deleted the destination, so must force the error to be EIO */
5544     if ((retval != 0) && (pre_delete != 0))
5545 	errno = EIO;
5546 
5547     return retval;
5548 }
5549 /*}}}*/
5550 
5551 
5552 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5553 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5554  * to expand file specification.  Allows for a single default file
5555  * specification and a simple mask of options.  If outbuf is non-NULL,
5556  * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5557  * the resultant file specification is placed.  If outbuf is NULL, the
5558  * resultant file specification is placed into a static buffer.
5559  * The third argument, if non-NULL, is taken to be a default file
5560  * specification string.  The fourth argument is unused at present.
5561  * rmesexpand() returns the address of the resultant string if
5562  * successful, and NULL on error.
5563  *
5564  * New functionality for previously unused opts value:
5565  *  PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5566  *  PERL_RMSEXPAND_M_LONG - Want output in long formst
5567  *  PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5568  *  PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5569  */
5570 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5571 
5572 static char *
5573 int_rmsexpand
5574    (const char *filespec,
5575     char *outbuf,
5576     const char *defspec,
5577     unsigned opts,
5578     int * fs_utf8,
5579     int * dfs_utf8)
5580 {
5581   char * ret_spec;
5582   const char * in_spec;
5583   char * spec_buf;
5584   const char * def_spec;
5585   char * vmsfspec, *vmsdefspec;
5586   char * esa;
5587   char * esal = NULL;
5588   char * outbufl;
5589   struct FAB myfab = cc$rms_fab;
5590   rms_setup_nam(mynam);
5591   STRLEN speclen;
5592   unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5593   int sts;
5594 
5595   /* temp hack until UTF8 is actually implemented */
5596   if (fs_utf8 != NULL)
5597     *fs_utf8 = 0;
5598 
5599   if (!filespec || !*filespec) {
5600     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5601     return NULL;
5602   }
5603 
5604   vmsfspec = NULL;
5605   vmsdefspec = NULL;
5606   outbufl = NULL;
5607 
5608   in_spec = filespec;
5609   isunix = 0;
5610   if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5611       char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5612       int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5613 
5614       /* If this is a UNIX file spec, convert it to VMS */
5615       sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5616                            &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5617                            &e_len, &vs_spec, &vs_len);
5618       if (sts != 0) {
5619           isunix = 1;
5620           char * ret_spec;
5621 
5622           vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5623           if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5624           ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5625           if (ret_spec == NULL) {
5626               PerlMem_free(vmsfspec);
5627               return NULL;
5628           }
5629           in_spec = (const char *)vmsfspec;
5630 
5631           /* Unless we are forcing to VMS format, a UNIX input means
5632            * UNIX output, and that requires long names to be used
5633            */
5634           if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5635 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5636               opts |= PERL_RMSEXPAND_M_LONG;
5637 #else
5638               NOOP;
5639 #endif
5640           else
5641               isunix = 0;
5642       }
5643 
5644   }
5645 
5646   rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5647   rms_bind_fab_nam(myfab, mynam);
5648 
5649   /* Process the default file specification if present */
5650   def_spec = defspec;
5651   if (defspec && *defspec) {
5652     int t_isunix;
5653     t_isunix = is_unix_filespec(defspec);
5654     if (t_isunix) {
5655       vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5656       if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5657       ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5658 
5659       if (ret_spec == NULL) {
5660           /* Clean up and bail */
5661           PerlMem_free(vmsdefspec);
5662           if (vmsfspec != NULL)
5663               PerlMem_free(vmsfspec);
5664               return NULL;
5665           }
5666           def_spec = (const char *)vmsdefspec;
5667       }
5668       rms_set_dna(myfab, mynam,
5669                   (char *)def_spec, strlen(def_spec)); /* cast ok */
5670   }
5671 
5672   /* Now we need the expansion buffers */
5673   esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5674   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5675 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5676   esal = PerlMem_malloc(VMS_MAXRSS);
5677   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5678 #endif
5679   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5680 
5681   /* If a NAML block is used RMS always writes to the long and short
5682    * addresses unless you suppress the short name.
5683    */
5684 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5685   outbufl = PerlMem_malloc(VMS_MAXRSS);
5686   if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5687 #endif
5688    rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5689 
5690 #ifdef NAM$M_NO_SHORT_UPCASE
5691   if (decc_efs_case_preserve)
5692     rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5693 #endif
5694 
5695    /* We may not want to follow symbolic links */
5696 #ifdef NAML$M_OPEN_SPECIAL
5697   if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5698     rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5699 #endif
5700 
5701   /* First attempt to parse as an existing file */
5702   retsts = sys$parse(&myfab,0,0);
5703   if (!(retsts & STS$K_SUCCESS)) {
5704 
5705     /* Could not find the file, try as syntax only if error is not fatal */
5706     rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5707     if (retsts == RMS$_DNF ||
5708         retsts == RMS$_DIR ||
5709         retsts == RMS$_DEV ||
5710         retsts == RMS$_PRV) {
5711       retsts = sys$parse(&myfab,0,0);
5712       if (retsts & STS$K_SUCCESS) goto int_expanded;
5713     }
5714 
5715      /* Still could not parse the file specification */
5716     /*----------------------------------------------*/
5717     sts = rms_free_search_context(&myfab); /* Free search context */
5718     if (vmsdefspec != NULL)
5719 	PerlMem_free(vmsdefspec);
5720     if (vmsfspec != NULL)
5721 	PerlMem_free(vmsfspec);
5722     if (outbufl != NULL)
5723 	PerlMem_free(outbufl);
5724     PerlMem_free(esa);
5725     if (esal != NULL)
5726 	PerlMem_free(esal);
5727     set_vaxc_errno(retsts);
5728     if      (retsts == RMS$_PRV) set_errno(EACCES);
5729     else if (retsts == RMS$_DEV) set_errno(ENODEV);
5730     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5731     else                         set_errno(EVMSERR);
5732     return NULL;
5733   }
5734   retsts = sys$search(&myfab,0,0);
5735   if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5736     sts = rms_free_search_context(&myfab); /* Free search context */
5737     if (vmsdefspec != NULL)
5738 	PerlMem_free(vmsdefspec);
5739     if (vmsfspec != NULL)
5740 	PerlMem_free(vmsfspec);
5741     if (outbufl != NULL)
5742 	PerlMem_free(outbufl);
5743     PerlMem_free(esa);
5744     if (esal != NULL)
5745 	PerlMem_free(esal);
5746     set_vaxc_errno(retsts);
5747     if      (retsts == RMS$_PRV) set_errno(EACCES);
5748     else                         set_errno(EVMSERR);
5749     return NULL;
5750   }
5751 
5752   /* If the input filespec contained any lowercase characters,
5753    * downcase the result for compatibility with Unix-minded code. */
5754 int_expanded:
5755   if (!decc_efs_case_preserve) {
5756     char * tbuf;
5757     for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5758       if (islower(*tbuf)) { haslower = 1; break; }
5759   }
5760 
5761    /* Is a long or a short name expected */
5762   /*------------------------------------*/
5763   spec_buf = NULL;
5764 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5765   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5766     if (rms_nam_rsll(mynam)) {
5767 	spec_buf = outbufl;
5768 	speclen = rms_nam_rsll(mynam);
5769     }
5770     else {
5771 	spec_buf = esal; /* Not esa */
5772 	speclen = rms_nam_esll(mynam);
5773     }
5774   }
5775   else {
5776 #endif
5777     if (rms_nam_rsl(mynam)) {
5778 	spec_buf = outbuf;
5779 	speclen = rms_nam_rsl(mynam);
5780     }
5781     else {
5782 	spec_buf = esa; /* Not esal */
5783 	speclen = rms_nam_esl(mynam);
5784     }
5785 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5786   }
5787 #endif
5788   spec_buf[speclen] = '\0';
5789 
5790   /* Trim off null fields added by $PARSE
5791    * If type > 1 char, must have been specified in original or default spec
5792    * (not true for version; $SEARCH may have added version of existing file).
5793    */
5794   trimver  = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5795   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5796     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5797              ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5798   }
5799   else {
5800     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5801              ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5802   }
5803   if (trimver || trimtype) {
5804     if (defspec && *defspec) {
5805       char *defesal = NULL;
5806       char *defesa = NULL;
5807       defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5808       if (defesa != NULL) {
5809         struct FAB deffab = cc$rms_fab;
5810 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5811         defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5812         if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5813 #endif
5814 	rms_setup_nam(defnam);
5815 
5816 	rms_bind_fab_nam(deffab, defnam);
5817 
5818 	/* Cast ok */
5819 	rms_set_fna
5820 	    (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5821 
5822 	/* RMS needs the esa/esal as a work area if wildcards are involved */
5823 	rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5824 
5825 	rms_clear_nam_nop(defnam);
5826 	rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5827 #ifdef NAM$M_NO_SHORT_UPCASE
5828 	if (decc_efs_case_preserve)
5829 	  rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5830 #endif
5831 #ifdef NAML$M_OPEN_SPECIAL
5832 	if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5833 	  rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5834 #endif
5835 	if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5836 	  if (trimver) {
5837 	     trimver  = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5838 	  }
5839 	  if (trimtype) {
5840 	    trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5841 	  }
5842 	}
5843 	if (defesal != NULL)
5844 	    PerlMem_free(defesal);
5845 	PerlMem_free(defesa);
5846       } else {
5847           _ckvmssts_noperl(SS$_INSFMEM);
5848       }
5849     }
5850     if (trimver) {
5851       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5852 	if (*(rms_nam_verl(mynam)) != '\"')
5853 	  speclen = rms_nam_verl(mynam) - spec_buf;
5854       }
5855       else {
5856 	if (*(rms_nam_ver(mynam)) != '\"')
5857 	  speclen = rms_nam_ver(mynam) - spec_buf;
5858       }
5859     }
5860     if (trimtype) {
5861       /* If we didn't already trim version, copy down */
5862       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5863 	if (speclen > rms_nam_verl(mynam) - spec_buf)
5864 	  memmove
5865 	   (rms_nam_typel(mynam),
5866 	    rms_nam_verl(mynam),
5867 	    speclen - (rms_nam_verl(mynam) - spec_buf));
5868 	  speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5869       }
5870       else {
5871 	if (speclen > rms_nam_ver(mynam) - spec_buf)
5872 	  memmove
5873 	   (rms_nam_type(mynam),
5874 	    rms_nam_ver(mynam),
5875 	    speclen - (rms_nam_ver(mynam) - spec_buf));
5876 	  speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5877       }
5878     }
5879   }
5880 
5881    /* Done with these copies of the input files */
5882   /*-------------------------------------------*/
5883   if (vmsfspec != NULL)
5884 	PerlMem_free(vmsfspec);
5885   if (vmsdefspec != NULL)
5886 	PerlMem_free(vmsdefspec);
5887 
5888   /* If we just had a directory spec on input, $PARSE "helpfully"
5889    * adds an empty name and type for us */
5890 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5891   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5892     if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5893 	rms_nam_verl(mynam)  == rms_nam_typel(mynam) + 1 &&
5894 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5895       speclen = rms_nam_namel(mynam) - spec_buf;
5896   }
5897   else
5898 #endif
5899   {
5900     if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5901 	rms_nam_ver(mynam)  == rms_nam_ver(mynam) + 1 &&
5902 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5903       speclen = rms_nam_name(mynam) - spec_buf;
5904   }
5905 
5906   /* Posix format specifications must have matching quotes */
5907   if (speclen < (VMS_MAXRSS - 1)) {
5908     if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5909       if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5910         spec_buf[speclen] = '\"';
5911         speclen++;
5912       }
5913     }
5914   }
5915   spec_buf[speclen] = '\0';
5916   if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5917 
5918   /* Have we been working with an expanded, but not resultant, spec? */
5919   /* Also, convert back to Unix syntax if necessary. */
5920   {
5921   int rsl;
5922 
5923 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5924     if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5925       rsl = rms_nam_rsll(mynam);
5926     } else
5927 #endif
5928     {
5929       rsl = rms_nam_rsl(mynam);
5930     }
5931     if (!rsl) {
5932       /* rsl is not present, it means that spec_buf is either */
5933       /* esa or esal, and needs to be copied to outbuf */
5934       /* convert to Unix if desired */
5935       if (isunix) {
5936         ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5937       } else {
5938         /* VMS file specs are not in UTF-8 */
5939         if (fs_utf8 != NULL)
5940             *fs_utf8 = 0;
5941         strcpy(outbuf, spec_buf);
5942         ret_spec = outbuf;
5943       }
5944     }
5945     else {
5946       /* Now spec_buf is either outbuf or outbufl */
5947       /* We need the result into outbuf */
5948       if (isunix) {
5949            /* If we need this in UNIX, then we need another buffer */
5950            /* to keep things in order */
5951            char * src;
5952            char * new_src = NULL;
5953            if (spec_buf == outbuf) {
5954                new_src = PerlMem_malloc(VMS_MAXRSS);
5955                strcpy(new_src, spec_buf);
5956            } else {
5957                src = spec_buf;
5958            }
5959            ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5960            if (new_src) {
5961                PerlMem_free(new_src);
5962            }
5963       } else {
5964            /* VMS file specs are not in UTF-8 */
5965            if (fs_utf8 != NULL)
5966                *fs_utf8 = 0;
5967 
5968            /* Copy the buffer if needed */
5969            if (outbuf != spec_buf)
5970                strcpy(outbuf, spec_buf);
5971            ret_spec = outbuf;
5972       }
5973     }
5974   }
5975 
5976   /* Need to clean up the search context */
5977   rms_set_rsal(mynam, NULL, 0, NULL, 0);
5978   sts = rms_free_search_context(&myfab); /* Free search context */
5979 
5980   /* Clean up the extra buffers */
5981   if (esal != NULL)
5982       PerlMem_free(esal);
5983   PerlMem_free(esa);
5984   if (outbufl != NULL)
5985      PerlMem_free(outbufl);
5986 
5987   /* Return the result */
5988   return ret_spec;
5989 }
5990 
5991 /* Common simple case - Expand an already VMS spec */
5992 static char *
5993 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5994     opts |= PERL_RMSEXPAND_M_VMS_IN;
5995     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5996 }
5997 
5998 /* Common simple case - Expand to a VMS spec */
5999 static char *
6000 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
6001     opts |= PERL_RMSEXPAND_M_VMS;
6002     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
6003 }
6004 
6005 
6006 /* Entry point used by perl routines */
6007 static char *
6008 mp_do_rmsexpand
6009    (pTHX_ const char *filespec,
6010     char *outbuf,
6011     int ts,
6012     const char *defspec,
6013     unsigned opts,
6014     int * fs_utf8,
6015     int * dfs_utf8)
6016 {
6017     static char __rmsexpand_retbuf[VMS_MAXRSS];
6018     char * expanded, *ret_spec, *ret_buf;
6019 
6020     expanded = NULL;
6021     ret_buf = outbuf;
6022     if (ret_buf == NULL) {
6023         if (ts) {
6024             Newx(expanded, VMS_MAXRSS, char);
6025             if (expanded == NULL)
6026                 _ckvmssts(SS$_INSFMEM);
6027             ret_buf = expanded;
6028         } else {
6029             ret_buf = __rmsexpand_retbuf;
6030         }
6031     }
6032 
6033 
6034     ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
6035                              opts, fs_utf8,  dfs_utf8);
6036 
6037     if (ret_spec == NULL) {
6038        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6039        if (expanded)
6040            Safefree(expanded);
6041     }
6042 
6043     return ret_spec;
6044 }
6045 /*}}}*/
6046 /* External entry points */
6047 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6048 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
6049 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6050 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
6051 char *Perl_rmsexpand_utf8
6052   (pTHX_ const char *spec, char *buf, const char *def,
6053    unsigned opt, int * fs_utf8, int * dfs_utf8)
6054 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
6055 char *Perl_rmsexpand_utf8_ts
6056   (pTHX_ const char *spec, char *buf, const char *def,
6057    unsigned opt, int * fs_utf8, int * dfs_utf8)
6058 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
6059 
6060 
6061 /*
6062 ** The following routines are provided to make life easier when
6063 ** converting among VMS-style and Unix-style directory specifications.
6064 ** All will take input specifications in either VMS or Unix syntax. On
6065 ** failure, all return NULL.  If successful, the routines listed below
6066 ** return a pointer to a buffer containing the appropriately
6067 ** reformatted spec (and, therefore, subsequent calls to that routine
6068 ** will clobber the result), while the routines of the same names with
6069 ** a _ts suffix appended will return a pointer to a mallocd string
6070 ** containing the appropriately reformatted spec.
6071 ** In all cases, only explicit syntax is altered; no check is made that
6072 ** the resulting string is valid or that the directory in question
6073 ** actually exists.
6074 **
6075 **   fileify_dirspec() - convert a directory spec into the name of the
6076 **     directory file (i.e. what you can stat() to see if it's a dir).
6077 **     The style (VMS or Unix) of the result is the same as the style
6078 **     of the parameter passed in.
6079 **   pathify_dirspec() - convert a directory spec into a path (i.e.
6080 **     what you prepend to a filename to indicate what directory it's in).
6081 **     The style (VMS or Unix) of the result is the same as the style
6082 **     of the parameter passed in.
6083 **   tounixpath() - convert a directory spec into a Unix-style path.
6084 **   tovmspath() - convert a directory spec into a VMS-style path.
6085 **   tounixspec() - convert any file spec into a Unix-style file spec.
6086 **   tovmsspec() - convert any file spec into a VMS-style spec.
6087 **   xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6088 **
6089 ** Copyright 1996 by Charles Bailey  <bailey@newman.upenn.edu>
6090 ** Permission is given to distribute this code as part of the Perl
6091 ** standard distribution under the terms of the GNU General Public
6092 ** License or the Perl Artistic License.  Copies of each may be
6093 ** found in the Perl standard distribution.
6094  */
6095 
6096 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6097 static char *
6098 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6099 {
6100     unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
6101     char *cp1, *cp2, *lastdir;
6102     char *trndir, *vmsdir;
6103     unsigned short int trnlnm_iter_count;
6104     int is_vms = 0;
6105     int is_unix = 0;
6106     int sts;
6107     if (utf8_fl != NULL)
6108 	*utf8_fl = 0;
6109 
6110     if (!dir || !*dir) {
6111       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6112     }
6113     dirlen = strlen(dir);
6114     while (dirlen && dir[dirlen-1] == '/') --dirlen;
6115     if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6116       if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6117         dir = "/sys$disk";
6118         dirlen = 9;
6119       }
6120       else
6121 	dirlen = 1;
6122     }
6123     if (dirlen > (VMS_MAXRSS - 1)) {
6124       set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6125       return NULL;
6126     }
6127     trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6128     if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6129     if (!strpbrk(dir+1,"/]>:")  &&
6130 	(!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6131       strcpy(trndir,*dir == '/' ? dir + 1: dir);
6132       trnlnm_iter_count = 0;
6133       while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6134         trnlnm_iter_count++;
6135         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6136       }
6137       dirlen = strlen(trndir);
6138     }
6139     else {
6140       strncpy(trndir,dir,dirlen);
6141       trndir[dirlen] = '\0';
6142     }
6143 
6144     /* At this point we are done with *dir and use *trndir which is a
6145      * copy that can be modified.  *dir must not be modified.
6146      */
6147 
6148     /* If we were handed a rooted logical name or spec, treat it like a
6149      * simple directory, so that
6150      *    $ Define myroot dev:[dir.]
6151      *    ... do_fileify_dirspec("myroot",buf,1) ...
6152      * does something useful.
6153      */
6154     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6155       trndir[--dirlen] = '\0';
6156       trndir[dirlen-1] = ']';
6157     }
6158     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6159       trndir[--dirlen] = '\0';
6160       trndir[dirlen-1] = '>';
6161     }
6162 
6163     if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6164       /* If we've got an explicit filename, we can just shuffle the string. */
6165       if (*(cp1+1)) hasfilename = 1;
6166       /* Similarly, we can just back up a level if we've got multiple levels
6167          of explicit directories in a VMS spec which ends with directories. */
6168       else {
6169         for (cp2 = cp1; cp2 > trndir; cp2--) {
6170 	  if (*cp2 == '.') {
6171 	    if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6172 /* fix-me, can not scan EFS file specs backward like this */
6173               *cp2 = *cp1; *cp1 = '\0';
6174               hasfilename = 1;
6175 	      break;
6176 	    }
6177           }
6178           if (*cp2 == '[' || *cp2 == '<') break;
6179         }
6180       }
6181     }
6182 
6183     vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6184     if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6185     cp1 = strpbrk(trndir,"]:>");
6186     if (hasfilename || !cp1) { /* filename present or not VMS */
6187 
6188       if (decc_efs_charset && !cp1) {
6189 
6190           /* EFS handling for UNIX mode */
6191 
6192           /* Just remove the trailing '/' and we should be done */
6193           STRLEN trndir_len;
6194           trndir_len = strlen(trndir);
6195 
6196           if (trndir_len > 1) {
6197               trndir_len--;
6198               if (trndir[trndir_len] == '/') {
6199                   trndir[trndir_len] = '\0';
6200               }
6201           }
6202           strcpy(buf, trndir);
6203           PerlMem_free(trndir);
6204           PerlMem_free(vmsdir);
6205           return buf;
6206       }
6207 
6208       /* For non-EFS mode, this is left for backwards compatibility */
6209       /* For EFS mode, this is only done for VMS format filespecs as */
6210       /* Perl programs generally have problems when a UNIX format spec */
6211       /* returns a VMS format spec */
6212       if (trndir[0] == '.') {
6213         if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6214 	  PerlMem_free(trndir);
6215 	  PerlMem_free(vmsdir);
6216           return int_fileify_dirspec("[]", buf, NULL);
6217 	}
6218         else if (trndir[1] == '.' &&
6219                (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6220 	  PerlMem_free(trndir);
6221 	  PerlMem_free(vmsdir);
6222           return int_fileify_dirspec("[-]", buf, NULL);
6223 	}
6224       }
6225       if (dirlen && trndir[dirlen-1] == '/') {    /* path ends with '/'; just add .dir;1 */
6226         dirlen -= 1;                 /* to last element */
6227         lastdir = strrchr(trndir,'/');
6228       }
6229       else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6230         /* If we have "/." or "/..", VMSify it and let the VMS code
6231          * below expand it, rather than repeating the code to handle
6232          * relative components of a filespec here */
6233         do {
6234           if (*(cp1+2) == '.') cp1++;
6235           if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6236 	    char * ret_chr;
6237             if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6238 		PerlMem_free(trndir);
6239 		PerlMem_free(vmsdir);
6240 		return NULL;
6241 	    }
6242             if (strchr(vmsdir,'/') != NULL) {
6243               /* If int_tovmsspec() returned it, it must have VMS syntax
6244                * delimiters in it, so it's a mixed VMS/Unix spec.  We take
6245                * the time to check this here only so we avoid a recursion
6246                * loop; otherwise, gigo.
6247                */
6248 	      PerlMem_free(trndir);
6249 	      PerlMem_free(vmsdir);
6250               set_errno(EINVAL);  set_vaxc_errno(RMS$_SYN);
6251 	      return NULL;
6252             }
6253             if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6254 		PerlMem_free(trndir);
6255 		PerlMem_free(vmsdir);
6256 		return NULL;
6257 	    }
6258 	    ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6259 	    PerlMem_free(trndir);
6260 	    PerlMem_free(vmsdir);
6261             return ret_chr;
6262           }
6263           cp1++;
6264         } while ((cp1 = strstr(cp1,"/.")) != NULL);
6265         lastdir = strrchr(trndir,'/');
6266       }
6267       else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6268 	char * ret_chr;
6269         /* Ditto for specs that end in an MFD -- let the VMS code
6270          * figure out whether it's a real device or a rooted logical. */
6271 
6272         /* This should not happen any more.  Allowing the fake /000000
6273          * in a UNIX pathname causes all sorts of problems when trying
6274          * to run in UNIX emulation.  So the VMS to UNIX conversions
6275          * now remove the fake /000000 directories.
6276          */
6277 
6278         trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6279         if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6280 	    PerlMem_free(trndir);
6281 	    PerlMem_free(vmsdir);
6282 	    return NULL;
6283 	}
6284         if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6285 	    PerlMem_free(trndir);
6286 	    PerlMem_free(vmsdir);
6287 	    return NULL;
6288 	}
6289 	ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6290 	PerlMem_free(trndir);
6291 	PerlMem_free(vmsdir);
6292         return ret_chr;
6293       }
6294       else {
6295 
6296         if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6297              !(lastdir = cp1 = strrchr(trndir,']')) &&
6298              !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6299 
6300         cp2 = strrchr(cp1,'.');
6301         if (cp2) {
6302             int e_len, vs_len = 0;
6303             int is_dir = 0;
6304             char * cp3;
6305             cp3 = strchr(cp2,';');
6306             e_len = strlen(cp2);
6307             if (cp3) {
6308                 vs_len = strlen(cp3);
6309                 e_len = e_len - vs_len;
6310             }
6311             is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6312             if (!is_dir) {
6313                 if (!decc_efs_charset) {
6314                     /* If this is not EFS, then not a directory */
6315                     PerlMem_free(trndir);
6316                     PerlMem_free(vmsdir);
6317                     set_errno(ENOTDIR);
6318                     set_vaxc_errno(RMS$_DIR);
6319                     return NULL;
6320                 }
6321             } else {
6322                 /* Ok, here we have an issue, technically if a .dir shows */
6323                 /* from inside a directory, then we should treat it as */
6324                 /* xxx^.dir.dir.  But we do not have that context at this */
6325                 /* point unless this is totally restructured, so we remove */
6326                 /* The .dir for now, and fix this better later */
6327                 dirlen = cp2 - trndir;
6328             }
6329         }
6330 
6331       }
6332 
6333       retlen = dirlen + 6;
6334       memcpy(buf, trndir, dirlen);
6335       buf[dirlen] = '\0';
6336 
6337       /* We've picked up everything up to the directory file name.
6338          Now just add the type and version, and we're set. */
6339 
6340       /* We should only add type for VMS syntax, but historically Perl
6341          has added it for UNIX style also */
6342 
6343       /* Fix me - we should not be using the same routine for VMS and
6344          UNIX format files.  Things are too tangled so we need to lookup
6345          what syntax the output is */
6346 
6347       is_unix = 0;
6348       is_vms = 0;
6349       lastdir = strrchr(trndir,'/');
6350       if (lastdir) {
6351           is_unix = 1;
6352       } else {
6353           lastdir = strpbrk(trndir,"]:>");
6354           if (lastdir) {
6355               is_vms = 1;
6356           }
6357       }
6358 
6359       if ((is_vms == 0) && (is_unix == 0)) {
6360           /* We still do not  know? */
6361           is_unix = decc_filename_unix_report;
6362           if (is_unix == 0)
6363               is_vms = 1;
6364       }
6365 
6366       if ((is_unix && !decc_efs_charset) || is_vms) {
6367 
6368            /* It is a bug to add a .dir to a UNIX format directory spec */
6369            /* However Perl on VMS may have programs that expect this so */
6370            /* If not using EFS character specifications allow it. */
6371 
6372            if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6373                /* Traditionally Perl expects filenames in lower case */
6374                strcat(buf, ".dir");
6375            } else {
6376                /* VMS expects the .DIR to be in upper case */
6377                strcat(buf, ".DIR");
6378            }
6379 
6380            /* It is also a bug to put a VMS format version on a UNIX file */
6381            /* specification.  Perl self tests are looking for this */
6382            if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6383                strcat(buf, ";1");
6384       }
6385       PerlMem_free(trndir);
6386       PerlMem_free(vmsdir);
6387       return buf;
6388     }
6389     else {  /* VMS-style directory spec */
6390 
6391       char *esa, *esal, term, *cp;
6392       char *my_esa;
6393       int my_esa_len;
6394       unsigned long int sts, cmplen, haslower = 0;
6395       unsigned int nam_fnb;
6396       char * nam_type;
6397       struct FAB dirfab = cc$rms_fab;
6398       rms_setup_nam(savnam);
6399       rms_setup_nam(dirnam);
6400 
6401       esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6402       if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6403       esal = NULL;
6404 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6405       esal = PerlMem_malloc(VMS_MAXRSS);
6406       if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6407 #endif
6408       rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6409       rms_bind_fab_nam(dirfab, dirnam);
6410       rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6411       rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6412 #ifdef NAM$M_NO_SHORT_UPCASE
6413       if (decc_efs_case_preserve)
6414 	rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6415 #endif
6416 
6417       for (cp = trndir; *cp; cp++)
6418         if (islower(*cp)) { haslower = 1; break; }
6419       if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6420         if ((dirfab.fab$l_sts == RMS$_DIR) ||
6421             (dirfab.fab$l_sts == RMS$_DNF) ||
6422             (dirfab.fab$l_sts == RMS$_PRV)) {
6423             rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6424             sts = sys$parse(&dirfab);
6425         }
6426         if (!sts) {
6427 	  PerlMem_free(esa);
6428 	  if (esal != NULL)
6429 	      PerlMem_free(esal);
6430 	  PerlMem_free(trndir);
6431 	  PerlMem_free(vmsdir);
6432           set_errno(EVMSERR);
6433           set_vaxc_errno(dirfab.fab$l_sts);
6434           return NULL;
6435         }
6436       }
6437       else {
6438         savnam = dirnam;
6439 	/* Does the file really exist? */
6440         if (sys$search(&dirfab)& STS$K_SUCCESS) {
6441           /* Yes; fake the fnb bits so we'll check type below */
6442           rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6443         }
6444         else { /* No; just work with potential name */
6445           if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6446           else {
6447 	    int fab_sts;
6448 	    fab_sts = dirfab.fab$l_sts;
6449 	    sts = rms_free_search_context(&dirfab);
6450 	    PerlMem_free(esa);
6451 	    if (esal != NULL)
6452 		PerlMem_free(esal);
6453 	    PerlMem_free(trndir);
6454 	    PerlMem_free(vmsdir);
6455             set_errno(EVMSERR);  set_vaxc_errno(fab_sts);
6456             return NULL;
6457           }
6458         }
6459       }
6460 
6461       /* Make sure we are using the right buffer */
6462 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6463       if (esal != NULL) {
6464 	my_esa = esal;
6465 	my_esa_len = rms_nam_esll(dirnam);
6466       } else {
6467 #endif
6468 	my_esa = esa;
6469         my_esa_len = rms_nam_esl(dirnam);
6470 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6471       }
6472 #endif
6473       my_esa[my_esa_len] = '\0';
6474       if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6475         cp1 = strchr(my_esa,']');
6476         if (!cp1) cp1 = strchr(my_esa,'>');
6477         if (cp1) {  /* Should always be true */
6478           my_esa_len -= cp1 - my_esa - 1;
6479           memmove(my_esa, cp1 + 1, my_esa_len);
6480         }
6481       }
6482       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) {  /* Was type specified? */
6483         /* Yep; check version while we're at it, if it's there. */
6484         cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6485         if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6486           /* Something other than .DIR[;1].  Bzzt. */
6487 	  sts = rms_free_search_context(&dirfab);
6488 	  PerlMem_free(esa);
6489 	  if (esal != NULL)
6490 	     PerlMem_free(esal);
6491 	  PerlMem_free(trndir);
6492 	  PerlMem_free(vmsdir);
6493           set_errno(ENOTDIR);
6494           set_vaxc_errno(RMS$_DIR);
6495           return NULL;
6496         }
6497       }
6498 
6499       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6500         /* They provided at least the name; we added the type, if necessary, */
6501         strcpy(buf, my_esa);
6502 	sts = rms_free_search_context(&dirfab);
6503 	PerlMem_free(trndir);
6504 	PerlMem_free(esa);
6505 	if (esal != NULL)
6506 	    PerlMem_free(esal);
6507 	PerlMem_free(vmsdir);
6508         return buf;
6509       }
6510       if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6511         for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6512         *cp1 = '\0';
6513         my_esa_len -= 9;
6514       }
6515       if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6516       if (cp1 == NULL) { /* should never happen */
6517 	sts = rms_free_search_context(&dirfab);
6518 	PerlMem_free(trndir);
6519 	PerlMem_free(esa);
6520 	if (esal != NULL)
6521 	    PerlMem_free(esal);
6522 	PerlMem_free(vmsdir);
6523         return NULL;
6524       }
6525       term = *cp1;
6526       *cp1 = '\0';
6527       retlen = strlen(my_esa);
6528       cp1 = strrchr(my_esa,'.');
6529       /* ODS-5 directory specifications can have extra "." in them. */
6530       /* Fix-me, can not scan EFS file specifications backwards */
6531       while (cp1 != NULL) {
6532         if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6533 	  break;
6534 	else {
6535 	   cp1--;
6536 	   while ((cp1 > my_esa) && (*cp1 != '.'))
6537 	     cp1--;
6538 	}
6539 	if (cp1 == my_esa)
6540 	  cp1 = NULL;
6541       }
6542 
6543       if ((cp1) != NULL) {
6544         /* There's more than one directory in the path.  Just roll back. */
6545         *cp1 = term;
6546         strcpy(buf, my_esa);
6547       }
6548       else {
6549         if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6550           /* Go back and expand rooted logical name */
6551           rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6552 #ifdef NAM$M_NO_SHORT_UPCASE
6553 	  if (decc_efs_case_preserve)
6554 	    rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6555 #endif
6556           if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6557 	    sts = rms_free_search_context(&dirfab);
6558 	    PerlMem_free(esa);
6559 	    if (esal != NULL)
6560 		PerlMem_free(esal);
6561 	    PerlMem_free(trndir);
6562 	    PerlMem_free(vmsdir);
6563             set_errno(EVMSERR);
6564             set_vaxc_errno(dirfab.fab$l_sts);
6565             return NULL;
6566           }
6567 
6568 	  /* This changes the length of the string of course */
6569 	  if (esal != NULL) {
6570 	      my_esa_len = rms_nam_esll(dirnam);
6571 	  } else {
6572 	      my_esa_len = rms_nam_esl(dirnam);
6573 	  }
6574 
6575           retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6576           cp1 = strstr(my_esa,"][");
6577           if (!cp1) cp1 = strstr(my_esa,"]<");
6578           dirlen = cp1 - my_esa;
6579           memcpy(buf, my_esa, dirlen);
6580           if (!strncmp(cp1+2,"000000]",7)) {
6581             buf[dirlen-1] = '\0';
6582 	    /* fix-me Not full ODS-5, just extra dots in directories for now */
6583 	    cp1 = buf + dirlen - 1;
6584 	    while (cp1 > buf)
6585 	    {
6586 	      if (*cp1 == '[')
6587 		break;
6588 	      if (*cp1 == '.') {
6589 		if (*(cp1-1) != '^')
6590 		  break;
6591 	      }
6592 	      cp1--;
6593 	    }
6594             if (*cp1 == '.') *cp1 = ']';
6595             else {
6596               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6597               memmove(cp1+1,"000000]",7);
6598             }
6599           }
6600           else {
6601             memmove(buf+dirlen, cp1+2, retlen-dirlen);
6602             buf[retlen] = '\0';
6603             /* Convert last '.' to ']' */
6604             cp1 = buf+retlen-1;
6605 	    while (*cp != '[') {
6606 	      cp1--;
6607 	      if (*cp1 == '.') {
6608 		/* Do not trip on extra dots in ODS-5 directories */
6609 		if ((cp1 == buf) || (*(cp1-1) != '^'))
6610 		break;
6611 	      }
6612 	    }
6613             if (*cp1 == '.') *cp1 = ']';
6614             else {
6615               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6616               memmove(cp1+1,"000000]",7);
6617             }
6618           }
6619         }
6620         else {  /* This is a top-level dir.  Add the MFD to the path. */
6621           cp1 = my_esa;
6622           cp2 = buf;
6623           while ((*cp1 != ':')  && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6624           strcpy(cp2,":[000000]");
6625           cp1 += 2;
6626           strcpy(cp2+9,cp1);
6627         }
6628       }
6629       sts = rms_free_search_context(&dirfab);
6630       /* We've set up the string up through the filename.  Add the
6631          type and version, and we're done. */
6632       strcat(buf,".DIR;1");
6633 
6634       /* $PARSE may have upcased filespec, so convert output to lower
6635        * case if input contained any lowercase characters. */
6636       if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6637       PerlMem_free(trndir);
6638       PerlMem_free(esa);
6639       if (esal != NULL)
6640 	PerlMem_free(esal);
6641       PerlMem_free(vmsdir);
6642       return buf;
6643     }
6644 }  /* end of int_fileify_dirspec() */
6645 
6646 
6647 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6648 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6649 {
6650     static char __fileify_retbuf[VMS_MAXRSS];
6651     char * fileified, *ret_spec, *ret_buf;
6652 
6653     fileified = NULL;
6654     ret_buf = buf;
6655     if (ret_buf == NULL) {
6656         if (ts) {
6657             Newx(fileified, VMS_MAXRSS, char);
6658             if (fileified == NULL)
6659                 _ckvmssts(SS$_INSFMEM);
6660             ret_buf = fileified;
6661         } else {
6662             ret_buf = __fileify_retbuf;
6663         }
6664     }
6665 
6666     ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6667 
6668     if (ret_spec == NULL) {
6669        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6670        if (fileified)
6671            Safefree(fileified);
6672     }
6673 
6674     return ret_spec;
6675 }  /* end of do_fileify_dirspec() */
6676 /*}}}*/
6677 
6678 /* External entry points */
6679 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6680 { return do_fileify_dirspec(dir,buf,0,NULL); }
6681 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6682 { return do_fileify_dirspec(dir,buf,1,NULL); }
6683 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6684 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6685 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6686 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6687 
6688 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6689     char * v_spec, int v_len, char * r_spec, int r_len,
6690     char * d_spec, int d_len, char * n_spec, int n_len,
6691     char * e_spec, int e_len, char * vs_spec, int vs_len) {
6692 
6693     /* VMS specification - Try to do this the simple way */
6694     if ((v_len + r_len > 0) || (d_len > 0)) {
6695         int is_dir;
6696 
6697         /* No name or extension component, already a directory */
6698         if ((n_len + e_len + vs_len) == 0) {
6699             strcpy(buf, dir);
6700             return buf;
6701         }
6702 
6703         /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6704         /* This results from catfile() being used instead of catdir() */
6705         /* So even though it should not work, we need to allow it */
6706 
6707         /* If this is .DIR;1 then do a simple conversion */
6708         is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6709         if (is_dir || (e_len == 0) && (d_len > 0)) {
6710              int len;
6711              len = v_len + r_len + d_len - 1;
6712              char dclose = d_spec[d_len - 1];
6713              strncpy(buf, dir, len);
6714              buf[len] = '.';
6715              len++;
6716              strncpy(&buf[len], n_spec, n_len);
6717              len += n_len;
6718              buf[len] = dclose;
6719              buf[len + 1] = '\0';
6720              return buf;
6721         }
6722 
6723 #ifdef HAS_SYMLINK
6724         else if (d_len > 0) {
6725             /* In the olden days, a directory needed to have a .DIR */
6726             /* extension to be a valid directory, but now it could  */
6727             /* be a symbolic link */
6728             int len;
6729             len = v_len + r_len + d_len - 1;
6730             char dclose = d_spec[d_len - 1];
6731             strncpy(buf, dir, len);
6732             buf[len] = '.';
6733             len++;
6734             strncpy(&buf[len], n_spec, n_len);
6735             len += n_len;
6736             if (e_len > 0) {
6737                 if (decc_efs_charset) {
6738                     buf[len] = '^';
6739                     len++;
6740                     strncpy(&buf[len], e_spec, e_len);
6741                     len += e_len;
6742                 } else {
6743                     set_vaxc_errno(RMS$_DIR);
6744                     set_errno(ENOTDIR);
6745                     return NULL;
6746                 }
6747             }
6748             buf[len] = dclose;
6749             buf[len + 1] = '\0';
6750             return buf;
6751         }
6752 #else
6753         else {
6754             set_vaxc_errno(RMS$_DIR);
6755             set_errno(ENOTDIR);
6756             return NULL;
6757         }
6758 #endif
6759     }
6760     set_vaxc_errno(RMS$_DIR);
6761     set_errno(ENOTDIR);
6762     return NULL;
6763 }
6764 
6765 
6766 /* Internal routine to make sure or convert a directory to be in a */
6767 /* path specification.  No utf8 flag because it is not changed or used */
6768 static char *int_pathify_dirspec(const char *dir, char *buf)
6769 {
6770     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6771     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6772     char * exp_spec, *ret_spec;
6773     char * trndir;
6774     unsigned short int trnlnm_iter_count;
6775     STRLEN trnlen;
6776     int need_to_lower;
6777 
6778     if (vms_debug_fileify) {
6779         if (dir == NULL)
6780             fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6781         else
6782             fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6783     }
6784 
6785     /* We may need to lower case the result if we translated  */
6786     /* a logical name or got the current working directory */
6787     need_to_lower = 0;
6788 
6789     if (!dir || !*dir) {
6790       set_errno(EINVAL);
6791       set_vaxc_errno(SS$_BADPARAM);
6792       return NULL;
6793     }
6794 
6795     trndir = PerlMem_malloc(VMS_MAXRSS);
6796     if (trndir == NULL)
6797         _ckvmssts_noperl(SS$_INSFMEM);
6798 
6799     /* If no directory specified use the current default */
6800     if (*dir)
6801         strcpy(trndir, dir);
6802     else {
6803         getcwd(trndir, VMS_MAXRSS - 1);
6804         need_to_lower = 1;
6805     }
6806 
6807     /* now deal with bare names that could be logical names */
6808     trnlnm_iter_count = 0;
6809     while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6810            && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6811         trnlnm_iter_count++;
6812         need_to_lower = 1;
6813         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6814             break;
6815         trnlen = strlen(trndir);
6816 
6817         /* Trap simple rooted lnms, and return lnm:[000000] */
6818         if (!strcmp(trndir+trnlen-2,".]")) {
6819             strcpy(buf, dir);
6820             strcat(buf, ":[000000]");
6821             PerlMem_free(trndir);
6822 
6823             if (vms_debug_fileify) {
6824                 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6825             }
6826             return buf;
6827         }
6828     }
6829 
6830     /* At this point we do not work with *dir, but the copy in  *trndir */
6831 
6832     if (need_to_lower && !decc_efs_case_preserve) {
6833         /* Legacy mode, lower case the returned value */
6834         __mystrtolower(trndir);
6835     }
6836 
6837 
6838     /* Some special cases, '..', '.' */
6839     sts = 0;
6840     if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6841        /* Force UNIX filespec */
6842        sts = 1;
6843 
6844     } else {
6845         /* Is this Unix or VMS format? */
6846         sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6847                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6848                              &e_len, &vs_spec, &vs_len);
6849         if (sts == 0) {
6850 
6851             /* Just a filename? */
6852             if ((v_len + r_len + d_len) == 0) {
6853 
6854                 /* Now we have a problem, this could be Unix or VMS */
6855                 /* We have to guess.  .DIR usually means VMS */
6856 
6857                 /* In UNIX report mode, the .DIR extension is removed */
6858                 /* if one shows up, it is for a non-directory or a directory */
6859                 /* in EFS charset mode */
6860 
6861                 /* So if we are in Unix report mode, assume that this */
6862                 /* is a relative Unix directory specification */
6863 
6864                 sts = 1;
6865                 if (!decc_filename_unix_report && decc_efs_charset) {
6866                     int is_dir;
6867                     is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6868 
6869                     if (is_dir) {
6870                         /* Traditional mode, assume .DIR is directory */
6871                         buf[0] = '[';
6872                         buf[1] = '.';
6873                         strncpy(&buf[2], n_spec, n_len);
6874                         buf[n_len + 2] = ']';
6875                         buf[n_len + 3] = '\0';
6876                         PerlMem_free(trndir);
6877                         if (vms_debug_fileify) {
6878                             fprintf(stderr,
6879                                     "int_pathify_dirspec: buf = %s\n",
6880                                     buf);
6881                         }
6882                         return buf;
6883                     }
6884                 }
6885             }
6886         }
6887     }
6888     if (sts == 0) {
6889         ret_spec = int_pathify_dirspec_simple(trndir, buf,
6890             v_spec, v_len, r_spec, r_len,
6891             d_spec, d_len, n_spec, n_len,
6892             e_spec, e_len, vs_spec, vs_len);
6893 
6894         if (ret_spec != NULL) {
6895             PerlMem_free(trndir);
6896             if (vms_debug_fileify) {
6897                 fprintf(stderr,
6898                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6899             }
6900             return ret_spec;
6901         }
6902 
6903         /* Simple way did not work, which means that a logical name */
6904         /* was present for the directory specification.             */
6905         /* Need to use an rmsexpand variant to decode it completely */
6906         exp_spec = PerlMem_malloc(VMS_MAXRSS);
6907         if (exp_spec == NULL)
6908             _ckvmssts_noperl(SS$_INSFMEM);
6909 
6910         ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6911         if (ret_spec != NULL) {
6912             sts = vms_split_path(exp_spec, &v_spec, &v_len,
6913                                  &r_spec, &r_len, &d_spec, &d_len,
6914                                  &n_spec, &n_len, &e_spec,
6915                                  &e_len, &vs_spec, &vs_len);
6916             if (sts == 0) {
6917                 ret_spec = int_pathify_dirspec_simple(
6918                     exp_spec, buf, v_spec, v_len, r_spec, r_len,
6919                     d_spec, d_len, n_spec, n_len,
6920                     e_spec, e_len, vs_spec, vs_len);
6921 
6922                 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6923                     /* Legacy mode, lower case the returned value */
6924                     __mystrtolower(ret_spec);
6925                 }
6926             } else {
6927                 set_vaxc_errno(RMS$_DIR);
6928                 set_errno(ENOTDIR);
6929                 ret_spec = NULL;
6930             }
6931         }
6932         PerlMem_free(exp_spec);
6933         PerlMem_free(trndir);
6934         if (vms_debug_fileify) {
6935             if (ret_spec == NULL)
6936                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6937             else
6938                 fprintf(stderr,
6939                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6940         }
6941         return ret_spec;
6942 
6943     } else {
6944         /* Unix specification, Could be trivial conversion */
6945         STRLEN dir_len;
6946         dir_len = strlen(trndir);
6947 
6948         /* If the extended file character set is in effect */
6949         /* then pathify is simple */
6950 
6951         if (!decc_efs_charset) {
6952             /* Have to deal with traiing '.dir' or extra '.' */
6953             /* that should not be there in legacy mode, but is */
6954 
6955             char * lastdot;
6956             char * lastslash;
6957             int is_dir;
6958 
6959             lastslash = strrchr(trndir, '/');
6960             if (lastslash == NULL)
6961                 lastslash = trndir;
6962             else
6963                 lastslash++;
6964 
6965             lastdot = NULL;
6966 
6967             /* '..' or '.' are valid directory components */
6968             is_dir = 0;
6969             if (lastslash[0] == '.') {
6970                 if (lastslash[1] == '\0') {
6971                    is_dir = 1;
6972                 } else if (lastslash[1] == '.') {
6973                     if (lastslash[2] == '\0') {
6974                         is_dir = 1;
6975                     } else {
6976                         /* And finally allow '...' */
6977                         if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6978                             is_dir = 1;
6979                         }
6980                     }
6981                 }
6982             }
6983 
6984             if (!is_dir) {
6985                lastdot = strrchr(lastslash, '.');
6986             }
6987             if (lastdot != NULL) {
6988                 STRLEN e_len;
6989 
6990                 /* '.dir' is discarded, and any other '.' is invalid */
6991                 e_len = strlen(lastdot);
6992 
6993                 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6994 
6995                 if (is_dir) {
6996                     dir_len = dir_len - 4;
6997 
6998                 }
6999             }
7000         }
7001 
7002         strcpy(buf, trndir);
7003         if (buf[dir_len - 1] != '/') {
7004             buf[dir_len] = '/';
7005             buf[dir_len + 1] = '\0';
7006         }
7007 
7008         /* Under ODS-2 rules, '.' becomes '_', so fix it up */
7009         if (!decc_efs_charset) {
7010              int dir_start = 0;
7011              char * str = buf;
7012              if (str[0] == '.') {
7013                  char * dots = str;
7014                  int cnt = 1;
7015                  while ((dots[cnt] == '.') && (cnt < 3))
7016                      cnt++;
7017                  if (cnt <= 3) {
7018                      if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
7019                          dir_start = 1;
7020                          str += cnt;
7021                      }
7022                  }
7023              }
7024              for (; *str; ++str) {
7025                  while (*str == '/') {
7026                      dir_start = 1;
7027                      *str++;
7028                  }
7029                  if (dir_start) {
7030 
7031                      /* Have to skip up to three dots which could be */
7032                      /* directories, 3 dots being a VMS extension for Perl */
7033                      char * dots = str;
7034                      int cnt = 0;
7035                      while ((dots[cnt] == '.') && (cnt < 3)) {
7036                          cnt++;
7037                      }
7038                      if (dots[cnt] == '\0')
7039                          break;
7040                      if ((cnt > 1) && (dots[cnt] != '/')) {
7041                          dir_start = 0;
7042                      } else {
7043                          str += cnt;
7044                      }
7045 
7046                      /* too many dots? */
7047                      if ((cnt == 0) || (cnt > 3)) {
7048                          dir_start = 0;
7049                      }
7050                  }
7051                  if (!dir_start && (*str == '.')) {
7052                      *str = '_';
7053                  }
7054              }
7055         }
7056         PerlMem_free(trndir);
7057         ret_spec = buf;
7058         if (vms_debug_fileify) {
7059             if (ret_spec == NULL)
7060                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
7061             else
7062                 fprintf(stderr,
7063                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
7064         }
7065         return ret_spec;
7066     }
7067 }
7068 
7069 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
7070 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
7071 {
7072     static char __pathify_retbuf[VMS_MAXRSS];
7073     char * pathified, *ret_spec, *ret_buf;
7074 
7075     pathified = NULL;
7076     ret_buf = buf;
7077     if (ret_buf == NULL) {
7078         if (ts) {
7079             Newx(pathified, VMS_MAXRSS, char);
7080             if (pathified == NULL)
7081                 _ckvmssts(SS$_INSFMEM);
7082             ret_buf = pathified;
7083         } else {
7084             ret_buf = __pathify_retbuf;
7085         }
7086     }
7087 
7088     ret_spec = int_pathify_dirspec(dir, ret_buf);
7089 
7090     if (ret_spec == NULL) {
7091        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7092        if (pathified)
7093            Safefree(pathified);
7094     }
7095 
7096     return ret_spec;
7097 
7098 }  /* end of do_pathify_dirspec() */
7099 
7100 
7101 /* External entry points */
7102 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
7103 { return do_pathify_dirspec(dir,buf,0,NULL); }
7104 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7105 { return do_pathify_dirspec(dir,buf,1,NULL); }
7106 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7107 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7108 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7109 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
7110 
7111 /* Internal tounixspec routine that does not use a thread context */
7112 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7113 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7114 {
7115   char *dirend, *cp1, *cp3, *tmp;
7116   const char *cp2;
7117   int devlen, dirlen, retlen = VMS_MAXRSS;
7118   int expand = 1; /* guarantee room for leading and trailing slashes */
7119   unsigned short int trnlnm_iter_count;
7120   int cmp_rslt;
7121   if (utf8_fl != NULL)
7122     *utf8_fl = 0;
7123 
7124   if (vms_debug_fileify) {
7125       if (spec == NULL)
7126           fprintf(stderr, "int_tounixspec: spec = NULL\n");
7127       else
7128           fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7129   }
7130 
7131 
7132   if (spec == NULL) {
7133       set_errno(EINVAL);
7134       set_vaxc_errno(SS$_BADPARAM);
7135       return NULL;
7136   }
7137   if (strlen(spec) > (VMS_MAXRSS-1)) {
7138       set_errno(E2BIG);
7139       set_vaxc_errno(SS$_BUFFEROVF);
7140       return NULL;
7141   }
7142 
7143   /* New VMS specific format needs translation
7144    * glob passes filenames with trailing '\n' and expects this preserved.
7145    */
7146   if (decc_posix_compliant_pathnames) {
7147     if (strncmp(spec, "\"^UP^", 5) == 0) {
7148       char * uspec;
7149       char *tunix;
7150       int tunix_len;
7151       int nl_flag;
7152 
7153       tunix = PerlMem_malloc(VMS_MAXRSS);
7154       if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7155       strcpy(tunix, spec);
7156       tunix_len = strlen(tunix);
7157       nl_flag = 0;
7158       if (tunix[tunix_len - 1] == '\n') {
7159 	tunix[tunix_len - 1] = '\"';
7160 	tunix[tunix_len] = '\0';
7161 	tunix_len--;
7162 	nl_flag = 1;
7163       }
7164       uspec = decc$translate_vms(tunix);
7165       PerlMem_free(tunix);
7166       if ((int)uspec > 0) {
7167 	strcpy(rslt,uspec);
7168 	if (nl_flag) {
7169 	  strcat(rslt,"\n");
7170 	}
7171 	else {
7172 	  /* If we can not translate it, makemaker wants as-is */
7173 	  strcpy(rslt, spec);
7174 	}
7175 	return rslt;
7176       }
7177     }
7178   }
7179 
7180   cmp_rslt = 0; /* Presume VMS */
7181   cp1 = strchr(spec, '/');
7182   if (cp1 == NULL)
7183     cmp_rslt = 0;
7184 
7185     /* Look for EFS ^/ */
7186     if (decc_efs_charset) {
7187       while (cp1 != NULL) {
7188 	cp2 = cp1 - 1;
7189 	if (*cp2 != '^') {
7190 	  /* Found illegal VMS, assume UNIX */
7191 	  cmp_rslt = 1;
7192 	  break;
7193 	}
7194       cp1++;
7195       cp1 = strchr(cp1, '/');
7196     }
7197   }
7198 
7199   /* Look for "." and ".." */
7200   if (decc_filename_unix_report) {
7201     if (spec[0] == '.') {
7202       if ((spec[1] == '\0') || (spec[1] == '\n')) {
7203 	cmp_rslt = 1;
7204       }
7205       else {
7206 	if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7207 	  cmp_rslt = 1;
7208 	}
7209       }
7210     }
7211   }
7212   /* This is already UNIX or at least nothing VMS understands */
7213   if (cmp_rslt) {
7214     strcpy(rslt,spec);
7215     if (vms_debug_fileify) {
7216         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7217     }
7218     return rslt;
7219   }
7220 
7221   cp1 = rslt;
7222   cp2 = spec;
7223   dirend = strrchr(spec,']');
7224   if (dirend == NULL) dirend = strrchr(spec,'>');
7225   if (dirend == NULL) dirend = strchr(spec,':');
7226   if (dirend == NULL) {
7227     strcpy(rslt,spec);
7228     if (vms_debug_fileify) {
7229         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7230     }
7231     return rslt;
7232   }
7233 
7234   /* Special case 1 - sys$posix_root = / */
7235 #if __CRTL_VER >= 70000000
7236   if (!decc_disable_posix_root) {
7237     if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7238       *cp1 = '/';
7239       cp1++;
7240       cp2 = cp2 + 15;
7241       }
7242   }
7243 #endif
7244 
7245   /* Special case 2 - Convert NLA0: to /dev/null */
7246 #if __CRTL_VER < 70000000
7247   cmp_rslt = strncmp(spec,"NLA0:", 5);
7248   if (cmp_rslt != 0)
7249      cmp_rslt = strncmp(spec,"nla0:", 5);
7250 #else
7251   cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7252 #endif
7253   if (cmp_rslt == 0) {
7254     strcpy(rslt, "/dev/null");
7255     cp1 = cp1 + 9;
7256     cp2 = cp2 + 5;
7257     if (spec[6] != '\0') {
7258       cp1[9] == '/';
7259       cp1++;
7260       cp2++;
7261     }
7262   }
7263 
7264    /* Also handle special case "SYS$SCRATCH:" */
7265 #if __CRTL_VER < 70000000
7266   cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7267   if (cmp_rslt != 0)
7268      cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7269 #else
7270   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7271 #endif
7272   tmp = PerlMem_malloc(VMS_MAXRSS);
7273   if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7274   if (cmp_rslt == 0) {
7275   int islnm;
7276 
7277     islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7278     if (!islnm) {
7279       strcpy(rslt, "/tmp");
7280       cp1 = cp1 + 4;
7281       cp2 = cp2 + 12;
7282       if (spec[12] != '\0') {
7283 	cp1[4] == '/';
7284 	cp1++;
7285 	cp2++;
7286       }
7287     }
7288   }
7289 
7290   if (*cp2 != '[' && *cp2 != '<') {
7291     *(cp1++) = '/';
7292   }
7293   else {  /* the VMS spec begins with directories */
7294     cp2++;
7295     if (*cp2 == ']' || *cp2 == '>') {
7296       *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7297       PerlMem_free(tmp);
7298       return rslt;
7299     }
7300     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7301       if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7302 	PerlMem_free(tmp);
7303         if (vms_debug_fileify) {
7304             fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7305         }
7306         return NULL;
7307       }
7308       trnlnm_iter_count = 0;
7309       do {
7310         cp3 = tmp;
7311         while (*cp3 != ':' && *cp3) cp3++;
7312         *(cp3++) = '\0';
7313         if (strchr(cp3,']') != NULL) break;
7314         trnlnm_iter_count++;
7315         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7316       } while (vmstrnenv(tmp,tmp,0,fildev,0));
7317       cp1 = rslt;
7318       cp3 = tmp;
7319       *(cp1++) = '/';
7320       while (*cp3) {
7321         *(cp1++) = *(cp3++);
7322         if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7323 	    PerlMem_free(tmp);
7324             set_errno(ENAMETOOLONG);
7325             set_vaxc_errno(SS$_BUFFEROVF);
7326             if (vms_debug_fileify) {
7327                 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7328             }
7329 	    return NULL; /* No room */
7330 	}
7331       }
7332       *(cp1++) = '/';
7333     }
7334     if ((*cp2 == '^')) {
7335 	/* EFS file escape, pass the next character as is */
7336 	/* Fix me: HEX encoding for Unicode not implemented */
7337 	cp2++;
7338     }
7339     else if ( *cp2 == '.') {
7340       if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7341         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7342         cp2 += 3;
7343       }
7344       else cp2++;
7345     }
7346   }
7347   PerlMem_free(tmp);
7348   for (; cp2 <= dirend; cp2++) {
7349     if ((*cp2 == '^')) {
7350 	/* EFS file escape, pass the next character as is */
7351 	/* Fix me: HEX encoding for Unicode not implemented */
7352 	*(cp1++) = *(++cp2);
7353         /* An escaped dot stays as is -- don't convert to slash */
7354         if (*cp2 == '.') cp2++;
7355     }
7356     if (*cp2 == ':') {
7357       *(cp1++) = '/';
7358       if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7359     }
7360     else if (*cp2 == ']' || *cp2 == '>') {
7361       if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7362     }
7363     else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7364       *(cp1++) = '/';
7365       if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7366         while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7367                *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7368         if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7369             *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7370       }
7371       else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7372         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7373         cp2 += 2;
7374       }
7375     }
7376     else if (*cp2 == '-') {
7377       if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7378         while (*cp2 == '-') {
7379           cp2++;
7380           *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7381         }
7382         if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7383                                                          /* filespecs like */
7384           set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);   /* [fred.--foo.bar] */
7385           if (vms_debug_fileify) {
7386               fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7387           }
7388           return NULL;
7389         }
7390       }
7391       else *(cp1++) = *cp2;
7392     }
7393     else *(cp1++) = *cp2;
7394   }
7395   /* Translate the rest of the filename. */
7396   while (*cp2) {
7397       int dot_seen;
7398       dot_seen = 0;
7399       switch(*cp2) {
7400       /* Fixme - for compatibility with the CRTL we should be removing */
7401       /* spaces from the file specifications, but this may show that */
7402       /* some tests that were appearing to pass are not really passing */
7403       case '%':
7404           cp2++;
7405           *(cp1++) = '?';
7406           break;
7407       case '^':
7408           /* Fix me hex expansions not implemented */
7409           cp2++;  /* '^.' --> '.' and other. */
7410           if (*cp2) {
7411               if (*cp2 == '_') {
7412                   cp2++;
7413                   *(cp1++) = ' ';
7414               } else {
7415                   *(cp1++) = *(cp2++);
7416               }
7417           }
7418           break;
7419       case ';':
7420           if (decc_filename_unix_no_version) {
7421               /* Easy, drop the version */
7422               while (*cp2)
7423                   cp2++;
7424               break;
7425           } else {
7426               /* Punt - passing the version as a dot will probably */
7427               /* break perl in weird ways, but so did passing */
7428               /* through the ; as a version.  Follow the CRTL and */
7429               /* hope for the best. */
7430               cp2++;
7431               *(cp1++) = '.';
7432           }
7433           break;
7434       case '.':
7435           if (dot_seen) {
7436               /* We will need to fix this properly later */
7437               /* As Perl may be installed on an ODS-5 volume, but not */
7438               /* have the EFS_CHARSET enabled, it still may encounter */
7439               /* filenames with extra dots in them, and a precedent got */
7440               /* set which allowed them to work, that we will uphold here */
7441               /* If extra dots are present in a name and no ^ is on them */
7442               /* VMS assumes that the first one is the extension delimiter */
7443               /* the rest have an implied ^. */
7444 
7445               /* this is also a conflict as the . is also a version */
7446               /* delimiter in VMS, */
7447 
7448               *(cp1++) = *(cp2++);
7449               break;
7450           }
7451           dot_seen = 1;
7452           /* This is an extension */
7453           if (decc_readdir_dropdotnotype) {
7454               cp2++;
7455               if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7456                   /* Drop the dot for the extension */
7457                   break;
7458               } else {
7459                   *(cp1++) = '.';
7460               }
7461               break;
7462           }
7463       default:
7464           *(cp1++) = *(cp2++);
7465       }
7466   }
7467   *cp1 = '\0';
7468 
7469   /* This still leaves /000000/ when working with a
7470    * VMS device root or concealed root.
7471    */
7472   {
7473   int ulen;
7474   char * zeros;
7475 
7476       ulen = strlen(rslt);
7477 
7478       /* Get rid of "000000/ in rooted filespecs */
7479       if (ulen > 7) {
7480 	zeros = strstr(rslt, "/000000/");
7481 	if (zeros != NULL) {
7482 	  int mlen;
7483 	  mlen = ulen - (zeros - rslt) - 7;
7484 	  memmove(zeros, &zeros[7], mlen);
7485 	  ulen = ulen - 7;
7486 	  rslt[ulen] = '\0';
7487 	}
7488       }
7489   }
7490 
7491   if (vms_debug_fileify) {
7492       fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7493   }
7494   return rslt;
7495 
7496 }  /* end of int_tounixspec() */
7497 
7498 
7499 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7500 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7501 {
7502     static char __tounixspec_retbuf[VMS_MAXRSS];
7503     char * unixspec, *ret_spec, *ret_buf;
7504 
7505     unixspec = NULL;
7506     ret_buf = buf;
7507     if (ret_buf == NULL) {
7508         if (ts) {
7509             Newx(unixspec, VMS_MAXRSS, char);
7510             if (unixspec == NULL)
7511                 _ckvmssts(SS$_INSFMEM);
7512             ret_buf = unixspec;
7513         } else {
7514             ret_buf = __tounixspec_retbuf;
7515         }
7516     }
7517 
7518     ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7519 
7520     if (ret_spec == NULL) {
7521        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7522        if (unixspec)
7523            Safefree(unixspec);
7524     }
7525 
7526     return ret_spec;
7527 
7528 }  /* end of do_tounixspec() */
7529 /*}}}*/
7530 /* External entry points */
7531 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7532   { return do_tounixspec(spec,buf,0, NULL); }
7533 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7534   { return do_tounixspec(spec,buf,1, NULL); }
7535 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7536   { return do_tounixspec(spec,buf,0, utf8_fl); }
7537 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7538   { return do_tounixspec(spec,buf,1, utf8_fl); }
7539 
7540 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7541 
7542 /*
7543  This procedure is used to identify if a path is based in either
7544  the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7545  it returns the OpenVMS format directory for it.
7546 
7547  It is expecting specifications of only '/' or '/xxxx/'
7548 
7549  If a posix root does not exist, or 'xxxx' is not a directory
7550  in the posix root, it returns a failure.
7551 
7552  FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7553 
7554  It is used only internally by posix_to_vmsspec_hardway().
7555  */
7556 
7557 static int posix_root_to_vms
7558   (char *vmspath, int vmspath_len,
7559    const char *unixpath,
7560    const int * utf8_fl)
7561 {
7562 int sts;
7563 struct FAB myfab = cc$rms_fab;
7564 rms_setup_nam(mynam);
7565 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7566 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7567 char * esa, * esal, * rsa, * rsal;
7568 char *vms_delim;
7569 int dir_flag;
7570 int unixlen;
7571 
7572     dir_flag = 0;
7573     vmspath[0] = '\0';
7574     unixlen = strlen(unixpath);
7575     if (unixlen == 0) {
7576       return RMS$_FNF;
7577     }
7578 
7579 #if __CRTL_VER >= 80200000
7580   /* If not a posix spec already, convert it */
7581   if (decc_posix_compliant_pathnames) {
7582     if (strncmp(unixpath,"\"^UP^",5) != 0) {
7583       sprintf(vmspath,"\"^UP^%s\"",unixpath);
7584     }
7585     else {
7586       /* This is already a VMS specification, no conversion */
7587       unixlen--;
7588       strncpy(vmspath,unixpath, vmspath_len);
7589     }
7590   }
7591   else
7592 #endif
7593   {
7594   int path_len;
7595   int i,j;
7596 
7597      /* Check to see if this is under the POSIX root */
7598      if (decc_disable_posix_root) {
7599 	return RMS$_FNF;
7600      }
7601 
7602      /* Skip leading / */
7603      if (unixpath[0] == '/') {
7604 	unixpath++;
7605 	unixlen--;
7606      }
7607 
7608 
7609      strcpy(vmspath,"SYS$POSIX_ROOT:");
7610 
7611      /* If this is only the / , or blank, then... */
7612      if (unixpath[0] == '\0') {
7613 	/* by definition, this is the answer */
7614 	return SS$_NORMAL;
7615      }
7616 
7617      /* Need to look up a directory */
7618      vmspath[15] = '[';
7619      vmspath[16] = '\0';
7620 
7621      /* Copy and add '^' escape characters as needed */
7622      j = 16;
7623      i = 0;
7624      while (unixpath[i] != 0) {
7625      int k;
7626 
7627 	j += copy_expand_unix_filename_escape
7628 	    (&vmspath[j], &unixpath[i], &k, utf8_fl);
7629 	i += k;
7630      }
7631 
7632      path_len = strlen(vmspath);
7633      if (vmspath[path_len - 1] == '/')
7634 	path_len--;
7635      vmspath[path_len] = ']';
7636      path_len++;
7637      vmspath[path_len] = '\0';
7638 
7639   }
7640   vmspath[vmspath_len] = 0;
7641   if (unixpath[unixlen - 1] == '/')
7642   dir_flag = 1;
7643   esal = PerlMem_malloc(VMS_MAXRSS);
7644   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7645   esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7646   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7647   rsal = PerlMem_malloc(VMS_MAXRSS);
7648   if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7649   rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7650   if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7651   rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7652   rms_bind_fab_nam(myfab, mynam);
7653   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7654   rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7655   if (decc_efs_case_preserve)
7656     mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7657 #ifdef NAML$M_OPEN_SPECIAL
7658   mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7659 #endif
7660 
7661   /* Set up the remaining naml fields */
7662   sts = sys$parse(&myfab);
7663 
7664   /* It failed! Try again as a UNIX filespec */
7665   if (!(sts & 1)) {
7666     PerlMem_free(esal);
7667     PerlMem_free(esa);
7668     PerlMem_free(rsal);
7669     PerlMem_free(rsa);
7670     return sts;
7671   }
7672 
7673    /* get the Device ID and the FID */
7674    sts = sys$search(&myfab);
7675 
7676    /* These are no longer needed */
7677    PerlMem_free(esa);
7678    PerlMem_free(rsal);
7679    PerlMem_free(rsa);
7680 
7681    /* on any failure, returned the POSIX ^UP^ filespec */
7682    if (!(sts & 1)) {
7683       PerlMem_free(esal);
7684       return sts;
7685    }
7686    specdsc.dsc$a_pointer = vmspath;
7687    specdsc.dsc$w_length = vmspath_len;
7688 
7689    dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7690    dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7691    sts = lib$fid_to_name
7692       (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7693 
7694   /* on any failure, returned the POSIX ^UP^ filespec */
7695   if (!(sts & 1)) {
7696      /* This can happen if user does not have permission to read directories */
7697      if (strncmp(unixpath,"\"^UP^",5) != 0)
7698        sprintf(vmspath,"\"^UP^%s\"",unixpath);
7699      else
7700        strcpy(vmspath, unixpath);
7701   }
7702   else {
7703     vmspath[specdsc.dsc$w_length] = 0;
7704 
7705     /* Are we expecting a directory? */
7706     if (dir_flag != 0) {
7707     int i;
7708     char *eptr;
7709 
7710       eptr = NULL;
7711 
7712       i = specdsc.dsc$w_length - 1;
7713       while (i > 0) {
7714       int zercnt;
7715 	zercnt = 0;
7716 	/* Version must be '1' */
7717 	if (vmspath[i--] != '1')
7718 	  break;
7719 	/* Version delimiter is one of ".;" */
7720 	if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7721 	  break;
7722 	i--;
7723 	if (vmspath[i--] != 'R')
7724 	  break;
7725 	if (vmspath[i--] != 'I')
7726 	  break;
7727 	if (vmspath[i--] != 'D')
7728 	  break;
7729 	if (vmspath[i--] != '.')
7730 	  break;
7731 	eptr = &vmspath[i+1];
7732  	while (i > 0) {
7733 	  if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7734 	    if (vmspath[i-1] != '^') {
7735 	      if (zercnt != 6) {
7736   		*eptr = vmspath[i];
7737 		eptr[1] = '\0';
7738 		vmspath[i] = '.';
7739   		break;
7740 	      }
7741 	      else {
7742  		/* Get rid of 6 imaginary zero directory filename */
7743   		vmspath[i+1] = '\0';
7744  	      }
7745 	    }
7746 	  }
7747 	  if (vmspath[i] == '0')
7748 	    zercnt++;
7749 	  else
7750 	    zercnt = 10;
7751 	  i--;
7752 	}
7753 	break;
7754       }
7755     }
7756   }
7757   PerlMem_free(esal);
7758   return sts;
7759 }
7760 
7761 /* /dev/mumble needs to be handled special.
7762    /dev/null becomes NLA0:, And there is the potential for other stuff
7763    like /dev/tty which may need to be mapped to something.
7764 */
7765 
7766 static int
7767 slash_dev_special_to_vms
7768    (const char * unixptr,
7769     char * vmspath,
7770     int vmspath_len)
7771 {
7772 char * nextslash;
7773 int len;
7774 int cmp;
7775 int islnm;
7776 
7777     unixptr += 4;
7778     nextslash = strchr(unixptr, '/');
7779     len = strlen(unixptr);
7780     if (nextslash != NULL)
7781 	len = nextslash - unixptr;
7782     cmp = strncmp("null", unixptr, 5);
7783     if (cmp == 0) {
7784 	if (vmspath_len >= 6) {
7785 	    strcpy(vmspath, "_NLA0:");
7786 	    return SS$_NORMAL;
7787 	}
7788     }
7789 }
7790 
7791 
7792 /* The built in routines do not understand perl's special needs, so
7793     doing a manual conversion from UNIX to VMS
7794 
7795     If the utf8_fl is not null and points to a non-zero value, then
7796     treat 8 bit characters as UTF-8.
7797 
7798     The sequence starting with '$(' and ending with ')' will be passed
7799     through with out interpretation instead of being escaped.
7800 
7801   */
7802 static int posix_to_vmsspec_hardway
7803   (char *vmspath, int vmspath_len,
7804    const char *unixpath,
7805    int dir_flag,
7806    int * utf8_fl) {
7807 
7808 char *esa;
7809 const char *unixptr;
7810 const char *unixend;
7811 char *vmsptr;
7812 const char *lastslash;
7813 const char *lastdot;
7814 int unixlen;
7815 int vmslen;
7816 int dir_start;
7817 int dir_dot;
7818 int quoted;
7819 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7820 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7821 
7822   if (utf8_fl != NULL)
7823     *utf8_fl = 0;
7824 
7825   unixptr = unixpath;
7826   dir_dot = 0;
7827 
7828   /* Ignore leading "/" characters */
7829   while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7830     unixptr++;
7831   }
7832   unixlen = strlen(unixptr);
7833 
7834   /* Do nothing with blank paths */
7835   if (unixlen == 0) {
7836     vmspath[0] = '\0';
7837     return SS$_NORMAL;
7838   }
7839 
7840   quoted = 0;
7841   /* This could have a "^UP^ on the front */
7842   if (strncmp(unixptr,"\"^UP^",5) == 0) {
7843     quoted = 1;
7844     unixptr+= 5;
7845     unixlen-= 5;
7846   }
7847 
7848   lastslash = strrchr(unixptr,'/');
7849   lastdot = strrchr(unixptr,'.');
7850   unixend = strrchr(unixptr,'\"');
7851   if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7852     unixend = unixptr + unixlen;
7853   }
7854 
7855   /* last dot is last dot or past end of string */
7856   if (lastdot == NULL)
7857     lastdot = unixptr + unixlen;
7858 
7859   /* if no directories, set last slash to beginning of string */
7860   if (lastslash == NULL) {
7861     lastslash = unixptr;
7862   }
7863   else {
7864     /* Watch out for trailing "." after last slash, still a directory */
7865     if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7866       lastslash = unixptr + unixlen;
7867     }
7868 
7869     /* Watch out for traiing ".." after last slash, still a directory */
7870     if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7871       lastslash = unixptr + unixlen;
7872     }
7873 
7874     /* dots in directories are aways escaped */
7875     if (lastdot < lastslash)
7876       lastdot = unixptr + unixlen;
7877   }
7878 
7879   /* if (unixptr < lastslash) then we are in a directory */
7880 
7881   dir_start = 0;
7882 
7883   vmsptr = vmspath;
7884   vmslen = 0;
7885 
7886   /* Start with the UNIX path */
7887   if (*unixptr != '/') {
7888     /* relative paths */
7889 
7890     /* If allowing logical names on relative pathnames, then handle here */
7891     if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7892 	!decc_posix_compliant_pathnames) {
7893     char * nextslash;
7894     int seg_len;
7895     char * trn;
7896     int islnm;
7897 
7898 	/* Find the next slash */
7899 	nextslash = strchr(unixptr,'/');
7900 
7901 	esa = PerlMem_malloc(vmspath_len);
7902 	if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7903 
7904 	trn = PerlMem_malloc(VMS_MAXRSS);
7905 	if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7906 
7907 	if (nextslash != NULL) {
7908 
7909 	    seg_len = nextslash - unixptr;
7910 	    strncpy(esa, unixptr, seg_len);
7911 	    esa[seg_len] = 0;
7912 	}
7913 	else {
7914 	    strcpy(esa, unixptr);
7915 	    seg_len = strlen(unixptr);
7916 	}
7917 	/* trnlnm(section) */
7918 	islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7919 
7920 	if (islnm) {
7921 	    /* Now fix up the directory */
7922 
7923 	    /* Split up the path to find the components */
7924 	    sts = vms_split_path
7925 		  (trn,
7926 		   &v_spec,
7927 		   &v_len,
7928 		   &r_spec,
7929 		   &r_len,
7930 		   &d_spec,
7931 		   &d_len,
7932 		   &n_spec,
7933 		   &n_len,
7934 		   &e_spec,
7935 		   &e_len,
7936 		   &vs_spec,
7937 		   &vs_len);
7938 
7939 	    while (sts == 0) {
7940 	    char * strt;
7941 	    int cmp;
7942 
7943 		/* A logical name must be a directory  or the full
7944 		   specification.  It is only a full specification if
7945 		   it is the only component */
7946 		if ((unixptr[seg_len] == '\0') ||
7947 		    (unixptr[seg_len+1] == '\0')) {
7948 
7949 		    /* Is a directory being required? */
7950 		    if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7951 			/* Not a logical name */
7952 			break;
7953 		    }
7954 
7955 
7956 		    if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7957 			/* This must be a directory */
7958 			if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7959 			    strcpy(vmsptr, esa);
7960 			    vmslen=strlen(vmsptr);
7961 			    vmsptr[vmslen] = ':';
7962 			    vmslen++;
7963 			    vmsptr[vmslen] = '\0';
7964 			    return SS$_NORMAL;
7965 			}
7966 		    }
7967 
7968 		}
7969 
7970 
7971 		/* must be dev/directory - ignore version */
7972 		if ((n_len + e_len) != 0)
7973 		    break;
7974 
7975 		/* transfer the volume */
7976 		if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7977 		    strncpy(vmsptr, v_spec, v_len);
7978 		    vmsptr += v_len;
7979 		    vmsptr[0] = '\0';
7980 		    vmslen += v_len;
7981 		}
7982 
7983 		/* unroot the rooted directory */
7984 		if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7985 		    r_spec[0] = '[';
7986 		    r_spec[r_len - 1] = ']';
7987 
7988 		    /* This should not be there, but nothing is perfect */
7989 		    if (r_len > 9) {
7990 			cmp = strcmp(&r_spec[1], "000000.");
7991 			if (cmp == 0) {
7992 			    r_spec += 7;
7993 			    r_spec[7] = '[';
7994 			    r_len -= 7;
7995 			    if (r_len == 2)
7996 				r_len = 0;
7997 			}
7998 		    }
7999 		    if (r_len > 0) {
8000 			strncpy(vmsptr, r_spec, r_len);
8001 			vmsptr += r_len;
8002 			vmslen += r_len;
8003 			vmsptr[0] = '\0';
8004 		    }
8005 		}
8006 		/* Bring over the directory. */
8007 		if ((d_len > 0) &&
8008 		    ((d_len + vmslen) < vmspath_len)) {
8009 		    d_spec[0] = '[';
8010 		    d_spec[d_len - 1] = ']';
8011 		    if (d_len > 9) {
8012 			cmp = strcmp(&d_spec[1], "000000.");
8013 			if (cmp == 0) {
8014 			    d_spec += 7;
8015 			    d_spec[7] = '[';
8016 			    d_len -= 7;
8017 			    if (d_len == 2)
8018 				d_len = 0;
8019 			}
8020 		    }
8021 
8022 		    if (r_len > 0) {
8023 			/* Remove the redundant root */
8024 			if (r_len > 0) {
8025 			    /* remove the ][ */
8026 			    vmsptr--;
8027 			    vmslen--;
8028 			    d_spec++;
8029 			    d_len--;
8030 			}
8031 			strncpy(vmsptr, d_spec, d_len);
8032 			    vmsptr += d_len;
8033 			    vmslen += d_len;
8034 			    vmsptr[0] = '\0';
8035 		    }
8036 		}
8037 		break;
8038 	    }
8039 	}
8040 
8041 	PerlMem_free(esa);
8042 	PerlMem_free(trn);
8043     }
8044 
8045     if (lastslash > unixptr) {
8046     int dotdir_seen;
8047 
8048       /* skip leading ./ */
8049       dotdir_seen = 0;
8050       while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
8051 	dotdir_seen = 1;
8052 	unixptr++;
8053 	unixptr++;
8054       }
8055 
8056       /* Are we still in a directory? */
8057       if (unixptr <= lastslash) {
8058  	*vmsptr++ = '[';
8059  	vmslen = 1;
8060  	dir_start = 1;
8061 
8062  	/* if not backing up, then it is relative forward. */
8063  	if (!((*unixptr == '.') && (unixptr[1] == '.') &&
8064  	      ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
8065  	  *vmsptr++ = '.';
8066  	  vmslen++;
8067  	  dir_dot = 1;
8068  	  }
8069        }
8070        else {
8071 	 if (dotdir_seen) {
8072 	   /* Perl wants an empty directory here to tell the difference
8073 	    * between a DCL commmand and a filename
8074 	    */
8075 	  *vmsptr++ = '[';
8076 	  *vmsptr++ = ']';
8077 	  vmslen = 2;
8078  	}
8079       }
8080     }
8081     else {
8082       /* Handle two special files . and .. */
8083       if (unixptr[0] == '.') {
8084         if (&unixptr[1] == unixend) {
8085 	  *vmsptr++ = '[';
8086 	  *vmsptr++ = ']';
8087 	  vmslen += 2;
8088 	  *vmsptr++ = '\0';
8089 	  return SS$_NORMAL;
8090 	}
8091         if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
8092 	  *vmsptr++ = '[';
8093 	  *vmsptr++ = '-';
8094 	  *vmsptr++ = ']';
8095 	  vmslen += 3;
8096 	  *vmsptr++ = '\0';
8097 	  return SS$_NORMAL;
8098 	}
8099       }
8100     }
8101   }
8102   else {	/* Absolute PATH handling */
8103   int sts;
8104   char * nextslash;
8105   int seg_len;
8106     /* Need to find out where root is */
8107 
8108     /* In theory, this procedure should never get an absolute POSIX pathname
8109      * that can not be found on the POSIX root.
8110      * In practice, that can not be relied on, and things will show up
8111      * here that are a VMS device name or concealed logical name instead.
8112      * So to make things work, this procedure must be tolerant.
8113      */
8114     esa = PerlMem_malloc(vmspath_len);
8115     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8116 
8117     sts = SS$_NORMAL;
8118     nextslash = strchr(&unixptr[1],'/');
8119     seg_len = 0;
8120     if (nextslash != NULL) {
8121     int cmp;
8122       seg_len = nextslash - &unixptr[1];
8123       strncpy(vmspath, unixptr, seg_len + 1);
8124       vmspath[seg_len+1] = 0;
8125       cmp = 1;
8126       if (seg_len == 3) {
8127 	cmp = strncmp(vmspath, "dev", 4);
8128 	if (cmp == 0) {
8129 	    sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8130 	    if (sts = SS$_NORMAL)
8131 		return SS$_NORMAL;
8132 	}
8133       }
8134       sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8135     }
8136 
8137     if ($VMS_STATUS_SUCCESS(sts)) {
8138       /* This is verified to be a real path */
8139 
8140       sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8141       if ($VMS_STATUS_SUCCESS(sts)) {
8142 	strcpy(vmspath, esa);
8143 	vmslen = strlen(vmspath);
8144 	vmsptr = vmspath + vmslen;
8145 	unixptr++;
8146 	if (unixptr < lastslash) {
8147 	char * rptr;
8148 	  vmsptr--;
8149 	  *vmsptr++ = '.';
8150 	  dir_start = 1;
8151 	  dir_dot = 1;
8152 	  if (vmslen > 7) {
8153 	  int cmp;
8154 	    rptr = vmsptr - 7;
8155 	    cmp = strcmp(rptr,"000000.");
8156 	    if (cmp == 0) {
8157 	      vmslen -= 7;
8158 	      vmsptr -= 7;
8159 	      vmsptr[1] = '\0';
8160 	    } /* removing 6 zeros */
8161 	  } /* vmslen < 7, no 6 zeros possible */
8162 	} /* Not in a directory */
8163       } /* Posix root found */
8164       else {
8165 	/* No posix root, fall back to default directory */
8166 	strcpy(vmspath, "SYS$DISK:[");
8167 	vmsptr = &vmspath[10];
8168 	vmslen = 10;
8169 	if (unixptr > lastslash) {
8170 	   *vmsptr = ']';
8171 	   vmsptr++;
8172 	   vmslen++;
8173 	}
8174 	else {
8175 	   dir_start = 1;
8176 	}
8177       }
8178     } /* end of verified real path handling */
8179     else {
8180     int add_6zero;
8181     int islnm;
8182 
8183       /* Ok, we have a device or a concealed root that is not in POSIX
8184        * or we have garbage.  Make the best of it.
8185        */
8186 
8187       /* Posix to VMS destroyed this, so copy it again */
8188       strncpy(vmspath, &unixptr[1], seg_len);
8189       vmspath[seg_len] = 0;
8190       vmslen = seg_len;
8191       vmsptr = &vmsptr[vmslen];
8192       islnm = 0;
8193 
8194       /* Now do we need to add the fake 6 zero directory to it? */
8195       add_6zero = 1;
8196       if ((*lastslash == '/') && (nextslash < lastslash)) {
8197 	/* No there is another directory */
8198 	add_6zero = 0;
8199       }
8200       else {
8201       int trnend;
8202       int cmp;
8203 
8204 	/* now we have foo:bar or foo:[000000]bar to decide from */
8205 	islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8206 
8207         if (!islnm && !decc_posix_compliant_pathnames) {
8208 
8209 	    cmp = strncmp("bin", vmspath, 4);
8210 	    if (cmp == 0) {
8211 	        /* bin => SYS$SYSTEM: */
8212 		islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8213 	    }
8214 	    else {
8215 	        /* tmp => SYS$SCRATCH: */
8216 	        cmp = strncmp("tmp", vmspath, 4);
8217 		if (cmp == 0) {
8218 		    islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8219 		}
8220 	    }
8221 	}
8222 
8223         trnend = islnm ? islnm - 1 : 0;
8224 
8225 	/* if this was a logical name, ']' or '>' must be present */
8226 	/* if not a logical name, then assume a device and hope. */
8227 	islnm =  trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8228 
8229 	/* if log name and trailing '.' then rooted - treat as device */
8230 	add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8231 
8232 	/* Fix me, if not a logical name, a device lookup should be
8233          * done to see if the device is file structured.  If the device
8234          * is not file structured, the 6 zeros should not be put on.
8235          *
8236          * As it is, perl is occasionally looking for dev:[000000]tty.
8237 	 * which looks a little strange.
8238 	 *
8239 	 * Not that easy to detect as "/dev" may be file structured with
8240 	 * special device files.
8241          */
8242 
8243 	if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8244 	    (&nextslash[1] == unixend)) {
8245 	  /* No real directory present */
8246 	  add_6zero = 1;
8247 	}
8248       }
8249 
8250       /* Put the device delimiter on */
8251       *vmsptr++ = ':';
8252       vmslen++;
8253       unixptr = nextslash;
8254       unixptr++;
8255 
8256       /* Start directory if needed */
8257       if (!islnm || add_6zero) {
8258 	*vmsptr++ = '[';
8259 	vmslen++;
8260 	dir_start = 1;
8261       }
8262 
8263       /* add fake 000000] if needed */
8264       if (add_6zero) {
8265 	*vmsptr++ = '0';
8266 	*vmsptr++ = '0';
8267 	*vmsptr++ = '0';
8268 	*vmsptr++ = '0';
8269 	*vmsptr++ = '0';
8270 	*vmsptr++ = '0';
8271 	*vmsptr++ = ']';
8272 	vmslen += 7;
8273 	dir_start = 0;
8274       }
8275 
8276     } /* non-POSIX translation */
8277     PerlMem_free(esa);
8278   } /* End of relative/absolute path handling */
8279 
8280   while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8281   int dash_flag;
8282   int in_cnt;
8283   int out_cnt;
8284 
8285     dash_flag = 0;
8286 
8287     if (dir_start != 0) {
8288 
8289       /* First characters in a directory are handled special */
8290       while ((*unixptr == '/') ||
8291 	     ((*unixptr == '.') &&
8292 	      ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8293 		(&unixptr[1]==unixend)))) {
8294       int loop_flag;
8295 
8296 	loop_flag = 0;
8297 
8298         /* Skip redundant / in specification */
8299         while ((*unixptr == '/') && (dir_start != 0)) {
8300 	  loop_flag = 1;
8301 	  unixptr++;
8302 	  if (unixptr == lastslash)
8303 	    break;
8304 	}
8305 	if (unixptr == lastslash)
8306 	  break;
8307 
8308         /* Skip redundant ./ characters */
8309 	while ((*unixptr == '.') &&
8310 	       ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8311 	  loop_flag = 1;
8312 	  unixptr++;
8313 	  if (unixptr == lastslash)
8314 	    break;
8315 	  if (*unixptr == '/')
8316 	    unixptr++;
8317 	}
8318 	if (unixptr == lastslash)
8319 	  break;
8320 
8321 	/* Skip redundant ../ characters */
8322 	while ((*unixptr == '.') && (unixptr[1] == '.') &&
8323 	     ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8324 	  /* Set the backing up flag */
8325 	  loop_flag = 1;
8326 	  dir_dot = 0;
8327 	  dash_flag = 1;
8328 	  *vmsptr++ = '-';
8329 	  vmslen++;
8330 	  unixptr++; /* first . */
8331 	  unixptr++; /* second . */
8332 	  if (unixptr == lastslash)
8333 	    break;
8334 	  if (*unixptr == '/') /* The slash */
8335 	    unixptr++;
8336 	}
8337 	if (unixptr == lastslash)
8338 	  break;
8339 
8340 	/* To do: Perl expects /.../ to be translated to [...] on VMS */
8341   	/* Not needed when VMS is pretending to be UNIX. */
8342 
8343 	/* Is this loop stuck because of too many dots? */
8344 	if (loop_flag == 0) {
8345 	  /* Exit the loop and pass the rest through */
8346 	  break;
8347 	}
8348       }
8349 
8350       /* Are we done with directories yet? */
8351       if (unixptr >= lastslash) {
8352 
8353 	/* Watch out for trailing dots */
8354 	if (dir_dot != 0) {
8355 	    vmslen --;
8356 	    vmsptr--;
8357 	}
8358 	*vmsptr++ = ']';
8359 	vmslen++;
8360 	dash_flag = 0;
8361 	dir_start = 0;
8362 	if (*unixptr == '/')
8363 	  unixptr++;
8364       }
8365       else {
8366 	/* Have we stopped backing up? */
8367 	if (dash_flag) {
8368 	  *vmsptr++ = '.';
8369 	  vmslen++;
8370 	  dash_flag = 0;
8371 	  /* dir_start continues to be = 1 */
8372 	}
8373 	if (*unixptr == '-') {
8374 	  *vmsptr++ = '^';
8375 	  *vmsptr++ = *unixptr++;
8376 	  vmslen += 2;
8377 	  dir_start = 0;
8378 
8379 	  /* Now are we done with directories yet? */
8380 	  if (unixptr >= lastslash) {
8381 
8382 	    /* Watch out for trailing dots */
8383 	    if (dir_dot != 0) {
8384 	      vmslen --;
8385 	      vmsptr--;
8386 	    }
8387 
8388 	    *vmsptr++ = ']';
8389 	    vmslen++;
8390 	    dash_flag = 0;
8391 	    dir_start = 0;
8392 	  }
8393 	}
8394       }
8395     }
8396 
8397     /* All done? */
8398     if (unixptr >= unixend)
8399       break;
8400 
8401     /* Normal characters - More EFS work probably needed */
8402     dir_start = 0;
8403     dir_dot = 0;
8404 
8405     switch(*unixptr) {
8406     case '/':
8407 	/* remove multiple / */
8408 	while (unixptr[1] == '/') {
8409 	   unixptr++;
8410 	}
8411 	if (unixptr == lastslash) {
8412 	  /* Watch out for trailing dots */
8413 	  if (dir_dot != 0) {
8414 	    vmslen --;
8415 	    vmsptr--;
8416 	  }
8417 	  *vmsptr++ = ']';
8418 	}
8419 	else {
8420 	  dir_start = 1;
8421 	  *vmsptr++ = '.';
8422 	  dir_dot = 1;
8423 
8424 	  /* To do: Perl expects /.../ to be translated to [...] on VMS */
8425  	  /* Not needed when VMS is pretending to be UNIX. */
8426 
8427 	}
8428 	dash_flag = 0;
8429 	if (unixptr != unixend)
8430 	  unixptr++;
8431 	vmslen++;
8432 	break;
8433     case '.':
8434 	if ((unixptr < lastdot) || (unixptr < lastslash) ||
8435 	    (&unixptr[1] == unixend)) {
8436 	  *vmsptr++ = '^';
8437 	  *vmsptr++ = '.';
8438 	  vmslen += 2;
8439 	  unixptr++;
8440 
8441 	  /* trailing dot ==> '^..' on VMS */
8442 	  if (unixptr == unixend) {
8443 	    *vmsptr++ = '.';
8444 	    vmslen++;
8445 	    unixptr++;
8446 	  }
8447 	  break;
8448 	}
8449 
8450 	*vmsptr++ = *unixptr++;
8451 	vmslen ++;
8452 	break;
8453     case '"':
8454 	if (quoted && (&unixptr[1] == unixend)) {
8455 	    unixptr++;
8456 	    break;
8457 	}
8458 	in_cnt = copy_expand_unix_filename_escape
8459 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8460 	vmsptr += out_cnt;
8461 	unixptr += in_cnt;
8462 	break;
8463     case '~':
8464     case ';':
8465     case '\\':
8466     case '?':
8467     case ' ':
8468     default:
8469 	in_cnt = copy_expand_unix_filename_escape
8470 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8471 	vmsptr += out_cnt;
8472 	unixptr += in_cnt;
8473 	break;
8474     }
8475   }
8476 
8477   /* Make sure directory is closed */
8478   if (unixptr == lastslash) {
8479     char *vmsptr2;
8480     vmsptr2 = vmsptr - 1;
8481 
8482     if (*vmsptr2 != ']') {
8483       *vmsptr2--;
8484 
8485       /* directories do not end in a dot bracket */
8486       if (*vmsptr2 == '.') {
8487 	vmsptr2--;
8488 
8489 	/* ^. is allowed */
8490         if (*vmsptr2 != '^') {
8491 	  vmsptr--; /* back up over the dot */
8492  	}
8493       }
8494       *vmsptr++ = ']';
8495     }
8496   }
8497   else {
8498     char *vmsptr2;
8499     /* Add a trailing dot if a file with no extension */
8500     vmsptr2 = vmsptr - 1;
8501     if ((vmslen > 1) &&
8502 	(*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8503 	(*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8504 	*vmsptr++ = '.';
8505         vmslen++;
8506     }
8507   }
8508 
8509   *vmsptr = '\0';
8510   return SS$_NORMAL;
8511 }
8512 #endif
8513 
8514  /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8515 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8516 {
8517 char * result;
8518 int utf8_flag;
8519 
8520    /* If a UTF8 flag is being passed, honor it */
8521    utf8_flag = 0;
8522    if (utf8_fl != NULL) {
8523      utf8_flag = *utf8_fl;
8524     *utf8_fl = 0;
8525    }
8526 
8527    if (utf8_flag) {
8528      /* If there is a possibility of UTF8, then if any UTF8 characters
8529         are present, then they must be converted to VTF-7
8530       */
8531      result = strcpy(rslt, path); /* FIX-ME */
8532    }
8533    else
8534      result = strcpy(rslt, path);
8535 
8536    return result;
8537 }
8538 
8539 
8540 
8541 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8542 static char *int_tovmsspec
8543    (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8544   char *dirend;
8545   char *lastdot;
8546   char *vms_delim;
8547   register char *cp1;
8548   const char *cp2;
8549   unsigned long int infront = 0, hasdir = 1;
8550   int rslt_len;
8551   int no_type_seen;
8552   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8553   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8554 
8555   if (vms_debug_fileify) {
8556       if (path == NULL)
8557           fprintf(stderr, "int_tovmsspec: path = NULL\n");
8558       else
8559           fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8560   }
8561 
8562   if (path == NULL) {
8563       /* If we fail, we should be setting errno */
8564       set_errno(EINVAL);
8565       set_vaxc_errno(SS$_BADPARAM);
8566       return NULL;
8567   }
8568   rslt_len = VMS_MAXRSS-1;
8569 
8570   /* '.' and '..' are "[]" and "[-]" for a quick check */
8571   if (path[0] == '.') {
8572     if (path[1] == '\0') {
8573       strcpy(rslt,"[]");
8574       if (utf8_flag != NULL)
8575 	*utf8_flag = 0;
8576       return rslt;
8577     }
8578     else {
8579       if (path[1] == '.' && path[2] == '\0') {
8580 	strcpy(rslt,"[-]");
8581 	if (utf8_flag != NULL)
8582 	   *utf8_flag = 0;
8583 	return rslt;
8584       }
8585     }
8586   }
8587 
8588    /* Posix specifications are now a native VMS format */
8589   /*--------------------------------------------------*/
8590 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8591   if (decc_posix_compliant_pathnames) {
8592     if (strncmp(path,"\"^UP^",5) == 0) {
8593       posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8594       return rslt;
8595     }
8596   }
8597 #endif
8598 
8599   /* This is really the only way to see if this is already in VMS format */
8600   sts = vms_split_path
8601        (path,
8602 	&v_spec,
8603 	&v_len,
8604 	&r_spec,
8605 	&r_len,
8606 	&d_spec,
8607 	&d_len,
8608 	&n_spec,
8609 	&n_len,
8610 	&e_spec,
8611 	&e_len,
8612 	&vs_spec,
8613 	&vs_len);
8614   if (sts == 0) {
8615     /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8616        replacement, because the above parse just took care of most of
8617        what is needed to do vmspath when the specification is already
8618        in VMS format.
8619 
8620        And if it is not already, it is easier to do the conversion as
8621        part of this routine than to call this routine and then work on
8622        the result.
8623      */
8624 
8625     /* If VMS punctuation was found, it is already VMS format */
8626     if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8627       if (utf8_flag != NULL)
8628 	*utf8_flag = 0;
8629       strcpy(rslt, path);
8630       if (vms_debug_fileify) {
8631           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8632       }
8633       return rslt;
8634     }
8635     /* Now, what to do with trailing "." cases where there is no
8636        extension?  If this is a UNIX specification, and EFS characters
8637        are enabled, then the trailing "." should be converted to a "^.".
8638        But if this was already a VMS specification, then it should be
8639        left alone.
8640 
8641        So in the case of ambiguity, leave the specification alone.
8642      */
8643 
8644 
8645     /* If there is a possibility of UTF8, then if any UTF8 characters
8646         are present, then they must be converted to VTF-7
8647      */
8648     if (utf8_flag != NULL)
8649       *utf8_flag = 0;
8650     strcpy(rslt, path);
8651     if (vms_debug_fileify) {
8652         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8653     }
8654     return rslt;
8655   }
8656 
8657   dirend = strrchr(path,'/');
8658 
8659   if (dirend == NULL) {
8660      char *macro_start;
8661      int has_macro;
8662 
8663      /* If we get here with no UNIX directory delimiters, then this is
8664         not a complete file specification, either garbage a UNIX glob
8665 	specification that can not be converted to a VMS wildcard, or
8666 	it a UNIX shell macro.  MakeMaker wants shell macros passed
8667 	through AS-IS,
8668 
8669 	utf8 flag setting needs to be preserved.
8670       */
8671       hasdir = 0;
8672 
8673       has_macro = 0;
8674       macro_start = strchr(path,'$');
8675       if (macro_start != NULL) {
8676           if (macro_start[1] == '(') {
8677               has_macro = 1;
8678           }
8679       }
8680       if ((decc_efs_charset == 0) || (has_macro)) {
8681           strcpy(rslt, path);
8682           if (vms_debug_fileify) {
8683               fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8684           }
8685           return rslt;
8686       }
8687   }
8688 
8689 /* If EFS charset mode active, handle the conversion */
8690 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8691   if (decc_efs_charset) {
8692     posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8693     if (vms_debug_fileify) {
8694         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8695     }
8696     return rslt;
8697   }
8698 #endif
8699 
8700   if (*(dirend+1) == '.') {  /* do we have trailing "/." or "/.." or "/..."? */
8701     if (!*(dirend+2)) dirend +=2;
8702     if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8703     if (decc_efs_charset == 0) {
8704       if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8705     }
8706   }
8707 
8708   cp1 = rslt;
8709   cp2 = path;
8710   lastdot = strrchr(cp2,'.');
8711   if (*cp2 == '/') {
8712     char *trndev;
8713     int islnm, rooted;
8714     STRLEN trnend;
8715 
8716     while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8717     if (!*(cp2+1)) {
8718       if (decc_disable_posix_root) {
8719 	strcpy(rslt,"sys$disk:[000000]");
8720       }
8721       else {
8722 	strcpy(rslt,"sys$posix_root:[000000]");
8723       }
8724       if (utf8_flag != NULL)
8725 	*utf8_flag = 0;
8726       if (vms_debug_fileify) {
8727           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8728       }
8729       return rslt;
8730     }
8731     while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8732     *cp1 = '\0';
8733     trndev = PerlMem_malloc(VMS_MAXRSS);
8734     if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8735     islnm =  simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8736 
8737      /* DECC special handling */
8738     if (!islnm) {
8739       if (strcmp(rslt,"bin") == 0) {
8740 	strcpy(rslt,"sys$system");
8741 	cp1 = rslt + 10;
8742 	*cp1 = 0;
8743 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8744       }
8745       else if (strcmp(rslt,"tmp") == 0) {
8746 	strcpy(rslt,"sys$scratch");
8747 	cp1 = rslt + 11;
8748 	*cp1 = 0;
8749 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8750       }
8751       else if (!decc_disable_posix_root) {
8752         strcpy(rslt, "sys$posix_root");
8753 	cp1 = rslt + 14;
8754 	*cp1 = 0;
8755 	cp2 = path;
8756         while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8757 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8758       }
8759       else if (strcmp(rslt,"dev") == 0) {
8760 	if (strncmp(cp2,"/null", 5) == 0) {
8761 	  if ((cp2[5] == 0) || (cp2[5] == '/')) {
8762 	    strcpy(rslt,"NLA0");
8763 	    cp1 = rslt + 4;
8764 	    *cp1 = 0;
8765 	    cp2 = cp2 + 5;
8766 	    islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8767 	  }
8768 	}
8769       }
8770     }
8771 
8772     trnend = islnm ? strlen(trndev) - 1 : 0;
8773     islnm =  trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8774     rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8775     /* If the first element of the path is a logical name, determine
8776      * whether it has to be translated so we can add more directories. */
8777     if (!islnm || rooted) {
8778       *(cp1++) = ':';
8779       *(cp1++) = '[';
8780       if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8781       else cp2++;
8782     }
8783     else {
8784       if (cp2 != dirend) {
8785         strcpy(rslt,trndev);
8786         cp1 = rslt + trnend;
8787 	if (*cp2 != 0) {
8788           *(cp1++) = '.';
8789           cp2++;
8790         }
8791       }
8792       else {
8793 	if (decc_disable_posix_root) {
8794 	  *(cp1++) = ':';
8795 	  hasdir = 0;
8796 	}
8797       }
8798     }
8799     PerlMem_free(trndev);
8800   }
8801   else {
8802     *(cp1++) = '[';
8803     if (*cp2 == '.') {
8804       if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8805         cp2 += 2;         /* skip over "./" - it's redundant */
8806         *(cp1++) = '.';   /* but it does indicate a relative dirspec */
8807       }
8808       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8809         *(cp1++) = '-';                                 /* "../" --> "-" */
8810         cp2 += 3;
8811       }
8812       else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8813                (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8814         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8815         if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8816         cp2 += 4;
8817       }
8818       else if ((cp2 != lastdot) || (lastdot < dirend)) {
8819 	/* Escape the extra dots in EFS file specifications */
8820 	*(cp1++) = '^';
8821       }
8822       if (cp2 > dirend) cp2 = dirend;
8823     }
8824     else *(cp1++) = '.';
8825   }
8826   for (; cp2 < dirend; cp2++) {
8827     if (*cp2 == '/') {
8828       if (*(cp2-1) == '/') continue;
8829       if (*(cp1-1) != '.') *(cp1++) = '.';
8830       infront = 0;
8831     }
8832     else if (!infront && *cp2 == '.') {
8833       if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8834       else if (*(cp2+1) == '/') cp2++;   /* skip over "./" - it's redundant */
8835       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8836         if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8837         else if (*(cp1-2) == '[') *(cp1-1) = '-';
8838         else {  /* back up over previous directory name */
8839           cp1--;
8840           while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8841           if (*(cp1-1) == '[') {
8842             memcpy(cp1,"000000.",7);
8843             cp1 += 7;
8844           }
8845         }
8846         cp2 += 2;
8847         if (cp2 == dirend) break;
8848       }
8849       else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8850                 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8851         if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8852         *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8853         if (!*(cp2+3)) {
8854           *(cp1++) = '.';  /* Simulate trailing '/' */
8855           cp2 += 2;  /* for loop will incr this to == dirend */
8856         }
8857         else cp2 += 3;  /* Trailing '/' was there, so skip it, too */
8858       }
8859       else {
8860         if (decc_efs_charset == 0)
8861 	  *(cp1++) = '_';  /* fix up syntax - '.' in name not allowed */
8862 	else {
8863 	  *(cp1++) = '^';  /* fix up syntax - '.' in name is allowed */
8864 	  *(cp1++) = '.';
8865 	}
8866       }
8867     }
8868     else {
8869       if (!infront && *(cp1-1) == '-')  *(cp1++) = '.';
8870       if (*cp2 == '.') {
8871         if (decc_efs_charset == 0)
8872 	  *(cp1++) = '_';
8873 	else {
8874 	  *(cp1++) = '^';
8875 	  *(cp1++) = '.';
8876 	}
8877       }
8878       else                  *(cp1++) =  *cp2;
8879       infront = 1;
8880     }
8881   }
8882   if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8883   if (hasdir) *(cp1++) = ']';
8884   if (*cp2) cp2++;  /* check in case we ended with trailing '..' */
8885   /* fixme for ODS5 */
8886   no_type_seen = 0;
8887   if (cp2 > lastdot)
8888     no_type_seen = 1;
8889   while (*cp2) {
8890     switch(*cp2) {
8891     case '?':
8892         if (decc_efs_charset == 0)
8893 	  *(cp1++) = '%';
8894 	else
8895 	  *(cp1++) = '?';
8896 	cp2++;
8897     case ' ':
8898 	*(cp1)++ = '^';
8899 	*(cp1)++ = '_';
8900 	cp2++;
8901 	break;
8902     case '.':
8903 	if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8904 	    decc_readdir_dropdotnotype) {
8905 	  *(cp1)++ = '^';
8906 	  *(cp1)++ = '.';
8907 	  cp2++;
8908 
8909 	  /* trailing dot ==> '^..' on VMS */
8910 	  if (*cp2 == '\0') {
8911 	    *(cp1++) = '.';
8912 	    no_type_seen = 0;
8913 	  }
8914 	}
8915 	else {
8916 	  *(cp1++) = *(cp2++);
8917 	  no_type_seen = 0;
8918 	}
8919 	break;
8920     case '$':
8921 	 /* This could be a macro to be passed through */
8922 	*(cp1++) = *(cp2++);
8923 	if (*cp2 == '(') {
8924 	const char * save_cp2;
8925 	char * save_cp1;
8926 	int is_macro;
8927 
8928 	    /* paranoid check */
8929 	    save_cp2 = cp2;
8930 	    save_cp1 = cp1;
8931 	    is_macro = 0;
8932 
8933 	    /* Test through */
8934 	    *(cp1++) = *(cp2++);
8935 	    if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8936 		*(cp1++) = *(cp2++);
8937 		while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8938 		    *(cp1++) = *(cp2++);
8939 		}
8940 		if (*cp2 == ')') {
8941 		    *(cp1++) = *(cp2++);
8942 		    is_macro = 1;
8943 		}
8944 	    }
8945 	    if (is_macro == 0) {
8946 		/* Not really a macro - never mind */
8947 		cp2 = save_cp2;
8948 		cp1 = save_cp1;
8949 	    }
8950 	}
8951 	break;
8952     case '\"':
8953     case '~':
8954     case '`':
8955     case '!':
8956     case '#':
8957     case '%':
8958     case '^':
8959         /* Don't escape again if following character is
8960          * already something we escape.
8961          */
8962         if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8963 	    *(cp1++) = *(cp2++);
8964 	    break;
8965         }
8966         /* But otherwise fall through and escape it. */
8967     case '&':
8968     case '(':
8969     case ')':
8970     case '=':
8971     case '+':
8972     case '\'':
8973     case '@':
8974     case '[':
8975     case ']':
8976     case '{':
8977     case '}':
8978     case ':':
8979     case '\\':
8980     case '|':
8981     case '<':
8982     case '>':
8983 	*(cp1++) = '^';
8984 	*(cp1++) = *(cp2++);
8985 	break;
8986     case ';':
8987 	/* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8988 	 * which is wrong.  UNIX notation should be ".dir." unless
8989 	 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8990 	 * changing this behavior could break more things at this time.
8991 	 * efs character set effectively does not allow "." to be a version
8992 	 * delimiter as a further complication about changing this.
8993 	 */
8994 	if (decc_filename_unix_report != 0) {
8995 	  *(cp1++) = '^';
8996 	}
8997 	*(cp1++) = *(cp2++);
8998 	break;
8999     default:
9000 	*(cp1++) = *(cp2++);
9001     }
9002   }
9003   if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
9004   char *lcp1;
9005     lcp1 = cp1;
9006     lcp1--;
9007      /* Fix me for "^]", but that requires making sure that you do
9008       * not back up past the start of the filename
9009       */
9010     if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
9011       *cp1++ = '.';
9012   }
9013   *cp1 = '\0';
9014 
9015   if (utf8_flag != NULL)
9016     *utf8_flag = 0;
9017   if (vms_debug_fileify) {
9018       fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
9019   }
9020   return rslt;
9021 
9022 }  /* end of int_tovmsspec() */
9023 
9024 
9025 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
9026 static char *mp_do_tovmsspec
9027    (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
9028   static char __tovmsspec_retbuf[VMS_MAXRSS];
9029     char * vmsspec, *ret_spec, *ret_buf;
9030 
9031     vmsspec = NULL;
9032     ret_buf = buf;
9033     if (ret_buf == NULL) {
9034         if (ts) {
9035             Newx(vmsspec, VMS_MAXRSS, char);
9036             if (vmsspec == NULL)
9037                 _ckvmssts(SS$_INSFMEM);
9038             ret_buf = vmsspec;
9039         } else {
9040             ret_buf = __tovmsspec_retbuf;
9041         }
9042     }
9043 
9044     ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
9045 
9046     if (ret_spec == NULL) {
9047        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
9048        if (vmsspec)
9049            Safefree(vmsspec);
9050     }
9051 
9052     return ret_spec;
9053 
9054 }  /* end of mp_do_tovmsspec() */
9055 /*}}}*/
9056 /* External entry points */
9057 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
9058   { return do_tovmsspec(path,buf,0,NULL); }
9059 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
9060   { return do_tovmsspec(path,buf,1,NULL); }
9061 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9062   { return do_tovmsspec(path,buf,0,utf8_fl); }
9063 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9064   { return do_tovmsspec(path,buf,1,utf8_fl); }
9065 
9066 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
9067 /* Internal routine for use with out an explict context present */
9068 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
9069 
9070     char * ret_spec, *pathified;
9071 
9072     if (path == NULL)
9073         return NULL;
9074 
9075     pathified = PerlMem_malloc(VMS_MAXRSS);
9076     if (pathified == NULL)
9077         _ckvmssts_noperl(SS$_INSFMEM);
9078 
9079     ret_spec = int_pathify_dirspec(path, pathified);
9080 
9081     if (ret_spec == NULL) {
9082         PerlMem_free(pathified);
9083         return NULL;
9084     }
9085 
9086     ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
9087 
9088     PerlMem_free(pathified);
9089     return ret_spec;
9090 
9091 }
9092 
9093 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9094 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9095   static char __tovmspath_retbuf[VMS_MAXRSS];
9096   int vmslen;
9097   char *pathified, *vmsified, *cp;
9098 
9099   if (path == NULL) return NULL;
9100   pathified = PerlMem_malloc(VMS_MAXRSS);
9101   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9102   if (int_pathify_dirspec(path, pathified) == NULL) {
9103     PerlMem_free(pathified);
9104     return NULL;
9105   }
9106 
9107   vmsified = NULL;
9108   if (buf == NULL)
9109      Newx(vmsified, VMS_MAXRSS, char);
9110   if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
9111     PerlMem_free(pathified);
9112     if (vmsified) Safefree(vmsified);
9113     return NULL;
9114   }
9115   PerlMem_free(pathified);
9116   if (buf) {
9117     return buf;
9118   }
9119   else if (ts) {
9120     vmslen = strlen(vmsified);
9121     Newx(cp,vmslen+1,char);
9122     memcpy(cp,vmsified,vmslen);
9123     cp[vmslen] = '\0';
9124     Safefree(vmsified);
9125     return cp;
9126   }
9127   else {
9128     strcpy(__tovmspath_retbuf,vmsified);
9129     Safefree(vmsified);
9130     return __tovmspath_retbuf;
9131   }
9132 
9133 }  /* end of do_tovmspath() */
9134 /*}}}*/
9135 /* External entry points */
9136 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9137   { return do_tovmspath(path,buf,0, NULL); }
9138 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9139   { return do_tovmspath(path,buf,1, NULL); }
9140 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9141   { return do_tovmspath(path,buf,0,utf8_fl); }
9142 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9143   { return do_tovmspath(path,buf,1,utf8_fl); }
9144 
9145 
9146 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9147 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9148   static char __tounixpath_retbuf[VMS_MAXRSS];
9149   int unixlen;
9150   char *pathified, *unixified, *cp;
9151 
9152   if (path == NULL) return NULL;
9153   pathified = PerlMem_malloc(VMS_MAXRSS);
9154   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9155   if (int_pathify_dirspec(path, pathified) == NULL) {
9156     PerlMem_free(pathified);
9157     return NULL;
9158   }
9159 
9160   unixified = NULL;
9161   if (buf == NULL) {
9162       Newx(unixified, VMS_MAXRSS, char);
9163   }
9164   if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9165     PerlMem_free(pathified);
9166     if (unixified) Safefree(unixified);
9167     return NULL;
9168   }
9169   PerlMem_free(pathified);
9170   if (buf) {
9171     return buf;
9172   }
9173   else if (ts) {
9174     unixlen = strlen(unixified);
9175     Newx(cp,unixlen+1,char);
9176     memcpy(cp,unixified,unixlen);
9177     cp[unixlen] = '\0';
9178     Safefree(unixified);
9179     return cp;
9180   }
9181   else {
9182     strcpy(__tounixpath_retbuf,unixified);
9183     Safefree(unixified);
9184     return __tounixpath_retbuf;
9185   }
9186 
9187 }  /* end of do_tounixpath() */
9188 /*}}}*/
9189 /* External entry points */
9190 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9191   { return do_tounixpath(path,buf,0,NULL); }
9192 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9193   { return do_tounixpath(path,buf,1,NULL); }
9194 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9195   { return do_tounixpath(path,buf,0,utf8_fl); }
9196 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9197   { return do_tounixpath(path,buf,1,utf8_fl); }
9198 
9199 /*
9200  * @(#)argproc.c 2.2 94/08/16	Mark Pizzolato (mark AT infocomm DOT com)
9201  *
9202  *****************************************************************************
9203  *                                                                           *
9204  *  Copyright (C) 1989-1994, 2007 by                                         *
9205  *  Mark Pizzolato - INFO COMM, Danville, California  (510) 837-5600         *
9206  *                                                                           *
9207  *  Permission is hereby granted for the reproduction of this software       *
9208  *  on condition that this copyright notice is included in source            *
9209  *  distributions of the software.  The code may be modified and             *
9210  *  distributed under the same terms as Perl itself.                         *
9211  *                                                                           *
9212  *  27-Aug-1994 Modified for inclusion in perl5                              *
9213  *              by Charles Bailey  (bailey AT newman DOT upenn DOT edu)      *
9214  *****************************************************************************
9215  */
9216 
9217 /*
9218  * getredirection() is intended to aid in porting C programs
9219  * to VMS (Vax-11 C).  The native VMS environment does not support
9220  * '>' and '<' I/O redirection, or command line wild card expansion,
9221  * or a command line pipe mechanism using the '|' AND background
9222  * command execution '&'.  All of these capabilities are provided to any
9223  * C program which calls this procedure as the first thing in the
9224  * main program.
9225  * The piping mechanism will probably work with almost any 'filter' type
9226  * of program.  With suitable modification, it may useful for other
9227  * portability problems as well.
9228  *
9229  * Author:  Mark Pizzolato	(mark AT infocomm DOT com)
9230  */
9231 struct list_item
9232     {
9233     struct list_item *next;
9234     char *value;
9235     };
9236 
9237 static void add_item(struct list_item **head,
9238 		     struct list_item **tail,
9239 		     char *value,
9240 		     int *count);
9241 
9242 static void mp_expand_wild_cards(pTHX_ char *item,
9243 				struct list_item **head,
9244 				struct list_item **tail,
9245 				int *count);
9246 
9247 static int background_process(pTHX_ int argc, char **argv);
9248 
9249 static void pipe_and_fork(pTHX_ char **cmargv);
9250 
9251 /*{{{ void getredirection(int *ac, char ***av)*/
9252 static void
9253 mp_getredirection(pTHX_ int *ac, char ***av)
9254 /*
9255  * Process vms redirection arg's.  Exit if any error is seen.
9256  * If getredirection() processes an argument, it is erased
9257  * from the vector.  getredirection() returns a new argc and argv value.
9258  * In the event that a background command is requested (by a trailing "&"),
9259  * this routine creates a background subprocess, and simply exits the program.
9260  *
9261  * Warning: do not try to simplify the code for vms.  The code
9262  * presupposes that getredirection() is called before any data is
9263  * read from stdin or written to stdout.
9264  *
9265  * Normal usage is as follows:
9266  *
9267  *	main(argc, argv)
9268  *	int		argc;
9269  *    	char		*argv[];
9270  *	{
9271  *		getredirection(&argc, &argv);
9272  *	}
9273  */
9274 {
9275     int			argc = *ac;	/* Argument Count	  */
9276     char		**argv = *av;	/* Argument Vector	  */
9277     char		*ap;   		/* Argument pointer	  */
9278     int	       		j;		/* argv[] index		  */
9279     int			item_count = 0;	/* Count of Items in List */
9280     struct list_item 	*list_head = 0;	/* First Item in List	    */
9281     struct list_item	*list_tail;	/* Last Item in List	    */
9282     char 		*in = NULL;	/* Input File Name	    */
9283     char 		*out = NULL;	/* Output File Name	    */
9284     char 		*outmode = "w";	/* Mode to Open Output File */
9285     char 		*err = NULL;	/* Error File Name	    */
9286     char 		*errmode = "w";	/* Mode to Open Error File  */
9287     int			cmargc = 0;    	/* Piped Command Arg Count  */
9288     char		**cmargv = NULL;/* Piped Command Arg Vector */
9289 
9290     /*
9291      * First handle the case where the last thing on the line ends with
9292      * a '&'.  This indicates the desire for the command to be run in a
9293      * subprocess, so we satisfy that desire.
9294      */
9295     ap = argv[argc-1];
9296     if (0 == strcmp("&", ap))
9297        exit(background_process(aTHX_ --argc, argv));
9298     if (*ap && '&' == ap[strlen(ap)-1])
9299 	{
9300 	ap[strlen(ap)-1] = '\0';
9301        exit(background_process(aTHX_ argc, argv));
9302 	}
9303     /*
9304      * Now we handle the general redirection cases that involve '>', '>>',
9305      * '<', and pipes '|'.
9306      */
9307     for (j = 0; j < argc; ++j)
9308 	{
9309 	if (0 == strcmp("<", argv[j]))
9310 	    {
9311 	    if (j+1 >= argc)
9312 		{
9313 		fprintf(stderr,"No input file after < on command line");
9314 		exit(LIB$_WRONUMARG);
9315 		}
9316 	    in = argv[++j];
9317 	    continue;
9318 	    }
9319 	if ('<' == *(ap = argv[j]))
9320 	    {
9321 	    in = 1 + ap;
9322 	    continue;
9323 	    }
9324 	if (0 == strcmp(">", ap))
9325 	    {
9326 	    if (j+1 >= argc)
9327 		{
9328 		fprintf(stderr,"No output file after > on command line");
9329 		exit(LIB$_WRONUMARG);
9330 		}
9331 	    out = argv[++j];
9332 	    continue;
9333 	    }
9334 	if ('>' == *ap)
9335 	    {
9336 	    if ('>' == ap[1])
9337 		{
9338 		outmode = "a";
9339 		if ('\0' == ap[2])
9340 		    out = argv[++j];
9341 		else
9342 		    out = 2 + ap;
9343 		}
9344 	    else
9345 		out = 1 + ap;
9346 	    if (j >= argc)
9347 		{
9348 		fprintf(stderr,"No output file after > or >> on command line");
9349 		exit(LIB$_WRONUMARG);
9350 		}
9351 	    continue;
9352 	    }
9353 	if (('2' == *ap) && ('>' == ap[1]))
9354 	    {
9355 	    if ('>' == ap[2])
9356 		{
9357 		errmode = "a";
9358 		if ('\0' == ap[3])
9359 		    err = argv[++j];
9360 		else
9361 		    err = 3 + ap;
9362 		}
9363 	    else
9364 		if ('\0' == ap[2])
9365 		    err = argv[++j];
9366 		else
9367 		    err = 2 + ap;
9368 	    if (j >= argc)
9369 		{
9370 		fprintf(stderr,"No output file after 2> or 2>> on command line");
9371 		exit(LIB$_WRONUMARG);
9372 		}
9373 	    continue;
9374 	    }
9375 	if (0 == strcmp("|", argv[j]))
9376 	    {
9377 	    if (j+1 >= argc)
9378 		{
9379 		fprintf(stderr,"No command into which to pipe on command line");
9380 		exit(LIB$_WRONUMARG);
9381 		}
9382 	    cmargc = argc-(j+1);
9383 	    cmargv = &argv[j+1];
9384 	    argc = j;
9385 	    continue;
9386 	    }
9387 	if ('|' == *(ap = argv[j]))
9388 	    {
9389 	    ++argv[j];
9390 	    cmargc = argc-j;
9391 	    cmargv = &argv[j];
9392 	    argc = j;
9393 	    continue;
9394 	    }
9395 	expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9396 	}
9397     /*
9398      * Allocate and fill in the new argument vector, Some Unix's terminate
9399      * the list with an extra null pointer.
9400      */
9401     argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9402     if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9403     *av = argv;
9404     for (j = 0; j < item_count; ++j, list_head = list_head->next)
9405 	argv[j] = list_head->value;
9406     *ac = item_count;
9407     if (cmargv != NULL)
9408 	{
9409 	if (out != NULL)
9410 	    {
9411 	    fprintf(stderr,"'|' and '>' may not both be specified on command line");
9412 	    exit(LIB$_INVARGORD);
9413 	    }
9414 	pipe_and_fork(aTHX_ cmargv);
9415 	}
9416 
9417     /* Check for input from a pipe (mailbox) */
9418 
9419     if (in == NULL && 1 == isapipe(0))
9420 	{
9421 	char mbxname[L_tmpnam];
9422 	long int bufsize;
9423 	long int dvi_item = DVI$_DEVBUFSIZ;
9424 	$DESCRIPTOR(mbxnam, "");
9425 	$DESCRIPTOR(mbxdevnam, "");
9426 
9427 	/* Input from a pipe, reopen it in binary mode to disable	*/
9428 	/* carriage control processing.	 				*/
9429 
9430 	fgetname(stdin, mbxname, 1);
9431 	mbxnam.dsc$a_pointer = mbxname;
9432 	mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9433 	lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9434 	mbxdevnam.dsc$a_pointer = mbxname;
9435 	mbxdevnam.dsc$w_length = sizeof(mbxname);
9436 	dvi_item = DVI$_DEVNAM;
9437 	lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9438 	mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9439 	set_errno(0);
9440 	set_vaxc_errno(1);
9441 	freopen(mbxname, "rb", stdin);
9442 	if (errno != 0)
9443 	    {
9444 	    fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9445 	    exit(vaxc$errno);
9446 	    }
9447 	}
9448     if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9449 	{
9450 	fprintf(stderr,"Can't open input file %s as stdin",in);
9451 	exit(vaxc$errno);
9452 	}
9453     if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9454 	{
9455 	fprintf(stderr,"Can't open output file %s as stdout",out);
9456 	exit(vaxc$errno);
9457 	}
9458 	if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9459 
9460     if (err != NULL) {
9461         if (strcmp(err,"&1") == 0) {
9462             dup2(fileno(stdout), fileno(stderr));
9463             Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9464         } else {
9465 	FILE *tmperr;
9466 	if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9467 	    {
9468 	    fprintf(stderr,"Can't open error file %s as stderr",err);
9469 	    exit(vaxc$errno);
9470 	    }
9471 	    fclose(tmperr);
9472            if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9473 		{
9474 		exit(vaxc$errno);
9475 		}
9476 	    Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9477 	}
9478         }
9479 #ifdef ARGPROC_DEBUG
9480     PerlIO_printf(Perl_debug_log, "Arglist:\n");
9481     for (j = 0; j < *ac;  ++j)
9482 	PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9483 #endif
9484    /* Clear errors we may have hit expanding wildcards, so they don't
9485       show up in Perl's $! later */
9486    set_errno(0); set_vaxc_errno(1);
9487 }  /* end of getredirection() */
9488 /*}}}*/
9489 
9490 static void add_item(struct list_item **head,
9491 		     struct list_item **tail,
9492 		     char *value,
9493 		     int *count)
9494 {
9495     if (*head == 0)
9496 	{
9497 	*head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9498 	if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9499 	*tail = *head;
9500 	}
9501     else {
9502 	(*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9503 	if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9504 	*tail = (*tail)->next;
9505 	}
9506     (*tail)->value = value;
9507     ++(*count);
9508 }
9509 
9510 static void mp_expand_wild_cards(pTHX_ char *item,
9511 			      struct list_item **head,
9512 			      struct list_item **tail,
9513 			      int *count)
9514 {
9515 int expcount = 0;
9516 unsigned long int context = 0;
9517 int isunix = 0;
9518 int item_len = 0;
9519 char *had_version;
9520 char *had_device;
9521 int had_directory;
9522 char *devdir,*cp;
9523 char *vmsspec;
9524 $DESCRIPTOR(filespec, "");
9525 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9526 $DESCRIPTOR(resultspec, "");
9527 unsigned long int lff_flags = 0;
9528 int sts;
9529 int rms_sts;
9530 
9531 #ifdef VMS_LONGNAME_SUPPORT
9532     lff_flags = LIB$M_FIL_LONG_NAMES;
9533 #endif
9534 
9535     for (cp = item; *cp; cp++) {
9536 	if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9537 	if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9538     }
9539     if (!*cp || isspace(*cp))
9540 	{
9541 	add_item(head, tail, item, count);
9542 	return;
9543 	}
9544     else
9545         {
9546      /* "double quoted" wild card expressions pass as is */
9547      /* From DCL that means using e.g.:                  */
9548      /* perl program """perl.*"""                        */
9549      item_len = strlen(item);
9550      if ( '"' == *item && '"' == item[item_len-1] )
9551        {
9552        item++;
9553        item[item_len-2] = '\0';
9554        add_item(head, tail, item, count);
9555        return;
9556        }
9557      }
9558     resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9559     resultspec.dsc$b_class = DSC$K_CLASS_D;
9560     resultspec.dsc$a_pointer = NULL;
9561     vmsspec = PerlMem_malloc(VMS_MAXRSS);
9562     if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9563     if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9564       filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9565     if (!isunix || !filespec.dsc$a_pointer)
9566       filespec.dsc$a_pointer = item;
9567     filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9568     /*
9569      * Only return version specs, if the caller specified a version
9570      */
9571     had_version = strchr(item, ';');
9572     /*
9573      * Only return device and directory specs, if the caller specifed either.
9574      */
9575     had_device = strchr(item, ':');
9576     had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9577 
9578     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9579 				 (&filespec, &resultspec, &context,
9580     				  &defaultspec, 0, &rms_sts, &lff_flags)))
9581 	{
9582 	char *string;
9583 	char *c;
9584 
9585 	string = PerlMem_malloc(resultspec.dsc$w_length+1);
9586         if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9587 	strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9588 	string[resultspec.dsc$w_length] = '\0';
9589 	if (NULL == had_version)
9590 	    *(strrchr(string, ';')) = '\0';
9591 	if ((!had_directory) && (had_device == NULL))
9592 	    {
9593 	    if (NULL == (devdir = strrchr(string, ']')))
9594 		devdir = strrchr(string, '>');
9595 	    strcpy(string, devdir + 1);
9596 	    }
9597 	/*
9598 	 * Be consistent with what the C RTL has already done to the rest of
9599 	 * the argv items and lowercase all of these names.
9600 	 */
9601 	if (!decc_efs_case_preserve) {
9602 	    for (c = string; *c; ++c)
9603 	    if (isupper(*c))
9604 		*c = tolower(*c);
9605 	}
9606 	if (isunix) trim_unixpath(string,item,1);
9607 	add_item(head, tail, string, count);
9608 	++expcount;
9609     }
9610     PerlMem_free(vmsspec);
9611     if (sts != RMS$_NMF)
9612 	{
9613 	set_vaxc_errno(sts);
9614 	switch (sts)
9615 	    {
9616 	    case RMS$_FNF: case RMS$_DNF:
9617 		set_errno(ENOENT); break;
9618 	    case RMS$_DIR:
9619 		set_errno(ENOTDIR); break;
9620 	    case RMS$_DEV:
9621 		set_errno(ENODEV); break;
9622 	    case RMS$_FNM: case RMS$_SYN:
9623 		set_errno(EINVAL); break;
9624 	    case RMS$_PRV:
9625 		set_errno(EACCES); break;
9626 	    default:
9627 		_ckvmssts_noperl(sts);
9628 	    }
9629 	}
9630     if (expcount == 0)
9631 	add_item(head, tail, item, count);
9632     _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9633     _ckvmssts_noperl(lib$find_file_end(&context));
9634 }
9635 
9636 static int child_st[2];/* Event Flag set when child process completes	*/
9637 
9638 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox		*/
9639 
9640 static unsigned long int exit_handler(int *status)
9641 {
9642 short iosb[4];
9643 
9644     if (0 == child_st[0])
9645 	{
9646 #ifdef ARGPROC_DEBUG
9647 	PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9648 #endif
9649 	fflush(stdout);	    /* Have to flush pipe for binary data to	*/
9650 			    /* terminate properly -- <tp@mccall.com>	*/
9651 	sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9652 	sys$dassgn(child_chan);
9653 	fclose(stdout);
9654 	sys$synch(0, child_st);
9655 	}
9656     return(1);
9657 }
9658 
9659 static void sig_child(int chan)
9660 {
9661 #ifdef ARGPROC_DEBUG
9662     PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9663 #endif
9664     if (child_st[0] == 0)
9665 	child_st[0] = 1;
9666 }
9667 
9668 static struct exit_control_block exit_block =
9669     {
9670     0,
9671     exit_handler,
9672     1,
9673     &exit_block.exit_status,
9674     0
9675     };
9676 
9677 static void
9678 pipe_and_fork(pTHX_ char **cmargv)
9679 {
9680     PerlIO *fp;
9681     struct dsc$descriptor_s *vmscmd;
9682     char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9683     int sts, j, l, ismcr, quote, tquote = 0;
9684 
9685     sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9686     vms_execfree(vmscmd);
9687 
9688     j = l = 0;
9689     p = subcmd;
9690     q = cmargv[0];
9691     ismcr = q && toupper(*q) == 'M'     && toupper(*(q+1)) == 'C'
9692               && toupper(*(q+2)) == 'R' && !*(q+3);
9693 
9694     while (q && l < MAX_DCL_LINE_LENGTH) {
9695         if (!*q) {
9696             if (j > 0 && quote) {
9697                 *p++ = '"';
9698                 l++;
9699             }
9700             q = cmargv[++j];
9701             if (q) {
9702                 if (ismcr && j > 1) quote = 1;
9703                 tquote =  (strchr(q,' ')) != NULL || *q == '\0';
9704                 *p++ = ' ';
9705                 l++;
9706                 if (quote || tquote) {
9707                     *p++ = '"';
9708                     l++;
9709                 }
9710 	    }
9711         } else {
9712             if ((quote||tquote) && *q == '"') {
9713                 *p++ = '"';
9714                 l++;
9715 	    }
9716             *p++ = *q++;
9717             l++;
9718         }
9719     }
9720     *p = '\0';
9721 
9722     fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9723     if (fp == NULL) {
9724         PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9725     }
9726 }
9727 
9728 static int background_process(pTHX_ int argc, char **argv)
9729 {
9730 char command[MAX_DCL_SYMBOL + 1] = "$";
9731 $DESCRIPTOR(value, "");
9732 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9733 static $DESCRIPTOR(null, "NLA0:");
9734 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9735 char pidstring[80];
9736 $DESCRIPTOR(pidstr, "");
9737 int pid;
9738 unsigned long int flags = 17, one = 1, retsts;
9739 int len;
9740 
9741     strcat(command, argv[0]);
9742     len = strlen(command);
9743     while (--argc && (len < MAX_DCL_SYMBOL))
9744 	{
9745 	strcat(command, " \"");
9746 	strcat(command, *(++argv));
9747 	strcat(command, "\"");
9748 	len = strlen(command);
9749 	}
9750     value.dsc$a_pointer = command;
9751     value.dsc$w_length = strlen(value.dsc$a_pointer);
9752     _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9753     retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9754     if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9755 	_ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9756     }
9757     else {
9758 	_ckvmssts_noperl(retsts);
9759     }
9760 #ifdef ARGPROC_DEBUG
9761     PerlIO_printf(Perl_debug_log, "%s\n", command);
9762 #endif
9763     sprintf(pidstring, "%08X", pid);
9764     PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9765     pidstr.dsc$a_pointer = pidstring;
9766     pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9767     lib$set_symbol(&pidsymbol, &pidstr);
9768     return(SS$_NORMAL);
9769 }
9770 /*}}}*/
9771 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9772 
9773 
9774 /* OS-specific initialization at image activation (not thread startup) */
9775 /* Older VAXC header files lack these constants */
9776 #ifndef JPI$_RIGHTS_SIZE
9777 #  define JPI$_RIGHTS_SIZE 817
9778 #endif
9779 #ifndef KGB$M_SUBSYSTEM
9780 #  define KGB$M_SUBSYSTEM 0x8
9781 #endif
9782 
9783 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9784 
9785 /*{{{void vms_image_init(int *, char ***)*/
9786 void
9787 vms_image_init(int *argcp, char ***argvp)
9788 {
9789   int status;
9790   char eqv[LNM$C_NAMLENGTH+1] = "";
9791   unsigned int len, tabct = 8, tabidx = 0;
9792   unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9793   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9794   unsigned short int dummy, rlen;
9795   struct dsc$descriptor_s **tabvec;
9796 #if defined(PERL_IMPLICIT_CONTEXT)
9797   pTHX = NULL;
9798 #endif
9799   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
9800                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
9801                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9802                                  {          0,                0,    0,      0} };
9803 
9804 #ifdef KILL_BY_SIGPRC
9805     Perl_csighandler_init();
9806 #endif
9807 
9808 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9809     /* This was moved from the pre-image init handler because on threaded */
9810     /* Perl it was always returning 0 for the default value. */
9811     status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9812     if (status > 0) {
9813         int s;
9814 	s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9815 	if (s > 0) {
9816             int initial;
9817 	    initial = decc$feature_get_value(s, 4);
9818 	    if (initial > 0) {
9819                 /* initial is: 0 if nothing has set the feature */
9820                 /*            -1 if initialized to default */
9821                 /*             1 if set by logical name */
9822                 /*             2 if set by decc$feature_set_value */
9823 		decc_disable_posix_root = decc$feature_get_value(s, 1);
9824 
9825                 /* If the value is not valid, force the feature off */
9826 		if (decc_disable_posix_root < 0) {
9827 		    decc$feature_set_value(s, 1, 1);
9828 		    decc_disable_posix_root = 1;
9829 		}
9830 	    }
9831 	    else {
9832 		/* Nothing has asked for it explicitly, so use our own default. */
9833 		decc_disable_posix_root = 1;
9834 		decc$feature_set_value(s, 1, 1);
9835 	    }
9836 	}
9837     }
9838 #endif
9839 
9840   _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9841   _ckvmssts_noperl(iosb[0]);
9842   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9843     if (iprv[i]) {           /* Running image installed with privs? */
9844       _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
9845       will_taint = TRUE;
9846       break;
9847     }
9848   }
9849   /* Rights identifiers might trigger tainting as well. */
9850   if (!will_taint && (rlen || rsz)) {
9851     while (rlen < rsz) {
9852       /* We didn't get all the identifiers on the first pass.  Allocate a
9853        * buffer much larger than $GETJPI wants (rsz is size in bytes that
9854        * were needed to hold all identifiers at time of last call; we'll
9855        * allocate that many unsigned long ints), and go back and get 'em.
9856        * If it gave us less than it wanted to despite ample buffer space,
9857        * something's broken.  Is your system missing a system identifier?
9858        */
9859       if (rsz <= jpilist[1].buflen) {
9860          /* Perl_croak accvios when used this early in startup. */
9861          fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9862                          rsz, (unsigned long) jpilist[1].buflen,
9863                          "Check your rights database for corruption.\n");
9864          exit(SS$_ABORT);
9865       }
9866       if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9867       jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9868       if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9869       jpilist[1].buflen = rsz * sizeof(unsigned long int);
9870       _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9871       _ckvmssts_noperl(iosb[0]);
9872     }
9873     mask = jpilist[1].bufadr;
9874     /* Check attribute flags for each identifier (2nd longword); protected
9875      * subsystem identifiers trigger tainting.
9876      */
9877     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9878       if (mask[i] & KGB$M_SUBSYSTEM) {
9879         will_taint = TRUE;
9880         break;
9881       }
9882     }
9883     if (mask != rlst) PerlMem_free(mask);
9884   }
9885 
9886   /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9887    * logical, some versions of the CRTL will add a phanthom /000000/
9888    * directory.  This needs to be removed.
9889    */
9890   if (decc_filename_unix_report) {
9891   char * zeros;
9892   int ulen;
9893     ulen = strlen(argvp[0][0]);
9894     if (ulen > 7) {
9895       zeros = strstr(argvp[0][0], "/000000/");
9896       if (zeros != NULL) {
9897 	int mlen;
9898 	mlen = ulen - (zeros - argvp[0][0]) - 7;
9899 	memmove(zeros, &zeros[7], mlen);
9900 	ulen = ulen - 7;
9901 	argvp[0][0][ulen] = '\0';
9902       }
9903     }
9904     /* It also may have a trailing dot that needs to be removed otherwise
9905      * it will be converted to VMS mode incorrectly.
9906      */
9907     ulen--;
9908     if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9909       argvp[0][0][ulen] = '\0';
9910   }
9911 
9912   /* We need to use this hack to tell Perl it should run with tainting,
9913    * since its tainting flag may be part of the PL_curinterp struct, which
9914    * hasn't been allocated when vms_image_init() is called.
9915    */
9916   if (will_taint) {
9917     char **newargv, **oldargv;
9918     oldargv = *argvp;
9919     newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9920     if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9921     newargv[0] = oldargv[0];
9922     newargv[1] = PerlMem_malloc(3 * sizeof(char));
9923     if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9924     strcpy(newargv[1], "-T");
9925     Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9926     (*argcp)++;
9927     newargv[*argcp] = NULL;
9928     /* We orphan the old argv, since we don't know where it's come from,
9929      * so we don't know how to free it.
9930      */
9931     *argvp = newargv;
9932   }
9933   else {  /* Did user explicitly request tainting? */
9934     int i;
9935     char *cp, **av = *argvp;
9936     for (i = 1; i < *argcp; i++) {
9937       if (*av[i] != '-') break;
9938       for (cp = av[i]+1; *cp; cp++) {
9939         if (*cp == 'T') { will_taint = 1; break; }
9940         else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9941                   strchr("DFIiMmx",*cp)) break;
9942       }
9943       if (will_taint) break;
9944     }
9945   }
9946 
9947   for (tabidx = 0;
9948        len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9949        tabidx++) {
9950     if (!tabidx) {
9951       tabvec = (struct dsc$descriptor_s **)
9952 	    PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9953       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9954     }
9955     else if (tabidx >= tabct) {
9956       tabct += 8;
9957       tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9958       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9959     }
9960     tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9961     if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9962     tabvec[tabidx]->dsc$w_length  = 0;
9963     tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
9964     tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_D;
9965     tabvec[tabidx]->dsc$a_pointer = NULL;
9966     _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9967   }
9968   if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9969 
9970   getredirection(argcp,argvp);
9971 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9972   {
9973 # include <reentrancy.h>
9974   decc$set_reentrancy(C$C_MULTITHREAD);
9975   }
9976 #endif
9977   return;
9978 }
9979 /*}}}*/
9980 
9981 
9982 /* trim_unixpath()
9983  * Trim Unix-style prefix off filespec, so it looks like what a shell
9984  * glob expansion would return (i.e. from specified prefix on, not
9985  * full path).  Note that returned filespec is Unix-style, regardless
9986  * of whether input filespec was VMS-style or Unix-style.
9987  *
9988  * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9989  * determine prefix (both may be in VMS or Unix syntax).  opts is a bit
9990  * vector of options; at present, only bit 0 is used, and if set tells
9991  * trim unixpath to try the current default directory as a prefix when
9992  * presented with a possibly ambiguous ... wildcard.
9993  *
9994  * Returns !=0 on success, with trimmed filespec replacing contents of
9995  * fspec, and 0 on failure, with contents of fpsec unchanged.
9996  */
9997 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9998 int
9999 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
10000 {
10001   char *unixified, *unixwild,
10002        *template, *base, *end, *cp1, *cp2;
10003   register int tmplen, reslen = 0, dirs = 0;
10004 
10005   if (!wildspec || !fspec) return 0;
10006 
10007   unixwild = PerlMem_malloc(VMS_MAXRSS);
10008   if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10009   template = unixwild;
10010   if (strpbrk(wildspec,"]>:") != NULL) {
10011     if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
10012         PerlMem_free(unixwild);
10013 	return 0;
10014     }
10015   }
10016   else {
10017     strncpy(unixwild, wildspec, VMS_MAXRSS-1);
10018     unixwild[VMS_MAXRSS-1] = 0;
10019   }
10020   unixified = PerlMem_malloc(VMS_MAXRSS);
10021   if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10022   if (strpbrk(fspec,"]>:") != NULL) {
10023     if (int_tounixspec(fspec, unixified, NULL) == NULL) {
10024         PerlMem_free(unixwild);
10025         PerlMem_free(unixified);
10026 	return 0;
10027     }
10028     else base = unixified;
10029     /* reslen != 0 ==> we had to unixify resultant filespec, so we must
10030      * check to see that final result fits into (isn't longer than) fspec */
10031     reslen = strlen(fspec);
10032   }
10033   else base = fspec;
10034 
10035   /* No prefix or absolute path on wildcard, so nothing to remove */
10036   if (!*template || *template == '/') {
10037     PerlMem_free(unixwild);
10038     if (base == fspec) {
10039         PerlMem_free(unixified);
10040 	return 1;
10041     }
10042     tmplen = strlen(unixified);
10043     if (tmplen > reslen) {
10044         PerlMem_free(unixified);
10045 	return 0;  /* not enough space */
10046     }
10047     /* Copy unixified resultant, including trailing NUL */
10048     memmove(fspec,unixified,tmplen+1);
10049     PerlMem_free(unixified);
10050     return 1;
10051   }
10052 
10053   for (end = base; *end; end++) ;  /* Find end of resultant filespec */
10054   if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
10055     for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
10056     for (cp1 = end ;cp1 >= base; cp1--)
10057       if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
10058         { cp1++; break; }
10059     if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
10060     PerlMem_free(unixified);
10061     PerlMem_free(unixwild);
10062     return 1;
10063   }
10064   else {
10065     char *tpl, *lcres;
10066     char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
10067     int ells = 1, totells, segdirs, match;
10068     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
10069                             resdsc =  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10070 
10071     while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
10072     totells = ells;
10073     for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
10074     tpl = PerlMem_malloc(VMS_MAXRSS);
10075     if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10076     if (ellipsis == template && opts & 1) {
10077       /* Template begins with an ellipsis.  Since we can't tell how many
10078        * directory names at the front of the resultant to keep for an
10079        * arbitrary starting point, we arbitrarily choose the current
10080        * default directory as a starting point.  If it's there as a prefix,
10081        * clip it off.  If not, fall through and act as if the leading
10082        * ellipsis weren't there (i.e. return shortest possible path that
10083        * could match template).
10084        */
10085       if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
10086 	  PerlMem_free(tpl);
10087 	  PerlMem_free(unixified);
10088 	  PerlMem_free(unixwild);
10089 	  return 0;
10090       }
10091       if (!decc_efs_case_preserve) {
10092  	for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10093 	  if (_tolower(*cp1) != _tolower(*cp2)) break;
10094       }
10095       segdirs = dirs - totells;  /* Min # of dirs we must have left */
10096       for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
10097       if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
10098         memmove(fspec,cp2+1,end - cp2);
10099 	PerlMem_free(tpl);
10100 	PerlMem_free(unixified);
10101 	PerlMem_free(unixwild);
10102         return 1;
10103       }
10104     }
10105     /* First off, back up over constant elements at end of path */
10106     if (dirs) {
10107       for (front = end ; front >= base; front--)
10108          if (*front == '/' && !dirs--) { front++; break; }
10109     }
10110     lcres = PerlMem_malloc(VMS_MAXRSS);
10111     if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10112     for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10113          cp1++,cp2++) {
10114 	    if (!decc_efs_case_preserve) {
10115 		*cp2 = _tolower(*cp1);  /* Make lc copy for match */
10116 	    }
10117 	    else {
10118 		*cp2 = *cp1;
10119 	    }
10120     }
10121     if (cp1 != '\0') {
10122 	PerlMem_free(tpl);
10123 	PerlMem_free(unixified);
10124 	PerlMem_free(unixwild);
10125 	PerlMem_free(lcres);
10126 	return 0;  /* Path too long. */
10127     }
10128     lcend = cp2;
10129     *cp2 = '\0';  /* Pick up with memcpy later */
10130     lcfront = lcres + (front - base);
10131     /* Now skip over each ellipsis and try to match the path in front of it. */
10132     while (ells--) {
10133       for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10134         if (*(cp1)   == '.' && *(cp1+1) == '.' &&
10135             *(cp1+2) == '.' && *(cp1+3) == '/'    ) break;
10136       if (cp1 < template) break; /* template started with an ellipsis */
10137       if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10138         ellipsis = cp1; continue;
10139       }
10140       wilddsc.dsc$a_pointer = tpl;
10141       wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10142       nextell = cp1;
10143       for (segdirs = 0, cp2 = tpl;
10144            cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10145            cp1++, cp2++) {
10146          if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10147          else {
10148 	    if (!decc_efs_case_preserve) {
10149 	      *cp2 = _tolower(*cp1);  /* else lowercase for match */
10150 	    }
10151 	    else {
10152 	      *cp2 = *cp1;  /* else preserve case for match */
10153 	    }
10154 	 }
10155          if (*cp2 == '/') segdirs++;
10156       }
10157       if (cp1 != ellipsis - 1) {
10158 	  PerlMem_free(tpl);
10159 	  PerlMem_free(unixified);
10160 	  PerlMem_free(unixwild);
10161 	  PerlMem_free(lcres);
10162 	  return 0; /* Path too long */
10163       }
10164       /* Back up at least as many dirs as in template before matching */
10165       for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10166         if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10167       for (match = 0; cp1 > lcres;) {
10168         resdsc.dsc$a_pointer = cp1;
10169         if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10170           match++;
10171           if (match == 1) lcfront = cp1;
10172         }
10173         for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10174       }
10175       if (!match) {
10176 	PerlMem_free(tpl);
10177 	PerlMem_free(unixified);
10178 	PerlMem_free(unixwild);
10179 	PerlMem_free(lcres);
10180 	return 0;  /* Can't find prefix ??? */
10181       }
10182       if (match > 1 && opts & 1) {
10183         /* This ... wildcard could cover more than one set of dirs (i.e.
10184          * a set of similar dir names is repeated).  If the template
10185          * contains more than 1 ..., upstream elements could resolve the
10186          * ambiguity, but it's not worth a full backtracking setup here.
10187          * As a quick heuristic, clip off the current default directory
10188          * if it's present to find the trimmed spec, else use the
10189          * shortest string that this ... could cover.
10190          */
10191         char def[NAM$C_MAXRSS+1], *st;
10192 
10193         if (getcwd(def, sizeof def,0) == NULL) {
10194 	    PerlMem_free(unixified);
10195 	    PerlMem_free(unixwild);
10196 	    PerlMem_free(lcres);
10197 	    PerlMem_free(tpl);
10198 	    return 0;
10199 	}
10200 	if (!decc_efs_case_preserve) {
10201 	  for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10202 	    if (_tolower(*cp1) != _tolower(*cp2)) break;
10203 	}
10204         segdirs = dirs - totells;  /* Min # of dirs we must have left */
10205         for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10206         if (*cp1 == '\0' && *cp2 == '/') {
10207           memmove(fspec,cp2+1,end - cp2);
10208 	  PerlMem_free(tpl);
10209 	  PerlMem_free(unixified);
10210 	  PerlMem_free(unixwild);
10211 	  PerlMem_free(lcres);
10212           return 1;
10213         }
10214         /* Nope -- stick with lcfront from above and keep going. */
10215       }
10216     }
10217     memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10218     PerlMem_free(tpl);
10219     PerlMem_free(unixified);
10220     PerlMem_free(unixwild);
10221     PerlMem_free(lcres);
10222     return 1;
10223     ellipsis = nextell;
10224   }
10225 
10226 }  /* end of trim_unixpath() */
10227 /*}}}*/
10228 
10229 
10230 /*
10231  *  VMS readdir() routines.
10232  *  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10233  *
10234  *  21-Jul-1994  Charles Bailey  bailey@newman.upenn.edu
10235  *  Minor modifications to original routines.
10236  */
10237 
10238 /* readdir may have been redefined by reentr.h, so make sure we get
10239  * the local version for what we do here.
10240  */
10241 #ifdef readdir
10242 # undef readdir
10243 #endif
10244 #if !defined(PERL_IMPLICIT_CONTEXT)
10245 # define readdir Perl_readdir
10246 #else
10247 # define readdir(a) Perl_readdir(aTHX_ a)
10248 #endif
10249 
10250     /* Number of elements in vms_versions array */
10251 #define VERSIZE(e)	(sizeof e->vms_versions / sizeof e->vms_versions[0])
10252 
10253 /*
10254  *  Open a directory, return a handle for later use.
10255  */
10256 /*{{{ DIR *opendir(char*name) */
10257 DIR *
10258 Perl_opendir(pTHX_ const char *name)
10259 {
10260     DIR *dd;
10261     char *dir;
10262     Stat_t sb;
10263 
10264     Newx(dir, VMS_MAXRSS, char);
10265     if (int_tovmspath(name, dir, NULL) == NULL) {
10266       Safefree(dir);
10267       return NULL;
10268     }
10269     /* Check access before stat; otherwise stat does not
10270      * accurately report whether it's a directory.
10271      */
10272     if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10273       /* cando_by_name has already set errno */
10274       Safefree(dir);
10275       return NULL;
10276     }
10277     if (flex_stat(dir,&sb) == -1) return NULL;
10278     if (!S_ISDIR(sb.st_mode)) {
10279       Safefree(dir);
10280       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
10281       return NULL;
10282     }
10283     /* Get memory for the handle, and the pattern. */
10284     Newx(dd,1,DIR);
10285     Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10286 
10287     /* Fill in the fields; mainly playing with the descriptor. */
10288     sprintf(dd->pattern, "%s*.*",dir);
10289     Safefree(dir);
10290     dd->context = 0;
10291     dd->count = 0;
10292     dd->flags = 0;
10293     /* By saying we always want the result of readdir() in unix format, we
10294      * are really saying we want all the escapes removed.  Otherwise the caller,
10295      * having no way to know whether it's already in VMS format, might send it
10296      * through tovmsspec again, thus double escaping.
10297      */
10298     dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10299     dd->pat.dsc$a_pointer = dd->pattern;
10300     dd->pat.dsc$w_length = strlen(dd->pattern);
10301     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10302     dd->pat.dsc$b_class = DSC$K_CLASS_S;
10303 #if defined(USE_ITHREADS)
10304     Newx(dd->mutex,1,perl_mutex);
10305     MUTEX_INIT( (perl_mutex *) dd->mutex );
10306 #else
10307     dd->mutex = NULL;
10308 #endif
10309 
10310     return dd;
10311 }  /* end of opendir() */
10312 /*}}}*/
10313 
10314 /*
10315  *  Set the flag to indicate we want versions or not.
10316  */
10317 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10318 void
10319 vmsreaddirversions(DIR *dd, int flag)
10320 {
10321     if (flag)
10322 	dd->flags |= PERL_VMSDIR_M_VERSIONS;
10323     else
10324 	dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10325 }
10326 /*}}}*/
10327 
10328 /*
10329  *  Free up an opened directory.
10330  */
10331 /*{{{ void closedir(DIR *dd)*/
10332 void
10333 Perl_closedir(DIR *dd)
10334 {
10335     int sts;
10336 
10337     sts = lib$find_file_end(&dd->context);
10338     Safefree(dd->pattern);
10339 #if defined(USE_ITHREADS)
10340     MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10341     Safefree(dd->mutex);
10342 #endif
10343     Safefree(dd);
10344 }
10345 /*}}}*/
10346 
10347 /*
10348  *  Collect all the version numbers for the current file.
10349  */
10350 static void
10351 collectversions(pTHX_ DIR *dd)
10352 {
10353     struct dsc$descriptor_s	pat;
10354     struct dsc$descriptor_s	res;
10355     struct dirent *e;
10356     char *p, *text, *buff;
10357     int i;
10358     unsigned long context, tmpsts;
10359 
10360     /* Convenient shorthand. */
10361     e = &dd->entry;
10362 
10363     /* Add the version wildcard, ignoring the "*.*" put on before */
10364     i = strlen(dd->pattern);
10365     Newx(text,i + e->d_namlen + 3,char);
10366     strcpy(text, dd->pattern);
10367     sprintf(&text[i - 3], "%s;*", e->d_name);
10368 
10369     /* Set up the pattern descriptor. */
10370     pat.dsc$a_pointer = text;
10371     pat.dsc$w_length = i + e->d_namlen - 1;
10372     pat.dsc$b_dtype = DSC$K_DTYPE_T;
10373     pat.dsc$b_class = DSC$K_CLASS_S;
10374 
10375     /* Set up result descriptor. */
10376     Newx(buff, VMS_MAXRSS, char);
10377     res.dsc$a_pointer = buff;
10378     res.dsc$w_length = VMS_MAXRSS - 1;
10379     res.dsc$b_dtype = DSC$K_DTYPE_T;
10380     res.dsc$b_class = DSC$K_CLASS_S;
10381 
10382     /* Read files, collecting versions. */
10383     for (context = 0, e->vms_verscount = 0;
10384          e->vms_verscount < VERSIZE(e);
10385          e->vms_verscount++) {
10386 	unsigned long rsts;
10387 	unsigned long flags = 0;
10388 
10389 #ifdef VMS_LONGNAME_SUPPORT
10390 	flags = LIB$M_FIL_LONG_NAMES;
10391 #endif
10392 	tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10393 	if (tmpsts == RMS$_NMF || context == 0) break;
10394 	_ckvmssts(tmpsts);
10395 	buff[VMS_MAXRSS - 1] = '\0';
10396 	if ((p = strchr(buff, ';')))
10397 	    e->vms_versions[e->vms_verscount] = atoi(p + 1);
10398 	else
10399 	    e->vms_versions[e->vms_verscount] = -1;
10400     }
10401 
10402     _ckvmssts(lib$find_file_end(&context));
10403     Safefree(text);
10404     Safefree(buff);
10405 
10406 }  /* end of collectversions() */
10407 
10408 /*
10409  *  Read the next entry from the directory.
10410  */
10411 /*{{{ struct dirent *readdir(DIR *dd)*/
10412 struct dirent *
10413 Perl_readdir(pTHX_ DIR *dd)
10414 {
10415     struct dsc$descriptor_s	res;
10416     char *p, *buff;
10417     unsigned long int tmpsts;
10418     unsigned long rsts;
10419     unsigned long flags = 0;
10420     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10421     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10422 
10423     /* Set up result descriptor, and get next file. */
10424     Newx(buff, VMS_MAXRSS, char);
10425     res.dsc$a_pointer = buff;
10426     res.dsc$w_length = VMS_MAXRSS - 1;
10427     res.dsc$b_dtype = DSC$K_DTYPE_T;
10428     res.dsc$b_class = DSC$K_CLASS_S;
10429 
10430 #ifdef VMS_LONGNAME_SUPPORT
10431     flags = LIB$M_FIL_LONG_NAMES;
10432 #endif
10433 
10434     tmpsts = lib$find_file
10435 	(&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10436     if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL;  /* None left. */
10437     if (!(tmpsts & 1)) {
10438       set_vaxc_errno(tmpsts);
10439       switch (tmpsts) {
10440         case RMS$_PRV:
10441           set_errno(EACCES); break;
10442         case RMS$_DEV:
10443           set_errno(ENODEV); break;
10444         case RMS$_DIR:
10445           set_errno(ENOTDIR); break;
10446         case RMS$_FNF: case RMS$_DNF:
10447           set_errno(ENOENT); break;
10448         default:
10449           set_errno(EVMSERR);
10450       }
10451       Safefree(buff);
10452       return NULL;
10453     }
10454     dd->count++;
10455     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10456     buff[res.dsc$w_length] = '\0';
10457     p = buff + res.dsc$w_length;
10458     while (--p >= buff) if (!isspace(*p)) break;
10459     *p = '\0';
10460     if (!decc_efs_case_preserve) {
10461       for (p = buff; *p; p++) *p = _tolower(*p);
10462     }
10463 
10464     /* Skip any directory component and just copy the name. */
10465     sts = vms_split_path
10466        (buff,
10467 	&v_spec,
10468 	&v_len,
10469 	&r_spec,
10470 	&r_len,
10471 	&d_spec,
10472 	&d_len,
10473 	&n_spec,
10474 	&n_len,
10475 	&e_spec,
10476 	&e_len,
10477 	&vs_spec,
10478 	&vs_len);
10479 
10480     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10481 
10482         /* In Unix report mode, remove the ".dir;1" from the name */
10483         /* if it is a real directory. */
10484         if (decc_filename_unix_report || decc_efs_charset) {
10485             if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10486                 Stat_t statbuf;
10487                 int ret_sts;
10488 
10489                 ret_sts = flex_lstat(buff, &statbuf);
10490                 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10491                     e_len = 0;
10492                     e_spec[0] = 0;
10493                 }
10494             }
10495         }
10496 
10497         /* Drop NULL extensions on UNIX file specification */
10498 	if ((e_len == 1) && decc_readdir_dropdotnotype) {
10499 	    e_len = 0;
10500 	    e_spec[0] = '\0';
10501         }
10502     }
10503 
10504     strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10505     dd->entry.d_name[n_len + e_len] = '\0';
10506     dd->entry.d_namlen = strlen(dd->entry.d_name);
10507 
10508     /* Convert the filename to UNIX format if needed */
10509     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10510 
10511 	/* Translate the encoded characters. */
10512 	/* Fixme: Unicode handling could result in embedded 0 characters */
10513 	if (strchr(dd->entry.d_name, '^') != NULL) {
10514 	    char new_name[256];
10515 	    char * q;
10516 	    p = dd->entry.d_name;
10517 	    q = new_name;
10518 	    while (*p != 0) {
10519 		int inchars_read, outchars_added;
10520 		inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10521 		p += inchars_read;
10522 		q += outchars_added;
10523 		/* fix-me */
10524 		/* if outchars_added > 1, then this is a wide file specification */
10525 		/* Wide file specifications need to be passed in Perl */
10526 		/* counted strings apparently with a Unicode flag */
10527 	    }
10528 	    *q = 0;
10529 	    strcpy(dd->entry.d_name, new_name);
10530 	    dd->entry.d_namlen = strlen(dd->entry.d_name);
10531 	}
10532     }
10533 
10534     dd->entry.vms_verscount = 0;
10535     if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10536     Safefree(buff);
10537     return &dd->entry;
10538 
10539 }  /* end of readdir() */
10540 /*}}}*/
10541 
10542 /*
10543  *  Read the next entry from the directory -- thread-safe version.
10544  */
10545 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10546 int
10547 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10548 {
10549     int retval;
10550 
10551     MUTEX_LOCK( (perl_mutex *) dd->mutex );
10552 
10553     entry = readdir(dd);
10554     *result = entry;
10555     retval = ( *result == NULL ? errno : 0 );
10556 
10557     MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10558 
10559     return retval;
10560 
10561 }  /* end of readdir_r() */
10562 /*}}}*/
10563 
10564 /*
10565  *  Return something that can be used in a seekdir later.
10566  */
10567 /*{{{ long telldir(DIR *dd)*/
10568 long
10569 Perl_telldir(DIR *dd)
10570 {
10571     return dd->count;
10572 }
10573 /*}}}*/
10574 
10575 /*
10576  *  Return to a spot where we used to be.  Brute force.
10577  */
10578 /*{{{ void seekdir(DIR *dd,long count)*/
10579 void
10580 Perl_seekdir(pTHX_ DIR *dd, long count)
10581 {
10582     int old_flags;
10583 
10584     /* If we haven't done anything yet... */
10585     if (dd->count == 0)
10586 	return;
10587 
10588     /* Remember some state, and clear it. */
10589     old_flags = dd->flags;
10590     dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10591     _ckvmssts(lib$find_file_end(&dd->context));
10592     dd->context = 0;
10593 
10594     /* The increment is in readdir(). */
10595     for (dd->count = 0; dd->count < count; )
10596 	readdir(dd);
10597 
10598     dd->flags = old_flags;
10599 
10600 }  /* end of seekdir() */
10601 /*}}}*/
10602 
10603 /* VMS subprocess management
10604  *
10605  * my_vfork() - just a vfork(), after setting a flag to record that
10606  * the current script is trying a Unix-style fork/exec.
10607  *
10608  * vms_do_aexec() and vms_do_exec() are called in response to the
10609  * perl 'exec' function.  If this follows a vfork call, then they
10610  * call out the regular perl routines in doio.c which do an
10611  * execvp (for those who really want to try this under VMS).
10612  * Otherwise, they do exactly what the perl docs say exec should
10613  * do - terminate the current script and invoke a new command
10614  * (See below for notes on command syntax.)
10615  *
10616  * do_aspawn() and do_spawn() implement the VMS side of the perl
10617  * 'system' function.
10618  *
10619  * Note on command arguments to perl 'exec' and 'system': When handled
10620  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10621  * are concatenated to form a DCL command string.  If the first non-numeric
10622  * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10623  * the command string is handed off to DCL directly.  Otherwise,
10624  * the first token of the command is taken as the filespec of an image
10625  * to run.  The filespec is expanded using a default type of '.EXE' and
10626  * the process defaults for device, directory, etc., and if found, the resultant
10627  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10628  * the command string as parameters.  This is perhaps a bit complicated,
10629  * but I hope it will form a happy medium between what VMS folks expect
10630  * from lib$spawn and what Unix folks expect from exec.
10631  */
10632 
10633 static int vfork_called;
10634 
10635 /*{{{int my_vfork()*/
10636 int
10637 my_vfork()
10638 {
10639   vfork_called++;
10640   return vfork();
10641 }
10642 /*}}}*/
10643 
10644 
10645 static void
10646 vms_execfree(struct dsc$descriptor_s *vmscmd)
10647 {
10648   if (vmscmd) {
10649       if (vmscmd->dsc$a_pointer) {
10650           PerlMem_free(vmscmd->dsc$a_pointer);
10651       }
10652       PerlMem_free(vmscmd);
10653   }
10654 }
10655 
10656 static char *
10657 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10658 {
10659   char *junk, *tmps = NULL;
10660   register size_t cmdlen = 0;
10661   size_t rlen;
10662   register SV **idx;
10663   STRLEN n_a;
10664 
10665   idx = mark;
10666   if (really) {
10667     tmps = SvPV(really,rlen);
10668     if (*tmps) {
10669       cmdlen += rlen + 1;
10670       idx++;
10671     }
10672   }
10673 
10674   for (idx++; idx <= sp; idx++) {
10675     if (*idx) {
10676       junk = SvPVx(*idx,rlen);
10677       cmdlen += rlen ? rlen + 1 : 0;
10678     }
10679   }
10680   Newx(PL_Cmd, cmdlen+1, char);
10681 
10682   if (tmps && *tmps) {
10683     strcpy(PL_Cmd,tmps);
10684     mark++;
10685   }
10686   else *PL_Cmd = '\0';
10687   while (++mark <= sp) {
10688     if (*mark) {
10689       char *s = SvPVx(*mark,n_a);
10690       if (!*s) continue;
10691       if (*PL_Cmd) strcat(PL_Cmd," ");
10692       strcat(PL_Cmd,s);
10693     }
10694   }
10695   return PL_Cmd;
10696 
10697 }  /* end of setup_argstr() */
10698 
10699 
10700 static unsigned long int
10701 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10702                    struct dsc$descriptor_s **pvmscmd)
10703 {
10704   char * vmsspec;
10705   char * resspec;
10706   char image_name[NAM$C_MAXRSS+1];
10707   char image_argv[NAM$C_MAXRSS+1];
10708   $DESCRIPTOR(defdsc,".EXE");
10709   $DESCRIPTOR(defdsc2,".");
10710   struct dsc$descriptor_s resdsc;
10711   struct dsc$descriptor_s *vmscmd;
10712   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10713   unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10714   register char *s, *rest, *cp, *wordbreak;
10715   char * cmd;
10716   int cmdlen;
10717   register int isdcl;
10718 
10719   vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10720   if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10721 
10722   /* vmsspec is a DCL command buffer, not just a filename */
10723   vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10724   if (vmsspec == NULL)
10725       _ckvmssts_noperl(SS$_INSFMEM);
10726 
10727   resspec = PerlMem_malloc(VMS_MAXRSS);
10728   if (resspec == NULL)
10729       _ckvmssts_noperl(SS$_INSFMEM);
10730 
10731   /* Make a copy for modification */
10732   cmdlen = strlen(incmd);
10733   cmd = PerlMem_malloc(cmdlen+1);
10734   if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10735   strncpy(cmd, incmd, cmdlen);
10736   cmd[cmdlen] = 0;
10737   image_name[0] = 0;
10738   image_argv[0] = 0;
10739 
10740   resdsc.dsc$a_pointer = resspec;
10741   resdsc.dsc$b_dtype  = DSC$K_DTYPE_T;
10742   resdsc.dsc$b_class  = DSC$K_CLASS_S;
10743   resdsc.dsc$w_length = VMS_MAXRSS - 1;
10744 
10745   vmscmd->dsc$a_pointer = NULL;
10746   vmscmd->dsc$b_dtype  = DSC$K_DTYPE_T;
10747   vmscmd->dsc$b_class  = DSC$K_CLASS_S;
10748   vmscmd->dsc$w_length = 0;
10749   if (pvmscmd) *pvmscmd = vmscmd;
10750 
10751   if (suggest_quote) *suggest_quote = 0;
10752 
10753   if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10754     PerlMem_free(cmd);
10755     PerlMem_free(vmsspec);
10756     PerlMem_free(resspec);
10757     return CLI$_BUFOVF;                /* continuation lines currently unsupported */
10758   }
10759 
10760   s = cmd;
10761 
10762   while (*s && isspace(*s)) s++;
10763 
10764   if (*s == '@' || *s == '$') {
10765     vmsspec[0] = *s;  rest = s + 1;
10766     for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10767   }
10768   else { cp = vmsspec; rest = s; }
10769   if (*rest == '.' || *rest == '/') {
10770     char *cp2;
10771     for (cp2 = resspec;
10772          *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10773          rest++, cp2++) *cp2 = *rest;
10774     *cp2 = '\0';
10775     if (int_tovmsspec(resspec, cp, 0, NULL)) {
10776       s = vmsspec;
10777 
10778       /* When a UNIX spec with no file type is translated to VMS, */
10779       /* A trailing '.' is appended under ODS-5 rules.            */
10780       /* Here we do not want that trailing "." as it prevents     */
10781       /* Looking for a implied ".exe" type. */
10782       if (decc_efs_charset) {
10783           int i;
10784           i = strlen(vmsspec);
10785           if (vmsspec[i-1] == '.') {
10786               vmsspec[i-1] = '\0';
10787           }
10788       }
10789 
10790       if (*rest) {
10791         for (cp2 = vmsspec + strlen(vmsspec);
10792              *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10793              rest++, cp2++) *cp2 = *rest;
10794         *cp2 = '\0';
10795       }
10796     }
10797   }
10798   /* Intuit whether verb (first word of cmd) is a DCL command:
10799    *   - if first nonspace char is '@', it's a DCL indirection
10800    * otherwise
10801    *   - if verb contains a filespec separator, it's not a DCL command
10802    *   - if it doesn't, caller tells us whether to default to a DCL
10803    *     command, or to a local image unless told it's DCL (by leading '$')
10804    */
10805   if (*s == '@') {
10806       isdcl = 1;
10807       if (suggest_quote) *suggest_quote = 1;
10808   } else {
10809     register char *filespec = strpbrk(s,":<[.;");
10810     rest = wordbreak = strpbrk(s," \"\t/");
10811     if (!wordbreak) wordbreak = s + strlen(s);
10812     if (*s == '$') check_img = 0;
10813     if (filespec && (filespec < wordbreak)) isdcl = 0;
10814     else isdcl = !check_img;
10815   }
10816 
10817   if (!isdcl) {
10818     int rsts;
10819     imgdsc.dsc$a_pointer = s;
10820     imgdsc.dsc$w_length = wordbreak - s;
10821     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10822     if (!(retsts&1)) {
10823         _ckvmssts_noperl(lib$find_file_end(&cxt));
10824         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10825       if (!(retsts & 1) && *s == '$') {
10826         _ckvmssts_noperl(lib$find_file_end(&cxt));
10827 	imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10828 	retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10829 	if (!(retsts&1)) {
10830 	  _ckvmssts_noperl(lib$find_file_end(&cxt));
10831           retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10832         }
10833       }
10834     }
10835     _ckvmssts_noperl(lib$find_file_end(&cxt));
10836 
10837     if (retsts & 1) {
10838       FILE *fp;
10839       s = resspec;
10840       while (*s && !isspace(*s)) s++;
10841       *s = '\0';
10842 
10843       /* check that it's really not DCL with no file extension */
10844       fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10845       if (fp) {
10846         char b[256] = {0,0,0,0};
10847         read(fileno(fp), b, 256);
10848         isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10849 	if (isdcl) {
10850 	  int shebang_len;
10851 
10852 	  /* Check for script */
10853 	  shebang_len = 0;
10854 	  if ((b[0] == '#') && (b[1] == '!'))
10855 	     shebang_len = 2;
10856 #ifdef ALTERNATE_SHEBANG
10857 	  else {
10858 	    shebang_len = strlen(ALTERNATE_SHEBANG);
10859 	    if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10860 	      char * perlstr;
10861 		perlstr = strstr("perl",b);
10862 		if (perlstr == NULL)
10863 		  shebang_len = 0;
10864 	    }
10865 	    else
10866 	      shebang_len = 0;
10867 	  }
10868 #endif
10869 
10870 	  if (shebang_len > 0) {
10871 	  int i;
10872 	  int j;
10873 	  char tmpspec[NAM$C_MAXRSS + 1];
10874 
10875 	    i = shebang_len;
10876 	     /* Image is following after white space */
10877 	    /*--------------------------------------*/
10878 	    while (isprint(b[i]) && isspace(b[i]))
10879 		i++;
10880 
10881 	    j = 0;
10882 	    while (isprint(b[i]) && !isspace(b[i])) {
10883 		tmpspec[j++] = b[i++];
10884 		if (j >= NAM$C_MAXRSS)
10885 		   break;
10886 	    }
10887 	    tmpspec[j] = '\0';
10888 
10889 	     /* There may be some default parameters to the image */
10890 	    /*---------------------------------------------------*/
10891 	    j = 0;
10892 	    while (isprint(b[i])) {
10893 		image_argv[j++] = b[i++];
10894 		if (j >= NAM$C_MAXRSS)
10895 		   break;
10896 	    }
10897 	    while ((j > 0) && !isprint(image_argv[j-1]))
10898 		j--;
10899 	    image_argv[j] = 0;
10900 
10901 	    /* It will need to be converted to VMS format and validated */
10902 	    if (tmpspec[0] != '\0') {
10903 	      char * iname;
10904 
10905 	       /* Try to find the exact program requested to be run */
10906 	      /*---------------------------------------------------*/
10907 	      iname = int_rmsexpand
10908 		 (tmpspec, image_name, ".exe",
10909 		  PERL_RMSEXPAND_M_VMS, NULL, NULL);
10910 	      if (iname != NULL) {
10911 		if (cando_by_name_int
10912 			(S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10913 		  /* MCR prefix needed */
10914 		  isdcl = 0;
10915 		}
10916 		else {
10917 		   /* Try again with a null type */
10918 		  /*----------------------------*/
10919 		  iname = int_rmsexpand
10920 		    (tmpspec, image_name, ".",
10921 		     PERL_RMSEXPAND_M_VMS, NULL, NULL);
10922 		  if (iname != NULL) {
10923 		    if (cando_by_name_int
10924 			 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10925 		      /* MCR prefix needed */
10926 		      isdcl = 0;
10927 		    }
10928 		  }
10929 		}
10930 
10931 		 /* Did we find the image to run the script? */
10932 		/*------------------------------------------*/
10933 		if (isdcl) {
10934 		  char *tchr;
10935 
10936 		   /* Assume DCL or foreign command exists */
10937 		  /*--------------------------------------*/
10938 		  tchr = strrchr(tmpspec, '/');
10939 		  if (tchr != NULL) {
10940 		    tchr++;
10941 		  }
10942 		  else {
10943 		    tchr = tmpspec;
10944 		  }
10945 		  strcpy(image_name, tchr);
10946 		}
10947 	      }
10948 	    }
10949 	  }
10950 	}
10951         fclose(fp);
10952       }
10953       if (check_img && isdcl) {
10954           PerlMem_free(cmd);
10955           PerlMem_free(resspec);
10956           PerlMem_free(vmsspec);
10957           return RMS$_FNF;
10958       }
10959 
10960       if (cando_by_name(S_IXUSR,0,resspec)) {
10961         vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10962 	if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10963         if (!isdcl) {
10964             strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10965 	    if (image_name[0] != 0) {
10966 		strcat(vmscmd->dsc$a_pointer, image_name);
10967 		strcat(vmscmd->dsc$a_pointer, " ");
10968 	    }
10969 	} else if (image_name[0] != 0) {
10970 	    strcpy(vmscmd->dsc$a_pointer, image_name);
10971 	    strcat(vmscmd->dsc$a_pointer, " ");
10972         } else {
10973             strcpy(vmscmd->dsc$a_pointer,"@");
10974         }
10975         if (suggest_quote) *suggest_quote = 1;
10976 
10977 	/* If there is an image name, use original command */
10978 	if (image_name[0] == 0)
10979 	    strcat(vmscmd->dsc$a_pointer,resspec);
10980 	else {
10981 	    rest = cmd;
10982 	    while (*rest && isspace(*rest)) rest++;
10983 	}
10984 
10985 	if (image_argv[0] != 0) {
10986 	  strcat(vmscmd->dsc$a_pointer,image_argv);
10987 	  strcat(vmscmd->dsc$a_pointer, " ");
10988 	}
10989         if (rest) {
10990 	   int rest_len;
10991 	   int vmscmd_len;
10992 
10993 	   rest_len = strlen(rest);
10994 	   vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10995 	   if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10996 	      strcat(vmscmd->dsc$a_pointer,rest);
10997 	   else
10998 	     retsts = CLI$_BUFOVF;
10999 	}
11000         vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
11001         PerlMem_free(cmd);
11002         PerlMem_free(vmsspec);
11003         PerlMem_free(resspec);
11004         return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
11005       }
11006       else
11007 	retsts = RMS$_PRV;
11008     }
11009   }
11010   /* It's either a DCL command or we couldn't find a suitable image */
11011   vmscmd->dsc$w_length = strlen(cmd);
11012 
11013   vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
11014   strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
11015   vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
11016 
11017   PerlMem_free(cmd);
11018   PerlMem_free(resspec);
11019   PerlMem_free(vmsspec);
11020 
11021   /* check if it's a symbol (for quoting purposes) */
11022   if (suggest_quote && !*suggest_quote) {
11023     int iss;
11024     char equiv[LNM$C_NAMLENGTH];
11025     struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11026     eqvdsc.dsc$a_pointer = equiv;
11027 
11028     iss = lib$get_symbol(vmscmd,&eqvdsc);
11029     if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
11030   }
11031   if (!(retsts & 1)) {
11032     /* just hand off status values likely to be due to user error */
11033     if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
11034         retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
11035        (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
11036     else { _ckvmssts_noperl(retsts); }
11037   }
11038 
11039   return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
11040 
11041 }  /* end of setup_cmddsc() */
11042 
11043 
11044 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
11045 bool
11046 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
11047 {
11048 bool exec_sts;
11049 char * cmd;
11050 
11051   if (sp > mark) {
11052     if (vfork_called) {           /* this follows a vfork - act Unixish */
11053       vfork_called--;
11054       if (vfork_called < 0) {
11055         Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11056         vfork_called = 0;
11057       }
11058       else return do_aexec(really,mark,sp);
11059     }
11060                                            /* no vfork - act VMSish */
11061     cmd = setup_argstr(aTHX_ really,mark,sp);
11062     exec_sts = vms_do_exec(cmd);
11063     Safefree(cmd);  /* Clean up from setup_argstr() */
11064     return exec_sts;
11065   }
11066 
11067   return FALSE;
11068 }  /* end of vms_do_aexec() */
11069 /*}}}*/
11070 
11071 /* {{{bool vms_do_exec(char *cmd) */
11072 bool
11073 Perl_vms_do_exec(pTHX_ const char *cmd)
11074 {
11075   struct dsc$descriptor_s *vmscmd;
11076 
11077   if (vfork_called) {             /* this follows a vfork - act Unixish */
11078     vfork_called--;
11079     if (vfork_called < 0) {
11080       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11081       vfork_called = 0;
11082     }
11083     else return do_exec(cmd);
11084   }
11085 
11086   {                               /* no vfork - act VMSish */
11087     unsigned long int retsts;
11088 
11089     TAINT_ENV();
11090     TAINT_PROPER("exec");
11091     if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11092       retsts = lib$do_command(vmscmd);
11093 
11094     switch (retsts) {
11095       case RMS$_FNF: case RMS$_DNF:
11096         set_errno(ENOENT); break;
11097       case RMS$_DIR:
11098         set_errno(ENOTDIR); break;
11099       case RMS$_DEV:
11100         set_errno(ENODEV); break;
11101       case RMS$_PRV:
11102         set_errno(EACCES); break;
11103       case RMS$_SYN:
11104         set_errno(EINVAL); break;
11105       case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11106         set_errno(E2BIG); break;
11107       case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11108         _ckvmssts_noperl(retsts); /* fall through */
11109       default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11110         set_errno(EVMSERR);
11111     }
11112     set_vaxc_errno(retsts);
11113     if (ckWARN(WARN_EXEC)) {
11114       Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11115              vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11116     }
11117     vms_execfree(vmscmd);
11118   }
11119 
11120   return FALSE;
11121 
11122 }  /* end of vms_do_exec() */
11123 /*}}}*/
11124 
11125 int do_spawn2(pTHX_ const char *, int);
11126 
11127 int
11128 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11129 {
11130 unsigned long int sts;
11131 char * cmd;
11132 int flags = 0;
11133 
11134   if (sp > mark) {
11135 
11136     /* We'll copy the (undocumented?) Win32 behavior and allow a
11137      * numeric first argument.  But the only value we'll support
11138      * through do_aspawn is a value of 1, which means spawn without
11139      * waiting for completion -- other values are ignored.
11140      */
11141     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11142 	++mark;
11143 	flags = SvIVx(*mark);
11144     }
11145 
11146     if (flags && flags == 1)     /* the Win32 P_NOWAIT value */
11147         flags = CLI$M_NOWAIT;
11148     else
11149         flags = 0;
11150 
11151     cmd = setup_argstr(aTHX_ really, mark, sp);
11152     sts = do_spawn2(aTHX_ cmd, flags);
11153     /* pp_sys will clean up cmd */
11154     return sts;
11155   }
11156   return SS$_ABORT;
11157 }  /* end of do_aspawn() */
11158 /*}}}*/
11159 
11160 
11161 /* {{{int do_spawn(char* cmd) */
11162 int
11163 Perl_do_spawn(pTHX_ char* cmd)
11164 {
11165     PERL_ARGS_ASSERT_DO_SPAWN;
11166 
11167     return do_spawn2(aTHX_ cmd, 0);
11168 }
11169 /*}}}*/
11170 
11171 /* {{{int do_spawn_nowait(char* cmd) */
11172 int
11173 Perl_do_spawn_nowait(pTHX_ char* cmd)
11174 {
11175     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11176 
11177     return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11178 }
11179 /*}}}*/
11180 
11181 /* {{{int do_spawn2(char *cmd) */
11182 int
11183 do_spawn2(pTHX_ const char *cmd, int flags)
11184 {
11185   unsigned long int sts, substs;
11186 
11187   /* The caller of this routine expects to Safefree(PL_Cmd) */
11188   Newx(PL_Cmd,10,char);
11189 
11190   TAINT_ENV();
11191   TAINT_PROPER("spawn");
11192   if (!cmd || !*cmd) {
11193     sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11194     if (!(sts & 1)) {
11195       switch (sts) {
11196         case RMS$_FNF:  case RMS$_DNF:
11197           set_errno(ENOENT); break;
11198         case RMS$_DIR:
11199           set_errno(ENOTDIR); break;
11200         case RMS$_DEV:
11201           set_errno(ENODEV); break;
11202         case RMS$_PRV:
11203           set_errno(EACCES); break;
11204         case RMS$_SYN:
11205           set_errno(EINVAL); break;
11206         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11207           set_errno(E2BIG); break;
11208         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11209           _ckvmssts_noperl(sts); /* fall through */
11210         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11211           set_errno(EVMSERR);
11212       }
11213       set_vaxc_errno(sts);
11214       if (ckWARN(WARN_EXEC)) {
11215         Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11216 		    Strerror(errno));
11217       }
11218     }
11219     sts = substs;
11220   }
11221   else {
11222     char mode[3];
11223     PerlIO * fp;
11224     if (flags & CLI$M_NOWAIT)
11225         strcpy(mode, "n");
11226     else
11227         strcpy(mode, "nW");
11228 
11229     fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11230     if (fp != NULL)
11231       my_pclose(fp);
11232     /* sts will be the pid in the nowait case */
11233   }
11234   return sts;
11235 }  /* end of do_spawn2() */
11236 /*}}}*/
11237 
11238 
11239 static unsigned int *sockflags, sockflagsize;
11240 
11241 /*
11242  * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11243  * routines found in some versions of the CRTL can't deal with sockets.
11244  * We don't shim the other file open routines since a socket isn't
11245  * likely to be opened by a name.
11246  */
11247 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11248 FILE *my_fdopen(int fd, const char *mode)
11249 {
11250   FILE *fp = fdopen(fd, mode);
11251 
11252   if (fp) {
11253     unsigned int fdoff = fd / sizeof(unsigned int);
11254     Stat_t sbuf; /* native stat; we don't need flex_stat */
11255     if (!sockflagsize || fdoff > sockflagsize) {
11256       if (sockflags) Renew(     sockflags,fdoff+2,unsigned int);
11257       else           Newx  (sockflags,fdoff+2,unsigned int);
11258       memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11259       sockflagsize = fdoff + 2;
11260     }
11261     if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11262       sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11263   }
11264   return fp;
11265 
11266 }
11267 /*}}}*/
11268 
11269 
11270 /*
11271  * Clear the corresponding bit when the (possibly) socket stream is closed.
11272  * There still a small hole: we miss an implicit close which might occur
11273  * via freopen().  >> Todo
11274  */
11275 /*{{{ int my_fclose(FILE *fp)*/
11276 int my_fclose(FILE *fp) {
11277   if (fp) {
11278     unsigned int fd = fileno(fp);
11279     unsigned int fdoff = fd / sizeof(unsigned int);
11280 
11281     if (sockflagsize && fdoff < sockflagsize)
11282       sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11283   }
11284   return fclose(fp);
11285 }
11286 /*}}}*/
11287 
11288 
11289 /*
11290  * A simple fwrite replacement which outputs itmsz*nitm chars without
11291  * introducing record boundaries every itmsz chars.
11292  * We are using fputs, which depends on a terminating null.  We may
11293  * well be writing binary data, so we need to accommodate not only
11294  * data with nulls sprinkled in the middle but also data with no null
11295  * byte at the end.
11296  */
11297 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11298 int
11299 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11300 {
11301   register char *cp, *end, *cpd;
11302   char *data;
11303   register unsigned int fd = fileno(dest);
11304   register unsigned int fdoff = fd / sizeof(unsigned int);
11305   int retval;
11306   int bufsize = itmsz * nitm + 1;
11307 
11308   if (fdoff < sockflagsize &&
11309       (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11310     if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11311     return nitm;
11312   }
11313 
11314   _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11315   memcpy( data, src, itmsz*nitm );
11316   data[itmsz*nitm] = '\0';
11317 
11318   end = data + itmsz * nitm;
11319   retval = (int) nitm; /* on success return # items written */
11320 
11321   cpd = data;
11322   while (cpd <= end) {
11323     for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11324     if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11325     if (cp < end)
11326       if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11327     cpd = cp + 1;
11328   }
11329 
11330   if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11331   return retval;
11332 
11333 }  /* end of my_fwrite() */
11334 /*}}}*/
11335 
11336 /*{{{ int my_flush(FILE *fp)*/
11337 int
11338 Perl_my_flush(pTHX_ FILE *fp)
11339 {
11340     int res;
11341     if ((res = fflush(fp)) == 0 && fp) {
11342 #ifdef VMS_DO_SOCKETS
11343 	Stat_t s;
11344 	if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11345 #endif
11346 	    res = fsync(fileno(fp));
11347     }
11348 /*
11349  * If the flush succeeded but set end-of-file, we need to clear
11350  * the error because our caller may check ferror().  BTW, this
11351  * probably means we just flushed an empty file.
11352  */
11353     if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11354 
11355     return res;
11356 }
11357 /*}}}*/
11358 
11359 /* fgetname() is not returning the correct file specifications when
11360  * decc_filename_unix_report mode is active.  So we have to have it
11361  * aways return filenames in VMS mode and convert it ourselves.
11362  */
11363 
11364 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11365 char *
11366 Perl_my_fgetname(FILE *fp, char * buf) {
11367     char * retname;
11368     char * vms_name;
11369 
11370     retname = fgetname(fp, buf, 1);
11371 
11372     /* If we are in VMS mode, then we are done */
11373     if (!decc_filename_unix_report || (retname == NULL)) {
11374        return retname;
11375     }
11376 
11377     /* Convert this to Unix format */
11378     vms_name = PerlMem_malloc(VMS_MAXRSS + 1);
11379     strcpy(vms_name, retname);
11380     retname = int_tounixspec(vms_name, buf, NULL);
11381     PerlMem_free(vms_name);
11382 
11383     return retname;
11384 }
11385 /*}}}*/
11386 
11387 /*
11388  * Here are replacements for the following Unix routines in the VMS environment:
11389  *      getpwuid    Get information for a particular UIC or UID
11390  *      getpwnam    Get information for a named user
11391  *      getpwent    Get information for each user in the rights database
11392  *      setpwent    Reset search to the start of the rights database
11393  *      endpwent    Finish searching for users in the rights database
11394  *
11395  * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11396  * (defined in pwd.h), which contains the following fields:-
11397  *      struct passwd {
11398  *              char        *pw_name;    Username (in lower case)
11399  *              char        *pw_passwd;  Hashed password
11400  *              unsigned int pw_uid;     UIC
11401  *              unsigned int pw_gid;     UIC group  number
11402  *              char        *pw_unixdir; Default device/directory (VMS-style)
11403  *              char        *pw_gecos;   Owner name
11404  *              char        *pw_dir;     Default device/directory (Unix-style)
11405  *              char        *pw_shell;   Default CLI name (eg. DCL)
11406  *      };
11407  * If the specified user does not exist, getpwuid and getpwnam return NULL.
11408  *
11409  * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11410  * not the UIC member number (eg. what's returned by getuid()),
11411  * getpwuid() can accept either as input (if uid is specified, the caller's
11412  * UIC group is used), though it won't recognise gid=0.
11413  *
11414  * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11415  * information about other users in your group or in other groups, respectively.
11416  * If the required privilege is not available, then these routines fill only
11417  * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11418  * string).
11419  *
11420  * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11421  */
11422 
11423 /* sizes of various UAF record fields */
11424 #define UAI$S_USERNAME 12
11425 #define UAI$S_IDENT    31
11426 #define UAI$S_OWNER    31
11427 #define UAI$S_DEFDEV   31
11428 #define UAI$S_DEFDIR   63
11429 #define UAI$S_DEFCLI   31
11430 #define UAI$S_PWD       8
11431 
11432 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT  && \
11433                         (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11434                         (uic).uic$v_group  != UIC$K_WILD_GROUP)
11435 
11436 static char __empty[]= "";
11437 static struct passwd __passwd_empty=
11438     {(char *) __empty, (char *) __empty, 0, 0,
11439      (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11440 static int contxt= 0;
11441 static struct passwd __pwdcache;
11442 static char __pw_namecache[UAI$S_IDENT+1];
11443 
11444 /*
11445  * This routine does most of the work extracting the user information.
11446  */
11447 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11448 {
11449     static struct {
11450         unsigned char length;
11451         char pw_gecos[UAI$S_OWNER+1];
11452     } owner;
11453     static union uicdef uic;
11454     static struct {
11455         unsigned char length;
11456         char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11457     } defdev;
11458     static struct {
11459         unsigned char length;
11460         char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11461     } defdir;
11462     static struct {
11463         unsigned char length;
11464         char pw_shell[UAI$S_DEFCLI+1];
11465     } defcli;
11466     static char pw_passwd[UAI$S_PWD+1];
11467 
11468     static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11469     struct dsc$descriptor_s name_desc;
11470     unsigned long int sts;
11471 
11472     static struct itmlst_3 itmlst[]= {
11473         {UAI$S_OWNER+1,    UAI$_OWNER,  &owner,    &lowner},
11474         {sizeof(uic),      UAI$_UIC,    &uic,      &luic},
11475         {UAI$S_DEFDEV+1,   UAI$_DEFDEV, &defdev,   &ldefdev},
11476         {UAI$S_DEFDIR+1,   UAI$_DEFDIR, &defdir,   &ldefdir},
11477         {UAI$S_DEFCLI+1,   UAI$_DEFCLI, &defcli,   &ldefcli},
11478         {UAI$S_PWD,        UAI$_PWD,    pw_passwd, &lpwd},
11479         {0,                0,           NULL,    NULL}};
11480 
11481     name_desc.dsc$w_length=  strlen(name);
11482     name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11483     name_desc.dsc$b_class=   DSC$K_CLASS_S;
11484     name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11485 
11486 /*  Note that sys$getuai returns many fields as counted strings. */
11487     sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11488     if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11489       set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11490     }
11491     else { _ckvmssts(sts); }
11492     if (!(sts & 1)) return 0;  /* out here in case _ckvmssts() doesn't abort */
11493 
11494     if ((int) owner.length  < lowner)  lowner=  (int) owner.length;
11495     if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11496     if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11497     if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11498     memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11499     owner.pw_gecos[lowner]=            '\0';
11500     defdev.pw_dir[ldefdev+ldefdir]= '\0';
11501     defcli.pw_shell[ldefcli]=          '\0';
11502     if (valid_uic(uic)) {
11503         pwd->pw_uid= uic.uic$l_uic;
11504         pwd->pw_gid= uic.uic$v_group;
11505     }
11506     else
11507       Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11508     pwd->pw_passwd=  pw_passwd;
11509     pwd->pw_gecos=   owner.pw_gecos;
11510     pwd->pw_dir=     defdev.pw_dir;
11511     pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11512     pwd->pw_shell=   defcli.pw_shell;
11513     if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11514         int ldir;
11515         ldir= strlen(pwd->pw_unixdir) - 1;
11516         if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11517     }
11518     else
11519         strcpy(pwd->pw_unixdir, pwd->pw_dir);
11520     if (!decc_efs_case_preserve)
11521         __mystrtolower(pwd->pw_unixdir);
11522     return 1;
11523 }
11524 
11525 /*
11526  * Get information for a named user.
11527 */
11528 /*{{{struct passwd *getpwnam(char *name)*/
11529 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11530 {
11531     struct dsc$descriptor_s name_desc;
11532     union uicdef uic;
11533     unsigned long int status, sts;
11534 
11535     __pwdcache = __passwd_empty;
11536     if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11537       /* We still may be able to determine pw_uid and pw_gid */
11538       name_desc.dsc$w_length=  strlen(name);
11539       name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11540       name_desc.dsc$b_class=   DSC$K_CLASS_S;
11541       name_desc.dsc$a_pointer= (char *) name;
11542       if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11543         __pwdcache.pw_uid= uic.uic$l_uic;
11544         __pwdcache.pw_gid= uic.uic$v_group;
11545       }
11546       else {
11547         if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11548           set_vaxc_errno(sts);
11549           set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11550           return NULL;
11551         }
11552         else { _ckvmssts(sts); }
11553       }
11554     }
11555     strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11556     __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11557     __pwdcache.pw_name= __pw_namecache;
11558     return &__pwdcache;
11559 }  /* end of my_getpwnam() */
11560 /*}}}*/
11561 
11562 /*
11563  * Get information for a particular UIC or UID.
11564  * Called by my_getpwent with uid=-1 to list all users.
11565 */
11566 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11567 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11568 {
11569     const $DESCRIPTOR(name_desc,__pw_namecache);
11570     unsigned short lname;
11571     union uicdef uic;
11572     unsigned long int status;
11573 
11574     if (uid == (unsigned int) -1) {
11575       do {
11576         status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11577         if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11578           set_vaxc_errno(status);
11579           set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11580           my_endpwent();
11581           return NULL;
11582         }
11583         else { _ckvmssts(status); }
11584       } while (!valid_uic (uic));
11585     }
11586     else {
11587       uic.uic$l_uic= uid;
11588       if (!uic.uic$v_group)
11589         uic.uic$v_group= PerlProc_getgid();
11590       if (valid_uic(uic))
11591         status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11592       else status = SS$_IVIDENT;
11593       if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11594           status == RMS$_PRV) {
11595         set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11596         return NULL;
11597       }
11598       else { _ckvmssts(status); }
11599     }
11600     __pw_namecache[lname]= '\0';
11601     __mystrtolower(__pw_namecache);
11602 
11603     __pwdcache = __passwd_empty;
11604     __pwdcache.pw_name = __pw_namecache;
11605 
11606 /*  Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11607     The identifier's value is usually the UIC, but it doesn't have to be,
11608     so if we can, we let fillpasswd update this. */
11609     __pwdcache.pw_uid =  uic.uic$l_uic;
11610     __pwdcache.pw_gid =  uic.uic$v_group;
11611 
11612     fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11613     return &__pwdcache;
11614 
11615 }  /* end of my_getpwuid() */
11616 /*}}}*/
11617 
11618 /*
11619  * Get information for next user.
11620 */
11621 /*{{{struct passwd *my_getpwent()*/
11622 struct passwd *Perl_my_getpwent(pTHX)
11623 {
11624     return (my_getpwuid((unsigned int) -1));
11625 }
11626 /*}}}*/
11627 
11628 /*
11629  * Finish searching rights database for users.
11630 */
11631 /*{{{void my_endpwent()*/
11632 void Perl_my_endpwent(pTHX)
11633 {
11634     if (contxt) {
11635       _ckvmssts(sys$finish_rdb(&contxt));
11636       contxt= 0;
11637     }
11638 }
11639 /*}}}*/
11640 
11641 #ifdef HOMEGROWN_POSIX_SIGNALS
11642   /* Signal handling routines, pulled into the core from POSIX.xs.
11643    *
11644    * We need these for threads, so they've been rolled into the core,
11645    * rather than left in POSIX.xs.
11646    *
11647    * (DRS, Oct 23, 1997)
11648    */
11649 
11650   /* sigset_t is atomic under VMS, so these routines are easy */
11651 /*{{{int my_sigemptyset(sigset_t *) */
11652 int my_sigemptyset(sigset_t *set) {
11653     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11654     *set = 0; return 0;
11655 }
11656 /*}}}*/
11657 
11658 
11659 /*{{{int my_sigfillset(sigset_t *)*/
11660 int my_sigfillset(sigset_t *set) {
11661     int i;
11662     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11663     for (i = 0; i < NSIG; i++) *set |= (1 << i);
11664     return 0;
11665 }
11666 /*}}}*/
11667 
11668 
11669 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11670 int my_sigaddset(sigset_t *set, int sig) {
11671     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11672     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11673     *set |= (1 << (sig - 1));
11674     return 0;
11675 }
11676 /*}}}*/
11677 
11678 
11679 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11680 int my_sigdelset(sigset_t *set, int sig) {
11681     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11682     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11683     *set &= ~(1 << (sig - 1));
11684     return 0;
11685 }
11686 /*}}}*/
11687 
11688 
11689 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11690 int my_sigismember(sigset_t *set, int sig) {
11691     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11692     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11693     return *set & (1 << (sig - 1));
11694 }
11695 /*}}}*/
11696 
11697 
11698 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11699 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11700     sigset_t tempmask;
11701 
11702     /* If set and oset are both null, then things are badly wrong. Bail out. */
11703     if ((oset == NULL) && (set == NULL)) {
11704       set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11705       return -1;
11706     }
11707 
11708     /* If set's null, then we're just handling a fetch. */
11709     if (set == NULL) {
11710         tempmask = sigblock(0);
11711     }
11712     else {
11713       switch (how) {
11714       case SIG_SETMASK:
11715         tempmask = sigsetmask(*set);
11716         break;
11717       case SIG_BLOCK:
11718         tempmask = sigblock(*set);
11719         break;
11720       case SIG_UNBLOCK:
11721         tempmask = sigblock(0);
11722         sigsetmask(*oset & ~tempmask);
11723         break;
11724       default:
11725         set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11726         return -1;
11727       }
11728     }
11729 
11730     /* Did they pass us an oset? If so, stick our holding mask into it */
11731     if (oset)
11732       *oset = tempmask;
11733 
11734     return 0;
11735 }
11736 /*}}}*/
11737 #endif  /* HOMEGROWN_POSIX_SIGNALS */
11738 
11739 
11740 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11741  * my_utime(), and flex_stat(), all of which operate on UTC unless
11742  * VMSISH_TIMES is true.
11743  */
11744 /* method used to handle UTC conversions:
11745  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
11746  */
11747 static int gmtime_emulation_type;
11748 /* number of secs to add to UTC POSIX-style time to get local time */
11749 static long int utc_offset_secs;
11750 
11751 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11752  * in vmsish.h.  #undef them here so we can call the CRTL routines
11753  * directly.
11754  */
11755 #undef gmtime
11756 #undef localtime
11757 #undef time
11758 
11759 
11760 /*
11761  * DEC C previous to 6.0 corrupts the behavior of the /prefix
11762  * qualifier with the extern prefix pragma.  This provisional
11763  * hack circumvents this prefix pragma problem in previous
11764  * precompilers.
11765  */
11766 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11767 #  if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11768 #    pragma __extern_prefix save
11769 #    pragma __extern_prefix ""  /* set to empty to prevent prefixing */
11770 #    define gmtime decc$__utctz_gmtime
11771 #    define localtime decc$__utctz_localtime
11772 #    define time decc$__utc_time
11773 #    pragma __extern_prefix restore
11774 
11775      struct tm *gmtime(), *localtime();
11776 
11777 #  endif
11778 #endif
11779 
11780 
11781 static time_t toutc_dst(time_t loc) {
11782   struct tm *rsltmp;
11783 
11784   if ((rsltmp = localtime(&loc)) == NULL) return -1;
11785   loc -= utc_offset_secs;
11786   if (rsltmp->tm_isdst) loc -= 3600;
11787   return loc;
11788 }
11789 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11790        ((gmtime_emulation_type || my_time(NULL)), \
11791        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11792        ((secs) - utc_offset_secs))))
11793 
11794 static time_t toloc_dst(time_t utc) {
11795   struct tm *rsltmp;
11796 
11797   utc += utc_offset_secs;
11798   if ((rsltmp = localtime(&utc)) == NULL) return -1;
11799   if (rsltmp->tm_isdst) utc += 3600;
11800   return utc;
11801 }
11802 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11803        ((gmtime_emulation_type || my_time(NULL)), \
11804        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11805        ((secs) + utc_offset_secs))))
11806 
11807 #ifndef RTL_USES_UTC
11808 /*
11809 
11810     ucx$tz = "EST5EDT4,M4.1.0,M10.5.0"  typical
11811         DST starts on 1st sun of april      at 02:00  std time
11812             ends on last sun of october     at 02:00  dst time
11813     see the UCX management command reference, SET CONFIG TIMEZONE
11814     for formatting info.
11815 
11816     No, it's not as general as it should be, but then again, NOTHING
11817     will handle UK times in a sensible way.
11818 */
11819 
11820 
11821 /*
11822     parse the DST start/end info:
11823     (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11824 */
11825 
11826 static char *
11827 tz_parse_startend(char *s, struct tm *w, int *past)
11828 {
11829     int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11830     int ly, dozjd, d, m, n, hour, min, sec, j, k;
11831     time_t g;
11832 
11833     if (!s)    return 0;
11834     if (!w) return 0;
11835     if (!past) return 0;
11836 
11837     ly = 0;
11838     if (w->tm_year % 4        == 0) ly = 1;
11839     if (w->tm_year % 100      == 0) ly = 0;
11840     if (w->tm_year+1900 % 400 == 0) ly = 1;
11841     if (ly) dinm[1]++;
11842 
11843     dozjd = isdigit(*s);
11844     if (*s == 'J' || *s == 'j' || dozjd) {
11845         if (!dozjd && !isdigit(*++s)) return 0;
11846         d = *s++ - '0';
11847         if (isdigit(*s)) {
11848             d = d*10 + *s++ - '0';
11849             if (isdigit(*s)) {
11850                 d = d*10 + *s++ - '0';
11851             }
11852         }
11853         if (d == 0) return 0;
11854         if (d > 366) return 0;
11855         d--;
11856         if (!dozjd && d > 58 && ly) d++;  /* after 28 feb */
11857         g = d * 86400;
11858         dozjd = 1;
11859     } else if (*s == 'M' || *s == 'm') {
11860         if (!isdigit(*++s)) return 0;
11861         m = *s++ - '0';
11862         if (isdigit(*s)) m = 10*m + *s++ - '0';
11863         if (*s != '.') return 0;
11864         if (!isdigit(*++s)) return 0;
11865         n = *s++ - '0';
11866         if (n < 1 || n > 5) return 0;
11867         if (*s != '.') return 0;
11868         if (!isdigit(*++s)) return 0;
11869         d = *s++ - '0';
11870         if (d > 6) return 0;
11871     }
11872 
11873     if (*s == '/') {
11874         if (!isdigit(*++s)) return 0;
11875         hour = *s++ - '0';
11876         if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11877         if (*s == ':') {
11878             if (!isdigit(*++s)) return 0;
11879             min = *s++ - '0';
11880             if (isdigit(*s)) min = 10*min + *s++ - '0';
11881             if (*s == ':') {
11882                 if (!isdigit(*++s)) return 0;
11883                 sec = *s++ - '0';
11884                 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11885             }
11886         }
11887     } else {
11888         hour = 2;
11889         min = 0;
11890         sec = 0;
11891     }
11892 
11893     if (dozjd) {
11894         if (w->tm_yday < d) goto before;
11895         if (w->tm_yday > d) goto after;
11896     } else {
11897         if (w->tm_mon+1 < m) goto before;
11898         if (w->tm_mon+1 > m) goto after;
11899 
11900         j = (42 + w->tm_wday - w->tm_mday)%7;   /*dow of mday 0 */
11901         k = d - j; /* mday of first d */
11902         if (k <= 0) k += 7;
11903         k += 7 * ((n>4?4:n)-1);  /* mday of n'th d */
11904         if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11905         if (w->tm_mday < k) goto before;
11906         if (w->tm_mday > k) goto after;
11907     }
11908 
11909     if (w->tm_hour < hour) goto before;
11910     if (w->tm_hour > hour) goto after;
11911     if (w->tm_min  < min)  goto before;
11912     if (w->tm_min  > min)  goto after;
11913     if (w->tm_sec  < sec)  goto before;
11914     goto after;
11915 
11916 before:
11917     *past = 0;
11918     return s;
11919 after:
11920     *past = 1;
11921     return s;
11922 }
11923 
11924 
11925 
11926 
11927 /*  parse the offset:   (+|-)hh[:mm[:ss]]  */
11928 
11929 static char *
11930 tz_parse_offset(char *s, int *offset)
11931 {
11932     int hour = 0, min = 0, sec = 0;
11933     int neg = 0;
11934     if (!s) return 0;
11935     if (!offset) return 0;
11936 
11937     if (*s == '-') {neg++; s++;}
11938     if (*s == '+') s++;
11939     if (!isdigit(*s)) return 0;
11940     hour = *s++ - '0';
11941     if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11942     if (hour > 24) return 0;
11943     if (*s == ':') {
11944         if (!isdigit(*++s)) return 0;
11945         min = *s++ - '0';
11946         if (isdigit(*s)) min = min*10 + (*s++ - '0');
11947         if (min > 59) return 0;
11948         if (*s == ':') {
11949             if (!isdigit(*++s)) return 0;
11950             sec = *s++ - '0';
11951             if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11952             if (sec > 59) return 0;
11953         }
11954     }
11955 
11956     *offset = (hour*60+min)*60 + sec;
11957     if (neg) *offset = -*offset;
11958     return s;
11959 }
11960 
11961 /*
11962     input time is w, whatever type of time the CRTL localtime() uses.
11963     sets dst, the zone, and the gmtoff (seconds)
11964 
11965     caches the value of TZ and UCX$TZ env variables; note that
11966     my_setenv looks for these and sets a flag if they're changed
11967     for efficiency.
11968 
11969     We have to watch out for the "australian" case (dst starts in
11970     october, ends in april)...flagged by "reverse" and checked by
11971     scanning through the months of the previous year.
11972 
11973 */
11974 
11975 static int
11976 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11977 {
11978     time_t when;
11979     struct tm *w2;
11980     char *s,*s2;
11981     char *dstzone, *tz, *s_start, *s_end;
11982     int std_off, dst_off, isdst;
11983     int y, dststart, dstend;
11984     static char envtz[1025];  /* longer than any logical, symbol, ... */
11985     static char ucxtz[1025];
11986     static char reversed = 0;
11987 
11988     if (!w) return 0;
11989 
11990     if (tz_updated) {
11991         tz_updated = 0;
11992         reversed = -1;  /* flag need to check  */
11993         envtz[0] = ucxtz[0] = '\0';
11994         tz = my_getenv("TZ",0);
11995         if (tz) strcpy(envtz, tz);
11996         tz = my_getenv("UCX$TZ",0);
11997         if (tz) strcpy(ucxtz, tz);
11998         if (!envtz[0] && !ucxtz[0]) return 0;  /* we give up */
11999     }
12000     tz = envtz;
12001     if (!*tz) tz = ucxtz;
12002 
12003     s = tz;
12004     while (isalpha(*s)) s++;
12005     s = tz_parse_offset(s, &std_off);
12006     if (!s) return 0;
12007     if (!*s) {                  /* no DST, hurray we're done! */
12008         isdst = 0;
12009         goto done;
12010     }
12011 
12012     dstzone = s;
12013     while (isalpha(*s)) s++;
12014     s2 = tz_parse_offset(s, &dst_off);
12015     if (s2) {
12016         s = s2;
12017     } else {
12018         dst_off = std_off - 3600;
12019     }
12020 
12021     if (!*s) {      /* default dst start/end?? */
12022         if (tz != ucxtz) {          /* if TZ tells zone only, UCX$TZ tells rule */
12023             s = strchr(ucxtz,',');
12024         }
12025         if (!s || !*s) s = ",M4.1.0,M10.5.0";   /* we know we do dst, default rule */
12026     }
12027     if (*s != ',') return 0;
12028 
12029     when = *w;
12030     when = _toutc(when);      /* convert to utc */
12031     when = when - std_off;    /* convert to pseudolocal time*/
12032 
12033     w2 = localtime(&when);
12034     y = w2->tm_year;
12035     s_start = s+1;
12036     s = tz_parse_startend(s_start,w2,&dststart);
12037     if (!s) return 0;
12038     if (*s != ',') return 0;
12039 
12040     when = *w;
12041     when = _toutc(when);      /* convert to utc */
12042     when = when - dst_off;    /* convert to pseudolocal time*/
12043     w2 = localtime(&when);
12044     if (w2->tm_year != y) {   /* spans a year, just check one time */
12045         when += dst_off - std_off;
12046         w2 = localtime(&when);
12047     }
12048     s_end = s+1;
12049     s = tz_parse_startend(s_end,w2,&dstend);
12050     if (!s) return 0;
12051 
12052     if (reversed == -1) {  /* need to check if start later than end */
12053         int j, ds, de;
12054 
12055         when = *w;
12056         if (when < 2*365*86400) {
12057             when += 2*365*86400;
12058         } else {
12059             when -= 365*86400;
12060         }
12061         w2 =localtime(&when);
12062         when = when + (15 - w2->tm_yday) * 86400;   /* jan 15 */
12063 
12064         for (j = 0; j < 12; j++) {
12065             w2 =localtime(&when);
12066             tz_parse_startend(s_start,w2,&ds);
12067             tz_parse_startend(s_end,w2,&de);
12068             if (ds != de) break;
12069             when += 30*86400;
12070         }
12071         reversed = 0;
12072         if (de && !ds) reversed = 1;
12073     }
12074 
12075     isdst = dststart && !dstend;
12076     if (reversed) isdst = dststart  || !dstend;
12077 
12078 done:
12079     if (dst)    *dst = isdst;
12080     if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
12081     if (isdst)  tz = dstzone;
12082     if (zone) {
12083         while(isalpha(*tz))  *zone++ = *tz++;
12084         *zone = '\0';
12085     }
12086     return 1;
12087 }
12088 
12089 #endif /* !RTL_USES_UTC */
12090 
12091 /* my_time(), my_localtime(), my_gmtime()
12092  * By default traffic in UTC time values, using CRTL gmtime() or
12093  * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
12094  * Note: We need to use these functions even when the CRTL has working
12095  * UTC support, since they also handle C<use vmsish qw(times);>
12096  *
12097  * Contributed by Chuck Lane  <lane@duphy4.physics.drexel.edu>
12098  * Modified by Charles Bailey <bailey@newman.upenn.edu>
12099  */
12100 
12101 /*{{{time_t my_time(time_t *timep)*/
12102 time_t Perl_my_time(pTHX_ time_t *timep)
12103 {
12104   time_t when;
12105   struct tm *tm_p;
12106 
12107   if (gmtime_emulation_type == 0) {
12108     int dstnow;
12109     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
12110                               /* results of calls to gmtime() and localtime() */
12111                               /* for same &base */
12112 
12113     gmtime_emulation_type++;
12114     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
12115       char off[LNM$C_NAMLENGTH+1];;
12116 
12117       gmtime_emulation_type++;
12118       if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
12119         gmtime_emulation_type++;
12120         utc_offset_secs = 0;
12121         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
12122       }
12123       else { utc_offset_secs = atol(off); }
12124     }
12125     else { /* We've got a working gmtime() */
12126       struct tm gmt, local;
12127 
12128       gmt = *tm_p;
12129       tm_p = localtime(&base);
12130       local = *tm_p;
12131       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
12132       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12133       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
12134       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
12135     }
12136   }
12137 
12138   when = time(NULL);
12139 # ifdef VMSISH_TIME
12140 # ifdef RTL_USES_UTC
12141   if (VMSISH_TIME) when = _toloc(when);
12142 # else
12143   if (!VMSISH_TIME) when = _toutc(when);
12144 # endif
12145 # endif
12146   if (timep != NULL) *timep = when;
12147   return when;
12148 
12149 }  /* end of my_time() */
12150 /*}}}*/
12151 
12152 
12153 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12154 struct tm *
12155 Perl_my_gmtime(pTHX_ const time_t *timep)
12156 {
12157   char *p;
12158   time_t when;
12159   struct tm *rsltmp;
12160 
12161   if (timep == NULL) {
12162     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12163     return NULL;
12164   }
12165   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
12166 
12167   when = *timep;
12168 # ifdef VMSISH_TIME
12169   if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12170 #  endif
12171 # ifdef RTL_USES_UTC  /* this implies that the CRTL has a working gmtime() */
12172   return gmtime(&when);
12173 # else
12174   /* CRTL localtime() wants local time as input, so does no tz correction */
12175   rsltmp = localtime(&when);
12176   if (rsltmp) rsltmp->tm_isdst = 0;  /* We already took DST into account */
12177   return rsltmp;
12178 #endif
12179 }  /* end of my_gmtime() */
12180 /*}}}*/
12181 
12182 
12183 /*{{{struct tm *my_localtime(const time_t *timep)*/
12184 struct tm *
12185 Perl_my_localtime(pTHX_ const time_t *timep)
12186 {
12187   time_t when, whenutc;
12188   struct tm *rsltmp;
12189   int dst, offset;
12190 
12191   if (timep == NULL) {
12192     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12193     return NULL;
12194   }
12195   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
12196   if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12197 
12198   when = *timep;
12199 # ifdef RTL_USES_UTC
12200 # ifdef VMSISH_TIME
12201   if (VMSISH_TIME) when = _toutc(when);
12202 # endif
12203   /* CRTL localtime() wants UTC as input, does tz correction itself */
12204   return localtime(&when);
12205 
12206 # else /* !RTL_USES_UTC */
12207   whenutc = when;
12208 # ifdef VMSISH_TIME
12209   if (!VMSISH_TIME) when = _toloc(whenutc);  /*  input was UTC */
12210   if (VMSISH_TIME) whenutc = _toutc(when);   /*  input was truelocal */
12211 # endif
12212   dst = -1;
12213 #ifndef RTL_USES_UTC
12214   if (tz_parse(aTHX_ &when, &dst, 0, &offset)) {   /* truelocal determines DST*/
12215       when = whenutc - offset;                   /* pseudolocal time*/
12216   }
12217 # endif
12218   /* CRTL localtime() wants local time as input, so does no tz correction */
12219   rsltmp = localtime(&when);
12220   if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12221   return rsltmp;
12222 # endif
12223 
12224 } /*  end of my_localtime() */
12225 /*}}}*/
12226 
12227 /* Reset definitions for later calls */
12228 #define gmtime(t)    my_gmtime(t)
12229 #define localtime(t) my_localtime(t)
12230 #define time(t)      my_time(t)
12231 
12232 
12233 /* my_utime - update modification/access time of a file
12234  *
12235  * VMS 7.3 and later implementation
12236  * Only the UTC translation is home-grown. The rest is handled by the
12237  * CRTL utime(), which will take into account the relevant feature
12238  * logicals and ODS-5 volume characteristics for true access times.
12239  *
12240  * pre VMS 7.3 implementation:
12241  * The calling sequence is identical to POSIX utime(), but under
12242  * VMS with ODS-2, only the modification time is changed; ODS-2 does
12243  * not maintain access times.  Restrictions differ from the POSIX
12244  * definition in that the time can be changed as long as the
12245  * caller has permission to execute the necessary IO$_MODIFY $QIO;
12246  * no separate checks are made to insure that the caller is the
12247  * owner of the file or has special privs enabled.
12248  * Code here is based on Joe Meadows' FILE utility.
12249  *
12250  */
12251 
12252 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12253  *              to VMS epoch  (01-JAN-1858 00:00:00.00)
12254  * in 100 ns intervals.
12255  */
12256 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12257 
12258 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12259 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12260 {
12261 #if __CRTL_VER >= 70300000
12262   struct utimbuf utc_utimes, *utc_utimesp;
12263 
12264   if (utimes != NULL) {
12265     utc_utimes.actime = utimes->actime;
12266     utc_utimes.modtime = utimes->modtime;
12267 # ifdef VMSISH_TIME
12268     /* If input was local; convert to UTC for sys svc */
12269     if (VMSISH_TIME) {
12270       utc_utimes.actime = _toutc(utimes->actime);
12271       utc_utimes.modtime = _toutc(utimes->modtime);
12272     }
12273 # endif
12274     utc_utimesp = &utc_utimes;
12275   }
12276   else {
12277     utc_utimesp = NULL;
12278   }
12279 
12280   return utime(file, utc_utimesp);
12281 
12282 #else /* __CRTL_VER < 70300000 */
12283 
12284   register int i;
12285   int sts;
12286   long int bintime[2], len = 2, lowbit, unixtime,
12287            secscale = 10000000; /* seconds --> 100 ns intervals */
12288   unsigned long int chan, iosb[2], retsts;
12289   char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12290   struct FAB myfab = cc$rms_fab;
12291   struct NAM mynam = cc$rms_nam;
12292 #if defined (__DECC) && defined (__VAX)
12293   /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12294    * at least through VMS V6.1, which causes a type-conversion warning.
12295    */
12296 #  pragma message save
12297 #  pragma message disable cvtdiftypes
12298 #endif
12299   struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12300   struct fibdef myfib;
12301 #if defined (__DECC) && defined (__VAX)
12302   /* This should be right after the declaration of myatr, but due
12303    * to a bug in VAX DEC C, this takes effect a statement early.
12304    */
12305 #  pragma message restore
12306 #endif
12307   /* cast ok for read only parameter */
12308   struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12309                         devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12310                         fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12311 
12312   if (file == NULL || *file == '\0') {
12313     SETERRNO(ENOENT, LIB$_INVARG);
12314     return -1;
12315   }
12316 
12317   /* Convert to VMS format ensuring that it will fit in 255 characters */
12318   if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12319       SETERRNO(ENOENT, LIB$_INVARG);
12320       return -1;
12321   }
12322   if (utimes != NULL) {
12323     /* Convert Unix time    (seconds since 01-JAN-1970 00:00:00.00)
12324      * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12325      * Since time_t is unsigned long int, and lib$emul takes a signed long int
12326      * as input, we force the sign bit to be clear by shifting unixtime right
12327      * one bit, then multiplying by an extra factor of 2 in lib$emul().
12328      */
12329     lowbit = (utimes->modtime & 1) ? secscale : 0;
12330     unixtime = (long int) utimes->modtime;
12331 #   ifdef VMSISH_TIME
12332     /* If input was UTC; convert to local for sys svc */
12333     if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12334 #   endif
12335     unixtime >>= 1;  secscale <<= 1;
12336     retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12337     if (!(retsts & 1)) {
12338       SETERRNO(EVMSERR, retsts);
12339       return -1;
12340     }
12341     retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12342     if (!(retsts & 1)) {
12343       SETERRNO(EVMSERR, retsts);
12344       return -1;
12345     }
12346   }
12347   else {
12348     /* Just get the current time in VMS format directly */
12349     retsts = sys$gettim(bintime);
12350     if (!(retsts & 1)) {
12351       SETERRNO(EVMSERR, retsts);
12352       return -1;
12353     }
12354   }
12355 
12356   myfab.fab$l_fna = vmsspec;
12357   myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12358   myfab.fab$l_nam = &mynam;
12359   mynam.nam$l_esa = esa;
12360   mynam.nam$b_ess = (unsigned char) sizeof esa;
12361   mynam.nam$l_rsa = rsa;
12362   mynam.nam$b_rss = (unsigned char) sizeof rsa;
12363   if (decc_efs_case_preserve)
12364       mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12365 
12366   /* Look for the file to be affected, letting RMS parse the file
12367    * specification for us as well.  I have set errno using only
12368    * values documented in the utime() man page for VMS POSIX.
12369    */
12370   retsts = sys$parse(&myfab,0,0);
12371   if (!(retsts & 1)) {
12372     set_vaxc_errno(retsts);
12373     if      (retsts == RMS$_PRV) set_errno(EACCES);
12374     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12375     else                         set_errno(EVMSERR);
12376     return -1;
12377   }
12378   retsts = sys$search(&myfab,0,0);
12379   if (!(retsts & 1)) {
12380     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12381     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12382     set_vaxc_errno(retsts);
12383     if      (retsts == RMS$_PRV) set_errno(EACCES);
12384     else if (retsts == RMS$_FNF) set_errno(ENOENT);
12385     else                         set_errno(EVMSERR);
12386     return -1;
12387   }
12388 
12389   devdsc.dsc$w_length = mynam.nam$b_dev;
12390   /* cast ok for read only parameter */
12391   devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12392 
12393   retsts = sys$assign(&devdsc,&chan,0,0);
12394   if (!(retsts & 1)) {
12395     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12396     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12397     set_vaxc_errno(retsts);
12398     if      (retsts == SS$_IVDEVNAM)   set_errno(ENOTDIR);
12399     else if (retsts == SS$_NOPRIV)     set_errno(EACCES);
12400     else if (retsts == SS$_NOSUCHDEV)  set_errno(ENOTDIR);
12401     else                               set_errno(EVMSERR);
12402     return -1;
12403   }
12404 
12405   fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12406   fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12407 
12408   memset((void *) &myfib, 0, sizeof myfib);
12409 #if defined(__DECC) || defined(__DECCXX)
12410   for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12411   for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12412   /* This prevents the revision time of the file being reset to the current
12413    * time as a result of our IO$_MODIFY $QIO. */
12414   myfib.fib$l_acctl = FIB$M_NORECORD;
12415 #else
12416   for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12417   for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12418   myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12419 #endif
12420   retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12421   mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12422   myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12423   _ckvmssts(sys$dassgn(chan));
12424   if (retsts & 1) retsts = iosb[0];
12425   if (!(retsts & 1)) {
12426     set_vaxc_errno(retsts);
12427     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12428     else                      set_errno(EVMSERR);
12429     return -1;
12430   }
12431 
12432   return 0;
12433 
12434 #endif /* #if __CRTL_VER >= 70300000 */
12435 
12436 }  /* end of my_utime() */
12437 /*}}}*/
12438 
12439 /*
12440  * flex_stat, flex_lstat, flex_fstat
12441  * basic stat, but gets it right when asked to stat
12442  * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12443  */
12444 
12445 #ifndef _USE_STD_STAT
12446 /* encode_dev packs a VMS device name string into an integer to allow
12447  * simple comparisons. This can be used, for example, to check whether two
12448  * files are located on the same device, by comparing their encoded device
12449  * names. Even a string comparison would not do, because stat() reuses the
12450  * device name buffer for each call; so without encode_dev, it would be
12451  * necessary to save the buffer and use strcmp (this would mean a number of
12452  * changes to the standard Perl code, to say nothing of what a Perl script
12453  * would have to do.
12454  *
12455  * The device lock id, if it exists, should be unique (unless perhaps compared
12456  * with lock ids transferred from other nodes). We have a lock id if the disk is
12457  * mounted cluster-wide, which is when we tend to get long (host-qualified)
12458  * device names. Thus we use the lock id in preference, and only if that isn't
12459  * available, do we try to pack the device name into an integer (flagged by
12460  * the sign bit (LOCKID_MASK) being set).
12461  *
12462  * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12463  * name and its encoded form, but it seems very unlikely that we will find
12464  * two files on different disks that share the same encoded device names,
12465  * and even more remote that they will share the same file id (if the test
12466  * is to check for the same file).
12467  *
12468  * A better method might be to use sys$device_scan on the first call, and to
12469  * search for the device, returning an index into the cached array.
12470  * The number returned would be more intelligible.
12471  * This is probably not worth it, and anyway would take quite a bit longer
12472  * on the first call.
12473  */
12474 #define LOCKID_MASK 0x80000000     /* Use 0 to force device name use only */
12475 static mydev_t encode_dev (pTHX_ const char *dev)
12476 {
12477   int i;
12478   unsigned long int f;
12479   mydev_t enc;
12480   char c;
12481   const char *q;
12482 
12483   if (!dev || !dev[0]) return 0;
12484 
12485 #if LOCKID_MASK
12486   {
12487     struct dsc$descriptor_s dev_desc;
12488     unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12489 
12490     /* For cluster-mounted disks, the disk lock identifier is unique, so we
12491        can try that first. */
12492     dev_desc.dsc$w_length =  strlen (dev);
12493     dev_desc.dsc$b_dtype =   DSC$K_DTYPE_T;
12494     dev_desc.dsc$b_class =   DSC$K_CLASS_S;
12495     dev_desc.dsc$a_pointer = (char *) dev;  /* Read only parameter */
12496     status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12497     if (!$VMS_STATUS_SUCCESS(status)) {
12498       switch (status) {
12499         case SS$_NOSUCHDEV:
12500           SETERRNO(ENODEV, status);
12501           return 0;
12502         default:
12503           _ckvmssts(status);
12504       }
12505     }
12506     if (lockid) return (lockid & ~LOCKID_MASK);
12507   }
12508 #endif
12509 
12510   /* Otherwise we try to encode the device name */
12511   enc = 0;
12512   f = 1;
12513   i = 0;
12514   for (q = dev + strlen(dev); q--; q >= dev) {
12515     if (*q == ':')
12516 	break;
12517     if (isdigit (*q))
12518       c= (*q) - '0';
12519     else if (isalpha (toupper (*q)))
12520       c= toupper (*q) - 'A' + (char)10;
12521     else
12522       continue; /* Skip '$'s */
12523     i++;
12524     if (i>6) break;     /* 36^7 is too large to fit in an unsigned long int */
12525     if (i>1) f *= 36;
12526     enc += f * (unsigned long int) c;
12527   }
12528   return (enc | LOCKID_MASK);  /* May have already overflowed into bit 31 */
12529 
12530 }  /* end of encode_dev() */
12531 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12532 	device_no = encode_dev(aTHX_ devname)
12533 #else
12534 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12535 	device_no = new_dev_no
12536 #endif
12537 
12538 static int
12539 is_null_device(name)
12540     const char *name;
12541 {
12542   if (decc_bug_devnull != 0) {
12543     if (strncmp("/dev/null", name, 9) == 0)
12544       return 1;
12545   }
12546     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12547        The underscore prefix, controller letter, and unit number are
12548        independently optional; for our purposes, the colon punctuation
12549        is not.  The colon can be trailed by optional directory and/or
12550        filename, but two consecutive colons indicates a nodename rather
12551        than a device.  [pr]  */
12552   if (*name == '_') ++name;
12553   if (tolower(*name++) != 'n') return 0;
12554   if (tolower(*name++) != 'l') return 0;
12555   if (tolower(*name) == 'a') ++name;
12556   if (*name == '0') ++name;
12557   return (*name++ == ':') && (*name != ':');
12558 }
12559 
12560 static int
12561 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12562 
12563 #define flex_stat_int(a,b,c)		Perl_flex_stat_int(aTHX_ a,b,c)
12564 
12565 static I32
12566 Perl_cando_by_name_int
12567    (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12568 {
12569   char usrname[L_cuserid];
12570   struct dsc$descriptor_s usrdsc =
12571          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12572   char *vmsname = NULL, *fileified = NULL;
12573   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12574   unsigned short int retlen, trnlnm_iter_count;
12575   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12576   union prvdef curprv;
12577   struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12578          {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12579          {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12580   struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12581          {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12582          {0,0,0,0}};
12583   struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12584          {0,0,0,0}};
12585   struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12586   Stat_t st;
12587   static int profile_context = -1;
12588 
12589   if (!fname || !*fname) return FALSE;
12590 
12591   /* Make sure we expand logical names, since sys$check_access doesn't */
12592   fileified = PerlMem_malloc(VMS_MAXRSS);
12593   if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12594   if (!strpbrk(fname,"/]>:")) {
12595       strcpy(fileified,fname);
12596       trnlnm_iter_count = 0;
12597       while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12598         trnlnm_iter_count++;
12599         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12600       }
12601       fname = fileified;
12602   }
12603 
12604   vmsname = PerlMem_malloc(VMS_MAXRSS);
12605   if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12606   if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12607     /* Don't know if already in VMS format, so make sure */
12608     if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12609       PerlMem_free(fileified);
12610       PerlMem_free(vmsname);
12611       return FALSE;
12612     }
12613   }
12614   else {
12615     strcpy(vmsname,fname);
12616   }
12617 
12618   /* sys$check_access needs a file spec, not a directory spec.
12619    * flex_stat now will handle a null thread context during startup.
12620    */
12621 
12622   retlen = namdsc.dsc$w_length = strlen(vmsname);
12623   if (vmsname[retlen-1] == ']'
12624       || vmsname[retlen-1] == '>'
12625       || vmsname[retlen-1] == ':'
12626       || (!flex_stat_int(vmsname, &st, 1) &&
12627           S_ISDIR(st.st_mode))) {
12628 
12629       if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12630         PerlMem_free(fileified);
12631         PerlMem_free(vmsname);
12632         return FALSE;
12633       }
12634       fname = fileified;
12635   }
12636   else {
12637       fname = vmsname;
12638   }
12639 
12640   retlen = namdsc.dsc$w_length = strlen(fname);
12641   namdsc.dsc$a_pointer = (char *)fname;
12642 
12643   switch (bit) {
12644     case S_IXUSR: case S_IXGRP: case S_IXOTH:
12645       access = ARM$M_EXECUTE;
12646       flags = CHP$M_READ;
12647       break;
12648     case S_IRUSR: case S_IRGRP: case S_IROTH:
12649       access = ARM$M_READ;
12650       flags = CHP$M_READ | CHP$M_USEREADALL;
12651       break;
12652     case S_IWUSR: case S_IWGRP: case S_IWOTH:
12653       access = ARM$M_WRITE;
12654       flags = CHP$M_READ | CHP$M_WRITE;
12655       break;
12656     case S_IDUSR: case S_IDGRP: case S_IDOTH:
12657       access = ARM$M_DELETE;
12658       flags = CHP$M_READ | CHP$M_WRITE;
12659       break;
12660     default:
12661       if (fileified != NULL)
12662 	PerlMem_free(fileified);
12663       if (vmsname != NULL)
12664 	PerlMem_free(vmsname);
12665       return FALSE;
12666   }
12667 
12668   /* Before we call $check_access, create a user profile with the current
12669    * process privs since otherwise it just uses the default privs from the
12670    * UAF and might give false positives or negatives.  This only works on
12671    * VMS versions v6.0 and later since that's when sys$create_user_profile
12672    * became available.
12673    */
12674 
12675   /* get current process privs and username */
12676   _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12677   _ckvmssts_noperl(iosb[0]);
12678 
12679 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12680 
12681   /* find out the space required for the profile */
12682   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12683                                     &usrprodsc.dsc$w_length,&profile_context));
12684 
12685   /* allocate space for the profile and get it filled in */
12686   usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12687   if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12688   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12689                                     &usrprodsc.dsc$w_length,&profile_context));
12690 
12691   /* use the profile to check access to the file; free profile & analyze results */
12692   retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12693   PerlMem_free(usrprodsc.dsc$a_pointer);
12694   if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12695 
12696 #else
12697 
12698   retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12699 
12700 #endif
12701 
12702   if (retsts == SS$_NOPRIV      || retsts == SS$_NOSUCHOBJECT ||
12703       retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12704       retsts == RMS$_DIR        || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12705     set_vaxc_errno(retsts);
12706     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12707     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12708     else set_errno(ENOENT);
12709     if (fileified != NULL)
12710       PerlMem_free(fileified);
12711     if (vmsname != NULL)
12712       PerlMem_free(vmsname);
12713     return FALSE;
12714   }
12715   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12716     if (fileified != NULL)
12717       PerlMem_free(fileified);
12718     if (vmsname != NULL)
12719       PerlMem_free(vmsname);
12720     return TRUE;
12721   }
12722   _ckvmssts_noperl(retsts);
12723 
12724   if (fileified != NULL)
12725     PerlMem_free(fileified);
12726   if (vmsname != NULL)
12727     PerlMem_free(vmsname);
12728   return FALSE;  /* Should never get here */
12729 
12730 }
12731 
12732 /* Do the permissions allow some operation?  Assumes PL_statcache already set. */
12733 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12734  * subset of the applicable information.
12735  */
12736 bool
12737 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12738 {
12739   return cando_by_name_int
12740 	(bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12741 }  /* end of cando() */
12742 /*}}}*/
12743 
12744 
12745 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12746 I32
12747 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12748 {
12749    return cando_by_name_int(bit, effective, fname, 0);
12750 
12751 }  /* end of cando_by_name() */
12752 /*}}}*/
12753 
12754 
12755 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12756 int
12757 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12758 {
12759   if (!fstat(fd, &statbufp->crtl_stat)) {
12760     char *cptr;
12761     char *vms_filename;
12762     vms_filename = PerlMem_malloc(VMS_MAXRSS);
12763     if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12764 
12765     /* Save name for cando by name in VMS format */
12766     cptr = getname(fd, vms_filename, 1);
12767 
12768     /* This should not happen, but just in case */
12769     if (cptr == NULL) {
12770 	statbufp->st_devnam[0] = 0;
12771     }
12772     else {
12773 	/* Make sure that the saved name fits in 255 characters */
12774 	cptr = int_rmsexpand_vms
12775 		       (vms_filename,
12776 			statbufp->st_devnam,
12777 			0);
12778 	if (cptr == NULL)
12779 	    statbufp->st_devnam[0] = 0;
12780     }
12781     PerlMem_free(vms_filename);
12782 
12783     VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12784     VMS_DEVICE_ENCODE
12785 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12786 
12787 #   ifdef RTL_USES_UTC
12788 #   ifdef VMSISH_TIME
12789     if (VMSISH_TIME) {
12790       statbufp->st_mtime = _toloc(statbufp->st_mtime);
12791       statbufp->st_atime = _toloc(statbufp->st_atime);
12792       statbufp->st_ctime = _toloc(statbufp->st_ctime);
12793     }
12794 #   endif
12795 #   else
12796 #   ifdef VMSISH_TIME
12797     if (!VMSISH_TIME) { /* Return UTC instead of local time */
12798 #   else
12799     if (1) {
12800 #   endif
12801       statbufp->st_mtime = _toutc(statbufp->st_mtime);
12802       statbufp->st_atime = _toutc(statbufp->st_atime);
12803       statbufp->st_ctime = _toutc(statbufp->st_ctime);
12804     }
12805 #endif
12806     return 0;
12807   }
12808   return -1;
12809 
12810 }  /* end of flex_fstat() */
12811 /*}}}*/
12812 
12813 static int
12814 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12815 {
12816     char *fileified;
12817     char *temp_fspec;
12818     const char *save_spec;
12819     char *ret_spec;
12820     int retval = -1;
12821     int efs_hack = 0;
12822     dSAVEDERRNO;
12823 
12824     if (!fspec) {
12825         errno = EINVAL;
12826         return retval;
12827     }
12828 
12829     if (decc_bug_devnull != 0) {
12830       if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12831 	memset(statbufp,0,sizeof *statbufp);
12832         VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12833 	statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12834 	statbufp->st_uid = 0x00010001;
12835 	statbufp->st_gid = 0x0001;
12836 	time((time_t *)&statbufp->st_mtime);
12837 	statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12838 	return 0;
12839       }
12840     }
12841 
12842     /* Try for a directory name first.  If fspec contains a filename without
12843      * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12844      * and sea:[wine.dark]water. exist, we prefer the directory here.
12845      * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12846      * not sea:[wine.dark]., if the latter exists.  If the intended target is
12847      * the file with null type, specify this by calling flex_stat() with
12848      * a '.' at the end of fspec.
12849      *
12850      * If we are in Posix filespec mode, accept the filename as is.
12851      */
12852 
12853 
12854     fileified = PerlMem_malloc(VMS_MAXRSS);
12855     if (fileified == NULL)
12856         _ckvmssts_noperl(SS$_INSFMEM);
12857 
12858     temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12859     if (temp_fspec == NULL)
12860         _ckvmssts_noperl(SS$_INSFMEM);
12861 
12862     strcpy(temp_fspec, fspec);
12863 
12864     SAVE_ERRNO;
12865 
12866 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12867   if (decc_posix_compliant_pathnames == 0) {
12868 #endif
12869 
12870     /* We may be able to optimize this, but in order for fileify_dirspec to
12871      * always return a usuable answer, we have to call vmspath first to
12872      * make sure that it is in VMS directory format, as stat/lstat on 8.3
12873      * can not handle directories in unix format that it does not have read
12874      * access to.  Vmspath handles the case where a bare name which could be
12875      * a logical name gets passed.
12876      */
12877     ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12878     if (ret_spec != NULL) {
12879         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
12880         if (ret_spec != NULL) {
12881             if (lstat_flag == 0)
12882                 retval = stat(fileified, &statbufp->crtl_stat);
12883             else
12884                 retval = lstat(fileified, &statbufp->crtl_stat);
12885             save_spec = fileified;
12886         }
12887     }
12888 
12889     if (retval && vms_bug_stat_filename) {
12890 
12891         /* We should try again as a vmsified file specification */
12892         /* However Perl traditionally has not done this, which  */
12893         /* causes problems with existing tests */
12894 
12895         ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12896         if (ret_spec != NULL) {
12897             if (lstat_flag == 0)
12898                 retval = stat(temp_fspec, &statbufp->crtl_stat);
12899             else
12900                 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12901             save_spec = temp_fspec;
12902         }
12903     }
12904 
12905     if (retval) {
12906         /* Last chance - allow multiple dots with out EFS CHARSET */
12907         /* The CRTL stat() falls down hard on multi-dot filenames in unix
12908          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12909          * enable it if it isn't already.
12910          */
12911 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12912         if (!decc_efs_charset && (decc_efs_charset_index > 0))
12913             decc$feature_set_value(decc_efs_charset_index, 1, 1);
12914 #endif
12915         if (lstat_flag == 0)
12916 	    retval = stat(fspec, &statbufp->crtl_stat);
12917         else
12918 	    retval = lstat(fspec, &statbufp->crtl_stat);
12919         save_spec = fspec;
12920 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12921         if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12922             decc$feature_set_value(decc_efs_charset_index, 1, 0);
12923             efs_hack = 1;
12924         }
12925 #endif
12926     }
12927 
12928 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12929   } else {
12930     if (lstat_flag == 0)
12931       retval = stat(temp_fspec, &statbufp->crtl_stat);
12932     else
12933       retval = lstat(temp_fspec, &statbufp->crtl_stat);
12934       save_spec = temp_fspec;
12935   }
12936 #endif
12937 
12938 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12939   /* As you were... */
12940   if (!decc_efs_charset)
12941     decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12942 #endif
12943 
12944     if (!retval) {
12945     char * cptr;
12946     int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12947 
12948       /* If this is an lstat, do not follow the link */
12949       if (lstat_flag)
12950 	rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12951 
12952 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12953       /* If we used the efs_hack above, we must also use it here for */
12954       /* perl_cando to work */
12955       if (efs_hack && (decc_efs_charset_index > 0)) {
12956           decc$feature_set_value(decc_efs_charset_index, 1, 1);
12957       }
12958 #endif
12959       cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12960 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12961       if (efs_hack && (decc_efs_charset_index > 0)) {
12962           decc$feature_set_value(decc_efs_charset, 1, 0);
12963       }
12964 #endif
12965 
12966       /* Fix me: If this is NULL then stat found a file, and we could */
12967       /* not convert the specification to VMS - Should never happen */
12968       if (cptr == NULL)
12969 	statbufp->st_devnam[0] = 0;
12970 
12971       VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12972       VMS_DEVICE_ENCODE
12973 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12974 #     ifdef RTL_USES_UTC
12975 #     ifdef VMSISH_TIME
12976       if (VMSISH_TIME) {
12977         statbufp->st_mtime = _toloc(statbufp->st_mtime);
12978         statbufp->st_atime = _toloc(statbufp->st_atime);
12979         statbufp->st_ctime = _toloc(statbufp->st_ctime);
12980       }
12981 #     endif
12982 #     else
12983 #     ifdef VMSISH_TIME
12984       if (!VMSISH_TIME) { /* Return UTC instead of local time */
12985 #     else
12986       if (1) {
12987 #     endif
12988         statbufp->st_mtime = _toutc(statbufp->st_mtime);
12989         statbufp->st_atime = _toutc(statbufp->st_atime);
12990         statbufp->st_ctime = _toutc(statbufp->st_ctime);
12991       }
12992 #     endif
12993     }
12994     /* If we were successful, leave errno where we found it */
12995     if (retval == 0) RESTORE_ERRNO;
12996     PerlMem_free(temp_fspec);
12997     PerlMem_free(fileified);
12998     return retval;
12999 
13000 }  /* end of flex_stat_int() */
13001 
13002 
13003 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
13004 int
13005 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
13006 {
13007    return flex_stat_int(fspec, statbufp, 0);
13008 }
13009 /*}}}*/
13010 
13011 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
13012 int
13013 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
13014 {
13015    return flex_stat_int(fspec, statbufp, 1);
13016 }
13017 /*}}}*/
13018 
13019 
13020 /*{{{char *my_getlogin()*/
13021 /* VMS cuserid == Unix getlogin, except calling sequence */
13022 char *
13023 my_getlogin(void)
13024 {
13025     static char user[L_cuserid];
13026     return cuserid(user);
13027 }
13028 /*}}}*/
13029 
13030 
13031 /*  rmscopy - copy a file using VMS RMS routines
13032  *
13033  *  Copies contents and attributes of spec_in to spec_out, except owner
13034  *  and protection information.  Name and type of spec_in are used as
13035  *  defaults for spec_out.  The third parameter specifies whether rmscopy()
13036  *  should try to propagate timestamps from the input file to the output file.
13037  *  If it is less than 0, no timestamps are preserved.  If it is 0, then
13038  *  rmscopy() will behave similarly to the DCL COPY command: timestamps are
13039  *  propagated to the output file at creation iff the output file specification
13040  *  did not contain an explicit name or type, and the revision date is always
13041  *  updated at the end of the copy operation.  If it is greater than 0, then
13042  *  it is interpreted as a bitmask, in which bit 0 indicates that timestamps
13043  *  other than the revision date should be propagated, and bit 1 indicates
13044  *  that the revision date should be propagated.
13045  *
13046  *  Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
13047  *
13048  *  Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
13049  *  Incorporates, with permission, some code from EZCOPY by Tim Adye
13050  *  <T.J.Adye@rl.ac.uk>.  Permission is given to distribute this code
13051  * as part of the Perl standard distribution under the terms of the
13052  * GNU General Public License or the Perl Artistic License.  Copies
13053  * of each may be found in the Perl standard distribution.
13054  */ /* FIXME */
13055 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
13056 int
13057 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
13058 {
13059     char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
13060          *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
13061     unsigned long int i, sts, sts2;
13062     int dna_len;
13063     struct FAB fab_in, fab_out;
13064     struct RAB rab_in, rab_out;
13065     rms_setup_nam(nam);
13066     rms_setup_nam(nam_out);
13067     struct XABDAT xabdat;
13068     struct XABFHC xabfhc;
13069     struct XABRDT xabrdt;
13070     struct XABSUM xabsum;
13071 
13072     vmsin = PerlMem_malloc(VMS_MAXRSS);
13073     if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13074     vmsout = PerlMem_malloc(VMS_MAXRSS);
13075     if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13076     if (!spec_in  || !*spec_in  || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
13077         !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
13078       PerlMem_free(vmsin);
13079       PerlMem_free(vmsout);
13080       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13081       return 0;
13082     }
13083 
13084     esa = PerlMem_malloc(VMS_MAXRSS);
13085     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13086     esal = NULL;
13087 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13088     esal = PerlMem_malloc(VMS_MAXRSS);
13089     if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13090 #endif
13091     fab_in = cc$rms_fab;
13092     rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
13093     fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
13094     fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
13095     fab_in.fab$l_fop = FAB$M_SQO;
13096     rms_bind_fab_nam(fab_in, nam);
13097     fab_in.fab$l_xab = (void *) &xabdat;
13098 
13099     rsa = PerlMem_malloc(VMS_MAXRSS);
13100     if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13101     rsal = NULL;
13102 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13103     rsal = PerlMem_malloc(VMS_MAXRSS);
13104     if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13105 #endif
13106     rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
13107     rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
13108     rms_nam_esl(nam) = 0;
13109     rms_nam_rsl(nam) = 0;
13110     rms_nam_esll(nam) = 0;
13111     rms_nam_rsll(nam) = 0;
13112 #ifdef NAM$M_NO_SHORT_UPCASE
13113     if (decc_efs_case_preserve)
13114 	rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
13115 #endif
13116 
13117     xabdat = cc$rms_xabdat;        /* To get creation date */
13118     xabdat.xab$l_nxt = (void *) &xabfhc;
13119 
13120     xabfhc = cc$rms_xabfhc;        /* To get record length */
13121     xabfhc.xab$l_nxt = (void *) &xabsum;
13122 
13123     xabsum = cc$rms_xabsum;        /* To get key and area information */
13124 
13125     if (!((sts = sys$open(&fab_in)) & 1)) {
13126       PerlMem_free(vmsin);
13127       PerlMem_free(vmsout);
13128       PerlMem_free(esa);
13129       if (esal != NULL)
13130 	PerlMem_free(esal);
13131       PerlMem_free(rsa);
13132       if (rsal != NULL)
13133 	PerlMem_free(rsal);
13134       set_vaxc_errno(sts);
13135       switch (sts) {
13136         case RMS$_FNF: case RMS$_DNF:
13137           set_errno(ENOENT); break;
13138         case RMS$_DIR:
13139           set_errno(ENOTDIR); break;
13140         case RMS$_DEV:
13141           set_errno(ENODEV); break;
13142         case RMS$_SYN:
13143           set_errno(EINVAL); break;
13144         case RMS$_PRV:
13145           set_errno(EACCES); break;
13146         default:
13147           set_errno(EVMSERR);
13148       }
13149       return 0;
13150     }
13151 
13152     nam_out = nam;
13153     fab_out = fab_in;
13154     fab_out.fab$w_ifi = 0;
13155     fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13156     fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13157     fab_out.fab$l_fop = FAB$M_SQO;
13158     rms_bind_fab_nam(fab_out, nam_out);
13159     rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13160     dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13161     rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13162     esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13163     if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13164     rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13165     if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13166     esal_out = NULL;
13167     rsal_out = NULL;
13168 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13169     esal_out = PerlMem_malloc(VMS_MAXRSS);
13170     if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13171     rsal_out = PerlMem_malloc(VMS_MAXRSS);
13172     if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13173 #endif
13174     rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13175     rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13176 
13177     if (preserve_dates == 0) {  /* Act like DCL COPY */
13178       rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13179       fab_out.fab$l_xab = NULL;  /* Don't disturb data from input file */
13180       if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13181 	PerlMem_free(vmsin);
13182 	PerlMem_free(vmsout);
13183 	PerlMem_free(esa);
13184 	if (esal != NULL)
13185 	    PerlMem_free(esal);
13186 	PerlMem_free(rsa);
13187 	if (rsal != NULL)
13188 	    PerlMem_free(rsal);
13189 	PerlMem_free(esa_out);
13190 	if (esal_out != NULL)
13191 	    PerlMem_free(esal_out);
13192 	PerlMem_free(rsa_out);
13193 	if (rsal_out != NULL)
13194 	    PerlMem_free(rsal_out);
13195         set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13196         set_vaxc_errno(sts);
13197         return 0;
13198       }
13199       fab_out.fab$l_xab = (void *) &xabdat;
13200       if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13201 	preserve_dates = 1;
13202     }
13203     if (preserve_dates < 0)   /* Clear all bits; we'll use it as a */
13204       preserve_dates =0;      /* bitmask from this point forward   */
13205 
13206     if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13207     if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13208       PerlMem_free(vmsin);
13209       PerlMem_free(vmsout);
13210       PerlMem_free(esa);
13211       if (esal != NULL)
13212 	  PerlMem_free(esal);
13213       PerlMem_free(rsa);
13214       if (rsal != NULL)
13215 	  PerlMem_free(rsal);
13216       PerlMem_free(esa_out);
13217       if (esal_out != NULL)
13218 	  PerlMem_free(esal_out);
13219       PerlMem_free(rsa_out);
13220       if (rsal_out != NULL)
13221 	  PerlMem_free(rsal_out);
13222       set_vaxc_errno(sts);
13223       switch (sts) {
13224         case RMS$_DNF:
13225           set_errno(ENOENT); break;
13226         case RMS$_DIR:
13227           set_errno(ENOTDIR); break;
13228         case RMS$_DEV:
13229           set_errno(ENODEV); break;
13230         case RMS$_SYN:
13231           set_errno(EINVAL); break;
13232         case RMS$_PRV:
13233           set_errno(EACCES); break;
13234         default:
13235           set_errno(EVMSERR);
13236       }
13237       return 0;
13238     }
13239     fab_out.fab$l_fop |= FAB$M_DLT;  /* in case we have to bail out */
13240     if (preserve_dates & 2) {
13241       /* sys$close() will process xabrdt, not xabdat */
13242       xabrdt = cc$rms_xabrdt;
13243 #ifndef __GNUC__
13244       xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13245 #else
13246       /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13247        * is unsigned long[2], while DECC & VAXC use a struct */
13248       memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13249 #endif
13250       fab_out.fab$l_xab = (void *) &xabrdt;
13251     }
13252 
13253     ubf = PerlMem_malloc(32256);
13254     if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13255     rab_in = cc$rms_rab;
13256     rab_in.rab$l_fab = &fab_in;
13257     rab_in.rab$l_rop = RAB$M_BIO;
13258     rab_in.rab$l_ubf = ubf;
13259     rab_in.rab$w_usz = 32256;
13260     if (!((sts = sys$connect(&rab_in)) & 1)) {
13261       sys$close(&fab_in); sys$close(&fab_out);
13262       PerlMem_free(vmsin);
13263       PerlMem_free(vmsout);
13264       PerlMem_free(ubf);
13265       PerlMem_free(esa);
13266       if (esal != NULL)
13267 	  PerlMem_free(esal);
13268       PerlMem_free(rsa);
13269       if (rsal != NULL)
13270 	  PerlMem_free(rsal);
13271       PerlMem_free(esa_out);
13272       if (esal_out != NULL)
13273 	  PerlMem_free(esal_out);
13274       PerlMem_free(rsa_out);
13275       if (rsal_out != NULL)
13276 	  PerlMem_free(rsal_out);
13277       set_errno(EVMSERR); set_vaxc_errno(sts);
13278       return 0;
13279     }
13280 
13281     rab_out = cc$rms_rab;
13282     rab_out.rab$l_fab = &fab_out;
13283     rab_out.rab$l_rbf = ubf;
13284     if (!((sts = sys$connect(&rab_out)) & 1)) {
13285       sys$close(&fab_in); sys$close(&fab_out);
13286       PerlMem_free(vmsin);
13287       PerlMem_free(vmsout);
13288       PerlMem_free(ubf);
13289       PerlMem_free(esa);
13290       if (esal != NULL)
13291 	  PerlMem_free(esal);
13292       PerlMem_free(rsa);
13293       if (rsal != NULL)
13294 	  PerlMem_free(rsal);
13295       PerlMem_free(esa_out);
13296       if (esal_out != NULL)
13297 	  PerlMem_free(esal_out);
13298       PerlMem_free(rsa_out);
13299       if (rsal_out != NULL)
13300 	  PerlMem_free(rsal_out);
13301       set_errno(EVMSERR); set_vaxc_errno(sts);
13302       return 0;
13303     }
13304 
13305     while ((sts = sys$read(&rab_in))) {  /* always true  */
13306       if (sts == RMS$_EOF) break;
13307       rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13308       if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13309         sys$close(&fab_in); sys$close(&fab_out);
13310 	PerlMem_free(vmsin);
13311 	PerlMem_free(vmsout);
13312 	PerlMem_free(ubf);
13313 	PerlMem_free(esa);
13314 	if (esal != NULL)
13315 	    PerlMem_free(esal);
13316 	PerlMem_free(rsa);
13317 	if (rsal != NULL)
13318 	    PerlMem_free(rsal);
13319 	PerlMem_free(esa_out);
13320  	if (esal_out != NULL)
13321 	    PerlMem_free(esal_out);
13322 	PerlMem_free(rsa_out);
13323  	if (rsal_out != NULL)
13324 	    PerlMem_free(rsal_out);
13325         set_errno(EVMSERR); set_vaxc_errno(sts);
13326         return 0;
13327       }
13328     }
13329 
13330 
13331     fab_out.fab$l_fop &= ~FAB$M_DLT;  /* We got this far; keep the output */
13332     sys$close(&fab_in);  sys$close(&fab_out);
13333     sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13334 
13335     PerlMem_free(vmsin);
13336     PerlMem_free(vmsout);
13337     PerlMem_free(ubf);
13338     PerlMem_free(esa);
13339     if (esal != NULL)
13340 	PerlMem_free(esal);
13341     PerlMem_free(rsa);
13342     if (rsal != NULL)
13343 	PerlMem_free(rsal);
13344     PerlMem_free(esa_out);
13345     if (esal_out != NULL)
13346 	PerlMem_free(esal_out);
13347     PerlMem_free(rsa_out);
13348     if (rsal_out != NULL)
13349 	PerlMem_free(rsal_out);
13350 
13351     if (!(sts & 1)) {
13352       set_errno(EVMSERR); set_vaxc_errno(sts);
13353       return 0;
13354     }
13355 
13356     return 1;
13357 
13358 }  /* end of rmscopy() */
13359 /*}}}*/
13360 
13361 
13362 /***  The following glue provides 'hooks' to make some of the routines
13363  * from this file available from Perl.  These routines are sufficiently
13364  * basic, and are required sufficiently early in the build process,
13365  * that's it's nice to have them available to miniperl as well as the
13366  * full Perl, so they're set up here instead of in an extension.  The
13367  * Perl code which handles importation of these names into a given
13368  * package lives in [.VMS]Filespec.pm in @INC.
13369  */
13370 
13371 void
13372 rmsexpand_fromperl(pTHX_ CV *cv)
13373 {
13374   dXSARGS;
13375   char *fspec, *defspec = NULL, *rslt;
13376   STRLEN n_a;
13377   int fs_utf8, dfs_utf8;
13378 
13379   fs_utf8 = 0;
13380   dfs_utf8 = 0;
13381   if (!items || items > 2)
13382     Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13383   fspec = SvPV(ST(0),n_a);
13384   fs_utf8 = SvUTF8(ST(0));
13385   if (!fspec || !*fspec) XSRETURN_UNDEF;
13386   if (items == 2) {
13387     defspec = SvPV(ST(1),n_a);
13388     dfs_utf8 = SvUTF8(ST(1));
13389   }
13390   rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13391   ST(0) = sv_newmortal();
13392   if (rslt != NULL) {
13393     sv_usepvn(ST(0),rslt,strlen(rslt));
13394     if (fs_utf8) {
13395 	SvUTF8_on(ST(0));
13396     }
13397   }
13398   XSRETURN(1);
13399 }
13400 
13401 void
13402 vmsify_fromperl(pTHX_ CV *cv)
13403 {
13404   dXSARGS;
13405   char *vmsified;
13406   STRLEN n_a;
13407   int utf8_fl;
13408 
13409   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13410   utf8_fl = SvUTF8(ST(0));
13411   vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13412   ST(0) = sv_newmortal();
13413   if (vmsified != NULL) {
13414     sv_usepvn(ST(0),vmsified,strlen(vmsified));
13415     if (utf8_fl) {
13416 	SvUTF8_on(ST(0));
13417     }
13418   }
13419   XSRETURN(1);
13420 }
13421 
13422 void
13423 unixify_fromperl(pTHX_ CV *cv)
13424 {
13425   dXSARGS;
13426   char *unixified;
13427   STRLEN n_a;
13428   int utf8_fl;
13429 
13430   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13431   utf8_fl = SvUTF8(ST(0));
13432   unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13433   ST(0) = sv_newmortal();
13434   if (unixified != NULL) {
13435     sv_usepvn(ST(0),unixified,strlen(unixified));
13436     if (utf8_fl) {
13437 	SvUTF8_on(ST(0));
13438     }
13439   }
13440   XSRETURN(1);
13441 }
13442 
13443 void
13444 fileify_fromperl(pTHX_ CV *cv)
13445 {
13446   dXSARGS;
13447   char *fileified;
13448   STRLEN n_a;
13449   int utf8_fl;
13450 
13451   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13452   utf8_fl = SvUTF8(ST(0));
13453   fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13454   ST(0) = sv_newmortal();
13455   if (fileified != NULL) {
13456     sv_usepvn(ST(0),fileified,strlen(fileified));
13457     if (utf8_fl) {
13458 	SvUTF8_on(ST(0));
13459     }
13460   }
13461   XSRETURN(1);
13462 }
13463 
13464 void
13465 pathify_fromperl(pTHX_ CV *cv)
13466 {
13467   dXSARGS;
13468   char *pathified;
13469   STRLEN n_a;
13470   int utf8_fl;
13471 
13472   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13473   utf8_fl = SvUTF8(ST(0));
13474   pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13475   ST(0) = sv_newmortal();
13476   if (pathified != NULL) {
13477     sv_usepvn(ST(0),pathified,strlen(pathified));
13478     if (utf8_fl) {
13479 	SvUTF8_on(ST(0));
13480     }
13481   }
13482   XSRETURN(1);
13483 }
13484 
13485 void
13486 vmspath_fromperl(pTHX_ CV *cv)
13487 {
13488   dXSARGS;
13489   char *vmspath;
13490   STRLEN n_a;
13491   int utf8_fl;
13492 
13493   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13494   utf8_fl = SvUTF8(ST(0));
13495   vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13496   ST(0) = sv_newmortal();
13497   if (vmspath != NULL) {
13498     sv_usepvn(ST(0),vmspath,strlen(vmspath));
13499     if (utf8_fl) {
13500 	SvUTF8_on(ST(0));
13501     }
13502   }
13503   XSRETURN(1);
13504 }
13505 
13506 void
13507 unixpath_fromperl(pTHX_ CV *cv)
13508 {
13509   dXSARGS;
13510   char *unixpath;
13511   STRLEN n_a;
13512   int utf8_fl;
13513 
13514   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13515   utf8_fl = SvUTF8(ST(0));
13516   unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13517   ST(0) = sv_newmortal();
13518   if (unixpath != NULL) {
13519     sv_usepvn(ST(0),unixpath,strlen(unixpath));
13520     if (utf8_fl) {
13521 	SvUTF8_on(ST(0));
13522     }
13523   }
13524   XSRETURN(1);
13525 }
13526 
13527 void
13528 candelete_fromperl(pTHX_ CV *cv)
13529 {
13530   dXSARGS;
13531   char *fspec, *fsp;
13532   SV *mysv;
13533   IO *io;
13534   STRLEN n_a;
13535 
13536   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13537 
13538   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13539   Newx(fspec, VMS_MAXRSS, char);
13540   if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13541   if (SvTYPE(mysv) == SVt_PVGV) {
13542     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13543       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13544       ST(0) = &PL_sv_no;
13545       Safefree(fspec);
13546       XSRETURN(1);
13547     }
13548     fsp = fspec;
13549   }
13550   else {
13551     if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13552       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13553       ST(0) = &PL_sv_no;
13554       Safefree(fspec);
13555       XSRETURN(1);
13556     }
13557   }
13558 
13559   ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13560   Safefree(fspec);
13561   XSRETURN(1);
13562 }
13563 
13564 void
13565 rmscopy_fromperl(pTHX_ CV *cv)
13566 {
13567   dXSARGS;
13568   char *inspec, *outspec, *inp, *outp;
13569   int date_flag;
13570   struct dsc$descriptor indsc  = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13571                         outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13572   unsigned long int sts;
13573   SV *mysv;
13574   IO *io;
13575   STRLEN n_a;
13576 
13577   if (items < 2 || items > 3)
13578     Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13579 
13580   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13581   Newx(inspec, VMS_MAXRSS, char);
13582   if (SvTYPE(mysv) == SVt_PVGV) {
13583     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13584       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13585       ST(0) = sv_2mortal(newSViv(0));
13586       Safefree(inspec);
13587       XSRETURN(1);
13588     }
13589     inp = inspec;
13590   }
13591   else {
13592     if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13593       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13594       ST(0) = sv_2mortal(newSViv(0));
13595       Safefree(inspec);
13596       XSRETURN(1);
13597     }
13598   }
13599   mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13600   Newx(outspec, VMS_MAXRSS, char);
13601   if (SvTYPE(mysv) == SVt_PVGV) {
13602     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13603       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13604       ST(0) = sv_2mortal(newSViv(0));
13605       Safefree(inspec);
13606       Safefree(outspec);
13607       XSRETURN(1);
13608     }
13609     outp = outspec;
13610   }
13611   else {
13612     if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13613       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13614       ST(0) = sv_2mortal(newSViv(0));
13615       Safefree(inspec);
13616       Safefree(outspec);
13617       XSRETURN(1);
13618     }
13619   }
13620   date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13621 
13622   ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
13623   Safefree(inspec);
13624   Safefree(outspec);
13625   XSRETURN(1);
13626 }
13627 
13628 /* The mod2fname is limited to shorter filenames by design, so it should
13629  * not be modified to support longer EFS pathnames
13630  */
13631 void
13632 mod2fname(pTHX_ CV *cv)
13633 {
13634   dXSARGS;
13635   char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13636        workbuff[NAM$C_MAXRSS*1 + 1];
13637   int total_namelen = 3, counter, num_entries;
13638   /* ODS-5 ups this, but we want to be consistent, so... */
13639   int max_name_len = 39;
13640   AV *in_array = (AV *)SvRV(ST(0));
13641 
13642   num_entries = av_len(in_array);
13643 
13644   /* All the names start with PL_. */
13645   strcpy(ultimate_name, "PL_");
13646 
13647   /* Clean up our working buffer */
13648   Zero(work_name, sizeof(work_name), char);
13649 
13650   /* Run through the entries and build up a working name */
13651   for(counter = 0; counter <= num_entries; counter++) {
13652     /* If it's not the first name then tack on a __ */
13653     if (counter) {
13654       strcat(work_name, "__");
13655     }
13656     strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13657   }
13658 
13659   /* Check to see if we actually have to bother...*/
13660   if (strlen(work_name) + 3 <= max_name_len) {
13661     strcat(ultimate_name, work_name);
13662   } else {
13663     /* It's too darned big, so we need to go strip. We use the same */
13664     /* algorithm as xsubpp does. First, strip out doubled __ */
13665     char *source, *dest, last;
13666     dest = workbuff;
13667     last = 0;
13668     for (source = work_name; *source; source++) {
13669       if (last == *source && last == '_') {
13670 	continue;
13671       }
13672       *dest++ = *source;
13673       last = *source;
13674     }
13675     /* Go put it back */
13676     strcpy(work_name, workbuff);
13677     /* Is it still too big? */
13678     if (strlen(work_name) + 3 > max_name_len) {
13679       /* Strip duplicate letters */
13680       last = 0;
13681       dest = workbuff;
13682       for (source = work_name; *source; source++) {
13683 	if (last == toupper(*source)) {
13684 	continue;
13685 	}
13686 	*dest++ = *source;
13687 	last = toupper(*source);
13688       }
13689       strcpy(work_name, workbuff);
13690     }
13691 
13692     /* Is it *still* too big? */
13693     if (strlen(work_name) + 3 > max_name_len) {
13694       /* Too bad, we truncate */
13695       work_name[max_name_len - 2] = 0;
13696     }
13697     strcat(ultimate_name, work_name);
13698   }
13699 
13700   /* Okay, return it */
13701   ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13702   XSRETURN(1);
13703 }
13704 
13705 void
13706 hushexit_fromperl(pTHX_ CV *cv)
13707 {
13708     dXSARGS;
13709 
13710     if (items > 0) {
13711         VMSISH_HUSHED = SvTRUE(ST(0));
13712     }
13713     ST(0) = boolSV(VMSISH_HUSHED);
13714     XSRETURN(1);
13715 }
13716 
13717 
13718 PerlIO *
13719 Perl_vms_start_glob
13720    (pTHX_ SV *tmpglob,
13721     IO *io)
13722 {
13723     PerlIO *fp;
13724     struct vs_str_st *rslt;
13725     char *vmsspec;
13726     char *rstr;
13727     char *begin, *cp;
13728     $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13729     PerlIO *tmpfp;
13730     STRLEN i;
13731     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13732     struct dsc$descriptor_vs rsdsc;
13733     unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13734     unsigned long hasver = 0, isunix = 0;
13735     unsigned long int lff_flags = 0;
13736     int rms_sts;
13737     int vms_old_glob = 1;
13738 
13739     if (!SvOK(tmpglob)) {
13740         SETERRNO(ENOENT,RMS$_FNF);
13741         return NULL;
13742     }
13743 
13744     vms_old_glob = !decc_filename_unix_report;
13745 
13746 #ifdef VMS_LONGNAME_SUPPORT
13747     lff_flags = LIB$M_FIL_LONG_NAMES;
13748 #endif
13749     /* The Newx macro will not allow me to assign a smaller array
13750      * to the rslt pointer, so we will assign it to the begin char pointer
13751      * and then copy the value into the rslt pointer.
13752      */
13753     Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13754     rslt = (struct vs_str_st *)begin;
13755     rslt->length = 0;
13756     rstr = &rslt->str[0];
13757     rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13758     rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13759     rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13760     rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13761 
13762     Newx(vmsspec, VMS_MAXRSS, char);
13763 
13764 	/* We could find out if there's an explicit dev/dir or version
13765 	   by peeking into lib$find_file's internal context at
13766 	   ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13767 	   but that's unsupported, so I don't want to do it now and
13768 	   have it bite someone in the future. */
13769 	/* Fix-me: vms_split_path() is the only way to do this, the
13770 	   existing method will fail with many legal EFS or UNIX specifications
13771 	 */
13772 
13773     cp = SvPV(tmpglob,i);
13774 
13775     for (; i; i--) {
13776 	if (cp[i] == ';') hasver = 1;
13777 	if (cp[i] == '.') {
13778 	    if (sts) hasver = 1;
13779 	    else sts = 1;
13780 	}
13781 	if (cp[i] == '/') {
13782 	    hasdir = isunix = 1;
13783 	    break;
13784 	}
13785 	if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13786 	    hasdir = 1;
13787 	    break;
13788 	}
13789     }
13790 
13791     /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13792     if ((hasdir == 0) && decc_filename_unix_report) {
13793         isunix = 1;
13794     }
13795 
13796     if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13797 	char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13798 	int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13799 	int wildstar = 0;
13800 	int wildquery = 0;
13801 	int found = 0;
13802 	Stat_t st;
13803 	int stat_sts;
13804 	stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13805 	if (!stat_sts && S_ISDIR(st.st_mode)) {
13806             char * vms_dir;
13807             const char * fname;
13808             STRLEN fname_len;
13809 
13810             /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13811             /* path delimiter of ':>]', if so, then the old behavior has */
13812             /* obviously been specificially requested */
13813 
13814             fname = SvPVX_const(tmpglob);
13815             fname_len = strlen(fname);
13816             vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13817             if (vms_old_glob || (vms_dir != NULL)) {
13818                 wilddsc.dsc$a_pointer = tovmspath_utf8(
13819                                             SvPVX(tmpglob),vmsspec,NULL);
13820                 ok = (wilddsc.dsc$a_pointer != NULL);
13821                 /* maybe passed 'foo' rather than '[.foo]', thus not
13822                    detected above */
13823                 hasdir = 1;
13824             } else {
13825                 /* Operate just on the directory, the special stat/fstat for */
13826                 /* leaves the fileified  specification in the st_devnam */
13827                 /* member. */
13828                 wilddsc.dsc$a_pointer = st.st_devnam;
13829                 ok = 1;
13830             }
13831 	}
13832 	else {
13833 	    wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13834 	    ok = (wilddsc.dsc$a_pointer != NULL);
13835 	}
13836 	if (ok)
13837 	    wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13838 
13839 	/* If not extended character set, replace ? with % */
13840 	/* With extended character set, ? is a wildcard single character */
13841 	for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13842 	    if (*cp == '?') {
13843                 wildquery = 1;
13844                 if (!decc_efs_case_preserve)
13845                     *cp = '%';
13846             } else if (*cp == '%') {
13847                 wildquery = 1;
13848             } else if (*cp == '*') {
13849                 wildstar = 1;
13850             }
13851 	}
13852 
13853         if (ok) {
13854             wv_sts = vms_split_path(
13855                 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13856                 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13857                 &wvs_spec, &wvs_len);
13858         } else {
13859             wn_spec = NULL;
13860             wn_len = 0;
13861             we_spec = NULL;
13862             we_len = 0;
13863         }
13864 
13865 	sts = SS$_NORMAL;
13866 	while (ok && $VMS_STATUS_SUCCESS(sts)) {
13867 	 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13868 	 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13869          int valid_find;
13870 
13871             valid_find = 0;
13872 	    sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13873 				&dfltdsc,NULL,&rms_sts,&lff_flags);
13874 	    if (!$VMS_STATUS_SUCCESS(sts))
13875 		break;
13876 
13877 	    /* with varying string, 1st word of buffer contains result length */
13878 	    rstr[rslt->length] = '\0';
13879 
13880 	     /* Find where all the components are */
13881 	     v_sts = vms_split_path
13882 		       (rstr,
13883 			&v_spec,
13884 			&v_len,
13885 			&r_spec,
13886 			&r_len,
13887 			&d_spec,
13888 			&d_len,
13889 			&n_spec,
13890 			&n_len,
13891 			&e_spec,
13892 			&e_len,
13893 			&vs_spec,
13894 			&vs_len);
13895 
13896 	    /* If no version on input, truncate the version on output */
13897 	    if (!hasver && (vs_len > 0)) {
13898 		*vs_spec = '\0';
13899 		vs_len = 0;
13900             }
13901 
13902             if (isunix) {
13903 
13904                 /* In Unix report mode, remove the ".dir;1" from the name */
13905                 /* if it is a real directory */
13906                 if (decc_filename_unix_report || decc_efs_charset) {
13907                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13908                         Stat_t statbuf;
13909                         int ret_sts;
13910 
13911                         ret_sts = flex_lstat(rstr, &statbuf);
13912                         if ((ret_sts == 0) &&
13913                             S_ISDIR(statbuf.st_mode)) {
13914                             e_len = 0;
13915                             e_spec[0] = 0;
13916                         }
13917                     }
13918                 }
13919 
13920 		/* No version & a null extension on UNIX handling */
13921 		if ((e_len == 1) && decc_readdir_dropdotnotype) {
13922 		    e_len = 0;
13923 		    *e_spec = '\0';
13924 		}
13925 	    }
13926 
13927 	    if (!decc_efs_case_preserve) {
13928 	        for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13929 	    }
13930 
13931             /* Find File treats a Null extension as return all extensions */
13932             /* This is contrary to Perl expectations */
13933 
13934             if (wildstar || wildquery || vms_old_glob) {
13935                 /* really need to see if the returned file name matched */
13936                 /* but for now will assume that it matches */
13937                 valid_find = 1;
13938             } else {
13939                 /* Exact Match requested */
13940                 /* How are directories handled? - like a file */
13941                 if ((e_len == we_len) && (n_len == wn_len)) {
13942                     int t1;
13943                     t1 = e_len;
13944                     if (t1 > 0)
13945                         t1 = strncmp(e_spec, we_spec, e_len);
13946                     if (t1 == 0) {
13947                        t1 = n_len;
13948                        if (t1 > 0)
13949                            t1 = strncmp(n_spec, we_spec, n_len);
13950                        if (t1 == 0)
13951                            valid_find = 1;
13952                     }
13953                 }
13954             }
13955 
13956             if (valid_find) {
13957 	        found++;
13958 
13959 	        if (hasdir) {
13960 		    if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13961 		    begin = rstr;
13962 	        }
13963 	        else {
13964 		    /* Start with the name */
13965 		    begin = n_spec;
13966 	        }
13967 	        strcat(begin,"\n");
13968 	        ok = (PerlIO_puts(tmpfp,begin) != EOF);
13969             }
13970 	}
13971 	if (cxt) (void)lib$find_file_end(&cxt);
13972 
13973 	if (!found) {
13974 	    /* Be POSIXish: return the input pattern when no matches */
13975 	    strcpy(rstr,SvPVX(tmpglob));
13976 	    strcat(rstr,"\n");
13977 	    ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13978 	}
13979 
13980 	if (ok && sts != RMS$_NMF &&
13981 	    sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13982 	if (!ok) {
13983 	    if (!(sts & 1)) {
13984 		SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13985 	    }
13986 	    PerlIO_close(tmpfp);
13987 	    fp = NULL;
13988 	}
13989 	else {
13990 	    PerlIO_rewind(tmpfp);
13991 	    IoTYPE(io) = IoTYPE_RDONLY;
13992 	    IoIFP(io) = fp = tmpfp;
13993 	    IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
13994 	}
13995     }
13996     Safefree(vmsspec);
13997     Safefree(rslt);
13998     return fp;
13999 }
14000 
14001 
14002 static char *
14003 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
14004 		   int *utf8_fl);
14005 
14006 void
14007 unixrealpath_fromperl(pTHX_ CV *cv)
14008 {
14009     dXSARGS;
14010     char *fspec, *rslt_spec, *rslt;
14011     STRLEN n_a;
14012 
14013     if (!items || items != 1)
14014 	Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
14015 
14016     fspec = SvPV(ST(0),n_a);
14017     if (!fspec || !*fspec) XSRETURN_UNDEF;
14018 
14019     Newx(rslt_spec, VMS_MAXRSS + 1, char);
14020     rslt = do_vms_realpath(fspec, rslt_spec, NULL);
14021 
14022     ST(0) = sv_newmortal();
14023     if (rslt != NULL)
14024 	sv_usepvn(ST(0),rslt,strlen(rslt));
14025     else
14026 	Safefree(rslt_spec);
14027 	XSRETURN(1);
14028 }
14029 
14030 static char *
14031 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
14032 		   int *utf8_fl);
14033 
14034 void
14035 vmsrealpath_fromperl(pTHX_ CV *cv)
14036 {
14037     dXSARGS;
14038     char *fspec, *rslt_spec, *rslt;
14039     STRLEN n_a;
14040 
14041     if (!items || items != 1)
14042 	Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
14043 
14044     fspec = SvPV(ST(0),n_a);
14045     if (!fspec || !*fspec) XSRETURN_UNDEF;
14046 
14047     Newx(rslt_spec, VMS_MAXRSS + 1, char);
14048     rslt = do_vms_realname(fspec, rslt_spec, NULL);
14049 
14050     ST(0) = sv_newmortal();
14051     if (rslt != NULL)
14052 	sv_usepvn(ST(0),rslt,strlen(rslt));
14053     else
14054 	Safefree(rslt_spec);
14055 	XSRETURN(1);
14056 }
14057 
14058 #ifdef HAS_SYMLINK
14059 /*
14060  * A thin wrapper around decc$symlink to make sure we follow the
14061  * standard and do not create a symlink with a zero-length name.
14062  *
14063  * Also in ODS-2 mode, existing tests assume that the link target
14064  * will be converted to UNIX format.
14065  */
14066 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
14067 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
14068   if (!link_name || !*link_name) {
14069     SETERRNO(ENOENT, SS$_NOSUCHFILE);
14070     return -1;
14071   }
14072 
14073   if (decc_efs_charset) {
14074       return symlink(contents, link_name);
14075   } else {
14076       int sts;
14077       char * utarget;
14078 
14079       /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
14080       /* because in order to work, the symlink target must be in UNIX format */
14081 
14082       /* As symbolic links can hold things other than files, we will only do */
14083       /* the conversion in in ODS-2 mode */
14084 
14085       utarget = PerlMem_malloc(VMS_MAXRSS + 1);
14086       if (int_tounixspec(contents, utarget, NULL) == NULL) {
14087 
14088           /* This should not fail, as an untranslatable filename */
14089           /* should be passed through */
14090           utarget = (char *)contents;
14091       }
14092       sts = symlink(utarget, link_name);
14093       PerlMem_free(utarget);
14094       return sts;
14095   }
14096 
14097 }
14098 /*}}}*/
14099 
14100 #endif /* HAS_SYMLINK */
14101 
14102 int do_vms_case_tolerant(void);
14103 
14104 void
14105 case_tolerant_process_fromperl(pTHX_ CV *cv)
14106 {
14107   dXSARGS;
14108   ST(0) = boolSV(do_vms_case_tolerant());
14109   XSRETURN(1);
14110 }
14111 
14112 #ifdef USE_ITHREADS
14113 
14114 void
14115 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14116                           struct interp_intern *dst)
14117 {
14118     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14119 
14120     memcpy(dst,src,sizeof(struct interp_intern));
14121 }
14122 
14123 #endif
14124 
14125 void
14126 Perl_sys_intern_clear(pTHX)
14127 {
14128 }
14129 
14130 void
14131 Perl_sys_intern_init(pTHX)
14132 {
14133     unsigned int ix = RAND_MAX;
14134     double x;
14135 
14136     VMSISH_HUSHED = 0;
14137 
14138     MY_POSIX_EXIT = vms_posix_exit;
14139 
14140     x = (float)ix;
14141     MY_INV_RAND_MAX = 1./x;
14142 }
14143 
14144 void
14145 init_os_extras(void)
14146 {
14147   dTHX;
14148   char* file = __FILE__;
14149   if (decc_disable_to_vms_logname_translation) {
14150     no_translate_barewords = TRUE;
14151   } else {
14152     no_translate_barewords = FALSE;
14153   }
14154 
14155   newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14156   newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14157   newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14158   newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14159   newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14160   newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14161   newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14162   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14163   newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14164   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14165   newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14166   newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14167   newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14168   newXSproto("VMS::Filespec::case_tolerant_process",
14169       case_tolerant_process_fromperl,file,"");
14170 
14171   store_pipelocs(aTHX);         /* will redo any earlier attempts */
14172 
14173   return;
14174 }
14175 
14176 #if __CRTL_VER == 80200000
14177 /* This missed getting in to the DECC SDK for 8.2 */
14178 char *realpath(const char *file_name, char * resolved_name, ...);
14179 #endif
14180 
14181 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14182 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14183  * The perl fallback routine to provide realpath() is not as efficient
14184  * on OpenVMS.
14185  */
14186 
14187 /* Hack, use old stat() as fastest way of getting ino_t and device */
14188 int decc$stat(const char *name, void * statbuf);
14189 #if !defined(__VAX) && __CRTL_VER >= 80200000
14190 int decc$lstat(const char *name, void * statbuf);
14191 #else
14192 #define decc$lstat decc$stat
14193 #endif
14194 
14195 
14196 /* Realpath is fragile.  In 8.3 it does not work if the feature
14197  * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14198  * links are implemented in RMS, not the CRTL. It also can fail if the
14199  * user does not have read/execute access to some of the directories.
14200  * So in order for Do What I Mean mode to work, if realpath() fails,
14201  * fall back to looking up the filename by the device name and FID.
14202  */
14203 
14204 int vms_fid_to_name(char * outname, int outlen,
14205                     const char * name, int lstat_flag, mode_t * mode)
14206 {
14207 #pragma message save
14208 #pragma message disable MISALGNDSTRCT
14209 #pragma message disable MISALGNDMEM
14210 #pragma member_alignment save
14211 #pragma nomember_alignment
14212 struct statbuf_t {
14213     char	   * st_dev;
14214     unsigned short st_ino[3];
14215     unsigned short old_st_mode;
14216     unsigned long  padl[30];  /* plenty of room */
14217 } statbuf;
14218 #pragma message restore
14219 #pragma member_alignment restore
14220 
14221     int sts;
14222     struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14223     struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14224     char *fileified;
14225     char *temp_fspec;
14226     char *ret_spec;
14227 
14228     /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14229      * unexpected answers
14230      */
14231 
14232     fileified = PerlMem_malloc(VMS_MAXRSS);
14233     if (fileified == NULL)
14234         _ckvmssts_noperl(SS$_INSFMEM);
14235 
14236     temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14237     if (temp_fspec == NULL)
14238         _ckvmssts_noperl(SS$_INSFMEM);
14239 
14240     sts = -1;
14241     /* First need to try as a directory */
14242     ret_spec = int_tovmspath(name, temp_fspec, NULL);
14243     if (ret_spec != NULL) {
14244         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14245         if (ret_spec != NULL) {
14246             if (lstat_flag == 0)
14247                 sts = decc$stat(fileified, &statbuf);
14248             else
14249                 sts = decc$lstat(fileified, &statbuf);
14250         }
14251     }
14252 
14253     /* Then as a VMS file spec */
14254     if (sts != 0) {
14255         ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14256         if (ret_spec != NULL) {
14257             if (lstat_flag == 0) {
14258                 sts = decc$stat(temp_fspec, &statbuf);
14259             } else {
14260                 sts = decc$lstat(temp_fspec, &statbuf);
14261             }
14262         }
14263     }
14264 
14265     if (sts) {
14266         /* Next try - allow multiple dots with out EFS CHARSET */
14267         /* The CRTL stat() falls down hard on multi-dot filenames in unix
14268          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14269          * enable it if it isn't already.
14270          */
14271 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14272         if (!decc_efs_charset && (decc_efs_charset_index > 0))
14273             decc$feature_set_value(decc_efs_charset_index, 1, 1);
14274 #endif
14275         ret_spec = int_tovmspath(name, temp_fspec, NULL);
14276         if (lstat_flag == 0) {
14277             sts = decc$stat(name, &statbuf);
14278         } else {
14279             sts = decc$lstat(name, &statbuf);
14280         }
14281 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14282         if (!decc_efs_charset && (decc_efs_charset_index > 0))
14283             decc$feature_set_value(decc_efs_charset_index, 1, 0);
14284 #endif
14285     }
14286 
14287 
14288     /* and then because the Perl Unix to VMS conversion is not perfect */
14289     /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14290     /* characters from filenames so we need to try it as-is */
14291     if (sts) {
14292         if (lstat_flag == 0) {
14293             sts = decc$stat(name, &statbuf);
14294         } else {
14295             sts = decc$lstat(name, &statbuf);
14296         }
14297     }
14298 
14299     if (sts == 0) {
14300         int vms_sts;
14301 
14302 	dvidsc.dsc$a_pointer=statbuf.st_dev;
14303         dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14304 
14305 	specdsc.dsc$a_pointer = outname;
14306 	specdsc.dsc$w_length = outlen-1;
14307 
14308         vms_sts = lib$fid_to_name
14309 	    (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14310         if ($VMS_STATUS_SUCCESS(vms_sts)) {
14311 	    outname[specdsc.dsc$w_length] = 0;
14312 
14313             /* Return the mode */
14314             if (mode) {
14315                 *mode = statbuf.old_st_mode;
14316             }
14317 	}
14318     }
14319     PerlMem_free(temp_fspec);
14320     PerlMem_free(fileified);
14321     return sts;
14322 }
14323 
14324 
14325 
14326 static char *
14327 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14328 		   int *utf8_fl)
14329 {
14330     char * rslt = NULL;
14331 
14332 #ifdef HAS_SYMLINK
14333     if (decc_posix_compliant_pathnames > 0 ) {
14334 	/* realpath currently only works if posix compliant pathnames are
14335 	 * enabled.  It may start working when they are not, but in that
14336 	 * case we still want the fallback behavior for backwards compatibility
14337 	 */
14338         rslt = realpath(filespec, outbuf);
14339     }
14340 #endif
14341 
14342     if (rslt == NULL) {
14343         char * vms_spec;
14344         char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14345         int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14346         int file_len;
14347         mode_t my_mode;
14348 
14349 	/* Fall back to fid_to_name */
14350 
14351         Newx(vms_spec, VMS_MAXRSS + 1, char);
14352 
14353 	sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14354 	if (sts == 0) {
14355 
14356 
14357 	    /* Now need to trim the version off */
14358 	    sts = vms_split_path
14359 		  (vms_spec,
14360 		   &v_spec,
14361 		   &v_len,
14362 		   &r_spec,
14363 		   &r_len,
14364 		   &d_spec,
14365 		   &d_len,
14366 		   &n_spec,
14367 		   &n_len,
14368 		   &e_spec,
14369 		   &e_len,
14370 		   &vs_spec,
14371 		   &vs_len);
14372 
14373 
14374 		if (sts == 0) {
14375 	            int haslower = 0;
14376 	            const char *cp;
14377 
14378 	            /* Trim off the version */
14379 	            int file_len = v_len + r_len + d_len + n_len + e_len;
14380 	            vms_spec[file_len] = 0;
14381 
14382 	            /* Trim off the .DIR if this is a directory */
14383 	            if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
14384                         if (S_ISDIR(my_mode)) {
14385                             e_len = 0;
14386                             e_spec[0] = 0;
14387                         }
14388 	            }
14389 
14390 	            /* Drop NULL extensions on UNIX file specification */
14391 		    if ((e_len == 1) && decc_readdir_dropdotnotype) {
14392 			e_len = 0;
14393 			e_spec[0] = '\0';
14394 		    }
14395 
14396 	            /* The result is expected to be in UNIX format */
14397 		    rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14398 
14399                     /* Downcase if input had any lower case letters and
14400 	             * case preservation is not in effect.
14401 	             */
14402 	            if (!decc_efs_case_preserve) {
14403 	                for (cp = filespec; *cp; cp++)
14404 	                    if (islower(*cp)) { haslower = 1; break; }
14405 
14406 	                if (haslower) __mystrtolower(rslt);
14407 	            }
14408 	        }
14409 	} else {
14410 
14411 	    /* Now for some hacks to deal with backwards and forward */
14412 	    /* compatibilty */
14413 	    if (!decc_efs_charset) {
14414 
14415 		/* 1. ODS-2 mode wants to do a syntax only translation */
14416 		rslt = int_rmsexpand(filespec, outbuf,
14417 				    NULL, 0, NULL, utf8_fl);
14418 
14419 	    } else {
14420 		if (decc_filename_unix_report) {
14421 		    char * dir_name;
14422 		    char * vms_dir_name;
14423 		    char * file_name;
14424 
14425 		    /* 2. ODS-5 / UNIX report mode should return a failure */
14426 		    /*    if the parent directory also does not exist */
14427 		    /*    Otherwise, get the real path for the parent */
14428 		    /*    and add the child to it.
14429 
14430 		    /* basename / dirname only available for VMS 7.0+ */
14431 		    /* So we may need to implement them as common routines */
14432 
14433 		    Newx(dir_name, VMS_MAXRSS + 1, char);
14434 		    Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14435 		    dir_name[0] = '\0';
14436 		    file_name = NULL;
14437 
14438 		    /* First try a VMS parse */
14439 		    sts = vms_split_path
14440 			  (filespec,
14441 			   &v_spec,
14442 			   &v_len,
14443 			   &r_spec,
14444 			   &r_len,
14445 			   &d_spec,
14446 			   &d_len,
14447 			   &n_spec,
14448 			   &n_len,
14449 			   &e_spec,
14450 			   &e_len,
14451 			   &vs_spec,
14452 			   &vs_len);
14453 
14454 		    if (sts == 0) {
14455 			/* This is VMS */
14456 
14457 			int dir_len = v_len + r_len + d_len + n_len;
14458 			if (dir_len > 0) {
14459 			   strncpy(dir_name, filespec, dir_len);
14460 			   dir_name[dir_len] = '\0';
14461 			   file_name = (char *)&filespec[dir_len + 1];
14462 			}
14463 		    } else {
14464 			/* This must be UNIX */
14465 			char * tchar;
14466 
14467 			tchar = strrchr(filespec, '/');
14468 
14469 			if (tchar != NULL) {
14470 			    int dir_len = tchar - filespec;
14471 			    strncpy(dir_name, filespec, dir_len);
14472 			    dir_name[dir_len] = '\0';
14473 			    file_name = (char *) &filespec[dir_len + 1];
14474 			}
14475 		    }
14476 
14477 		    /* Dir name is defaulted */
14478 		    if (dir_name[0] == 0) {
14479 			dir_name[0] = '.';
14480 			dir_name[1] = '\0';
14481 		    }
14482 
14483 		    /* Need realpath for the directory */
14484 		    sts = vms_fid_to_name(vms_dir_name,
14485 					  VMS_MAXRSS + 1,
14486 					  dir_name, 0, NULL);
14487 
14488 		    if (sts == 0) {
14489 		        /* Now need to pathify it.
14490 		        char *tdir = int_pathify_dirspec(vms_dir_name,
14491 							 outbuf);
14492 
14493 			/* And now add the original filespec to it */
14494 			if (file_name != NULL) {
14495 			    strcat(outbuf, file_name);
14496 			}
14497 			return outbuf;
14498 		    }
14499 		    Safefree(vms_dir_name);
14500 		    Safefree(dir_name);
14501 		}
14502             }
14503         }
14504         Safefree(vms_spec);
14505     }
14506     return rslt;
14507 }
14508 
14509 static char *
14510 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14511 		   int *utf8_fl)
14512 {
14513     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14514     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14515     int file_len;
14516 
14517     /* Fall back to fid_to_name */
14518 
14519     sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14520     if (sts != 0) {
14521 	return NULL;
14522     }
14523     else {
14524 
14525 
14526 	/* Now need to trim the version off */
14527 	sts = vms_split_path
14528 		  (outbuf,
14529 		   &v_spec,
14530 		   &v_len,
14531 		   &r_spec,
14532 		   &r_len,
14533 		   &d_spec,
14534 		   &d_len,
14535 		   &n_spec,
14536 		   &n_len,
14537 		   &e_spec,
14538 		   &e_len,
14539 		   &vs_spec,
14540 		   &vs_len);
14541 
14542 
14543 	if (sts == 0) {
14544 	    int haslower = 0;
14545 	    const char *cp;
14546 
14547 	    /* Trim off the version */
14548 	    int file_len = v_len + r_len + d_len + n_len + e_len;
14549 	    outbuf[file_len] = 0;
14550 
14551 	    /* Downcase if input had any lower case letters and
14552 	     * case preservation is not in effect.
14553 	     */
14554 	    if (!decc_efs_case_preserve) {
14555 	        for (cp = filespec; *cp; cp++)
14556 	            if (islower(*cp)) { haslower = 1; break; }
14557 
14558 	        if (haslower) __mystrtolower(outbuf);
14559 	    }
14560 	}
14561     }
14562     return outbuf;
14563 }
14564 
14565 
14566 /*}}}*/
14567 /* External entry points */
14568 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14569 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14570 
14571 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14572 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14573 
14574 /* case_tolerant */
14575 
14576 /*{{{int do_vms_case_tolerant(void)*/
14577 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14578  * controlled by a process setting.
14579  */
14580 int do_vms_case_tolerant(void)
14581 {
14582     return vms_process_case_tolerant;
14583 }
14584 /*}}}*/
14585 /* External entry points */
14586 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14587 int Perl_vms_case_tolerant(void)
14588 { return do_vms_case_tolerant(); }
14589 #else
14590 int Perl_vms_case_tolerant(void)
14591 { return vms_process_case_tolerant; }
14592 #endif
14593 
14594 
14595  /* Start of DECC RTL Feature handling */
14596 
14597 static int sys_trnlnm
14598    (const char * logname,
14599     char * value,
14600     int value_len)
14601 {
14602     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14603     const unsigned long attr = LNM$M_CASE_BLIND;
14604     struct dsc$descriptor_s name_dsc;
14605     int status;
14606     unsigned short result;
14607     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14608                                 {0, 0, 0, 0}};
14609 
14610     name_dsc.dsc$w_length = strlen(logname);
14611     name_dsc.dsc$a_pointer = (char *)logname;
14612     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14613     name_dsc.dsc$b_class = DSC$K_CLASS_S;
14614 
14615     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14616 
14617     if ($VMS_STATUS_SUCCESS(status)) {
14618 
14619 	 /* Null terminate and return the string */
14620 	/*--------------------------------------*/
14621 	value[result] = 0;
14622     }
14623 
14624     return status;
14625 }
14626 
14627 static int sys_crelnm
14628    (const char * logname,
14629     const char * value)
14630 {
14631     int ret_val;
14632     const char * proc_table = "LNM$PROCESS_TABLE";
14633     struct dsc$descriptor_s proc_table_dsc;
14634     struct dsc$descriptor_s logname_dsc;
14635     struct itmlst_3 item_list[2];
14636 
14637     proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14638     proc_table_dsc.dsc$w_length = strlen(proc_table);
14639     proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14640     proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14641 
14642     logname_dsc.dsc$a_pointer = (char *) logname;
14643     logname_dsc.dsc$w_length = strlen(logname);
14644     logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14645     logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14646 
14647     item_list[0].buflen = strlen(value);
14648     item_list[0].itmcode = LNM$_STRING;
14649     item_list[0].bufadr = (char *)value;
14650     item_list[0].retlen = NULL;
14651 
14652     item_list[1].buflen = 0;
14653     item_list[1].itmcode = 0;
14654 
14655     ret_val = sys$crelnm
14656 		       (NULL,
14657 			(const struct dsc$descriptor_s *)&proc_table_dsc,
14658 			(const struct dsc$descriptor_s *)&logname_dsc,
14659 			NULL,
14660 			(const struct item_list_3 *) item_list);
14661 
14662     return ret_val;
14663 }
14664 
14665 /* C RTL Feature settings */
14666 
14667 static int set_features
14668    (int (* init_coroutine)(int *, int *, void *),  /* Needs casts if used */
14669     int (* cli_routine)(void),	/* Not documented */
14670     void *image_info)		/* Not documented */
14671 {
14672     int status;
14673     int s;
14674     char* str;
14675     char val_str[10];
14676 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14677     const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14678     const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14679     unsigned long case_perm;
14680     unsigned long case_image;
14681 #endif
14682 
14683     /* Allow an exception to bring Perl into the VMS debugger */
14684     vms_debug_on_exception = 0;
14685     status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14686     if ($VMS_STATUS_SUCCESS(status)) {
14687        val_str[0] = _toupper(val_str[0]);
14688        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14689 	 vms_debug_on_exception = 1;
14690        else
14691 	 vms_debug_on_exception = 0;
14692     }
14693 
14694     /* Debug unix/vms file translation routines */
14695     vms_debug_fileify = 0;
14696     status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14697     if ($VMS_STATUS_SUCCESS(status)) {
14698 	val_str[0] = _toupper(val_str[0]);
14699         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14700 	    vms_debug_fileify = 1;
14701         else
14702 	    vms_debug_fileify = 0;
14703     }
14704 
14705 
14706     /* Historically PERL has been doing vmsify / stat differently than */
14707     /* the CRTL.  In particular, under some conditions the CRTL will   */
14708     /* remove some illegal characters like spaces from filenames       */
14709     /* resulting in some differences.  The stat()/lstat() wrapper has  */
14710     /* been reporting such file names as invalid and fails to stat them */
14711     /* fixing this bug so that stat()/lstat() accept these like the     */
14712     /* CRTL does will result in several tests failing.                  */
14713     /* This should really be fixed, but for now, set up a feature to    */
14714     /* enable it so that the impact can be studied.                     */
14715     vms_bug_stat_filename = 0;
14716     status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14717     if ($VMS_STATUS_SUCCESS(status)) {
14718 	val_str[0] = _toupper(val_str[0]);
14719         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14720 	    vms_bug_stat_filename = 1;
14721         else
14722 	    vms_bug_stat_filename = 0;
14723     }
14724 
14725 
14726     /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14727     vms_vtf7_filenames = 0;
14728     status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14729     if ($VMS_STATUS_SUCCESS(status)) {
14730        val_str[0] = _toupper(val_str[0]);
14731        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14732 	 vms_vtf7_filenames = 1;
14733        else
14734 	 vms_vtf7_filenames = 0;
14735     }
14736 
14737     /* unlink all versions on unlink() or rename() */
14738     vms_unlink_all_versions = 0;
14739     status = sys_trnlnm
14740 	("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14741     if ($VMS_STATUS_SUCCESS(status)) {
14742        val_str[0] = _toupper(val_str[0]);
14743        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14744 	 vms_unlink_all_versions = 1;
14745        else
14746 	 vms_unlink_all_versions = 0;
14747     }
14748 
14749     /* Dectect running under GNV Bash or other UNIX like shell */
14750 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14751     gnv_unix_shell = 0;
14752     status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14753     if ($VMS_STATUS_SUCCESS(status)) {
14754 	 gnv_unix_shell = 1;
14755 	 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14756 	 set_feature_default("DECC$EFS_CHARSET", 1);
14757 	 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14758 	 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14759 	 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14760 	 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14761 	 vms_unlink_all_versions = 1;
14762 	 vms_posix_exit = 1;
14763     }
14764 #endif
14765 
14766     /* hacks to see if known bugs are still present for testing */
14767 
14768     /* PCP mode requires creating /dev/null special device file */
14769     decc_bug_devnull = 0;
14770     status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14771     if ($VMS_STATUS_SUCCESS(status)) {
14772        val_str[0] = _toupper(val_str[0]);
14773        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14774           decc_bug_devnull = 1;
14775        else
14776 	  decc_bug_devnull = 0;
14777     }
14778 
14779     /* UNIX directory names with no paths are broken in a lot of places */
14780     decc_dir_barename = 1;
14781     status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14782     if ($VMS_STATUS_SUCCESS(status)) {
14783       val_str[0] = _toupper(val_str[0]);
14784       if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14785 	decc_dir_barename = 1;
14786       else
14787 	decc_dir_barename = 0;
14788     }
14789 
14790 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14791     s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14792     if (s >= 0) {
14793 	decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14794 	if (decc_disable_to_vms_logname_translation < 0)
14795 	    decc_disable_to_vms_logname_translation = 0;
14796     }
14797 
14798     s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14799     if (s >= 0) {
14800 	decc_efs_case_preserve = decc$feature_get_value(s, 1);
14801 	if (decc_efs_case_preserve < 0)
14802 	    decc_efs_case_preserve = 0;
14803     }
14804 
14805     s = decc$feature_get_index("DECC$EFS_CHARSET");
14806     decc_efs_charset_index = s;
14807     if (s >= 0) {
14808 	decc_efs_charset = decc$feature_get_value(s, 1);
14809 	if (decc_efs_charset < 0)
14810 	    decc_efs_charset = 0;
14811     }
14812 
14813     s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14814     if (s >= 0) {
14815 	decc_filename_unix_report = decc$feature_get_value(s, 1);
14816 	if (decc_filename_unix_report > 0) {
14817 	    decc_filename_unix_report = 1;
14818 	    vms_posix_exit = 1;
14819 	}
14820 	else
14821 	    decc_filename_unix_report = 0;
14822     }
14823 
14824     s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14825     if (s >= 0) {
14826 	decc_filename_unix_only = decc$feature_get_value(s, 1);
14827 	if (decc_filename_unix_only > 0) {
14828 	    decc_filename_unix_only = 1;
14829 	}
14830 	else {
14831 	    decc_filename_unix_only = 0;
14832 	}
14833     }
14834 
14835     s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14836     if (s >= 0) {
14837 	decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14838 	if (decc_filename_unix_no_version < 0)
14839 	    decc_filename_unix_no_version = 0;
14840     }
14841 
14842     s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14843     if (s >= 0) {
14844 	decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14845 	if (decc_readdir_dropdotnotype < 0)
14846 	    decc_readdir_dropdotnotype = 0;
14847     }
14848 
14849 #if __CRTL_VER >= 80200000
14850     s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14851     if (s >= 0) {
14852 	decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14853 	if (decc_posix_compliant_pathnames < 0)
14854 	    decc_posix_compliant_pathnames = 0;
14855 	if (decc_posix_compliant_pathnames > 4)
14856 	    decc_posix_compliant_pathnames = 0;
14857     }
14858 
14859 #endif
14860 #else
14861     status = sys_trnlnm
14862 	("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14863     if ($VMS_STATUS_SUCCESS(status)) {
14864 	val_str[0] = _toupper(val_str[0]);
14865 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14866 	   decc_disable_to_vms_logname_translation = 1;
14867 	}
14868     }
14869 
14870 #ifndef __VAX
14871     status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14872     if ($VMS_STATUS_SUCCESS(status)) {
14873 	val_str[0] = _toupper(val_str[0]);
14874 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14875 	   decc_efs_case_preserve = 1;
14876 	}
14877     }
14878 #endif
14879 
14880     status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14881     if ($VMS_STATUS_SUCCESS(status)) {
14882 	val_str[0] = _toupper(val_str[0]);
14883 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14884 	   decc_filename_unix_report = 1;
14885 	}
14886     }
14887     status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14888     if ($VMS_STATUS_SUCCESS(status)) {
14889 	val_str[0] = _toupper(val_str[0]);
14890 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14891 	   decc_filename_unix_only = 1;
14892 	   decc_filename_unix_report = 1;
14893 	}
14894     }
14895     status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14896     if ($VMS_STATUS_SUCCESS(status)) {
14897 	val_str[0] = _toupper(val_str[0]);
14898 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14899 	   decc_filename_unix_no_version = 1;
14900 	}
14901     }
14902     status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14903     if ($VMS_STATUS_SUCCESS(status)) {
14904 	val_str[0] = _toupper(val_str[0]);
14905 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14906 	   decc_readdir_dropdotnotype = 1;
14907 	}
14908     }
14909 #endif
14910 
14911 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14912 
14913      /* Report true case tolerance */
14914     /*----------------------------*/
14915     status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14916     if (!$VMS_STATUS_SUCCESS(status))
14917 	case_perm = PPROP$K_CASE_BLIND;
14918     status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14919     if (!$VMS_STATUS_SUCCESS(status))
14920 	case_image = PPROP$K_CASE_BLIND;
14921     if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14922 	(case_image == PPROP$K_CASE_SENSITIVE))
14923 	vms_process_case_tolerant = 0;
14924 
14925 #endif
14926 
14927     /* USE POSIX/DCL Exit codes - Recommended, but needs to default to  */
14928     /* for strict backward compatibilty */
14929     status = sys_trnlnm
14930 	("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14931     if ($VMS_STATUS_SUCCESS(status)) {
14932        val_str[0] = _toupper(val_str[0]);
14933        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14934 	 vms_posix_exit = 1;
14935        else
14936 	 vms_posix_exit = 0;
14937     }
14938 
14939 
14940     /* CRTL can be initialized past this point, but not before. */
14941 /*    DECC$CRTL_INIT(); */
14942 
14943     return SS$_NORMAL;
14944 }
14945 
14946 #ifdef __DECC
14947 #pragma nostandard
14948 #pragma extern_model save
14949 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14950 	const __align (LONGWORD) int spare[8] = {0};
14951 
14952 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14953 #if __DECC_VER >= 60560002
14954 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14955 #else
14956 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14957 #endif
14958 #endif /* __DECC */
14959 
14960 const long vms_cc_features = (const long)set_features;
14961 
14962 /*
14963 ** Force a reference to LIB$INITIALIZE to ensure it
14964 ** exists in the image.
14965 */
14966 int lib$initialize(void);
14967 #ifdef __DECC
14968 #pragma extern_model strict_refdef
14969 #endif
14970     int lib_init_ref = (int) lib$initialize;
14971 
14972 #ifdef __DECC
14973 #pragma extern_model restore
14974 #pragma standard
14975 #endif
14976 
14977 /*  End of vms.c */
14978