xref: /openbsd-src/gnu/usr.bin/perl/vms/vms.c (revision 404b540a9034ac75a6199ad1a32d1bbc7a0d4210)
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 #define PERL_BUFSIZ        512
2889 
2890 
2891 static void
2892 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2893 {
2894   unsigned long int mbxbufsiz;
2895   static unsigned long int syssize = 0;
2896   unsigned long int dviitm = DVI$_DEVNAM;
2897   char csize[LNM$C_NAMLENGTH+1];
2898   int sts;
2899 
2900   if (!syssize) {
2901     unsigned long syiitm = SYI$_MAXBUF;
2902     /*
2903      * Get the SYSGEN parameter MAXBUF
2904      *
2905      * If the logical 'PERL_MBX_SIZE' is defined
2906      * use the value of the logical instead of PERL_BUFSIZ, but
2907      * keep the size between 128 and MAXBUF.
2908      *
2909      */
2910     _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2911   }
2912 
2913   if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2914       mbxbufsiz = atoi(csize);
2915   } else {
2916       mbxbufsiz = PERL_BUFSIZ;
2917   }
2918   if (mbxbufsiz < 128) mbxbufsiz = 128;
2919   if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2920 
2921   _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2922 
2923   sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2924   _ckvmssts_noperl(sts);
2925   namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2926 
2927 }  /* end of create_mbx() */
2928 
2929 
2930 /*{{{  my_popen and my_pclose*/
2931 
2932 typedef struct _iosb           IOSB;
2933 typedef struct _iosb*         pIOSB;
2934 typedef struct _pipe           Pipe;
2935 typedef struct _pipe*         pPipe;
2936 typedef struct pipe_details    Info;
2937 typedef struct pipe_details*  pInfo;
2938 typedef struct _srqp            RQE;
2939 typedef struct _srqp*          pRQE;
2940 typedef struct _tochildbuf      CBuf;
2941 typedef struct _tochildbuf*    pCBuf;
2942 
2943 struct _iosb {
2944     unsigned short status;
2945     unsigned short count;
2946     unsigned long  dvispec;
2947 };
2948 
2949 #pragma member_alignment save
2950 #pragma nomember_alignment quadword
2951 struct _srqp {          /* VMS self-relative queue entry */
2952     unsigned long qptr[2];
2953 };
2954 #pragma member_alignment restore
2955 static RQE  RQE_ZERO = {0,0};
2956 
2957 struct _tochildbuf {
2958     RQE             q;
2959     int             eof;
2960     unsigned short  size;
2961     char            *buf;
2962 };
2963 
2964 struct _pipe {
2965     RQE            free;
2966     RQE            wait;
2967     int            fd_out;
2968     unsigned short chan_in;
2969     unsigned short chan_out;
2970     char          *buf;
2971     unsigned int   bufsize;
2972     IOSB           iosb;
2973     IOSB           iosb2;
2974     int           *pipe_done;
2975     int            retry;
2976     int            type;
2977     int            shut_on_empty;
2978     int            need_wake;
2979     pPipe         *home;
2980     pInfo          info;
2981     pCBuf          curr;
2982     pCBuf          curr2;
2983 #if defined(PERL_IMPLICIT_CONTEXT)
2984     void	    *thx;	    /* Either a thread or an interpreter */
2985                                     /* pointer, depending on how we're built */
2986 #endif
2987 };
2988 
2989 
2990 struct pipe_details
2991 {
2992     pInfo           next;
2993     PerlIO *fp;  /* file pointer to pipe mailbox */
2994     int useFILE; /* using stdio, not perlio */
2995     int pid;   /* PID of subprocess */
2996     int mode;  /* == 'r' if pipe open for reading */
2997     int done;  /* subprocess has completed */
2998     int waiting; /* waiting for completion/closure */
2999     int             closing;        /* my_pclose is closing this pipe */
3000     unsigned long   completion;     /* termination status of subprocess */
3001     pPipe           in;             /* pipe in to sub */
3002     pPipe           out;            /* pipe out of sub */
3003     pPipe           err;            /* pipe of sub's sys$error */
3004     int             in_done;        /* true when in pipe finished */
3005     int             out_done;
3006     int             err_done;
3007     unsigned short  xchan;	    /* channel to debug xterm */
3008     unsigned short  xchan_valid;    /* channel is assigned */
3009 };
3010 
3011 struct exit_control_block
3012 {
3013     struct exit_control_block *flink;
3014     unsigned long int	(*exit_routine)();
3015     unsigned long int arg_count;
3016     unsigned long int *status_address;
3017     unsigned long int exit_status;
3018 };
3019 
3020 typedef struct _closed_pipes    Xpipe;
3021 typedef struct _closed_pipes*  pXpipe;
3022 
3023 struct _closed_pipes {
3024     int             pid;            /* PID of subprocess */
3025     unsigned long   completion;     /* termination status of subprocess */
3026 };
3027 #define NKEEPCLOSED 50
3028 static Xpipe closed_list[NKEEPCLOSED];
3029 static int   closed_index = 0;
3030 static int   closed_num = 0;
3031 
3032 #define RETRY_DELAY     "0 ::0.20"
3033 #define MAX_RETRY              50
3034 
3035 static int pipe_ef = 0;          /* first call to safe_popen inits these*/
3036 static unsigned long mypid;
3037 static unsigned long delaytime[2];
3038 
3039 static pInfo open_pipes = NULL;
3040 static $DESCRIPTOR(nl_desc, "NL:");
3041 
3042 #define PIPE_COMPLETION_WAIT    30  /* seconds, for EOF/FORCEX wait */
3043 
3044 
3045 
3046 static unsigned long int
3047 pipe_exit_routine()
3048 {
3049     pInfo info;
3050     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3051     int sts, did_stuff, need_eof, j;
3052 
3053    /*
3054     * Flush any pending i/o, but since we are in process run-down, be
3055     * careful about referencing PerlIO structures that may already have
3056     * been deallocated.  We may not even have an interpreter anymore.
3057     */
3058     info = open_pipes;
3059     while (info) {
3060         if (info->fp) {
3061 #if defined(PERL_IMPLICIT_CONTEXT)
3062            /* We need to use the Perl context of the thread that created */
3063            /* the pipe. */
3064            pTHX;
3065            if (info->err)
3066                aTHX = info->err->thx;
3067            else if (info->out)
3068                aTHX = info->out->thx;
3069            else if (info->in)
3070                aTHX = info->in->thx;
3071 #endif
3072            if (!info->useFILE
3073 #if defined(USE_ITHREADS)
3074              && my_perl
3075 #endif
3076              && PL_perlio_fd_refcnt)
3077                PerlIO_flush(info->fp);
3078            else
3079                fflush((FILE *)info->fp);
3080         }
3081         info = info->next;
3082     }
3083 
3084     /*
3085      next we try sending an EOF...ignore if doesn't work, make sure we
3086      don't hang
3087     */
3088     did_stuff = 0;
3089     info = open_pipes;
3090 
3091     while (info) {
3092       int need_eof;
3093       _ckvmssts_noperl(sys$setast(0));
3094       if (info->in && !info->in->shut_on_empty) {
3095         _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3096                                  0, 0, 0, 0, 0, 0));
3097         info->waiting = 1;
3098         did_stuff = 1;
3099       }
3100       _ckvmssts_noperl(sys$setast(1));
3101       info = info->next;
3102     }
3103 
3104     /* wait for EOF to have effect, up to ~ 30 sec [default] */
3105 
3106     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3107         int nwait = 0;
3108 
3109         info = open_pipes;
3110         while (info) {
3111           _ckvmssts_noperl(sys$setast(0));
3112           if (info->waiting && info->done)
3113                 info->waiting = 0;
3114           nwait += info->waiting;
3115           _ckvmssts_noperl(sys$setast(1));
3116           info = info->next;
3117         }
3118         if (!nwait) break;
3119         sleep(1);
3120     }
3121 
3122     did_stuff = 0;
3123     info = open_pipes;
3124     while (info) {
3125       _ckvmssts_noperl(sys$setast(0));
3126       if (!info->done) { /* Tap them gently on the shoulder . . .*/
3127         sts = sys$forcex(&info->pid,0,&abort);
3128         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3129         did_stuff = 1;
3130       }
3131       _ckvmssts_noperl(sys$setast(1));
3132       info = info->next;
3133     }
3134 
3135     /* again, wait for effect */
3136 
3137     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3138         int nwait = 0;
3139 
3140         info = open_pipes;
3141         while (info) {
3142           _ckvmssts_noperl(sys$setast(0));
3143           if (info->waiting && info->done)
3144                 info->waiting = 0;
3145           nwait += info->waiting;
3146           _ckvmssts_noperl(sys$setast(1));
3147           info = info->next;
3148         }
3149         if (!nwait) break;
3150         sleep(1);
3151     }
3152 
3153     info = open_pipes;
3154     while (info) {
3155       _ckvmssts_noperl(sys$setast(0));
3156       if (!info->done) {  /* We tried to be nice . . . */
3157         sts = sys$delprc(&info->pid,0);
3158         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3159         info->done = 1;  /* sys$delprc is as done as we're going to get. */
3160       }
3161       _ckvmssts_noperl(sys$setast(1));
3162       info = info->next;
3163     }
3164 
3165     while(open_pipes) {
3166 
3167 #if defined(PERL_IMPLICIT_CONTEXT)
3168       /* We need to use the Perl context of the thread that created */
3169       /* the pipe. */
3170       pTHX;
3171       if (open_pipes->err)
3172           aTHX = open_pipes->err->thx;
3173       else if (open_pipes->out)
3174           aTHX = open_pipes->out->thx;
3175       else if (open_pipes->in)
3176           aTHX = open_pipes->in->thx;
3177 #endif
3178       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3179       else if (!(sts & 1)) retsts = sts;
3180     }
3181     return retsts;
3182 }
3183 
3184 static struct exit_control_block pipe_exitblock =
3185        {(struct exit_control_block *) 0,
3186         pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3187 
3188 static void pipe_mbxtofd_ast(pPipe p);
3189 static void pipe_tochild1_ast(pPipe p);
3190 static void pipe_tochild2_ast(pPipe p);
3191 
3192 static void
3193 popen_completion_ast(pInfo info)
3194 {
3195   pInfo i = open_pipes;
3196   int iss;
3197   int sts;
3198   pXpipe x;
3199 
3200   info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3201   closed_list[closed_index].pid = info->pid;
3202   closed_list[closed_index].completion = info->completion;
3203   closed_index++;
3204   if (closed_index == NKEEPCLOSED)
3205     closed_index = 0;
3206   closed_num++;
3207 
3208   while (i) {
3209     if (i == info) break;
3210     i = i->next;
3211   }
3212   if (!i) return;       /* unlinked, probably freed too */
3213 
3214   info->done = TRUE;
3215 
3216 /*
3217     Writing to subprocess ...
3218             if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3219 
3220             chan_out may be waiting for "done" flag, or hung waiting
3221             for i/o completion to child...cancel the i/o.  This will
3222             put it into "snarf mode" (done but no EOF yet) that discards
3223             input.
3224 
3225     Output from subprocess (stdout, stderr) needs to be flushed and
3226     shut down.   We try sending an EOF, but if the mbx is full the pipe
3227     routine should still catch the "shut_on_empty" flag, telling it to
3228     use immediate-style reads so that "mbx empty" -> EOF.
3229 
3230 
3231 */
3232   if (info->in && !info->in_done) {               /* only for mode=w */
3233         if (info->in->shut_on_empty && info->in->need_wake) {
3234             info->in->need_wake = FALSE;
3235             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3236         } else {
3237             _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3238         }
3239   }
3240 
3241   if (info->out && !info->out_done) {             /* were we also piping output? */
3242       info->out->shut_on_empty = TRUE;
3243       iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3244       if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3245       _ckvmssts_noperl(iss);
3246   }
3247 
3248   if (info->err && !info->err_done) {        /* we were piping stderr */
3249         info->err->shut_on_empty = TRUE;
3250         iss = sys$qio(0,info->err->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   _ckvmssts_noperl(sys$setef(pipe_ef));
3255 
3256 }
3257 
3258 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3259 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3260 
3261 /*
3262     we actually differ from vmstrnenv since we use this to
3263     get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3264     are pointing to the same thing
3265 */
3266 
3267 static unsigned short
3268 popen_translate(pTHX_ char *logical, char *result)
3269 {
3270     int iss;
3271     $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3272     $DESCRIPTOR(d_log,"");
3273     struct _il3 {
3274         unsigned short length;
3275         unsigned short code;
3276         char *         buffer_addr;
3277         unsigned short *retlenaddr;
3278     } itmlst[2];
3279     unsigned short l, ifi;
3280 
3281     d_log.dsc$a_pointer = logical;
3282     d_log.dsc$w_length  = strlen(logical);
3283 
3284     itmlst[0].code = LNM$_STRING;
3285     itmlst[0].length = 255;
3286     itmlst[0].buffer_addr = result;
3287     itmlst[0].retlenaddr = &l;
3288 
3289     itmlst[1].code = 0;
3290     itmlst[1].length = 0;
3291     itmlst[1].buffer_addr = 0;
3292     itmlst[1].retlenaddr = 0;
3293 
3294     iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3295     if (iss == SS$_NOLOGNAM) {
3296         iss = SS$_NORMAL;
3297         l = 0;
3298     }
3299     if (!(iss&1)) lib$signal(iss);
3300     result[l] = '\0';
3301 /*
3302     logicals for PPFs have a 4 byte prefix  ESC+NUL+(RMS IFI)
3303     strip it off and return the ifi, if any
3304 */
3305     ifi  = 0;
3306     if (result[0] == 0x1b && result[1] == 0x00) {
3307         memmove(&ifi,result+2,2);
3308         strcpy(result,result+4);
3309     }
3310     return ifi;     /* this is the RMS internal file id */
3311 }
3312 
3313 static void pipe_infromchild_ast(pPipe p);
3314 
3315 /*
3316     I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3317     inside an AST routine without worrying about reentrancy and which Perl
3318     memory allocator is being used.
3319 
3320     We read data and queue up the buffers, then spit them out one at a
3321     time to the output mailbox when the output mailbox is ready for one.
3322 
3323 */
3324 #define INITIAL_TOCHILDQUEUE  2
3325 
3326 static pPipe
3327 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3328 {
3329     pPipe p;
3330     pCBuf b;
3331     char mbx1[64], mbx2[64];
3332     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3333                                       DSC$K_CLASS_S, mbx1},
3334                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3335                                       DSC$K_CLASS_S, mbx2};
3336     unsigned int dviitm = DVI$_DEVBUFSIZ;
3337     int j, n;
3338 
3339     n = sizeof(Pipe);
3340     _ckvmssts_noperl(lib$get_vm(&n, &p));
3341 
3342     create_mbx(&p->chan_in , &d_mbx1);
3343     create_mbx(&p->chan_out, &d_mbx2);
3344     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3345 
3346     p->buf           = 0;
3347     p->shut_on_empty = FALSE;
3348     p->need_wake     = FALSE;
3349     p->type          = 0;
3350     p->retry         = 0;
3351     p->iosb.status   = SS$_NORMAL;
3352     p->iosb2.status  = SS$_NORMAL;
3353     p->free          = RQE_ZERO;
3354     p->wait          = RQE_ZERO;
3355     p->curr          = 0;
3356     p->curr2         = 0;
3357     p->info          = 0;
3358 #ifdef PERL_IMPLICIT_CONTEXT
3359     p->thx	     = aTHX;
3360 #endif
3361 
3362     n = sizeof(CBuf) + p->bufsize;
3363 
3364     for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3365         _ckvmssts_noperl(lib$get_vm(&n, &b));
3366         b->buf = (char *) b + sizeof(CBuf);
3367         _ckvmssts_noperl(lib$insqhi(b, &p->free));
3368     }
3369 
3370     pipe_tochild2_ast(p);
3371     pipe_tochild1_ast(p);
3372     strcpy(wmbx, mbx1);
3373     strcpy(rmbx, mbx2);
3374     return p;
3375 }
3376 
3377 /*  reads the MBX Perl is writing, and queues */
3378 
3379 static void
3380 pipe_tochild1_ast(pPipe p)
3381 {
3382     pCBuf b = p->curr;
3383     int iss = p->iosb.status;
3384     int eof = (iss == SS$_ENDOFFILE);
3385     int sts;
3386 #ifdef PERL_IMPLICIT_CONTEXT
3387     pTHX = p->thx;
3388 #endif
3389 
3390     if (p->retry) {
3391         if (eof) {
3392             p->shut_on_empty = TRUE;
3393             b->eof     = TRUE;
3394             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3395         } else  {
3396             _ckvmssts_noperl(iss);
3397         }
3398 
3399         b->eof  = eof;
3400         b->size = p->iosb.count;
3401         _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3402         if (p->need_wake) {
3403             p->need_wake = FALSE;
3404             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3405         }
3406     } else {
3407         p->retry = 1;   /* initial call */
3408     }
3409 
3410     if (eof) {                  /* flush the free queue, return when done */
3411         int n = sizeof(CBuf) + p->bufsize;
3412         while (1) {
3413             iss = lib$remqti(&p->free, &b);
3414             if (iss == LIB$_QUEWASEMP) return;
3415             _ckvmssts_noperl(iss);
3416             _ckvmssts_noperl(lib$free_vm(&n, &b));
3417         }
3418     }
3419 
3420     iss = lib$remqti(&p->free, &b);
3421     if (iss == LIB$_QUEWASEMP) {
3422         int n = sizeof(CBuf) + p->bufsize;
3423         _ckvmssts_noperl(lib$get_vm(&n, &b));
3424         b->buf = (char *) b + sizeof(CBuf);
3425     } else {
3426        _ckvmssts_noperl(iss);
3427     }
3428 
3429     p->curr = b;
3430     iss = sys$qio(0,p->chan_in,
3431              IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3432              &p->iosb,
3433              pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3434     if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3435     _ckvmssts_noperl(iss);
3436 }
3437 
3438 
3439 /* writes queued buffers to output, waits for each to complete before
3440    doing the next */
3441 
3442 static void
3443 pipe_tochild2_ast(pPipe p)
3444 {
3445     pCBuf b = p->curr2;
3446     int iss = p->iosb2.status;
3447     int n = sizeof(CBuf) + p->bufsize;
3448     int done = (p->info && p->info->done) ||
3449               iss == SS$_CANCEL || iss == SS$_ABORT;
3450 #if defined(PERL_IMPLICIT_CONTEXT)
3451     pTHX = p->thx;
3452 #endif
3453 
3454     do {
3455         if (p->type) {         /* type=1 has old buffer, dispose */
3456             if (p->shut_on_empty) {
3457                 _ckvmssts_noperl(lib$free_vm(&n, &b));
3458             } else {
3459                 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3460             }
3461             p->type = 0;
3462         }
3463 
3464         iss = lib$remqti(&p->wait, &b);
3465         if (iss == LIB$_QUEWASEMP) {
3466             if (p->shut_on_empty) {
3467                 if (done) {
3468                     _ckvmssts_noperl(sys$dassgn(p->chan_out));
3469                     *p->pipe_done = TRUE;
3470                     _ckvmssts_noperl(sys$setef(pipe_ef));
3471                 } else {
3472                     _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3473                         &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3474                 }
3475                 return;
3476             }
3477             p->need_wake = TRUE;
3478             return;
3479         }
3480         _ckvmssts_noperl(iss);
3481         p->type = 1;
3482     } while (done);
3483 
3484 
3485     p->curr2 = b;
3486     if (b->eof) {
3487         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3488             &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3489     } else {
3490         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3491             &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3492     }
3493 
3494     return;
3495 
3496 }
3497 
3498 
3499 static pPipe
3500 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3501 {
3502     pPipe p;
3503     char mbx1[64], mbx2[64];
3504     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3505                                       DSC$K_CLASS_S, mbx1},
3506                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3507                                       DSC$K_CLASS_S, mbx2};
3508     unsigned int dviitm = DVI$_DEVBUFSIZ;
3509 
3510     int n = sizeof(Pipe);
3511     _ckvmssts_noperl(lib$get_vm(&n, &p));
3512     create_mbx(&p->chan_in , &d_mbx1);
3513     create_mbx(&p->chan_out, &d_mbx2);
3514 
3515     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3516     n = p->bufsize * sizeof(char);
3517     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3518     p->shut_on_empty = FALSE;
3519     p->info   = 0;
3520     p->type   = 0;
3521     p->iosb.status = SS$_NORMAL;
3522 #if defined(PERL_IMPLICIT_CONTEXT)
3523     p->thx = aTHX;
3524 #endif
3525     pipe_infromchild_ast(p);
3526 
3527     strcpy(wmbx, mbx1);
3528     strcpy(rmbx, mbx2);
3529     return p;
3530 }
3531 
3532 static void
3533 pipe_infromchild_ast(pPipe p)
3534 {
3535     int iss = p->iosb.status;
3536     int eof = (iss == SS$_ENDOFFILE);
3537     int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3538     int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3539 #if defined(PERL_IMPLICIT_CONTEXT)
3540     pTHX = p->thx;
3541 #endif
3542 
3543     if (p->info && p->info->closing && p->chan_out)  {           /* output shutdown */
3544         _ckvmssts_noperl(sys$dassgn(p->chan_out));
3545         p->chan_out = 0;
3546     }
3547 
3548     /* read completed:
3549             input shutdown if EOF from self (done or shut_on_empty)
3550             output shutdown if closing flag set (my_pclose)
3551             send data/eof from child or eof from self
3552             otherwise, re-read (snarf of data from child)
3553     */
3554 
3555     if (p->type == 1) {
3556         p->type = 0;
3557         if (myeof && p->chan_in) {                  /* input shutdown */
3558             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3559             p->chan_in = 0;
3560         }
3561 
3562         if (p->chan_out) {
3563             if (myeof || kideof) {      /* pass EOF to parent */
3564                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3565                                          pipe_infromchild_ast, p,
3566                                          0, 0, 0, 0, 0, 0));
3567                 return;
3568             } else if (eof) {       /* eat EOF --- fall through to read*/
3569 
3570             } else {                /* transmit data */
3571                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3572                                          pipe_infromchild_ast,p,
3573                                          p->buf, p->iosb.count, 0, 0, 0, 0));
3574                 return;
3575             }
3576         }
3577     }
3578 
3579     /*  everything shut? flag as done */
3580 
3581     if (!p->chan_in && !p->chan_out) {
3582         *p->pipe_done = TRUE;
3583         _ckvmssts_noperl(sys$setef(pipe_ef));
3584         return;
3585     }
3586 
3587     /* write completed (or read, if snarfing from child)
3588             if still have input active,
3589                queue read...immediate mode if shut_on_empty so we get EOF if empty
3590             otherwise,
3591                check if Perl reading, generate EOFs as needed
3592     */
3593 
3594     if (p->type == 0) {
3595         p->type = 1;
3596         if (p->chan_in) {
3597             iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3598                           pipe_infromchild_ast,p,
3599                           p->buf, p->bufsize, 0, 0, 0, 0);
3600             if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3601             _ckvmssts_noperl(iss);
3602         } else {           /* send EOFs for extra reads */
3603             p->iosb.status = SS$_ENDOFFILE;
3604             p->iosb.dvispec = 0;
3605             _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3606                                      0, 0, 0,
3607                                      pipe_infromchild_ast, p, 0, 0, 0, 0));
3608         }
3609     }
3610 }
3611 
3612 static pPipe
3613 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3614 {
3615     pPipe p;
3616     char mbx[64];
3617     unsigned long dviitm = DVI$_DEVBUFSIZ;
3618     struct stat s;
3619     struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3620                                       DSC$K_CLASS_S, mbx};
3621     int n = sizeof(Pipe);
3622 
3623     /* things like terminals and mbx's don't need this filter */
3624     if (fd && fstat(fd,&s) == 0) {
3625         unsigned long dviitm = DVI$_DEVCHAR, devchar;
3626 	char device[65];
3627 	unsigned short dev_len;
3628 	struct dsc$descriptor_s d_dev;
3629 	char * cptr;
3630 	struct item_list_3 items[3];
3631 	int status;
3632 	unsigned short dvi_iosb[4];
3633 
3634 	cptr = getname(fd, out, 1);
3635 	if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3636 	d_dev.dsc$a_pointer = out;
3637 	d_dev.dsc$w_length = strlen(out);
3638 	d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3639 	d_dev.dsc$b_class = DSC$K_CLASS_S;
3640 
3641 	items[0].len = 4;
3642 	items[0].code = DVI$_DEVCHAR;
3643 	items[0].bufadr = &devchar;
3644 	items[0].retadr = NULL;
3645 	items[1].len = 64;
3646 	items[1].code = DVI$_FULLDEVNAM;
3647 	items[1].bufadr = device;
3648 	items[1].retadr = &dev_len;
3649 	items[2].len = 0;
3650 	items[2].code = 0;
3651 
3652 	status = sys$getdviw
3653 	        (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3654 	_ckvmssts_noperl(status);
3655 	if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3656 	    device[dev_len] = 0;
3657 
3658 	    if (!(devchar & DEV$M_DIR)) {
3659 		strcpy(out, device);
3660 		return 0;
3661 	    }
3662 	}
3663     }
3664 
3665     _ckvmssts_noperl(lib$get_vm(&n, &p));
3666     p->fd_out = dup(fd);
3667     create_mbx(&p->chan_in, &d_mbx);
3668     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3669     n = (p->bufsize+1) * sizeof(char);
3670     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3671     p->shut_on_empty = FALSE;
3672     p->retry = 0;
3673     p->info  = 0;
3674     strcpy(out, mbx);
3675 
3676     _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3677                              pipe_mbxtofd_ast, p,
3678                              p->buf, p->bufsize, 0, 0, 0, 0));
3679 
3680     return p;
3681 }
3682 
3683 static void
3684 pipe_mbxtofd_ast(pPipe p)
3685 {
3686     int iss = p->iosb.status;
3687     int done = p->info->done;
3688     int iss2;
3689     int eof = (iss == SS$_ENDOFFILE);
3690     int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3691     int err = !(iss&1) && !eof;
3692 #if defined(PERL_IMPLICIT_CONTEXT)
3693     pTHX = p->thx;
3694 #endif
3695 
3696     if (done && myeof) {               /* end piping */
3697         close(p->fd_out);
3698         sys$dassgn(p->chan_in);
3699         *p->pipe_done = TRUE;
3700         _ckvmssts_noperl(sys$setef(pipe_ef));
3701         return;
3702     }
3703 
3704     if (!err && !eof) {             /* good data to send to file */
3705         p->buf[p->iosb.count] = '\n';
3706         iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3707         if (iss2 < 0) {
3708             p->retry++;
3709             if (p->retry < MAX_RETRY) {
3710                 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3711                 return;
3712             }
3713         }
3714         p->retry = 0;
3715     } else if (err) {
3716         _ckvmssts_noperl(iss);
3717     }
3718 
3719 
3720     iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3721           pipe_mbxtofd_ast, p,
3722           p->buf, p->bufsize, 0, 0, 0, 0);
3723     if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3724     _ckvmssts_noperl(iss);
3725 }
3726 
3727 
3728 typedef struct _pipeloc     PLOC;
3729 typedef struct _pipeloc*   pPLOC;
3730 
3731 struct _pipeloc {
3732     pPLOC   next;
3733     char    dir[NAM$C_MAXRSS+1];
3734 };
3735 static pPLOC  head_PLOC = 0;
3736 
3737 void
3738 free_pipelocs(pTHX_ void *head)
3739 {
3740     pPLOC p, pnext;
3741     pPLOC *pHead = (pPLOC *)head;
3742 
3743     p = *pHead;
3744     while (p) {
3745         pnext = p->next;
3746         PerlMem_free(p);
3747         p = pnext;
3748     }
3749     *pHead = 0;
3750 }
3751 
3752 static void
3753 store_pipelocs(pTHX)
3754 {
3755     int    i;
3756     pPLOC  p;
3757     AV    *av = 0;
3758     SV    *dirsv;
3759     GV    *gv;
3760     char  *dir, *x;
3761     char  *unixdir;
3762     char  temp[NAM$C_MAXRSS+1];
3763     STRLEN n_a;
3764 
3765     if (head_PLOC)
3766         free_pipelocs(aTHX_ &head_PLOC);
3767 
3768 /*  the . directory from @INC comes last */
3769 
3770     p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3771     if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3772     p->next = head_PLOC;
3773     head_PLOC = p;
3774     strcpy(p->dir,"./");
3775 
3776 /*  get the directory from $^X */
3777 
3778     unixdir = PerlMem_malloc(VMS_MAXRSS);
3779     if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3780 
3781 #ifdef PERL_IMPLICIT_CONTEXT
3782     if (aTHX && PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3783 #else
3784     if (PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3785 #endif
3786         strcpy(temp, PL_origargv[0]);
3787         x = strrchr(temp,']');
3788 	if (x == NULL) {
3789 	x = strrchr(temp,'>');
3790 	  if (x == NULL) {
3791 	    /* It could be a UNIX path */
3792 	    x = strrchr(temp,'/');
3793 	  }
3794 	}
3795 	if (x)
3796 	  x[1] = '\0';
3797 	else {
3798 	  /* Got a bare name, so use default directory */
3799 	  temp[0] = '.';
3800 	  temp[1] = '\0';
3801 	}
3802 
3803         if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3804             p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3805 	    if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3806             p->next = head_PLOC;
3807             head_PLOC = p;
3808             strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3809             p->dir[NAM$C_MAXRSS] = '\0';
3810 	}
3811     }
3812 
3813 /*  reverse order of @INC entries, skip "." since entered above */
3814 
3815 #ifdef PERL_IMPLICIT_CONTEXT
3816     if (aTHX)
3817 #endif
3818     if (PL_incgv) av = GvAVn(PL_incgv);
3819 
3820     for (i = 0; av && i <= AvFILL(av); i++) {
3821         dirsv = *av_fetch(av,i,TRUE);
3822 
3823         if (SvROK(dirsv)) continue;
3824         dir = SvPVx(dirsv,n_a);
3825         if (strcmp(dir,".") == 0) continue;
3826         if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3827             continue;
3828 
3829         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3830         p->next = head_PLOC;
3831         head_PLOC = p;
3832         strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3833         p->dir[NAM$C_MAXRSS] = '\0';
3834     }
3835 
3836 /* most likely spot (ARCHLIB) put first in the list */
3837 
3838 #ifdef ARCHLIB_EXP
3839     if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3840         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3841 	if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3842         p->next = head_PLOC;
3843         head_PLOC = p;
3844         strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3845         p->dir[NAM$C_MAXRSS] = '\0';
3846     }
3847 #endif
3848     PerlMem_free(unixdir);
3849 }
3850 
3851 static I32
3852 Perl_cando_by_name_int
3853    (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3854 #if !defined(PERL_IMPLICIT_CONTEXT)
3855 #define cando_by_name_int		Perl_cando_by_name_int
3856 #else
3857 #define cando_by_name_int(a,b,c,d)	Perl_cando_by_name_int(aTHX_ a,b,c,d)
3858 #endif
3859 
3860 static char *
3861 find_vmspipe(pTHX)
3862 {
3863     static int   vmspipe_file_status = 0;
3864     static char  vmspipe_file[NAM$C_MAXRSS+1];
3865 
3866     /* already found? Check and use ... need read+execute permission */
3867 
3868     if (vmspipe_file_status == 1) {
3869         if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3870          && cando_by_name_int
3871 	   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3872             return vmspipe_file;
3873         }
3874         vmspipe_file_status = 0;
3875     }
3876 
3877     /* scan through stored @INC, $^X */
3878 
3879     if (vmspipe_file_status == 0) {
3880         char file[NAM$C_MAXRSS+1];
3881         pPLOC  p = head_PLOC;
3882 
3883         while (p) {
3884 	    char * exp_res;
3885 	    int dirlen;
3886             strcpy(file, p->dir);
3887 	    dirlen = strlen(file);
3888             strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3889             file[NAM$C_MAXRSS] = '\0';
3890             p = p->next;
3891 
3892             exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3893             if (!exp_res) continue;
3894 
3895             if (cando_by_name_int
3896 		(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3897              && cando_by_name_int
3898 		   (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3899                 vmspipe_file_status = 1;
3900                 return vmspipe_file;
3901             }
3902         }
3903         vmspipe_file_status = -1;   /* failed, use tempfiles */
3904     }
3905 
3906     return 0;
3907 }
3908 
3909 static FILE *
3910 vmspipe_tempfile(pTHX)
3911 {
3912     char file[NAM$C_MAXRSS+1];
3913     FILE *fp;
3914     static int index = 0;
3915     Stat_t s0, s1;
3916     int cmp_result;
3917 
3918     /* create a tempfile */
3919 
3920     /* we can't go from   W, shr=get to  R, shr=get without
3921        an intermediate vulnerable state, so don't bother trying...
3922 
3923        and lib$spawn doesn't shr=put, so have to close the write
3924 
3925        So... match up the creation date/time and the FID to
3926        make sure we're dealing with the same file
3927 
3928     */
3929 
3930     index++;
3931     if (!decc_filename_unix_only) {
3932       sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3933       fp = fopen(file,"w");
3934       if (!fp) {
3935         sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3936         fp = fopen(file,"w");
3937         if (!fp) {
3938             sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3939             fp = fopen(file,"w");
3940 	}
3941       }
3942      }
3943      else {
3944       sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3945       fp = fopen(file,"w");
3946       if (!fp) {
3947 	sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3948 	fp = fopen(file,"w");
3949 	if (!fp) {
3950 	  sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3951 	  fp = fopen(file,"w");
3952 	}
3953       }
3954     }
3955     if (!fp) return 0;  /* we're hosed */
3956 
3957     fprintf(fp,"$! 'f$verify(0)'\n");
3958     fprintf(fp,"$!  ---  protect against nonstandard definitions ---\n");
3959     fprintf(fp,"$ perl_cfile  = f$environment(\"procedure\")\n");
3960     fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3961     fprintf(fp,"$ perl_on     = \"set noon\"\n");
3962     fprintf(fp,"$ perl_exit   = \"exit\"\n");
3963     fprintf(fp,"$ perl_del    = \"delete\"\n");
3964     fprintf(fp,"$ pif         = \"if\"\n");
3965     fprintf(fp,"$!  --- define i/o redirection (sys$output set by lib$spawn)\n");
3966     fprintf(fp,"$ pif perl_popen_in  .nes. \"\" then perl_define/user/name_attributes=confine sys$input  'perl_popen_in'\n");
3967     fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error  'perl_popen_err'\n");
3968     fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define      sys$output 'perl_popen_out'\n");
3969     fprintf(fp,"$!  --- build command line to get max possible length\n");
3970     fprintf(fp,"$c=perl_popen_cmd0\n");
3971     fprintf(fp,"$c=c+perl_popen_cmd1\n");
3972     fprintf(fp,"$c=c+perl_popen_cmd2\n");
3973     fprintf(fp,"$x=perl_popen_cmd3\n");
3974     fprintf(fp,"$c=c+x\n");
3975     fprintf(fp,"$ perl_on\n");
3976     fprintf(fp,"$ 'c'\n");
3977     fprintf(fp,"$ perl_status = $STATUS\n");
3978     fprintf(fp,"$ perl_del  'perl_cfile'\n");
3979     fprintf(fp,"$ perl_exit 'perl_status'\n");
3980     fsync(fileno(fp));
3981 
3982     fgetname(fp, file, 1);
3983     fstat(fileno(fp), &s0.crtl_stat);
3984     fclose(fp);
3985 
3986     if (decc_filename_unix_only)
3987 	int_tounixspec(file, file, NULL);
3988     fp = fopen(file,"r","shr=get");
3989     if (!fp) return 0;
3990     fstat(fileno(fp), &s1.crtl_stat);
3991 
3992     cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3993     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
3994         fclose(fp);
3995         return 0;
3996     }
3997 
3998     return fp;
3999 }
4000 
4001 
4002 static int vms_is_syscommand_xterm(void)
4003 {
4004     const static struct dsc$descriptor_s syscommand_dsc =
4005       { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
4006 
4007     const static struct dsc$descriptor_s decwdisplay_dsc =
4008       { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
4009 
4010     struct item_list_3 items[2];
4011     unsigned short dvi_iosb[4];
4012     unsigned long devchar;
4013     unsigned long devclass;
4014     int status;
4015 
4016     /* Very simple check to guess if sys$command is a decterm? */
4017     /* First see if the DECW$DISPLAY: device exists */
4018     items[0].len = 4;
4019     items[0].code = DVI$_DEVCHAR;
4020     items[0].bufadr = &devchar;
4021     items[0].retadr = NULL;
4022     items[1].len = 0;
4023     items[1].code = 0;
4024 
4025     status = sys$getdviw
4026 	(NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
4027 
4028     if ($VMS_STATUS_SUCCESS(status)) {
4029         status = dvi_iosb[0];
4030     }
4031 
4032     if (!$VMS_STATUS_SUCCESS(status)) {
4033         SETERRNO(EVMSERR, status);
4034 	return -1;
4035     }
4036 
4037     /* If it does, then for now assume that we are on a workstation */
4038     /* Now verify that SYS$COMMAND is a terminal */
4039     /* for creating the debugger DECTerm */
4040 
4041     items[0].len = 4;
4042     items[0].code = DVI$_DEVCLASS;
4043     items[0].bufadr = &devclass;
4044     items[0].retadr = NULL;
4045     items[1].len = 0;
4046     items[1].code = 0;
4047 
4048     status = sys$getdviw
4049 	(NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
4050 
4051     if ($VMS_STATUS_SUCCESS(status)) {
4052         status = dvi_iosb[0];
4053     }
4054 
4055     if (!$VMS_STATUS_SUCCESS(status)) {
4056         SETERRNO(EVMSERR, status);
4057 	return -1;
4058     }
4059     else {
4060 	if (devclass == DC$_TERM) {
4061 	    return 0;
4062 	}
4063     }
4064     return -1;
4065 }
4066 
4067 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
4068 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
4069 {
4070     int status;
4071     int ret_stat;
4072     char * ret_char;
4073     char device_name[65];
4074     unsigned short device_name_len;
4075     struct dsc$descriptor_s customization_dsc;
4076     struct dsc$descriptor_s device_name_dsc;
4077     const char * cptr;
4078     char * tptr;
4079     char customization[200];
4080     char title[40];
4081     pInfo info = NULL;
4082     char mbx1[64];
4083     unsigned short p_chan;
4084     int n;
4085     unsigned short iosb[4];
4086     struct item_list_3 items[2];
4087     const char * cust_str =
4088         "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4089     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4090                                           DSC$K_CLASS_S, mbx1};
4091 
4092      /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4093     /*---------------------------------------*/
4094     VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4095 
4096 
4097     /* Make sure that this is from the Perl debugger */
4098     ret_char = strstr(cmd," xterm ");
4099     if (ret_char == NULL)
4100 	return NULL;
4101     cptr = ret_char + 7;
4102     ret_char = strstr(cmd,"tty");
4103     if (ret_char == NULL)
4104 	return NULL;
4105     ret_char = strstr(cmd,"sleep");
4106     if (ret_char == NULL)
4107 	return NULL;
4108 
4109     if (decw_term_port == 0) {
4110 	$DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4111 	$DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4112 	$DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4113 
4114        status = lib$find_image_symbol
4115 			       (&filename1_dsc,
4116 				&decw_term_port_dsc,
4117 				(void *)&decw_term_port,
4118 				NULL,
4119 				0);
4120 
4121 	/* Try again with the other image name */
4122 	if (!$VMS_STATUS_SUCCESS(status)) {
4123 
4124            status = lib$find_image_symbol
4125 			       (&filename2_dsc,
4126 				&decw_term_port_dsc,
4127 				(void *)&decw_term_port,
4128 				NULL,
4129 				0);
4130 
4131 	}
4132 
4133     }
4134 
4135 
4136     /* No decw$term_port, give it up */
4137     if (!$VMS_STATUS_SUCCESS(status))
4138 	return NULL;
4139 
4140     /* Are we on a workstation? */
4141     /* to do: capture the rows / columns and pass their properties */
4142     ret_stat = vms_is_syscommand_xterm();
4143     if (ret_stat < 0)
4144 	return NULL;
4145 
4146     /* Make the title: */
4147     ret_char = strstr(cptr,"-title");
4148     if (ret_char != NULL) {
4149 	while ((*cptr != 0) && (*cptr != '\"')) {
4150 	    cptr++;
4151 	}
4152 	if (*cptr == '\"')
4153 	    cptr++;
4154 	n = 0;
4155 	while ((*cptr != 0) && (*cptr != '\"')) {
4156 	    title[n] = *cptr;
4157 	    n++;
4158 	    if (n == 39) {
4159 		title[39] == 0;
4160 		break;
4161 	    }
4162 	    cptr++;
4163 	}
4164 	title[n] = 0;
4165     }
4166     else {
4167 	    /* Default title */
4168 	    strcpy(title,"Perl Debug DECTerm");
4169     }
4170     sprintf(customization, cust_str, title);
4171 
4172     customization_dsc.dsc$a_pointer = customization;
4173     customization_dsc.dsc$w_length = strlen(customization);
4174     customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4175     customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4176 
4177     device_name_dsc.dsc$a_pointer = device_name;
4178     device_name_dsc.dsc$w_length = sizeof device_name -1;
4179     device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4180     device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4181 
4182     device_name_len = 0;
4183 
4184     /* Try to create the window */
4185      status = (*decw_term_port)
4186        (NULL,
4187 	NULL,
4188 	&customization_dsc,
4189 	&device_name_dsc,
4190 	&device_name_len,
4191 	NULL,
4192 	NULL,
4193 	NULL);
4194     if (!$VMS_STATUS_SUCCESS(status)) {
4195         SETERRNO(EVMSERR, status);
4196 	return NULL;
4197     }
4198 
4199     device_name[device_name_len] = '\0';
4200 
4201     /* Need to set this up to look like a pipe for cleanup */
4202     n = sizeof(Info);
4203     status = lib$get_vm(&n, &info);
4204     if (!$VMS_STATUS_SUCCESS(status)) {
4205         SETERRNO(ENOMEM, status);
4206         return NULL;
4207     }
4208 
4209     info->mode = *mode;
4210     info->done = FALSE;
4211     info->completion = 0;
4212     info->closing    = FALSE;
4213     info->in         = 0;
4214     info->out        = 0;
4215     info->err        = 0;
4216     info->fp         = NULL;
4217     info->useFILE    = 0;
4218     info->waiting    = 0;
4219     info->in_done    = TRUE;
4220     info->out_done   = TRUE;
4221     info->err_done   = TRUE;
4222 
4223     /* Assign a channel on this so that it will persist, and not login */
4224     /* We stash this channel in the info structure for reference. */
4225     /* The created xterm self destructs when the last channel is removed */
4226     /* and it appears that perl5db.pl (perl debugger) does this routinely */
4227     /* So leave this assigned. */
4228     device_name_dsc.dsc$w_length = device_name_len;
4229     status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4230     if (!$VMS_STATUS_SUCCESS(status)) {
4231         SETERRNO(EVMSERR, status);
4232 	return NULL;
4233     }
4234     info->xchan_valid = 1;
4235 
4236     /* Now create a mailbox to be read by the application */
4237 
4238     create_mbx(&p_chan, &d_mbx1);
4239 
4240     /* write the name of the created terminal to the mailbox */
4241     status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4242             iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4243 
4244     if (!$VMS_STATUS_SUCCESS(status)) {
4245         SETERRNO(EVMSERR, status);
4246 	return NULL;
4247     }
4248 
4249     info->fp  = PerlIO_open(mbx1, mode);
4250 
4251     /* Done with this channel */
4252     sys$dassgn(p_chan);
4253 
4254     /* If any errors, then clean up */
4255     if (!info->fp) {
4256        	n = sizeof(Info);
4257 	_ckvmssts_noperl(lib$free_vm(&n, &info));
4258 	return NULL;
4259         }
4260 
4261     /* All done */
4262     return info->fp;
4263 }
4264 
4265 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4266 
4267 static PerlIO *
4268 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4269 {
4270     static int handler_set_up = FALSE;
4271     PerlIO * ret_fp;
4272     unsigned long int sts, flags = CLI$M_NOWAIT;
4273     /* The use of a GLOBAL table (as was done previously) rendered
4274      * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4275      * environment.  Hence we've switched to LOCAL symbol table.
4276      */
4277     unsigned int table = LIB$K_CLI_LOCAL_SYM;
4278     int j, wait = 0, n;
4279     char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4280     char *in, *out, *err, mbx[512];
4281     FILE *tpipe = 0;
4282     char tfilebuf[NAM$C_MAXRSS+1];
4283     pInfo info = NULL;
4284     char cmd_sym_name[20];
4285     struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4286                                       DSC$K_CLASS_S, symbol};
4287     struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4288                                       DSC$K_CLASS_S, 0};
4289     struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4290                                       DSC$K_CLASS_S, cmd_sym_name};
4291     struct dsc$descriptor_s *vmscmd;
4292     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4293     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4294     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4295 
4296     /* Check here for Xterm create request.  This means looking for
4297      * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4298      *  is possible to create an xterm.
4299      */
4300     if (*in_mode == 'r') {
4301         PerlIO * xterm_fd;
4302 
4303 #if defined(PERL_IMPLICIT_CONTEXT)
4304         /* Can not fork an xterm with a NULL context */
4305         /* This probably could never happen */
4306         xterm_fd = NULL;
4307         if (aTHX != NULL)
4308 #endif
4309 	xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4310 	if (xterm_fd != NULL)
4311 	    return xterm_fd;
4312     }
4313 
4314     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static vmspipe file */
4315 
4316     /* once-per-program initialization...
4317        note that the SETAST calls and the dual test of pipe_ef
4318        makes sure that only the FIRST thread through here does
4319        the initialization...all other threads wait until it's
4320        done.
4321 
4322        Yeah, uglier than a pthread call, it's got all the stuff inline
4323        rather than in a separate routine.
4324     */
4325 
4326     if (!pipe_ef) {
4327         _ckvmssts_noperl(sys$setast(0));
4328         if (!pipe_ef) {
4329             unsigned long int pidcode = JPI$_PID;
4330             $DESCRIPTOR(d_delay, RETRY_DELAY);
4331             _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4332             _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4333             _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4334         }
4335         if (!handler_set_up) {
4336           _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4337           handler_set_up = TRUE;
4338         }
4339         _ckvmssts_noperl(sys$setast(1));
4340     }
4341 
4342     /* see if we can find a VMSPIPE.COM */
4343 
4344     tfilebuf[0] = '@';
4345     vmspipe = find_vmspipe(aTHX);
4346     if (vmspipe) {
4347         strcpy(tfilebuf+1,vmspipe);
4348     } else {        /* uh, oh...we're in tempfile hell */
4349         tpipe = vmspipe_tempfile(aTHX);
4350         if (!tpipe) {       /* a fish popular in Boston */
4351             if (ckWARN(WARN_PIPE)) {
4352                 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4353             }
4354         return NULL;
4355         }
4356         fgetname(tpipe,tfilebuf+1,1);
4357     }
4358     vmspipedsc.dsc$a_pointer = tfilebuf;
4359     vmspipedsc.dsc$w_length  = strlen(tfilebuf);
4360 
4361     sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4362     if (!(sts & 1)) {
4363       switch (sts) {
4364         case RMS$_FNF:  case RMS$_DNF:
4365           set_errno(ENOENT); break;
4366         case RMS$_DIR:
4367           set_errno(ENOTDIR); break;
4368         case RMS$_DEV:
4369           set_errno(ENODEV); break;
4370         case RMS$_PRV:
4371           set_errno(EACCES); break;
4372         case RMS$_SYN:
4373           set_errno(EINVAL); break;
4374         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4375           set_errno(E2BIG); break;
4376         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4377           _ckvmssts_noperl(sts); /* fall through */
4378         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4379           set_errno(EVMSERR);
4380       }
4381       set_vaxc_errno(sts);
4382       if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4383         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4384       }
4385       *psts = sts;
4386       return NULL;
4387     }
4388     n = sizeof(Info);
4389     _ckvmssts_noperl(lib$get_vm(&n, &info));
4390 
4391     strcpy(mode,in_mode);
4392     info->mode = *mode;
4393     info->done = FALSE;
4394     info->completion = 0;
4395     info->closing    = FALSE;
4396     info->in         = 0;
4397     info->out        = 0;
4398     info->err        = 0;
4399     info->fp         = NULL;
4400     info->useFILE    = 0;
4401     info->waiting    = 0;
4402     info->in_done    = TRUE;
4403     info->out_done   = TRUE;
4404     info->err_done   = TRUE;
4405     info->xchan      = 0;
4406     info->xchan_valid = 0;
4407 
4408     in = PerlMem_malloc(VMS_MAXRSS);
4409     if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4410     out = PerlMem_malloc(VMS_MAXRSS);
4411     if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4412     err = PerlMem_malloc(VMS_MAXRSS);
4413     if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4414 
4415     in[0] = out[0] = err[0] = '\0';
4416 
4417     if ((p = strchr(mode,'F')) != NULL) {   /* F -> use FILE* */
4418         info->useFILE = 1;
4419         strcpy(p,p+1);
4420     }
4421     if ((p = strchr(mode,'W')) != NULL) {   /* W -> wait for completion */
4422         wait = 1;
4423         strcpy(p,p+1);
4424     }
4425 
4426     if (*mode == 'r') {             /* piping from subroutine */
4427 
4428         info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4429         if (info->out) {
4430             info->out->pipe_done = &info->out_done;
4431             info->out_done = FALSE;
4432             info->out->info = info;
4433         }
4434         if (!info->useFILE) {
4435 	    info->fp  = PerlIO_open(mbx, mode);
4436         } else {
4437             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4438             Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4439         }
4440 
4441         if (!info->fp && info->out) {
4442             sys$cancel(info->out->chan_out);
4443 
4444             while (!info->out_done) {
4445                 int done;
4446                 _ckvmssts_noperl(sys$setast(0));
4447                 done = info->out_done;
4448                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4449                 _ckvmssts_noperl(sys$setast(1));
4450                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4451             }
4452 
4453             if (info->out->buf) {
4454                 n = info->out->bufsize * sizeof(char);
4455                 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4456             }
4457             n = sizeof(Pipe);
4458             _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4459             n = sizeof(Info);
4460             _ckvmssts_noperl(lib$free_vm(&n, &info));
4461             *psts = RMS$_FNF;
4462             return NULL;
4463         }
4464 
4465         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4466         if (info->err) {
4467             info->err->pipe_done = &info->err_done;
4468             info->err_done = FALSE;
4469             info->err->info = info;
4470         }
4471 
4472     } else if (*mode == 'w') {      /* piping to subroutine */
4473 
4474         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4475         if (info->out) {
4476             info->out->pipe_done = &info->out_done;
4477             info->out_done = FALSE;
4478             info->out->info = info;
4479         }
4480 
4481         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4482         if (info->err) {
4483             info->err->pipe_done = &info->err_done;
4484             info->err_done = FALSE;
4485             info->err->info = info;
4486         }
4487 
4488         info->in = pipe_tochild_setup(aTHX_ in,mbx);
4489         if (!info->useFILE) {
4490 	    info->fp  = PerlIO_open(mbx, mode);
4491         } else {
4492             info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4493             Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4494         }
4495 
4496         if (info->in) {
4497             info->in->pipe_done = &info->in_done;
4498             info->in_done = FALSE;
4499             info->in->info = info;
4500         }
4501 
4502         /* error cleanup */
4503         if (!info->fp && info->in) {
4504             info->done = TRUE;
4505             _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4506                                       0, 0, 0, 0, 0, 0, 0, 0));
4507 
4508             while (!info->in_done) {
4509                 int done;
4510                 _ckvmssts_noperl(sys$setast(0));
4511                 done = info->in_done;
4512                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4513                 _ckvmssts_noperl(sys$setast(1));
4514                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4515             }
4516 
4517             if (info->in->buf) {
4518                 n = info->in->bufsize * sizeof(char);
4519                 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4520             }
4521             n = sizeof(Pipe);
4522             _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4523             n = sizeof(Info);
4524             _ckvmssts_noperl(lib$free_vm(&n, &info));
4525             *psts = RMS$_FNF;
4526             return NULL;
4527         }
4528 
4529 
4530     } else if (*mode == 'n') {       /* separate subprocess, no Perl i/o */
4531         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4532         if (info->out) {
4533             info->out->pipe_done = &info->out_done;
4534             info->out_done = FALSE;
4535             info->out->info = info;
4536         }
4537 
4538         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4539         if (info->err) {
4540             info->err->pipe_done = &info->err_done;
4541             info->err_done = FALSE;
4542             info->err->info = info;
4543         }
4544     }
4545 
4546     symbol[MAX_DCL_SYMBOL] = '\0';
4547 
4548     strncpy(symbol, in, MAX_DCL_SYMBOL);
4549     d_symbol.dsc$w_length = strlen(symbol);
4550     _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4551 
4552     strncpy(symbol, err, MAX_DCL_SYMBOL);
4553     d_symbol.dsc$w_length = strlen(symbol);
4554     _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4555 
4556     strncpy(symbol, out, MAX_DCL_SYMBOL);
4557     d_symbol.dsc$w_length = strlen(symbol);
4558     _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4559 
4560     /* Done with the names for the pipes */
4561     PerlMem_free(err);
4562     PerlMem_free(out);
4563     PerlMem_free(in);
4564 
4565     p = vmscmd->dsc$a_pointer;
4566     while (*p == ' ' || *p == '\t') p++;        /* remove leading whitespace */
4567     if (*p == '$') p++;                         /* remove leading $ */
4568     while (*p == ' ' || *p == '\t') p++;
4569 
4570     for (j = 0; j < 4; j++) {
4571         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4572         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4573 
4574     strncpy(symbol, p, MAX_DCL_SYMBOL);
4575     d_symbol.dsc$w_length = strlen(symbol);
4576     _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4577 
4578         if (strlen(p) > MAX_DCL_SYMBOL) {
4579             p += MAX_DCL_SYMBOL;
4580         } else {
4581             p += strlen(p);
4582         }
4583     }
4584     _ckvmssts_noperl(sys$setast(0));
4585     info->next=open_pipes;  /* prepend to list */
4586     open_pipes=info;
4587     _ckvmssts_noperl(sys$setast(1));
4588     /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4589      * and SYS$COMMAND.  vmspipe.com will redefine SYS$INPUT, but we'll still
4590      * have SYS$COMMAND if we need it.
4591      */
4592     _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4593                       0, &info->pid, &info->completion,
4594                       0, popen_completion_ast,info,0,0,0));
4595 
4596     /* if we were using a tempfile, close it now */
4597 
4598     if (tpipe) fclose(tpipe);
4599 
4600     /* once the subprocess is spawned, it has copied the symbols and
4601        we can get rid of ours */
4602 
4603     for (j = 0; j < 4; j++) {
4604         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4605         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4606     _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4607     }
4608     _ckvmssts_noperl(lib$delete_symbol(&d_sym_in,  &table));
4609     _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4610     _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4611     vms_execfree(vmscmd);
4612 
4613 #ifdef PERL_IMPLICIT_CONTEXT
4614     if (aTHX)
4615 #endif
4616     PL_forkprocess = info->pid;
4617 
4618     ret_fp = info->fp;
4619     if (wait) {
4620          dSAVEDERRNO;
4621          int done = 0;
4622          while (!done) {
4623              _ckvmssts_noperl(sys$setast(0));
4624              done = info->done;
4625              if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4626              _ckvmssts_noperl(sys$setast(1));
4627              if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4628          }
4629         *psts = info->completion;
4630 /* Caller thinks it is open and tries to close it. */
4631 /* This causes some problems, as it changes the error status */
4632 /*        my_pclose(info->fp); */
4633 
4634          /* If we did not have a file pointer open, then we have to */
4635          /* clean up here or eventually we will run out of something */
4636          SAVE_ERRNO;
4637          if (info->fp == NULL) {
4638              my_pclose_pinfo(aTHX_ info);
4639          }
4640          RESTORE_ERRNO;
4641 
4642     } else {
4643         *psts = info->pid;
4644     }
4645     return ret_fp;
4646 }  /* end of safe_popen */
4647 
4648 
4649 /*{{{  PerlIO *my_popen(char *cmd, char *mode)*/
4650 PerlIO *
4651 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4652 {
4653     int sts;
4654     TAINT_ENV();
4655     TAINT_PROPER("popen");
4656     PERL_FLUSHALL_FOR_CHILD;
4657     return safe_popen(aTHX_ cmd,mode,&sts);
4658 }
4659 
4660 /*}}}*/
4661 
4662 
4663 /* Routine to close and cleanup a pipe info structure */
4664 
4665 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4666 
4667     unsigned long int retsts;
4668     int done, iss, n;
4669     int status;
4670     pInfo next, last;
4671 
4672     /* If we were writing to a subprocess, insure that someone reading from
4673      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
4674      * produce an EOF record in the mailbox.
4675      *
4676      *  well, at least sometimes it *does*, so we have to watch out for
4677      *  the first EOF closing the pipe (and DASSGN'ing the channel)...
4678      */
4679      if (info->fp) {
4680         if (!info->useFILE
4681 #if defined(USE_ITHREADS)
4682           && my_perl
4683 #endif
4684           && PL_perlio_fd_refcnt)
4685             PerlIO_flush(info->fp);
4686         else
4687             fflush((FILE *)info->fp);
4688     }
4689 
4690     _ckvmssts(sys$setast(0));
4691      info->closing = TRUE;
4692      done = info->done && info->in_done && info->out_done && info->err_done;
4693      /* hanging on write to Perl's input? cancel it */
4694      if (info->mode == 'r' && info->out && !info->out_done) {
4695         if (info->out->chan_out) {
4696             _ckvmssts(sys$cancel(info->out->chan_out));
4697             if (!info->out->chan_in) {   /* EOF generation, need AST */
4698                 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4699             }
4700         }
4701      }
4702      if (info->in && !info->in_done && !info->in->shut_on_empty)  /* EOF if hasn't had one yet */
4703          _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4704                            0, 0, 0, 0, 0, 0));
4705     _ckvmssts(sys$setast(1));
4706     if (info->fp) {
4707      if (!info->useFILE
4708 #if defined(USE_ITHREADS)
4709          && my_perl
4710 #endif
4711          && PL_perlio_fd_refcnt)
4712         PerlIO_close(info->fp);
4713      else
4714         fclose((FILE *)info->fp);
4715     }
4716      /*
4717         we have to wait until subprocess completes, but ALSO wait until all
4718         the i/o completes...otherwise we'll be freeing the "info" structure
4719         that the i/o ASTs could still be using...
4720      */
4721 
4722      while (!done) {
4723          _ckvmssts(sys$setast(0));
4724          done = info->done && info->in_done && info->out_done && info->err_done;
4725          if (!done) _ckvmssts(sys$clref(pipe_ef));
4726          _ckvmssts(sys$setast(1));
4727          if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4728      }
4729      retsts = info->completion;
4730 
4731     /* remove from list of open pipes */
4732     _ckvmssts(sys$setast(0));
4733     last = NULL;
4734     for (next = open_pipes; next != NULL; last = next, next = next->next) {
4735         if (next == info)
4736             break;
4737     }
4738 
4739     if (last)
4740         last->next = info->next;
4741     else
4742         open_pipes = info->next;
4743     _ckvmssts(sys$setast(1));
4744 
4745     /* free buffers and structures */
4746 
4747     if (info->in) {
4748         if (info->in->buf) {
4749             n = info->in->bufsize * sizeof(char);
4750             _ckvmssts(lib$free_vm(&n, &info->in->buf));
4751         }
4752         n = sizeof(Pipe);
4753         _ckvmssts(lib$free_vm(&n, &info->in));
4754     }
4755     if (info->out) {
4756         if (info->out->buf) {
4757             n = info->out->bufsize * sizeof(char);
4758             _ckvmssts(lib$free_vm(&n, &info->out->buf));
4759         }
4760         n = sizeof(Pipe);
4761         _ckvmssts(lib$free_vm(&n, &info->out));
4762     }
4763     if (info->err) {
4764         if (info->err->buf) {
4765             n = info->err->bufsize * sizeof(char);
4766             _ckvmssts(lib$free_vm(&n, &info->err->buf));
4767         }
4768         n = sizeof(Pipe);
4769         _ckvmssts(lib$free_vm(&n, &info->err));
4770     }
4771     n = sizeof(Info);
4772     _ckvmssts(lib$free_vm(&n, &info));
4773 
4774     return retsts;
4775 }
4776 
4777 
4778 /*{{{  I32 my_pclose(PerlIO *fp)*/
4779 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4780 {
4781     pInfo info, last = NULL;
4782     I32 ret_status;
4783 
4784     /* Fixme - need ast and mutex protection here */
4785     for (info = open_pipes; info != NULL; last = info, info = info->next)
4786         if (info->fp == fp) break;
4787 
4788     if (info == NULL) {  /* no such pipe open */
4789       set_errno(ECHILD); /* quoth POSIX */
4790       set_vaxc_errno(SS$_NONEXPR);
4791       return -1;
4792     }
4793 
4794     ret_status = my_pclose_pinfo(aTHX_ info);
4795 
4796     return ret_status;
4797 
4798 }  /* end of my_pclose() */
4799 
4800 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4801   /* Roll our own prototype because we want this regardless of whether
4802    * _VMS_WAIT is defined.
4803    */
4804   __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4805 #endif
4806 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4807    created with popen(); otherwise partially emulate waitpid() unless
4808    we have a suitable one from the CRTL that came with VMS 7.2 and later.
4809    Also check processes not considered by the CRTL waitpid().
4810  */
4811 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4812 Pid_t
4813 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4814 {
4815     pInfo info;
4816     int done;
4817     int sts;
4818     int j;
4819 
4820     if (statusp) *statusp = 0;
4821 
4822     for (info = open_pipes; info != NULL; info = info->next)
4823         if (info->pid == pid) break;
4824 
4825     if (info != NULL) {  /* we know about this child */
4826       while (!info->done) {
4827           _ckvmssts(sys$setast(0));
4828           done = info->done;
4829           if (!done) _ckvmssts(sys$clref(pipe_ef));
4830           _ckvmssts(sys$setast(1));
4831           if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4832       }
4833 
4834       if (statusp) *statusp = info->completion;
4835       return pid;
4836     }
4837 
4838     /* child that already terminated? */
4839 
4840     for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4841         if (closed_list[j].pid == pid) {
4842             if (statusp) *statusp = closed_list[j].completion;
4843             return pid;
4844         }
4845     }
4846 
4847     /* fall through if this child is not one of our own pipe children */
4848 
4849 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4850 
4851       /* waitpid() became available in the CRTL as of VMS 7.0, but only
4852        * in 7.2 did we get a version that fills in the VMS completion
4853        * status as Perl has always tried to do.
4854        */
4855 
4856       sts = __vms_waitpid( pid, statusp, flags );
4857 
4858       if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4859          return sts;
4860 
4861       /* If the real waitpid tells us the child does not exist, we
4862        * fall through here to implement waiting for a child that
4863        * was created by some means other than exec() (say, spawned
4864        * from DCL) or to wait for a process that is not a subprocess
4865        * of the current process.
4866        */
4867 
4868 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4869 
4870     {
4871       $DESCRIPTOR(intdsc,"0 00:00:01");
4872       unsigned long int ownercode = JPI$_OWNER, ownerpid;
4873       unsigned long int pidcode = JPI$_PID, mypid;
4874       unsigned long int interval[2];
4875       unsigned int jpi_iosb[2];
4876       struct itmlst_3 jpilist[2] = {
4877           {sizeof(ownerpid),        JPI$_OWNER, &ownerpid,        0},
4878           {                      0,         0,                 0, 0}
4879       };
4880 
4881       if (pid <= 0) {
4882         /* Sorry folks, we don't presently implement rooting around for
4883            the first child we can find, and we definitely don't want to
4884            pass a pid of -1 to $getjpi, where it is a wildcard operation.
4885          */
4886         set_errno(ENOTSUP);
4887         return -1;
4888       }
4889 
4890       /* Get the owner of the child so I can warn if it's not mine. If the
4891        * process doesn't exist or I don't have the privs to look at it,
4892        * I can go home early.
4893        */
4894       sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4895       if (sts & 1) sts = jpi_iosb[0];
4896       if (!(sts & 1)) {
4897         switch (sts) {
4898             case SS$_NONEXPR:
4899                 set_errno(ECHILD);
4900                 break;
4901             case SS$_NOPRIV:
4902                 set_errno(EACCES);
4903                 break;
4904             default:
4905                 _ckvmssts(sts);
4906         }
4907         set_vaxc_errno(sts);
4908         return -1;
4909       }
4910 
4911       if (ckWARN(WARN_EXEC)) {
4912         /* remind folks they are asking for non-standard waitpid behavior */
4913         _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4914         if (ownerpid != mypid)
4915           Perl_warner(aTHX_ packWARN(WARN_EXEC),
4916                       "waitpid: process %x is not a child of process %x",
4917                       pid,mypid);
4918       }
4919 
4920       /* simply check on it once a second until it's not there anymore. */
4921 
4922       _ckvmssts(sys$bintim(&intdsc,interval));
4923       while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4924             _ckvmssts(sys$schdwk(0,0,interval,0));
4925             _ckvmssts(sys$hiber());
4926       }
4927       if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4928 
4929       _ckvmssts(sts);
4930       return pid;
4931     }
4932 }  /* end of waitpid() */
4933 /*}}}*/
4934 /*}}}*/
4935 /*}}}*/
4936 
4937 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4938 char *
4939 my_gconvert(double val, int ndig, int trail, char *buf)
4940 {
4941   static char __gcvtbuf[DBL_DIG+1];
4942   char *loc;
4943 
4944   loc = buf ? buf : __gcvtbuf;
4945 
4946 #ifndef __DECC  /* VAXCRTL gcvt uses E format for numbers < 1 */
4947   if (val < 1) {
4948     sprintf(loc,"%.*g",ndig,val);
4949     return loc;
4950   }
4951 #endif
4952 
4953   if (val) {
4954     if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4955     return gcvt(val,ndig,loc);
4956   }
4957   else {
4958     loc[0] = '0'; loc[1] = '\0';
4959     return loc;
4960   }
4961 
4962 }
4963 /*}}}*/
4964 
4965 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4966 static int rms_free_search_context(struct FAB * fab)
4967 {
4968 struct NAM * nam;
4969 
4970     nam = fab->fab$l_nam;
4971     nam->nam$b_nop |= NAM$M_SYNCHK;
4972     nam->nam$l_rlf = NULL;
4973     fab->fab$b_dns = 0;
4974     return sys$parse(fab, NULL, NULL);
4975 }
4976 
4977 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4978 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4979 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4980 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4981 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4982 #define rms_nam_esll(nam) nam.nam$b_esl
4983 #define rms_nam_esl(nam) nam.nam$b_esl
4984 #define rms_nam_name(nam) nam.nam$l_name
4985 #define rms_nam_namel(nam) nam.nam$l_name
4986 #define rms_nam_type(nam) nam.nam$l_type
4987 #define rms_nam_typel(nam) nam.nam$l_type
4988 #define rms_nam_ver(nam) nam.nam$l_ver
4989 #define rms_nam_verl(nam) nam.nam$l_ver
4990 #define rms_nam_rsll(nam) nam.nam$b_rsl
4991 #define rms_nam_rsl(nam) nam.nam$b_rsl
4992 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4993 #define rms_set_fna(fab, nam, name, size) \
4994 	{ fab.fab$b_fns = size; fab.fab$l_fna = name; }
4995 #define rms_get_fna(fab, nam) fab.fab$l_fna
4996 #define rms_set_dna(fab, nam, name, size) \
4997 	{ fab.fab$b_dns = size; fab.fab$l_dna = name; }
4998 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4999 #define rms_set_esa(nam, name, size) \
5000 	{ nam.nam$b_ess = size; nam.nam$l_esa = name; }
5001 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5002 	{ nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
5003 #define rms_set_rsa(nam, name, size) \
5004 	{ nam.nam$l_rsa = name; nam.nam$b_rss = size; }
5005 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5006 	{ nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
5007 #define rms_nam_name_type_l_size(nam) \
5008 	(nam.nam$b_name + nam.nam$b_type)
5009 #else
5010 static int rms_free_search_context(struct FAB * fab)
5011 {
5012 struct NAML * nam;
5013 
5014     nam = fab->fab$l_naml;
5015     nam->naml$b_nop |= NAM$M_SYNCHK;
5016     nam->naml$l_rlf = NULL;
5017     nam->naml$l_long_defname_size = 0;
5018 
5019     fab->fab$b_dns = 0;
5020     return sys$parse(fab, NULL, NULL);
5021 }
5022 
5023 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
5024 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
5025 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
5026 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
5027 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
5028 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
5029 #define rms_nam_esl(nam) nam.naml$b_esl
5030 #define rms_nam_name(nam) nam.naml$l_name
5031 #define rms_nam_namel(nam) nam.naml$l_long_name
5032 #define rms_nam_type(nam) nam.naml$l_type
5033 #define rms_nam_typel(nam) nam.naml$l_long_type
5034 #define rms_nam_ver(nam) nam.naml$l_ver
5035 #define rms_nam_verl(nam) nam.naml$l_long_ver
5036 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
5037 #define rms_nam_rsl(nam) nam.naml$b_rsl
5038 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
5039 #define rms_set_fna(fab, nam, name, size) \
5040 	{ fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
5041 	nam.naml$l_long_filename_size = size; \
5042 	nam.naml$l_long_filename = name;}
5043 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
5044 #define rms_set_dna(fab, nam, name, size) \
5045 	{ fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
5046 	nam.naml$l_long_defname_size = size; \
5047 	nam.naml$l_long_defname = name; }
5048 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
5049 #define rms_set_esa(nam, name, size) \
5050 	{ nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
5051 	nam.naml$l_long_expand_alloc = size; \
5052 	nam.naml$l_long_expand = name; }
5053 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5054 	{ nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
5055 	nam.naml$l_long_expand = l_name; \
5056 	nam.naml$l_long_expand_alloc = l_size; }
5057 #define rms_set_rsa(nam, name, size) \
5058 	{ nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
5059 	nam.naml$l_long_result = name; \
5060 	nam.naml$l_long_result_alloc = size; }
5061 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5062 	{ nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
5063 	nam.naml$l_long_result = l_name; \
5064 	nam.naml$l_long_result_alloc = l_size; }
5065 #define rms_nam_name_type_l_size(nam) \
5066 	(nam.naml$l_long_name_size + nam.naml$l_long_type_size)
5067 #endif
5068 
5069 
5070 /* rms_erase
5071  * The CRTL for 8.3 and later can create symbolic links in any mode,
5072  * however in 8.3 the unlink/remove/delete routines will only properly handle
5073  * them if one of the PCP modes is active.
5074  */
5075 static int rms_erase(const char * vmsname)
5076 {
5077   int status;
5078   struct FAB myfab = cc$rms_fab;
5079   rms_setup_nam(mynam);
5080 
5081   rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
5082   rms_bind_fab_nam(myfab, mynam);
5083 
5084 #ifdef NAML$M_OPEN_SPECIAL
5085   rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5086 #endif
5087 
5088   status = sys$erase(&myfab, 0, 0);
5089 
5090   return status;
5091 }
5092 
5093 
5094 static int
5095 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5096 		    const struct dsc$descriptor_s * vms_dst_dsc,
5097 		    unsigned long flags)
5098 {
5099     /*  VMS and UNIX handle file permissions differently and the
5100      * the same ACL trick may be needed for renaming files,
5101      * especially if they are directories.
5102      */
5103 
5104    /* todo: get kill_file and rename to share common code */
5105    /* I can not find online documentation for $change_acl
5106     * it appears to be replaced by $set_security some time ago */
5107 
5108 const unsigned int access_mode = 0;
5109 $DESCRIPTOR(obj_file_dsc,"FILE");
5110 char *vmsname;
5111 char *rslt;
5112 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5113 int aclsts, fndsts, rnsts = -1;
5114 unsigned int ctx = 0;
5115 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5116 struct dsc$descriptor_s * clean_dsc;
5117 
5118 struct myacedef {
5119     unsigned char myace$b_length;
5120     unsigned char myace$b_type;
5121     unsigned short int myace$w_flags;
5122     unsigned long int myace$l_access;
5123     unsigned long int myace$l_ident;
5124 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5125 	     ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5126 	     0},
5127 	     oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5128 
5129 struct item_list_3
5130 	findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5131 		      {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5132 		      {0,0,0,0}},
5133 	addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5134 	dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5135 		     {0,0,0,0}};
5136 
5137 
5138     /* Expand the input spec using RMS, since we do not want to put
5139      * ACLs on the target of a symbolic link */
5140     vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5141     if (vmsname == NULL)
5142 	return SS$_INSFMEM;
5143 
5144     rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5145 			vmsname,
5146 			PERL_RMSEXPAND_M_SYMLINK);
5147     if (rslt == NULL) {
5148 	PerlMem_free(vmsname);
5149 	return SS$_INSFMEM;
5150     }
5151 
5152     /* So we get our own UIC to use as a rights identifier,
5153      * and the insert an ACE at the head of the ACL which allows us
5154      * to delete the file.
5155      */
5156     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5157 
5158     fildsc.dsc$w_length = strlen(vmsname);
5159     fildsc.dsc$a_pointer = vmsname;
5160     ctx = 0;
5161     newace.myace$l_ident = oldace.myace$l_ident;
5162     rnsts = SS$_ABORT;
5163 
5164     /* Grab any existing ACEs with this identifier in case we fail */
5165     clean_dsc = &fildsc;
5166     aclsts = fndsts = sys$get_security(&obj_file_dsc,
5167 			       &fildsc,
5168 			       NULL,
5169 			       OSS$M_WLOCK,
5170 			       findlst,
5171 			       &ctx,
5172 			       &access_mode);
5173 
5174     if ($VMS_STATUS_SUCCESS(fndsts)  || (fndsts == SS$_ACLEMPTY)) {
5175 	/* Add the new ACE . . . */
5176 
5177 	/* if the sys$get_security succeeded, then ctx is valid, and the
5178 	 * object/file descriptors will be ignored.  But otherwise they
5179 	 * are needed
5180 	 */
5181 	aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5182 				  OSS$M_RELCTX, addlst, &ctx, &access_mode);
5183 	if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5184 	    set_errno(EVMSERR);
5185 	    set_vaxc_errno(aclsts);
5186 	    PerlMem_free(vmsname);
5187 	    return aclsts;
5188 	}
5189 
5190 	rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5191 				NULL, NULL,
5192 				&flags,
5193 				NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5194 
5195 	if ($VMS_STATUS_SUCCESS(rnsts)) {
5196 	    clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5197 	}
5198 
5199 	/* Put things back the way they were. */
5200 	ctx = 0;
5201 	aclsts = sys$get_security(&obj_file_dsc,
5202 				  clean_dsc,
5203 				  NULL,
5204 				  OSS$M_WLOCK,
5205 				  findlst,
5206 				  &ctx,
5207 				  &access_mode);
5208 
5209 	if ($VMS_STATUS_SUCCESS(aclsts)) {
5210 	int sec_flags;
5211 
5212 	    sec_flags = 0;
5213 	    if (!$VMS_STATUS_SUCCESS(fndsts))
5214 		sec_flags = OSS$M_RELCTX;
5215 
5216 	    /* Get rid of the new ACE */
5217 	    aclsts = sys$set_security(NULL, NULL, NULL,
5218 				  sec_flags, dellst, &ctx, &access_mode);
5219 
5220 	    /* If there was an old ACE, put it back */
5221 	    if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5222 		addlst[0].bufadr = &oldace;
5223 		aclsts = sys$set_security(NULL, NULL, NULL,
5224 				      OSS$M_RELCTX, addlst, &ctx, &access_mode);
5225 		if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5226 		    set_errno(EVMSERR);
5227 		    set_vaxc_errno(aclsts);
5228 		    rnsts = aclsts;
5229 		}
5230 	    } else {
5231 	    int aclsts2;
5232 
5233 		/* Try to clear the lock on the ACL list */
5234 		aclsts2 = sys$set_security(NULL, NULL, NULL,
5235 				      OSS$M_RELCTX, NULL, &ctx, &access_mode);
5236 
5237 		/* Rename errors are most important */
5238 		if (!$VMS_STATUS_SUCCESS(rnsts))
5239 		    aclsts = rnsts;
5240 		set_errno(EVMSERR);
5241 		set_vaxc_errno(aclsts);
5242 		rnsts = aclsts;
5243 	    }
5244 	}
5245 	else {
5246 	    if (aclsts != SS$_ACLEMPTY)
5247 		rnsts = aclsts;
5248 	}
5249     }
5250     else
5251 	rnsts = fndsts;
5252 
5253     PerlMem_free(vmsname);
5254     return rnsts;
5255 }
5256 
5257 
5258 /*{{{int rename(const char *, const char * */
5259 /* Not exactly what X/Open says to do, but doing it absolutely right
5260  * and efficiently would require a lot more work.  This should be close
5261  * enough to pass all but the most strict X/Open compliance test.
5262  */
5263 int
5264 Perl_rename(pTHX_ const char *src, const char * dst)
5265 {
5266 int retval;
5267 int pre_delete = 0;
5268 int src_sts;
5269 int dst_sts;
5270 Stat_t src_st;
5271 Stat_t dst_st;
5272 
5273     /* Validate the source file */
5274     src_sts = flex_lstat(src, &src_st);
5275     if (src_sts != 0) {
5276 
5277 	/* No source file or other problem */
5278 	return src_sts;
5279     }
5280     if (src_st.st_devnam[0] == 0)  {
5281         /* This may be possible so fail if it is seen. */
5282         errno = EIO;
5283         return -1;
5284     }
5285 
5286     dst_sts = flex_lstat(dst, &dst_st);
5287     if (dst_sts == 0) {
5288 
5289 	if (dst_st.st_dev != src_st.st_dev) {
5290 	    /* Must be on the same device */
5291 	    errno = EXDEV;
5292 	    return -1;
5293 	}
5294 
5295 	/* VMS_INO_T_COMPARE is true if the inodes are different
5296 	 * to match the output of memcmp
5297 	 */
5298 
5299 	if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5300 	    /* That was easy, the files are the same! */
5301 	    return 0;
5302 	}
5303 
5304 	if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5305 	    /* If source is a directory, so must be dest */
5306 		errno = EISDIR;
5307 		return -1;
5308 	}
5309 
5310     }
5311 
5312 
5313     if ((dst_sts == 0) &&
5314 	(vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5315 
5316 	/* We have issues here if vms_unlink_all_versions is set
5317 	 * If the destination exists, and is not a directory, then
5318 	 * we must delete in advance.
5319 	 *
5320 	 * If the src is a directory, then we must always pre-delete
5321 	 * the destination.
5322 	 *
5323 	 * If we successfully delete the dst in advance, and the rename fails
5324 	 * X/Open requires that errno be EIO.
5325 	 *
5326 	 */
5327 
5328 	if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5329 	    int d_sts;
5330 	    d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5331 	                             S_ISDIR(dst_st.st_mode));
5332 
5333            /* Need to delete all versions ? */
5334            if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5335                 int i = 0;
5336 
5337                 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5338                     d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5339                     if (d_sts != 0)
5340                         break;
5341                     i++;
5342 
5343                     /* Make sure that we do not loop forever */
5344                     if (i > 32767) {
5345                         errno = EIO;
5346                         d_sts = -1;
5347                         break;
5348                     }
5349                 }
5350            }
5351 
5352 	    if (d_sts != 0)
5353 		return d_sts;
5354 
5355 	    /* We killed the destination, so only errno now is EIO */
5356 	    pre_delete = 1;
5357 	}
5358     }
5359 
5360     /* Originally the idea was to call the CRTL rename() and only
5361      * try the lib$rename_file if it failed.
5362      * It turns out that there are too many variants in what the
5363      * the CRTL rename might do, so only use lib$rename_file
5364      */
5365     retval = -1;
5366 
5367     {
5368 	/* Is the source and dest both in VMS format */
5369 	/* if the source is a directory, then need to fileify */
5370 	/*  and dest must be a directory or non-existant. */
5371 
5372 	char * vms_dst;
5373 	int sts;
5374 	char * ret_str;
5375 	unsigned long flags;
5376 	struct dsc$descriptor_s old_file_dsc;
5377 	struct dsc$descriptor_s new_file_dsc;
5378 
5379 	/* We need to modify the src and dst depending
5380 	 * on if one or more of them are directories.
5381 	 */
5382 
5383 	vms_dst = PerlMem_malloc(VMS_MAXRSS);
5384 	if (vms_dst == NULL)
5385 	    _ckvmssts_noperl(SS$_INSFMEM);
5386 
5387 	if (S_ISDIR(src_st.st_mode)) {
5388 	char * ret_str;
5389 	char * vms_dir_file;
5390 
5391 	    vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5392 	    if (vms_dir_file == NULL)
5393 		_ckvmssts_noperl(SS$_INSFMEM);
5394 
5395 	    /* If the dest is a directory, we must remove it
5396 	    if (dst_sts == 0) {
5397 		int d_sts;
5398 		d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5399 		if (d_sts != 0) {
5400 		    PerlMem_free(vms_dst);
5401 		    errno = EIO;
5402 		    return sts;
5403 		}
5404 
5405 		pre_delete = 1;
5406 	    }
5407 
5408 	   /* The dest must be a VMS file specification */
5409 	   ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5410 	   if (ret_str == NULL) {
5411 		PerlMem_free(vms_dst);
5412 		errno = EIO;
5413 		return -1;
5414 	   }
5415 
5416 	    /* The source must be a file specification */
5417 	    vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5418 	    if (vms_dir_file == NULL)
5419 		_ckvmssts_noperl(SS$_INSFMEM);
5420 
5421 	    ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5422 	    if (ret_str == NULL) {
5423 		PerlMem_free(vms_dst);
5424 		PerlMem_free(vms_dir_file);
5425 		errno = EIO;
5426 		return -1;
5427 	    }
5428 	    PerlMem_free(vms_dst);
5429 	    vms_dst = vms_dir_file;
5430 
5431 	} else {
5432 	    /* File to file or file to new dir */
5433 
5434 	    if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5435 		/* VMS pathify a dir target */
5436 		ret_str = int_tovmspath(dst, vms_dst, NULL);
5437 		if (ret_str == NULL) {
5438 		    PerlMem_free(vms_dst);
5439 		    errno = EIO;
5440 		    return -1;
5441 		}
5442 	    } else {
5443                 char * v_spec, * r_spec, * d_spec, * n_spec;
5444                 char * e_spec, * vs_spec;
5445                 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5446 
5447 		/* fileify a target VMS file specification */
5448 		ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5449 		if (ret_str == NULL) {
5450 		    PerlMem_free(vms_dst);
5451 		    errno = EIO;
5452 		    return -1;
5453 		}
5454 
5455 		sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5456                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5457                              &e_len, &vs_spec, &vs_len);
5458 		if (sts == 0) {
5459 		     if (e_len == 0) {
5460 		         /* Get rid of the version */
5461 		         if (vs_len != 0) {
5462 		             *vs_spec = '\0';
5463 		         }
5464 		         /* Need to specify a '.' so that the extension */
5465 		         /* is not inherited */
5466 		         strcat(vms_dst,".");
5467 		     }
5468 		}
5469 	    }
5470 	}
5471 
5472 	old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5473 	old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5474 	old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5475 	old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5476 
5477 	new_file_dsc.dsc$a_pointer = vms_dst;
5478 	new_file_dsc.dsc$w_length = strlen(vms_dst);
5479 	new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5480 	new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5481 
5482 	flags = 0;
5483 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5484 	flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5485 #endif
5486 
5487 	sts = lib$rename_file(&old_file_dsc,
5488 			      &new_file_dsc,
5489 			      NULL, NULL,
5490 			      &flags,
5491 			      NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5492 	if (!$VMS_STATUS_SUCCESS(sts)) {
5493 
5494 	   /* We could have failed because VMS style permissions do not
5495 	    * permit renames that UNIX will allow.  Just like the hack
5496 	    * in for kill_file.
5497 	    */
5498 	   sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5499 	}
5500 
5501 	PerlMem_free(vms_dst);
5502 	if (!$VMS_STATUS_SUCCESS(sts)) {
5503 	    errno = EIO;
5504 	    return -1;
5505 	}
5506 	retval = 0;
5507     }
5508 
5509     if (vms_unlink_all_versions) {
5510 	/* Now get rid of any previous versions of the source file that
5511 	 * might still exist
5512 	 */
5513 	int i = 0;
5514 	dSAVEDERRNO;
5515 	SAVE_ERRNO;
5516 	src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5517 	                           S_ISDIR(src_st.st_mode));
5518 	while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5519 	     src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5520 	                               S_ISDIR(src_st.st_mode));
5521 	     if (src_sts != 0)
5522 	         break;
5523 	     i++;
5524 
5525 	     /* Make sure that we do not loop forever */
5526 	     if (i > 32767) {
5527 	         src_sts = -1;
5528 	         break;
5529 	     }
5530 	}
5531 	RESTORE_ERRNO;
5532     }
5533 
5534     /* We deleted the destination, so must force the error to be EIO */
5535     if ((retval != 0) && (pre_delete != 0))
5536 	errno = EIO;
5537 
5538     return retval;
5539 }
5540 /*}}}*/
5541 
5542 
5543 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5544 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5545  * to expand file specification.  Allows for a single default file
5546  * specification and a simple mask of options.  If outbuf is non-NULL,
5547  * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5548  * the resultant file specification is placed.  If outbuf is NULL, the
5549  * resultant file specification is placed into a static buffer.
5550  * The third argument, if non-NULL, is taken to be a default file
5551  * specification string.  The fourth argument is unused at present.
5552  * rmesexpand() returns the address of the resultant string if
5553  * successful, and NULL on error.
5554  *
5555  * New functionality for previously unused opts value:
5556  *  PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5557  *  PERL_RMSEXPAND_M_LONG - Want output in long formst
5558  *  PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5559  *  PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5560  */
5561 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5562 
5563 static char *
5564 int_rmsexpand
5565    (const char *filespec,
5566     char *outbuf,
5567     const char *defspec,
5568     unsigned opts,
5569     int * fs_utf8,
5570     int * dfs_utf8)
5571 {
5572   char * ret_spec;
5573   const char * in_spec;
5574   char * spec_buf;
5575   const char * def_spec;
5576   char * vmsfspec, *vmsdefspec;
5577   char * esa;
5578   char * esal = NULL;
5579   char * outbufl;
5580   struct FAB myfab = cc$rms_fab;
5581   rms_setup_nam(mynam);
5582   STRLEN speclen;
5583   unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5584   int sts;
5585 
5586   /* temp hack until UTF8 is actually implemented */
5587   if (fs_utf8 != NULL)
5588     *fs_utf8 = 0;
5589 
5590   if (!filespec || !*filespec) {
5591     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5592     return NULL;
5593   }
5594 
5595   vmsfspec = NULL;
5596   vmsdefspec = NULL;
5597   outbufl = NULL;
5598 
5599   in_spec = filespec;
5600   isunix = 0;
5601   if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5602       char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5603       int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5604 
5605       /* If this is a UNIX file spec, convert it to VMS */
5606       sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5607                            &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5608                            &e_len, &vs_spec, &vs_len);
5609       if (sts != 0) {
5610           isunix = 1;
5611           char * ret_spec;
5612 
5613           vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5614           if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5615           ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5616           if (ret_spec == NULL) {
5617               PerlMem_free(vmsfspec);
5618               return NULL;
5619           }
5620           in_spec = (const char *)vmsfspec;
5621 
5622           /* Unless we are forcing to VMS format, a UNIX input means
5623            * UNIX output, and that requires long names to be used
5624            */
5625           if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5626 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5627               opts |= PERL_RMSEXPAND_M_LONG;
5628 #else
5629               NOOP;
5630 #endif
5631           else
5632               isunix = 0;
5633       }
5634 
5635   }
5636 
5637   rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5638   rms_bind_fab_nam(myfab, mynam);
5639 
5640   /* Process the default file specification if present */
5641   def_spec = defspec;
5642   if (defspec && *defspec) {
5643     int t_isunix;
5644     t_isunix = is_unix_filespec(defspec);
5645     if (t_isunix) {
5646       vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5647       if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5648       ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5649 
5650       if (ret_spec == NULL) {
5651           /* Clean up and bail */
5652           PerlMem_free(vmsdefspec);
5653           if (vmsfspec != NULL)
5654               PerlMem_free(vmsfspec);
5655               return NULL;
5656           }
5657           def_spec = (const char *)vmsdefspec;
5658       }
5659       rms_set_dna(myfab, mynam,
5660                   (char *)def_spec, strlen(def_spec)); /* cast ok */
5661   }
5662 
5663   /* Now we need the expansion buffers */
5664   esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5665   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5666 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5667   esal = PerlMem_malloc(VMS_MAXRSS);
5668   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5669 #endif
5670   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5671 
5672   /* If a NAML block is used RMS always writes to the long and short
5673    * addresses unless you suppress the short name.
5674    */
5675 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5676   outbufl = PerlMem_malloc(VMS_MAXRSS);
5677   if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5678 #endif
5679    rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5680 
5681 #ifdef NAM$M_NO_SHORT_UPCASE
5682   if (decc_efs_case_preserve)
5683     rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5684 #endif
5685 
5686    /* We may not want to follow symbolic links */
5687 #ifdef NAML$M_OPEN_SPECIAL
5688   if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5689     rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5690 #endif
5691 
5692   /* First attempt to parse as an existing file */
5693   retsts = sys$parse(&myfab,0,0);
5694   if (!(retsts & STS$K_SUCCESS)) {
5695 
5696     /* Could not find the file, try as syntax only if error is not fatal */
5697     rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5698     if (retsts == RMS$_DNF ||
5699         retsts == RMS$_DIR ||
5700         retsts == RMS$_DEV ||
5701         retsts == RMS$_PRV) {
5702       retsts = sys$parse(&myfab,0,0);
5703       if (retsts & STS$K_SUCCESS) goto int_expanded;
5704     }
5705 
5706      /* Still could not parse the file specification */
5707     /*----------------------------------------------*/
5708     sts = rms_free_search_context(&myfab); /* Free search context */
5709     if (vmsdefspec != NULL)
5710 	PerlMem_free(vmsdefspec);
5711     if (vmsfspec != NULL)
5712 	PerlMem_free(vmsfspec);
5713     if (outbufl != NULL)
5714 	PerlMem_free(outbufl);
5715     PerlMem_free(esa);
5716     if (esal != NULL)
5717 	PerlMem_free(esal);
5718     set_vaxc_errno(retsts);
5719     if      (retsts == RMS$_PRV) set_errno(EACCES);
5720     else if (retsts == RMS$_DEV) set_errno(ENODEV);
5721     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5722     else                         set_errno(EVMSERR);
5723     return NULL;
5724   }
5725   retsts = sys$search(&myfab,0,0);
5726   if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5727     sts = rms_free_search_context(&myfab); /* Free search context */
5728     if (vmsdefspec != NULL)
5729 	PerlMem_free(vmsdefspec);
5730     if (vmsfspec != NULL)
5731 	PerlMem_free(vmsfspec);
5732     if (outbufl != NULL)
5733 	PerlMem_free(outbufl);
5734     PerlMem_free(esa);
5735     if (esal != NULL)
5736 	PerlMem_free(esal);
5737     set_vaxc_errno(retsts);
5738     if      (retsts == RMS$_PRV) set_errno(EACCES);
5739     else                         set_errno(EVMSERR);
5740     return NULL;
5741   }
5742 
5743   /* If the input filespec contained any lowercase characters,
5744    * downcase the result for compatibility with Unix-minded code. */
5745 int_expanded:
5746   if (!decc_efs_case_preserve) {
5747     char * tbuf;
5748     for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5749       if (islower(*tbuf)) { haslower = 1; break; }
5750   }
5751 
5752    /* Is a long or a short name expected */
5753   /*------------------------------------*/
5754   spec_buf = NULL;
5755 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5756   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5757     if (rms_nam_rsll(mynam)) {
5758 	spec_buf = outbufl;
5759 	speclen = rms_nam_rsll(mynam);
5760     }
5761     else {
5762 	spec_buf = esal; /* Not esa */
5763 	speclen = rms_nam_esll(mynam);
5764     }
5765   }
5766   else {
5767 #endif
5768     if (rms_nam_rsl(mynam)) {
5769 	spec_buf = outbuf;
5770 	speclen = rms_nam_rsl(mynam);
5771     }
5772     else {
5773 	spec_buf = esa; /* Not esal */
5774 	speclen = rms_nam_esl(mynam);
5775     }
5776 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5777   }
5778 #endif
5779   spec_buf[speclen] = '\0';
5780 
5781   /* Trim off null fields added by $PARSE
5782    * If type > 1 char, must have been specified in original or default spec
5783    * (not true for version; $SEARCH may have added version of existing file).
5784    */
5785   trimver  = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5786   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5787     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5788              ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5789   }
5790   else {
5791     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5792              ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5793   }
5794   if (trimver || trimtype) {
5795     if (defspec && *defspec) {
5796       char *defesal = NULL;
5797       char *defesa = NULL;
5798       defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5799       if (defesa != NULL) {
5800         struct FAB deffab = cc$rms_fab;
5801 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5802         defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5803         if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5804 #endif
5805 	rms_setup_nam(defnam);
5806 
5807 	rms_bind_fab_nam(deffab, defnam);
5808 
5809 	/* Cast ok */
5810 	rms_set_fna
5811 	    (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5812 
5813 	/* RMS needs the esa/esal as a work area if wildcards are involved */
5814 	rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5815 
5816 	rms_clear_nam_nop(defnam);
5817 	rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5818 #ifdef NAM$M_NO_SHORT_UPCASE
5819 	if (decc_efs_case_preserve)
5820 	  rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5821 #endif
5822 #ifdef NAML$M_OPEN_SPECIAL
5823 	if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5824 	  rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5825 #endif
5826 	if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5827 	  if (trimver) {
5828 	     trimver  = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5829 	  }
5830 	  if (trimtype) {
5831 	    trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5832 	  }
5833 	}
5834 	if (defesal != NULL)
5835 	    PerlMem_free(defesal);
5836 	PerlMem_free(defesa);
5837       } else {
5838           _ckvmssts_noperl(SS$_INSFMEM);
5839       }
5840     }
5841     if (trimver) {
5842       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5843 	if (*(rms_nam_verl(mynam)) != '\"')
5844 	  speclen = rms_nam_verl(mynam) - spec_buf;
5845       }
5846       else {
5847 	if (*(rms_nam_ver(mynam)) != '\"')
5848 	  speclen = rms_nam_ver(mynam) - spec_buf;
5849       }
5850     }
5851     if (trimtype) {
5852       /* If we didn't already trim version, copy down */
5853       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5854 	if (speclen > rms_nam_verl(mynam) - spec_buf)
5855 	  memmove
5856 	   (rms_nam_typel(mynam),
5857 	    rms_nam_verl(mynam),
5858 	    speclen - (rms_nam_verl(mynam) - spec_buf));
5859 	  speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5860       }
5861       else {
5862 	if (speclen > rms_nam_ver(mynam) - spec_buf)
5863 	  memmove
5864 	   (rms_nam_type(mynam),
5865 	    rms_nam_ver(mynam),
5866 	    speclen - (rms_nam_ver(mynam) - spec_buf));
5867 	  speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5868       }
5869     }
5870   }
5871 
5872    /* Done with these copies of the input files */
5873   /*-------------------------------------------*/
5874   if (vmsfspec != NULL)
5875 	PerlMem_free(vmsfspec);
5876   if (vmsdefspec != NULL)
5877 	PerlMem_free(vmsdefspec);
5878 
5879   /* If we just had a directory spec on input, $PARSE "helpfully"
5880    * adds an empty name and type for us */
5881 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5882   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5883     if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5884 	rms_nam_verl(mynam)  == rms_nam_typel(mynam) + 1 &&
5885 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5886       speclen = rms_nam_namel(mynam) - spec_buf;
5887   }
5888   else
5889 #endif
5890   {
5891     if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5892 	rms_nam_ver(mynam)  == rms_nam_ver(mynam) + 1 &&
5893 	!(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5894       speclen = rms_nam_name(mynam) - spec_buf;
5895   }
5896 
5897   /* Posix format specifications must have matching quotes */
5898   if (speclen < (VMS_MAXRSS - 1)) {
5899     if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5900       if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5901         spec_buf[speclen] = '\"';
5902         speclen++;
5903       }
5904     }
5905   }
5906   spec_buf[speclen] = '\0';
5907   if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5908 
5909   /* Have we been working with an expanded, but not resultant, spec? */
5910   /* Also, convert back to Unix syntax if necessary. */
5911   {
5912   int rsl;
5913 
5914 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5915     if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5916       rsl = rms_nam_rsll(mynam);
5917     } else
5918 #endif
5919     {
5920       rsl = rms_nam_rsl(mynam);
5921     }
5922     if (!rsl) {
5923       /* rsl is not present, it means that spec_buf is either */
5924       /* esa or esal, and needs to be copied to outbuf */
5925       /* convert to Unix if desired */
5926       if (isunix) {
5927         ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5928       } else {
5929         /* VMS file specs are not in UTF-8 */
5930         if (fs_utf8 != NULL)
5931             *fs_utf8 = 0;
5932         strcpy(outbuf, spec_buf);
5933         ret_spec = outbuf;
5934       }
5935     }
5936     else {
5937       /* Now spec_buf is either outbuf or outbufl */
5938       /* We need the result into outbuf */
5939       if (isunix) {
5940            /* If we need this in UNIX, then we need another buffer */
5941            /* to keep things in order */
5942            char * src;
5943            char * new_src = NULL;
5944            if (spec_buf == outbuf) {
5945                new_src = PerlMem_malloc(VMS_MAXRSS);
5946                strcpy(new_src, spec_buf);
5947            } else {
5948                src = spec_buf;
5949            }
5950            ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5951            if (new_src) {
5952                PerlMem_free(new_src);
5953            }
5954       } else {
5955            /* VMS file specs are not in UTF-8 */
5956            if (fs_utf8 != NULL)
5957                *fs_utf8 = 0;
5958 
5959            /* Copy the buffer if needed */
5960            if (outbuf != spec_buf)
5961                strcpy(outbuf, spec_buf);
5962            ret_spec = outbuf;
5963       }
5964     }
5965   }
5966 
5967   /* Need to clean up the search context */
5968   rms_set_rsal(mynam, NULL, 0, NULL, 0);
5969   sts = rms_free_search_context(&myfab); /* Free search context */
5970 
5971   /* Clean up the extra buffers */
5972   if (esal != NULL)
5973       PerlMem_free(esal);
5974   PerlMem_free(esa);
5975   if (outbufl != NULL)
5976      PerlMem_free(outbufl);
5977 
5978   /* Return the result */
5979   return ret_spec;
5980 }
5981 
5982 /* Common simple case - Expand an already VMS spec */
5983 static char *
5984 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5985     opts |= PERL_RMSEXPAND_M_VMS_IN;
5986     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5987 }
5988 
5989 /* Common simple case - Expand to a VMS spec */
5990 static char *
5991 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5992     opts |= PERL_RMSEXPAND_M_VMS;
5993     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5994 }
5995 
5996 
5997 /* Entry point used by perl routines */
5998 static char *
5999 mp_do_rmsexpand
6000    (pTHX_ const char *filespec,
6001     char *outbuf,
6002     int ts,
6003     const char *defspec,
6004     unsigned opts,
6005     int * fs_utf8,
6006     int * dfs_utf8)
6007 {
6008     static char __rmsexpand_retbuf[VMS_MAXRSS];
6009     char * expanded, *ret_spec, *ret_buf;
6010 
6011     expanded = NULL;
6012     ret_buf = outbuf;
6013     if (ret_buf == NULL) {
6014         if (ts) {
6015             Newx(expanded, VMS_MAXRSS, char);
6016             if (expanded == NULL)
6017                 _ckvmssts(SS$_INSFMEM);
6018             ret_buf = expanded;
6019         } else {
6020             ret_buf = __rmsexpand_retbuf;
6021         }
6022     }
6023 
6024 
6025     ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
6026                              opts, fs_utf8,  dfs_utf8);
6027 
6028     if (ret_spec == NULL) {
6029        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6030        if (expanded)
6031            Safefree(expanded);
6032     }
6033 
6034     return ret_spec;
6035 }
6036 /*}}}*/
6037 /* External entry points */
6038 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6039 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
6040 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6041 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
6042 char *Perl_rmsexpand_utf8
6043   (pTHX_ const char *spec, char *buf, const char *def,
6044    unsigned opt, int * fs_utf8, int * dfs_utf8)
6045 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
6046 char *Perl_rmsexpand_utf8_ts
6047   (pTHX_ const char *spec, char *buf, const char *def,
6048    unsigned opt, int * fs_utf8, int * dfs_utf8)
6049 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
6050 
6051 
6052 /*
6053 ** The following routines are provided to make life easier when
6054 ** converting among VMS-style and Unix-style directory specifications.
6055 ** All will take input specifications in either VMS or Unix syntax. On
6056 ** failure, all return NULL.  If successful, the routines listed below
6057 ** return a pointer to a buffer containing the appropriately
6058 ** reformatted spec (and, therefore, subsequent calls to that routine
6059 ** will clobber the result), while the routines of the same names with
6060 ** a _ts suffix appended will return a pointer to a mallocd string
6061 ** containing the appropriately reformatted spec.
6062 ** In all cases, only explicit syntax is altered; no check is made that
6063 ** the resulting string is valid or that the directory in question
6064 ** actually exists.
6065 **
6066 **   fileify_dirspec() - convert a directory spec into the name of the
6067 **     directory file (i.e. what you can stat() to see if it's a dir).
6068 **     The style (VMS or Unix) of the result is the same as the style
6069 **     of the parameter passed in.
6070 **   pathify_dirspec() - convert a directory spec into a path (i.e.
6071 **     what you prepend to a filename to indicate what directory it's in).
6072 **     The style (VMS or Unix) of the result is the same as the style
6073 **     of the parameter passed in.
6074 **   tounixpath() - convert a directory spec into a Unix-style path.
6075 **   tovmspath() - convert a directory spec into a VMS-style path.
6076 **   tounixspec() - convert any file spec into a Unix-style file spec.
6077 **   tovmsspec() - convert any file spec into a VMS-style spec.
6078 **   xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6079 **
6080 ** Copyright 1996 by Charles Bailey  <bailey@newman.upenn.edu>
6081 ** Permission is given to distribute this code as part of the Perl
6082 ** standard distribution under the terms of the GNU General Public
6083 ** License or the Perl Artistic License.  Copies of each may be
6084 ** found in the Perl standard distribution.
6085  */
6086 
6087 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6088 static char *
6089 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6090 {
6091     unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
6092     char *cp1, *cp2, *lastdir;
6093     char *trndir, *vmsdir;
6094     unsigned short int trnlnm_iter_count;
6095     int is_vms = 0;
6096     int is_unix = 0;
6097     int sts;
6098     if (utf8_fl != NULL)
6099 	*utf8_fl = 0;
6100 
6101     if (!dir || !*dir) {
6102       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6103     }
6104     dirlen = strlen(dir);
6105     while (dirlen && dir[dirlen-1] == '/') --dirlen;
6106     if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6107       if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6108         dir = "/sys$disk";
6109         dirlen = 9;
6110       }
6111       else
6112 	dirlen = 1;
6113     }
6114     if (dirlen > (VMS_MAXRSS - 1)) {
6115       set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6116       return NULL;
6117     }
6118     trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6119     if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6120     if (!strpbrk(dir+1,"/]>:")  &&
6121 	(!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6122       strcpy(trndir,*dir == '/' ? dir + 1: dir);
6123       trnlnm_iter_count = 0;
6124       while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6125         trnlnm_iter_count++;
6126         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6127       }
6128       dirlen = strlen(trndir);
6129     }
6130     else {
6131       strncpy(trndir,dir,dirlen);
6132       trndir[dirlen] = '\0';
6133     }
6134 
6135     /* At this point we are done with *dir and use *trndir which is a
6136      * copy that can be modified.  *dir must not be modified.
6137      */
6138 
6139     /* If we were handed a rooted logical name or spec, treat it like a
6140      * simple directory, so that
6141      *    $ Define myroot dev:[dir.]
6142      *    ... do_fileify_dirspec("myroot",buf,1) ...
6143      * does something useful.
6144      */
6145     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6146       trndir[--dirlen] = '\0';
6147       trndir[dirlen-1] = ']';
6148     }
6149     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6150       trndir[--dirlen] = '\0';
6151       trndir[dirlen-1] = '>';
6152     }
6153 
6154     if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6155       /* If we've got an explicit filename, we can just shuffle the string. */
6156       if (*(cp1+1)) hasfilename = 1;
6157       /* Similarly, we can just back up a level if we've got multiple levels
6158          of explicit directories in a VMS spec which ends with directories. */
6159       else {
6160         for (cp2 = cp1; cp2 > trndir; cp2--) {
6161 	  if (*cp2 == '.') {
6162 	    if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6163 /* fix-me, can not scan EFS file specs backward like this */
6164               *cp2 = *cp1; *cp1 = '\0';
6165               hasfilename = 1;
6166 	      break;
6167 	    }
6168           }
6169           if (*cp2 == '[' || *cp2 == '<') break;
6170         }
6171       }
6172     }
6173 
6174     vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6175     if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6176     cp1 = strpbrk(trndir,"]:>");
6177     if (hasfilename || !cp1) { /* filename present or not VMS */
6178 
6179       if (decc_efs_charset && !cp1) {
6180 
6181           /* EFS handling for UNIX mode */
6182 
6183           /* Just remove the trailing '/' and we should be done */
6184           STRLEN trndir_len;
6185           trndir_len = strlen(trndir);
6186 
6187           if (trndir_len > 1) {
6188               trndir_len--;
6189               if (trndir[trndir_len] == '/') {
6190                   trndir[trndir_len] = '\0';
6191               }
6192           }
6193           strcpy(buf, trndir);
6194           PerlMem_free(trndir);
6195           PerlMem_free(vmsdir);
6196           return buf;
6197       }
6198 
6199       /* For non-EFS mode, this is left for backwards compatibility */
6200       /* For EFS mode, this is only done for VMS format filespecs as */
6201       /* Perl programs generally have problems when a UNIX format spec */
6202       /* returns a VMS format spec */
6203       if (trndir[0] == '.') {
6204         if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6205 	  PerlMem_free(trndir);
6206 	  PerlMem_free(vmsdir);
6207           return int_fileify_dirspec("[]", buf, NULL);
6208 	}
6209         else if (trndir[1] == '.' &&
6210                (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6211 	  PerlMem_free(trndir);
6212 	  PerlMem_free(vmsdir);
6213           return int_fileify_dirspec("[-]", buf, NULL);
6214 	}
6215       }
6216       if (dirlen && trndir[dirlen-1] == '/') {    /* path ends with '/'; just add .dir;1 */
6217         dirlen -= 1;                 /* to last element */
6218         lastdir = strrchr(trndir,'/');
6219       }
6220       else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6221         /* If we have "/." or "/..", VMSify it and let the VMS code
6222          * below expand it, rather than repeating the code to handle
6223          * relative components of a filespec here */
6224         do {
6225           if (*(cp1+2) == '.') cp1++;
6226           if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6227 	    char * ret_chr;
6228             if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6229 		PerlMem_free(trndir);
6230 		PerlMem_free(vmsdir);
6231 		return NULL;
6232 	    }
6233             if (strchr(vmsdir,'/') != NULL) {
6234               /* If int_tovmsspec() returned it, it must have VMS syntax
6235                * delimiters in it, so it's a mixed VMS/Unix spec.  We take
6236                * the time to check this here only so we avoid a recursion
6237                * loop; otherwise, gigo.
6238                */
6239 	      PerlMem_free(trndir);
6240 	      PerlMem_free(vmsdir);
6241               set_errno(EINVAL);  set_vaxc_errno(RMS$_SYN);
6242 	      return NULL;
6243             }
6244             if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6245 		PerlMem_free(trndir);
6246 		PerlMem_free(vmsdir);
6247 		return NULL;
6248 	    }
6249 	    ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6250 	    PerlMem_free(trndir);
6251 	    PerlMem_free(vmsdir);
6252             return ret_chr;
6253           }
6254           cp1++;
6255         } while ((cp1 = strstr(cp1,"/.")) != NULL);
6256         lastdir = strrchr(trndir,'/');
6257       }
6258       else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6259 	char * ret_chr;
6260         /* Ditto for specs that end in an MFD -- let the VMS code
6261          * figure out whether it's a real device or a rooted logical. */
6262 
6263         /* This should not happen any more.  Allowing the fake /000000
6264          * in a UNIX pathname causes all sorts of problems when trying
6265          * to run in UNIX emulation.  So the VMS to UNIX conversions
6266          * now remove the fake /000000 directories.
6267          */
6268 
6269         trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6270         if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6271 	    PerlMem_free(trndir);
6272 	    PerlMem_free(vmsdir);
6273 	    return NULL;
6274 	}
6275         if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6276 	    PerlMem_free(trndir);
6277 	    PerlMem_free(vmsdir);
6278 	    return NULL;
6279 	}
6280 	ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6281 	PerlMem_free(trndir);
6282 	PerlMem_free(vmsdir);
6283         return ret_chr;
6284       }
6285       else {
6286 
6287         if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6288              !(lastdir = cp1 = strrchr(trndir,']')) &&
6289              !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6290 
6291         cp2 = strrchr(cp1,'.');
6292         if (cp2) {
6293             int e_len, vs_len = 0;
6294             int is_dir = 0;
6295             char * cp3;
6296             cp3 = strchr(cp2,';');
6297             e_len = strlen(cp2);
6298             if (cp3) {
6299                 vs_len = strlen(cp3);
6300                 e_len = e_len - vs_len;
6301             }
6302             is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6303             if (!is_dir) {
6304                 if (!decc_efs_charset) {
6305                     /* If this is not EFS, then not a directory */
6306                     PerlMem_free(trndir);
6307                     PerlMem_free(vmsdir);
6308                     set_errno(ENOTDIR);
6309                     set_vaxc_errno(RMS$_DIR);
6310                     return NULL;
6311                 }
6312             } else {
6313                 /* Ok, here we have an issue, technically if a .dir shows */
6314                 /* from inside a directory, then we should treat it as */
6315                 /* xxx^.dir.dir.  But we do not have that context at this */
6316                 /* point unless this is totally restructured, so we remove */
6317                 /* The .dir for now, and fix this better later */
6318                 dirlen = cp2 - trndir;
6319             }
6320         }
6321 
6322       }
6323 
6324       retlen = dirlen + 6;
6325       memcpy(buf, trndir, dirlen);
6326       buf[dirlen] = '\0';
6327 
6328       /* We've picked up everything up to the directory file name.
6329          Now just add the type and version, and we're set. */
6330 
6331       /* We should only add type for VMS syntax, but historically Perl
6332          has added it for UNIX style also */
6333 
6334       /* Fix me - we should not be using the same routine for VMS and
6335          UNIX format files.  Things are too tangled so we need to lookup
6336          what syntax the output is */
6337 
6338       is_unix = 0;
6339       is_vms = 0;
6340       lastdir = strrchr(trndir,'/');
6341       if (lastdir) {
6342           is_unix = 1;
6343       } else {
6344           lastdir = strpbrk(trndir,"]:>");
6345           if (lastdir) {
6346               is_vms = 1;
6347           }
6348       }
6349 
6350       if ((is_vms == 0) && (is_unix == 0)) {
6351           /* We still do not  know? */
6352           is_unix = decc_filename_unix_report;
6353           if (is_unix == 0)
6354               is_vms = 1;
6355       }
6356 
6357       if ((is_unix && !decc_efs_charset) || is_vms) {
6358 
6359            /* It is a bug to add a .dir to a UNIX format directory spec */
6360            /* However Perl on VMS may have programs that expect this so */
6361            /* If not using EFS character specifications allow it. */
6362 
6363            if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6364                /* Traditionally Perl expects filenames in lower case */
6365                strcat(buf, ".dir");
6366            } else {
6367                /* VMS expects the .DIR to be in upper case */
6368                strcat(buf, ".DIR");
6369            }
6370 
6371            /* It is also a bug to put a VMS format version on a UNIX file */
6372            /* specification.  Perl self tests are looking for this */
6373            if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6374                strcat(buf, ";1");
6375       }
6376       PerlMem_free(trndir);
6377       PerlMem_free(vmsdir);
6378       return buf;
6379     }
6380     else {  /* VMS-style directory spec */
6381 
6382       char *esa, *esal, term, *cp;
6383       char *my_esa;
6384       int my_esa_len;
6385       unsigned long int sts, cmplen, haslower = 0;
6386       unsigned int nam_fnb;
6387       char * nam_type;
6388       struct FAB dirfab = cc$rms_fab;
6389       rms_setup_nam(savnam);
6390       rms_setup_nam(dirnam);
6391 
6392       esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6393       if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6394       esal = NULL;
6395 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6396       esal = PerlMem_malloc(VMS_MAXRSS);
6397       if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6398 #endif
6399       rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6400       rms_bind_fab_nam(dirfab, dirnam);
6401       rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6402       rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6403 #ifdef NAM$M_NO_SHORT_UPCASE
6404       if (decc_efs_case_preserve)
6405 	rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6406 #endif
6407 
6408       for (cp = trndir; *cp; cp++)
6409         if (islower(*cp)) { haslower = 1; break; }
6410       if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6411         if ((dirfab.fab$l_sts == RMS$_DIR) ||
6412             (dirfab.fab$l_sts == RMS$_DNF) ||
6413             (dirfab.fab$l_sts == RMS$_PRV)) {
6414             rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6415             sts = sys$parse(&dirfab);
6416         }
6417         if (!sts) {
6418 	  PerlMem_free(esa);
6419 	  if (esal != NULL)
6420 	      PerlMem_free(esal);
6421 	  PerlMem_free(trndir);
6422 	  PerlMem_free(vmsdir);
6423           set_errno(EVMSERR);
6424           set_vaxc_errno(dirfab.fab$l_sts);
6425           return NULL;
6426         }
6427       }
6428       else {
6429         savnam = dirnam;
6430 	/* Does the file really exist? */
6431         if (sys$search(&dirfab)& STS$K_SUCCESS) {
6432           /* Yes; fake the fnb bits so we'll check type below */
6433           rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6434         }
6435         else { /* No; just work with potential name */
6436           if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6437           else {
6438 	    int fab_sts;
6439 	    fab_sts = dirfab.fab$l_sts;
6440 	    sts = rms_free_search_context(&dirfab);
6441 	    PerlMem_free(esa);
6442 	    if (esal != NULL)
6443 		PerlMem_free(esal);
6444 	    PerlMem_free(trndir);
6445 	    PerlMem_free(vmsdir);
6446             set_errno(EVMSERR);  set_vaxc_errno(fab_sts);
6447             return NULL;
6448           }
6449         }
6450       }
6451 
6452       /* Make sure we are using the right buffer */
6453 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6454       if (esal != NULL) {
6455 	my_esa = esal;
6456 	my_esa_len = rms_nam_esll(dirnam);
6457       } else {
6458 #endif
6459 	my_esa = esa;
6460         my_esa_len = rms_nam_esl(dirnam);
6461 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6462       }
6463 #endif
6464       my_esa[my_esa_len] = '\0';
6465       if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6466         cp1 = strchr(my_esa,']');
6467         if (!cp1) cp1 = strchr(my_esa,'>');
6468         if (cp1) {  /* Should always be true */
6469           my_esa_len -= cp1 - my_esa - 1;
6470           memmove(my_esa, cp1 + 1, my_esa_len);
6471         }
6472       }
6473       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) {  /* Was type specified? */
6474         /* Yep; check version while we're at it, if it's there. */
6475         cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6476         if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6477           /* Something other than .DIR[;1].  Bzzt. */
6478 	  sts = rms_free_search_context(&dirfab);
6479 	  PerlMem_free(esa);
6480 	  if (esal != NULL)
6481 	     PerlMem_free(esal);
6482 	  PerlMem_free(trndir);
6483 	  PerlMem_free(vmsdir);
6484           set_errno(ENOTDIR);
6485           set_vaxc_errno(RMS$_DIR);
6486           return NULL;
6487         }
6488       }
6489 
6490       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6491         /* They provided at least the name; we added the type, if necessary, */
6492         strcpy(buf, my_esa);
6493 	sts = rms_free_search_context(&dirfab);
6494 	PerlMem_free(trndir);
6495 	PerlMem_free(esa);
6496 	if (esal != NULL)
6497 	    PerlMem_free(esal);
6498 	PerlMem_free(vmsdir);
6499         return buf;
6500       }
6501       if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6502         for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6503         *cp1 = '\0';
6504         my_esa_len -= 9;
6505       }
6506       if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6507       if (cp1 == NULL) { /* should never happen */
6508 	sts = rms_free_search_context(&dirfab);
6509 	PerlMem_free(trndir);
6510 	PerlMem_free(esa);
6511 	if (esal != NULL)
6512 	    PerlMem_free(esal);
6513 	PerlMem_free(vmsdir);
6514         return NULL;
6515       }
6516       term = *cp1;
6517       *cp1 = '\0';
6518       retlen = strlen(my_esa);
6519       cp1 = strrchr(my_esa,'.');
6520       /* ODS-5 directory specifications can have extra "." in them. */
6521       /* Fix-me, can not scan EFS file specifications backwards */
6522       while (cp1 != NULL) {
6523         if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6524 	  break;
6525 	else {
6526 	   cp1--;
6527 	   while ((cp1 > my_esa) && (*cp1 != '.'))
6528 	     cp1--;
6529 	}
6530 	if (cp1 == my_esa)
6531 	  cp1 = NULL;
6532       }
6533 
6534       if ((cp1) != NULL) {
6535         /* There's more than one directory in the path.  Just roll back. */
6536         *cp1 = term;
6537         strcpy(buf, my_esa);
6538       }
6539       else {
6540         if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6541           /* Go back and expand rooted logical name */
6542           rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6543 #ifdef NAM$M_NO_SHORT_UPCASE
6544 	  if (decc_efs_case_preserve)
6545 	    rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6546 #endif
6547           if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6548 	    sts = rms_free_search_context(&dirfab);
6549 	    PerlMem_free(esa);
6550 	    if (esal != NULL)
6551 		PerlMem_free(esal);
6552 	    PerlMem_free(trndir);
6553 	    PerlMem_free(vmsdir);
6554             set_errno(EVMSERR);
6555             set_vaxc_errno(dirfab.fab$l_sts);
6556             return NULL;
6557           }
6558 
6559 	  /* This changes the length of the string of course */
6560 	  if (esal != NULL) {
6561 	      my_esa_len = rms_nam_esll(dirnam);
6562 	  } else {
6563 	      my_esa_len = rms_nam_esl(dirnam);
6564 	  }
6565 
6566           retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6567           cp1 = strstr(my_esa,"][");
6568           if (!cp1) cp1 = strstr(my_esa,"]<");
6569           dirlen = cp1 - my_esa;
6570           memcpy(buf, my_esa, dirlen);
6571           if (!strncmp(cp1+2,"000000]",7)) {
6572             buf[dirlen-1] = '\0';
6573 	    /* fix-me Not full ODS-5, just extra dots in directories for now */
6574 	    cp1 = buf + dirlen - 1;
6575 	    while (cp1 > buf)
6576 	    {
6577 	      if (*cp1 == '[')
6578 		break;
6579 	      if (*cp1 == '.') {
6580 		if (*(cp1-1) != '^')
6581 		  break;
6582 	      }
6583 	      cp1--;
6584 	    }
6585             if (*cp1 == '.') *cp1 = ']';
6586             else {
6587               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6588               memmove(cp1+1,"000000]",7);
6589             }
6590           }
6591           else {
6592             memmove(buf+dirlen, cp1+2, retlen-dirlen);
6593             buf[retlen] = '\0';
6594             /* Convert last '.' to ']' */
6595             cp1 = buf+retlen-1;
6596 	    while (*cp != '[') {
6597 	      cp1--;
6598 	      if (*cp1 == '.') {
6599 		/* Do not trip on extra dots in ODS-5 directories */
6600 		if ((cp1 == buf) || (*(cp1-1) != '^'))
6601 		break;
6602 	      }
6603 	    }
6604             if (*cp1 == '.') *cp1 = ']';
6605             else {
6606               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6607               memmove(cp1+1,"000000]",7);
6608             }
6609           }
6610         }
6611         else {  /* This is a top-level dir.  Add the MFD to the path. */
6612           cp1 = my_esa;
6613           cp2 = buf;
6614           while ((*cp1 != ':')  && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6615           strcpy(cp2,":[000000]");
6616           cp1 += 2;
6617           strcpy(cp2+9,cp1);
6618         }
6619       }
6620       sts = rms_free_search_context(&dirfab);
6621       /* We've set up the string up through the filename.  Add the
6622          type and version, and we're done. */
6623       strcat(buf,".DIR;1");
6624 
6625       /* $PARSE may have upcased filespec, so convert output to lower
6626        * case if input contained any lowercase characters. */
6627       if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6628       PerlMem_free(trndir);
6629       PerlMem_free(esa);
6630       if (esal != NULL)
6631 	PerlMem_free(esal);
6632       PerlMem_free(vmsdir);
6633       return buf;
6634     }
6635 }  /* end of int_fileify_dirspec() */
6636 
6637 
6638 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6639 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6640 {
6641     static char __fileify_retbuf[VMS_MAXRSS];
6642     char * fileified, *ret_spec, *ret_buf;
6643 
6644     fileified = NULL;
6645     ret_buf = buf;
6646     if (ret_buf == NULL) {
6647         if (ts) {
6648             Newx(fileified, VMS_MAXRSS, char);
6649             if (fileified == NULL)
6650                 _ckvmssts(SS$_INSFMEM);
6651             ret_buf = fileified;
6652         } else {
6653             ret_buf = __fileify_retbuf;
6654         }
6655     }
6656 
6657     ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6658 
6659     if (ret_spec == NULL) {
6660        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6661        if (fileified)
6662            Safefree(fileified);
6663     }
6664 
6665     return ret_spec;
6666 }  /* end of do_fileify_dirspec() */
6667 /*}}}*/
6668 
6669 /* External entry points */
6670 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6671 { return do_fileify_dirspec(dir,buf,0,NULL); }
6672 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6673 { return do_fileify_dirspec(dir,buf,1,NULL); }
6674 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6675 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6676 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6677 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6678 
6679 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6680     char * v_spec, int v_len, char * r_spec, int r_len,
6681     char * d_spec, int d_len, char * n_spec, int n_len,
6682     char * e_spec, int e_len, char * vs_spec, int vs_len) {
6683 
6684     /* VMS specification - Try to do this the simple way */
6685     if ((v_len + r_len > 0) || (d_len > 0)) {
6686         int is_dir;
6687 
6688         /* No name or extension component, already a directory */
6689         if ((n_len + e_len + vs_len) == 0) {
6690             strcpy(buf, dir);
6691             return buf;
6692         }
6693 
6694         /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6695         /* This results from catfile() being used instead of catdir() */
6696         /* So even though it should not work, we need to allow it */
6697 
6698         /* If this is .DIR;1 then do a simple conversion */
6699         is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6700         if (is_dir || (e_len == 0) && (d_len > 0)) {
6701              int len;
6702              len = v_len + r_len + d_len - 1;
6703              char dclose = d_spec[d_len - 1];
6704              strncpy(buf, dir, len);
6705              buf[len] = '.';
6706              len++;
6707              strncpy(&buf[len], n_spec, n_len);
6708              len += n_len;
6709              buf[len] = dclose;
6710              buf[len + 1] = '\0';
6711              return buf;
6712         }
6713 
6714 #ifdef HAS_SYMLINK
6715         else if (d_len > 0) {
6716             /* In the olden days, a directory needed to have a .DIR */
6717             /* extension to be a valid directory, but now it could  */
6718             /* be a symbolic link */
6719             int len;
6720             len = v_len + r_len + d_len - 1;
6721             char dclose = d_spec[d_len - 1];
6722             strncpy(buf, dir, len);
6723             buf[len] = '.';
6724             len++;
6725             strncpy(&buf[len], n_spec, n_len);
6726             len += n_len;
6727             if (e_len > 0) {
6728                 if (decc_efs_charset) {
6729                     buf[len] = '^';
6730                     len++;
6731                     strncpy(&buf[len], e_spec, e_len);
6732                     len += e_len;
6733                 } else {
6734                     set_vaxc_errno(RMS$_DIR);
6735                     set_errno(ENOTDIR);
6736                     return NULL;
6737                 }
6738             }
6739             buf[len] = dclose;
6740             buf[len + 1] = '\0';
6741             return buf;
6742         }
6743 #else
6744         else {
6745             set_vaxc_errno(RMS$_DIR);
6746             set_errno(ENOTDIR);
6747             return NULL;
6748         }
6749 #endif
6750     }
6751     set_vaxc_errno(RMS$_DIR);
6752     set_errno(ENOTDIR);
6753     return NULL;
6754 }
6755 
6756 
6757 /* Internal routine to make sure or convert a directory to be in a */
6758 /* path specification.  No utf8 flag because it is not changed or used */
6759 static char *int_pathify_dirspec(const char *dir, char *buf)
6760 {
6761     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6762     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6763     char * exp_spec, *ret_spec;
6764     char * trndir;
6765     unsigned short int trnlnm_iter_count;
6766     STRLEN trnlen;
6767     int need_to_lower;
6768 
6769     if (vms_debug_fileify) {
6770         if (dir == NULL)
6771             fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6772         else
6773             fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6774     }
6775 
6776     /* We may need to lower case the result if we translated  */
6777     /* a logical name or got the current working directory */
6778     need_to_lower = 0;
6779 
6780     if (!dir || !*dir) {
6781       set_errno(EINVAL);
6782       set_vaxc_errno(SS$_BADPARAM);
6783       return NULL;
6784     }
6785 
6786     trndir = PerlMem_malloc(VMS_MAXRSS);
6787     if (trndir == NULL)
6788         _ckvmssts_noperl(SS$_INSFMEM);
6789 
6790     /* If no directory specified use the current default */
6791     if (*dir)
6792         strcpy(trndir, dir);
6793     else {
6794         getcwd(trndir, VMS_MAXRSS - 1);
6795         need_to_lower = 1;
6796     }
6797 
6798     /* now deal with bare names that could be logical names */
6799     trnlnm_iter_count = 0;
6800     while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6801            && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6802         trnlnm_iter_count++;
6803         need_to_lower = 1;
6804         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6805             break;
6806         trnlen = strlen(trndir);
6807 
6808         /* Trap simple rooted lnms, and return lnm:[000000] */
6809         if (!strcmp(trndir+trnlen-2,".]")) {
6810             strcpy(buf, dir);
6811             strcat(buf, ":[000000]");
6812             PerlMem_free(trndir);
6813 
6814             if (vms_debug_fileify) {
6815                 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6816             }
6817             return buf;
6818         }
6819     }
6820 
6821     /* At this point we do not work with *dir, but the copy in  *trndir */
6822 
6823     if (need_to_lower && !decc_efs_case_preserve) {
6824         /* Legacy mode, lower case the returned value */
6825         __mystrtolower(trndir);
6826     }
6827 
6828 
6829     /* Some special cases, '..', '.' */
6830     sts = 0;
6831     if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6832        /* Force UNIX filespec */
6833        sts = 1;
6834 
6835     } else {
6836         /* Is this Unix or VMS format? */
6837         sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6838                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6839                              &e_len, &vs_spec, &vs_len);
6840         if (sts == 0) {
6841 
6842             /* Just a filename? */
6843             if ((v_len + r_len + d_len) == 0) {
6844 
6845                 /* Now we have a problem, this could be Unix or VMS */
6846                 /* We have to guess.  .DIR usually means VMS */
6847 
6848                 /* In UNIX report mode, the .DIR extension is removed */
6849                 /* if one shows up, it is for a non-directory or a directory */
6850                 /* in EFS charset mode */
6851 
6852                 /* So if we are in Unix report mode, assume that this */
6853                 /* is a relative Unix directory specification */
6854 
6855                 sts = 1;
6856                 if (!decc_filename_unix_report && decc_efs_charset) {
6857                     int is_dir;
6858                     is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6859 
6860                     if (is_dir) {
6861                         /* Traditional mode, assume .DIR is directory */
6862                         buf[0] = '[';
6863                         buf[1] = '.';
6864                         strncpy(&buf[2], n_spec, n_len);
6865                         buf[n_len + 2] = ']';
6866                         buf[n_len + 3] = '\0';
6867                         PerlMem_free(trndir);
6868                         if (vms_debug_fileify) {
6869                             fprintf(stderr,
6870                                     "int_pathify_dirspec: buf = %s\n",
6871                                     buf);
6872                         }
6873                         return buf;
6874                     }
6875                 }
6876             }
6877         }
6878     }
6879     if (sts == 0) {
6880         ret_spec = int_pathify_dirspec_simple(trndir, buf,
6881             v_spec, v_len, r_spec, r_len,
6882             d_spec, d_len, n_spec, n_len,
6883             e_spec, e_len, vs_spec, vs_len);
6884 
6885         if (ret_spec != NULL) {
6886             PerlMem_free(trndir);
6887             if (vms_debug_fileify) {
6888                 fprintf(stderr,
6889                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6890             }
6891             return ret_spec;
6892         }
6893 
6894         /* Simple way did not work, which means that a logical name */
6895         /* was present for the directory specification.             */
6896         /* Need to use an rmsexpand variant to decode it completely */
6897         exp_spec = PerlMem_malloc(VMS_MAXRSS);
6898         if (exp_spec == NULL)
6899             _ckvmssts_noperl(SS$_INSFMEM);
6900 
6901         ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6902         if (ret_spec != NULL) {
6903             sts = vms_split_path(exp_spec, &v_spec, &v_len,
6904                                  &r_spec, &r_len, &d_spec, &d_len,
6905                                  &n_spec, &n_len, &e_spec,
6906                                  &e_len, &vs_spec, &vs_len);
6907             if (sts == 0) {
6908                 ret_spec = int_pathify_dirspec_simple(
6909                     exp_spec, buf, v_spec, v_len, r_spec, r_len,
6910                     d_spec, d_len, n_spec, n_len,
6911                     e_spec, e_len, vs_spec, vs_len);
6912 
6913                 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6914                     /* Legacy mode, lower case the returned value */
6915                     __mystrtolower(ret_spec);
6916                 }
6917             } else {
6918                 set_vaxc_errno(RMS$_DIR);
6919                 set_errno(ENOTDIR);
6920                 ret_spec = NULL;
6921             }
6922         }
6923         PerlMem_free(exp_spec);
6924         PerlMem_free(trndir);
6925         if (vms_debug_fileify) {
6926             if (ret_spec == NULL)
6927                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6928             else
6929                 fprintf(stderr,
6930                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6931         }
6932         return ret_spec;
6933 
6934     } else {
6935         /* Unix specification, Could be trivial conversion */
6936         STRLEN dir_len;
6937         dir_len = strlen(trndir);
6938 
6939         /* If the extended file character set is in effect */
6940         /* then pathify is simple */
6941 
6942         if (!decc_efs_charset) {
6943             /* Have to deal with traiing '.dir' or extra '.' */
6944             /* that should not be there in legacy mode, but is */
6945 
6946             char * lastdot;
6947             char * lastslash;
6948             int is_dir;
6949 
6950             lastslash = strrchr(trndir, '/');
6951             if (lastslash == NULL)
6952                 lastslash = trndir;
6953             else
6954                 lastslash++;
6955 
6956             lastdot = NULL;
6957 
6958             /* '..' or '.' are valid directory components */
6959             is_dir = 0;
6960             if (lastslash[0] == '.') {
6961                 if (lastslash[1] == '\0') {
6962                    is_dir = 1;
6963                 } else if (lastslash[1] == '.') {
6964                     if (lastslash[2] == '\0') {
6965                         is_dir = 1;
6966                     } else {
6967                         /* And finally allow '...' */
6968                         if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6969                             is_dir = 1;
6970                         }
6971                     }
6972                 }
6973             }
6974 
6975             if (!is_dir) {
6976                lastdot = strrchr(lastslash, '.');
6977             }
6978             if (lastdot != NULL) {
6979                 STRLEN e_len;
6980 
6981                 /* '.dir' is discarded, and any other '.' is invalid */
6982                 e_len = strlen(lastdot);
6983 
6984                 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6985 
6986                 if (is_dir) {
6987                     dir_len = dir_len - 4;
6988 
6989                 }
6990             }
6991         }
6992 
6993         strcpy(buf, trndir);
6994         if (buf[dir_len - 1] != '/') {
6995             buf[dir_len] = '/';
6996             buf[dir_len + 1] = '\0';
6997         }
6998 
6999         /* Under ODS-2 rules, '.' becomes '_', so fix it up */
7000         if (!decc_efs_charset) {
7001              int dir_start = 0;
7002              char * str = buf;
7003              if (str[0] == '.') {
7004                  char * dots = str;
7005                  int cnt = 1;
7006                  while ((dots[cnt] == '.') && (cnt < 3))
7007                      cnt++;
7008                  if (cnt <= 3) {
7009                      if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
7010                          dir_start = 1;
7011                          str += cnt;
7012                      }
7013                  }
7014              }
7015              for (; *str; ++str) {
7016                  while (*str == '/') {
7017                      dir_start = 1;
7018                      *str++;
7019                  }
7020                  if (dir_start) {
7021 
7022                      /* Have to skip up to three dots which could be */
7023                      /* directories, 3 dots being a VMS extension for Perl */
7024                      char * dots = str;
7025                      int cnt = 0;
7026                      while ((dots[cnt] == '.') && (cnt < 3)) {
7027                          cnt++;
7028                      }
7029                      if (dots[cnt] == '\0')
7030                          break;
7031                      if ((cnt > 1) && (dots[cnt] != '/')) {
7032                          dir_start = 0;
7033                      } else {
7034                          str += cnt;
7035                      }
7036 
7037                      /* too many dots? */
7038                      if ((cnt == 0) || (cnt > 3)) {
7039                          dir_start = 0;
7040                      }
7041                  }
7042                  if (!dir_start && (*str == '.')) {
7043                      *str = '_';
7044                  }
7045              }
7046         }
7047         PerlMem_free(trndir);
7048         ret_spec = buf;
7049         if (vms_debug_fileify) {
7050             if (ret_spec == NULL)
7051                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
7052             else
7053                 fprintf(stderr,
7054                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
7055         }
7056         return ret_spec;
7057     }
7058 }
7059 
7060 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
7061 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
7062 {
7063     static char __pathify_retbuf[VMS_MAXRSS];
7064     char * pathified, *ret_spec, *ret_buf;
7065 
7066     pathified = NULL;
7067     ret_buf = buf;
7068     if (ret_buf == NULL) {
7069         if (ts) {
7070             Newx(pathified, VMS_MAXRSS, char);
7071             if (pathified == NULL)
7072                 _ckvmssts(SS$_INSFMEM);
7073             ret_buf = pathified;
7074         } else {
7075             ret_buf = __pathify_retbuf;
7076         }
7077     }
7078 
7079     ret_spec = int_pathify_dirspec(dir, ret_buf);
7080 
7081     if (ret_spec == NULL) {
7082        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7083        if (pathified)
7084            Safefree(pathified);
7085     }
7086 
7087     return ret_spec;
7088 
7089 }  /* end of do_pathify_dirspec() */
7090 
7091 
7092 /* External entry points */
7093 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
7094 { return do_pathify_dirspec(dir,buf,0,NULL); }
7095 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7096 { return do_pathify_dirspec(dir,buf,1,NULL); }
7097 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7098 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7099 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7100 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
7101 
7102 /* Internal tounixspec routine that does not use a thread context */
7103 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7104 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7105 {
7106   char *dirend, *cp1, *cp3, *tmp;
7107   const char *cp2;
7108   int devlen, dirlen, retlen = VMS_MAXRSS;
7109   int expand = 1; /* guarantee room for leading and trailing slashes */
7110   unsigned short int trnlnm_iter_count;
7111   int cmp_rslt;
7112   if (utf8_fl != NULL)
7113     *utf8_fl = 0;
7114 
7115   if (vms_debug_fileify) {
7116       if (spec == NULL)
7117           fprintf(stderr, "int_tounixspec: spec = NULL\n");
7118       else
7119           fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7120   }
7121 
7122 
7123   if (spec == NULL) {
7124       set_errno(EINVAL);
7125       set_vaxc_errno(SS$_BADPARAM);
7126       return NULL;
7127   }
7128   if (strlen(spec) > (VMS_MAXRSS-1)) {
7129       set_errno(E2BIG);
7130       set_vaxc_errno(SS$_BUFFEROVF);
7131       return NULL;
7132   }
7133 
7134   /* New VMS specific format needs translation
7135    * glob passes filenames with trailing '\n' and expects this preserved.
7136    */
7137   if (decc_posix_compliant_pathnames) {
7138     if (strncmp(spec, "\"^UP^", 5) == 0) {
7139       char * uspec;
7140       char *tunix;
7141       int tunix_len;
7142       int nl_flag;
7143 
7144       tunix = PerlMem_malloc(VMS_MAXRSS);
7145       if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7146       strcpy(tunix, spec);
7147       tunix_len = strlen(tunix);
7148       nl_flag = 0;
7149       if (tunix[tunix_len - 1] == '\n') {
7150 	tunix[tunix_len - 1] = '\"';
7151 	tunix[tunix_len] = '\0';
7152 	tunix_len--;
7153 	nl_flag = 1;
7154       }
7155       uspec = decc$translate_vms(tunix);
7156       PerlMem_free(tunix);
7157       if ((int)uspec > 0) {
7158 	strcpy(rslt,uspec);
7159 	if (nl_flag) {
7160 	  strcat(rslt,"\n");
7161 	}
7162 	else {
7163 	  /* If we can not translate it, makemaker wants as-is */
7164 	  strcpy(rslt, spec);
7165 	}
7166 	return rslt;
7167       }
7168     }
7169   }
7170 
7171   cmp_rslt = 0; /* Presume VMS */
7172   cp1 = strchr(spec, '/');
7173   if (cp1 == NULL)
7174     cmp_rslt = 0;
7175 
7176     /* Look for EFS ^/ */
7177     if (decc_efs_charset) {
7178       while (cp1 != NULL) {
7179 	cp2 = cp1 - 1;
7180 	if (*cp2 != '^') {
7181 	  /* Found illegal VMS, assume UNIX */
7182 	  cmp_rslt = 1;
7183 	  break;
7184 	}
7185       cp1++;
7186       cp1 = strchr(cp1, '/');
7187     }
7188   }
7189 
7190   /* Look for "." and ".." */
7191   if (decc_filename_unix_report) {
7192     if (spec[0] == '.') {
7193       if ((spec[1] == '\0') || (spec[1] == '\n')) {
7194 	cmp_rslt = 1;
7195       }
7196       else {
7197 	if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7198 	  cmp_rslt = 1;
7199 	}
7200       }
7201     }
7202   }
7203   /* This is already UNIX or at least nothing VMS understands */
7204   if (cmp_rslt) {
7205     strcpy(rslt,spec);
7206     if (vms_debug_fileify) {
7207         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7208     }
7209     return rslt;
7210   }
7211 
7212   cp1 = rslt;
7213   cp2 = spec;
7214   dirend = strrchr(spec,']');
7215   if (dirend == NULL) dirend = strrchr(spec,'>');
7216   if (dirend == NULL) dirend = strchr(spec,':');
7217   if (dirend == NULL) {
7218     strcpy(rslt,spec);
7219     if (vms_debug_fileify) {
7220         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7221     }
7222     return rslt;
7223   }
7224 
7225   /* Special case 1 - sys$posix_root = / */
7226 #if __CRTL_VER >= 70000000
7227   if (!decc_disable_posix_root) {
7228     if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7229       *cp1 = '/';
7230       cp1++;
7231       cp2 = cp2 + 15;
7232       }
7233   }
7234 #endif
7235 
7236   /* Special case 2 - Convert NLA0: to /dev/null */
7237 #if __CRTL_VER < 70000000
7238   cmp_rslt = strncmp(spec,"NLA0:", 5);
7239   if (cmp_rslt != 0)
7240      cmp_rslt = strncmp(spec,"nla0:", 5);
7241 #else
7242   cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7243 #endif
7244   if (cmp_rslt == 0) {
7245     strcpy(rslt, "/dev/null");
7246     cp1 = cp1 + 9;
7247     cp2 = cp2 + 5;
7248     if (spec[6] != '\0') {
7249       cp1[9] == '/';
7250       cp1++;
7251       cp2++;
7252     }
7253   }
7254 
7255    /* Also handle special case "SYS$SCRATCH:" */
7256 #if __CRTL_VER < 70000000
7257   cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7258   if (cmp_rslt != 0)
7259      cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7260 #else
7261   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7262 #endif
7263   tmp = PerlMem_malloc(VMS_MAXRSS);
7264   if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7265   if (cmp_rslt == 0) {
7266   int islnm;
7267 
7268     islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7269     if (!islnm) {
7270       strcpy(rslt, "/tmp");
7271       cp1 = cp1 + 4;
7272       cp2 = cp2 + 12;
7273       if (spec[12] != '\0') {
7274 	cp1[4] == '/';
7275 	cp1++;
7276 	cp2++;
7277       }
7278     }
7279   }
7280 
7281   if (*cp2 != '[' && *cp2 != '<') {
7282     *(cp1++) = '/';
7283   }
7284   else {  /* the VMS spec begins with directories */
7285     cp2++;
7286     if (*cp2 == ']' || *cp2 == '>') {
7287       *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7288       PerlMem_free(tmp);
7289       return rslt;
7290     }
7291     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7292       if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7293 	PerlMem_free(tmp);
7294         if (vms_debug_fileify) {
7295             fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7296         }
7297         return NULL;
7298       }
7299       trnlnm_iter_count = 0;
7300       do {
7301         cp3 = tmp;
7302         while (*cp3 != ':' && *cp3) cp3++;
7303         *(cp3++) = '\0';
7304         if (strchr(cp3,']') != NULL) break;
7305         trnlnm_iter_count++;
7306         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7307       } while (vmstrnenv(tmp,tmp,0,fildev,0));
7308       cp1 = rslt;
7309       cp3 = tmp;
7310       *(cp1++) = '/';
7311       while (*cp3) {
7312         *(cp1++) = *(cp3++);
7313         if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7314 	    PerlMem_free(tmp);
7315             set_errno(ENAMETOOLONG);
7316             set_vaxc_errno(SS$_BUFFEROVF);
7317             if (vms_debug_fileify) {
7318                 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7319             }
7320 	    return NULL; /* No room */
7321 	}
7322       }
7323       *(cp1++) = '/';
7324     }
7325     if ((*cp2 == '^')) {
7326 	/* EFS file escape, pass the next character as is */
7327 	/* Fix me: HEX encoding for Unicode not implemented */
7328 	cp2++;
7329     }
7330     else if ( *cp2 == '.') {
7331       if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7332         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7333         cp2 += 3;
7334       }
7335       else cp2++;
7336     }
7337   }
7338   PerlMem_free(tmp);
7339   for (; cp2 <= dirend; cp2++) {
7340     if ((*cp2 == '^')) {
7341 	/* EFS file escape, pass the next character as is */
7342 	/* Fix me: HEX encoding for Unicode not implemented */
7343 	*(cp1++) = *(++cp2);
7344         /* An escaped dot stays as is -- don't convert to slash */
7345         if (*cp2 == '.') cp2++;
7346     }
7347     if (*cp2 == ':') {
7348       *(cp1++) = '/';
7349       if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7350     }
7351     else if (*cp2 == ']' || *cp2 == '>') {
7352       if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7353     }
7354     else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7355       *(cp1++) = '/';
7356       if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7357         while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7358                *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7359         if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7360             *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7361       }
7362       else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7363         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7364         cp2 += 2;
7365       }
7366     }
7367     else if (*cp2 == '-') {
7368       if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7369         while (*cp2 == '-') {
7370           cp2++;
7371           *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7372         }
7373         if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7374                                                          /* filespecs like */
7375           set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);   /* [fred.--foo.bar] */
7376           if (vms_debug_fileify) {
7377               fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7378           }
7379           return NULL;
7380         }
7381       }
7382       else *(cp1++) = *cp2;
7383     }
7384     else *(cp1++) = *cp2;
7385   }
7386   /* Translate the rest of the filename. */
7387   while (*cp2) {
7388       int dot_seen;
7389       dot_seen = 0;
7390       switch(*cp2) {
7391       /* Fixme - for compatibility with the CRTL we should be removing */
7392       /* spaces from the file specifications, but this may show that */
7393       /* some tests that were appearing to pass are not really passing */
7394       case '%':
7395           cp2++;
7396           *(cp1++) = '?';
7397           break;
7398       case '^':
7399           /* Fix me hex expansions not implemented */
7400           cp2++;  /* '^.' --> '.' and other. */
7401           if (*cp2) {
7402               if (*cp2 == '_') {
7403                   cp2++;
7404                   *(cp1++) = ' ';
7405               } else {
7406                   *(cp1++) = *(cp2++);
7407               }
7408           }
7409           break;
7410       case ';':
7411           if (decc_filename_unix_no_version) {
7412               /* Easy, drop the version */
7413               while (*cp2)
7414                   cp2++;
7415               break;
7416           } else {
7417               /* Punt - passing the version as a dot will probably */
7418               /* break perl in weird ways, but so did passing */
7419               /* through the ; as a version.  Follow the CRTL and */
7420               /* hope for the best. */
7421               cp2++;
7422               *(cp1++) = '.';
7423           }
7424           break;
7425       case '.':
7426           if (dot_seen) {
7427               /* We will need to fix this properly later */
7428               /* As Perl may be installed on an ODS-5 volume, but not */
7429               /* have the EFS_CHARSET enabled, it still may encounter */
7430               /* filenames with extra dots in them, and a precedent got */
7431               /* set which allowed them to work, that we will uphold here */
7432               /* If extra dots are present in a name and no ^ is on them */
7433               /* VMS assumes that the first one is the extension delimiter */
7434               /* the rest have an implied ^. */
7435 
7436               /* this is also a conflict as the . is also a version */
7437               /* delimiter in VMS, */
7438 
7439               *(cp1++) = *(cp2++);
7440               break;
7441           }
7442           dot_seen = 1;
7443           /* This is an extension */
7444           if (decc_readdir_dropdotnotype) {
7445               cp2++;
7446               if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7447                   /* Drop the dot for the extension */
7448                   break;
7449               } else {
7450                   *(cp1++) = '.';
7451               }
7452               break;
7453           }
7454       default:
7455           *(cp1++) = *(cp2++);
7456       }
7457   }
7458   *cp1 = '\0';
7459 
7460   /* This still leaves /000000/ when working with a
7461    * VMS device root or concealed root.
7462    */
7463   {
7464   int ulen;
7465   char * zeros;
7466 
7467       ulen = strlen(rslt);
7468 
7469       /* Get rid of "000000/ in rooted filespecs */
7470       if (ulen > 7) {
7471 	zeros = strstr(rslt, "/000000/");
7472 	if (zeros != NULL) {
7473 	  int mlen;
7474 	  mlen = ulen - (zeros - rslt) - 7;
7475 	  memmove(zeros, &zeros[7], mlen);
7476 	  ulen = ulen - 7;
7477 	  rslt[ulen] = '\0';
7478 	}
7479       }
7480   }
7481 
7482   if (vms_debug_fileify) {
7483       fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7484   }
7485   return rslt;
7486 
7487 }  /* end of int_tounixspec() */
7488 
7489 
7490 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7491 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7492 {
7493     static char __tounixspec_retbuf[VMS_MAXRSS];
7494     char * unixspec, *ret_spec, *ret_buf;
7495 
7496     unixspec = NULL;
7497     ret_buf = buf;
7498     if (ret_buf == NULL) {
7499         if (ts) {
7500             Newx(unixspec, VMS_MAXRSS, char);
7501             if (unixspec == NULL)
7502                 _ckvmssts(SS$_INSFMEM);
7503             ret_buf = unixspec;
7504         } else {
7505             ret_buf = __tounixspec_retbuf;
7506         }
7507     }
7508 
7509     ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7510 
7511     if (ret_spec == NULL) {
7512        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7513        if (unixspec)
7514            Safefree(unixspec);
7515     }
7516 
7517     return ret_spec;
7518 
7519 }  /* end of do_tounixspec() */
7520 /*}}}*/
7521 /* External entry points */
7522 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7523   { return do_tounixspec(spec,buf,0, NULL); }
7524 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7525   { return do_tounixspec(spec,buf,1, NULL); }
7526 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7527   { return do_tounixspec(spec,buf,0, utf8_fl); }
7528 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7529   { return do_tounixspec(spec,buf,1, utf8_fl); }
7530 
7531 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7532 
7533 /*
7534  This procedure is used to identify if a path is based in either
7535  the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7536  it returns the OpenVMS format directory for it.
7537 
7538  It is expecting specifications of only '/' or '/xxxx/'
7539 
7540  If a posix root does not exist, or 'xxxx' is not a directory
7541  in the posix root, it returns a failure.
7542 
7543  FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7544 
7545  It is used only internally by posix_to_vmsspec_hardway().
7546  */
7547 
7548 static int posix_root_to_vms
7549   (char *vmspath, int vmspath_len,
7550    const char *unixpath,
7551    const int * utf8_fl)
7552 {
7553 int sts;
7554 struct FAB myfab = cc$rms_fab;
7555 rms_setup_nam(mynam);
7556 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7557 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7558 char * esa, * esal, * rsa, * rsal;
7559 char *vms_delim;
7560 int dir_flag;
7561 int unixlen;
7562 
7563     dir_flag = 0;
7564     vmspath[0] = '\0';
7565     unixlen = strlen(unixpath);
7566     if (unixlen == 0) {
7567       return RMS$_FNF;
7568     }
7569 
7570 #if __CRTL_VER >= 80200000
7571   /* If not a posix spec already, convert it */
7572   if (decc_posix_compliant_pathnames) {
7573     if (strncmp(unixpath,"\"^UP^",5) != 0) {
7574       sprintf(vmspath,"\"^UP^%s\"",unixpath);
7575     }
7576     else {
7577       /* This is already a VMS specification, no conversion */
7578       unixlen--;
7579       strncpy(vmspath,unixpath, vmspath_len);
7580     }
7581   }
7582   else
7583 #endif
7584   {
7585   int path_len;
7586   int i,j;
7587 
7588      /* Check to see if this is under the POSIX root */
7589      if (decc_disable_posix_root) {
7590 	return RMS$_FNF;
7591      }
7592 
7593      /* Skip leading / */
7594      if (unixpath[0] == '/') {
7595 	unixpath++;
7596 	unixlen--;
7597      }
7598 
7599 
7600      strcpy(vmspath,"SYS$POSIX_ROOT:");
7601 
7602      /* If this is only the / , or blank, then... */
7603      if (unixpath[0] == '\0') {
7604 	/* by definition, this is the answer */
7605 	return SS$_NORMAL;
7606      }
7607 
7608      /* Need to look up a directory */
7609      vmspath[15] = '[';
7610      vmspath[16] = '\0';
7611 
7612      /* Copy and add '^' escape characters as needed */
7613      j = 16;
7614      i = 0;
7615      while (unixpath[i] != 0) {
7616      int k;
7617 
7618 	j += copy_expand_unix_filename_escape
7619 	    (&vmspath[j], &unixpath[i], &k, utf8_fl);
7620 	i += k;
7621      }
7622 
7623      path_len = strlen(vmspath);
7624      if (vmspath[path_len - 1] == '/')
7625 	path_len--;
7626      vmspath[path_len] = ']';
7627      path_len++;
7628      vmspath[path_len] = '\0';
7629 
7630   }
7631   vmspath[vmspath_len] = 0;
7632   if (unixpath[unixlen - 1] == '/')
7633   dir_flag = 1;
7634   esal = PerlMem_malloc(VMS_MAXRSS);
7635   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7636   esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7637   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7638   rsal = PerlMem_malloc(VMS_MAXRSS);
7639   if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7640   rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7641   if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7642   rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7643   rms_bind_fab_nam(myfab, mynam);
7644   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7645   rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7646   if (decc_efs_case_preserve)
7647     mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7648 #ifdef NAML$M_OPEN_SPECIAL
7649   mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7650 #endif
7651 
7652   /* Set up the remaining naml fields */
7653   sts = sys$parse(&myfab);
7654 
7655   /* It failed! Try again as a UNIX filespec */
7656   if (!(sts & 1)) {
7657     PerlMem_free(esal);
7658     PerlMem_free(esa);
7659     PerlMem_free(rsal);
7660     PerlMem_free(rsa);
7661     return sts;
7662   }
7663 
7664    /* get the Device ID and the FID */
7665    sts = sys$search(&myfab);
7666 
7667    /* These are no longer needed */
7668    PerlMem_free(esa);
7669    PerlMem_free(rsal);
7670    PerlMem_free(rsa);
7671 
7672    /* on any failure, returned the POSIX ^UP^ filespec */
7673    if (!(sts & 1)) {
7674       PerlMem_free(esal);
7675       return sts;
7676    }
7677    specdsc.dsc$a_pointer = vmspath;
7678    specdsc.dsc$w_length = vmspath_len;
7679 
7680    dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7681    dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7682    sts = lib$fid_to_name
7683       (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7684 
7685   /* on any failure, returned the POSIX ^UP^ filespec */
7686   if (!(sts & 1)) {
7687      /* This can happen if user does not have permission to read directories */
7688      if (strncmp(unixpath,"\"^UP^",5) != 0)
7689        sprintf(vmspath,"\"^UP^%s\"",unixpath);
7690      else
7691        strcpy(vmspath, unixpath);
7692   }
7693   else {
7694     vmspath[specdsc.dsc$w_length] = 0;
7695 
7696     /* Are we expecting a directory? */
7697     if (dir_flag != 0) {
7698     int i;
7699     char *eptr;
7700 
7701       eptr = NULL;
7702 
7703       i = specdsc.dsc$w_length - 1;
7704       while (i > 0) {
7705       int zercnt;
7706 	zercnt = 0;
7707 	/* Version must be '1' */
7708 	if (vmspath[i--] != '1')
7709 	  break;
7710 	/* Version delimiter is one of ".;" */
7711 	if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7712 	  break;
7713 	i--;
7714 	if (vmspath[i--] != 'R')
7715 	  break;
7716 	if (vmspath[i--] != 'I')
7717 	  break;
7718 	if (vmspath[i--] != 'D')
7719 	  break;
7720 	if (vmspath[i--] != '.')
7721 	  break;
7722 	eptr = &vmspath[i+1];
7723  	while (i > 0) {
7724 	  if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7725 	    if (vmspath[i-1] != '^') {
7726 	      if (zercnt != 6) {
7727   		*eptr = vmspath[i];
7728 		eptr[1] = '\0';
7729 		vmspath[i] = '.';
7730   		break;
7731 	      }
7732 	      else {
7733  		/* Get rid of 6 imaginary zero directory filename */
7734   		vmspath[i+1] = '\0';
7735  	      }
7736 	    }
7737 	  }
7738 	  if (vmspath[i] == '0')
7739 	    zercnt++;
7740 	  else
7741 	    zercnt = 10;
7742 	  i--;
7743 	}
7744 	break;
7745       }
7746     }
7747   }
7748   PerlMem_free(esal);
7749   return sts;
7750 }
7751 
7752 /* /dev/mumble needs to be handled special.
7753    /dev/null becomes NLA0:, And there is the potential for other stuff
7754    like /dev/tty which may need to be mapped to something.
7755 */
7756 
7757 static int
7758 slash_dev_special_to_vms
7759    (const char * unixptr,
7760     char * vmspath,
7761     int vmspath_len)
7762 {
7763 char * nextslash;
7764 int len;
7765 int cmp;
7766 int islnm;
7767 
7768     unixptr += 4;
7769     nextslash = strchr(unixptr, '/');
7770     len = strlen(unixptr);
7771     if (nextslash != NULL)
7772 	len = nextslash - unixptr;
7773     cmp = strncmp("null", unixptr, 5);
7774     if (cmp == 0) {
7775 	if (vmspath_len >= 6) {
7776 	    strcpy(vmspath, "_NLA0:");
7777 	    return SS$_NORMAL;
7778 	}
7779     }
7780 }
7781 
7782 
7783 /* The built in routines do not understand perl's special needs, so
7784     doing a manual conversion from UNIX to VMS
7785 
7786     If the utf8_fl is not null and points to a non-zero value, then
7787     treat 8 bit characters as UTF-8.
7788 
7789     The sequence starting with '$(' and ending with ')' will be passed
7790     through with out interpretation instead of being escaped.
7791 
7792   */
7793 static int posix_to_vmsspec_hardway
7794   (char *vmspath, int vmspath_len,
7795    const char *unixpath,
7796    int dir_flag,
7797    int * utf8_fl) {
7798 
7799 char *esa;
7800 const char *unixptr;
7801 const char *unixend;
7802 char *vmsptr;
7803 const char *lastslash;
7804 const char *lastdot;
7805 int unixlen;
7806 int vmslen;
7807 int dir_start;
7808 int dir_dot;
7809 int quoted;
7810 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7811 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7812 
7813   if (utf8_fl != NULL)
7814     *utf8_fl = 0;
7815 
7816   unixptr = unixpath;
7817   dir_dot = 0;
7818 
7819   /* Ignore leading "/" characters */
7820   while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7821     unixptr++;
7822   }
7823   unixlen = strlen(unixptr);
7824 
7825   /* Do nothing with blank paths */
7826   if (unixlen == 0) {
7827     vmspath[0] = '\0';
7828     return SS$_NORMAL;
7829   }
7830 
7831   quoted = 0;
7832   /* This could have a "^UP^ on the front */
7833   if (strncmp(unixptr,"\"^UP^",5) == 0) {
7834     quoted = 1;
7835     unixptr+= 5;
7836     unixlen-= 5;
7837   }
7838 
7839   lastslash = strrchr(unixptr,'/');
7840   lastdot = strrchr(unixptr,'.');
7841   unixend = strrchr(unixptr,'\"');
7842   if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7843     unixend = unixptr + unixlen;
7844   }
7845 
7846   /* last dot is last dot or past end of string */
7847   if (lastdot == NULL)
7848     lastdot = unixptr + unixlen;
7849 
7850   /* if no directories, set last slash to beginning of string */
7851   if (lastslash == NULL) {
7852     lastslash = unixptr;
7853   }
7854   else {
7855     /* Watch out for trailing "." after last slash, still a directory */
7856     if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7857       lastslash = unixptr + unixlen;
7858     }
7859 
7860     /* Watch out for traiing ".." after last slash, still a directory */
7861     if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7862       lastslash = unixptr + unixlen;
7863     }
7864 
7865     /* dots in directories are aways escaped */
7866     if (lastdot < lastslash)
7867       lastdot = unixptr + unixlen;
7868   }
7869 
7870   /* if (unixptr < lastslash) then we are in a directory */
7871 
7872   dir_start = 0;
7873 
7874   vmsptr = vmspath;
7875   vmslen = 0;
7876 
7877   /* Start with the UNIX path */
7878   if (*unixptr != '/') {
7879     /* relative paths */
7880 
7881     /* If allowing logical names on relative pathnames, then handle here */
7882     if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7883 	!decc_posix_compliant_pathnames) {
7884     char * nextslash;
7885     int seg_len;
7886     char * trn;
7887     int islnm;
7888 
7889 	/* Find the next slash */
7890 	nextslash = strchr(unixptr,'/');
7891 
7892 	esa = PerlMem_malloc(vmspath_len);
7893 	if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7894 
7895 	trn = PerlMem_malloc(VMS_MAXRSS);
7896 	if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7897 
7898 	if (nextslash != NULL) {
7899 
7900 	    seg_len = nextslash - unixptr;
7901 	    strncpy(esa, unixptr, seg_len);
7902 	    esa[seg_len] = 0;
7903 	}
7904 	else {
7905 	    strcpy(esa, unixptr);
7906 	    seg_len = strlen(unixptr);
7907 	}
7908 	/* trnlnm(section) */
7909 	islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7910 
7911 	if (islnm) {
7912 	    /* Now fix up the directory */
7913 
7914 	    /* Split up the path to find the components */
7915 	    sts = vms_split_path
7916 		  (trn,
7917 		   &v_spec,
7918 		   &v_len,
7919 		   &r_spec,
7920 		   &r_len,
7921 		   &d_spec,
7922 		   &d_len,
7923 		   &n_spec,
7924 		   &n_len,
7925 		   &e_spec,
7926 		   &e_len,
7927 		   &vs_spec,
7928 		   &vs_len);
7929 
7930 	    while (sts == 0) {
7931 	    char * strt;
7932 	    int cmp;
7933 
7934 		/* A logical name must be a directory  or the full
7935 		   specification.  It is only a full specification if
7936 		   it is the only component */
7937 		if ((unixptr[seg_len] == '\0') ||
7938 		    (unixptr[seg_len+1] == '\0')) {
7939 
7940 		    /* Is a directory being required? */
7941 		    if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7942 			/* Not a logical name */
7943 			break;
7944 		    }
7945 
7946 
7947 		    if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7948 			/* This must be a directory */
7949 			if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7950 			    strcpy(vmsptr, esa);
7951 			    vmslen=strlen(vmsptr);
7952 			    vmsptr[vmslen] = ':';
7953 			    vmslen++;
7954 			    vmsptr[vmslen] = '\0';
7955 			    return SS$_NORMAL;
7956 			}
7957 		    }
7958 
7959 		}
7960 
7961 
7962 		/* must be dev/directory - ignore version */
7963 		if ((n_len + e_len) != 0)
7964 		    break;
7965 
7966 		/* transfer the volume */
7967 		if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7968 		    strncpy(vmsptr, v_spec, v_len);
7969 		    vmsptr += v_len;
7970 		    vmsptr[0] = '\0';
7971 		    vmslen += v_len;
7972 		}
7973 
7974 		/* unroot the rooted directory */
7975 		if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7976 		    r_spec[0] = '[';
7977 		    r_spec[r_len - 1] = ']';
7978 
7979 		    /* This should not be there, but nothing is perfect */
7980 		    if (r_len > 9) {
7981 			cmp = strcmp(&r_spec[1], "000000.");
7982 			if (cmp == 0) {
7983 			    r_spec += 7;
7984 			    r_spec[7] = '[';
7985 			    r_len -= 7;
7986 			    if (r_len == 2)
7987 				r_len = 0;
7988 			}
7989 		    }
7990 		    if (r_len > 0) {
7991 			strncpy(vmsptr, r_spec, r_len);
7992 			vmsptr += r_len;
7993 			vmslen += r_len;
7994 			vmsptr[0] = '\0';
7995 		    }
7996 		}
7997 		/* Bring over the directory. */
7998 		if ((d_len > 0) &&
7999 		    ((d_len + vmslen) < vmspath_len)) {
8000 		    d_spec[0] = '[';
8001 		    d_spec[d_len - 1] = ']';
8002 		    if (d_len > 9) {
8003 			cmp = strcmp(&d_spec[1], "000000.");
8004 			if (cmp == 0) {
8005 			    d_spec += 7;
8006 			    d_spec[7] = '[';
8007 			    d_len -= 7;
8008 			    if (d_len == 2)
8009 				d_len = 0;
8010 			}
8011 		    }
8012 
8013 		    if (r_len > 0) {
8014 			/* Remove the redundant root */
8015 			if (r_len > 0) {
8016 			    /* remove the ][ */
8017 			    vmsptr--;
8018 			    vmslen--;
8019 			    d_spec++;
8020 			    d_len--;
8021 			}
8022 			strncpy(vmsptr, d_spec, d_len);
8023 			    vmsptr += d_len;
8024 			    vmslen += d_len;
8025 			    vmsptr[0] = '\0';
8026 		    }
8027 		}
8028 		break;
8029 	    }
8030 	}
8031 
8032 	PerlMem_free(esa);
8033 	PerlMem_free(trn);
8034     }
8035 
8036     if (lastslash > unixptr) {
8037     int dotdir_seen;
8038 
8039       /* skip leading ./ */
8040       dotdir_seen = 0;
8041       while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
8042 	dotdir_seen = 1;
8043 	unixptr++;
8044 	unixptr++;
8045       }
8046 
8047       /* Are we still in a directory? */
8048       if (unixptr <= lastslash) {
8049  	*vmsptr++ = '[';
8050  	vmslen = 1;
8051  	dir_start = 1;
8052 
8053  	/* if not backing up, then it is relative forward. */
8054  	if (!((*unixptr == '.') && (unixptr[1] == '.') &&
8055  	      ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
8056  	  *vmsptr++ = '.';
8057  	  vmslen++;
8058  	  dir_dot = 1;
8059  	  }
8060        }
8061        else {
8062 	 if (dotdir_seen) {
8063 	   /* Perl wants an empty directory here to tell the difference
8064 	    * between a DCL commmand and a filename
8065 	    */
8066 	  *vmsptr++ = '[';
8067 	  *vmsptr++ = ']';
8068 	  vmslen = 2;
8069  	}
8070       }
8071     }
8072     else {
8073       /* Handle two special files . and .. */
8074       if (unixptr[0] == '.') {
8075         if (&unixptr[1] == unixend) {
8076 	  *vmsptr++ = '[';
8077 	  *vmsptr++ = ']';
8078 	  vmslen += 2;
8079 	  *vmsptr++ = '\0';
8080 	  return SS$_NORMAL;
8081 	}
8082         if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
8083 	  *vmsptr++ = '[';
8084 	  *vmsptr++ = '-';
8085 	  *vmsptr++ = ']';
8086 	  vmslen += 3;
8087 	  *vmsptr++ = '\0';
8088 	  return SS$_NORMAL;
8089 	}
8090       }
8091     }
8092   }
8093   else {	/* Absolute PATH handling */
8094   int sts;
8095   char * nextslash;
8096   int seg_len;
8097     /* Need to find out where root is */
8098 
8099     /* In theory, this procedure should never get an absolute POSIX pathname
8100      * that can not be found on the POSIX root.
8101      * In practice, that can not be relied on, and things will show up
8102      * here that are a VMS device name or concealed logical name instead.
8103      * So to make things work, this procedure must be tolerant.
8104      */
8105     esa = PerlMem_malloc(vmspath_len);
8106     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8107 
8108     sts = SS$_NORMAL;
8109     nextslash = strchr(&unixptr[1],'/');
8110     seg_len = 0;
8111     if (nextslash != NULL) {
8112     int cmp;
8113       seg_len = nextslash - &unixptr[1];
8114       strncpy(vmspath, unixptr, seg_len + 1);
8115       vmspath[seg_len+1] = 0;
8116       cmp = 1;
8117       if (seg_len == 3) {
8118 	cmp = strncmp(vmspath, "dev", 4);
8119 	if (cmp == 0) {
8120 	    sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8121 	    if (sts = SS$_NORMAL)
8122 		return SS$_NORMAL;
8123 	}
8124       }
8125       sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8126     }
8127 
8128     if ($VMS_STATUS_SUCCESS(sts)) {
8129       /* This is verified to be a real path */
8130 
8131       sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8132       if ($VMS_STATUS_SUCCESS(sts)) {
8133 	strcpy(vmspath, esa);
8134 	vmslen = strlen(vmspath);
8135 	vmsptr = vmspath + vmslen;
8136 	unixptr++;
8137 	if (unixptr < lastslash) {
8138 	char * rptr;
8139 	  vmsptr--;
8140 	  *vmsptr++ = '.';
8141 	  dir_start = 1;
8142 	  dir_dot = 1;
8143 	  if (vmslen > 7) {
8144 	  int cmp;
8145 	    rptr = vmsptr - 7;
8146 	    cmp = strcmp(rptr,"000000.");
8147 	    if (cmp == 0) {
8148 	      vmslen -= 7;
8149 	      vmsptr -= 7;
8150 	      vmsptr[1] = '\0';
8151 	    } /* removing 6 zeros */
8152 	  } /* vmslen < 7, no 6 zeros possible */
8153 	} /* Not in a directory */
8154       } /* Posix root found */
8155       else {
8156 	/* No posix root, fall back to default directory */
8157 	strcpy(vmspath, "SYS$DISK:[");
8158 	vmsptr = &vmspath[10];
8159 	vmslen = 10;
8160 	if (unixptr > lastslash) {
8161 	   *vmsptr = ']';
8162 	   vmsptr++;
8163 	   vmslen++;
8164 	}
8165 	else {
8166 	   dir_start = 1;
8167 	}
8168       }
8169     } /* end of verified real path handling */
8170     else {
8171     int add_6zero;
8172     int islnm;
8173 
8174       /* Ok, we have a device or a concealed root that is not in POSIX
8175        * or we have garbage.  Make the best of it.
8176        */
8177 
8178       /* Posix to VMS destroyed this, so copy it again */
8179       strncpy(vmspath, &unixptr[1], seg_len);
8180       vmspath[seg_len] = 0;
8181       vmslen = seg_len;
8182       vmsptr = &vmsptr[vmslen];
8183       islnm = 0;
8184 
8185       /* Now do we need to add the fake 6 zero directory to it? */
8186       add_6zero = 1;
8187       if ((*lastslash == '/') && (nextslash < lastslash)) {
8188 	/* No there is another directory */
8189 	add_6zero = 0;
8190       }
8191       else {
8192       int trnend;
8193       int cmp;
8194 
8195 	/* now we have foo:bar or foo:[000000]bar to decide from */
8196 	islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8197 
8198         if (!islnm && !decc_posix_compliant_pathnames) {
8199 
8200 	    cmp = strncmp("bin", vmspath, 4);
8201 	    if (cmp == 0) {
8202 	        /* bin => SYS$SYSTEM: */
8203 		islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8204 	    }
8205 	    else {
8206 	        /* tmp => SYS$SCRATCH: */
8207 	        cmp = strncmp("tmp", vmspath, 4);
8208 		if (cmp == 0) {
8209 		    islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8210 		}
8211 	    }
8212 	}
8213 
8214         trnend = islnm ? islnm - 1 : 0;
8215 
8216 	/* if this was a logical name, ']' or '>' must be present */
8217 	/* if not a logical name, then assume a device and hope. */
8218 	islnm =  trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8219 
8220 	/* if log name and trailing '.' then rooted - treat as device */
8221 	add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8222 
8223 	/* Fix me, if not a logical name, a device lookup should be
8224          * done to see if the device is file structured.  If the device
8225          * is not file structured, the 6 zeros should not be put on.
8226          *
8227          * As it is, perl is occasionally looking for dev:[000000]tty.
8228 	 * which looks a little strange.
8229 	 *
8230 	 * Not that easy to detect as "/dev" may be file structured with
8231 	 * special device files.
8232          */
8233 
8234 	if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8235 	    (&nextslash[1] == unixend)) {
8236 	  /* No real directory present */
8237 	  add_6zero = 1;
8238 	}
8239       }
8240 
8241       /* Put the device delimiter on */
8242       *vmsptr++ = ':';
8243       vmslen++;
8244       unixptr = nextslash;
8245       unixptr++;
8246 
8247       /* Start directory if needed */
8248       if (!islnm || add_6zero) {
8249 	*vmsptr++ = '[';
8250 	vmslen++;
8251 	dir_start = 1;
8252       }
8253 
8254       /* add fake 000000] if needed */
8255       if (add_6zero) {
8256 	*vmsptr++ = '0';
8257 	*vmsptr++ = '0';
8258 	*vmsptr++ = '0';
8259 	*vmsptr++ = '0';
8260 	*vmsptr++ = '0';
8261 	*vmsptr++ = '0';
8262 	*vmsptr++ = ']';
8263 	vmslen += 7;
8264 	dir_start = 0;
8265       }
8266 
8267     } /* non-POSIX translation */
8268     PerlMem_free(esa);
8269   } /* End of relative/absolute path handling */
8270 
8271   while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8272   int dash_flag;
8273   int in_cnt;
8274   int out_cnt;
8275 
8276     dash_flag = 0;
8277 
8278     if (dir_start != 0) {
8279 
8280       /* First characters in a directory are handled special */
8281       while ((*unixptr == '/') ||
8282 	     ((*unixptr == '.') &&
8283 	      ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8284 		(&unixptr[1]==unixend)))) {
8285       int loop_flag;
8286 
8287 	loop_flag = 0;
8288 
8289         /* Skip redundant / in specification */
8290         while ((*unixptr == '/') && (dir_start != 0)) {
8291 	  loop_flag = 1;
8292 	  unixptr++;
8293 	  if (unixptr == lastslash)
8294 	    break;
8295 	}
8296 	if (unixptr == lastslash)
8297 	  break;
8298 
8299         /* Skip redundant ./ characters */
8300 	while ((*unixptr == '.') &&
8301 	       ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8302 	  loop_flag = 1;
8303 	  unixptr++;
8304 	  if (unixptr == lastslash)
8305 	    break;
8306 	  if (*unixptr == '/')
8307 	    unixptr++;
8308 	}
8309 	if (unixptr == lastslash)
8310 	  break;
8311 
8312 	/* Skip redundant ../ characters */
8313 	while ((*unixptr == '.') && (unixptr[1] == '.') &&
8314 	     ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8315 	  /* Set the backing up flag */
8316 	  loop_flag = 1;
8317 	  dir_dot = 0;
8318 	  dash_flag = 1;
8319 	  *vmsptr++ = '-';
8320 	  vmslen++;
8321 	  unixptr++; /* first . */
8322 	  unixptr++; /* second . */
8323 	  if (unixptr == lastslash)
8324 	    break;
8325 	  if (*unixptr == '/') /* The slash */
8326 	    unixptr++;
8327 	}
8328 	if (unixptr == lastslash)
8329 	  break;
8330 
8331 	/* To do: Perl expects /.../ to be translated to [...] on VMS */
8332   	/* Not needed when VMS is pretending to be UNIX. */
8333 
8334 	/* Is this loop stuck because of too many dots? */
8335 	if (loop_flag == 0) {
8336 	  /* Exit the loop and pass the rest through */
8337 	  break;
8338 	}
8339       }
8340 
8341       /* Are we done with directories yet? */
8342       if (unixptr >= lastslash) {
8343 
8344 	/* Watch out for trailing dots */
8345 	if (dir_dot != 0) {
8346 	    vmslen --;
8347 	    vmsptr--;
8348 	}
8349 	*vmsptr++ = ']';
8350 	vmslen++;
8351 	dash_flag = 0;
8352 	dir_start = 0;
8353 	if (*unixptr == '/')
8354 	  unixptr++;
8355       }
8356       else {
8357 	/* Have we stopped backing up? */
8358 	if (dash_flag) {
8359 	  *vmsptr++ = '.';
8360 	  vmslen++;
8361 	  dash_flag = 0;
8362 	  /* dir_start continues to be = 1 */
8363 	}
8364 	if (*unixptr == '-') {
8365 	  *vmsptr++ = '^';
8366 	  *vmsptr++ = *unixptr++;
8367 	  vmslen += 2;
8368 	  dir_start = 0;
8369 
8370 	  /* Now are we done with directories yet? */
8371 	  if (unixptr >= lastslash) {
8372 
8373 	    /* Watch out for trailing dots */
8374 	    if (dir_dot != 0) {
8375 	      vmslen --;
8376 	      vmsptr--;
8377 	    }
8378 
8379 	    *vmsptr++ = ']';
8380 	    vmslen++;
8381 	    dash_flag = 0;
8382 	    dir_start = 0;
8383 	  }
8384 	}
8385       }
8386     }
8387 
8388     /* All done? */
8389     if (unixptr >= unixend)
8390       break;
8391 
8392     /* Normal characters - More EFS work probably needed */
8393     dir_start = 0;
8394     dir_dot = 0;
8395 
8396     switch(*unixptr) {
8397     case '/':
8398 	/* remove multiple / */
8399 	while (unixptr[1] == '/') {
8400 	   unixptr++;
8401 	}
8402 	if (unixptr == lastslash) {
8403 	  /* Watch out for trailing dots */
8404 	  if (dir_dot != 0) {
8405 	    vmslen --;
8406 	    vmsptr--;
8407 	  }
8408 	  *vmsptr++ = ']';
8409 	}
8410 	else {
8411 	  dir_start = 1;
8412 	  *vmsptr++ = '.';
8413 	  dir_dot = 1;
8414 
8415 	  /* To do: Perl expects /.../ to be translated to [...] on VMS */
8416  	  /* Not needed when VMS is pretending to be UNIX. */
8417 
8418 	}
8419 	dash_flag = 0;
8420 	if (unixptr != unixend)
8421 	  unixptr++;
8422 	vmslen++;
8423 	break;
8424     case '.':
8425 	if ((unixptr < lastdot) || (unixptr < lastslash) ||
8426 	    (&unixptr[1] == unixend)) {
8427 	  *vmsptr++ = '^';
8428 	  *vmsptr++ = '.';
8429 	  vmslen += 2;
8430 	  unixptr++;
8431 
8432 	  /* trailing dot ==> '^..' on VMS */
8433 	  if (unixptr == unixend) {
8434 	    *vmsptr++ = '.';
8435 	    vmslen++;
8436 	    unixptr++;
8437 	  }
8438 	  break;
8439 	}
8440 
8441 	*vmsptr++ = *unixptr++;
8442 	vmslen ++;
8443 	break;
8444     case '"':
8445 	if (quoted && (&unixptr[1] == unixend)) {
8446 	    unixptr++;
8447 	    break;
8448 	}
8449 	in_cnt = copy_expand_unix_filename_escape
8450 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8451 	vmsptr += out_cnt;
8452 	unixptr += in_cnt;
8453 	break;
8454     case '~':
8455     case ';':
8456     case '\\':
8457     case '?':
8458     case ' ':
8459     default:
8460 	in_cnt = copy_expand_unix_filename_escape
8461 		(vmsptr, unixptr, &out_cnt, utf8_fl);
8462 	vmsptr += out_cnt;
8463 	unixptr += in_cnt;
8464 	break;
8465     }
8466   }
8467 
8468   /* Make sure directory is closed */
8469   if (unixptr == lastslash) {
8470     char *vmsptr2;
8471     vmsptr2 = vmsptr - 1;
8472 
8473     if (*vmsptr2 != ']') {
8474       *vmsptr2--;
8475 
8476       /* directories do not end in a dot bracket */
8477       if (*vmsptr2 == '.') {
8478 	vmsptr2--;
8479 
8480 	/* ^. is allowed */
8481         if (*vmsptr2 != '^') {
8482 	  vmsptr--; /* back up over the dot */
8483  	}
8484       }
8485       *vmsptr++ = ']';
8486     }
8487   }
8488   else {
8489     char *vmsptr2;
8490     /* Add a trailing dot if a file with no extension */
8491     vmsptr2 = vmsptr - 1;
8492     if ((vmslen > 1) &&
8493 	(*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8494 	(*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8495 	*vmsptr++ = '.';
8496         vmslen++;
8497     }
8498   }
8499 
8500   *vmsptr = '\0';
8501   return SS$_NORMAL;
8502 }
8503 #endif
8504 
8505  /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8506 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8507 {
8508 char * result;
8509 int utf8_flag;
8510 
8511    /* If a UTF8 flag is being passed, honor it */
8512    utf8_flag = 0;
8513    if (utf8_fl != NULL) {
8514      utf8_flag = *utf8_fl;
8515     *utf8_fl = 0;
8516    }
8517 
8518    if (utf8_flag) {
8519      /* If there is a possibility of UTF8, then if any UTF8 characters
8520         are present, then they must be converted to VTF-7
8521       */
8522      result = strcpy(rslt, path); /* FIX-ME */
8523    }
8524    else
8525      result = strcpy(rslt, path);
8526 
8527    return result;
8528 }
8529 
8530 
8531 
8532 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8533 static char *int_tovmsspec
8534    (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8535   char *dirend;
8536   char *lastdot;
8537   char *vms_delim;
8538   register char *cp1;
8539   const char *cp2;
8540   unsigned long int infront = 0, hasdir = 1;
8541   int rslt_len;
8542   int no_type_seen;
8543   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8544   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8545 
8546   if (vms_debug_fileify) {
8547       if (path == NULL)
8548           fprintf(stderr, "int_tovmsspec: path = NULL\n");
8549       else
8550           fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8551   }
8552 
8553   if (path == NULL) {
8554       /* If we fail, we should be setting errno */
8555       set_errno(EINVAL);
8556       set_vaxc_errno(SS$_BADPARAM);
8557       return NULL;
8558   }
8559   rslt_len = VMS_MAXRSS-1;
8560 
8561   /* '.' and '..' are "[]" and "[-]" for a quick check */
8562   if (path[0] == '.') {
8563     if (path[1] == '\0') {
8564       strcpy(rslt,"[]");
8565       if (utf8_flag != NULL)
8566 	*utf8_flag = 0;
8567       return rslt;
8568     }
8569     else {
8570       if (path[1] == '.' && path[2] == '\0') {
8571 	strcpy(rslt,"[-]");
8572 	if (utf8_flag != NULL)
8573 	   *utf8_flag = 0;
8574 	return rslt;
8575       }
8576     }
8577   }
8578 
8579    /* Posix specifications are now a native VMS format */
8580   /*--------------------------------------------------*/
8581 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8582   if (decc_posix_compliant_pathnames) {
8583     if (strncmp(path,"\"^UP^",5) == 0) {
8584       posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8585       return rslt;
8586     }
8587   }
8588 #endif
8589 
8590   /* This is really the only way to see if this is already in VMS format */
8591   sts = vms_split_path
8592        (path,
8593 	&v_spec,
8594 	&v_len,
8595 	&r_spec,
8596 	&r_len,
8597 	&d_spec,
8598 	&d_len,
8599 	&n_spec,
8600 	&n_len,
8601 	&e_spec,
8602 	&e_len,
8603 	&vs_spec,
8604 	&vs_len);
8605   if (sts == 0) {
8606     /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8607        replacement, because the above parse just took care of most of
8608        what is needed to do vmspath when the specification is already
8609        in VMS format.
8610 
8611        And if it is not already, it is easier to do the conversion as
8612        part of this routine than to call this routine and then work on
8613        the result.
8614      */
8615 
8616     /* If VMS punctuation was found, it is already VMS format */
8617     if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8618       if (utf8_flag != NULL)
8619 	*utf8_flag = 0;
8620       strcpy(rslt, path);
8621       if (vms_debug_fileify) {
8622           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8623       }
8624       return rslt;
8625     }
8626     /* Now, what to do with trailing "." cases where there is no
8627        extension?  If this is a UNIX specification, and EFS characters
8628        are enabled, then the trailing "." should be converted to a "^.".
8629        But if this was already a VMS specification, then it should be
8630        left alone.
8631 
8632        So in the case of ambiguity, leave the specification alone.
8633      */
8634 
8635 
8636     /* If there is a possibility of UTF8, then if any UTF8 characters
8637         are present, then they must be converted to VTF-7
8638      */
8639     if (utf8_flag != NULL)
8640       *utf8_flag = 0;
8641     strcpy(rslt, path);
8642     if (vms_debug_fileify) {
8643         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8644     }
8645     return rslt;
8646   }
8647 
8648   dirend = strrchr(path,'/');
8649 
8650   if (dirend == NULL) {
8651      char *macro_start;
8652      int has_macro;
8653 
8654      /* If we get here with no UNIX directory delimiters, then this is
8655         not a complete file specification, either garbage a UNIX glob
8656 	specification that can not be converted to a VMS wildcard, or
8657 	it a UNIX shell macro.  MakeMaker wants shell macros passed
8658 	through AS-IS,
8659 
8660 	utf8 flag setting needs to be preserved.
8661       */
8662       hasdir = 0;
8663 
8664       has_macro = 0;
8665       macro_start = strchr(path,'$');
8666       if (macro_start != NULL) {
8667           if (macro_start[1] == '(') {
8668               has_macro = 1;
8669           }
8670       }
8671       if ((decc_efs_charset == 0) || (has_macro)) {
8672           strcpy(rslt, path);
8673           if (vms_debug_fileify) {
8674               fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8675           }
8676           return rslt;
8677       }
8678   }
8679 
8680 /* If EFS charset mode active, handle the conversion */
8681 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8682   if (decc_efs_charset) {
8683     posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8684     if (vms_debug_fileify) {
8685         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8686     }
8687     return rslt;
8688   }
8689 #endif
8690 
8691   if (*(dirend+1) == '.') {  /* do we have trailing "/." or "/.." or "/..."? */
8692     if (!*(dirend+2)) dirend +=2;
8693     if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8694     if (decc_efs_charset == 0) {
8695       if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8696     }
8697   }
8698 
8699   cp1 = rslt;
8700   cp2 = path;
8701   lastdot = strrchr(cp2,'.');
8702   if (*cp2 == '/') {
8703     char *trndev;
8704     int islnm, rooted;
8705     STRLEN trnend;
8706 
8707     while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8708     if (!*(cp2+1)) {
8709       if (decc_disable_posix_root) {
8710 	strcpy(rslt,"sys$disk:[000000]");
8711       }
8712       else {
8713 	strcpy(rslt,"sys$posix_root:[000000]");
8714       }
8715       if (utf8_flag != NULL)
8716 	*utf8_flag = 0;
8717       if (vms_debug_fileify) {
8718           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8719       }
8720       return rslt;
8721     }
8722     while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8723     *cp1 = '\0';
8724     trndev = PerlMem_malloc(VMS_MAXRSS);
8725     if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8726     islnm =  simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8727 
8728      /* DECC special handling */
8729     if (!islnm) {
8730       if (strcmp(rslt,"bin") == 0) {
8731 	strcpy(rslt,"sys$system");
8732 	cp1 = rslt + 10;
8733 	*cp1 = 0;
8734 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8735       }
8736       else if (strcmp(rslt,"tmp") == 0) {
8737 	strcpy(rslt,"sys$scratch");
8738 	cp1 = rslt + 11;
8739 	*cp1 = 0;
8740 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8741       }
8742       else if (!decc_disable_posix_root) {
8743         strcpy(rslt, "sys$posix_root");
8744 	cp1 = rslt + 14;
8745 	*cp1 = 0;
8746 	cp2 = path;
8747         while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8748 	islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8749       }
8750       else if (strcmp(rslt,"dev") == 0) {
8751 	if (strncmp(cp2,"/null", 5) == 0) {
8752 	  if ((cp2[5] == 0) || (cp2[5] == '/')) {
8753 	    strcpy(rslt,"NLA0");
8754 	    cp1 = rslt + 4;
8755 	    *cp1 = 0;
8756 	    cp2 = cp2 + 5;
8757 	    islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8758 	  }
8759 	}
8760       }
8761     }
8762 
8763     trnend = islnm ? strlen(trndev) - 1 : 0;
8764     islnm =  trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8765     rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8766     /* If the first element of the path is a logical name, determine
8767      * whether it has to be translated so we can add more directories. */
8768     if (!islnm || rooted) {
8769       *(cp1++) = ':';
8770       *(cp1++) = '[';
8771       if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8772       else cp2++;
8773     }
8774     else {
8775       if (cp2 != dirend) {
8776         strcpy(rslt,trndev);
8777         cp1 = rslt + trnend;
8778 	if (*cp2 != 0) {
8779           *(cp1++) = '.';
8780           cp2++;
8781         }
8782       }
8783       else {
8784 	if (decc_disable_posix_root) {
8785 	  *(cp1++) = ':';
8786 	  hasdir = 0;
8787 	}
8788       }
8789     }
8790     PerlMem_free(trndev);
8791   }
8792   else {
8793     *(cp1++) = '[';
8794     if (*cp2 == '.') {
8795       if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8796         cp2 += 2;         /* skip over "./" - it's redundant */
8797         *(cp1++) = '.';   /* but it does indicate a relative dirspec */
8798       }
8799       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8800         *(cp1++) = '-';                                 /* "../" --> "-" */
8801         cp2 += 3;
8802       }
8803       else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8804                (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8805         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8806         if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8807         cp2 += 4;
8808       }
8809       else if ((cp2 != lastdot) || (lastdot < dirend)) {
8810 	/* Escape the extra dots in EFS file specifications */
8811 	*(cp1++) = '^';
8812       }
8813       if (cp2 > dirend) cp2 = dirend;
8814     }
8815     else *(cp1++) = '.';
8816   }
8817   for (; cp2 < dirend; cp2++) {
8818     if (*cp2 == '/') {
8819       if (*(cp2-1) == '/') continue;
8820       if (*(cp1-1) != '.') *(cp1++) = '.';
8821       infront = 0;
8822     }
8823     else if (!infront && *cp2 == '.') {
8824       if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8825       else if (*(cp2+1) == '/') cp2++;   /* skip over "./" - it's redundant */
8826       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8827         if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8828         else if (*(cp1-2) == '[') *(cp1-1) = '-';
8829         else {  /* back up over previous directory name */
8830           cp1--;
8831           while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8832           if (*(cp1-1) == '[') {
8833             memcpy(cp1,"000000.",7);
8834             cp1 += 7;
8835           }
8836         }
8837         cp2 += 2;
8838         if (cp2 == dirend) break;
8839       }
8840       else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8841                 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8842         if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8843         *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8844         if (!*(cp2+3)) {
8845           *(cp1++) = '.';  /* Simulate trailing '/' */
8846           cp2 += 2;  /* for loop will incr this to == dirend */
8847         }
8848         else cp2 += 3;  /* Trailing '/' was there, so skip it, too */
8849       }
8850       else {
8851         if (decc_efs_charset == 0)
8852 	  *(cp1++) = '_';  /* fix up syntax - '.' in name not allowed */
8853 	else {
8854 	  *(cp1++) = '^';  /* fix up syntax - '.' in name is allowed */
8855 	  *(cp1++) = '.';
8856 	}
8857       }
8858     }
8859     else {
8860       if (!infront && *(cp1-1) == '-')  *(cp1++) = '.';
8861       if (*cp2 == '.') {
8862         if (decc_efs_charset == 0)
8863 	  *(cp1++) = '_';
8864 	else {
8865 	  *(cp1++) = '^';
8866 	  *(cp1++) = '.';
8867 	}
8868       }
8869       else                  *(cp1++) =  *cp2;
8870       infront = 1;
8871     }
8872   }
8873   if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8874   if (hasdir) *(cp1++) = ']';
8875   if (*cp2) cp2++;  /* check in case we ended with trailing '..' */
8876   /* fixme for ODS5 */
8877   no_type_seen = 0;
8878   if (cp2 > lastdot)
8879     no_type_seen = 1;
8880   while (*cp2) {
8881     switch(*cp2) {
8882     case '?':
8883         if (decc_efs_charset == 0)
8884 	  *(cp1++) = '%';
8885 	else
8886 	  *(cp1++) = '?';
8887 	cp2++;
8888     case ' ':
8889 	*(cp1)++ = '^';
8890 	*(cp1)++ = '_';
8891 	cp2++;
8892 	break;
8893     case '.':
8894 	if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8895 	    decc_readdir_dropdotnotype) {
8896 	  *(cp1)++ = '^';
8897 	  *(cp1)++ = '.';
8898 	  cp2++;
8899 
8900 	  /* trailing dot ==> '^..' on VMS */
8901 	  if (*cp2 == '\0') {
8902 	    *(cp1++) = '.';
8903 	    no_type_seen = 0;
8904 	  }
8905 	}
8906 	else {
8907 	  *(cp1++) = *(cp2++);
8908 	  no_type_seen = 0;
8909 	}
8910 	break;
8911     case '$':
8912 	 /* This could be a macro to be passed through */
8913 	*(cp1++) = *(cp2++);
8914 	if (*cp2 == '(') {
8915 	const char * save_cp2;
8916 	char * save_cp1;
8917 	int is_macro;
8918 
8919 	    /* paranoid check */
8920 	    save_cp2 = cp2;
8921 	    save_cp1 = cp1;
8922 	    is_macro = 0;
8923 
8924 	    /* Test through */
8925 	    *(cp1++) = *(cp2++);
8926 	    if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8927 		*(cp1++) = *(cp2++);
8928 		while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8929 		    *(cp1++) = *(cp2++);
8930 		}
8931 		if (*cp2 == ')') {
8932 		    *(cp1++) = *(cp2++);
8933 		    is_macro = 1;
8934 		}
8935 	    }
8936 	    if (is_macro == 0) {
8937 		/* Not really a macro - never mind */
8938 		cp2 = save_cp2;
8939 		cp1 = save_cp1;
8940 	    }
8941 	}
8942 	break;
8943     case '\"':
8944     case '~':
8945     case '`':
8946     case '!':
8947     case '#':
8948     case '%':
8949     case '^':
8950         /* Don't escape again if following character is
8951          * already something we escape.
8952          */
8953         if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8954 	    *(cp1++) = *(cp2++);
8955 	    break;
8956         }
8957         /* But otherwise fall through and escape it. */
8958     case '&':
8959     case '(':
8960     case ')':
8961     case '=':
8962     case '+':
8963     case '\'':
8964     case '@':
8965     case '[':
8966     case ']':
8967     case '{':
8968     case '}':
8969     case ':':
8970     case '\\':
8971     case '|':
8972     case '<':
8973     case '>':
8974 	*(cp1++) = '^';
8975 	*(cp1++) = *(cp2++);
8976 	break;
8977     case ';':
8978 	/* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8979 	 * which is wrong.  UNIX notation should be ".dir." unless
8980 	 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8981 	 * changing this behavior could break more things at this time.
8982 	 * efs character set effectively does not allow "." to be a version
8983 	 * delimiter as a further complication about changing this.
8984 	 */
8985 	if (decc_filename_unix_report != 0) {
8986 	  *(cp1++) = '^';
8987 	}
8988 	*(cp1++) = *(cp2++);
8989 	break;
8990     default:
8991 	*(cp1++) = *(cp2++);
8992     }
8993   }
8994   if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8995   char *lcp1;
8996     lcp1 = cp1;
8997     lcp1--;
8998      /* Fix me for "^]", but that requires making sure that you do
8999       * not back up past the start of the filename
9000       */
9001     if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
9002       *cp1++ = '.';
9003   }
9004   *cp1 = '\0';
9005 
9006   if (utf8_flag != NULL)
9007     *utf8_flag = 0;
9008   if (vms_debug_fileify) {
9009       fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
9010   }
9011   return rslt;
9012 
9013 }  /* end of int_tovmsspec() */
9014 
9015 
9016 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
9017 static char *mp_do_tovmsspec
9018    (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
9019   static char __tovmsspec_retbuf[VMS_MAXRSS];
9020     char * vmsspec, *ret_spec, *ret_buf;
9021 
9022     vmsspec = NULL;
9023     ret_buf = buf;
9024     if (ret_buf == NULL) {
9025         if (ts) {
9026             Newx(vmsspec, VMS_MAXRSS, char);
9027             if (vmsspec == NULL)
9028                 _ckvmssts(SS$_INSFMEM);
9029             ret_buf = vmsspec;
9030         } else {
9031             ret_buf = __tovmsspec_retbuf;
9032         }
9033     }
9034 
9035     ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
9036 
9037     if (ret_spec == NULL) {
9038        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
9039        if (vmsspec)
9040            Safefree(vmsspec);
9041     }
9042 
9043     return ret_spec;
9044 
9045 }  /* end of mp_do_tovmsspec() */
9046 /*}}}*/
9047 /* External entry points */
9048 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
9049   { return do_tovmsspec(path,buf,0,NULL); }
9050 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
9051   { return do_tovmsspec(path,buf,1,NULL); }
9052 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9053   { return do_tovmsspec(path,buf,0,utf8_fl); }
9054 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9055   { return do_tovmsspec(path,buf,1,utf8_fl); }
9056 
9057 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
9058 /* Internal routine for use with out an explict context present */
9059 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
9060 
9061     char * ret_spec, *pathified;
9062 
9063     if (path == NULL)
9064         return NULL;
9065 
9066     pathified = PerlMem_malloc(VMS_MAXRSS);
9067     if (pathified == NULL)
9068         _ckvmssts_noperl(SS$_INSFMEM);
9069 
9070     ret_spec = int_pathify_dirspec(path, pathified);
9071 
9072     if (ret_spec == NULL) {
9073         PerlMem_free(pathified);
9074         return NULL;
9075     }
9076 
9077     ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
9078 
9079     PerlMem_free(pathified);
9080     return ret_spec;
9081 
9082 }
9083 
9084 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9085 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9086   static char __tovmspath_retbuf[VMS_MAXRSS];
9087   int vmslen;
9088   char *pathified, *vmsified, *cp;
9089 
9090   if (path == NULL) return NULL;
9091   pathified = PerlMem_malloc(VMS_MAXRSS);
9092   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9093   if (int_pathify_dirspec(path, pathified) == NULL) {
9094     PerlMem_free(pathified);
9095     return NULL;
9096   }
9097 
9098   vmsified = NULL;
9099   if (buf == NULL)
9100      Newx(vmsified, VMS_MAXRSS, char);
9101   if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
9102     PerlMem_free(pathified);
9103     if (vmsified) Safefree(vmsified);
9104     return NULL;
9105   }
9106   PerlMem_free(pathified);
9107   if (buf) {
9108     return buf;
9109   }
9110   else if (ts) {
9111     vmslen = strlen(vmsified);
9112     Newx(cp,vmslen+1,char);
9113     memcpy(cp,vmsified,vmslen);
9114     cp[vmslen] = '\0';
9115     Safefree(vmsified);
9116     return cp;
9117   }
9118   else {
9119     strcpy(__tovmspath_retbuf,vmsified);
9120     Safefree(vmsified);
9121     return __tovmspath_retbuf;
9122   }
9123 
9124 }  /* end of do_tovmspath() */
9125 /*}}}*/
9126 /* External entry points */
9127 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9128   { return do_tovmspath(path,buf,0, NULL); }
9129 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9130   { return do_tovmspath(path,buf,1, NULL); }
9131 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9132   { return do_tovmspath(path,buf,0,utf8_fl); }
9133 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9134   { return do_tovmspath(path,buf,1,utf8_fl); }
9135 
9136 
9137 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9138 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9139   static char __tounixpath_retbuf[VMS_MAXRSS];
9140   int unixlen;
9141   char *pathified, *unixified, *cp;
9142 
9143   if (path == NULL) return NULL;
9144   pathified = PerlMem_malloc(VMS_MAXRSS);
9145   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9146   if (int_pathify_dirspec(path, pathified) == NULL) {
9147     PerlMem_free(pathified);
9148     return NULL;
9149   }
9150 
9151   unixified = NULL;
9152   if (buf == NULL) {
9153       Newx(unixified, VMS_MAXRSS, char);
9154   }
9155   if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9156     PerlMem_free(pathified);
9157     if (unixified) Safefree(unixified);
9158     return NULL;
9159   }
9160   PerlMem_free(pathified);
9161   if (buf) {
9162     return buf;
9163   }
9164   else if (ts) {
9165     unixlen = strlen(unixified);
9166     Newx(cp,unixlen+1,char);
9167     memcpy(cp,unixified,unixlen);
9168     cp[unixlen] = '\0';
9169     Safefree(unixified);
9170     return cp;
9171   }
9172   else {
9173     strcpy(__tounixpath_retbuf,unixified);
9174     Safefree(unixified);
9175     return __tounixpath_retbuf;
9176   }
9177 
9178 }  /* end of do_tounixpath() */
9179 /*}}}*/
9180 /* External entry points */
9181 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9182   { return do_tounixpath(path,buf,0,NULL); }
9183 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9184   { return do_tounixpath(path,buf,1,NULL); }
9185 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9186   { return do_tounixpath(path,buf,0,utf8_fl); }
9187 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9188   { return do_tounixpath(path,buf,1,utf8_fl); }
9189 
9190 /*
9191  * @(#)argproc.c 2.2 94/08/16	Mark Pizzolato (mark AT infocomm DOT com)
9192  *
9193  *****************************************************************************
9194  *                                                                           *
9195  *  Copyright (C) 1989-1994, 2007 by                                         *
9196  *  Mark Pizzolato - INFO COMM, Danville, California  (510) 837-5600         *
9197  *                                                                           *
9198  *  Permission is hereby granted for the reproduction of this software       *
9199  *  on condition that this copyright notice is included in source            *
9200  *  distributions of the software.  The code may be modified and             *
9201  *  distributed under the same terms as Perl itself.                         *
9202  *                                                                           *
9203  *  27-Aug-1994 Modified for inclusion in perl5                              *
9204  *              by Charles Bailey  (bailey AT newman DOT upenn DOT edu)      *
9205  *****************************************************************************
9206  */
9207 
9208 /*
9209  * getredirection() is intended to aid in porting C programs
9210  * to VMS (Vax-11 C).  The native VMS environment does not support
9211  * '>' and '<' I/O redirection, or command line wild card expansion,
9212  * or a command line pipe mechanism using the '|' AND background
9213  * command execution '&'.  All of these capabilities are provided to any
9214  * C program which calls this procedure as the first thing in the
9215  * main program.
9216  * The piping mechanism will probably work with almost any 'filter' type
9217  * of program.  With suitable modification, it may useful for other
9218  * portability problems as well.
9219  *
9220  * Author:  Mark Pizzolato	(mark AT infocomm DOT com)
9221  */
9222 struct list_item
9223     {
9224     struct list_item *next;
9225     char *value;
9226     };
9227 
9228 static void add_item(struct list_item **head,
9229 		     struct list_item **tail,
9230 		     char *value,
9231 		     int *count);
9232 
9233 static void mp_expand_wild_cards(pTHX_ char *item,
9234 				struct list_item **head,
9235 				struct list_item **tail,
9236 				int *count);
9237 
9238 static int background_process(pTHX_ int argc, char **argv);
9239 
9240 static void pipe_and_fork(pTHX_ char **cmargv);
9241 
9242 /*{{{ void getredirection(int *ac, char ***av)*/
9243 static void
9244 mp_getredirection(pTHX_ int *ac, char ***av)
9245 /*
9246  * Process vms redirection arg's.  Exit if any error is seen.
9247  * If getredirection() processes an argument, it is erased
9248  * from the vector.  getredirection() returns a new argc and argv value.
9249  * In the event that a background command is requested (by a trailing "&"),
9250  * this routine creates a background subprocess, and simply exits the program.
9251  *
9252  * Warning: do not try to simplify the code for vms.  The code
9253  * presupposes that getredirection() is called before any data is
9254  * read from stdin or written to stdout.
9255  *
9256  * Normal usage is as follows:
9257  *
9258  *	main(argc, argv)
9259  *	int		argc;
9260  *    	char		*argv[];
9261  *	{
9262  *		getredirection(&argc, &argv);
9263  *	}
9264  */
9265 {
9266     int			argc = *ac;	/* Argument Count	  */
9267     char		**argv = *av;	/* Argument Vector	  */
9268     char		*ap;   		/* Argument pointer	  */
9269     int	       		j;		/* argv[] index		  */
9270     int			item_count = 0;	/* Count of Items in List */
9271     struct list_item 	*list_head = 0;	/* First Item in List	    */
9272     struct list_item	*list_tail;	/* Last Item in List	    */
9273     char 		*in = NULL;	/* Input File Name	    */
9274     char 		*out = NULL;	/* Output File Name	    */
9275     char 		*outmode = "w";	/* Mode to Open Output File */
9276     char 		*err = NULL;	/* Error File Name	    */
9277     char 		*errmode = "w";	/* Mode to Open Error File  */
9278     int			cmargc = 0;    	/* Piped Command Arg Count  */
9279     char		**cmargv = NULL;/* Piped Command Arg Vector */
9280 
9281     /*
9282      * First handle the case where the last thing on the line ends with
9283      * a '&'.  This indicates the desire for the command to be run in a
9284      * subprocess, so we satisfy that desire.
9285      */
9286     ap = argv[argc-1];
9287     if (0 == strcmp("&", ap))
9288        exit(background_process(aTHX_ --argc, argv));
9289     if (*ap && '&' == ap[strlen(ap)-1])
9290 	{
9291 	ap[strlen(ap)-1] = '\0';
9292        exit(background_process(aTHX_ argc, argv));
9293 	}
9294     /*
9295      * Now we handle the general redirection cases that involve '>', '>>',
9296      * '<', and pipes '|'.
9297      */
9298     for (j = 0; j < argc; ++j)
9299 	{
9300 	if (0 == strcmp("<", argv[j]))
9301 	    {
9302 	    if (j+1 >= argc)
9303 		{
9304 		fprintf(stderr,"No input file after < on command line");
9305 		exit(LIB$_WRONUMARG);
9306 		}
9307 	    in = argv[++j];
9308 	    continue;
9309 	    }
9310 	if ('<' == *(ap = argv[j]))
9311 	    {
9312 	    in = 1 + ap;
9313 	    continue;
9314 	    }
9315 	if (0 == strcmp(">", ap))
9316 	    {
9317 	    if (j+1 >= argc)
9318 		{
9319 		fprintf(stderr,"No output file after > on command line");
9320 		exit(LIB$_WRONUMARG);
9321 		}
9322 	    out = argv[++j];
9323 	    continue;
9324 	    }
9325 	if ('>' == *ap)
9326 	    {
9327 	    if ('>' == ap[1])
9328 		{
9329 		outmode = "a";
9330 		if ('\0' == ap[2])
9331 		    out = argv[++j];
9332 		else
9333 		    out = 2 + ap;
9334 		}
9335 	    else
9336 		out = 1 + ap;
9337 	    if (j >= argc)
9338 		{
9339 		fprintf(stderr,"No output file after > or >> on command line");
9340 		exit(LIB$_WRONUMARG);
9341 		}
9342 	    continue;
9343 	    }
9344 	if (('2' == *ap) && ('>' == ap[1]))
9345 	    {
9346 	    if ('>' == ap[2])
9347 		{
9348 		errmode = "a";
9349 		if ('\0' == ap[3])
9350 		    err = argv[++j];
9351 		else
9352 		    err = 3 + ap;
9353 		}
9354 	    else
9355 		if ('\0' == ap[2])
9356 		    err = argv[++j];
9357 		else
9358 		    err = 2 + ap;
9359 	    if (j >= argc)
9360 		{
9361 		fprintf(stderr,"No output file after 2> or 2>> on command line");
9362 		exit(LIB$_WRONUMARG);
9363 		}
9364 	    continue;
9365 	    }
9366 	if (0 == strcmp("|", argv[j]))
9367 	    {
9368 	    if (j+1 >= argc)
9369 		{
9370 		fprintf(stderr,"No command into which to pipe on command line");
9371 		exit(LIB$_WRONUMARG);
9372 		}
9373 	    cmargc = argc-(j+1);
9374 	    cmargv = &argv[j+1];
9375 	    argc = j;
9376 	    continue;
9377 	    }
9378 	if ('|' == *(ap = argv[j]))
9379 	    {
9380 	    ++argv[j];
9381 	    cmargc = argc-j;
9382 	    cmargv = &argv[j];
9383 	    argc = j;
9384 	    continue;
9385 	    }
9386 	expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9387 	}
9388     /*
9389      * Allocate and fill in the new argument vector, Some Unix's terminate
9390      * the list with an extra null pointer.
9391      */
9392     argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9393     if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9394     *av = argv;
9395     for (j = 0; j < item_count; ++j, list_head = list_head->next)
9396 	argv[j] = list_head->value;
9397     *ac = item_count;
9398     if (cmargv != NULL)
9399 	{
9400 	if (out != NULL)
9401 	    {
9402 	    fprintf(stderr,"'|' and '>' may not both be specified on command line");
9403 	    exit(LIB$_INVARGORD);
9404 	    }
9405 	pipe_and_fork(aTHX_ cmargv);
9406 	}
9407 
9408     /* Check for input from a pipe (mailbox) */
9409 
9410     if (in == NULL && 1 == isapipe(0))
9411 	{
9412 	char mbxname[L_tmpnam];
9413 	long int bufsize;
9414 	long int dvi_item = DVI$_DEVBUFSIZ;
9415 	$DESCRIPTOR(mbxnam, "");
9416 	$DESCRIPTOR(mbxdevnam, "");
9417 
9418 	/* Input from a pipe, reopen it in binary mode to disable	*/
9419 	/* carriage control processing.	 				*/
9420 
9421 	fgetname(stdin, mbxname, 1);
9422 	mbxnam.dsc$a_pointer = mbxname;
9423 	mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9424 	lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9425 	mbxdevnam.dsc$a_pointer = mbxname;
9426 	mbxdevnam.dsc$w_length = sizeof(mbxname);
9427 	dvi_item = DVI$_DEVNAM;
9428 	lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9429 	mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9430 	set_errno(0);
9431 	set_vaxc_errno(1);
9432 	freopen(mbxname, "rb", stdin);
9433 	if (errno != 0)
9434 	    {
9435 	    fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9436 	    exit(vaxc$errno);
9437 	    }
9438 	}
9439     if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9440 	{
9441 	fprintf(stderr,"Can't open input file %s as stdin",in);
9442 	exit(vaxc$errno);
9443 	}
9444     if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9445 	{
9446 	fprintf(stderr,"Can't open output file %s as stdout",out);
9447 	exit(vaxc$errno);
9448 	}
9449 	if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9450 
9451     if (err != NULL) {
9452         if (strcmp(err,"&1") == 0) {
9453             dup2(fileno(stdout), fileno(stderr));
9454             Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9455         } else {
9456 	FILE *tmperr;
9457 	if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9458 	    {
9459 	    fprintf(stderr,"Can't open error file %s as stderr",err);
9460 	    exit(vaxc$errno);
9461 	    }
9462 	    fclose(tmperr);
9463            if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9464 		{
9465 		exit(vaxc$errno);
9466 		}
9467 	    Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9468 	}
9469         }
9470 #ifdef ARGPROC_DEBUG
9471     PerlIO_printf(Perl_debug_log, "Arglist:\n");
9472     for (j = 0; j < *ac;  ++j)
9473 	PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9474 #endif
9475    /* Clear errors we may have hit expanding wildcards, so they don't
9476       show up in Perl's $! later */
9477    set_errno(0); set_vaxc_errno(1);
9478 }  /* end of getredirection() */
9479 /*}}}*/
9480 
9481 static void add_item(struct list_item **head,
9482 		     struct list_item **tail,
9483 		     char *value,
9484 		     int *count)
9485 {
9486     if (*head == 0)
9487 	{
9488 	*head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9489 	if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9490 	*tail = *head;
9491 	}
9492     else {
9493 	(*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9494 	if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9495 	*tail = (*tail)->next;
9496 	}
9497     (*tail)->value = value;
9498     ++(*count);
9499 }
9500 
9501 static void mp_expand_wild_cards(pTHX_ char *item,
9502 			      struct list_item **head,
9503 			      struct list_item **tail,
9504 			      int *count)
9505 {
9506 int expcount = 0;
9507 unsigned long int context = 0;
9508 int isunix = 0;
9509 int item_len = 0;
9510 char *had_version;
9511 char *had_device;
9512 int had_directory;
9513 char *devdir,*cp;
9514 char *vmsspec;
9515 $DESCRIPTOR(filespec, "");
9516 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9517 $DESCRIPTOR(resultspec, "");
9518 unsigned long int lff_flags = 0;
9519 int sts;
9520 int rms_sts;
9521 
9522 #ifdef VMS_LONGNAME_SUPPORT
9523     lff_flags = LIB$M_FIL_LONG_NAMES;
9524 #endif
9525 
9526     for (cp = item; *cp; cp++) {
9527 	if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9528 	if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9529     }
9530     if (!*cp || isspace(*cp))
9531 	{
9532 	add_item(head, tail, item, count);
9533 	return;
9534 	}
9535     else
9536         {
9537      /* "double quoted" wild card expressions pass as is */
9538      /* From DCL that means using e.g.:                  */
9539      /* perl program """perl.*"""                        */
9540      item_len = strlen(item);
9541      if ( '"' == *item && '"' == item[item_len-1] )
9542        {
9543        item++;
9544        item[item_len-2] = '\0';
9545        add_item(head, tail, item, count);
9546        return;
9547        }
9548      }
9549     resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9550     resultspec.dsc$b_class = DSC$K_CLASS_D;
9551     resultspec.dsc$a_pointer = NULL;
9552     vmsspec = PerlMem_malloc(VMS_MAXRSS);
9553     if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9554     if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9555       filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9556     if (!isunix || !filespec.dsc$a_pointer)
9557       filespec.dsc$a_pointer = item;
9558     filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9559     /*
9560      * Only return version specs, if the caller specified a version
9561      */
9562     had_version = strchr(item, ';');
9563     /*
9564      * Only return device and directory specs, if the caller specifed either.
9565      */
9566     had_device = strchr(item, ':');
9567     had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9568 
9569     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9570 				 (&filespec, &resultspec, &context,
9571     				  &defaultspec, 0, &rms_sts, &lff_flags)))
9572 	{
9573 	char *string;
9574 	char *c;
9575 
9576 	string = PerlMem_malloc(resultspec.dsc$w_length+1);
9577         if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9578 	strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9579 	string[resultspec.dsc$w_length] = '\0';
9580 	if (NULL == had_version)
9581 	    *(strrchr(string, ';')) = '\0';
9582 	if ((!had_directory) && (had_device == NULL))
9583 	    {
9584 	    if (NULL == (devdir = strrchr(string, ']')))
9585 		devdir = strrchr(string, '>');
9586 	    strcpy(string, devdir + 1);
9587 	    }
9588 	/*
9589 	 * Be consistent with what the C RTL has already done to the rest of
9590 	 * the argv items and lowercase all of these names.
9591 	 */
9592 	if (!decc_efs_case_preserve) {
9593 	    for (c = string; *c; ++c)
9594 	    if (isupper(*c))
9595 		*c = tolower(*c);
9596 	}
9597 	if (isunix) trim_unixpath(string,item,1);
9598 	add_item(head, tail, string, count);
9599 	++expcount;
9600     }
9601     PerlMem_free(vmsspec);
9602     if (sts != RMS$_NMF)
9603 	{
9604 	set_vaxc_errno(sts);
9605 	switch (sts)
9606 	    {
9607 	    case RMS$_FNF: case RMS$_DNF:
9608 		set_errno(ENOENT); break;
9609 	    case RMS$_DIR:
9610 		set_errno(ENOTDIR); break;
9611 	    case RMS$_DEV:
9612 		set_errno(ENODEV); break;
9613 	    case RMS$_FNM: case RMS$_SYN:
9614 		set_errno(EINVAL); break;
9615 	    case RMS$_PRV:
9616 		set_errno(EACCES); break;
9617 	    default:
9618 		_ckvmssts_noperl(sts);
9619 	    }
9620 	}
9621     if (expcount == 0)
9622 	add_item(head, tail, item, count);
9623     _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9624     _ckvmssts_noperl(lib$find_file_end(&context));
9625 }
9626 
9627 static int child_st[2];/* Event Flag set when child process completes	*/
9628 
9629 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox		*/
9630 
9631 static unsigned long int exit_handler(int *status)
9632 {
9633 short iosb[4];
9634 
9635     if (0 == child_st[0])
9636 	{
9637 #ifdef ARGPROC_DEBUG
9638 	PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9639 #endif
9640 	fflush(stdout);	    /* Have to flush pipe for binary data to	*/
9641 			    /* terminate properly -- <tp@mccall.com>	*/
9642 	sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9643 	sys$dassgn(child_chan);
9644 	fclose(stdout);
9645 	sys$synch(0, child_st);
9646 	}
9647     return(1);
9648 }
9649 
9650 static void sig_child(int chan)
9651 {
9652 #ifdef ARGPROC_DEBUG
9653     PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9654 #endif
9655     if (child_st[0] == 0)
9656 	child_st[0] = 1;
9657 }
9658 
9659 static struct exit_control_block exit_block =
9660     {
9661     0,
9662     exit_handler,
9663     1,
9664     &exit_block.exit_status,
9665     0
9666     };
9667 
9668 static void
9669 pipe_and_fork(pTHX_ char **cmargv)
9670 {
9671     PerlIO *fp;
9672     struct dsc$descriptor_s *vmscmd;
9673     char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9674     int sts, j, l, ismcr, quote, tquote = 0;
9675 
9676     sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9677     vms_execfree(vmscmd);
9678 
9679     j = l = 0;
9680     p = subcmd;
9681     q = cmargv[0];
9682     ismcr = q && toupper(*q) == 'M'     && toupper(*(q+1)) == 'C'
9683               && toupper(*(q+2)) == 'R' && !*(q+3);
9684 
9685     while (q && l < MAX_DCL_LINE_LENGTH) {
9686         if (!*q) {
9687             if (j > 0 && quote) {
9688                 *p++ = '"';
9689                 l++;
9690             }
9691             q = cmargv[++j];
9692             if (q) {
9693                 if (ismcr && j > 1) quote = 1;
9694                 tquote =  (strchr(q,' ')) != NULL || *q == '\0';
9695                 *p++ = ' ';
9696                 l++;
9697                 if (quote || tquote) {
9698                     *p++ = '"';
9699                     l++;
9700                 }
9701 	    }
9702         } else {
9703             if ((quote||tquote) && *q == '"') {
9704                 *p++ = '"';
9705                 l++;
9706 	    }
9707             *p++ = *q++;
9708             l++;
9709         }
9710     }
9711     *p = '\0';
9712 
9713     fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9714     if (fp == NULL) {
9715         PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9716     }
9717 }
9718 
9719 static int background_process(pTHX_ int argc, char **argv)
9720 {
9721 char command[MAX_DCL_SYMBOL + 1] = "$";
9722 $DESCRIPTOR(value, "");
9723 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9724 static $DESCRIPTOR(null, "NLA0:");
9725 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9726 char pidstring[80];
9727 $DESCRIPTOR(pidstr, "");
9728 int pid;
9729 unsigned long int flags = 17, one = 1, retsts;
9730 int len;
9731 
9732     strcat(command, argv[0]);
9733     len = strlen(command);
9734     while (--argc && (len < MAX_DCL_SYMBOL))
9735 	{
9736 	strcat(command, " \"");
9737 	strcat(command, *(++argv));
9738 	strcat(command, "\"");
9739 	len = strlen(command);
9740 	}
9741     value.dsc$a_pointer = command;
9742     value.dsc$w_length = strlen(value.dsc$a_pointer);
9743     _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9744     retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9745     if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9746 	_ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9747     }
9748     else {
9749 	_ckvmssts_noperl(retsts);
9750     }
9751 #ifdef ARGPROC_DEBUG
9752     PerlIO_printf(Perl_debug_log, "%s\n", command);
9753 #endif
9754     sprintf(pidstring, "%08X", pid);
9755     PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9756     pidstr.dsc$a_pointer = pidstring;
9757     pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9758     lib$set_symbol(&pidsymbol, &pidstr);
9759     return(SS$_NORMAL);
9760 }
9761 /*}}}*/
9762 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9763 
9764 
9765 /* OS-specific initialization at image activation (not thread startup) */
9766 /* Older VAXC header files lack these constants */
9767 #ifndef JPI$_RIGHTS_SIZE
9768 #  define JPI$_RIGHTS_SIZE 817
9769 #endif
9770 #ifndef KGB$M_SUBSYSTEM
9771 #  define KGB$M_SUBSYSTEM 0x8
9772 #endif
9773 
9774 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9775 
9776 /*{{{void vms_image_init(int *, char ***)*/
9777 void
9778 vms_image_init(int *argcp, char ***argvp)
9779 {
9780   int status;
9781   char eqv[LNM$C_NAMLENGTH+1] = "";
9782   unsigned int len, tabct = 8, tabidx = 0;
9783   unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9784   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9785   unsigned short int dummy, rlen;
9786   struct dsc$descriptor_s **tabvec;
9787 #if defined(PERL_IMPLICIT_CONTEXT)
9788   pTHX = NULL;
9789 #endif
9790   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
9791                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
9792                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9793                                  {          0,                0,    0,      0} };
9794 
9795 #ifdef KILL_BY_SIGPRC
9796     Perl_csighandler_init();
9797 #endif
9798 
9799 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9800     /* This was moved from the pre-image init handler because on threaded */
9801     /* Perl it was always returning 0 for the default value. */
9802     status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9803     if (status > 0) {
9804         int s;
9805 	s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9806 	if (s > 0) {
9807             int initial;
9808 	    initial = decc$feature_get_value(s, 4);
9809 	    if (initial > 0) {
9810                 /* initial is: 0 if nothing has set the feature */
9811                 /*            -1 if initialized to default */
9812                 /*             1 if set by logical name */
9813                 /*             2 if set by decc$feature_set_value */
9814 		decc_disable_posix_root = decc$feature_get_value(s, 1);
9815 
9816                 /* If the value is not valid, force the feature off */
9817 		if (decc_disable_posix_root < 0) {
9818 		    decc$feature_set_value(s, 1, 1);
9819 		    decc_disable_posix_root = 1;
9820 		}
9821 	    }
9822 	    else {
9823 		/* Nothing has asked for it explicitly, so use our own default. */
9824 		decc_disable_posix_root = 1;
9825 		decc$feature_set_value(s, 1, 1);
9826 	    }
9827 	}
9828     }
9829 #endif
9830 
9831   _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9832   _ckvmssts_noperl(iosb[0]);
9833   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9834     if (iprv[i]) {           /* Running image installed with privs? */
9835       _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
9836       will_taint = TRUE;
9837       break;
9838     }
9839   }
9840   /* Rights identifiers might trigger tainting as well. */
9841   if (!will_taint && (rlen || rsz)) {
9842     while (rlen < rsz) {
9843       /* We didn't get all the identifiers on the first pass.  Allocate a
9844        * buffer much larger than $GETJPI wants (rsz is size in bytes that
9845        * were needed to hold all identifiers at time of last call; we'll
9846        * allocate that many unsigned long ints), and go back and get 'em.
9847        * If it gave us less than it wanted to despite ample buffer space,
9848        * something's broken.  Is your system missing a system identifier?
9849        */
9850       if (rsz <= jpilist[1].buflen) {
9851          /* Perl_croak accvios when used this early in startup. */
9852          fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9853                          rsz, (unsigned long) jpilist[1].buflen,
9854                          "Check your rights database for corruption.\n");
9855          exit(SS$_ABORT);
9856       }
9857       if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9858       jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9859       if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9860       jpilist[1].buflen = rsz * sizeof(unsigned long int);
9861       _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9862       _ckvmssts_noperl(iosb[0]);
9863     }
9864     mask = jpilist[1].bufadr;
9865     /* Check attribute flags for each identifier (2nd longword); protected
9866      * subsystem identifiers trigger tainting.
9867      */
9868     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9869       if (mask[i] & KGB$M_SUBSYSTEM) {
9870         will_taint = TRUE;
9871         break;
9872       }
9873     }
9874     if (mask != rlst) PerlMem_free(mask);
9875   }
9876 
9877   /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9878    * logical, some versions of the CRTL will add a phanthom /000000/
9879    * directory.  This needs to be removed.
9880    */
9881   if (decc_filename_unix_report) {
9882   char * zeros;
9883   int ulen;
9884     ulen = strlen(argvp[0][0]);
9885     if (ulen > 7) {
9886       zeros = strstr(argvp[0][0], "/000000/");
9887       if (zeros != NULL) {
9888 	int mlen;
9889 	mlen = ulen - (zeros - argvp[0][0]) - 7;
9890 	memmove(zeros, &zeros[7], mlen);
9891 	ulen = ulen - 7;
9892 	argvp[0][0][ulen] = '\0';
9893       }
9894     }
9895     /* It also may have a trailing dot that needs to be removed otherwise
9896      * it will be converted to VMS mode incorrectly.
9897      */
9898     ulen--;
9899     if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9900       argvp[0][0][ulen] = '\0';
9901   }
9902 
9903   /* We need to use this hack to tell Perl it should run with tainting,
9904    * since its tainting flag may be part of the PL_curinterp struct, which
9905    * hasn't been allocated when vms_image_init() is called.
9906    */
9907   if (will_taint) {
9908     char **newargv, **oldargv;
9909     oldargv = *argvp;
9910     newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9911     if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9912     newargv[0] = oldargv[0];
9913     newargv[1] = PerlMem_malloc(3 * sizeof(char));
9914     if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9915     strcpy(newargv[1], "-T");
9916     Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9917     (*argcp)++;
9918     newargv[*argcp] = NULL;
9919     /* We orphan the old argv, since we don't know where it's come from,
9920      * so we don't know how to free it.
9921      */
9922     *argvp = newargv;
9923   }
9924   else {  /* Did user explicitly request tainting? */
9925     int i;
9926     char *cp, **av = *argvp;
9927     for (i = 1; i < *argcp; i++) {
9928       if (*av[i] != '-') break;
9929       for (cp = av[i]+1; *cp; cp++) {
9930         if (*cp == 'T') { will_taint = 1; break; }
9931         else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9932                   strchr("DFIiMmx",*cp)) break;
9933       }
9934       if (will_taint) break;
9935     }
9936   }
9937 
9938   for (tabidx = 0;
9939        len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9940        tabidx++) {
9941     if (!tabidx) {
9942       tabvec = (struct dsc$descriptor_s **)
9943 	    PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9944       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9945     }
9946     else if (tabidx >= tabct) {
9947       tabct += 8;
9948       tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9949       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9950     }
9951     tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9952     if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9953     tabvec[tabidx]->dsc$w_length  = 0;
9954     tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
9955     tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_D;
9956     tabvec[tabidx]->dsc$a_pointer = NULL;
9957     _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9958   }
9959   if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9960 
9961   getredirection(argcp,argvp);
9962 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9963   {
9964 # include <reentrancy.h>
9965   decc$set_reentrancy(C$C_MULTITHREAD);
9966   }
9967 #endif
9968   return;
9969 }
9970 /*}}}*/
9971 
9972 
9973 /* trim_unixpath()
9974  * Trim Unix-style prefix off filespec, so it looks like what a shell
9975  * glob expansion would return (i.e. from specified prefix on, not
9976  * full path).  Note that returned filespec is Unix-style, regardless
9977  * of whether input filespec was VMS-style or Unix-style.
9978  *
9979  * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9980  * determine prefix (both may be in VMS or Unix syntax).  opts is a bit
9981  * vector of options; at present, only bit 0 is used, and if set tells
9982  * trim unixpath to try the current default directory as a prefix when
9983  * presented with a possibly ambiguous ... wildcard.
9984  *
9985  * Returns !=0 on success, with trimmed filespec replacing contents of
9986  * fspec, and 0 on failure, with contents of fpsec unchanged.
9987  */
9988 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9989 int
9990 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9991 {
9992   char *unixified, *unixwild,
9993        *template, *base, *end, *cp1, *cp2;
9994   register int tmplen, reslen = 0, dirs = 0;
9995 
9996   if (!wildspec || !fspec) return 0;
9997 
9998   unixwild = PerlMem_malloc(VMS_MAXRSS);
9999   if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10000   template = unixwild;
10001   if (strpbrk(wildspec,"]>:") != NULL) {
10002     if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
10003         PerlMem_free(unixwild);
10004 	return 0;
10005     }
10006   }
10007   else {
10008     strncpy(unixwild, wildspec, VMS_MAXRSS-1);
10009     unixwild[VMS_MAXRSS-1] = 0;
10010   }
10011   unixified = PerlMem_malloc(VMS_MAXRSS);
10012   if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10013   if (strpbrk(fspec,"]>:") != NULL) {
10014     if (int_tounixspec(fspec, unixified, NULL) == NULL) {
10015         PerlMem_free(unixwild);
10016         PerlMem_free(unixified);
10017 	return 0;
10018     }
10019     else base = unixified;
10020     /* reslen != 0 ==> we had to unixify resultant filespec, so we must
10021      * check to see that final result fits into (isn't longer than) fspec */
10022     reslen = strlen(fspec);
10023   }
10024   else base = fspec;
10025 
10026   /* No prefix or absolute path on wildcard, so nothing to remove */
10027   if (!*template || *template == '/') {
10028     PerlMem_free(unixwild);
10029     if (base == fspec) {
10030         PerlMem_free(unixified);
10031 	return 1;
10032     }
10033     tmplen = strlen(unixified);
10034     if (tmplen > reslen) {
10035         PerlMem_free(unixified);
10036 	return 0;  /* not enough space */
10037     }
10038     /* Copy unixified resultant, including trailing NUL */
10039     memmove(fspec,unixified,tmplen+1);
10040     PerlMem_free(unixified);
10041     return 1;
10042   }
10043 
10044   for (end = base; *end; end++) ;  /* Find end of resultant filespec */
10045   if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
10046     for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
10047     for (cp1 = end ;cp1 >= base; cp1--)
10048       if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
10049         { cp1++; break; }
10050     if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
10051     PerlMem_free(unixified);
10052     PerlMem_free(unixwild);
10053     return 1;
10054   }
10055   else {
10056     char *tpl, *lcres;
10057     char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
10058     int ells = 1, totells, segdirs, match;
10059     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
10060                             resdsc =  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10061 
10062     while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
10063     totells = ells;
10064     for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
10065     tpl = PerlMem_malloc(VMS_MAXRSS);
10066     if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10067     if (ellipsis == template && opts & 1) {
10068       /* Template begins with an ellipsis.  Since we can't tell how many
10069        * directory names at the front of the resultant to keep for an
10070        * arbitrary starting point, we arbitrarily choose the current
10071        * default directory as a starting point.  If it's there as a prefix,
10072        * clip it off.  If not, fall through and act as if the leading
10073        * ellipsis weren't there (i.e. return shortest possible path that
10074        * could match template).
10075        */
10076       if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
10077 	  PerlMem_free(tpl);
10078 	  PerlMem_free(unixified);
10079 	  PerlMem_free(unixwild);
10080 	  return 0;
10081       }
10082       if (!decc_efs_case_preserve) {
10083  	for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10084 	  if (_tolower(*cp1) != _tolower(*cp2)) break;
10085       }
10086       segdirs = dirs - totells;  /* Min # of dirs we must have left */
10087       for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
10088       if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
10089         memmove(fspec,cp2+1,end - cp2);
10090 	PerlMem_free(tpl);
10091 	PerlMem_free(unixified);
10092 	PerlMem_free(unixwild);
10093         return 1;
10094       }
10095     }
10096     /* First off, back up over constant elements at end of path */
10097     if (dirs) {
10098       for (front = end ; front >= base; front--)
10099          if (*front == '/' && !dirs--) { front++; break; }
10100     }
10101     lcres = PerlMem_malloc(VMS_MAXRSS);
10102     if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10103     for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10104          cp1++,cp2++) {
10105 	    if (!decc_efs_case_preserve) {
10106 		*cp2 = _tolower(*cp1);  /* Make lc copy for match */
10107 	    }
10108 	    else {
10109 		*cp2 = *cp1;
10110 	    }
10111     }
10112     if (cp1 != '\0') {
10113 	PerlMem_free(tpl);
10114 	PerlMem_free(unixified);
10115 	PerlMem_free(unixwild);
10116 	PerlMem_free(lcres);
10117 	return 0;  /* Path too long. */
10118     }
10119     lcend = cp2;
10120     *cp2 = '\0';  /* Pick up with memcpy later */
10121     lcfront = lcres + (front - base);
10122     /* Now skip over each ellipsis and try to match the path in front of it. */
10123     while (ells--) {
10124       for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10125         if (*(cp1)   == '.' && *(cp1+1) == '.' &&
10126             *(cp1+2) == '.' && *(cp1+3) == '/'    ) break;
10127       if (cp1 < template) break; /* template started with an ellipsis */
10128       if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10129         ellipsis = cp1; continue;
10130       }
10131       wilddsc.dsc$a_pointer = tpl;
10132       wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10133       nextell = cp1;
10134       for (segdirs = 0, cp2 = tpl;
10135            cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10136            cp1++, cp2++) {
10137          if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10138          else {
10139 	    if (!decc_efs_case_preserve) {
10140 	      *cp2 = _tolower(*cp1);  /* else lowercase for match */
10141 	    }
10142 	    else {
10143 	      *cp2 = *cp1;  /* else preserve case for match */
10144 	    }
10145 	 }
10146          if (*cp2 == '/') segdirs++;
10147       }
10148       if (cp1 != ellipsis - 1) {
10149 	  PerlMem_free(tpl);
10150 	  PerlMem_free(unixified);
10151 	  PerlMem_free(unixwild);
10152 	  PerlMem_free(lcres);
10153 	  return 0; /* Path too long */
10154       }
10155       /* Back up at least as many dirs as in template before matching */
10156       for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10157         if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10158       for (match = 0; cp1 > lcres;) {
10159         resdsc.dsc$a_pointer = cp1;
10160         if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10161           match++;
10162           if (match == 1) lcfront = cp1;
10163         }
10164         for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10165       }
10166       if (!match) {
10167 	PerlMem_free(tpl);
10168 	PerlMem_free(unixified);
10169 	PerlMem_free(unixwild);
10170 	PerlMem_free(lcres);
10171 	return 0;  /* Can't find prefix ??? */
10172       }
10173       if (match > 1 && opts & 1) {
10174         /* This ... wildcard could cover more than one set of dirs (i.e.
10175          * a set of similar dir names is repeated).  If the template
10176          * contains more than 1 ..., upstream elements could resolve the
10177          * ambiguity, but it's not worth a full backtracking setup here.
10178          * As a quick heuristic, clip off the current default directory
10179          * if it's present to find the trimmed spec, else use the
10180          * shortest string that this ... could cover.
10181          */
10182         char def[NAM$C_MAXRSS+1], *st;
10183 
10184         if (getcwd(def, sizeof def,0) == NULL) {
10185 	    PerlMem_free(unixified);
10186 	    PerlMem_free(unixwild);
10187 	    PerlMem_free(lcres);
10188 	    PerlMem_free(tpl);
10189 	    return 0;
10190 	}
10191 	if (!decc_efs_case_preserve) {
10192 	  for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10193 	    if (_tolower(*cp1) != _tolower(*cp2)) break;
10194 	}
10195         segdirs = dirs - totells;  /* Min # of dirs we must have left */
10196         for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10197         if (*cp1 == '\0' && *cp2 == '/') {
10198           memmove(fspec,cp2+1,end - cp2);
10199 	  PerlMem_free(tpl);
10200 	  PerlMem_free(unixified);
10201 	  PerlMem_free(unixwild);
10202 	  PerlMem_free(lcres);
10203           return 1;
10204         }
10205         /* Nope -- stick with lcfront from above and keep going. */
10206       }
10207     }
10208     memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10209     PerlMem_free(tpl);
10210     PerlMem_free(unixified);
10211     PerlMem_free(unixwild);
10212     PerlMem_free(lcres);
10213     return 1;
10214     ellipsis = nextell;
10215   }
10216 
10217 }  /* end of trim_unixpath() */
10218 /*}}}*/
10219 
10220 
10221 /*
10222  *  VMS readdir() routines.
10223  *  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10224  *
10225  *  21-Jul-1994  Charles Bailey  bailey@newman.upenn.edu
10226  *  Minor modifications to original routines.
10227  */
10228 
10229 /* readdir may have been redefined by reentr.h, so make sure we get
10230  * the local version for what we do here.
10231  */
10232 #ifdef readdir
10233 # undef readdir
10234 #endif
10235 #if !defined(PERL_IMPLICIT_CONTEXT)
10236 # define readdir Perl_readdir
10237 #else
10238 # define readdir(a) Perl_readdir(aTHX_ a)
10239 #endif
10240 
10241     /* Number of elements in vms_versions array */
10242 #define VERSIZE(e)	(sizeof e->vms_versions / sizeof e->vms_versions[0])
10243 
10244 /*
10245  *  Open a directory, return a handle for later use.
10246  */
10247 /*{{{ DIR *opendir(char*name) */
10248 DIR *
10249 Perl_opendir(pTHX_ const char *name)
10250 {
10251     DIR *dd;
10252     char *dir;
10253     Stat_t sb;
10254 
10255     Newx(dir, VMS_MAXRSS, char);
10256     if (int_tovmspath(name, dir, NULL) == NULL) {
10257       Safefree(dir);
10258       return NULL;
10259     }
10260     /* Check access before stat; otherwise stat does not
10261      * accurately report whether it's a directory.
10262      */
10263     if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10264       /* cando_by_name has already set errno */
10265       Safefree(dir);
10266       return NULL;
10267     }
10268     if (flex_stat(dir,&sb) == -1) return NULL;
10269     if (!S_ISDIR(sb.st_mode)) {
10270       Safefree(dir);
10271       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
10272       return NULL;
10273     }
10274     /* Get memory for the handle, and the pattern. */
10275     Newx(dd,1,DIR);
10276     Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10277 
10278     /* Fill in the fields; mainly playing with the descriptor. */
10279     sprintf(dd->pattern, "%s*.*",dir);
10280     Safefree(dir);
10281     dd->context = 0;
10282     dd->count = 0;
10283     dd->flags = 0;
10284     /* By saying we always want the result of readdir() in unix format, we
10285      * are really saying we want all the escapes removed.  Otherwise the caller,
10286      * having no way to know whether it's already in VMS format, might send it
10287      * through tovmsspec again, thus double escaping.
10288      */
10289     dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10290     dd->pat.dsc$a_pointer = dd->pattern;
10291     dd->pat.dsc$w_length = strlen(dd->pattern);
10292     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10293     dd->pat.dsc$b_class = DSC$K_CLASS_S;
10294 #if defined(USE_ITHREADS)
10295     Newx(dd->mutex,1,perl_mutex);
10296     MUTEX_INIT( (perl_mutex *) dd->mutex );
10297 #else
10298     dd->mutex = NULL;
10299 #endif
10300 
10301     return dd;
10302 }  /* end of opendir() */
10303 /*}}}*/
10304 
10305 /*
10306  *  Set the flag to indicate we want versions or not.
10307  */
10308 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10309 void
10310 vmsreaddirversions(DIR *dd, int flag)
10311 {
10312     if (flag)
10313 	dd->flags |= PERL_VMSDIR_M_VERSIONS;
10314     else
10315 	dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10316 }
10317 /*}}}*/
10318 
10319 /*
10320  *  Free up an opened directory.
10321  */
10322 /*{{{ void closedir(DIR *dd)*/
10323 void
10324 Perl_closedir(DIR *dd)
10325 {
10326     int sts;
10327 
10328     sts = lib$find_file_end(&dd->context);
10329     Safefree(dd->pattern);
10330 #if defined(USE_ITHREADS)
10331     MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10332     Safefree(dd->mutex);
10333 #endif
10334     Safefree(dd);
10335 }
10336 /*}}}*/
10337 
10338 /*
10339  *  Collect all the version numbers for the current file.
10340  */
10341 static void
10342 collectversions(pTHX_ DIR *dd)
10343 {
10344     struct dsc$descriptor_s	pat;
10345     struct dsc$descriptor_s	res;
10346     struct dirent *e;
10347     char *p, *text, *buff;
10348     int i;
10349     unsigned long context, tmpsts;
10350 
10351     /* Convenient shorthand. */
10352     e = &dd->entry;
10353 
10354     /* Add the version wildcard, ignoring the "*.*" put on before */
10355     i = strlen(dd->pattern);
10356     Newx(text,i + e->d_namlen + 3,char);
10357     strcpy(text, dd->pattern);
10358     sprintf(&text[i - 3], "%s;*", e->d_name);
10359 
10360     /* Set up the pattern descriptor. */
10361     pat.dsc$a_pointer = text;
10362     pat.dsc$w_length = i + e->d_namlen - 1;
10363     pat.dsc$b_dtype = DSC$K_DTYPE_T;
10364     pat.dsc$b_class = DSC$K_CLASS_S;
10365 
10366     /* Set up result descriptor. */
10367     Newx(buff, VMS_MAXRSS, char);
10368     res.dsc$a_pointer = buff;
10369     res.dsc$w_length = VMS_MAXRSS - 1;
10370     res.dsc$b_dtype = DSC$K_DTYPE_T;
10371     res.dsc$b_class = DSC$K_CLASS_S;
10372 
10373     /* Read files, collecting versions. */
10374     for (context = 0, e->vms_verscount = 0;
10375          e->vms_verscount < VERSIZE(e);
10376          e->vms_verscount++) {
10377 	unsigned long rsts;
10378 	unsigned long flags = 0;
10379 
10380 #ifdef VMS_LONGNAME_SUPPORT
10381 	flags = LIB$M_FIL_LONG_NAMES;
10382 #endif
10383 	tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10384 	if (tmpsts == RMS$_NMF || context == 0) break;
10385 	_ckvmssts(tmpsts);
10386 	buff[VMS_MAXRSS - 1] = '\0';
10387 	if ((p = strchr(buff, ';')))
10388 	    e->vms_versions[e->vms_verscount] = atoi(p + 1);
10389 	else
10390 	    e->vms_versions[e->vms_verscount] = -1;
10391     }
10392 
10393     _ckvmssts(lib$find_file_end(&context));
10394     Safefree(text);
10395     Safefree(buff);
10396 
10397 }  /* end of collectversions() */
10398 
10399 /*
10400  *  Read the next entry from the directory.
10401  */
10402 /*{{{ struct dirent *readdir(DIR *dd)*/
10403 struct dirent *
10404 Perl_readdir(pTHX_ DIR *dd)
10405 {
10406     struct dsc$descriptor_s	res;
10407     char *p, *buff;
10408     unsigned long int tmpsts;
10409     unsigned long rsts;
10410     unsigned long flags = 0;
10411     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10412     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10413 
10414     /* Set up result descriptor, and get next file. */
10415     Newx(buff, VMS_MAXRSS, char);
10416     res.dsc$a_pointer = buff;
10417     res.dsc$w_length = VMS_MAXRSS - 1;
10418     res.dsc$b_dtype = DSC$K_DTYPE_T;
10419     res.dsc$b_class = DSC$K_CLASS_S;
10420 
10421 #ifdef VMS_LONGNAME_SUPPORT
10422     flags = LIB$M_FIL_LONG_NAMES;
10423 #endif
10424 
10425     tmpsts = lib$find_file
10426 	(&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10427     if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL;  /* None left. */
10428     if (!(tmpsts & 1)) {
10429       set_vaxc_errno(tmpsts);
10430       switch (tmpsts) {
10431         case RMS$_PRV:
10432           set_errno(EACCES); break;
10433         case RMS$_DEV:
10434           set_errno(ENODEV); break;
10435         case RMS$_DIR:
10436           set_errno(ENOTDIR); break;
10437         case RMS$_FNF: case RMS$_DNF:
10438           set_errno(ENOENT); break;
10439         default:
10440           set_errno(EVMSERR);
10441       }
10442       Safefree(buff);
10443       return NULL;
10444     }
10445     dd->count++;
10446     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10447     buff[res.dsc$w_length] = '\0';
10448     p = buff + res.dsc$w_length;
10449     while (--p >= buff) if (!isspace(*p)) break;
10450     *p = '\0';
10451     if (!decc_efs_case_preserve) {
10452       for (p = buff; *p; p++) *p = _tolower(*p);
10453     }
10454 
10455     /* Skip any directory component and just copy the name. */
10456     sts = vms_split_path
10457        (buff,
10458 	&v_spec,
10459 	&v_len,
10460 	&r_spec,
10461 	&r_len,
10462 	&d_spec,
10463 	&d_len,
10464 	&n_spec,
10465 	&n_len,
10466 	&e_spec,
10467 	&e_len,
10468 	&vs_spec,
10469 	&vs_len);
10470 
10471     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10472 
10473         /* In Unix report mode, remove the ".dir;1" from the name */
10474         /* if it is a real directory. */
10475         if (decc_filename_unix_report || decc_efs_charset) {
10476             if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10477                 Stat_t statbuf;
10478                 int ret_sts;
10479 
10480                 ret_sts = flex_lstat(buff, &statbuf);
10481                 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10482                     e_len = 0;
10483                     e_spec[0] = 0;
10484                 }
10485             }
10486         }
10487 
10488         /* Drop NULL extensions on UNIX file specification */
10489 	if ((e_len == 1) && decc_readdir_dropdotnotype) {
10490 	    e_len = 0;
10491 	    e_spec[0] = '\0';
10492         }
10493     }
10494 
10495     strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10496     dd->entry.d_name[n_len + e_len] = '\0';
10497     dd->entry.d_namlen = strlen(dd->entry.d_name);
10498 
10499     /* Convert the filename to UNIX format if needed */
10500     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10501 
10502 	/* Translate the encoded characters. */
10503 	/* Fixme: Unicode handling could result in embedded 0 characters */
10504 	if (strchr(dd->entry.d_name, '^') != NULL) {
10505 	    char new_name[256];
10506 	    char * q;
10507 	    p = dd->entry.d_name;
10508 	    q = new_name;
10509 	    while (*p != 0) {
10510 		int inchars_read, outchars_added;
10511 		inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10512 		p += inchars_read;
10513 		q += outchars_added;
10514 		/* fix-me */
10515 		/* if outchars_added > 1, then this is a wide file specification */
10516 		/* Wide file specifications need to be passed in Perl */
10517 		/* counted strings apparently with a Unicode flag */
10518 	    }
10519 	    *q = 0;
10520 	    strcpy(dd->entry.d_name, new_name);
10521 	    dd->entry.d_namlen = strlen(dd->entry.d_name);
10522 	}
10523     }
10524 
10525     dd->entry.vms_verscount = 0;
10526     if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10527     Safefree(buff);
10528     return &dd->entry;
10529 
10530 }  /* end of readdir() */
10531 /*}}}*/
10532 
10533 /*
10534  *  Read the next entry from the directory -- thread-safe version.
10535  */
10536 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10537 int
10538 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10539 {
10540     int retval;
10541 
10542     MUTEX_LOCK( (perl_mutex *) dd->mutex );
10543 
10544     entry = readdir(dd);
10545     *result = entry;
10546     retval = ( *result == NULL ? errno : 0 );
10547 
10548     MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10549 
10550     return retval;
10551 
10552 }  /* end of readdir_r() */
10553 /*}}}*/
10554 
10555 /*
10556  *  Return something that can be used in a seekdir later.
10557  */
10558 /*{{{ long telldir(DIR *dd)*/
10559 long
10560 Perl_telldir(DIR *dd)
10561 {
10562     return dd->count;
10563 }
10564 /*}}}*/
10565 
10566 /*
10567  *  Return to a spot where we used to be.  Brute force.
10568  */
10569 /*{{{ void seekdir(DIR *dd,long count)*/
10570 void
10571 Perl_seekdir(pTHX_ DIR *dd, long count)
10572 {
10573     int old_flags;
10574 
10575     /* If we haven't done anything yet... */
10576     if (dd->count == 0)
10577 	return;
10578 
10579     /* Remember some state, and clear it. */
10580     old_flags = dd->flags;
10581     dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10582     _ckvmssts(lib$find_file_end(&dd->context));
10583     dd->context = 0;
10584 
10585     /* The increment is in readdir(). */
10586     for (dd->count = 0; dd->count < count; )
10587 	readdir(dd);
10588 
10589     dd->flags = old_flags;
10590 
10591 }  /* end of seekdir() */
10592 /*}}}*/
10593 
10594 /* VMS subprocess management
10595  *
10596  * my_vfork() - just a vfork(), after setting a flag to record that
10597  * the current script is trying a Unix-style fork/exec.
10598  *
10599  * vms_do_aexec() and vms_do_exec() are called in response to the
10600  * perl 'exec' function.  If this follows a vfork call, then they
10601  * call out the regular perl routines in doio.c which do an
10602  * execvp (for those who really want to try this under VMS).
10603  * Otherwise, they do exactly what the perl docs say exec should
10604  * do - terminate the current script and invoke a new command
10605  * (See below for notes on command syntax.)
10606  *
10607  * do_aspawn() and do_spawn() implement the VMS side of the perl
10608  * 'system' function.
10609  *
10610  * Note on command arguments to perl 'exec' and 'system': When handled
10611  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10612  * are concatenated to form a DCL command string.  If the first non-numeric
10613  * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10614  * the command string is handed off to DCL directly.  Otherwise,
10615  * the first token of the command is taken as the filespec of an image
10616  * to run.  The filespec is expanded using a default type of '.EXE' and
10617  * the process defaults for device, directory, etc., and if found, the resultant
10618  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10619  * the command string as parameters.  This is perhaps a bit complicated,
10620  * but I hope it will form a happy medium between what VMS folks expect
10621  * from lib$spawn and what Unix folks expect from exec.
10622  */
10623 
10624 static int vfork_called;
10625 
10626 /*{{{int my_vfork()*/
10627 int
10628 my_vfork()
10629 {
10630   vfork_called++;
10631   return vfork();
10632 }
10633 /*}}}*/
10634 
10635 
10636 static void
10637 vms_execfree(struct dsc$descriptor_s *vmscmd)
10638 {
10639   if (vmscmd) {
10640       if (vmscmd->dsc$a_pointer) {
10641           PerlMem_free(vmscmd->dsc$a_pointer);
10642       }
10643       PerlMem_free(vmscmd);
10644   }
10645 }
10646 
10647 static char *
10648 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10649 {
10650   char *junk, *tmps = NULL;
10651   register size_t cmdlen = 0;
10652   size_t rlen;
10653   register SV **idx;
10654   STRLEN n_a;
10655 
10656   idx = mark;
10657   if (really) {
10658     tmps = SvPV(really,rlen);
10659     if (*tmps) {
10660       cmdlen += rlen + 1;
10661       idx++;
10662     }
10663   }
10664 
10665   for (idx++; idx <= sp; idx++) {
10666     if (*idx) {
10667       junk = SvPVx(*idx,rlen);
10668       cmdlen += rlen ? rlen + 1 : 0;
10669     }
10670   }
10671   Newx(PL_Cmd, cmdlen+1, char);
10672 
10673   if (tmps && *tmps) {
10674     strcpy(PL_Cmd,tmps);
10675     mark++;
10676   }
10677   else *PL_Cmd = '\0';
10678   while (++mark <= sp) {
10679     if (*mark) {
10680       char *s = SvPVx(*mark,n_a);
10681       if (!*s) continue;
10682       if (*PL_Cmd) strcat(PL_Cmd," ");
10683       strcat(PL_Cmd,s);
10684     }
10685   }
10686   return PL_Cmd;
10687 
10688 }  /* end of setup_argstr() */
10689 
10690 
10691 static unsigned long int
10692 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10693                    struct dsc$descriptor_s **pvmscmd)
10694 {
10695   char * vmsspec;
10696   char * resspec;
10697   char image_name[NAM$C_MAXRSS+1];
10698   char image_argv[NAM$C_MAXRSS+1];
10699   $DESCRIPTOR(defdsc,".EXE");
10700   $DESCRIPTOR(defdsc2,".");
10701   struct dsc$descriptor_s resdsc;
10702   struct dsc$descriptor_s *vmscmd;
10703   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10704   unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10705   register char *s, *rest, *cp, *wordbreak;
10706   char * cmd;
10707   int cmdlen;
10708   register int isdcl;
10709 
10710   vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10711   if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10712 
10713   /* vmsspec is a DCL command buffer, not just a filename */
10714   vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10715   if (vmsspec == NULL)
10716       _ckvmssts_noperl(SS$_INSFMEM);
10717 
10718   resspec = PerlMem_malloc(VMS_MAXRSS);
10719   if (resspec == NULL)
10720       _ckvmssts_noperl(SS$_INSFMEM);
10721 
10722   /* Make a copy for modification */
10723   cmdlen = strlen(incmd);
10724   cmd = PerlMem_malloc(cmdlen+1);
10725   if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10726   strncpy(cmd, incmd, cmdlen);
10727   cmd[cmdlen] = 0;
10728   image_name[0] = 0;
10729   image_argv[0] = 0;
10730 
10731   resdsc.dsc$a_pointer = resspec;
10732   resdsc.dsc$b_dtype  = DSC$K_DTYPE_T;
10733   resdsc.dsc$b_class  = DSC$K_CLASS_S;
10734   resdsc.dsc$w_length = VMS_MAXRSS - 1;
10735 
10736   vmscmd->dsc$a_pointer = NULL;
10737   vmscmd->dsc$b_dtype  = DSC$K_DTYPE_T;
10738   vmscmd->dsc$b_class  = DSC$K_CLASS_S;
10739   vmscmd->dsc$w_length = 0;
10740   if (pvmscmd) *pvmscmd = vmscmd;
10741 
10742   if (suggest_quote) *suggest_quote = 0;
10743 
10744   if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10745     PerlMem_free(cmd);
10746     PerlMem_free(vmsspec);
10747     PerlMem_free(resspec);
10748     return CLI$_BUFOVF;                /* continuation lines currently unsupported */
10749   }
10750 
10751   s = cmd;
10752 
10753   while (*s && isspace(*s)) s++;
10754 
10755   if (*s == '@' || *s == '$') {
10756     vmsspec[0] = *s;  rest = s + 1;
10757     for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10758   }
10759   else { cp = vmsspec; rest = s; }
10760   if (*rest == '.' || *rest == '/') {
10761     char *cp2;
10762     for (cp2 = resspec;
10763          *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10764          rest++, cp2++) *cp2 = *rest;
10765     *cp2 = '\0';
10766     if (int_tovmsspec(resspec, cp, 0, NULL)) {
10767       s = vmsspec;
10768 
10769       /* When a UNIX spec with no file type is translated to VMS, */
10770       /* A trailing '.' is appended under ODS-5 rules.            */
10771       /* Here we do not want that trailing "." as it prevents     */
10772       /* Looking for a implied ".exe" type. */
10773       if (decc_efs_charset) {
10774           int i;
10775           i = strlen(vmsspec);
10776           if (vmsspec[i-1] == '.') {
10777               vmsspec[i-1] = '\0';
10778           }
10779       }
10780 
10781       if (*rest) {
10782         for (cp2 = vmsspec + strlen(vmsspec);
10783              *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10784              rest++, cp2++) *cp2 = *rest;
10785         *cp2 = '\0';
10786       }
10787     }
10788   }
10789   /* Intuit whether verb (first word of cmd) is a DCL command:
10790    *   - if first nonspace char is '@', it's a DCL indirection
10791    * otherwise
10792    *   - if verb contains a filespec separator, it's not a DCL command
10793    *   - if it doesn't, caller tells us whether to default to a DCL
10794    *     command, or to a local image unless told it's DCL (by leading '$')
10795    */
10796   if (*s == '@') {
10797       isdcl = 1;
10798       if (suggest_quote) *suggest_quote = 1;
10799   } else {
10800     register char *filespec = strpbrk(s,":<[.;");
10801     rest = wordbreak = strpbrk(s," \"\t/");
10802     if (!wordbreak) wordbreak = s + strlen(s);
10803     if (*s == '$') check_img = 0;
10804     if (filespec && (filespec < wordbreak)) isdcl = 0;
10805     else isdcl = !check_img;
10806   }
10807 
10808   if (!isdcl) {
10809     int rsts;
10810     imgdsc.dsc$a_pointer = s;
10811     imgdsc.dsc$w_length = wordbreak - s;
10812     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10813     if (!(retsts&1)) {
10814         _ckvmssts_noperl(lib$find_file_end(&cxt));
10815         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10816       if (!(retsts & 1) && *s == '$') {
10817         _ckvmssts_noperl(lib$find_file_end(&cxt));
10818 	imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10819 	retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10820 	if (!(retsts&1)) {
10821 	  _ckvmssts_noperl(lib$find_file_end(&cxt));
10822           retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10823         }
10824       }
10825     }
10826     _ckvmssts_noperl(lib$find_file_end(&cxt));
10827 
10828     if (retsts & 1) {
10829       FILE *fp;
10830       s = resspec;
10831       while (*s && !isspace(*s)) s++;
10832       *s = '\0';
10833 
10834       /* check that it's really not DCL with no file extension */
10835       fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10836       if (fp) {
10837         char b[256] = {0,0,0,0};
10838         read(fileno(fp), b, 256);
10839         isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10840 	if (isdcl) {
10841 	  int shebang_len;
10842 
10843 	  /* Check for script */
10844 	  shebang_len = 0;
10845 	  if ((b[0] == '#') && (b[1] == '!'))
10846 	     shebang_len = 2;
10847 #ifdef ALTERNATE_SHEBANG
10848 	  else {
10849 	    shebang_len = strlen(ALTERNATE_SHEBANG);
10850 	    if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10851 	      char * perlstr;
10852 		perlstr = strstr("perl",b);
10853 		if (perlstr == NULL)
10854 		  shebang_len = 0;
10855 	    }
10856 	    else
10857 	      shebang_len = 0;
10858 	  }
10859 #endif
10860 
10861 	  if (shebang_len > 0) {
10862 	  int i;
10863 	  int j;
10864 	  char tmpspec[NAM$C_MAXRSS + 1];
10865 
10866 	    i = shebang_len;
10867 	     /* Image is following after white space */
10868 	    /*--------------------------------------*/
10869 	    while (isprint(b[i]) && isspace(b[i]))
10870 		i++;
10871 
10872 	    j = 0;
10873 	    while (isprint(b[i]) && !isspace(b[i])) {
10874 		tmpspec[j++] = b[i++];
10875 		if (j >= NAM$C_MAXRSS)
10876 		   break;
10877 	    }
10878 	    tmpspec[j] = '\0';
10879 
10880 	     /* There may be some default parameters to the image */
10881 	    /*---------------------------------------------------*/
10882 	    j = 0;
10883 	    while (isprint(b[i])) {
10884 		image_argv[j++] = b[i++];
10885 		if (j >= NAM$C_MAXRSS)
10886 		   break;
10887 	    }
10888 	    while ((j > 0) && !isprint(image_argv[j-1]))
10889 		j--;
10890 	    image_argv[j] = 0;
10891 
10892 	    /* It will need to be converted to VMS format and validated */
10893 	    if (tmpspec[0] != '\0') {
10894 	      char * iname;
10895 
10896 	       /* Try to find the exact program requested to be run */
10897 	      /*---------------------------------------------------*/
10898 	      iname = int_rmsexpand
10899 		 (tmpspec, image_name, ".exe",
10900 		  PERL_RMSEXPAND_M_VMS, NULL, NULL);
10901 	      if (iname != NULL) {
10902 		if (cando_by_name_int
10903 			(S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10904 		  /* MCR prefix needed */
10905 		  isdcl = 0;
10906 		}
10907 		else {
10908 		   /* Try again with a null type */
10909 		  /*----------------------------*/
10910 		  iname = int_rmsexpand
10911 		    (tmpspec, image_name, ".",
10912 		     PERL_RMSEXPAND_M_VMS, NULL, NULL);
10913 		  if (iname != NULL) {
10914 		    if (cando_by_name_int
10915 			 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10916 		      /* MCR prefix needed */
10917 		      isdcl = 0;
10918 		    }
10919 		  }
10920 		}
10921 
10922 		 /* Did we find the image to run the script? */
10923 		/*------------------------------------------*/
10924 		if (isdcl) {
10925 		  char *tchr;
10926 
10927 		   /* Assume DCL or foreign command exists */
10928 		  /*--------------------------------------*/
10929 		  tchr = strrchr(tmpspec, '/');
10930 		  if (tchr != NULL) {
10931 		    tchr++;
10932 		  }
10933 		  else {
10934 		    tchr = tmpspec;
10935 		  }
10936 		  strcpy(image_name, tchr);
10937 		}
10938 	      }
10939 	    }
10940 	  }
10941 	}
10942         fclose(fp);
10943       }
10944       if (check_img && isdcl) {
10945           PerlMem_free(cmd);
10946           PerlMem_free(resspec);
10947           PerlMem_free(vmsspec);
10948           return RMS$_FNF;
10949       }
10950 
10951       if (cando_by_name(S_IXUSR,0,resspec)) {
10952         vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10953 	if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10954         if (!isdcl) {
10955             strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10956 	    if (image_name[0] != 0) {
10957 		strcat(vmscmd->dsc$a_pointer, image_name);
10958 		strcat(vmscmd->dsc$a_pointer, " ");
10959 	    }
10960 	} else if (image_name[0] != 0) {
10961 	    strcpy(vmscmd->dsc$a_pointer, image_name);
10962 	    strcat(vmscmd->dsc$a_pointer, " ");
10963         } else {
10964             strcpy(vmscmd->dsc$a_pointer,"@");
10965         }
10966         if (suggest_quote) *suggest_quote = 1;
10967 
10968 	/* If there is an image name, use original command */
10969 	if (image_name[0] == 0)
10970 	    strcat(vmscmd->dsc$a_pointer,resspec);
10971 	else {
10972 	    rest = cmd;
10973 	    while (*rest && isspace(*rest)) rest++;
10974 	}
10975 
10976 	if (image_argv[0] != 0) {
10977 	  strcat(vmscmd->dsc$a_pointer,image_argv);
10978 	  strcat(vmscmd->dsc$a_pointer, " ");
10979 	}
10980         if (rest) {
10981 	   int rest_len;
10982 	   int vmscmd_len;
10983 
10984 	   rest_len = strlen(rest);
10985 	   vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10986 	   if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10987 	      strcat(vmscmd->dsc$a_pointer,rest);
10988 	   else
10989 	     retsts = CLI$_BUFOVF;
10990 	}
10991         vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10992         PerlMem_free(cmd);
10993         PerlMem_free(vmsspec);
10994         PerlMem_free(resspec);
10995         return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10996       }
10997       else
10998 	retsts = RMS$_PRV;
10999     }
11000   }
11001   /* It's either a DCL command or we couldn't find a suitable image */
11002   vmscmd->dsc$w_length = strlen(cmd);
11003 
11004   vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
11005   strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
11006   vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
11007 
11008   PerlMem_free(cmd);
11009   PerlMem_free(resspec);
11010   PerlMem_free(vmsspec);
11011 
11012   /* check if it's a symbol (for quoting purposes) */
11013   if (suggest_quote && !*suggest_quote) {
11014     int iss;
11015     char equiv[LNM$C_NAMLENGTH];
11016     struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11017     eqvdsc.dsc$a_pointer = equiv;
11018 
11019     iss = lib$get_symbol(vmscmd,&eqvdsc);
11020     if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
11021   }
11022   if (!(retsts & 1)) {
11023     /* just hand off status values likely to be due to user error */
11024     if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
11025         retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
11026        (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
11027     else { _ckvmssts_noperl(retsts); }
11028   }
11029 
11030   return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
11031 
11032 }  /* end of setup_cmddsc() */
11033 
11034 
11035 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
11036 bool
11037 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
11038 {
11039 bool exec_sts;
11040 char * cmd;
11041 
11042   if (sp > mark) {
11043     if (vfork_called) {           /* this follows a vfork - act Unixish */
11044       vfork_called--;
11045       if (vfork_called < 0) {
11046         Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11047         vfork_called = 0;
11048       }
11049       else return do_aexec(really,mark,sp);
11050     }
11051                                            /* no vfork - act VMSish */
11052     cmd = setup_argstr(aTHX_ really,mark,sp);
11053     exec_sts = vms_do_exec(cmd);
11054     Safefree(cmd);  /* Clean up from setup_argstr() */
11055     return exec_sts;
11056   }
11057 
11058   return FALSE;
11059 }  /* end of vms_do_aexec() */
11060 /*}}}*/
11061 
11062 /* {{{bool vms_do_exec(char *cmd) */
11063 bool
11064 Perl_vms_do_exec(pTHX_ const char *cmd)
11065 {
11066   struct dsc$descriptor_s *vmscmd;
11067 
11068   if (vfork_called) {             /* this follows a vfork - act Unixish */
11069     vfork_called--;
11070     if (vfork_called < 0) {
11071       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11072       vfork_called = 0;
11073     }
11074     else return do_exec(cmd);
11075   }
11076 
11077   {                               /* no vfork - act VMSish */
11078     unsigned long int retsts;
11079 
11080     TAINT_ENV();
11081     TAINT_PROPER("exec");
11082     if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11083       retsts = lib$do_command(vmscmd);
11084 
11085     switch (retsts) {
11086       case RMS$_FNF: case RMS$_DNF:
11087         set_errno(ENOENT); break;
11088       case RMS$_DIR:
11089         set_errno(ENOTDIR); break;
11090       case RMS$_DEV:
11091         set_errno(ENODEV); break;
11092       case RMS$_PRV:
11093         set_errno(EACCES); break;
11094       case RMS$_SYN:
11095         set_errno(EINVAL); break;
11096       case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11097         set_errno(E2BIG); break;
11098       case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11099         _ckvmssts_noperl(retsts); /* fall through */
11100       default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11101         set_errno(EVMSERR);
11102     }
11103     set_vaxc_errno(retsts);
11104     if (ckWARN(WARN_EXEC)) {
11105       Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11106              vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11107     }
11108     vms_execfree(vmscmd);
11109   }
11110 
11111   return FALSE;
11112 
11113 }  /* end of vms_do_exec() */
11114 /*}}}*/
11115 
11116 int do_spawn2(pTHX_ const char *, int);
11117 
11118 int
11119 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11120 {
11121 unsigned long int sts;
11122 char * cmd;
11123 int flags = 0;
11124 
11125   if (sp > mark) {
11126 
11127     /* We'll copy the (undocumented?) Win32 behavior and allow a
11128      * numeric first argument.  But the only value we'll support
11129      * through do_aspawn is a value of 1, which means spawn without
11130      * waiting for completion -- other values are ignored.
11131      */
11132     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11133 	++mark;
11134 	flags = SvIVx(*mark);
11135     }
11136 
11137     if (flags && flags == 1)     /* the Win32 P_NOWAIT value */
11138         flags = CLI$M_NOWAIT;
11139     else
11140         flags = 0;
11141 
11142     cmd = setup_argstr(aTHX_ really, mark, sp);
11143     sts = do_spawn2(aTHX_ cmd, flags);
11144     /* pp_sys will clean up cmd */
11145     return sts;
11146   }
11147   return SS$_ABORT;
11148 }  /* end of do_aspawn() */
11149 /*}}}*/
11150 
11151 
11152 /* {{{int do_spawn(char* cmd) */
11153 int
11154 Perl_do_spawn(pTHX_ char* cmd)
11155 {
11156     PERL_ARGS_ASSERT_DO_SPAWN;
11157 
11158     return do_spawn2(aTHX_ cmd, 0);
11159 }
11160 /*}}}*/
11161 
11162 /* {{{int do_spawn_nowait(char* cmd) */
11163 int
11164 Perl_do_spawn_nowait(pTHX_ char* cmd)
11165 {
11166     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11167 
11168     return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11169 }
11170 /*}}}*/
11171 
11172 /* {{{int do_spawn2(char *cmd) */
11173 int
11174 do_spawn2(pTHX_ const char *cmd, int flags)
11175 {
11176   unsigned long int sts, substs;
11177 
11178   /* The caller of this routine expects to Safefree(PL_Cmd) */
11179   Newx(PL_Cmd,10,char);
11180 
11181   TAINT_ENV();
11182   TAINT_PROPER("spawn");
11183   if (!cmd || !*cmd) {
11184     sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11185     if (!(sts & 1)) {
11186       switch (sts) {
11187         case RMS$_FNF:  case RMS$_DNF:
11188           set_errno(ENOENT); break;
11189         case RMS$_DIR:
11190           set_errno(ENOTDIR); break;
11191         case RMS$_DEV:
11192           set_errno(ENODEV); break;
11193         case RMS$_PRV:
11194           set_errno(EACCES); break;
11195         case RMS$_SYN:
11196           set_errno(EINVAL); break;
11197         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11198           set_errno(E2BIG); break;
11199         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11200           _ckvmssts_noperl(sts); /* fall through */
11201         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11202           set_errno(EVMSERR);
11203       }
11204       set_vaxc_errno(sts);
11205       if (ckWARN(WARN_EXEC)) {
11206         Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11207 		    Strerror(errno));
11208       }
11209     }
11210     sts = substs;
11211   }
11212   else {
11213     char mode[3];
11214     PerlIO * fp;
11215     if (flags & CLI$M_NOWAIT)
11216         strcpy(mode, "n");
11217     else
11218         strcpy(mode, "nW");
11219 
11220     fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11221     if (fp != NULL)
11222       my_pclose(fp);
11223     /* sts will be the pid in the nowait case */
11224   }
11225   return sts;
11226 }  /* end of do_spawn2() */
11227 /*}}}*/
11228 
11229 
11230 static unsigned int *sockflags, sockflagsize;
11231 
11232 /*
11233  * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11234  * routines found in some versions of the CRTL can't deal with sockets.
11235  * We don't shim the other file open routines since a socket isn't
11236  * likely to be opened by a name.
11237  */
11238 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11239 FILE *my_fdopen(int fd, const char *mode)
11240 {
11241   FILE *fp = fdopen(fd, mode);
11242 
11243   if (fp) {
11244     unsigned int fdoff = fd / sizeof(unsigned int);
11245     Stat_t sbuf; /* native stat; we don't need flex_stat */
11246     if (!sockflagsize || fdoff > sockflagsize) {
11247       if (sockflags) Renew(     sockflags,fdoff+2,unsigned int);
11248       else           Newx  (sockflags,fdoff+2,unsigned int);
11249       memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11250       sockflagsize = fdoff + 2;
11251     }
11252     if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11253       sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11254   }
11255   return fp;
11256 
11257 }
11258 /*}}}*/
11259 
11260 
11261 /*
11262  * Clear the corresponding bit when the (possibly) socket stream is closed.
11263  * There still a small hole: we miss an implicit close which might occur
11264  * via freopen().  >> Todo
11265  */
11266 /*{{{ int my_fclose(FILE *fp)*/
11267 int my_fclose(FILE *fp) {
11268   if (fp) {
11269     unsigned int fd = fileno(fp);
11270     unsigned int fdoff = fd / sizeof(unsigned int);
11271 
11272     if (sockflagsize && fdoff < sockflagsize)
11273       sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11274   }
11275   return fclose(fp);
11276 }
11277 /*}}}*/
11278 
11279 
11280 /*
11281  * A simple fwrite replacement which outputs itmsz*nitm chars without
11282  * introducing record boundaries every itmsz chars.
11283  * We are using fputs, which depends on a terminating null.  We may
11284  * well be writing binary data, so we need to accommodate not only
11285  * data with nulls sprinkled in the middle but also data with no null
11286  * byte at the end.
11287  */
11288 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11289 int
11290 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11291 {
11292   register char *cp, *end, *cpd, *data;
11293   register unsigned int fd = fileno(dest);
11294   register unsigned int fdoff = fd / sizeof(unsigned int);
11295   int retval;
11296   int bufsize = itmsz * nitm + 1;
11297 
11298   if (fdoff < sockflagsize &&
11299       (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11300     if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11301     return nitm;
11302   }
11303 
11304   _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11305   memcpy( data, src, itmsz*nitm );
11306   data[itmsz*nitm] = '\0';
11307 
11308   end = data + itmsz * nitm;
11309   retval = (int) nitm; /* on success return # items written */
11310 
11311   cpd = data;
11312   while (cpd <= end) {
11313     for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11314     if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11315     if (cp < end)
11316       if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11317     cpd = cp + 1;
11318   }
11319 
11320   if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11321   return retval;
11322 
11323 }  /* end of my_fwrite() */
11324 /*}}}*/
11325 
11326 /*{{{ int my_flush(FILE *fp)*/
11327 int
11328 Perl_my_flush(pTHX_ FILE *fp)
11329 {
11330     int res;
11331     if ((res = fflush(fp)) == 0 && fp) {
11332 #ifdef VMS_DO_SOCKETS
11333 	Stat_t s;
11334 	if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11335 #endif
11336 	    res = fsync(fileno(fp));
11337     }
11338 /*
11339  * If the flush succeeded but set end-of-file, we need to clear
11340  * the error because our caller may check ferror().  BTW, this
11341  * probably means we just flushed an empty file.
11342  */
11343     if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11344 
11345     return res;
11346 }
11347 /*}}}*/
11348 
11349 /* fgetname() is not returning the correct file specifications when
11350  * decc_filename_unix_report mode is active.  So we have to have it
11351  * aways return filenames in VMS mode and convert it ourselves.
11352  */
11353 
11354 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11355 char *
11356 Perl_my_fgetname(FILE *fp, char * buf) {
11357     char * retname;
11358     char * vms_name;
11359 
11360     retname = fgetname(fp, buf, 1);
11361 
11362     /* If we are in VMS mode, then we are done */
11363     if (!decc_filename_unix_report || (retname == NULL)) {
11364        return retname;
11365     }
11366 
11367     /* Convert this to Unix format */
11368     vms_name = PerlMem_malloc(VMS_MAXRSS + 1);
11369     strcpy(vms_name, retname);
11370     retname = int_tounixspec(vms_name, buf, NULL);
11371     PerlMem_free(vms_name);
11372 
11373     return retname;
11374 }
11375 /*}}}*/
11376 
11377 /*
11378  * Here are replacements for the following Unix routines in the VMS environment:
11379  *      getpwuid    Get information for a particular UIC or UID
11380  *      getpwnam    Get information for a named user
11381  *      getpwent    Get information for each user in the rights database
11382  *      setpwent    Reset search to the start of the rights database
11383  *      endpwent    Finish searching for users in the rights database
11384  *
11385  * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11386  * (defined in pwd.h), which contains the following fields:-
11387  *      struct passwd {
11388  *              char        *pw_name;    Username (in lower case)
11389  *              char        *pw_passwd;  Hashed password
11390  *              unsigned int pw_uid;     UIC
11391  *              unsigned int pw_gid;     UIC group  number
11392  *              char        *pw_unixdir; Default device/directory (VMS-style)
11393  *              char        *pw_gecos;   Owner name
11394  *              char        *pw_dir;     Default device/directory (Unix-style)
11395  *              char        *pw_shell;   Default CLI name (eg. DCL)
11396  *      };
11397  * If the specified user does not exist, getpwuid and getpwnam return NULL.
11398  *
11399  * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11400  * not the UIC member number (eg. what's returned by getuid()),
11401  * getpwuid() can accept either as input (if uid is specified, the caller's
11402  * UIC group is used), though it won't recognise gid=0.
11403  *
11404  * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11405  * information about other users in your group or in other groups, respectively.
11406  * If the required privilege is not available, then these routines fill only
11407  * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11408  * string).
11409  *
11410  * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11411  */
11412 
11413 /* sizes of various UAF record fields */
11414 #define UAI$S_USERNAME 12
11415 #define UAI$S_IDENT    31
11416 #define UAI$S_OWNER    31
11417 #define UAI$S_DEFDEV   31
11418 #define UAI$S_DEFDIR   63
11419 #define UAI$S_DEFCLI   31
11420 #define UAI$S_PWD       8
11421 
11422 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT  && \
11423                         (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11424                         (uic).uic$v_group  != UIC$K_WILD_GROUP)
11425 
11426 static char __empty[]= "";
11427 static struct passwd __passwd_empty=
11428     {(char *) __empty, (char *) __empty, 0, 0,
11429      (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11430 static int contxt= 0;
11431 static struct passwd __pwdcache;
11432 static char __pw_namecache[UAI$S_IDENT+1];
11433 
11434 /*
11435  * This routine does most of the work extracting the user information.
11436  */
11437 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11438 {
11439     static struct {
11440         unsigned char length;
11441         char pw_gecos[UAI$S_OWNER+1];
11442     } owner;
11443     static union uicdef uic;
11444     static struct {
11445         unsigned char length;
11446         char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11447     } defdev;
11448     static struct {
11449         unsigned char length;
11450         char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11451     } defdir;
11452     static struct {
11453         unsigned char length;
11454         char pw_shell[UAI$S_DEFCLI+1];
11455     } defcli;
11456     static char pw_passwd[UAI$S_PWD+1];
11457 
11458     static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11459     struct dsc$descriptor_s name_desc;
11460     unsigned long int sts;
11461 
11462     static struct itmlst_3 itmlst[]= {
11463         {UAI$S_OWNER+1,    UAI$_OWNER,  &owner,    &lowner},
11464         {sizeof(uic),      UAI$_UIC,    &uic,      &luic},
11465         {UAI$S_DEFDEV+1,   UAI$_DEFDEV, &defdev,   &ldefdev},
11466         {UAI$S_DEFDIR+1,   UAI$_DEFDIR, &defdir,   &ldefdir},
11467         {UAI$S_DEFCLI+1,   UAI$_DEFCLI, &defcli,   &ldefcli},
11468         {UAI$S_PWD,        UAI$_PWD,    pw_passwd, &lpwd},
11469         {0,                0,           NULL,    NULL}};
11470 
11471     name_desc.dsc$w_length=  strlen(name);
11472     name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11473     name_desc.dsc$b_class=   DSC$K_CLASS_S;
11474     name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11475 
11476 /*  Note that sys$getuai returns many fields as counted strings. */
11477     sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11478     if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11479       set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11480     }
11481     else { _ckvmssts(sts); }
11482     if (!(sts & 1)) return 0;  /* out here in case _ckvmssts() doesn't abort */
11483 
11484     if ((int) owner.length  < lowner)  lowner=  (int) owner.length;
11485     if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11486     if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11487     if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11488     memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11489     owner.pw_gecos[lowner]=            '\0';
11490     defdev.pw_dir[ldefdev+ldefdir]= '\0';
11491     defcli.pw_shell[ldefcli]=          '\0';
11492     if (valid_uic(uic)) {
11493         pwd->pw_uid= uic.uic$l_uic;
11494         pwd->pw_gid= uic.uic$v_group;
11495     }
11496     else
11497       Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11498     pwd->pw_passwd=  pw_passwd;
11499     pwd->pw_gecos=   owner.pw_gecos;
11500     pwd->pw_dir=     defdev.pw_dir;
11501     pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11502     pwd->pw_shell=   defcli.pw_shell;
11503     if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11504         int ldir;
11505         ldir= strlen(pwd->pw_unixdir) - 1;
11506         if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11507     }
11508     else
11509         strcpy(pwd->pw_unixdir, pwd->pw_dir);
11510     if (!decc_efs_case_preserve)
11511         __mystrtolower(pwd->pw_unixdir);
11512     return 1;
11513 }
11514 
11515 /*
11516  * Get information for a named user.
11517 */
11518 /*{{{struct passwd *getpwnam(char *name)*/
11519 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11520 {
11521     struct dsc$descriptor_s name_desc;
11522     union uicdef uic;
11523     unsigned long int status, sts;
11524 
11525     __pwdcache = __passwd_empty;
11526     if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11527       /* We still may be able to determine pw_uid and pw_gid */
11528       name_desc.dsc$w_length=  strlen(name);
11529       name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11530       name_desc.dsc$b_class=   DSC$K_CLASS_S;
11531       name_desc.dsc$a_pointer= (char *) name;
11532       if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11533         __pwdcache.pw_uid= uic.uic$l_uic;
11534         __pwdcache.pw_gid= uic.uic$v_group;
11535       }
11536       else {
11537         if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11538           set_vaxc_errno(sts);
11539           set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11540           return NULL;
11541         }
11542         else { _ckvmssts(sts); }
11543       }
11544     }
11545     strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11546     __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11547     __pwdcache.pw_name= __pw_namecache;
11548     return &__pwdcache;
11549 }  /* end of my_getpwnam() */
11550 /*}}}*/
11551 
11552 /*
11553  * Get information for a particular UIC or UID.
11554  * Called by my_getpwent with uid=-1 to list all users.
11555 */
11556 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11557 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11558 {
11559     const $DESCRIPTOR(name_desc,__pw_namecache);
11560     unsigned short lname;
11561     union uicdef uic;
11562     unsigned long int status;
11563 
11564     if (uid == (unsigned int) -1) {
11565       do {
11566         status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11567         if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11568           set_vaxc_errno(status);
11569           set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11570           my_endpwent();
11571           return NULL;
11572         }
11573         else { _ckvmssts(status); }
11574       } while (!valid_uic (uic));
11575     }
11576     else {
11577       uic.uic$l_uic= uid;
11578       if (!uic.uic$v_group)
11579         uic.uic$v_group= PerlProc_getgid();
11580       if (valid_uic(uic))
11581         status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11582       else status = SS$_IVIDENT;
11583       if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11584           status == RMS$_PRV) {
11585         set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11586         return NULL;
11587       }
11588       else { _ckvmssts(status); }
11589     }
11590     __pw_namecache[lname]= '\0';
11591     __mystrtolower(__pw_namecache);
11592 
11593     __pwdcache = __passwd_empty;
11594     __pwdcache.pw_name = __pw_namecache;
11595 
11596 /*  Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11597     The identifier's value is usually the UIC, but it doesn't have to be,
11598     so if we can, we let fillpasswd update this. */
11599     __pwdcache.pw_uid =  uic.uic$l_uic;
11600     __pwdcache.pw_gid =  uic.uic$v_group;
11601 
11602     fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11603     return &__pwdcache;
11604 
11605 }  /* end of my_getpwuid() */
11606 /*}}}*/
11607 
11608 /*
11609  * Get information for next user.
11610 */
11611 /*{{{struct passwd *my_getpwent()*/
11612 struct passwd *Perl_my_getpwent(pTHX)
11613 {
11614     return (my_getpwuid((unsigned int) -1));
11615 }
11616 /*}}}*/
11617 
11618 /*
11619  * Finish searching rights database for users.
11620 */
11621 /*{{{void my_endpwent()*/
11622 void Perl_my_endpwent(pTHX)
11623 {
11624     if (contxt) {
11625       _ckvmssts(sys$finish_rdb(&contxt));
11626       contxt= 0;
11627     }
11628 }
11629 /*}}}*/
11630 
11631 #ifdef HOMEGROWN_POSIX_SIGNALS
11632   /* Signal handling routines, pulled into the core from POSIX.xs.
11633    *
11634    * We need these for threads, so they've been rolled into the core,
11635    * rather than left in POSIX.xs.
11636    *
11637    * (DRS, Oct 23, 1997)
11638    */
11639 
11640   /* sigset_t is atomic under VMS, so these routines are easy */
11641 /*{{{int my_sigemptyset(sigset_t *) */
11642 int my_sigemptyset(sigset_t *set) {
11643     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11644     *set = 0; return 0;
11645 }
11646 /*}}}*/
11647 
11648 
11649 /*{{{int my_sigfillset(sigset_t *)*/
11650 int my_sigfillset(sigset_t *set) {
11651     int i;
11652     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11653     for (i = 0; i < NSIG; i++) *set |= (1 << i);
11654     return 0;
11655 }
11656 /*}}}*/
11657 
11658 
11659 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11660 int my_sigaddset(sigset_t *set, int sig) {
11661     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11662     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11663     *set |= (1 << (sig - 1));
11664     return 0;
11665 }
11666 /*}}}*/
11667 
11668 
11669 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11670 int my_sigdelset(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_sigismember(sigset_t *set, int sig)*/
11680 int my_sigismember(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     return *set & (1 << (sig - 1));
11684 }
11685 /*}}}*/
11686 
11687 
11688 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11689 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11690     sigset_t tempmask;
11691 
11692     /* If set and oset are both null, then things are badly wrong. Bail out. */
11693     if ((oset == NULL) && (set == NULL)) {
11694       set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11695       return -1;
11696     }
11697 
11698     /* If set's null, then we're just handling a fetch. */
11699     if (set == NULL) {
11700         tempmask = sigblock(0);
11701     }
11702     else {
11703       switch (how) {
11704       case SIG_SETMASK:
11705         tempmask = sigsetmask(*set);
11706         break;
11707       case SIG_BLOCK:
11708         tempmask = sigblock(*set);
11709         break;
11710       case SIG_UNBLOCK:
11711         tempmask = sigblock(0);
11712         sigsetmask(*oset & ~tempmask);
11713         break;
11714       default:
11715         set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11716         return -1;
11717       }
11718     }
11719 
11720     /* Did they pass us an oset? If so, stick our holding mask into it */
11721     if (oset)
11722       *oset = tempmask;
11723 
11724     return 0;
11725 }
11726 /*}}}*/
11727 #endif  /* HOMEGROWN_POSIX_SIGNALS */
11728 
11729 
11730 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11731  * my_utime(), and flex_stat(), all of which operate on UTC unless
11732  * VMSISH_TIMES is true.
11733  */
11734 /* method used to handle UTC conversions:
11735  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
11736  */
11737 static int gmtime_emulation_type;
11738 /* number of secs to add to UTC POSIX-style time to get local time */
11739 static long int utc_offset_secs;
11740 
11741 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11742  * in vmsish.h.  #undef them here so we can call the CRTL routines
11743  * directly.
11744  */
11745 #undef gmtime
11746 #undef localtime
11747 #undef time
11748 
11749 
11750 /*
11751  * DEC C previous to 6.0 corrupts the behavior of the /prefix
11752  * qualifier with the extern prefix pragma.  This provisional
11753  * hack circumvents this prefix pragma problem in previous
11754  * precompilers.
11755  */
11756 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11757 #  if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11758 #    pragma __extern_prefix save
11759 #    pragma __extern_prefix ""  /* set to empty to prevent prefixing */
11760 #    define gmtime decc$__utctz_gmtime
11761 #    define localtime decc$__utctz_localtime
11762 #    define time decc$__utc_time
11763 #    pragma __extern_prefix restore
11764 
11765      struct tm *gmtime(), *localtime();
11766 
11767 #  endif
11768 #endif
11769 
11770 
11771 static time_t toutc_dst(time_t loc) {
11772   struct tm *rsltmp;
11773 
11774   if ((rsltmp = localtime(&loc)) == NULL) return -1;
11775   loc -= utc_offset_secs;
11776   if (rsltmp->tm_isdst) loc -= 3600;
11777   return loc;
11778 }
11779 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11780        ((gmtime_emulation_type || my_time(NULL)), \
11781        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11782        ((secs) - utc_offset_secs))))
11783 
11784 static time_t toloc_dst(time_t utc) {
11785   struct tm *rsltmp;
11786 
11787   utc += utc_offset_secs;
11788   if ((rsltmp = localtime(&utc)) == NULL) return -1;
11789   if (rsltmp->tm_isdst) utc += 3600;
11790   return utc;
11791 }
11792 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11793        ((gmtime_emulation_type || my_time(NULL)), \
11794        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11795        ((secs) + utc_offset_secs))))
11796 
11797 #ifndef RTL_USES_UTC
11798 /*
11799 
11800     ucx$tz = "EST5EDT4,M4.1.0,M10.5.0"  typical
11801         DST starts on 1st sun of april      at 02:00  std time
11802             ends on last sun of october     at 02:00  dst time
11803     see the UCX management command reference, SET CONFIG TIMEZONE
11804     for formatting info.
11805 
11806     No, it's not as general as it should be, but then again, NOTHING
11807     will handle UK times in a sensible way.
11808 */
11809 
11810 
11811 /*
11812     parse the DST start/end info:
11813     (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11814 */
11815 
11816 static char *
11817 tz_parse_startend(char *s, struct tm *w, int *past)
11818 {
11819     int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11820     int ly, dozjd, d, m, n, hour, min, sec, j, k;
11821     time_t g;
11822 
11823     if (!s)    return 0;
11824     if (!w) return 0;
11825     if (!past) return 0;
11826 
11827     ly = 0;
11828     if (w->tm_year % 4        == 0) ly = 1;
11829     if (w->tm_year % 100      == 0) ly = 0;
11830     if (w->tm_year+1900 % 400 == 0) ly = 1;
11831     if (ly) dinm[1]++;
11832 
11833     dozjd = isdigit(*s);
11834     if (*s == 'J' || *s == 'j' || dozjd) {
11835         if (!dozjd && !isdigit(*++s)) return 0;
11836         d = *s++ - '0';
11837         if (isdigit(*s)) {
11838             d = d*10 + *s++ - '0';
11839             if (isdigit(*s)) {
11840                 d = d*10 + *s++ - '0';
11841             }
11842         }
11843         if (d == 0) return 0;
11844         if (d > 366) return 0;
11845         d--;
11846         if (!dozjd && d > 58 && ly) d++;  /* after 28 feb */
11847         g = d * 86400;
11848         dozjd = 1;
11849     } else if (*s == 'M' || *s == 'm') {
11850         if (!isdigit(*++s)) return 0;
11851         m = *s++ - '0';
11852         if (isdigit(*s)) m = 10*m + *s++ - '0';
11853         if (*s != '.') return 0;
11854         if (!isdigit(*++s)) return 0;
11855         n = *s++ - '0';
11856         if (n < 1 || n > 5) return 0;
11857         if (*s != '.') return 0;
11858         if (!isdigit(*++s)) return 0;
11859         d = *s++ - '0';
11860         if (d > 6) return 0;
11861     }
11862 
11863     if (*s == '/') {
11864         if (!isdigit(*++s)) return 0;
11865         hour = *s++ - '0';
11866         if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11867         if (*s == ':') {
11868             if (!isdigit(*++s)) return 0;
11869             min = *s++ - '0';
11870             if (isdigit(*s)) min = 10*min + *s++ - '0';
11871             if (*s == ':') {
11872                 if (!isdigit(*++s)) return 0;
11873                 sec = *s++ - '0';
11874                 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11875             }
11876         }
11877     } else {
11878         hour = 2;
11879         min = 0;
11880         sec = 0;
11881     }
11882 
11883     if (dozjd) {
11884         if (w->tm_yday < d) goto before;
11885         if (w->tm_yday > d) goto after;
11886     } else {
11887         if (w->tm_mon+1 < m) goto before;
11888         if (w->tm_mon+1 > m) goto after;
11889 
11890         j = (42 + w->tm_wday - w->tm_mday)%7;   /*dow of mday 0 */
11891         k = d - j; /* mday of first d */
11892         if (k <= 0) k += 7;
11893         k += 7 * ((n>4?4:n)-1);  /* mday of n'th d */
11894         if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11895         if (w->tm_mday < k) goto before;
11896         if (w->tm_mday > k) goto after;
11897     }
11898 
11899     if (w->tm_hour < hour) goto before;
11900     if (w->tm_hour > hour) goto after;
11901     if (w->tm_min  < min)  goto before;
11902     if (w->tm_min  > min)  goto after;
11903     if (w->tm_sec  < sec)  goto before;
11904     goto after;
11905 
11906 before:
11907     *past = 0;
11908     return s;
11909 after:
11910     *past = 1;
11911     return s;
11912 }
11913 
11914 
11915 
11916 
11917 /*  parse the offset:   (+|-)hh[:mm[:ss]]  */
11918 
11919 static char *
11920 tz_parse_offset(char *s, int *offset)
11921 {
11922     int hour = 0, min = 0, sec = 0;
11923     int neg = 0;
11924     if (!s) return 0;
11925     if (!offset) return 0;
11926 
11927     if (*s == '-') {neg++; s++;}
11928     if (*s == '+') s++;
11929     if (!isdigit(*s)) return 0;
11930     hour = *s++ - '0';
11931     if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11932     if (hour > 24) return 0;
11933     if (*s == ':') {
11934         if (!isdigit(*++s)) return 0;
11935         min = *s++ - '0';
11936         if (isdigit(*s)) min = min*10 + (*s++ - '0');
11937         if (min > 59) return 0;
11938         if (*s == ':') {
11939             if (!isdigit(*++s)) return 0;
11940             sec = *s++ - '0';
11941             if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11942             if (sec > 59) return 0;
11943         }
11944     }
11945 
11946     *offset = (hour*60+min)*60 + sec;
11947     if (neg) *offset = -*offset;
11948     return s;
11949 }
11950 
11951 /*
11952     input time is w, whatever type of time the CRTL localtime() uses.
11953     sets dst, the zone, and the gmtoff (seconds)
11954 
11955     caches the value of TZ and UCX$TZ env variables; note that
11956     my_setenv looks for these and sets a flag if they're changed
11957     for efficiency.
11958 
11959     We have to watch out for the "australian" case (dst starts in
11960     october, ends in april)...flagged by "reverse" and checked by
11961     scanning through the months of the previous year.
11962 
11963 */
11964 
11965 static int
11966 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11967 {
11968     time_t when;
11969     struct tm *w2;
11970     char *s,*s2;
11971     char *dstzone, *tz, *s_start, *s_end;
11972     int std_off, dst_off, isdst;
11973     int y, dststart, dstend;
11974     static char envtz[1025];  /* longer than any logical, symbol, ... */
11975     static char ucxtz[1025];
11976     static char reversed = 0;
11977 
11978     if (!w) return 0;
11979 
11980     if (tz_updated) {
11981         tz_updated = 0;
11982         reversed = -1;  /* flag need to check  */
11983         envtz[0] = ucxtz[0] = '\0';
11984         tz = my_getenv("TZ",0);
11985         if (tz) strcpy(envtz, tz);
11986         tz = my_getenv("UCX$TZ",0);
11987         if (tz) strcpy(ucxtz, tz);
11988         if (!envtz[0] && !ucxtz[0]) return 0;  /* we give up */
11989     }
11990     tz = envtz;
11991     if (!*tz) tz = ucxtz;
11992 
11993     s = tz;
11994     while (isalpha(*s)) s++;
11995     s = tz_parse_offset(s, &std_off);
11996     if (!s) return 0;
11997     if (!*s) {                  /* no DST, hurray we're done! */
11998         isdst = 0;
11999         goto done;
12000     }
12001 
12002     dstzone = s;
12003     while (isalpha(*s)) s++;
12004     s2 = tz_parse_offset(s, &dst_off);
12005     if (s2) {
12006         s = s2;
12007     } else {
12008         dst_off = std_off - 3600;
12009     }
12010 
12011     if (!*s) {      /* default dst start/end?? */
12012         if (tz != ucxtz) {          /* if TZ tells zone only, UCX$TZ tells rule */
12013             s = strchr(ucxtz,',');
12014         }
12015         if (!s || !*s) s = ",M4.1.0,M10.5.0";   /* we know we do dst, default rule */
12016     }
12017     if (*s != ',') return 0;
12018 
12019     when = *w;
12020     when = _toutc(when);      /* convert to utc */
12021     when = when - std_off;    /* convert to pseudolocal time*/
12022 
12023     w2 = localtime(&when);
12024     y = w2->tm_year;
12025     s_start = s+1;
12026     s = tz_parse_startend(s_start,w2,&dststart);
12027     if (!s) return 0;
12028     if (*s != ',') return 0;
12029 
12030     when = *w;
12031     when = _toutc(when);      /* convert to utc */
12032     when = when - dst_off;    /* convert to pseudolocal time*/
12033     w2 = localtime(&when);
12034     if (w2->tm_year != y) {   /* spans a year, just check one time */
12035         when += dst_off - std_off;
12036         w2 = localtime(&when);
12037     }
12038     s_end = s+1;
12039     s = tz_parse_startend(s_end,w2,&dstend);
12040     if (!s) return 0;
12041 
12042     if (reversed == -1) {  /* need to check if start later than end */
12043         int j, ds, de;
12044 
12045         when = *w;
12046         if (when < 2*365*86400) {
12047             when += 2*365*86400;
12048         } else {
12049             when -= 365*86400;
12050         }
12051         w2 =localtime(&when);
12052         when = when + (15 - w2->tm_yday) * 86400;   /* jan 15 */
12053 
12054         for (j = 0; j < 12; j++) {
12055             w2 =localtime(&when);
12056             tz_parse_startend(s_start,w2,&ds);
12057             tz_parse_startend(s_end,w2,&de);
12058             if (ds != de) break;
12059             when += 30*86400;
12060         }
12061         reversed = 0;
12062         if (de && !ds) reversed = 1;
12063     }
12064 
12065     isdst = dststart && !dstend;
12066     if (reversed) isdst = dststart  || !dstend;
12067 
12068 done:
12069     if (dst)    *dst = isdst;
12070     if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
12071     if (isdst)  tz = dstzone;
12072     if (zone) {
12073         while(isalpha(*tz))  *zone++ = *tz++;
12074         *zone = '\0';
12075     }
12076     return 1;
12077 }
12078 
12079 #endif /* !RTL_USES_UTC */
12080 
12081 /* my_time(), my_localtime(), my_gmtime()
12082  * By default traffic in UTC time values, using CRTL gmtime() or
12083  * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
12084  * Note: We need to use these functions even when the CRTL has working
12085  * UTC support, since they also handle C<use vmsish qw(times);>
12086  *
12087  * Contributed by Chuck Lane  <lane@duphy4.physics.drexel.edu>
12088  * Modified by Charles Bailey <bailey@newman.upenn.edu>
12089  */
12090 
12091 /*{{{time_t my_time(time_t *timep)*/
12092 time_t Perl_my_time(pTHX_ time_t *timep)
12093 {
12094   time_t when;
12095   struct tm *tm_p;
12096 
12097   if (gmtime_emulation_type == 0) {
12098     int dstnow;
12099     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
12100                               /* results of calls to gmtime() and localtime() */
12101                               /* for same &base */
12102 
12103     gmtime_emulation_type++;
12104     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
12105       char off[LNM$C_NAMLENGTH+1];;
12106 
12107       gmtime_emulation_type++;
12108       if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
12109         gmtime_emulation_type++;
12110         utc_offset_secs = 0;
12111         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
12112       }
12113       else { utc_offset_secs = atol(off); }
12114     }
12115     else { /* We've got a working gmtime() */
12116       struct tm gmt, local;
12117 
12118       gmt = *tm_p;
12119       tm_p = localtime(&base);
12120       local = *tm_p;
12121       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
12122       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12123       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
12124       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
12125     }
12126   }
12127 
12128   when = time(NULL);
12129 # ifdef VMSISH_TIME
12130 # ifdef RTL_USES_UTC
12131   if (VMSISH_TIME) when = _toloc(when);
12132 # else
12133   if (!VMSISH_TIME) when = _toutc(when);
12134 # endif
12135 # endif
12136   if (timep != NULL) *timep = when;
12137   return when;
12138 
12139 }  /* end of my_time() */
12140 /*}}}*/
12141 
12142 
12143 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12144 struct tm *
12145 Perl_my_gmtime(pTHX_ const time_t *timep)
12146 {
12147   char *p;
12148   time_t when;
12149   struct tm *rsltmp;
12150 
12151   if (timep == NULL) {
12152     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12153     return NULL;
12154   }
12155   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
12156 
12157   when = *timep;
12158 # ifdef VMSISH_TIME
12159   if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12160 #  endif
12161 # ifdef RTL_USES_UTC  /* this implies that the CRTL has a working gmtime() */
12162   return gmtime(&when);
12163 # else
12164   /* CRTL localtime() wants local time as input, so does no tz correction */
12165   rsltmp = localtime(&when);
12166   if (rsltmp) rsltmp->tm_isdst = 0;  /* We already took DST into account */
12167   return rsltmp;
12168 #endif
12169 }  /* end of my_gmtime() */
12170 /*}}}*/
12171 
12172 
12173 /*{{{struct tm *my_localtime(const time_t *timep)*/
12174 struct tm *
12175 Perl_my_localtime(pTHX_ const time_t *timep)
12176 {
12177   time_t when, whenutc;
12178   struct tm *rsltmp;
12179   int dst, offset;
12180 
12181   if (timep == NULL) {
12182     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12183     return NULL;
12184   }
12185   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
12186   if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12187 
12188   when = *timep;
12189 # ifdef RTL_USES_UTC
12190 # ifdef VMSISH_TIME
12191   if (VMSISH_TIME) when = _toutc(when);
12192 # endif
12193   /* CRTL localtime() wants UTC as input, does tz correction itself */
12194   return localtime(&when);
12195 
12196 # else /* !RTL_USES_UTC */
12197   whenutc = when;
12198 # ifdef VMSISH_TIME
12199   if (!VMSISH_TIME) when = _toloc(whenutc);  /*  input was UTC */
12200   if (VMSISH_TIME) whenutc = _toutc(when);   /*  input was truelocal */
12201 # endif
12202   dst = -1;
12203 #ifndef RTL_USES_UTC
12204   if (tz_parse(aTHX_ &when, &dst, 0, &offset)) {   /* truelocal determines DST*/
12205       when = whenutc - offset;                   /* pseudolocal time*/
12206   }
12207 # endif
12208   /* CRTL localtime() wants local time as input, so does no tz correction */
12209   rsltmp = localtime(&when);
12210   if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12211   return rsltmp;
12212 # endif
12213 
12214 } /*  end of my_localtime() */
12215 /*}}}*/
12216 
12217 /* Reset definitions for later calls */
12218 #define gmtime(t)    my_gmtime(t)
12219 #define localtime(t) my_localtime(t)
12220 #define time(t)      my_time(t)
12221 
12222 
12223 /* my_utime - update modification/access time of a file
12224  *
12225  * VMS 7.3 and later implementation
12226  * Only the UTC translation is home-grown. The rest is handled by the
12227  * CRTL utime(), which will take into account the relevant feature
12228  * logicals and ODS-5 volume characteristics for true access times.
12229  *
12230  * pre VMS 7.3 implementation:
12231  * The calling sequence is identical to POSIX utime(), but under
12232  * VMS with ODS-2, only the modification time is changed; ODS-2 does
12233  * not maintain access times.  Restrictions differ from the POSIX
12234  * definition in that the time can be changed as long as the
12235  * caller has permission to execute the necessary IO$_MODIFY $QIO;
12236  * no separate checks are made to insure that the caller is the
12237  * owner of the file or has special privs enabled.
12238  * Code here is based on Joe Meadows' FILE utility.
12239  *
12240  */
12241 
12242 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12243  *              to VMS epoch  (01-JAN-1858 00:00:00.00)
12244  * in 100 ns intervals.
12245  */
12246 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12247 
12248 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12249 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12250 {
12251 #if __CRTL_VER >= 70300000
12252   struct utimbuf utc_utimes, *utc_utimesp;
12253 
12254   if (utimes != NULL) {
12255     utc_utimes.actime = utimes->actime;
12256     utc_utimes.modtime = utimes->modtime;
12257 # ifdef VMSISH_TIME
12258     /* If input was local; convert to UTC for sys svc */
12259     if (VMSISH_TIME) {
12260       utc_utimes.actime = _toutc(utimes->actime);
12261       utc_utimes.modtime = _toutc(utimes->modtime);
12262     }
12263 # endif
12264     utc_utimesp = &utc_utimes;
12265   }
12266   else {
12267     utc_utimesp = NULL;
12268   }
12269 
12270   return utime(file, utc_utimesp);
12271 
12272 #else /* __CRTL_VER < 70300000 */
12273 
12274   register int i;
12275   int sts;
12276   long int bintime[2], len = 2, lowbit, unixtime,
12277            secscale = 10000000; /* seconds --> 100 ns intervals */
12278   unsigned long int chan, iosb[2], retsts;
12279   char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12280   struct FAB myfab = cc$rms_fab;
12281   struct NAM mynam = cc$rms_nam;
12282 #if defined (__DECC) && defined (__VAX)
12283   /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12284    * at least through VMS V6.1, which causes a type-conversion warning.
12285    */
12286 #  pragma message save
12287 #  pragma message disable cvtdiftypes
12288 #endif
12289   struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12290   struct fibdef myfib;
12291 #if defined (__DECC) && defined (__VAX)
12292   /* This should be right after the declaration of myatr, but due
12293    * to a bug in VAX DEC C, this takes effect a statement early.
12294    */
12295 #  pragma message restore
12296 #endif
12297   /* cast ok for read only parameter */
12298   struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12299                         devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12300                         fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12301 
12302   if (file == NULL || *file == '\0') {
12303     SETERRNO(ENOENT, LIB$_INVARG);
12304     return -1;
12305   }
12306 
12307   /* Convert to VMS format ensuring that it will fit in 255 characters */
12308   if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12309       SETERRNO(ENOENT, LIB$_INVARG);
12310       return -1;
12311   }
12312   if (utimes != NULL) {
12313     /* Convert Unix time    (seconds since 01-JAN-1970 00:00:00.00)
12314      * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12315      * Since time_t is unsigned long int, and lib$emul takes a signed long int
12316      * as input, we force the sign bit to be clear by shifting unixtime right
12317      * one bit, then multiplying by an extra factor of 2 in lib$emul().
12318      */
12319     lowbit = (utimes->modtime & 1) ? secscale : 0;
12320     unixtime = (long int) utimes->modtime;
12321 #   ifdef VMSISH_TIME
12322     /* If input was UTC; convert to local for sys svc */
12323     if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12324 #   endif
12325     unixtime >>= 1;  secscale <<= 1;
12326     retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12327     if (!(retsts & 1)) {
12328       SETERRNO(EVMSERR, retsts);
12329       return -1;
12330     }
12331     retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12332     if (!(retsts & 1)) {
12333       SETERRNO(EVMSERR, retsts);
12334       return -1;
12335     }
12336   }
12337   else {
12338     /* Just get the current time in VMS format directly */
12339     retsts = sys$gettim(bintime);
12340     if (!(retsts & 1)) {
12341       SETERRNO(EVMSERR, retsts);
12342       return -1;
12343     }
12344   }
12345 
12346   myfab.fab$l_fna = vmsspec;
12347   myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12348   myfab.fab$l_nam = &mynam;
12349   mynam.nam$l_esa = esa;
12350   mynam.nam$b_ess = (unsigned char) sizeof esa;
12351   mynam.nam$l_rsa = rsa;
12352   mynam.nam$b_rss = (unsigned char) sizeof rsa;
12353   if (decc_efs_case_preserve)
12354       mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12355 
12356   /* Look for the file to be affected, letting RMS parse the file
12357    * specification for us as well.  I have set errno using only
12358    * values documented in the utime() man page for VMS POSIX.
12359    */
12360   retsts = sys$parse(&myfab,0,0);
12361   if (!(retsts & 1)) {
12362     set_vaxc_errno(retsts);
12363     if      (retsts == RMS$_PRV) set_errno(EACCES);
12364     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12365     else                         set_errno(EVMSERR);
12366     return -1;
12367   }
12368   retsts = sys$search(&myfab,0,0);
12369   if (!(retsts & 1)) {
12370     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12371     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12372     set_vaxc_errno(retsts);
12373     if      (retsts == RMS$_PRV) set_errno(EACCES);
12374     else if (retsts == RMS$_FNF) set_errno(ENOENT);
12375     else                         set_errno(EVMSERR);
12376     return -1;
12377   }
12378 
12379   devdsc.dsc$w_length = mynam.nam$b_dev;
12380   /* cast ok for read only parameter */
12381   devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12382 
12383   retsts = sys$assign(&devdsc,&chan,0,0);
12384   if (!(retsts & 1)) {
12385     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12386     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12387     set_vaxc_errno(retsts);
12388     if      (retsts == SS$_IVDEVNAM)   set_errno(ENOTDIR);
12389     else if (retsts == SS$_NOPRIV)     set_errno(EACCES);
12390     else if (retsts == SS$_NOSUCHDEV)  set_errno(ENOTDIR);
12391     else                               set_errno(EVMSERR);
12392     return -1;
12393   }
12394 
12395   fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12396   fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12397 
12398   memset((void *) &myfib, 0, sizeof myfib);
12399 #if defined(__DECC) || defined(__DECCXX)
12400   for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12401   for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12402   /* This prevents the revision time of the file being reset to the current
12403    * time as a result of our IO$_MODIFY $QIO. */
12404   myfib.fib$l_acctl = FIB$M_NORECORD;
12405 #else
12406   for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12407   for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12408   myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12409 #endif
12410   retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12411   mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12412   myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12413   _ckvmssts(sys$dassgn(chan));
12414   if (retsts & 1) retsts = iosb[0];
12415   if (!(retsts & 1)) {
12416     set_vaxc_errno(retsts);
12417     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12418     else                      set_errno(EVMSERR);
12419     return -1;
12420   }
12421 
12422   return 0;
12423 
12424 #endif /* #if __CRTL_VER >= 70300000 */
12425 
12426 }  /* end of my_utime() */
12427 /*}}}*/
12428 
12429 /*
12430  * flex_stat, flex_lstat, flex_fstat
12431  * basic stat, but gets it right when asked to stat
12432  * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12433  */
12434 
12435 #ifndef _USE_STD_STAT
12436 /* encode_dev packs a VMS device name string into an integer to allow
12437  * simple comparisons. This can be used, for example, to check whether two
12438  * files are located on the same device, by comparing their encoded device
12439  * names. Even a string comparison would not do, because stat() reuses the
12440  * device name buffer for each call; so without encode_dev, it would be
12441  * necessary to save the buffer and use strcmp (this would mean a number of
12442  * changes to the standard Perl code, to say nothing of what a Perl script
12443  * would have to do.
12444  *
12445  * The device lock id, if it exists, should be unique (unless perhaps compared
12446  * with lock ids transferred from other nodes). We have a lock id if the disk is
12447  * mounted cluster-wide, which is when we tend to get long (host-qualified)
12448  * device names. Thus we use the lock id in preference, and only if that isn't
12449  * available, do we try to pack the device name into an integer (flagged by
12450  * the sign bit (LOCKID_MASK) being set).
12451  *
12452  * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12453  * name and its encoded form, but it seems very unlikely that we will find
12454  * two files on different disks that share the same encoded device names,
12455  * and even more remote that they will share the same file id (if the test
12456  * is to check for the same file).
12457  *
12458  * A better method might be to use sys$device_scan on the first call, and to
12459  * search for the device, returning an index into the cached array.
12460  * The number returned would be more intelligible.
12461  * This is probably not worth it, and anyway would take quite a bit longer
12462  * on the first call.
12463  */
12464 #define LOCKID_MASK 0x80000000     /* Use 0 to force device name use only */
12465 static mydev_t encode_dev (pTHX_ const char *dev)
12466 {
12467   int i;
12468   unsigned long int f;
12469   mydev_t enc;
12470   char c;
12471   const char *q;
12472 
12473   if (!dev || !dev[0]) return 0;
12474 
12475 #if LOCKID_MASK
12476   {
12477     struct dsc$descriptor_s dev_desc;
12478     unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12479 
12480     /* For cluster-mounted disks, the disk lock identifier is unique, so we
12481        can try that first. */
12482     dev_desc.dsc$w_length =  strlen (dev);
12483     dev_desc.dsc$b_dtype =   DSC$K_DTYPE_T;
12484     dev_desc.dsc$b_class =   DSC$K_CLASS_S;
12485     dev_desc.dsc$a_pointer = (char *) dev;  /* Read only parameter */
12486     status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12487     if (!$VMS_STATUS_SUCCESS(status)) {
12488       switch (status) {
12489         case SS$_NOSUCHDEV:
12490           SETERRNO(ENODEV, status);
12491           return 0;
12492         default:
12493           _ckvmssts(status);
12494       }
12495     }
12496     if (lockid) return (lockid & ~LOCKID_MASK);
12497   }
12498 #endif
12499 
12500   /* Otherwise we try to encode the device name */
12501   enc = 0;
12502   f = 1;
12503   i = 0;
12504   for (q = dev + strlen(dev); q--; q >= dev) {
12505     if (*q == ':')
12506 	break;
12507     if (isdigit (*q))
12508       c= (*q) - '0';
12509     else if (isalpha (toupper (*q)))
12510       c= toupper (*q) - 'A' + (char)10;
12511     else
12512       continue; /* Skip '$'s */
12513     i++;
12514     if (i>6) break;     /* 36^7 is too large to fit in an unsigned long int */
12515     if (i>1) f *= 36;
12516     enc += f * (unsigned long int) c;
12517   }
12518   return (enc | LOCKID_MASK);  /* May have already overflowed into bit 31 */
12519 
12520 }  /* end of encode_dev() */
12521 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12522 	device_no = encode_dev(aTHX_ devname)
12523 #else
12524 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12525 	device_no = new_dev_no
12526 #endif
12527 
12528 static int
12529 is_null_device(name)
12530     const char *name;
12531 {
12532   if (decc_bug_devnull != 0) {
12533     if (strncmp("/dev/null", name, 9) == 0)
12534       return 1;
12535   }
12536     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12537        The underscore prefix, controller letter, and unit number are
12538        independently optional; for our purposes, the colon punctuation
12539        is not.  The colon can be trailed by optional directory and/or
12540        filename, but two consecutive colons indicates a nodename rather
12541        than a device.  [pr]  */
12542   if (*name == '_') ++name;
12543   if (tolower(*name++) != 'n') return 0;
12544   if (tolower(*name++) != 'l') return 0;
12545   if (tolower(*name) == 'a') ++name;
12546   if (*name == '0') ++name;
12547   return (*name++ == ':') && (*name != ':');
12548 }
12549 
12550 static int
12551 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12552 
12553 #define flex_stat_int(a,b,c)		Perl_flex_stat_int(aTHX_ a,b,c)
12554 
12555 static I32
12556 Perl_cando_by_name_int
12557    (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12558 {
12559   char usrname[L_cuserid];
12560   struct dsc$descriptor_s usrdsc =
12561          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12562   char *vmsname = NULL, *fileified = NULL;
12563   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12564   unsigned short int retlen, trnlnm_iter_count;
12565   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12566   union prvdef curprv;
12567   struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12568          {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12569          {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12570   struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12571          {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12572          {0,0,0,0}};
12573   struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12574          {0,0,0,0}};
12575   struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12576   Stat_t st;
12577   static int profile_context = -1;
12578 
12579   if (!fname || !*fname) return FALSE;
12580 
12581   /* Make sure we expand logical names, since sys$check_access doesn't */
12582   fileified = PerlMem_malloc(VMS_MAXRSS);
12583   if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12584   if (!strpbrk(fname,"/]>:")) {
12585       strcpy(fileified,fname);
12586       trnlnm_iter_count = 0;
12587       while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12588         trnlnm_iter_count++;
12589         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12590       }
12591       fname = fileified;
12592   }
12593 
12594   vmsname = PerlMem_malloc(VMS_MAXRSS);
12595   if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12596   if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12597     /* Don't know if already in VMS format, so make sure */
12598     if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12599       PerlMem_free(fileified);
12600       PerlMem_free(vmsname);
12601       return FALSE;
12602     }
12603   }
12604   else {
12605     strcpy(vmsname,fname);
12606   }
12607 
12608   /* sys$check_access needs a file spec, not a directory spec.
12609    * flex_stat now will handle a null thread context during startup.
12610    */
12611 
12612   retlen = namdsc.dsc$w_length = strlen(vmsname);
12613   if (vmsname[retlen-1] == ']'
12614       || vmsname[retlen-1] == '>'
12615       || vmsname[retlen-1] == ':'
12616       || (!flex_stat_int(vmsname, &st, 1) &&
12617           S_ISDIR(st.st_mode))) {
12618 
12619       if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12620         PerlMem_free(fileified);
12621         PerlMem_free(vmsname);
12622         return FALSE;
12623       }
12624       fname = fileified;
12625   }
12626   else {
12627       fname = vmsname;
12628   }
12629 
12630   retlen = namdsc.dsc$w_length = strlen(fname);
12631   namdsc.dsc$a_pointer = (char *)fname;
12632 
12633   switch (bit) {
12634     case S_IXUSR: case S_IXGRP: case S_IXOTH:
12635       access = ARM$M_EXECUTE;
12636       flags = CHP$M_READ;
12637       break;
12638     case S_IRUSR: case S_IRGRP: case S_IROTH:
12639       access = ARM$M_READ;
12640       flags = CHP$M_READ | CHP$M_USEREADALL;
12641       break;
12642     case S_IWUSR: case S_IWGRP: case S_IWOTH:
12643       access = ARM$M_WRITE;
12644       flags = CHP$M_READ | CHP$M_WRITE;
12645       break;
12646     case S_IDUSR: case S_IDGRP: case S_IDOTH:
12647       access = ARM$M_DELETE;
12648       flags = CHP$M_READ | CHP$M_WRITE;
12649       break;
12650     default:
12651       if (fileified != NULL)
12652 	PerlMem_free(fileified);
12653       if (vmsname != NULL)
12654 	PerlMem_free(vmsname);
12655       return FALSE;
12656   }
12657 
12658   /* Before we call $check_access, create a user profile with the current
12659    * process privs since otherwise it just uses the default privs from the
12660    * UAF and might give false positives or negatives.  This only works on
12661    * VMS versions v6.0 and later since that's when sys$create_user_profile
12662    * became available.
12663    */
12664 
12665   /* get current process privs and username */
12666   _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12667   _ckvmssts_noperl(iosb[0]);
12668 
12669 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12670 
12671   /* find out the space required for the profile */
12672   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12673                                     &usrprodsc.dsc$w_length,&profile_context));
12674 
12675   /* allocate space for the profile and get it filled in */
12676   usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12677   if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12678   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12679                                     &usrprodsc.dsc$w_length,&profile_context));
12680 
12681   /* use the profile to check access to the file; free profile & analyze results */
12682   retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12683   PerlMem_free(usrprodsc.dsc$a_pointer);
12684   if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12685 
12686 #else
12687 
12688   retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12689 
12690 #endif
12691 
12692   if (retsts == SS$_NOPRIV      || retsts == SS$_NOSUCHOBJECT ||
12693       retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12694       retsts == RMS$_DIR        || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12695     set_vaxc_errno(retsts);
12696     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12697     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12698     else set_errno(ENOENT);
12699     if (fileified != NULL)
12700       PerlMem_free(fileified);
12701     if (vmsname != NULL)
12702       PerlMem_free(vmsname);
12703     return FALSE;
12704   }
12705   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12706     if (fileified != NULL)
12707       PerlMem_free(fileified);
12708     if (vmsname != NULL)
12709       PerlMem_free(vmsname);
12710     return TRUE;
12711   }
12712   _ckvmssts_noperl(retsts);
12713 
12714   if (fileified != NULL)
12715     PerlMem_free(fileified);
12716   if (vmsname != NULL)
12717     PerlMem_free(vmsname);
12718   return FALSE;  /* Should never get here */
12719 
12720 }
12721 
12722 /* Do the permissions allow some operation?  Assumes PL_statcache already set. */
12723 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12724  * subset of the applicable information.
12725  */
12726 bool
12727 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12728 {
12729   return cando_by_name_int
12730 	(bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12731 }  /* end of cando() */
12732 /*}}}*/
12733 
12734 
12735 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12736 I32
12737 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12738 {
12739    return cando_by_name_int(bit, effective, fname, 0);
12740 
12741 }  /* end of cando_by_name() */
12742 /*}}}*/
12743 
12744 
12745 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12746 int
12747 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12748 {
12749   if (!fstat(fd, &statbufp->crtl_stat)) {
12750     char *cptr;
12751     char *vms_filename;
12752     vms_filename = PerlMem_malloc(VMS_MAXRSS);
12753     if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12754 
12755     /* Save name for cando by name in VMS format */
12756     cptr = getname(fd, vms_filename, 1);
12757 
12758     /* This should not happen, but just in case */
12759     if (cptr == NULL) {
12760 	statbufp->st_devnam[0] = 0;
12761     }
12762     else {
12763 	/* Make sure that the saved name fits in 255 characters */
12764 	cptr = int_rmsexpand_vms
12765 		       (vms_filename,
12766 			statbufp->st_devnam,
12767 			0);
12768 	if (cptr == NULL)
12769 	    statbufp->st_devnam[0] = 0;
12770     }
12771     PerlMem_free(vms_filename);
12772 
12773     VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12774     VMS_DEVICE_ENCODE
12775 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12776 
12777 #   ifdef RTL_USES_UTC
12778 #   ifdef VMSISH_TIME
12779     if (VMSISH_TIME) {
12780       statbufp->st_mtime = _toloc(statbufp->st_mtime);
12781       statbufp->st_atime = _toloc(statbufp->st_atime);
12782       statbufp->st_ctime = _toloc(statbufp->st_ctime);
12783     }
12784 #   endif
12785 #   else
12786 #   ifdef VMSISH_TIME
12787     if (!VMSISH_TIME) { /* Return UTC instead of local time */
12788 #   else
12789     if (1) {
12790 #   endif
12791       statbufp->st_mtime = _toutc(statbufp->st_mtime);
12792       statbufp->st_atime = _toutc(statbufp->st_atime);
12793       statbufp->st_ctime = _toutc(statbufp->st_ctime);
12794     }
12795 #endif
12796     return 0;
12797   }
12798   return -1;
12799 
12800 }  /* end of flex_fstat() */
12801 /*}}}*/
12802 
12803 static int
12804 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12805 {
12806     char *fileified;
12807     char *temp_fspec;
12808     const char *save_spec;
12809     char *ret_spec;
12810     int retval = -1;
12811     int efs_hack = 0;
12812     dSAVEDERRNO;
12813 
12814     if (!fspec) {
12815         errno = EINVAL;
12816         return retval;
12817     }
12818 
12819     if (decc_bug_devnull != 0) {
12820       if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12821 	memset(statbufp,0,sizeof *statbufp);
12822         VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12823 	statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12824 	statbufp->st_uid = 0x00010001;
12825 	statbufp->st_gid = 0x0001;
12826 	time((time_t *)&statbufp->st_mtime);
12827 	statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12828 	return 0;
12829       }
12830     }
12831 
12832     /* Try for a directory name first.  If fspec contains a filename without
12833      * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12834      * and sea:[wine.dark]water. exist, we prefer the directory here.
12835      * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12836      * not sea:[wine.dark]., if the latter exists.  If the intended target is
12837      * the file with null type, specify this by calling flex_stat() with
12838      * a '.' at the end of fspec.
12839      *
12840      * If we are in Posix filespec mode, accept the filename as is.
12841      */
12842 
12843 
12844     fileified = PerlMem_malloc(VMS_MAXRSS);
12845     if (fileified == NULL)
12846         _ckvmssts_noperl(SS$_INSFMEM);
12847 
12848     temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12849     if (temp_fspec == NULL)
12850         _ckvmssts_noperl(SS$_INSFMEM);
12851 
12852     strcpy(temp_fspec, fspec);
12853 
12854     SAVE_ERRNO;
12855 
12856 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12857   if (decc_posix_compliant_pathnames == 0) {
12858 #endif
12859 
12860     /* We may be able to optimize this, but in order for fileify_dirspec to
12861      * always return a usuable answer, we have to call vmspath first to
12862      * make sure that it is in VMS directory format, as stat/lstat on 8.3
12863      * can not handle directories in unix format that it does not have read
12864      * access to.  Vmspath handles the case where a bare name which could be
12865      * a logical name gets passed.
12866      */
12867     ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12868     if (ret_spec != NULL) {
12869         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
12870         if (ret_spec != NULL) {
12871             if (lstat_flag == 0)
12872                 retval = stat(fileified, &statbufp->crtl_stat);
12873             else
12874                 retval = lstat(fileified, &statbufp->crtl_stat);
12875             save_spec = fileified;
12876         }
12877     }
12878 
12879     if (retval && vms_bug_stat_filename) {
12880 
12881         /* We should try again as a vmsified file specification */
12882         /* However Perl traditionally has not done this, which  */
12883         /* causes problems with existing tests */
12884 
12885         ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12886         if (ret_spec != NULL) {
12887             if (lstat_flag == 0)
12888                 retval = stat(temp_fspec, &statbufp->crtl_stat);
12889             else
12890                 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12891             save_spec = temp_fspec;
12892         }
12893     }
12894 
12895     if (retval) {
12896         /* Last chance - allow multiple dots with out EFS CHARSET */
12897         /* The CRTL stat() falls down hard on multi-dot filenames in unix
12898          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12899          * enable it if it isn't already.
12900          */
12901 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12902         if (!decc_efs_charset && (decc_efs_charset_index > 0))
12903             decc$feature_set_value(decc_efs_charset_index, 1, 1);
12904 #endif
12905         if (lstat_flag == 0)
12906 	    retval = stat(fspec, &statbufp->crtl_stat);
12907         else
12908 	    retval = lstat(fspec, &statbufp->crtl_stat);
12909         save_spec = fspec;
12910 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12911         if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12912             decc$feature_set_value(decc_efs_charset_index, 1, 0);
12913             efs_hack = 1;
12914         }
12915 #endif
12916     }
12917 
12918 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12919   } else {
12920     if (lstat_flag == 0)
12921       retval = stat(temp_fspec, &statbufp->crtl_stat);
12922     else
12923       retval = lstat(temp_fspec, &statbufp->crtl_stat);
12924       save_spec = temp_fspec;
12925   }
12926 #endif
12927 
12928 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12929   /* As you were... */
12930   if (!decc_efs_charset)
12931     decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12932 #endif
12933 
12934     if (!retval) {
12935     char * cptr;
12936     int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12937 
12938       /* If this is an lstat, do not follow the link */
12939       if (lstat_flag)
12940 	rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12941 
12942 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12943       /* If we used the efs_hack above, we must also use it here for */
12944       /* perl_cando to work */
12945       if (efs_hack && (decc_efs_charset_index > 0)) {
12946           decc$feature_set_value(decc_efs_charset_index, 1, 1);
12947       }
12948 #endif
12949       cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12950 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12951       if (efs_hack && (decc_efs_charset_index > 0)) {
12952           decc$feature_set_value(decc_efs_charset, 1, 0);
12953       }
12954 #endif
12955 
12956       /* Fix me: If this is NULL then stat found a file, and we could */
12957       /* not convert the specification to VMS - Should never happen */
12958       if (cptr == NULL)
12959 	statbufp->st_devnam[0] = 0;
12960 
12961       VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12962       VMS_DEVICE_ENCODE
12963 	(statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12964 #     ifdef RTL_USES_UTC
12965 #     ifdef VMSISH_TIME
12966       if (VMSISH_TIME) {
12967         statbufp->st_mtime = _toloc(statbufp->st_mtime);
12968         statbufp->st_atime = _toloc(statbufp->st_atime);
12969         statbufp->st_ctime = _toloc(statbufp->st_ctime);
12970       }
12971 #     endif
12972 #     else
12973 #     ifdef VMSISH_TIME
12974       if (!VMSISH_TIME) { /* Return UTC instead of local time */
12975 #     else
12976       if (1) {
12977 #     endif
12978         statbufp->st_mtime = _toutc(statbufp->st_mtime);
12979         statbufp->st_atime = _toutc(statbufp->st_atime);
12980         statbufp->st_ctime = _toutc(statbufp->st_ctime);
12981       }
12982 #     endif
12983     }
12984     /* If we were successful, leave errno where we found it */
12985     if (retval == 0) RESTORE_ERRNO;
12986     return retval;
12987 
12988 }  /* end of flex_stat_int() */
12989 
12990 
12991 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12992 int
12993 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12994 {
12995    return flex_stat_int(fspec, statbufp, 0);
12996 }
12997 /*}}}*/
12998 
12999 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
13000 int
13001 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
13002 {
13003    return flex_stat_int(fspec, statbufp, 1);
13004 }
13005 /*}}}*/
13006 
13007 
13008 /*{{{char *my_getlogin()*/
13009 /* VMS cuserid == Unix getlogin, except calling sequence */
13010 char *
13011 my_getlogin(void)
13012 {
13013     static char user[L_cuserid];
13014     return cuserid(user);
13015 }
13016 /*}}}*/
13017 
13018 
13019 /*  rmscopy - copy a file using VMS RMS routines
13020  *
13021  *  Copies contents and attributes of spec_in to spec_out, except owner
13022  *  and protection information.  Name and type of spec_in are used as
13023  *  defaults for spec_out.  The third parameter specifies whether rmscopy()
13024  *  should try to propagate timestamps from the input file to the output file.
13025  *  If it is less than 0, no timestamps are preserved.  If it is 0, then
13026  *  rmscopy() will behave similarly to the DCL COPY command: timestamps are
13027  *  propagated to the output file at creation iff the output file specification
13028  *  did not contain an explicit name or type, and the revision date is always
13029  *  updated at the end of the copy operation.  If it is greater than 0, then
13030  *  it is interpreted as a bitmask, in which bit 0 indicates that timestamps
13031  *  other than the revision date should be propagated, and bit 1 indicates
13032  *  that the revision date should be propagated.
13033  *
13034  *  Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
13035  *
13036  *  Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
13037  *  Incorporates, with permission, some code from EZCOPY by Tim Adye
13038  *  <T.J.Adye@rl.ac.uk>.  Permission is given to distribute this code
13039  * as part of the Perl standard distribution under the terms of the
13040  * GNU General Public License or the Perl Artistic License.  Copies
13041  * of each may be found in the Perl standard distribution.
13042  */ /* FIXME */
13043 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
13044 int
13045 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
13046 {
13047     char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
13048          *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
13049     unsigned long int i, sts, sts2;
13050     int dna_len;
13051     struct FAB fab_in, fab_out;
13052     struct RAB rab_in, rab_out;
13053     rms_setup_nam(nam);
13054     rms_setup_nam(nam_out);
13055     struct XABDAT xabdat;
13056     struct XABFHC xabfhc;
13057     struct XABRDT xabrdt;
13058     struct XABSUM xabsum;
13059 
13060     vmsin = PerlMem_malloc(VMS_MAXRSS);
13061     if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13062     vmsout = PerlMem_malloc(VMS_MAXRSS);
13063     if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13064     if (!spec_in  || !*spec_in  || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
13065         !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
13066       PerlMem_free(vmsin);
13067       PerlMem_free(vmsout);
13068       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13069       return 0;
13070     }
13071 
13072     esa = PerlMem_malloc(VMS_MAXRSS);
13073     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13074     esal = NULL;
13075 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13076     esal = PerlMem_malloc(VMS_MAXRSS);
13077     if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13078 #endif
13079     fab_in = cc$rms_fab;
13080     rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
13081     fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
13082     fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
13083     fab_in.fab$l_fop = FAB$M_SQO;
13084     rms_bind_fab_nam(fab_in, nam);
13085     fab_in.fab$l_xab = (void *) &xabdat;
13086 
13087     rsa = PerlMem_malloc(VMS_MAXRSS);
13088     if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13089     rsal = NULL;
13090 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13091     rsal = PerlMem_malloc(VMS_MAXRSS);
13092     if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13093 #endif
13094     rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
13095     rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
13096     rms_nam_esl(nam) = 0;
13097     rms_nam_rsl(nam) = 0;
13098     rms_nam_esll(nam) = 0;
13099     rms_nam_rsll(nam) = 0;
13100 #ifdef NAM$M_NO_SHORT_UPCASE
13101     if (decc_efs_case_preserve)
13102 	rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
13103 #endif
13104 
13105     xabdat = cc$rms_xabdat;        /* To get creation date */
13106     xabdat.xab$l_nxt = (void *) &xabfhc;
13107 
13108     xabfhc = cc$rms_xabfhc;        /* To get record length */
13109     xabfhc.xab$l_nxt = (void *) &xabsum;
13110 
13111     xabsum = cc$rms_xabsum;        /* To get key and area information */
13112 
13113     if (!((sts = sys$open(&fab_in)) & 1)) {
13114       PerlMem_free(vmsin);
13115       PerlMem_free(vmsout);
13116       PerlMem_free(esa);
13117       if (esal != NULL)
13118 	PerlMem_free(esal);
13119       PerlMem_free(rsa);
13120       if (rsal != NULL)
13121 	PerlMem_free(rsal);
13122       set_vaxc_errno(sts);
13123       switch (sts) {
13124         case RMS$_FNF: case RMS$_DNF:
13125           set_errno(ENOENT); break;
13126         case RMS$_DIR:
13127           set_errno(ENOTDIR); break;
13128         case RMS$_DEV:
13129           set_errno(ENODEV); break;
13130         case RMS$_SYN:
13131           set_errno(EINVAL); break;
13132         case RMS$_PRV:
13133           set_errno(EACCES); break;
13134         default:
13135           set_errno(EVMSERR);
13136       }
13137       return 0;
13138     }
13139 
13140     nam_out = nam;
13141     fab_out = fab_in;
13142     fab_out.fab$w_ifi = 0;
13143     fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13144     fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13145     fab_out.fab$l_fop = FAB$M_SQO;
13146     rms_bind_fab_nam(fab_out, nam_out);
13147     rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13148     dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13149     rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13150     esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13151     if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13152     rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13153     if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13154     esal_out = NULL;
13155     rsal_out = NULL;
13156 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13157     esal_out = PerlMem_malloc(VMS_MAXRSS);
13158     if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13159     rsal_out = PerlMem_malloc(VMS_MAXRSS);
13160     if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13161 #endif
13162     rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13163     rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13164 
13165     if (preserve_dates == 0) {  /* Act like DCL COPY */
13166       rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13167       fab_out.fab$l_xab = NULL;  /* Don't disturb data from input file */
13168       if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13169 	PerlMem_free(vmsin);
13170 	PerlMem_free(vmsout);
13171 	PerlMem_free(esa);
13172 	if (esal != NULL)
13173 	    PerlMem_free(esal);
13174 	PerlMem_free(rsa);
13175 	if (rsal != NULL)
13176 	    PerlMem_free(rsal);
13177 	PerlMem_free(esa_out);
13178 	if (esal_out != NULL)
13179 	    PerlMem_free(esal_out);
13180 	PerlMem_free(rsa_out);
13181 	if (rsal_out != NULL)
13182 	    PerlMem_free(rsal_out);
13183         set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13184         set_vaxc_errno(sts);
13185         return 0;
13186       }
13187       fab_out.fab$l_xab = (void *) &xabdat;
13188       if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13189 	preserve_dates = 1;
13190     }
13191     if (preserve_dates < 0)   /* Clear all bits; we'll use it as a */
13192       preserve_dates =0;      /* bitmask from this point forward   */
13193 
13194     if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13195     if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13196       PerlMem_free(vmsin);
13197       PerlMem_free(vmsout);
13198       PerlMem_free(esa);
13199       if (esal != NULL)
13200 	  PerlMem_free(esal);
13201       PerlMem_free(rsa);
13202       if (rsal != NULL)
13203 	  PerlMem_free(rsal);
13204       PerlMem_free(esa_out);
13205       if (esal_out != NULL)
13206 	  PerlMem_free(esal_out);
13207       PerlMem_free(rsa_out);
13208       if (rsal_out != NULL)
13209 	  PerlMem_free(rsal_out);
13210       set_vaxc_errno(sts);
13211       switch (sts) {
13212         case RMS$_DNF:
13213           set_errno(ENOENT); break;
13214         case RMS$_DIR:
13215           set_errno(ENOTDIR); break;
13216         case RMS$_DEV:
13217           set_errno(ENODEV); break;
13218         case RMS$_SYN:
13219           set_errno(EINVAL); break;
13220         case RMS$_PRV:
13221           set_errno(EACCES); break;
13222         default:
13223           set_errno(EVMSERR);
13224       }
13225       return 0;
13226     }
13227     fab_out.fab$l_fop |= FAB$M_DLT;  /* in case we have to bail out */
13228     if (preserve_dates & 2) {
13229       /* sys$close() will process xabrdt, not xabdat */
13230       xabrdt = cc$rms_xabrdt;
13231 #ifndef __GNUC__
13232       xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13233 #else
13234       /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13235        * is unsigned long[2], while DECC & VAXC use a struct */
13236       memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13237 #endif
13238       fab_out.fab$l_xab = (void *) &xabrdt;
13239     }
13240 
13241     ubf = PerlMem_malloc(32256);
13242     if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13243     rab_in = cc$rms_rab;
13244     rab_in.rab$l_fab = &fab_in;
13245     rab_in.rab$l_rop = RAB$M_BIO;
13246     rab_in.rab$l_ubf = ubf;
13247     rab_in.rab$w_usz = 32256;
13248     if (!((sts = sys$connect(&rab_in)) & 1)) {
13249       sys$close(&fab_in); sys$close(&fab_out);
13250       PerlMem_free(vmsin);
13251       PerlMem_free(vmsout);
13252       PerlMem_free(ubf);
13253       PerlMem_free(esa);
13254       if (esal != NULL)
13255 	  PerlMem_free(esal);
13256       PerlMem_free(rsa);
13257       if (rsal != NULL)
13258 	  PerlMem_free(rsal);
13259       PerlMem_free(esa_out);
13260       if (esal_out != NULL)
13261 	  PerlMem_free(esal_out);
13262       PerlMem_free(rsa_out);
13263       if (rsal_out != NULL)
13264 	  PerlMem_free(rsal_out);
13265       set_errno(EVMSERR); set_vaxc_errno(sts);
13266       return 0;
13267     }
13268 
13269     rab_out = cc$rms_rab;
13270     rab_out.rab$l_fab = &fab_out;
13271     rab_out.rab$l_rbf = ubf;
13272     if (!((sts = sys$connect(&rab_out)) & 1)) {
13273       sys$close(&fab_in); sys$close(&fab_out);
13274       PerlMem_free(vmsin);
13275       PerlMem_free(vmsout);
13276       PerlMem_free(ubf);
13277       PerlMem_free(esa);
13278       if (esal != NULL)
13279 	  PerlMem_free(esal);
13280       PerlMem_free(rsa);
13281       if (rsal != NULL)
13282 	  PerlMem_free(rsal);
13283       PerlMem_free(esa_out);
13284       if (esal_out != NULL)
13285 	  PerlMem_free(esal_out);
13286       PerlMem_free(rsa_out);
13287       if (rsal_out != NULL)
13288 	  PerlMem_free(rsal_out);
13289       set_errno(EVMSERR); set_vaxc_errno(sts);
13290       return 0;
13291     }
13292 
13293     while ((sts = sys$read(&rab_in))) {  /* always true  */
13294       if (sts == RMS$_EOF) break;
13295       rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13296       if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13297         sys$close(&fab_in); sys$close(&fab_out);
13298 	PerlMem_free(vmsin);
13299 	PerlMem_free(vmsout);
13300 	PerlMem_free(ubf);
13301 	PerlMem_free(esa);
13302 	if (esal != NULL)
13303 	    PerlMem_free(esal);
13304 	PerlMem_free(rsa);
13305 	if (rsal != NULL)
13306 	    PerlMem_free(rsal);
13307 	PerlMem_free(esa_out);
13308  	if (esal_out != NULL)
13309 	    PerlMem_free(esal_out);
13310 	PerlMem_free(rsa_out);
13311  	if (rsal_out != NULL)
13312 	    PerlMem_free(rsal_out);
13313         set_errno(EVMSERR); set_vaxc_errno(sts);
13314         return 0;
13315       }
13316     }
13317 
13318 
13319     fab_out.fab$l_fop &= ~FAB$M_DLT;  /* We got this far; keep the output */
13320     sys$close(&fab_in);  sys$close(&fab_out);
13321     sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13322 
13323     PerlMem_free(vmsin);
13324     PerlMem_free(vmsout);
13325     PerlMem_free(ubf);
13326     PerlMem_free(esa);
13327     if (esal != NULL)
13328 	PerlMem_free(esal);
13329     PerlMem_free(rsa);
13330     if (rsal != NULL)
13331 	PerlMem_free(rsal);
13332     PerlMem_free(esa_out);
13333     if (esal_out != NULL)
13334 	PerlMem_free(esal_out);
13335     PerlMem_free(rsa_out);
13336     if (rsal_out != NULL)
13337 	PerlMem_free(rsal_out);
13338 
13339     if (!(sts & 1)) {
13340       set_errno(EVMSERR); set_vaxc_errno(sts);
13341       return 0;
13342     }
13343 
13344     return 1;
13345 
13346 }  /* end of rmscopy() */
13347 /*}}}*/
13348 
13349 
13350 /***  The following glue provides 'hooks' to make some of the routines
13351  * from this file available from Perl.  These routines are sufficiently
13352  * basic, and are required sufficiently early in the build process,
13353  * that's it's nice to have them available to miniperl as well as the
13354  * full Perl, so they're set up here instead of in an extension.  The
13355  * Perl code which handles importation of these names into a given
13356  * package lives in [.VMS]Filespec.pm in @INC.
13357  */
13358 
13359 void
13360 rmsexpand_fromperl(pTHX_ CV *cv)
13361 {
13362   dXSARGS;
13363   char *fspec, *defspec = NULL, *rslt;
13364   STRLEN n_a;
13365   int fs_utf8, dfs_utf8;
13366 
13367   fs_utf8 = 0;
13368   dfs_utf8 = 0;
13369   if (!items || items > 2)
13370     Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13371   fspec = SvPV(ST(0),n_a);
13372   fs_utf8 = SvUTF8(ST(0));
13373   if (!fspec || !*fspec) XSRETURN_UNDEF;
13374   if (items == 2) {
13375     defspec = SvPV(ST(1),n_a);
13376     dfs_utf8 = SvUTF8(ST(1));
13377   }
13378   rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13379   ST(0) = sv_newmortal();
13380   if (rslt != NULL) {
13381     sv_usepvn(ST(0),rslt,strlen(rslt));
13382     if (fs_utf8) {
13383 	SvUTF8_on(ST(0));
13384     }
13385   }
13386   XSRETURN(1);
13387 }
13388 
13389 void
13390 vmsify_fromperl(pTHX_ CV *cv)
13391 {
13392   dXSARGS;
13393   char *vmsified;
13394   STRLEN n_a;
13395   int utf8_fl;
13396 
13397   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13398   utf8_fl = SvUTF8(ST(0));
13399   vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13400   ST(0) = sv_newmortal();
13401   if (vmsified != NULL) {
13402     sv_usepvn(ST(0),vmsified,strlen(vmsified));
13403     if (utf8_fl) {
13404 	SvUTF8_on(ST(0));
13405     }
13406   }
13407   XSRETURN(1);
13408 }
13409 
13410 void
13411 unixify_fromperl(pTHX_ CV *cv)
13412 {
13413   dXSARGS;
13414   char *unixified;
13415   STRLEN n_a;
13416   int utf8_fl;
13417 
13418   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13419   utf8_fl = SvUTF8(ST(0));
13420   unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13421   ST(0) = sv_newmortal();
13422   if (unixified != NULL) {
13423     sv_usepvn(ST(0),unixified,strlen(unixified));
13424     if (utf8_fl) {
13425 	SvUTF8_on(ST(0));
13426     }
13427   }
13428   XSRETURN(1);
13429 }
13430 
13431 void
13432 fileify_fromperl(pTHX_ CV *cv)
13433 {
13434   dXSARGS;
13435   char *fileified;
13436   STRLEN n_a;
13437   int utf8_fl;
13438 
13439   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13440   utf8_fl = SvUTF8(ST(0));
13441   fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13442   ST(0) = sv_newmortal();
13443   if (fileified != NULL) {
13444     sv_usepvn(ST(0),fileified,strlen(fileified));
13445     if (utf8_fl) {
13446 	SvUTF8_on(ST(0));
13447     }
13448   }
13449   XSRETURN(1);
13450 }
13451 
13452 void
13453 pathify_fromperl(pTHX_ CV *cv)
13454 {
13455   dXSARGS;
13456   char *pathified;
13457   STRLEN n_a;
13458   int utf8_fl;
13459 
13460   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13461   utf8_fl = SvUTF8(ST(0));
13462   pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13463   ST(0) = sv_newmortal();
13464   if (pathified != NULL) {
13465     sv_usepvn(ST(0),pathified,strlen(pathified));
13466     if (utf8_fl) {
13467 	SvUTF8_on(ST(0));
13468     }
13469   }
13470   XSRETURN(1);
13471 }
13472 
13473 void
13474 vmspath_fromperl(pTHX_ CV *cv)
13475 {
13476   dXSARGS;
13477   char *vmspath;
13478   STRLEN n_a;
13479   int utf8_fl;
13480 
13481   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13482   utf8_fl = SvUTF8(ST(0));
13483   vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13484   ST(0) = sv_newmortal();
13485   if (vmspath != NULL) {
13486     sv_usepvn(ST(0),vmspath,strlen(vmspath));
13487     if (utf8_fl) {
13488 	SvUTF8_on(ST(0));
13489     }
13490   }
13491   XSRETURN(1);
13492 }
13493 
13494 void
13495 unixpath_fromperl(pTHX_ CV *cv)
13496 {
13497   dXSARGS;
13498   char *unixpath;
13499   STRLEN n_a;
13500   int utf8_fl;
13501 
13502   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13503   utf8_fl = SvUTF8(ST(0));
13504   unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13505   ST(0) = sv_newmortal();
13506   if (unixpath != NULL) {
13507     sv_usepvn(ST(0),unixpath,strlen(unixpath));
13508     if (utf8_fl) {
13509 	SvUTF8_on(ST(0));
13510     }
13511   }
13512   XSRETURN(1);
13513 }
13514 
13515 void
13516 candelete_fromperl(pTHX_ CV *cv)
13517 {
13518   dXSARGS;
13519   char *fspec, *fsp;
13520   SV *mysv;
13521   IO *io;
13522   STRLEN n_a;
13523 
13524   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13525 
13526   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13527   Newx(fspec, VMS_MAXRSS, char);
13528   if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13529   if (SvTYPE(mysv) == SVt_PVGV) {
13530     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13531       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13532       ST(0) = &PL_sv_no;
13533       Safefree(fspec);
13534       XSRETURN(1);
13535     }
13536     fsp = fspec;
13537   }
13538   else {
13539     if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13540       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13541       ST(0) = &PL_sv_no;
13542       Safefree(fspec);
13543       XSRETURN(1);
13544     }
13545   }
13546 
13547   ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13548   Safefree(fspec);
13549   XSRETURN(1);
13550 }
13551 
13552 void
13553 rmscopy_fromperl(pTHX_ CV *cv)
13554 {
13555   dXSARGS;
13556   char *inspec, *outspec, *inp, *outp;
13557   int date_flag;
13558   struct dsc$descriptor indsc  = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13559                         outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13560   unsigned long int sts;
13561   SV *mysv;
13562   IO *io;
13563   STRLEN n_a;
13564 
13565   if (items < 2 || items > 3)
13566     Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13567 
13568   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13569   Newx(inspec, VMS_MAXRSS, char);
13570   if (SvTYPE(mysv) == SVt_PVGV) {
13571     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13572       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13573       ST(0) = &PL_sv_no;
13574       Safefree(inspec);
13575       XSRETURN(1);
13576     }
13577     inp = inspec;
13578   }
13579   else {
13580     if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13581       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13582       ST(0) = &PL_sv_no;
13583       Safefree(inspec);
13584       XSRETURN(1);
13585     }
13586   }
13587   mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13588   Newx(outspec, VMS_MAXRSS, char);
13589   if (SvTYPE(mysv) == SVt_PVGV) {
13590     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13591       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13592       ST(0) = &PL_sv_no;
13593       Safefree(inspec);
13594       Safefree(outspec);
13595       XSRETURN(1);
13596     }
13597     outp = outspec;
13598   }
13599   else {
13600     if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13601       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13602       ST(0) = &PL_sv_no;
13603       Safefree(inspec);
13604       Safefree(outspec);
13605       XSRETURN(1);
13606     }
13607   }
13608   date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13609 
13610   ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13611   Safefree(inspec);
13612   Safefree(outspec);
13613   XSRETURN(1);
13614 }
13615 
13616 /* The mod2fname is limited to shorter filenames by design, so it should
13617  * not be modified to support longer EFS pathnames
13618  */
13619 void
13620 mod2fname(pTHX_ CV *cv)
13621 {
13622   dXSARGS;
13623   char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13624        workbuff[NAM$C_MAXRSS*1 + 1];
13625   int total_namelen = 3, counter, num_entries;
13626   /* ODS-5 ups this, but we want to be consistent, so... */
13627   int max_name_len = 39;
13628   AV *in_array = (AV *)SvRV(ST(0));
13629 
13630   num_entries = av_len(in_array);
13631 
13632   /* All the names start with PL_. */
13633   strcpy(ultimate_name, "PL_");
13634 
13635   /* Clean up our working buffer */
13636   Zero(work_name, sizeof(work_name), char);
13637 
13638   /* Run through the entries and build up a working name */
13639   for(counter = 0; counter <= num_entries; counter++) {
13640     /* If it's not the first name then tack on a __ */
13641     if (counter) {
13642       strcat(work_name, "__");
13643     }
13644     strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13645   }
13646 
13647   /* Check to see if we actually have to bother...*/
13648   if (strlen(work_name) + 3 <= max_name_len) {
13649     strcat(ultimate_name, work_name);
13650   } else {
13651     /* It's too darned big, so we need to go strip. We use the same */
13652     /* algorithm as xsubpp does. First, strip out doubled __ */
13653     char *source, *dest, last;
13654     dest = workbuff;
13655     last = 0;
13656     for (source = work_name; *source; source++) {
13657       if (last == *source && last == '_') {
13658 	continue;
13659       }
13660       *dest++ = *source;
13661       last = *source;
13662     }
13663     /* Go put it back */
13664     strcpy(work_name, workbuff);
13665     /* Is it still too big? */
13666     if (strlen(work_name) + 3 > max_name_len) {
13667       /* Strip duplicate letters */
13668       last = 0;
13669       dest = workbuff;
13670       for (source = work_name; *source; source++) {
13671 	if (last == toupper(*source)) {
13672 	continue;
13673 	}
13674 	*dest++ = *source;
13675 	last = toupper(*source);
13676       }
13677       strcpy(work_name, workbuff);
13678     }
13679 
13680     /* Is it *still* too big? */
13681     if (strlen(work_name) + 3 > max_name_len) {
13682       /* Too bad, we truncate */
13683       work_name[max_name_len - 2] = 0;
13684     }
13685     strcat(ultimate_name, work_name);
13686   }
13687 
13688   /* Okay, return it */
13689   ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13690   XSRETURN(1);
13691 }
13692 
13693 void
13694 hushexit_fromperl(pTHX_ CV *cv)
13695 {
13696     dXSARGS;
13697 
13698     if (items > 0) {
13699         VMSISH_HUSHED = SvTRUE(ST(0));
13700     }
13701     ST(0) = boolSV(VMSISH_HUSHED);
13702     XSRETURN(1);
13703 }
13704 
13705 
13706 PerlIO *
13707 Perl_vms_start_glob
13708    (pTHX_ SV *tmpglob,
13709     IO *io)
13710 {
13711     PerlIO *fp;
13712     struct vs_str_st *rslt;
13713     char *vmsspec;
13714     char *rstr;
13715     char *begin, *cp;
13716     $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13717     PerlIO *tmpfp;
13718     STRLEN i;
13719     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13720     struct dsc$descriptor_vs rsdsc;
13721     unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13722     unsigned long hasver = 0, isunix = 0;
13723     unsigned long int lff_flags = 0;
13724     int rms_sts;
13725     int vms_old_glob = 1;
13726 
13727     if (!SvOK(tmpglob)) {
13728         SETERRNO(ENOENT,RMS$_FNF);
13729         return NULL;
13730     }
13731 
13732     vms_old_glob = !decc_filename_unix_report;
13733 
13734 #ifdef VMS_LONGNAME_SUPPORT
13735     lff_flags = LIB$M_FIL_LONG_NAMES;
13736 #endif
13737     /* The Newx macro will not allow me to assign a smaller array
13738      * to the rslt pointer, so we will assign it to the begin char pointer
13739      * and then copy the value into the rslt pointer.
13740      */
13741     Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13742     rslt = (struct vs_str_st *)begin;
13743     rslt->length = 0;
13744     rstr = &rslt->str[0];
13745     rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13746     rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13747     rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13748     rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13749 
13750     Newx(vmsspec, VMS_MAXRSS, char);
13751 
13752 	/* We could find out if there's an explicit dev/dir or version
13753 	   by peeking into lib$find_file's internal context at
13754 	   ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13755 	   but that's unsupported, so I don't want to do it now and
13756 	   have it bite someone in the future. */
13757 	/* Fix-me: vms_split_path() is the only way to do this, the
13758 	   existing method will fail with many legal EFS or UNIX specifications
13759 	 */
13760 
13761     cp = SvPV(tmpglob,i);
13762 
13763     for (; i; i--) {
13764 	if (cp[i] == ';') hasver = 1;
13765 	if (cp[i] == '.') {
13766 	    if (sts) hasver = 1;
13767 	    else sts = 1;
13768 	}
13769 	if (cp[i] == '/') {
13770 	    hasdir = isunix = 1;
13771 	    break;
13772 	}
13773 	if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13774 	    hasdir = 1;
13775 	    break;
13776 	}
13777     }
13778 
13779     /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13780     if ((hasdir == 0) && decc_filename_unix_report) {
13781         isunix = 1;
13782     }
13783 
13784     if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13785 	char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13786 	int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13787 	int wildstar = 0;
13788 	int wildquery = 0;
13789 	int found = 0;
13790 	Stat_t st;
13791 	int stat_sts;
13792 	stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13793 	if (!stat_sts && S_ISDIR(st.st_mode)) {
13794             char * vms_dir;
13795             const char * fname;
13796             STRLEN fname_len;
13797 
13798             /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13799             /* path delimiter of ':>]', if so, then the old behavior has */
13800             /* obviously been specificially requested */
13801 
13802             fname = SvPVX_const(tmpglob);
13803             fname_len = strlen(fname);
13804             vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13805             if (vms_old_glob || (vms_dir != NULL)) {
13806                 wilddsc.dsc$a_pointer = tovmspath_utf8(
13807                                             SvPVX(tmpglob),vmsspec,NULL);
13808                 ok = (wilddsc.dsc$a_pointer != NULL);
13809                 /* maybe passed 'foo' rather than '[.foo]', thus not
13810                    detected above */
13811                 hasdir = 1;
13812             } else {
13813                 /* Operate just on the directory, the special stat/fstat for */
13814                 /* leaves the fileified  specification in the st_devnam */
13815                 /* member. */
13816                 wilddsc.dsc$a_pointer = st.st_devnam;
13817                 ok = 1;
13818             }
13819 	}
13820 	else {
13821 	    wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13822 	    ok = (wilddsc.dsc$a_pointer != NULL);
13823 	}
13824 	if (ok)
13825 	    wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13826 
13827 	/* If not extended character set, replace ? with % */
13828 	/* With extended character set, ? is a wildcard single character */
13829 	for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13830 	    if (*cp == '?') {
13831                 wildquery = 1;
13832                 if (!decc_efs_case_preserve)
13833                     *cp = '%';
13834             } else if (*cp == '%') {
13835                 wildquery = 1;
13836             } else if (*cp == '*') {
13837                 wildstar = 1;
13838             }
13839 	}
13840 
13841         if (ok) {
13842             wv_sts = vms_split_path(
13843                 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13844                 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13845                 &wvs_spec, &wvs_len);
13846         } else {
13847             wn_spec = NULL;
13848             wn_len = 0;
13849             we_spec = NULL;
13850             we_len = 0;
13851         }
13852 
13853 	sts = SS$_NORMAL;
13854 	while (ok && $VMS_STATUS_SUCCESS(sts)) {
13855 	 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13856 	 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13857          int valid_find;
13858 
13859             valid_find = 0;
13860 	    sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13861 				&dfltdsc,NULL,&rms_sts,&lff_flags);
13862 	    if (!$VMS_STATUS_SUCCESS(sts))
13863 		break;
13864 
13865 	    /* with varying string, 1st word of buffer contains result length */
13866 	    rstr[rslt->length] = '\0';
13867 
13868 	     /* Find where all the components are */
13869 	     v_sts = vms_split_path
13870 		       (rstr,
13871 			&v_spec,
13872 			&v_len,
13873 			&r_spec,
13874 			&r_len,
13875 			&d_spec,
13876 			&d_len,
13877 			&n_spec,
13878 			&n_len,
13879 			&e_spec,
13880 			&e_len,
13881 			&vs_spec,
13882 			&vs_len);
13883 
13884 	    /* If no version on input, truncate the version on output */
13885 	    if (!hasver && (vs_len > 0)) {
13886 		*vs_spec = '\0';
13887 		vs_len = 0;
13888             }
13889 
13890             if (isunix) {
13891 
13892                 /* In Unix report mode, remove the ".dir;1" from the name */
13893                 /* if it is a real directory */
13894                 if (decc_filename_unix_report || decc_efs_charset) {
13895                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13896                         Stat_t statbuf;
13897                         int ret_sts;
13898 
13899                         ret_sts = flex_lstat(rstr, &statbuf);
13900                         if ((ret_sts == 0) &&
13901                             S_ISDIR(statbuf.st_mode)) {
13902                             e_len = 0;
13903                             e_spec[0] = 0;
13904                         }
13905                     }
13906                 }
13907 
13908 		/* No version & a null extension on UNIX handling */
13909 		if ((e_len == 1) && decc_readdir_dropdotnotype) {
13910 		    e_len = 0;
13911 		    *e_spec = '\0';
13912 		}
13913 	    }
13914 
13915 	    if (!decc_efs_case_preserve) {
13916 	        for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13917 	    }
13918 
13919             /* Find File treats a Null extension as return all extensions */
13920             /* This is contrary to Perl expectations */
13921 
13922             if (wildstar || wildquery || vms_old_glob) {
13923                 /* really need to see if the returned file name matched */
13924                 /* but for now will assume that it matches */
13925                 valid_find = 1;
13926             } else {
13927                 /* Exact Match requested */
13928                 /* How are directories handled? - like a file */
13929                 if ((e_len == we_len) && (n_len == wn_len)) {
13930                     int t1;
13931                     t1 = e_len;
13932                     if (t1 > 0)
13933                         t1 = strncmp(e_spec, we_spec, e_len);
13934                     if (t1 == 0) {
13935                        t1 = n_len;
13936                        if (t1 > 0)
13937                            t1 = strncmp(n_spec, we_spec, n_len);
13938                        if (t1 == 0)
13939                            valid_find = 1;
13940                     }
13941                 }
13942             }
13943 
13944             if (valid_find) {
13945 	        found++;
13946 
13947 	        if (hasdir) {
13948 		    if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13949 		    begin = rstr;
13950 	        }
13951 	        else {
13952 		    /* Start with the name */
13953 		    begin = n_spec;
13954 	        }
13955 	        strcat(begin,"\n");
13956 	        ok = (PerlIO_puts(tmpfp,begin) != EOF);
13957             }
13958 	}
13959 	if (cxt) (void)lib$find_file_end(&cxt);
13960 
13961 	if (!found) {
13962 	    /* Be POSIXish: return the input pattern when no matches */
13963 	    strcpy(rstr,SvPVX(tmpglob));
13964 	    strcat(rstr,"\n");
13965 	    ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13966 	}
13967 
13968 	if (ok && sts != RMS$_NMF &&
13969 	    sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13970 	if (!ok) {
13971 	    if (!(sts & 1)) {
13972 		SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13973 	    }
13974 	    PerlIO_close(tmpfp);
13975 	    fp = NULL;
13976 	}
13977 	else {
13978 	    PerlIO_rewind(tmpfp);
13979 	    IoTYPE(io) = IoTYPE_RDONLY;
13980 	    IoIFP(io) = fp = tmpfp;
13981 	    IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
13982 	}
13983     }
13984     Safefree(vmsspec);
13985     Safefree(rslt);
13986     return fp;
13987 }
13988 
13989 
13990 static char *
13991 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13992 		   int *utf8_fl);
13993 
13994 void
13995 unixrealpath_fromperl(pTHX_ CV *cv)
13996 {
13997     dXSARGS;
13998     char *fspec, *rslt_spec, *rslt;
13999     STRLEN n_a;
14000 
14001     if (!items || items != 1)
14002 	Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
14003 
14004     fspec = SvPV(ST(0),n_a);
14005     if (!fspec || !*fspec) XSRETURN_UNDEF;
14006 
14007     Newx(rslt_spec, VMS_MAXRSS + 1, char);
14008     rslt = do_vms_realpath(fspec, rslt_spec, NULL);
14009 
14010     ST(0) = sv_newmortal();
14011     if (rslt != NULL)
14012 	sv_usepvn(ST(0),rslt,strlen(rslt));
14013     else
14014 	Safefree(rslt_spec);
14015 	XSRETURN(1);
14016 }
14017 
14018 static char *
14019 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
14020 		   int *utf8_fl);
14021 
14022 void
14023 vmsrealpath_fromperl(pTHX_ CV *cv)
14024 {
14025     dXSARGS;
14026     char *fspec, *rslt_spec, *rslt;
14027     STRLEN n_a;
14028 
14029     if (!items || items != 1)
14030 	Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
14031 
14032     fspec = SvPV(ST(0),n_a);
14033     if (!fspec || !*fspec) XSRETURN_UNDEF;
14034 
14035     Newx(rslt_spec, VMS_MAXRSS + 1, char);
14036     rslt = do_vms_realname(fspec, rslt_spec, NULL);
14037 
14038     ST(0) = sv_newmortal();
14039     if (rslt != NULL)
14040 	sv_usepvn(ST(0),rslt,strlen(rslt));
14041     else
14042 	Safefree(rslt_spec);
14043 	XSRETURN(1);
14044 }
14045 
14046 #ifdef HAS_SYMLINK
14047 /*
14048  * A thin wrapper around decc$symlink to make sure we follow the
14049  * standard and do not create a symlink with a zero-length name.
14050  *
14051  * Also in ODS-2 mode, existing tests assume that the link target
14052  * will be converted to UNIX format.
14053  */
14054 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
14055 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
14056   if (!link_name || !*link_name) {
14057     SETERRNO(ENOENT, SS$_NOSUCHFILE);
14058     return -1;
14059   }
14060 
14061   if (decc_efs_charset) {
14062       return symlink(contents, link_name);
14063   } else {
14064       int sts;
14065       char * utarget;
14066 
14067       /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
14068       /* because in order to work, the symlink target must be in UNIX format */
14069 
14070       /* As symbolic links can hold things other than files, we will only do */
14071       /* the conversion in in ODS-2 mode */
14072 
14073       utarget = PerlMem_malloc(VMS_MAXRSS + 1);
14074       if (int_tounixspec(contents, utarget, NULL) == NULL) {
14075 
14076           /* This should not fail, as an untranslatable filename */
14077           /* should be passed through */
14078           utarget = (char *)contents;
14079       }
14080       sts = symlink(utarget, link_name);
14081       PerlMem_free(utarget);
14082       return sts;
14083   }
14084 
14085 }
14086 /*}}}*/
14087 
14088 #endif /* HAS_SYMLINK */
14089 
14090 int do_vms_case_tolerant(void);
14091 
14092 void
14093 case_tolerant_process_fromperl(pTHX_ CV *cv)
14094 {
14095   dXSARGS;
14096   ST(0) = boolSV(do_vms_case_tolerant());
14097   XSRETURN(1);
14098 }
14099 
14100 #ifdef USE_ITHREADS
14101 
14102 void
14103 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14104                           struct interp_intern *dst)
14105 {
14106     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14107 
14108     memcpy(dst,src,sizeof(struct interp_intern));
14109 }
14110 
14111 #endif
14112 
14113 void
14114 Perl_sys_intern_clear(pTHX)
14115 {
14116 }
14117 
14118 void
14119 Perl_sys_intern_init(pTHX)
14120 {
14121     unsigned int ix = RAND_MAX;
14122     double x;
14123 
14124     VMSISH_HUSHED = 0;
14125 
14126     MY_POSIX_EXIT = vms_posix_exit;
14127 
14128     x = (float)ix;
14129     MY_INV_RAND_MAX = 1./x;
14130 }
14131 
14132 void
14133 init_os_extras(void)
14134 {
14135   dTHX;
14136   char* file = __FILE__;
14137   if (decc_disable_to_vms_logname_translation) {
14138     no_translate_barewords = TRUE;
14139   } else {
14140     no_translate_barewords = FALSE;
14141   }
14142 
14143   newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14144   newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14145   newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14146   newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14147   newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14148   newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14149   newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14150   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14151   newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14152   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14153   newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14154   newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14155   newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14156   newXSproto("VMS::Filespec::case_tolerant_process",
14157       case_tolerant_process_fromperl,file,"");
14158 
14159   store_pipelocs(aTHX);         /* will redo any earlier attempts */
14160 
14161   return;
14162 }
14163 
14164 #if __CRTL_VER == 80200000
14165 /* This missed getting in to the DECC SDK for 8.2 */
14166 char *realpath(const char *file_name, char * resolved_name, ...);
14167 #endif
14168 
14169 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14170 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14171  * The perl fallback routine to provide realpath() is not as efficient
14172  * on OpenVMS.
14173  */
14174 
14175 /* Hack, use old stat() as fastest way of getting ino_t and device */
14176 int decc$stat(const char *name, void * statbuf);
14177 #if !defined(__VAX) && __CRTL_VER >= 80200000
14178 int decc$lstat(const char *name, void * statbuf);
14179 #else
14180 #define decc$lstat decc$stat
14181 #endif
14182 
14183 
14184 /* Realpath is fragile.  In 8.3 it does not work if the feature
14185  * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14186  * links are implemented in RMS, not the CRTL. It also can fail if the
14187  * user does not have read/execute access to some of the directories.
14188  * So in order for Do What I Mean mode to work, if realpath() fails,
14189  * fall back to looking up the filename by the device name and FID.
14190  */
14191 
14192 int vms_fid_to_name(char * outname, int outlen,
14193                     const char * name, int lstat_flag, mode_t * mode)
14194 {
14195 #pragma message save
14196 #pragma message disable MISALGNDSTRCT
14197 #pragma message disable MISALGNDMEM
14198 #pragma member_alignment save
14199 #pragma nomember_alignment
14200 struct statbuf_t {
14201     char	   * st_dev;
14202     unsigned short st_ino[3];
14203     unsigned short old_st_mode;
14204     unsigned long  padl[30];  /* plenty of room */
14205 } statbuf;
14206 #pragma message restore
14207 #pragma member_alignment restore
14208 
14209     int sts;
14210     struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14211     struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14212     char *fileified;
14213     char *temp_fspec;
14214     char *ret_spec;
14215 
14216     /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14217      * unexpected answers
14218      */
14219 
14220     fileified = PerlMem_malloc(VMS_MAXRSS);
14221     if (fileified == NULL)
14222         _ckvmssts_noperl(SS$_INSFMEM);
14223 
14224     temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14225     if (temp_fspec == NULL)
14226         _ckvmssts_noperl(SS$_INSFMEM);
14227 
14228     sts = -1;
14229     /* First need to try as a directory */
14230     ret_spec = int_tovmspath(name, temp_fspec, NULL);
14231     if (ret_spec != NULL) {
14232         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14233         if (ret_spec != NULL) {
14234             if (lstat_flag == 0)
14235                 sts = decc$stat(fileified, &statbuf);
14236             else
14237                 sts = decc$lstat(fileified, &statbuf);
14238         }
14239     }
14240 
14241     /* Then as a VMS file spec */
14242     if (sts != 0) {
14243         ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14244         if (ret_spec != NULL) {
14245             if (lstat_flag == 0) {
14246                 sts = decc$stat(temp_fspec, &statbuf);
14247             } else {
14248                 sts = decc$lstat(temp_fspec, &statbuf);
14249             }
14250         }
14251     }
14252 
14253     if (sts) {
14254         /* Next try - allow multiple dots with out EFS CHARSET */
14255         /* The CRTL stat() falls down hard on multi-dot filenames in unix
14256          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14257          * enable it if it isn't already.
14258          */
14259 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14260         if (!decc_efs_charset && (decc_efs_charset_index > 0))
14261             decc$feature_set_value(decc_efs_charset_index, 1, 1);
14262 #endif
14263         ret_spec = int_tovmspath(name, temp_fspec, NULL);
14264         if (lstat_flag == 0) {
14265             sts = decc$stat(name, &statbuf);
14266         } else {
14267             sts = decc$lstat(name, &statbuf);
14268         }
14269 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14270         if (!decc_efs_charset && (decc_efs_charset_index > 0))
14271             decc$feature_set_value(decc_efs_charset_index, 1, 0);
14272 #endif
14273     }
14274 
14275 
14276     /* and then because the Perl Unix to VMS conversion is not perfect */
14277     /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14278     /* characters from filenames so we need to try it as-is */
14279     if (sts) {
14280         if (lstat_flag == 0) {
14281             sts = decc$stat(name, &statbuf);
14282         } else {
14283             sts = decc$lstat(name, &statbuf);
14284         }
14285     }
14286 
14287     if (sts == 0) {
14288         int vms_sts;
14289 
14290 	dvidsc.dsc$a_pointer=statbuf.st_dev;
14291         dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14292 
14293 	specdsc.dsc$a_pointer = outname;
14294 	specdsc.dsc$w_length = outlen-1;
14295 
14296         vms_sts = lib$fid_to_name
14297 	    (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14298         if ($VMS_STATUS_SUCCESS(vms_sts)) {
14299 	    outname[specdsc.dsc$w_length] = 0;
14300 
14301             /* Return the mode */
14302             if (mode) {
14303                 *mode = statbuf.old_st_mode;
14304             }
14305 	    return 0;
14306 	}
14307     }
14308     return sts;
14309 }
14310 
14311 
14312 
14313 static char *
14314 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14315 		   int *utf8_fl)
14316 {
14317     char * rslt = NULL;
14318 
14319 #ifdef HAS_SYMLINK
14320     if (decc_posix_compliant_pathnames > 0 ) {
14321 	/* realpath currently only works if posix compliant pathnames are
14322 	 * enabled.  It may start working when they are not, but in that
14323 	 * case we still want the fallback behavior for backwards compatibility
14324 	 */
14325         rslt = realpath(filespec, outbuf);
14326     }
14327 #endif
14328 
14329     if (rslt == NULL) {
14330         char * vms_spec;
14331         char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14332         int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14333         int file_len;
14334         mode_t my_mode;
14335 
14336 	/* Fall back to fid_to_name */
14337 
14338         Newx(vms_spec, VMS_MAXRSS + 1, char);
14339 
14340 	sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14341 	if (sts == 0) {
14342 
14343 
14344 	    /* Now need to trim the version off */
14345 	    sts = vms_split_path
14346 		  (vms_spec,
14347 		   &v_spec,
14348 		   &v_len,
14349 		   &r_spec,
14350 		   &r_len,
14351 		   &d_spec,
14352 		   &d_len,
14353 		   &n_spec,
14354 		   &n_len,
14355 		   &e_spec,
14356 		   &e_len,
14357 		   &vs_spec,
14358 		   &vs_len);
14359 
14360 
14361 		if (sts == 0) {
14362 	            int haslower = 0;
14363 	            const char *cp;
14364 
14365 	            /* Trim off the version */
14366 	            int file_len = v_len + r_len + d_len + n_len + e_len;
14367 	            vms_spec[file_len] = 0;
14368 
14369 	            /* Trim off the .DIR if this is a directory */
14370 	            if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
14371                         if (S_ISDIR(my_mode)) {
14372                             e_len = 0;
14373                             e_spec[0] = 0;
14374                         }
14375 	            }
14376 
14377 	            /* Drop NULL extensions on UNIX file specification */
14378 		    if ((e_len == 1) && decc_readdir_dropdotnotype) {
14379 			e_len = 0;
14380 			e_spec[0] = '\0';
14381 		    }
14382 
14383 	            /* The result is expected to be in UNIX format */
14384 		    rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14385 
14386                     /* Downcase if input had any lower case letters and
14387 	             * case preservation is not in effect.
14388 	             */
14389 	            if (!decc_efs_case_preserve) {
14390 	                for (cp = filespec; *cp; cp++)
14391 	                    if (islower(*cp)) { haslower = 1; break; }
14392 
14393 	                if (haslower) __mystrtolower(rslt);
14394 	            }
14395 	        }
14396 	} else {
14397 
14398 	    /* Now for some hacks to deal with backwards and forward */
14399 	    /* compatibilty */
14400 	    if (!decc_efs_charset) {
14401 
14402 		/* 1. ODS-2 mode wants to do a syntax only translation */
14403 		rslt = int_rmsexpand(filespec, outbuf,
14404 				    NULL, 0, NULL, utf8_fl);
14405 
14406 	    } else {
14407 		if (decc_filename_unix_report) {
14408 		    char * dir_name;
14409 		    char * vms_dir_name;
14410 		    char * file_name;
14411 
14412 		    /* 2. ODS-5 / UNIX report mode should return a failure */
14413 		    /*    if the parent directory also does not exist */
14414 		    /*    Otherwise, get the real path for the parent */
14415 		    /*    and add the child to it.
14416 
14417 		    /* basename / dirname only available for VMS 7.0+ */
14418 		    /* So we may need to implement them as common routines */
14419 
14420 		    Newx(dir_name, VMS_MAXRSS + 1, char);
14421 		    Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14422 		    dir_name[0] = '\0';
14423 		    file_name = NULL;
14424 
14425 		    /* First try a VMS parse */
14426 		    sts = vms_split_path
14427 			  (filespec,
14428 			   &v_spec,
14429 			   &v_len,
14430 			   &r_spec,
14431 			   &r_len,
14432 			   &d_spec,
14433 			   &d_len,
14434 			   &n_spec,
14435 			   &n_len,
14436 			   &e_spec,
14437 			   &e_len,
14438 			   &vs_spec,
14439 			   &vs_len);
14440 
14441 		    if (sts == 0) {
14442 			/* This is VMS */
14443 
14444 			int dir_len = v_len + r_len + d_len + n_len;
14445 			if (dir_len > 0) {
14446 			   strncpy(dir_name, filespec, dir_len);
14447 			   dir_name[dir_len] = '\0';
14448 			   file_name = (char *)&filespec[dir_len + 1];
14449 			}
14450 		    } else {
14451 			/* This must be UNIX */
14452 			char * tchar;
14453 
14454 			tchar = strrchr(filespec, '/');
14455 
14456 			if (tchar != NULL) {
14457 			    int dir_len = tchar - filespec;
14458 			    strncpy(dir_name, filespec, dir_len);
14459 			    dir_name[dir_len] = '\0';
14460 			    file_name = (char *) &filespec[dir_len + 1];
14461 			}
14462 		    }
14463 
14464 		    /* Dir name is defaulted */
14465 		    if (dir_name[0] == 0) {
14466 			dir_name[0] = '.';
14467 			dir_name[1] = '\0';
14468 		    }
14469 
14470 		    /* Need realpath for the directory */
14471 		    sts = vms_fid_to_name(vms_dir_name,
14472 					  VMS_MAXRSS + 1,
14473 					  dir_name, 0, NULL);
14474 
14475 		    if (sts == 0) {
14476 		        /* Now need to pathify it.
14477 		        char *tdir = int_pathify_dirspec(vms_dir_name,
14478 							 outbuf);
14479 
14480 			/* And now add the original filespec to it */
14481 			if (file_name != NULL) {
14482 			    strcat(outbuf, file_name);
14483 			}
14484 			return outbuf;
14485 		    }
14486 		    Safefree(vms_dir_name);
14487 		    Safefree(dir_name);
14488 		}
14489             }
14490         }
14491         Safefree(vms_spec);
14492     }
14493     return rslt;
14494 }
14495 
14496 static char *
14497 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14498 		   int *utf8_fl)
14499 {
14500     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14501     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14502     int file_len;
14503 
14504     /* Fall back to fid_to_name */
14505 
14506     sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14507     if (sts != 0) {
14508 	return NULL;
14509     }
14510     else {
14511 
14512 
14513 	/* Now need to trim the version off */
14514 	sts = vms_split_path
14515 		  (outbuf,
14516 		   &v_spec,
14517 		   &v_len,
14518 		   &r_spec,
14519 		   &r_len,
14520 		   &d_spec,
14521 		   &d_len,
14522 		   &n_spec,
14523 		   &n_len,
14524 		   &e_spec,
14525 		   &e_len,
14526 		   &vs_spec,
14527 		   &vs_len);
14528 
14529 
14530 	if (sts == 0) {
14531 	    int haslower = 0;
14532 	    const char *cp;
14533 
14534 	    /* Trim off the version */
14535 	    int file_len = v_len + r_len + d_len + n_len + e_len;
14536 	    outbuf[file_len] = 0;
14537 
14538 	    /* Downcase if input had any lower case letters and
14539 	     * case preservation is not in effect.
14540 	     */
14541 	    if (!decc_efs_case_preserve) {
14542 	        for (cp = filespec; *cp; cp++)
14543 	            if (islower(*cp)) { haslower = 1; break; }
14544 
14545 	        if (haslower) __mystrtolower(outbuf);
14546 	    }
14547 	}
14548     }
14549     return outbuf;
14550 }
14551 
14552 
14553 /*}}}*/
14554 /* External entry points */
14555 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14556 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14557 
14558 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14559 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14560 
14561 /* case_tolerant */
14562 
14563 /*{{{int do_vms_case_tolerant(void)*/
14564 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14565  * controlled by a process setting.
14566  */
14567 int do_vms_case_tolerant(void)
14568 {
14569     return vms_process_case_tolerant;
14570 }
14571 /*}}}*/
14572 /* External entry points */
14573 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14574 int Perl_vms_case_tolerant(void)
14575 { return do_vms_case_tolerant(); }
14576 #else
14577 int Perl_vms_case_tolerant(void)
14578 { return vms_process_case_tolerant; }
14579 #endif
14580 
14581 
14582  /* Start of DECC RTL Feature handling */
14583 
14584 static int sys_trnlnm
14585    (const char * logname,
14586     char * value,
14587     int value_len)
14588 {
14589     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14590     const unsigned long attr = LNM$M_CASE_BLIND;
14591     struct dsc$descriptor_s name_dsc;
14592     int status;
14593     unsigned short result;
14594     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14595                                 {0, 0, 0, 0}};
14596 
14597     name_dsc.dsc$w_length = strlen(logname);
14598     name_dsc.dsc$a_pointer = (char *)logname;
14599     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14600     name_dsc.dsc$b_class = DSC$K_CLASS_S;
14601 
14602     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14603 
14604     if ($VMS_STATUS_SUCCESS(status)) {
14605 
14606 	 /* Null terminate and return the string */
14607 	/*--------------------------------------*/
14608 	value[result] = 0;
14609     }
14610 
14611     return status;
14612 }
14613 
14614 static int sys_crelnm
14615    (const char * logname,
14616     const char * value)
14617 {
14618     int ret_val;
14619     const char * proc_table = "LNM$PROCESS_TABLE";
14620     struct dsc$descriptor_s proc_table_dsc;
14621     struct dsc$descriptor_s logname_dsc;
14622     struct itmlst_3 item_list[2];
14623 
14624     proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14625     proc_table_dsc.dsc$w_length = strlen(proc_table);
14626     proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14627     proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14628 
14629     logname_dsc.dsc$a_pointer = (char *) logname;
14630     logname_dsc.dsc$w_length = strlen(logname);
14631     logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14632     logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14633 
14634     item_list[0].buflen = strlen(value);
14635     item_list[0].itmcode = LNM$_STRING;
14636     item_list[0].bufadr = (char *)value;
14637     item_list[0].retlen = NULL;
14638 
14639     item_list[1].buflen = 0;
14640     item_list[1].itmcode = 0;
14641 
14642     ret_val = sys$crelnm
14643 		       (NULL,
14644 			(const struct dsc$descriptor_s *)&proc_table_dsc,
14645 			(const struct dsc$descriptor_s *)&logname_dsc,
14646 			NULL,
14647 			(const struct item_list_3 *) item_list);
14648 
14649     return ret_val;
14650 }
14651 
14652 /* C RTL Feature settings */
14653 
14654 static int set_features
14655    (int (* init_coroutine)(int *, int *, void *),  /* Needs casts if used */
14656     int (* cli_routine)(void),	/* Not documented */
14657     void *image_info)		/* Not documented */
14658 {
14659     int status;
14660     int s;
14661     char* str;
14662     char val_str[10];
14663 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14664     const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14665     const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14666     unsigned long case_perm;
14667     unsigned long case_image;
14668 #endif
14669 
14670     /* Allow an exception to bring Perl into the VMS debugger */
14671     vms_debug_on_exception = 0;
14672     status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14673     if ($VMS_STATUS_SUCCESS(status)) {
14674        val_str[0] = _toupper(val_str[0]);
14675        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14676 	 vms_debug_on_exception = 1;
14677        else
14678 	 vms_debug_on_exception = 0;
14679     }
14680 
14681     /* Debug unix/vms file translation routines */
14682     vms_debug_fileify = 0;
14683     status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14684     if ($VMS_STATUS_SUCCESS(status)) {
14685 	val_str[0] = _toupper(val_str[0]);
14686         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14687 	    vms_debug_fileify = 1;
14688         else
14689 	    vms_debug_fileify = 0;
14690     }
14691 
14692 
14693     /* Historically PERL has been doing vmsify / stat differently than */
14694     /* the CRTL.  In particular, under some conditions the CRTL will   */
14695     /* remove some illegal characters like spaces from filenames       */
14696     /* resulting in some differences.  The stat()/lstat() wrapper has  */
14697     /* been reporting such file names as invalid and fails to stat them */
14698     /* fixing this bug so that stat()/lstat() accept these like the     */
14699     /* CRTL does will result in several tests failing.                  */
14700     /* This should really be fixed, but for now, set up a feature to    */
14701     /* enable it so that the impact can be studied.                     */
14702     vms_bug_stat_filename = 0;
14703     status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14704     if ($VMS_STATUS_SUCCESS(status)) {
14705 	val_str[0] = _toupper(val_str[0]);
14706         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14707 	    vms_bug_stat_filename = 1;
14708         else
14709 	    vms_bug_stat_filename = 0;
14710     }
14711 
14712 
14713     /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14714     vms_vtf7_filenames = 0;
14715     status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14716     if ($VMS_STATUS_SUCCESS(status)) {
14717        val_str[0] = _toupper(val_str[0]);
14718        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14719 	 vms_vtf7_filenames = 1;
14720        else
14721 	 vms_vtf7_filenames = 0;
14722     }
14723 
14724     /* unlink all versions on unlink() or rename() */
14725     vms_unlink_all_versions = 0;
14726     status = sys_trnlnm
14727 	("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14728     if ($VMS_STATUS_SUCCESS(status)) {
14729        val_str[0] = _toupper(val_str[0]);
14730        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14731 	 vms_unlink_all_versions = 1;
14732        else
14733 	 vms_unlink_all_versions = 0;
14734     }
14735 
14736     /* Dectect running under GNV Bash or other UNIX like shell */
14737 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14738     gnv_unix_shell = 0;
14739     status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14740     if ($VMS_STATUS_SUCCESS(status)) {
14741 	 gnv_unix_shell = 1;
14742 	 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14743 	 set_feature_default("DECC$EFS_CHARSET", 1);
14744 	 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14745 	 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14746 	 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14747 	 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14748 	 vms_unlink_all_versions = 1;
14749 	 vms_posix_exit = 1;
14750     }
14751 #endif
14752 
14753     /* hacks to see if known bugs are still present for testing */
14754 
14755     /* PCP mode requires creating /dev/null special device file */
14756     decc_bug_devnull = 0;
14757     status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14758     if ($VMS_STATUS_SUCCESS(status)) {
14759        val_str[0] = _toupper(val_str[0]);
14760        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14761           decc_bug_devnull = 1;
14762        else
14763 	  decc_bug_devnull = 0;
14764     }
14765 
14766     /* UNIX directory names with no paths are broken in a lot of places */
14767     decc_dir_barename = 1;
14768     status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14769     if ($VMS_STATUS_SUCCESS(status)) {
14770       val_str[0] = _toupper(val_str[0]);
14771       if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14772 	decc_dir_barename = 1;
14773       else
14774 	decc_dir_barename = 0;
14775     }
14776 
14777 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14778     s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14779     if (s >= 0) {
14780 	decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14781 	if (decc_disable_to_vms_logname_translation < 0)
14782 	    decc_disable_to_vms_logname_translation = 0;
14783     }
14784 
14785     s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14786     if (s >= 0) {
14787 	decc_efs_case_preserve = decc$feature_get_value(s, 1);
14788 	if (decc_efs_case_preserve < 0)
14789 	    decc_efs_case_preserve = 0;
14790     }
14791 
14792     s = decc$feature_get_index("DECC$EFS_CHARSET");
14793     decc_efs_charset_index = s;
14794     if (s >= 0) {
14795 	decc_efs_charset = decc$feature_get_value(s, 1);
14796 	if (decc_efs_charset < 0)
14797 	    decc_efs_charset = 0;
14798     }
14799 
14800     s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14801     if (s >= 0) {
14802 	decc_filename_unix_report = decc$feature_get_value(s, 1);
14803 	if (decc_filename_unix_report > 0) {
14804 	    decc_filename_unix_report = 1;
14805 	    vms_posix_exit = 1;
14806 	}
14807 	else
14808 	    decc_filename_unix_report = 0;
14809     }
14810 
14811     s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14812     if (s >= 0) {
14813 	decc_filename_unix_only = decc$feature_get_value(s, 1);
14814 	if (decc_filename_unix_only > 0) {
14815 	    decc_filename_unix_only = 1;
14816 	}
14817 	else {
14818 	    decc_filename_unix_only = 0;
14819 	}
14820     }
14821 
14822     s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14823     if (s >= 0) {
14824 	decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14825 	if (decc_filename_unix_no_version < 0)
14826 	    decc_filename_unix_no_version = 0;
14827     }
14828 
14829     s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14830     if (s >= 0) {
14831 	decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14832 	if (decc_readdir_dropdotnotype < 0)
14833 	    decc_readdir_dropdotnotype = 0;
14834     }
14835 
14836 #if __CRTL_VER >= 80200000
14837     s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14838     if (s >= 0) {
14839 	decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14840 	if (decc_posix_compliant_pathnames < 0)
14841 	    decc_posix_compliant_pathnames = 0;
14842 	if (decc_posix_compliant_pathnames > 4)
14843 	    decc_posix_compliant_pathnames = 0;
14844     }
14845 
14846 #endif
14847 #else
14848     status = sys_trnlnm
14849 	("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14850     if ($VMS_STATUS_SUCCESS(status)) {
14851 	val_str[0] = _toupper(val_str[0]);
14852 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14853 	   decc_disable_to_vms_logname_translation = 1;
14854 	}
14855     }
14856 
14857 #ifndef __VAX
14858     status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14859     if ($VMS_STATUS_SUCCESS(status)) {
14860 	val_str[0] = _toupper(val_str[0]);
14861 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14862 	   decc_efs_case_preserve = 1;
14863 	}
14864     }
14865 #endif
14866 
14867     status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14868     if ($VMS_STATUS_SUCCESS(status)) {
14869 	val_str[0] = _toupper(val_str[0]);
14870 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14871 	   decc_filename_unix_report = 1;
14872 	}
14873     }
14874     status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14875     if ($VMS_STATUS_SUCCESS(status)) {
14876 	val_str[0] = _toupper(val_str[0]);
14877 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14878 	   decc_filename_unix_only = 1;
14879 	   decc_filename_unix_report = 1;
14880 	}
14881     }
14882     status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14883     if ($VMS_STATUS_SUCCESS(status)) {
14884 	val_str[0] = _toupper(val_str[0]);
14885 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14886 	   decc_filename_unix_no_version = 1;
14887 	}
14888     }
14889     status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14890     if ($VMS_STATUS_SUCCESS(status)) {
14891 	val_str[0] = _toupper(val_str[0]);
14892 	if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14893 	   decc_readdir_dropdotnotype = 1;
14894 	}
14895     }
14896 #endif
14897 
14898 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14899 
14900      /* Report true case tolerance */
14901     /*----------------------------*/
14902     status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14903     if (!$VMS_STATUS_SUCCESS(status))
14904 	case_perm = PPROP$K_CASE_BLIND;
14905     status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14906     if (!$VMS_STATUS_SUCCESS(status))
14907 	case_image = PPROP$K_CASE_BLIND;
14908     if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14909 	(case_image == PPROP$K_CASE_SENSITIVE))
14910 	vms_process_case_tolerant = 0;
14911 
14912 #endif
14913 
14914     /* USE POSIX/DCL Exit codes - Recommended, but needs to default to  */
14915     /* for strict backward compatibilty */
14916     status = sys_trnlnm
14917 	("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14918     if ($VMS_STATUS_SUCCESS(status)) {
14919        val_str[0] = _toupper(val_str[0]);
14920        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14921 	 vms_posix_exit = 1;
14922        else
14923 	 vms_posix_exit = 0;
14924     }
14925 
14926 
14927     /* CRTL can be initialized past this point, but not before. */
14928 /*    DECC$CRTL_INIT(); */
14929 
14930     return SS$_NORMAL;
14931 }
14932 
14933 #ifdef __DECC
14934 #pragma nostandard
14935 #pragma extern_model save
14936 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14937 	const __align (LONGWORD) int spare[8] = {0};
14938 
14939 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14940 #if __DECC_VER >= 60560002
14941 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14942 #else
14943 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14944 #endif
14945 #endif /* __DECC */
14946 
14947 const long vms_cc_features = (const long)set_features;
14948 
14949 /*
14950 ** Force a reference to LIB$INITIALIZE to ensure it
14951 ** exists in the image.
14952 */
14953 int lib$initialize(void);
14954 #ifdef __DECC
14955 #pragma extern_model strict_refdef
14956 #endif
14957     int lib_init_ref = (int) lib$initialize;
14958 
14959 #ifdef __DECC
14960 #pragma extern_model restore
14961 #pragma standard
14962 #endif
14963 
14964 /*  End of vms.c */
14965