This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
POSIX.xs: White space only
[perl5.git] / vms / vms.c
1 /*    vms.c
2  *
3  *    VMS-specific routines for perl5
4  *
5  *    Copyright (C) 1993-2015 by Charles Bailey and others.
6  *
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9  */
10
11 /*
12  *   Yet small as was their hunted band
13  *   still fell and fearless was each hand,
14  *   and strong deeds they wrought yet oft,
15  *   and loved the woods, whose ways more soft
16  *   them seemed than thralls of that black throne
17  *   to live and languish in halls of stone.
18  *        "The Lay of Leithian", Canto II, lines 135-40
19  *
20  *     [p.162 of _The Lays of Beleriand_]
21  */
22  
23 #include <acedef.h>
24 #include <acldef.h>
25 #include <armdef.h>
26 #include <chpdef.h>
27 #include <clidef.h>
28 #include <climsgdef.h>
29 #include <dcdef.h>
30 #include <descrip.h>
31 #include <devdef.h>
32 #include <dvidef.h>
33 #include <float.h>
34 #include <fscndef.h>
35 #include <iodef.h>
36 #include <jpidef.h>
37 #include <kgbdef.h>
38 #include <libclidef.h>
39 #include <libdef.h>
40 #include <lib$routines.h>
41 #include <lnmdef.h>
42 #include <ossdef.h>
43 #include <ppropdef.h>
44 #include <prvdef.h>
45 #include <pscandef.h>
46 #include <psldef.h>
47 #include <rms.h>
48 #include <shrdef.h>
49 #include <ssdef.h>
50 #include <starlet.h>
51 #include <strdef.h>
52 #include <str$routines.h>
53 #include <syidef.h>
54 #include <uaidef.h>
55 #include <uicdef.h>
56 #include <stsdef.h>
57 #include <efndef.h>
58 #define NO_EFN EFN$C_ENF
59
60 #include <unixlib.h>
61
62 #pragma member_alignment save
63 #pragma nomember_alignment longword
64 struct item_list_3 {
65         unsigned short len;
66         unsigned short code;
67         void * bufadr;
68         unsigned short * retadr;
69 };
70 #pragma member_alignment restore
71
72 /* Older versions of ssdef.h don't have these */
73 #ifndef SS$_INVFILFOROP
74 #  define SS$_INVFILFOROP 3930
75 #endif
76 #ifndef SS$_NOSUCHOBJECT
77 #  define SS$_NOSUCHOBJECT 2696
78 #endif
79
80 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
81 #define PERLIO_NOT_STDIO 0 
82
83 /* Don't replace system definitions of vfork, getenv, lstat, and stat, 
84  * code below needs to get to the underlying CRTL routines. */
85 #define DONT_MASK_RTL_CALLS
86 #include "EXTERN.h"
87 #include "perl.h"
88 #include "XSUB.h"
89 /* Anticipating future expansion in lexical warnings . . . */
90 #ifndef WARN_INTERNAL
91 #  define WARN_INTERNAL WARN_MISC
92 #endif
93
94 #ifdef VMS_LONGNAME_SUPPORT
95 #include <libfildef.h>
96 #endif
97
98 #if __CRTL_VER >= 80200000
99 #ifdef lstat
100 #undef lstat
101 #endif
102 #else
103 #ifdef lstat
104 #undef lstat
105 #endif
106 #define lstat(_x, _y) stat(_x, _y)
107 #endif
108
109 /* Routine to create a decterm for use with the Perl debugger */
110 /* No headers, this information was found in the Programming Concepts Manual */
111
112 static int (*decw_term_port)
113    (const struct dsc$descriptor_s * display,
114     const struct dsc$descriptor_s * setup_file,
115     const struct dsc$descriptor_s * customization,
116     struct dsc$descriptor_s * result_device_name,
117     unsigned short * result_device_name_length,
118     void * controller,
119     void * char_buffer,
120     void * char_change_buffer) = 0;
121
122 #if defined(NEED_AN_H_ERRNO)
123 dEXT int h_errno;
124 #endif
125
126 #if defined(__DECC) || defined(__DECCXX)
127 #pragma member_alignment save
128 #pragma nomember_alignment longword
129 #pragma message save
130 #pragma message disable misalgndmem
131 #endif
132 struct itmlst_3 {
133   unsigned short int buflen;
134   unsigned short int itmcode;
135   void *bufadr;
136   unsigned short int *retlen;
137 };
138
139 struct filescan_itmlst_2 {
140     unsigned short length;
141     unsigned short itmcode;
142     char * component;
143 };
144
145 struct vs_str_st {
146     unsigned short length;
147     char str[VMS_MAXRSS];
148     unsigned short pad; /* for longword struct alignment */
149 };
150
151 #if defined(__DECC) || defined(__DECCXX)
152 #pragma message restore
153 #pragma member_alignment restore
154 #endif
155
156 #define do_fileify_dirspec(a,b,c,d)     mp_do_fileify_dirspec(aTHX_ a,b,c,d)
157 #define do_pathify_dirspec(a,b,c,d)     mp_do_pathify_dirspec(aTHX_ a,b,c,d)
158 #define do_tovmsspec(a,b,c,d)           mp_do_tovmsspec(aTHX_ a,b,c,0,d)
159 #define do_tovmspath(a,b,c,d)           mp_do_tovmspath(aTHX_ a,b,c,d)
160 #define do_rmsexpand(a,b,c,d,e,f,g)     mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
161 #define do_vms_realpath(a,b,c)          mp_do_vms_realpath(aTHX_ a,b,c)
162 #define do_vms_realname(a,b,c)          mp_do_vms_realname(aTHX_ a,b,c)
163 #define do_tounixspec(a,b,c,d)          mp_do_tounixspec(aTHX_ a,b,c,d)
164 #define do_tounixpath(a,b,c,d)          mp_do_tounixpath(aTHX_ a,b,c,d)
165 #define do_vms_case_tolerant(a)         mp_do_vms_case_tolerant(a)
166 #define expand_wild_cards(a,b,c,d)      mp_expand_wild_cards(aTHX_ a,b,c,d)
167 #define getredirection(a,b)             mp_getredirection(aTHX_ a,b)
168
169 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
170 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
171 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
172 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
173
174 static char *  int_rmsexpand_vms(
175     const char * filespec, char * outbuf, unsigned opts);
176 static char * int_rmsexpand_tovms(
177     const char * filespec, char * outbuf, unsigned opts);
178 static char *int_tovmsspec
179    (const char *path, char *buf, int dir_flag, int * utf8_flag);
180 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
181 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
182 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
183
184 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
185 #define PERL_LNM_MAX_ALLOWED_INDEX 127
186
187 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
188  * depending on the facility.  SHOW LOGICAL does 10, so we'll imitate that for
189  * the Perl facility.
190  */
191 #define PERL_LNM_MAX_ITER 10
192
193   /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
194 #define MAX_DCL_SYMBOL          (8192)
195 #define MAX_DCL_LINE_LENGTH     (4096 - 4)
196
197 static char *__mystrtolower(char *str)
198 {
199   if (str) for (; *str; ++str) *str= toLOWER_L1(*str);
200   return str;
201 }
202
203 static struct dsc$descriptor_s fildevdsc = 
204   { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
205 static struct dsc$descriptor_s crtlenvdsc = 
206   { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
207 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
208 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
209 static struct dsc$descriptor_s **env_tables = defenv;
210 static bool will_taint = FALSE;  /* tainting active, but no PL_curinterp yet */
211
212 /* True if we shouldn't treat barewords as logicals during directory */
213 /* munching */ 
214 static int no_translate_barewords;
215
216 /* DECC feature indexes.  We grab the indexes at start-up
217  * time for later use with decc$feature_get_value.
218  */
219 static int disable_to_vms_logname_translation_index = -1;
220 static int disable_posix_root_index = -1;
221 static int efs_case_preserve_index = -1;
222 static int efs_charset_index = -1;
223 static int filename_unix_no_version_index = -1;
224 static int filename_unix_only_index = -1;
225 static int filename_unix_report_index = -1;
226 static int posix_compliant_pathnames_index = -1;
227 static int readdir_dropdotnotype_index = -1;
228
229 #define DECC_DISABLE_TO_VMS_LOGNAME_TRANSLATION \
230     (decc$feature_get_value(disable_to_vms_logname_translation_index,__FEATURE_MODE_CURVAL)>0)
231 #define DECC_DISABLE_POSIX_ROOT  \
232     (decc$feature_get_value(disable_posix_root_index,__FEATURE_MODE_CURVAL)>0)
233 #define DECC_EFS_CASE_PRESERVE  \
234     (decc$feature_get_value(efs_case_preserve_index,__FEATURE_MODE_CURVAL)>0)
235 #define DECC_EFS_CHARSET  \
236     (decc$feature_get_value(efs_charset_index,__FEATURE_MODE_CURVAL)>0)
237 #define DECC_FILENAME_UNIX_NO_VERSION  \
238     (decc$feature_get_value(filename_unix_no_version_index,__FEATURE_MODE_CURVAL)>0)
239 #define DECC_FILENAME_UNIX_ONLY  \
240     (decc$feature_get_value(filename_unix_only_index,__FEATURE_MODE_CURVAL)>0)
241 #define DECC_FILENAME_UNIX_REPORT  \
242     (decc$feature_get_value(filename_unix_report_index,__FEATURE_MODE_CURVAL)>0)
243 #define DECC_POSIX_COMPLIANT_PATHNAMES   \
244     (decc$feature_get_value(posix_compliant_pathnames_index,__FEATURE_MODE_CURVAL)>0)
245 #define DECC_READDIR_DROPDOTNOTYPE  \
246     (decc$feature_get_value(readdir_dropdotnotype_index,__FEATURE_MODE_CURVAL)>0)
247
248 static int vms_process_case_tolerant = 1;
249 int vms_vtf7_filenames = 0;
250 int gnv_unix_shell = 0;
251 static int vms_unlink_all_versions = 0;
252 static int vms_posix_exit = 0;
253
254 /* bug workarounds if needed */
255 int decc_bug_devnull = 1;
256 int vms_bug_stat_filename = 0;
257
258 static int vms_debug_on_exception = 0;
259 static int vms_debug_fileify = 0;
260
261 /* Simple logical name translation */
262 static int
263 simple_trnlnm(const char * logname, char * value, int value_len)
264 {
265     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
266     const unsigned long attr = LNM$M_CASE_BLIND;
267     struct dsc$descriptor_s name_dsc;
268     int status;
269     unsigned short result;
270     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
271                                 {0, 0, 0, 0}};
272
273     name_dsc.dsc$w_length = strlen(logname);
274     name_dsc.dsc$a_pointer = (char *)logname;
275     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
276     name_dsc.dsc$b_class = DSC$K_CLASS_S;
277
278     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
279
280     if ($VMS_STATUS_SUCCESS(status)) {
281
282          /* Null terminate and return the string */
283         /*--------------------------------------*/
284         value[result] = 0;
285         return result;
286     }
287
288     return 0;
289 }
290
291
292 /* Is this a UNIX file specification?
293  *   No longer a simple check with EFS file specs
294  *   For now, not a full check, but need to
295  *   handle POSIX ^UP^ specifications
296  *   Fixing to handle ^/ cases would require
297  *   changes to many other conversion routines.
298  */
299
300 static int
301 is_unix_filespec(const char *path)
302 {
303     int ret_val;
304     const char * pch1;
305
306     ret_val = 0;
307     if (! strBEGINs(path,"\"^UP^")) {
308         pch1 = strchr(path, '/');
309         if (pch1 != NULL)
310             ret_val = 1;
311         else {
312
313             /* If the user wants UNIX files, "." needs to be treated as in UNIX */
314             if (DECC_FILENAME_UNIX_REPORT || DECC_FILENAME_UNIX_ONLY) {
315               if (strEQ(path,"."))
316                 ret_val = 1;
317             }
318         }
319     }
320     return ret_val;
321 }
322
323 /* This routine converts a UCS-2 character to be VTF-7 encoded.
324  */
325
326 static void
327 ucs2_to_vtf7(char *outspec, unsigned long ucs2_char, int * output_cnt)
328 {
329     unsigned char * ucs_ptr;
330     int hex;
331
332     ucs_ptr = (unsigned char *)&ucs2_char;
333
334     outspec[0] = '^';
335     outspec[1] = 'U';
336     hex = (ucs_ptr[1] >> 4) & 0xf;
337     if (hex < 0xA)
338         outspec[2] = hex + '0';
339     else
340         outspec[2] = (hex - 9) + 'A';
341     hex = ucs_ptr[1] & 0xF;
342     if (hex < 0xA)
343         outspec[3] = hex + '0';
344     else {
345         outspec[3] = (hex - 9) + 'A';
346     }
347     hex = (ucs_ptr[0] >> 4) & 0xf;
348     if (hex < 0xA)
349         outspec[4] = hex + '0';
350     else
351         outspec[4] = (hex - 9) + 'A';
352     hex = ucs_ptr[1] & 0xF;
353     if (hex < 0xA)
354         outspec[5] = hex + '0';
355     else {
356         outspec[5] = (hex - 9) + 'A';
357     }
358     *output_cnt = 6;
359 }
360
361
362 /* This handles the conversion of a UNIX extended character set to a ^
363  * escaped VMS character.
364  * in a UNIX file specification.
365  *
366  * The output count variable contains the number of characters added
367  * to the output string.
368  *
369  * The return value is the number of characters read from the input string
370  */
371 static int
372 copy_expand_unix_filename_escape(char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
373 {
374     int count;
375     int utf8_flag;
376
377     utf8_flag = 0;
378     if (utf8_fl)
379       utf8_flag = *utf8_fl;
380
381     count = 0;
382     *output_cnt = 0;
383     if (*inspec >= 0x80) {
384         if (utf8_fl && vms_vtf7_filenames) {
385         unsigned long ucs_char;
386
387             ucs_char = 0;
388
389             if ((*inspec & 0xE0) == 0xC0) {
390                 /* 2 byte Unicode */
391                 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
392                 if (ucs_char >= 0x80) {
393                     ucs2_to_vtf7(outspec, ucs_char, output_cnt);
394                     return 2;
395                 }
396             } else if ((*inspec & 0xF0) == 0xE0) {
397                 /* 3 byte Unicode */
398                 ucs_char = ((inspec[0] & 0xF) << 12) + 
399                    ((inspec[1] & 0x3f) << 6) +
400                    (inspec[2] & 0x3f);
401                 if (ucs_char >= 0x800) {
402                     ucs2_to_vtf7(outspec, ucs_char, output_cnt);
403                     return 3;
404                 }
405
406 #if 0 /* I do not see longer sequences supported by OpenVMS */
407       /* Maybe some one can fix this later */
408             } else if ((*inspec & 0xF8) == 0xF0) {
409                 /* 4 byte Unicode */
410                 /* UCS-4 to UCS-2 */
411             } else if ((*inspec & 0xFC) == 0xF8) {
412                 /* 5 byte Unicode */
413                 /* UCS-4 to UCS-2 */
414             } else if ((*inspec & 0xFE) == 0xFC) {
415                 /* 6 byte Unicode */
416                 /* UCS-4 to UCS-2 */
417 #endif
418             }
419         }
420
421         /* High bit set, but not a Unicode character! */
422
423         /* Non printing DECMCS or ISO Latin-1 character? */
424         if ((unsigned char)*inspec <= 0x9F) {
425             int hex;
426             outspec[0] = '^';
427             outspec++;
428             hex = (*inspec >> 4) & 0xF;
429             if (hex < 0xA)
430                 outspec[1] = hex + '0';
431             else {
432                 outspec[1] = (hex - 9) + 'A';
433             }
434             hex = *inspec & 0xF;
435             if (hex < 0xA)
436                 outspec[2] = hex + '0';
437             else {
438                 outspec[2] = (hex - 9) + 'A';
439             }
440             *output_cnt = 3;
441             return 1;
442         } else if ((unsigned char)*inspec == 0xA0) {
443             outspec[0] = '^';
444             outspec[1] = 'A';
445             outspec[2] = '0';
446             *output_cnt = 3;
447             return 1;
448         } else if ((unsigned char)*inspec == 0xFF) {
449             outspec[0] = '^';
450             outspec[1] = 'F';
451             outspec[2] = 'F';
452             *output_cnt = 3;
453             return 1;
454         }
455         *outspec = *inspec;
456         *output_cnt = 1;
457         return 1;
458     }
459
460     /* Is this a macro that needs to be passed through?
461      * Macros start with $( and an alpha character, followed
462      * by a string of alpha numeric characters ending with a )
463      * If this does not match, then encode it as ODS-5.
464      */
465     if ((inspec[0] == '$') && (inspec[1] == '(')) {
466     int tcnt;
467
468         if (isALPHA_L1(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
469             tcnt = 3;
470             outspec[0] = inspec[0];
471             outspec[1] = inspec[1];
472             outspec[2] = inspec[2];
473
474             while(isALPHA_L1(inspec[tcnt]) ||
475                   (inspec[2] == '.') || (inspec[2] == '_')) {
476                 outspec[tcnt] = inspec[tcnt];
477                 tcnt++;
478             }
479             if (inspec[tcnt] == ')') {
480                 outspec[tcnt] = inspec[tcnt];
481                 tcnt++;
482                 *output_cnt = tcnt;
483                 return tcnt;
484             }
485         }
486     }
487
488     switch (*inspec) {
489     case 0x7f:
490         outspec[0] = '^';
491         outspec[1] = '7';
492         outspec[2] = 'F';
493         *output_cnt = 3;
494         return 1;
495         break;
496     case '?':
497         if (!DECC_EFS_CHARSET)
498           outspec[0] = '%';
499         else
500           outspec[0] = '?';
501         *output_cnt = 1;
502         return 1;
503         break;
504     case '.':
505     case '!':
506     case '#':
507     case '&':
508     case '\'':
509     case '`':
510     case '(':
511     case ')':
512     case '+':
513     case '@':
514     case '{':
515     case '}':
516     case ',':
517     case ';':
518     case '[':
519     case ']':
520     case '%':
521     case '^':
522     case '\\':
523         /* Don't escape again if following character is 
524          * already something we escape.
525          */
526         if (strchr(".!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
527             *outspec = *inspec;
528             *output_cnt = 1;
529             return 1;
530             break;
531         }
532         /* But otherwise fall through and escape it. */
533     case '=':
534         /* Assume that this is to be escaped */
535         outspec[0] = '^';
536         outspec[1] = *inspec;
537         *output_cnt = 2;
538         return 1;
539         break;
540     case ' ': /* space */
541         /* Assume that this is to be escaped */
542         outspec[0] = '^';
543         outspec[1] = '_';
544         *output_cnt = 2;
545         return 1;
546         break;
547     default:
548         *outspec = *inspec;
549         *output_cnt = 1;
550         return 1;
551         break;
552     }
553     return 0;
554 }
555
556
557 /* This handles the expansion of a '^' prefix to the proper character
558  * in a UNIX file specification.
559  *
560  * The output count variable contains the number of characters added
561  * to the output string.
562  *
563  * The return value is the number of characters read from the input
564  * string
565  */
566 static int
567 copy_expand_vms_filename_escape(char *outspec, const char *inspec, int *output_cnt)
568 {
569     int count;
570     int scnt;
571
572     count = 0;
573     *output_cnt = 0;
574     if (*inspec == '^') {
575         inspec++;
576         switch (*inspec) {
577         /* Spaces and non-trailing dots should just be passed through, 
578          * but eat the escape character.
579          */
580         case '.':
581             *outspec = *inspec;
582             count += 2;
583             (*output_cnt)++;
584             break;
585         case '_': /* space */
586             *outspec = ' ';
587             count += 2;
588             (*output_cnt)++;
589             break;
590         case '^':
591             /* Hmm.  Better leave the escape escaped. */
592             outspec[0] = '^';
593             outspec[1] = '^';
594             count += 2;
595             (*output_cnt) += 2;
596             break;
597         case 'U': /* Unicode - FIX-ME this is wrong. */
598             inspec++;
599             count++;
600             scnt = strspn(inspec, "0123456789ABCDEFabcdef");
601             if (scnt == 4) {
602                 unsigned int c1, c2;
603                 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
604                 outspec[0] = c1 & 0xff;
605                 outspec[1] = c2 & 0xff;
606                 if (scnt > 1) {
607                     (*output_cnt) += 2;
608                     count += 4;
609                 }
610             }
611             else {
612                 /* Error - do best we can to continue */
613                 *outspec = 'U';
614                 outspec++;
615                 (*output_cnt++);
616                 *outspec = *inspec;
617                 count++;
618                 (*output_cnt++);
619             }
620             break;
621         default:
622             scnt = strspn(inspec, "0123456789ABCDEFabcdef");
623             if (scnt == 2) {
624                 /* Hex encoded */
625                 unsigned int c1;
626                 scnt = sscanf(inspec, "%2x", &c1);
627                 outspec[0] = c1 & 0xff;
628                 if (scnt > 0) {
629                     (*output_cnt++);
630                     count += 2;
631                 }
632             }
633             else {
634                 *outspec = *inspec;
635                 count++;
636                 (*output_cnt++);
637             }
638         }
639     }
640     else {
641         *outspec = *inspec;
642         count++;
643         (*output_cnt)++;
644     }
645     return count;
646 }
647
648 /* vms_split_path - Verify that the input file specification is a
649  * VMS format file specification, and provide pointers to the components of
650  * it.  With EFS format filenames, this is virtually the only way to
651  * parse a VMS path specification into components.
652  *
653  * If the sum of the components do not add up to the length of the
654  * string, then the passed file specification is probably a UNIX style
655  * path.
656  */
657 static int
658 vms_split_path(const char * path, char * * volume, int * vol_len, char * * root, int * root_len, 
659                char * * dir, int * dir_len, char * * name, int * name_len,
660                char * * ext, int * ext_len, char * * version, int * ver_len)
661 {
662     struct dsc$descriptor path_desc;
663     int status;
664     unsigned long flags;
665     int ret_stat;
666     struct filescan_itmlst_2 item_list[9];
667     const int filespec = 0;
668     const int nodespec = 1;
669     const int devspec = 2;
670     const int rootspec = 3;
671     const int dirspec = 4;
672     const int namespec = 5;
673     const int typespec = 6;
674     const int verspec = 7;
675
676     /* Assume the worst for an easy exit */
677     ret_stat = -1;
678     *volume = NULL;
679     *vol_len = 0;
680     *root = NULL;
681     *root_len = 0;
682     *dir = NULL;
683     *name = NULL;
684     *name_len = 0;
685     *ext = NULL;
686     *ext_len = 0;
687     *version = NULL;
688     *ver_len = 0;
689
690     path_desc.dsc$a_pointer = (char *)path; /* cast ok */
691     path_desc.dsc$w_length = strlen(path);
692     path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
693     path_desc.dsc$b_class = DSC$K_CLASS_S;
694
695     /* Get the total length, if it is shorter than the string passed
696      * then this was probably not a VMS formatted file specification
697      */
698     item_list[filespec].itmcode = FSCN$_FILESPEC;
699     item_list[filespec].length = 0;
700     item_list[filespec].component = NULL;
701
702     /* If the node is present, then it gets considered as part of the
703      * volume name to hopefully make things simple.
704      */
705     item_list[nodespec].itmcode = FSCN$_NODE;
706     item_list[nodespec].length = 0;
707     item_list[nodespec].component = NULL;
708
709     item_list[devspec].itmcode = FSCN$_DEVICE;
710     item_list[devspec].length = 0;
711     item_list[devspec].component = NULL;
712
713     /* root is a special case,  adding it to either the directory or
714      * the device components will probably complicate things for the
715      * callers of this routine, so leave it separate.
716      */
717     item_list[rootspec].itmcode = FSCN$_ROOT;
718     item_list[rootspec].length = 0;
719     item_list[rootspec].component = NULL;
720
721     item_list[dirspec].itmcode = FSCN$_DIRECTORY;
722     item_list[dirspec].length = 0;
723     item_list[dirspec].component = NULL;
724
725     item_list[namespec].itmcode = FSCN$_NAME;
726     item_list[namespec].length = 0;
727     item_list[namespec].component = NULL;
728
729     item_list[typespec].itmcode = FSCN$_TYPE;
730     item_list[typespec].length = 0;
731     item_list[typespec].component = NULL;
732
733     item_list[verspec].itmcode = FSCN$_VERSION;
734     item_list[verspec].length = 0;
735     item_list[verspec].component = NULL;
736
737     item_list[8].itmcode = 0;
738     item_list[8].length = 0;
739     item_list[8].component = NULL;
740
741     status = sys$filescan
742        ((const struct dsc$descriptor_s *)&path_desc, item_list,
743         &flags, NULL, NULL);
744     _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
745
746     /* If we parsed it successfully these two lengths should be the same */
747     if (path_desc.dsc$w_length != item_list[filespec].length)
748         return ret_stat;
749
750     /* If we got here, then it is a VMS file specification */
751     ret_stat = 0;
752
753     /* set the volume name */
754     if (item_list[nodespec].length > 0) {
755         *volume = item_list[nodespec].component;
756         *vol_len = item_list[nodespec].length + item_list[devspec].length;
757     }
758     else {
759         *volume = item_list[devspec].component;
760         *vol_len = item_list[devspec].length;
761     }
762
763     *root = item_list[rootspec].component;
764     *root_len = item_list[rootspec].length;
765
766     *dir = item_list[dirspec].component;
767     *dir_len = item_list[dirspec].length;
768
769     /* Now fun with versions and EFS file specifications
770      * The parser can not tell the difference when a "." is a version
771      * delimiter or a part of the file specification.
772      */
773     if ((DECC_EFS_CHARSET) &&
774         (item_list[verspec].length > 0) &&
775         (item_list[verspec].component[0] == '.')) {
776         *name = item_list[namespec].component;
777         *name_len = item_list[namespec].length + item_list[typespec].length;
778         *ext = item_list[verspec].component;
779         *ext_len = item_list[verspec].length;
780         *version = NULL;
781         *ver_len = 0;
782     }
783     else {
784         *name = item_list[namespec].component;
785         *name_len = item_list[namespec].length;
786         *ext = item_list[typespec].component;
787         *ext_len = item_list[typespec].length;
788         *version = item_list[verspec].component;
789         *ver_len = item_list[verspec].length;
790     }
791     return ret_stat;
792 }
793
794 /* Routine to determine if the file specification ends with .dir */
795 static int
796 is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len)
797 {
798
799     /* e_len must be 4, and version must be <= 2 characters */
800     if (e_len != 4 || vs_len > 2)
801         return 0;
802
803     /* If a version number is present, it needs to be one */
804     if ((vs_len == 2) && (vs_spec[1] != '1'))
805         return 0;
806
807     /* Look for the DIR on the extension */
808     if (vms_process_case_tolerant) {
809         if ((toUPPER_A(e_spec[1]) == 'D') &&
810             (toUPPER_A(e_spec[2]) == 'I') &&
811             (toUPPER_A(e_spec[3]) == 'R')) {
812             return 1;
813         }
814     } else {
815         /* Directory extensions are supposed to be in upper case only */
816         /* I would not be surprised if this rule can not be enforced */
817         /* if and when someone fully debugs the case sensitive mode */
818         if ((e_spec[1] == 'D') &&
819             (e_spec[2] == 'I') &&
820             (e_spec[3] == 'R')) {
821             return 1;
822         }
823     }
824     return 0;
825 }
826
827
828 /* my_maxidx
829  * Routine to retrieve the maximum equivalence index for an input
830  * logical name.  Some calls to this routine have no knowledge if
831  * the variable is a logical or not.  So on error we return a max
832  * index of zero.
833  */
834 /*{{{int my_maxidx(const char *lnm) */
835 static int
836 my_maxidx(const char *lnm)
837 {
838     int status;
839     int midx;
840     int attr = LNM$M_CASE_BLIND;
841     struct dsc$descriptor lnmdsc;
842     struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
843                                 {0, 0, 0, 0}};
844
845     lnmdsc.dsc$w_length = strlen(lnm);
846     lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
847     lnmdsc.dsc$b_class = DSC$K_CLASS_S;
848     lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
849
850     status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
851     if ((status & 1) == 0)
852        midx = 0;
853
854     return (midx);
855 }
856 /*}}}*/
857
858 /* Routine to remove the 2-byte prefix from the translation of a
859  * process-permanent file (PPF).
860  */
861 static inline unsigned short int
862 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
863 {
864     if (*((int *)lnm) == *((int *)"SYS$")                    &&
865         eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00      &&
866         ( (lnm[4] == 'O' && strEQ(lnm,"SYS$OUTPUT"))  ||
867           (lnm[4] == 'I' && strEQ(lnm,"SYS$INPUT"))   ||
868           (lnm[4] == 'E' && strEQ(lnm,"SYS$ERROR"))   ||
869           (lnm[4] == 'C' && strEQ(lnm,"SYS$COMMAND")) )  ) {
870
871         memmove(eqv, eqv+4, eqvlen-4);
872         eqvlen -= 4;
873     }
874     return eqvlen;
875 }
876
877 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
878 int
879 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
880   struct dsc$descriptor_s **tabvec, unsigned long int flags)
881 {
882     const char *cp1;
883     char uplnm[LNM$C_NAMLENGTH+1], *cp2;
884     unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
885     bool found_in_crtlenv = 0, found_in_clisym = 0;
886     unsigned long int retsts, attr = LNM$M_CASE_BLIND;
887     int midx;
888     unsigned char acmode;
889     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
890                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
891     struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
892                                  {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
893                                  {0, 0, 0, 0}};
894     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
895 #if defined(PERL_IMPLICIT_CONTEXT)
896     pTHX = NULL;
897     if (PL_curinterp) {
898       aTHX = PERL_GET_INTERP;
899     } else {
900       aTHX = NULL;
901     }
902 #endif
903
904     if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
905       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
906     }
907     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
908       *cp2 = toUPPER_A(*cp1);
909       if (cp1 - lnm > LNM$C_NAMLENGTH) {
910         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
911         return 0;
912       }
913     }
914     lnmdsc.dsc$w_length = cp1 - lnm;
915     lnmdsc.dsc$a_pointer = uplnm;
916     uplnm[lnmdsc.dsc$w_length] = '\0';
917     secure = flags & PERL__TRNENV_SECURE;
918     acmode = secure ? PSL$C_EXEC : PSL$C_USER;
919     if (!tabvec || !*tabvec) tabvec = env_tables;
920
921     for (curtab = 0; tabvec[curtab]; curtab++) {
922       if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
923         if (!ivenv && !secure) {
924           char *eq;
925           int i;
926           if (!environ) {
927             ivenv = 1; 
928 #if defined(PERL_IMPLICIT_CONTEXT)
929             if (aTHX == NULL) {
930                 fprintf(stderr,
931                     "Can't read CRTL environ\n");
932             } else
933 #endif
934                 Perl_warn(aTHX_ "Can't read CRTL environ\n");
935             continue;
936           }
937           retsts = SS$_NOLOGNAM;
938           for (i = 0; environ[i]; i++) { 
939             if ((eq = strchr(environ[i],'=')) && 
940                 lnmdsc.dsc$w_length == (eq - environ[i]) &&
941                 strnEQ(environ[i],lnm,eq - environ[i])) {
942               eq++;
943               for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
944               if (!eqvlen) continue;
945               retsts = SS$_NORMAL;
946               break;
947             }
948           }
949           if (retsts != SS$_NOLOGNAM) {
950               found_in_crtlenv = 1;
951               break;
952           }
953         }
954       }
955       else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
956                !str$case_blind_compare(&tmpdsc,&clisym)) {
957         if (!ivsym && !secure) {
958           unsigned short int deflen = LNM$C_NAMLENGTH;
959           struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
960           /* dynamic dsc to accommodate possible long value */
961           _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
962           retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
963           if (retsts & 1) { 
964             if (eqvlen > MAX_DCL_SYMBOL) {
965               set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
966               eqvlen = MAX_DCL_SYMBOL;
967               /* Special hack--we might be called before the interpreter's */
968               /* fully initialized, in which case either thr or PL_curcop */
969               /* might be bogus. We have to check, since ckWARN needs them */
970               /* both to be valid if running threaded */
971 #if defined(PERL_IMPLICIT_CONTEXT)
972               if (aTHX == NULL) {
973                   fprintf(stderr,
974                      "Value of CLI symbol \"%s\" too long",lnm);
975               } else
976 #endif
977                 if (ckWARN(WARN_MISC)) {
978                   Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
979                 }
980             }
981             strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
982           }
983           _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
984           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
985           if (retsts == LIB$_NOSUCHSYM) continue;
986           found_in_clisym = 1;
987           break;
988         }
989       }
990       else if (!ivlnm) {
991         if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
992           midx = my_maxidx(lnm);
993           for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
994             lnmlst[1].bufadr = cp2;
995             eqvlen = 0;
996             retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
997             if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
998             if (retsts == SS$_NOLOGNAM) break;
999             eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1000             cp2 += eqvlen;
1001             *cp2 = '\0';
1002           }
1003           if ((retsts == SS$_IVLOGNAM) ||
1004               (retsts == SS$_NOLOGNAM)) { continue; }
1005           eqvlen = strlen(eqv);
1006         }
1007         else {
1008           retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1009           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1010           if (retsts == SS$_NOLOGNAM) continue;
1011           eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1012           eqv[eqvlen] = '\0';
1013         }
1014         break;
1015       }
1016     }
1017     /* An index only makes sense for logical names, so make sure we aren't
1018      * iterating over an index for an environ var or DCL symbol and getting
1019      * the same answer ad infinitum.
1020      */
1021     if (idx > 0 && (found_in_crtlenv || found_in_clisym)) {
1022         return 0;
1023     }
1024     else if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1025     else if (retsts == LIB$_NOSUCHSYM ||
1026              retsts == SS$_NOLOGNAM) {
1027      /* Unsuccessful lookup is normal -- no need to set errno */
1028      return 0;
1029     }
1030     else if (retsts == LIB$_INVSYMNAM ||
1031              retsts == SS$_IVLOGNAM   ||
1032              retsts == SS$_IVLOGTAB) {
1033       set_errno(EINVAL);  set_vaxc_errno(retsts);
1034     }
1035     else _ckvmssts_noperl(retsts);
1036     return 0;
1037 }  /* end of vmstrnenv */
1038 /*}}}*/
1039
1040 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1041 /* Define as a function so we can access statics. */
1042 int
1043 Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1044 {
1045     int flags = 0;
1046
1047 #if defined(PERL_IMPLICIT_CONTEXT)
1048     if (aTHX != NULL)
1049 #endif
1050 #ifdef SECURE_INTERNAL_GETENV
1051         flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1052                  PERL__TRNENV_SECURE : 0;
1053 #endif
1054
1055     return vmstrnenv(lnm, eqv, idx, fildev, flags);
1056 }
1057 /*}}}*/
1058
1059 /* my_getenv
1060  * Note: Uses Perl temp to store result so char * can be returned to
1061  * caller; this pointer will be invalidated at next Perl statement
1062  * transition.
1063  * We define this as a function rather than a macro in terms of my_getenv_len()
1064  * so that it'll work when PL_curinterp is undefined (and we therefore can't
1065  * allocate SVs).
1066  */
1067 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1068 char *
1069 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1070 {
1071     const char *cp1;
1072     static char *__my_getenv_eqv = NULL;
1073     char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1074     unsigned long int idx = 0;
1075     int success, secure;
1076     int midx, flags;
1077     SV *tmpsv;
1078
1079     midx = my_maxidx(lnm) + 1;
1080
1081     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1082       /* Set up a temporary buffer for the return value; Perl will
1083        * clean it up at the next statement transition */
1084       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1085       if (!tmpsv) return NULL;
1086       eqv = SvPVX(tmpsv);
1087     }
1088     else {
1089       /* Assume no interpreter ==> single thread */
1090       if (__my_getenv_eqv != NULL) {
1091         Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1092       }
1093       else {
1094         Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1095       }
1096       eqv = __my_getenv_eqv;  
1097     }
1098
1099     for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = toUPPER_A(*cp1);
1100     if (memEQs(eqv, cp1 - lnm, "DEFAULT")) {
1101       int len;
1102       getcwd(eqv,LNM$C_NAMLENGTH);
1103
1104       len = strlen(eqv);
1105
1106       /* Get rid of "000000/ in rooted filespecs */
1107       if (len > 7) {
1108         char * zeros;
1109         zeros = strstr(eqv, "/000000/");
1110         if (zeros != NULL) {
1111           int mlen;
1112           mlen = len - (zeros - eqv) - 7;
1113           memmove(zeros, &zeros[7], mlen);
1114           len = len - 7;
1115           eqv[len] = '\0';
1116         }
1117       }
1118       return eqv;
1119     }
1120     else {
1121       /* Impose security constraints only if tainting */
1122       if (sys) {
1123         /* Impose security constraints only if tainting */
1124         secure = PL_curinterp ? TAINTING_get : will_taint;
1125       }
1126       else {
1127         secure = 0;
1128       }
1129
1130       flags = 
1131 #ifdef SECURE_INTERNAL_GETENV
1132               secure ? PERL__TRNENV_SECURE : 0
1133 #else
1134               0
1135 #endif
1136       ;
1137
1138       /* For the getenv interface we combine all the equivalence names
1139        * of a search list logical into one value to acquire a maximum
1140        * value length of 255*128 (assuming %ENV is using logicals).
1141        */
1142       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1143
1144       /* If the name contains a semicolon-delimited index, parse it
1145        * off and make sure we only retrieve the equivalence name for 
1146        * that index.  */
1147       if ((cp2 = strchr(lnm,';')) != NULL) {
1148         my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1149         idx = strtoul(cp2+1,NULL,0);
1150         lnm = uplnm;
1151         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1152       }
1153
1154       success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1155
1156       return success ? eqv : NULL;
1157     }
1158
1159 }  /* end of my_getenv() */
1160 /*}}}*/
1161
1162
1163 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1164 char *
1165 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1166 {
1167     const char *cp1;
1168     char *buf, *cp2;
1169     unsigned long idx = 0;
1170     int midx, flags;
1171     static char *__my_getenv_len_eqv = NULL;
1172     int secure;
1173     SV *tmpsv;
1174     
1175     midx = my_maxidx(lnm) + 1;
1176
1177     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1178       /* Set up a temporary buffer for the return value; Perl will
1179        * clean it up at the next statement transition */
1180       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1181       if (!tmpsv) return NULL;
1182       buf = SvPVX(tmpsv);
1183     }
1184     else {
1185       /* Assume no interpreter ==> single thread */
1186       if (__my_getenv_len_eqv != NULL) {
1187         Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1188       }
1189       else {
1190         Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1191       }
1192       buf = __my_getenv_len_eqv;  
1193     }
1194
1195     for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = toUPPER_A(*cp1);
1196     if (memEQs(buf, cp1 - lnm, "DEFAULT")) {
1197     char * zeros;
1198
1199       getcwd(buf,LNM$C_NAMLENGTH);
1200       *len = strlen(buf);
1201
1202       /* Get rid of "000000/ in rooted filespecs */
1203       if (*len > 7) {
1204       zeros = strstr(buf, "/000000/");
1205       if (zeros != NULL) {
1206         int mlen;
1207         mlen = *len - (zeros - buf) - 7;
1208         memmove(zeros, &zeros[7], mlen);
1209         *len = *len - 7;
1210         buf[*len] = '\0';
1211         }
1212       }
1213       return buf;
1214     }
1215     else {
1216       if (sys) {
1217         /* Impose security constraints only if tainting */
1218         secure = PL_curinterp ? TAINTING_get : will_taint;
1219       }
1220       else {
1221         secure = 0;
1222       }
1223
1224       flags = 
1225 #ifdef SECURE_INTERNAL_GETENV
1226               secure ? PERL__TRNENV_SECURE : 0
1227 #else
1228               0
1229 #endif
1230       ;
1231
1232       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1233
1234       if ((cp2 = strchr(lnm,';')) != NULL) {
1235         my_strlcpy(buf, lnm, cp2 - lnm + 1);
1236         idx = strtoul(cp2+1,NULL,0);
1237         lnm = buf;
1238         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1239       }
1240
1241       *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1242
1243       /* Get rid of "000000/ in rooted filespecs */
1244       if (*len > 7) {
1245         char * zeros;
1246         zeros = strstr(buf, "/000000/");
1247         if (zeros != NULL) {
1248           int mlen;
1249           mlen = *len - (zeros - buf) - 7;
1250           memmove(zeros, &zeros[7], mlen);
1251           *len = *len - 7;
1252           buf[*len] = '\0';
1253         }
1254       }
1255
1256       return *len ? buf : NULL;
1257     }
1258
1259 }  /* end of my_getenv_len() */
1260 /*}}}*/
1261
1262 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1263
1264 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1265
1266 /*{{{ void prime_env_iter() */
1267 void
1268 prime_env_iter(void)
1269 /* Fill the %ENV associative array with all logical names we can
1270  * find, in preparation for iterating over it.
1271  */
1272 {
1273   static int primed = 0;
1274   HV *seenhv = NULL, *envhv;
1275   SV *sv = NULL;
1276   char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1277   unsigned short int chan;
1278 #ifndef CLI$M_TRUSTED
1279 #  define CLI$M_TRUSTED 0x40  /* Missing from VAXC headers */
1280 #endif
1281   unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1282   unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1283   long int i;
1284   bool have_sym = FALSE, have_lnm = FALSE;
1285   struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1286   $DESCRIPTOR(cmddsc,cmd);    $DESCRIPTOR(nldsc,"_NLA0:");
1287   $DESCRIPTOR(clidsc,"DCL");  $DESCRIPTOR(clitabdsc,"DCLTABLES");
1288   $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1289   $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 
1290 #if defined(PERL_IMPLICIT_CONTEXT)
1291   pTHX;
1292 #endif
1293 #if defined(USE_ITHREADS)
1294   static perl_mutex primenv_mutex;
1295   MUTEX_INIT(&primenv_mutex);
1296 #endif
1297
1298 #if defined(PERL_IMPLICIT_CONTEXT)
1299     /* We jump through these hoops because we can be called at */
1300     /* platform-specific initialization time, which is before anything is */
1301     /* set up--we can't even do a plain dTHX since that relies on the */
1302     /* interpreter structure to be initialized */
1303     if (PL_curinterp) {
1304       aTHX = PERL_GET_INTERP;
1305     } else {
1306       /* we never get here because the NULL pointer will cause the */
1307       /* several of the routines called by this routine to access violate */
1308
1309       /* This routine is only called by hv.c/hv_iterinit which has a */
1310       /* context, so the real fix may be to pass it through instead of */
1311       /* the hoops above */
1312       aTHX = NULL;
1313     }
1314 #endif
1315
1316   if (primed || !PL_envgv) return;
1317   MUTEX_LOCK(&primenv_mutex);
1318   if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1319   envhv = GvHVn(PL_envgv);
1320   /* Perform a dummy fetch as an lval to insure that the hash table is
1321    * set up.  Otherwise, the hv_store() will turn into a nullop. */
1322   (void) hv_fetchs(envhv,"DEFAULT",TRUE);
1323
1324   for (i = 0; env_tables[i]; i++) {
1325      if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1326          !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1327      if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1328   }
1329   if (have_sym || have_lnm) {
1330     long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1331     _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1332     _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1333     _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1334   }
1335
1336   for (i--; i >= 0; i--) {
1337     if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1338       char *start;
1339       int j;
1340       /* Start at the end, so if there is a duplicate we keep the first one. */
1341       for (j = 0; environ[j]; j++);
1342       for (j--; j >= 0; j--) {
1343         if (!(start = strchr(environ[j],'='))) {
1344           if (ckWARN(WARN_INTERNAL)) 
1345             Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1346         }
1347         else {
1348           start++;
1349           sv = newSVpv(start,0);
1350           SvTAINTED_on(sv);
1351           (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1352         }
1353       }
1354       continue;
1355     }
1356     else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1357              !str$case_blind_compare(&tmpdsc,&clisym)) {
1358       my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1359       cmddsc.dsc$w_length = 20;
1360       if (env_tables[i]->dsc$w_length == 12 &&
1361           (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1362           !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local  *", sizeof(cmd)-12);
1363       flags = defflags | CLI$M_NOLOGNAM;
1364     }
1365     else {
1366       my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1367       if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1368         my_strlcat(cmd," /Table=", sizeof(cmd));
1369         cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1370       }
1371       else cmddsc.dsc$w_length = 14;  /* N.B. We test this below */
1372       flags = defflags | CLI$M_NOCLISYM;
1373     }
1374     
1375     /* Create a new subprocess to execute each command, to exclude the
1376      * remote possibility that someone could subvert a mbx or file used
1377      * to write multiple commands to a single subprocess.
1378      */
1379     do {
1380       retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1381                          0,&riseandshine,0,0,&clidsc,&clitabdsc);
1382       flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1383       defflags &= ~CLI$M_TRUSTED;
1384     } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1385     _ckvmssts(retsts);
1386     if (!buf) Newx(buf,mbxbufsiz + 1,char);
1387     if (seenhv) SvREFCNT_dec(seenhv);
1388     seenhv = newHV();
1389     while (1) {
1390       char *cp1, *cp2, *key;
1391       unsigned long int sts, iosb[2], retlen, keylen;
1392       U32 hash;
1393
1394       sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1395       if (sts & 1) sts = iosb[0] & 0xffff;
1396       if (sts == SS$_ENDOFFILE) {
1397         int wakect = 0;
1398         while (substs == 0) { sys$hiber(); wakect++;}
1399         if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
1400         _ckvmssts(substs);
1401         break;
1402       }
1403       _ckvmssts(sts);
1404       retlen = iosb[0] >> 16;      
1405       if (!retlen) continue;  /* blank line */
1406       buf[retlen] = '\0';
1407       if (iosb[1] != subpid) {
1408         if (iosb[1]) {
1409           Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1410         }
1411         continue;
1412       }
1413       if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1414         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1415
1416       for (cp1 = buf; *cp1 && isSPACE_L1(*cp1); cp1++) ;
1417       if (*cp1 == '(' || /* Logical name table name */
1418           *cp1 == '='    /* Next eqv of searchlist  */) continue;
1419       if (*cp1 == '"') cp1++;
1420       for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1421       key = cp1;  keylen = cp2 - cp1;
1422       if (keylen && hv_exists(seenhv,key,keylen)) continue;
1423       while (*cp2 && *cp2 != '=') cp2++;
1424       while (*cp2 && *cp2 == '=') cp2++;
1425       while (*cp2 && *cp2 == ' ') cp2++;
1426       if (*cp2 == '"') {  /* String translation; may embed "" */
1427         for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1428         cp2++;  cp1--; /* Skip "" surrounding translation */
1429       }
1430       else {  /* Numeric translation */
1431         for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1432         cp1--;  /* stop on last non-space char */
1433       }
1434       if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1435         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1436         continue;
1437       }
1438       PERL_HASH(hash,key,keylen);
1439
1440       if (cp1 == cp2 && *cp2 == '.') {
1441         /* A single dot usually means an unprintable character, such as a null
1442          * to indicate a zero-length value.  Get the actual value to make sure.
1443          */
1444         char lnm[LNM$C_NAMLENGTH+1];
1445         char eqv[MAX_DCL_SYMBOL+1];
1446         int trnlen;
1447         strncpy(lnm, key, keylen);
1448         trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1449         sv = newSVpvn(eqv, strlen(eqv));
1450       }
1451       else {
1452         sv = newSVpvn(cp2,cp1 - cp2 + 1);
1453       }
1454
1455       SvTAINTED_on(sv);
1456       hv_store(envhv,key,keylen,sv,hash);
1457       hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1458     }
1459     if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1460       /* get the PPFs for this process, not the subprocess */
1461       const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1462       char eqv[LNM$C_NAMLENGTH+1];
1463       int trnlen, i;
1464       for (i = 0; ppfs[i]; i++) {
1465         trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1466         sv = newSVpv(eqv,trnlen);
1467         SvTAINTED_on(sv);
1468         hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1469       }
1470     }
1471   }
1472   primed = 1;
1473   if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1474   if (buf) Safefree(buf);
1475   if (seenhv) SvREFCNT_dec(seenhv);
1476   MUTEX_UNLOCK(&primenv_mutex);
1477   return;
1478
1479 }  /* end of prime_env_iter */
1480 /*}}}*/
1481
1482
1483 /*{{{ int  vmssetenv(const char *lnm, const char *eqv)*/
1484 /* Define or delete an element in the same "environment" as
1485  * vmstrnenv().  If an element is to be deleted, it's removed from
1486  * the first place it's found.  If it's to be set, it's set in the
1487  * place designated by the first element of the table vector.
1488  * Like setenv() returns 0 for success, non-zero on error.
1489  */
1490 int
1491 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1492 {
1493     const char *cp1;
1494     char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1495     unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1496     int nseg = 0, j;
1497     unsigned long int retsts, usermode = PSL$C_USER;
1498     struct itmlst_3 *ile, *ilist;
1499     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1500                             eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1501                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1502     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1503     $DESCRIPTOR(local,"_LOCAL");
1504
1505     if (!lnm) {
1506         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1507         return SS$_IVLOGNAM;
1508     }
1509
1510     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1511       *cp2 = toUPPER_A(*cp1);
1512       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1513         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1514         return SS$_IVLOGNAM;
1515       }
1516     }
1517     lnmdsc.dsc$w_length = cp1 - lnm;
1518     if (!tabvec || !*tabvec) tabvec = env_tables;
1519
1520     if (!eqv) {  /* we're deleting n element */
1521       for (curtab = 0; tabvec[curtab]; curtab++) {
1522         if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1523         int i;
1524           for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1525             if ((cp1 = strchr(environ[i],'=')) && 
1526                 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1527                 strnEQ(environ[i],lnm,cp1 - environ[i])) {
1528               unsetenv(lnm);
1529               return 0;
1530             }
1531           }
1532           ivenv = 1; retsts = SS$_NOLOGNAM;
1533         }
1534         else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1535                  !str$case_blind_compare(&tmpdsc,&clisym)) {
1536           unsigned int symtype;
1537           if (tabvec[curtab]->dsc$w_length == 12 &&
1538               (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1539               !str$case_blind_compare(&tmpdsc,&local)) 
1540             symtype = LIB$K_CLI_LOCAL_SYM;
1541           else symtype = LIB$K_CLI_GLOBAL_SYM;
1542           retsts = lib$delete_symbol(&lnmdsc,&symtype);
1543           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1544           if (retsts == LIB$_NOSUCHSYM) continue;
1545           break;
1546         }
1547         else if (!ivlnm) {
1548           retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1549           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1550           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1551           retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1552           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1553         }
1554       }
1555     }
1556     else {  /* we're defining a value */
1557       if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1558         return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1559       }
1560       else {
1561         eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1562         eqvdsc.dsc$w_length  = strlen(eqv);
1563         if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1564             !str$case_blind_compare(&tmpdsc,&clisym)) {
1565           unsigned int symtype;
1566           if (tabvec[0]->dsc$w_length == 12 &&
1567               (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1568                !str$case_blind_compare(&tmpdsc,&local)) 
1569             symtype = LIB$K_CLI_LOCAL_SYM;
1570           else symtype = LIB$K_CLI_GLOBAL_SYM;
1571           retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1572         }
1573         else {
1574           if (!*eqv) eqvdsc.dsc$w_length = 1;
1575           if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1576
1577             nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1578             if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1579               Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1580                           lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1581               eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1582               nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1583             }
1584
1585             Newx(ilist,nseg+1,struct itmlst_3);
1586             ile = ilist;
1587             if (!ile) {
1588               set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1589               return SS$_INSFMEM;
1590             }
1591             memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1592
1593             for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1594               ile->itmcode = LNM$_STRING;
1595               ile->bufadr = c;
1596               if ((j+1) == nseg) {
1597                 ile->buflen = strlen(c);
1598                 /* in case we are truncating one that's too long */
1599                 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1600               }
1601               else {
1602                 ile->buflen = LNM$C_NAMLENGTH;
1603               }
1604             }
1605
1606             retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1607             Safefree (ilist);
1608           }
1609           else {
1610             retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1611           }
1612         }
1613       }
1614     }
1615     if (!(retsts & 1)) {
1616       switch (retsts) {
1617         case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1618         case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1619           set_errno(EVMSERR); break;
1620         case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 
1621         case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1622           set_errno(EINVAL); break;
1623         case SS$_NOPRIV:
1624           set_errno(EACCES); break;
1625         default:
1626           _ckvmssts(retsts);
1627           set_errno(EVMSERR);
1628        }
1629        set_vaxc_errno(retsts);
1630        return (int) retsts || 44; /* retsts should never be 0, but just in case */
1631     }
1632     else {
1633       /* We reset error values on success because Perl does an hv_fetch()
1634        * before each hv_store(), and if the thing we're setting didn't
1635        * previously exist, we've got a leftover error message.  (Of course,
1636        * this fails in the face of
1637        *    $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1638        * in that the error reported in $! isn't spurious, 
1639        * but it's right more often than not.)
1640        */
1641       set_errno(0); set_vaxc_errno(retsts);
1642       return 0;
1643     }
1644
1645 }  /* end of vmssetenv() */
1646 /*}}}*/
1647
1648 /*{{{ void  my_setenv(const char *lnm, const char *eqv)*/
1649 /* This has to be a function since there's a prototype for it in proto.h */
1650 void
1651 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1652 {
1653     if (lnm && *lnm) {
1654       int len = strlen(lnm);
1655       if  (len == 7) {
1656         char uplnm[8];
1657         int i;
1658         for (i = 0; lnm[i]; i++) uplnm[i] = toUPPER_A(lnm[i]);
1659         if (strEQ(uplnm,"DEFAULT")) {
1660           if (eqv && *eqv) my_chdir(eqv);
1661           return;
1662         }
1663     } 
1664   }
1665   (void) vmssetenv(lnm,eqv,NULL);
1666 }
1667 /*}}}*/
1668
1669 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1670 /*  vmssetuserlnm
1671  *  sets a user-mode logical in the process logical name table
1672  *  used for redirection of sys$error
1673  */
1674 void
1675 Perl_vmssetuserlnm(const char *name, const char *eqv)
1676 {
1677     $DESCRIPTOR(d_tab, "LNM$PROCESS");
1678     struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1679     unsigned long int iss, attr = LNM$M_CONFINE;
1680     unsigned char acmode = PSL$C_USER;
1681     struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1682                                  {0, 0, 0, 0}};
1683     d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1684     d_name.dsc$w_length = strlen(name);
1685
1686     lnmlst[0].buflen = strlen(eqv);
1687     lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1688
1689     iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1690     if (!(iss&1)) lib$signal(iss);
1691 }
1692 /*}}}*/
1693
1694
1695 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1696 /* my_crypt - VMS password hashing
1697  * my_crypt() provides an interface compatible with the Unix crypt()
1698  * C library function, and uses sys$hash_password() to perform VMS
1699  * password hashing.  The quadword hashed password value is returned
1700  * as a NUL-terminated 8 character string.  my_crypt() does not change
1701  * the case of its string arguments; in order to match the behavior
1702  * of LOGINOUT et al., alphabetic characters in both arguments must
1703  *  be upcased by the caller.
1704  *
1705  * - fix me to call ACM services when available
1706  */
1707 char *
1708 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1709 {
1710 #   ifndef UAI$C_PREFERRED_ALGORITHM
1711 #     define UAI$C_PREFERRED_ALGORITHM 127
1712 #   endif
1713     unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1714     unsigned short int salt = 0;
1715     unsigned long int sts;
1716     struct const_dsc {
1717         unsigned short int dsc$w_length;
1718         unsigned char      dsc$b_type;
1719         unsigned char      dsc$b_class;
1720         const char *       dsc$a_pointer;
1721     }  usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1722        txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1723     struct itmlst_3 uailst[3] = {
1724         { sizeof alg,  UAI$_ENCRYPT, &alg, 0},
1725         { sizeof salt, UAI$_SALT,    &salt, 0},
1726         { 0,           0,            NULL,  NULL}};
1727     static char hash[9];
1728
1729     usrdsc.dsc$w_length = strlen(usrname);
1730     usrdsc.dsc$a_pointer = usrname;
1731     if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1732       switch (sts) {
1733         case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1734           set_errno(EACCES);
1735           break;
1736         case RMS$_RNF:
1737           set_errno(ESRCH);  /* There isn't a Unix no-such-user error */
1738           break;
1739         default:
1740           set_errno(EVMSERR);
1741       }
1742       set_vaxc_errno(sts);
1743       if (sts != RMS$_RNF) return NULL;
1744     }
1745
1746     txtdsc.dsc$w_length = strlen(textpasswd);
1747     txtdsc.dsc$a_pointer = textpasswd;
1748     if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1749       set_errno(EVMSERR);  set_vaxc_errno(sts);  return NULL;
1750     }
1751
1752     return (char *) hash;
1753
1754 }  /* end of my_crypt() */
1755 /*}}}*/
1756
1757
1758 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1759 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1760 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1761
1762 /* 8.3, remove() is now broken on symbolic links */
1763 static int rms_erase(const char * vmsname);
1764
1765
1766 /* mp_do_kill_file
1767  * A little hack to get around a bug in some implementation of remove()
1768  * that do not know how to delete a directory
1769  *
1770  * Delete any file to which user has control access, regardless of whether
1771  * delete access is explicitly allowed.
1772  * Limitations: User must have write access to parent directory.
1773  *              Does not block signals or ASTs; if interrupted in midstream
1774  *              may leave file with an altered ACL.
1775  * HANDLE WITH CARE!
1776  */
1777 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1778 static int
1779 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1780 {
1781     char *vmsname;
1782     char *rslt;
1783     unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1784     unsigned long int cxt = 0, aclsts, fndsts;
1785     int rmsts = -1;
1786     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1787     struct myacedef {
1788       unsigned char myace$b_length;
1789       unsigned char myace$b_type;
1790       unsigned short int myace$w_flags;
1791       unsigned long int myace$l_access;
1792       unsigned long int myace$l_ident;
1793     } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1794                  ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1795       oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1796      struct itmlst_3
1797        findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1798                      {sizeof oldace, ACL$C_READACE,   &oldace, 0},{0,0,0,0}},
1799        addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1800        dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1801        lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1802        ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1803
1804     /* Expand the input spec using RMS, since the CRTL remove() and
1805      * system services won't do this by themselves, so we may miss
1806      * a file "hiding" behind a logical name or search list. */
1807     vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1808     if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1809
1810     rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1811     if (rslt == NULL) {
1812         PerlMem_free(vmsname);
1813         return -1;
1814       }
1815
1816     /* Erase the file */
1817     rmsts = rms_erase(vmsname);
1818
1819     /* Did it succeed */
1820     if ($VMS_STATUS_SUCCESS(rmsts)) {
1821         PerlMem_free(vmsname);
1822         return 0;
1823       }
1824
1825     /* If not, can changing protections help? */
1826     if (rmsts != RMS$_PRV) {
1827       set_vaxc_errno(rmsts);
1828       PerlMem_free(vmsname);
1829       return -1;
1830     }
1831
1832     /* No, so we get our own UIC to use as a rights identifier,
1833      * and the insert an ACE at the head of the ACL which allows us
1834      * to delete the file.
1835      */
1836     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1837     fildsc.dsc$w_length = strlen(vmsname);
1838     fildsc.dsc$a_pointer = vmsname;
1839     cxt = 0;
1840     newace.myace$l_ident = oldace.myace$l_ident;
1841     rmsts = -1;
1842     if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1843       switch (aclsts) {
1844         case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1845           set_errno(ENOENT); break;
1846         case RMS$_DIR:
1847           set_errno(ENOTDIR); break;
1848         case RMS$_DEV:
1849           set_errno(ENODEV); break;
1850         case RMS$_SYN: case SS$_INVFILFOROP:
1851           set_errno(EINVAL); break;
1852         case RMS$_PRV:
1853           set_errno(EACCES); break;
1854         default:
1855           _ckvmssts_noperl(aclsts);
1856       }
1857       set_vaxc_errno(aclsts);
1858       PerlMem_free(vmsname);
1859       return -1;
1860     }
1861     /* Grab any existing ACEs with this identifier in case we fail */
1862     aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1863     if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1864                     || fndsts == SS$_NOMOREACE ) {
1865       /* Add the new ACE . . . */
1866       if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1867         goto yourroom;
1868
1869       rmsts = rms_erase(vmsname);
1870       if ($VMS_STATUS_SUCCESS(rmsts)) {
1871         rmsts = 0;
1872         }
1873         else {
1874         rmsts = -1;
1875         /* We blew it - dir with files in it, no write priv for
1876          * parent directory, etc.  Put things back the way they were. */
1877         if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1878           goto yourroom;
1879         if (fndsts & 1) {
1880           addlst[0].bufadr = &oldace;
1881           if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1882             goto yourroom;
1883         }
1884       }
1885     }
1886
1887     yourroom:
1888     fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1889     /* We just deleted it, so of course it's not there.  Some versions of
1890      * VMS seem to return success on the unlock operation anyhow (after all
1891      * the unlock is successful), but others don't.
1892      */
1893     if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1894     if (aclsts & 1) aclsts = fndsts;
1895     if (!(aclsts & 1)) {
1896       set_errno(EVMSERR);
1897       set_vaxc_errno(aclsts);
1898     }
1899
1900     PerlMem_free(vmsname);
1901     return rmsts;
1902
1903 }  /* end of kill_file() */
1904 /*}}}*/
1905
1906
1907 /*{{{int do_rmdir(char *name)*/
1908 int
1909 Perl_do_rmdir(pTHX_ const char *name)
1910 {
1911     char * dirfile;
1912     int retval;
1913     Stat_t st;
1914
1915     /* lstat returns a VMS fileified specification of the name */
1916     /* that is looked up, and also lets verifies that this is a directory */
1917
1918     retval = flex_lstat(name, &st);
1919     if (retval != 0) {
1920         char * ret_spec;
1921
1922         /* Due to a historical feature, flex_stat/lstat can not see some */
1923         /* Unix format file names that the rest of the CRTL can see */
1924         /* Fixing that feature will cause some perl tests to fail */
1925         /* So try this one more time. */
1926
1927         retval = lstat(name, &st.crtl_stat);
1928         if (retval != 0)
1929             return -1;
1930
1931         /* force it to a file spec for the kill file to work. */
1932         ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1933         if (ret_spec == NULL) {
1934             errno = EIO;
1935             return -1;
1936         }
1937     }
1938
1939     if (!S_ISDIR(st.st_mode)) {
1940         errno = ENOTDIR;
1941         retval = -1;
1942     }
1943     else {
1944         dirfile = st.st_devnam;
1945
1946         /* It may be possible for flex_stat to find a file and vmsify() to */
1947         /* fail with ODS-2 specifications.  mp_do_kill_file can not deal */
1948         /* with that case, so fail it */
1949         if (dirfile[0] == 0) {
1950             errno = EIO;
1951             return -1;
1952         }
1953
1954         retval = mp_do_kill_file(aTHX_ dirfile, 1);
1955     }
1956
1957     return retval;
1958
1959 }  /* end of do_rmdir */
1960 /*}}}*/
1961
1962 /* kill_file
1963  * Delete any file to which user has control access, regardless of whether
1964  * delete access is explicitly allowed.
1965  * Limitations: User must have write access to parent directory.
1966  *              Does not block signals or ASTs; if interrupted in midstream
1967  *              may leave file with an altered ACL.
1968  * HANDLE WITH CARE!
1969  */
1970 /*{{{int kill_file(char *name)*/
1971 int
1972 Perl_kill_file(pTHX_ const char *name)
1973 {
1974     char * vmsfile;
1975     Stat_t st;
1976     int rmsts;
1977
1978     /* Convert the filename to VMS format and see if it is a directory */
1979     /* flex_lstat returns a vmsified file specification */
1980     rmsts = flex_lstat(name, &st);
1981     if (rmsts != 0) {
1982
1983         /* Due to a historical feature, flex_stat/lstat can not see some */
1984         /* Unix format file names that the rest of the CRTL can see when */
1985         /* ODS-2 file specifications are in use. */
1986         /* Fixing that feature will cause some perl tests to fail */
1987         /* [.lib.ExtUtils.t]Manifest.t is one of them */
1988         st.st_mode = 0;
1989         vmsfile = (char *) name; /* cast ok */
1990
1991     } else {
1992         vmsfile = st.st_devnam;
1993         if (vmsfile[0] == 0) {
1994             /* It may be possible for flex_stat to find a file and vmsify() */
1995             /* to fail with ODS-2 specifications.  mp_do_kill_file can not */
1996             /* deal with that case, so fail it */
1997             errno = EIO;
1998             return -1;
1999         }
2000     }
2001
2002     /* Remove() is allowed to delete directories, according to the X/Open
2003      * specifications.
2004      * This may need special handling to work with the ACL hacks.
2005      */
2006     if (S_ISDIR(st.st_mode)) {
2007         rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2008         return rmsts;
2009     }
2010
2011     rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2012
2013     /* Need to delete all versions ? */
2014     if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2015         int i = 0;
2016
2017         /* Just use lstat() here as do not need st_dev */
2018         /* and we know that the file is in VMS format or that */
2019         /* because of a historical bug, flex_stat can not see the file */
2020         while (lstat(vmsfile, (stat_t *)&st) == 0) {
2021             rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2022             if (rmsts != 0)
2023                 break;
2024             i++;
2025
2026             /* Make sure that we do not loop forever */
2027             if (i > 32767) {
2028                 errno = EIO;
2029                 rmsts = -1;
2030                 break;
2031             }
2032         }
2033     }
2034
2035     return rmsts;
2036
2037 }  /* end of kill_file() */
2038 /*}}}*/
2039
2040
2041 /*{{{int my_mkdir(char *,Mode_t)*/
2042 int
2043 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2044 {
2045   STRLEN dirlen = strlen(dir);
2046
2047   /* zero length string sometimes gives ACCVIO */
2048   if (dirlen == 0) return -1;
2049
2050   /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2051    * null file name/type.  However, it's commonplace under Unix,
2052    * so we'll allow it for a gain in portability.
2053    */
2054   if (dir[dirlen-1] == '/') {
2055     char *newdir = savepvn(dir,dirlen-1);
2056     int ret = mkdir(newdir,mode);
2057     Safefree(newdir);
2058     return ret;
2059   }
2060   else return mkdir(dir,mode);
2061 }  /* end of my_mkdir */
2062 /*}}}*/
2063
2064 /*{{{int my_chdir(char *)*/
2065 int
2066 Perl_my_chdir(pTHX_ const char *dir)
2067 {
2068   STRLEN dirlen = strlen(dir);
2069   const char *dir1 = dir;
2070
2071   /* POSIX says we should set ENOENT for zero length string. */
2072   if (dirlen == 0) {
2073     SETERRNO(ENOENT, RMS$_DNF);
2074     return -1;
2075   }
2076
2077   /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2078    * This does not work if DECC$EFS_CHARSET is active.  Hack it here
2079    * so that existing scripts do not need to be changed.
2080    */
2081   while ((dirlen > 0) && (*dir1 == ' ')) {
2082     dir1++;
2083     dirlen--;
2084   }
2085
2086   /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2087    * that implies
2088    * null file name/type.  However, it's commonplace under Unix,
2089    * so we'll allow it for a gain in portability.
2090    *
2091    *  '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2092    */
2093   if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2094       char *newdir;
2095       int ret;
2096       newdir = (char *)PerlMem_malloc(dirlen);
2097       if (newdir ==NULL)
2098           _ckvmssts_noperl(SS$_INSFMEM);
2099       memcpy(newdir, dir1, dirlen-1);
2100       newdir[dirlen-1] = '\0';
2101       ret = chdir(newdir);
2102       PerlMem_free(newdir);
2103       return ret;
2104   }
2105   else return chdir(dir1);
2106 }  /* end of my_chdir */
2107 /*}}}*/
2108
2109
2110 /*{{{int my_chmod(char *, mode_t)*/
2111 int
2112 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2113 {
2114   Stat_t st;
2115   int ret = -1;
2116   char * changefile;
2117   STRLEN speclen = strlen(file_spec);
2118
2119   /* zero length string sometimes gives ACCVIO */
2120   if (speclen == 0) return -1;
2121
2122   /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2123    * that implies null file name/type.  However, it's commonplace under Unix,
2124    * so we'll allow it for a gain in portability.
2125    *
2126    * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2127    * in VMS file.dir notation.
2128    */
2129   changefile = (char *) file_spec; /* cast ok */
2130   ret = flex_lstat(file_spec, &st);
2131   if (ret != 0) {
2132
2133         /* Due to a historical feature, flex_stat/lstat can not see some */
2134         /* Unix format file names that the rest of the CRTL can see when */
2135         /* ODS-2 file specifications are in use. */
2136         /* Fixing that feature will cause some perl tests to fail */
2137         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2138         st.st_mode = 0;
2139
2140   } else {
2141       /* It may be possible to get here with nothing in st_devname */
2142       /* chmod still may work though */
2143       if (st.st_devnam[0] != 0) {
2144           changefile = st.st_devnam;
2145       }
2146   }
2147   ret = chmod(changefile, mode);
2148   return ret;
2149 }  /* end of my_chmod */
2150 /*}}}*/
2151
2152
2153 /*{{{FILE *my_tmpfile()*/
2154 FILE *
2155 my_tmpfile(void)
2156 {
2157   FILE *fp;
2158   char *cp;
2159
2160   if ((fp = tmpfile())) return fp;
2161
2162   cp = (char *)PerlMem_malloc(L_tmpnam+24);
2163   if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2164
2165   if (DECC_FILENAME_UNIX_ONLY == 0)
2166     strcpy(cp,"Sys$Scratch:");
2167   else
2168     strcpy(cp,"/tmp/");
2169   tmpnam(cp+strlen(cp));
2170   strcat(cp,".Perltmp");
2171   fp = fopen(cp,"w+","fop=dlt");
2172   PerlMem_free(cp);
2173   return fp;
2174 }
2175 /*}}}*/
2176
2177
2178 /*
2179  * The C RTL's sigaction fails to check for invalid signal numbers so we 
2180  * help it out a bit.  The docs are correct, but the actual routine doesn't
2181  * do what the docs say it will.
2182  */
2183 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2184 int
2185 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act, 
2186                    struct sigaction* oact)
2187 {
2188   if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2189         SETERRNO(EINVAL, SS$_INVARG);
2190         return -1;
2191   }
2192   return sigaction(sig, act, oact);
2193 }
2194 /*}}}*/
2195
2196 #include <errnodef.h>
2197
2198 /* We implement our own kill() using the undocumented system service
2199    sys$sigprc for one of two reasons:
2200
2201    1.) If the kill() in an older CRTL uses sys$forcex, causing the
2202    target process to do a sys$exit, which usually can't be handled 
2203    gracefully...certainly not by Perl and the %SIG{} mechanism.
2204
2205    2.) If the kill() in the CRTL can't be called from a signal
2206    handler without disappearing into the ether, i.e., the signal
2207    it purportedly sends is never trapped. Still true as of VMS 7.3.
2208
2209    sys$sigprc has the same parameters as sys$forcex, but throws an exception
2210    in the target process rather than calling sys$exit.
2211
2212    Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2213    on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2214    provide.  On VMS 7.0+ this is taken care of by doing sys$sigprc
2215    with condition codes C$_SIG0+nsig*8, catching the exception on the 
2216    target process and resignaling with appropriate arguments.
2217
2218    But we don't have that VMS 7.0+ exception handler, so if you
2219    Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS.  Oh well.
2220
2221    Also note that SIGTERM is listed in the docs as being "unimplemented",
2222    yet always seems to be signaled with a VMS condition code of 4 (and
2223    correctly handled for that code).  So we hardwire it in.
2224
2225    Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2226    number to see if it's valid.  So Perl_my_kill(pid,0) returns -1 rather
2227    than signalling with an unrecognized (and unhandled by CRTL) code.
2228 */
2229
2230 #define _MY_SIG_MAX 28
2231
2232 static unsigned int
2233 Perl_sig_to_vmscondition_int(int sig)
2234 {
2235     static unsigned int sig_code[_MY_SIG_MAX+1] = 
2236     {
2237         0,                  /*  0 ZERO     */
2238         SS$_HANGUP,         /*  1 SIGHUP   */
2239         SS$_CONTROLC,       /*  2 SIGINT   */
2240         SS$_CONTROLY,       /*  3 SIGQUIT  */
2241         SS$_RADRMOD,        /*  4 SIGILL   */
2242         SS$_BREAK,          /*  5 SIGTRAP  */
2243         SS$_OPCCUS,         /*  6 SIGABRT  */
2244         SS$_COMPAT,         /*  7 SIGEMT   */
2245         SS$_HPARITH,        /*  8 SIGFPE AXP */
2246         SS$_ABORT,          /*  9 SIGKILL  */
2247         SS$_ACCVIO,         /* 10 SIGBUS   */
2248         SS$_ACCVIO,         /* 11 SIGSEGV  */
2249         SS$_BADPARAM,       /* 12 SIGSYS   */
2250         SS$_NOMBX,          /* 13 SIGPIPE  */
2251         SS$_ASTFLT,         /* 14 SIGALRM  */
2252         4,                  /* 15 SIGTERM  */
2253         0,                  /* 16 SIGUSR1  */
2254         0,                  /* 17 SIGUSR2  */
2255         0,                  /* 18 */
2256         0,                  /* 19 */
2257         0,                  /* 20 SIGCHLD  */
2258         0,                  /* 21 SIGCONT  */
2259         0,                  /* 22 SIGSTOP  */
2260         0,                  /* 23 SIGTSTP  */
2261         0,                  /* 24 SIGTTIN  */
2262         0,                  /* 25 SIGTTOU  */
2263         0,                  /* 26 */
2264         0,                  /* 27 */
2265         0                   /* 28 SIGWINCH  */
2266     };
2267
2268     static int initted = 0;
2269     if (!initted) {
2270         initted = 1;
2271         sig_code[16] = C$_SIGUSR1;
2272         sig_code[17] = C$_SIGUSR2;
2273         sig_code[20] = C$_SIGCHLD;
2274         sig_code[28] = C$_SIGWINCH;
2275     }
2276
2277     if (sig < _SIG_MIN) return 0;
2278     if (sig > _MY_SIG_MAX) return 0;
2279     return sig_code[sig];
2280 }
2281
2282 unsigned int
2283 Perl_sig_to_vmscondition(int sig)
2284 {
2285 #ifdef SS$_DEBUG
2286     if (vms_debug_on_exception != 0)
2287         lib$signal(SS$_DEBUG);
2288 #endif
2289     return Perl_sig_to_vmscondition_int(sig);
2290 }
2291
2292
2293 #ifdef KILL_BY_SIGPRC
2294 #define sys$sigprc SYS$SIGPRC
2295 #ifdef __cplusplus
2296 extern "C" {
2297 #endif
2298 int sys$sigprc(unsigned int *pidadr,
2299                struct dsc$descriptor_s *prcname,
2300                unsigned int code);
2301 #ifdef __cplusplus
2302 }
2303 #endif
2304
2305 int
2306 Perl_my_kill(int pid, int sig)
2307 {
2308     int iss;
2309     unsigned int code;
2310
2311      /* sig 0 means validate the PID */
2312     /*------------------------------*/
2313     if (sig == 0) {
2314         const unsigned long int jpicode = JPI$_PID;
2315         pid_t ret_pid;
2316         int status;
2317         status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2318         if ($VMS_STATUS_SUCCESS(status))
2319            return 0;
2320         switch (status) {
2321         case SS$_NOSUCHNODE:
2322         case SS$_UNREACHABLE:
2323         case SS$_NONEXPR:
2324            errno = ESRCH;
2325            break;
2326         case SS$_NOPRIV:
2327            errno = EPERM;
2328            break;
2329         default:
2330            errno = EVMSERR;
2331         }
2332         vaxc$errno=status;
2333         return -1;
2334     }
2335
2336     code = Perl_sig_to_vmscondition_int(sig);
2337
2338     if (!code) {
2339         SETERRNO(EINVAL, SS$_BADPARAM);
2340         return -1;
2341     }
2342
2343     /* Per official UNIX specification: If pid = 0, or negative then
2344      * signals are to be sent to multiple processes.
2345      *  pid = 0 - all processes in group except ones that the system exempts
2346      *  pid = -1 - all processes except ones that the system exempts
2347      *  pid = -n - all processes in group (abs(n)) except ... 
2348      *
2349      * Handle these via killpg, which is redundant for the -n case, since OP_KILL
2350      * in doio.c already does that. killpg currently does not support the -1 case.
2351      */
2352
2353     if (pid <= 0) {
2354         return killpg(-pid, sig);
2355     }
2356
2357     iss = sys$sigprc((unsigned int *)&pid,0,code);
2358     if (iss&1) return 0;
2359
2360     switch (iss) {
2361       case SS$_NOPRIV:
2362         set_errno(EPERM);  break;
2363       case SS$_NONEXPR:  
2364       case SS$_NOSUCHNODE:
2365       case SS$_UNREACHABLE:
2366         set_errno(ESRCH);  break;
2367       case SS$_INSFMEM:
2368         set_errno(ENOMEM); break;
2369       default:
2370         _ckvmssts_noperl(iss);
2371         set_errno(EVMSERR);
2372     } 
2373     set_vaxc_errno(iss);
2374  
2375     return -1;
2376 }
2377 #endif
2378
2379 int
2380 Perl_my_killpg(pid_t master_pid, int signum)
2381 {
2382     int pid, status, i;
2383     unsigned long int jpi_context;
2384     unsigned short int iosb[4];
2385     struct itmlst_3  il3[3];
2386
2387     /* All processes on the system?  Seems dangerous, but it looks
2388      * like we could implement this pretty easily with a wildcard
2389      * input to sys$process_scan.
2390      */
2391     if (master_pid == -1) {
2392         SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2393         return -1;
2394     }
2395
2396     /* All processes in the current process group; find the master
2397      * pid for the current process.
2398      */
2399     if (master_pid == 0) {
2400         i = 0;
2401         il3[i].buflen   = sizeof( int );
2402         il3[i].itmcode   = JPI$_MASTER_PID;
2403         il3[i].bufadr   = &master_pid;
2404         il3[i++].retlen = NULL;
2405
2406         il3[i].buflen   = 0;
2407         il3[i].itmcode   = 0;
2408         il3[i].bufadr   = NULL;
2409         il3[i++].retlen = NULL;
2410
2411         status = sys$getjpiw(EFN$C_ENF, NULL, NULL, il3, iosb, NULL, 0);
2412         if ($VMS_STATUS_SUCCESS(status))
2413             status = iosb[0];
2414
2415         switch (status) {
2416             case SS$_NORMAL:
2417                 break;
2418             case SS$_NOPRIV:
2419             case SS$_SUSPENDED:
2420                 SETERRNO(EPERM, status);
2421                 break;
2422             case SS$_NOMOREPROC:
2423             case SS$_NONEXPR:
2424             case SS$_NOSUCHNODE:
2425             case SS$_UNREACHABLE:
2426                 SETERRNO(ESRCH, status);
2427                 break;
2428             case SS$_ACCVIO:
2429             case SS$_BADPARAM:
2430                 SETERRNO(EINVAL, status);
2431                 break;
2432             default:
2433                 SETERRNO(EVMSERR, status);
2434         }
2435         if (!$VMS_STATUS_SUCCESS(status))
2436             return -1;
2437     }
2438
2439     /* Set up a process context for those processes we will scan
2440      * with sys$getjpiw.  Ask for all processes belonging to the
2441      * master pid.
2442      */
2443
2444     i = 0;
2445     il3[i].buflen   = 0;
2446     il3[i].itmcode   = PSCAN$_MASTER_PID;
2447     il3[i].bufadr   = (void *)master_pid;
2448     il3[i++].retlen = NULL;
2449
2450     il3[i].buflen   = 0;
2451     il3[i].itmcode   = 0;
2452     il3[i].bufadr   = NULL;
2453     il3[i++].retlen = NULL;
2454
2455     status = sys$process_scan(&jpi_context, il3);
2456     switch (status) {
2457         case SS$_NORMAL:
2458             break;
2459         case SS$_ACCVIO:
2460         case SS$_BADPARAM:
2461         case SS$_IVBUFLEN:
2462         case SS$_IVSSRQ:
2463             SETERRNO(EINVAL, status);
2464             break;
2465         default:
2466             SETERRNO(EVMSERR, status);
2467     }
2468     if (!$VMS_STATUS_SUCCESS(status))
2469         return -1;
2470
2471     i = 0;
2472     il3[i].buflen   = sizeof(int);
2473     il3[i].itmcode  = JPI$_PID;
2474     il3[i].bufadr   = &pid;
2475     il3[i++].retlen = NULL;
2476
2477     il3[i].buflen   = 0;
2478     il3[i].itmcode  = 0;
2479     il3[i].bufadr   = NULL;
2480     il3[i++].retlen = NULL;
2481
2482     /* Loop through the processes matching our specified criteria
2483      */
2484
2485     while (1) {
2486         /* Find the next process...
2487          */
2488         status = sys$getjpiw( EFN$C_ENF, &jpi_context, NULL, il3, iosb, NULL, 0);
2489         if ($VMS_STATUS_SUCCESS(status)) status = iosb[0];
2490
2491         switch (status) {
2492             case SS$_NORMAL:
2493                 if (kill(pid, signum) == -1)
2494                     break;
2495
2496                 continue;     /* next process */
2497             case SS$_NOPRIV:
2498             case SS$_SUSPENDED:
2499                 SETERRNO(EPERM, status);
2500                 break;
2501             case SS$_NOMOREPROC:
2502                 break;
2503             case SS$_NONEXPR:
2504             case SS$_NOSUCHNODE:
2505             case SS$_UNREACHABLE:
2506                 SETERRNO(ESRCH, status);
2507                 break;
2508             case SS$_ACCVIO:
2509             case SS$_BADPARAM:
2510                 SETERRNO(EINVAL, status);
2511                 break;
2512             default:
2513                SETERRNO(EVMSERR, status);
2514         }
2515
2516         if (!$VMS_STATUS_SUCCESS(status))
2517             break;
2518     }
2519
2520     /* Release context-related resources.
2521      */
2522     (void) sys$process_scan(&jpi_context);
2523
2524     if (status != SS$_NOMOREPROC)
2525         return -1;
2526
2527     return 0;
2528 }
2529
2530 /* Routine to convert a VMS status code to a UNIX status code.
2531 ** More tricky than it appears because of conflicting conventions with
2532 ** existing code.
2533 **
2534 ** VMS status codes are a bit mask, with the least significant bit set for
2535 ** success.
2536 **
2537 ** Special UNIX status of EVMSERR indicates that no translation is currently
2538 ** available, and programs should check the VMS status code.
2539 **
2540 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2541 ** decoding.
2542 */
2543
2544 #ifndef C_FACILITY_NO
2545 #define C_FACILITY_NO 0x350000
2546 #endif
2547 #ifndef DCL_IVVERB
2548 #define DCL_IVVERB 0x38090
2549 #endif
2550
2551 int
2552 Perl_vms_status_to_unix(int vms_status, int child_flag)
2553 {
2554   int facility;
2555   int fac_sp;
2556   int msg_no;
2557   int msg_status;
2558   int unix_status;
2559
2560   /* Assume the best or the worst */
2561   if (vms_status & STS$M_SUCCESS)
2562     unix_status = 0;
2563   else
2564     unix_status = EVMSERR;
2565
2566   msg_status = vms_status & ~STS$M_CONTROL;
2567
2568   facility = vms_status & STS$M_FAC_NO;
2569   fac_sp = vms_status & STS$M_FAC_SP;
2570   msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2571
2572   if (((facility == 0) || (fac_sp == 0))  && (child_flag == 0)) {
2573     switch(msg_no) {
2574     case SS$_NORMAL:
2575         unix_status = 0;
2576         break;
2577     case SS$_ACCVIO:
2578         unix_status = EFAULT;
2579         break;
2580     case SS$_DEVOFFLINE:
2581         unix_status = EBUSY;
2582         break;
2583     case SS$_CLEARED:
2584         unix_status = ENOTCONN;
2585         break;
2586     case SS$_IVCHAN:
2587     case SS$_IVLOGNAM:
2588     case SS$_BADPARAM:
2589     case SS$_IVLOGTAB:
2590     case SS$_NOLOGNAM:
2591     case SS$_NOLOGTAB:
2592     case SS$_INVFILFOROP:
2593     case SS$_INVARG:
2594     case SS$_NOSUCHID:
2595     case SS$_IVIDENT:
2596         unix_status = EINVAL;
2597         break;
2598     case SS$_UNSUPPORTED:
2599         unix_status = ENOTSUP;
2600         break;
2601     case SS$_FILACCERR:
2602     case SS$_NOGRPPRV:
2603     case SS$_NOSYSPRV:
2604         unix_status = EACCES;
2605         break;
2606     case SS$_DEVICEFULL:
2607         unix_status = ENOSPC;
2608         break;
2609     case SS$_NOSUCHDEV:
2610         unix_status = ENODEV;
2611         break;
2612     case SS$_NOSUCHFILE:
2613     case SS$_NOSUCHOBJECT:
2614         unix_status = ENOENT;
2615         break;
2616     case SS$_ABORT:                                 /* Fatal case */
2617     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2618     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2619         unix_status = EINTR;
2620         break;
2621     case SS$_BUFFEROVF:
2622         unix_status = E2BIG;
2623         break;
2624     case SS$_INSFMEM:
2625         unix_status = ENOMEM;
2626         break;
2627     case SS$_NOPRIV:
2628         unix_status = EPERM;
2629         break;
2630     case SS$_NOSUCHNODE:
2631     case SS$_UNREACHABLE:
2632         unix_status = ESRCH;
2633         break;
2634     case SS$_NONEXPR:
2635         unix_status = ECHILD;
2636         break;
2637     default:
2638         if ((facility == 0) && (msg_no < 8)) {
2639           /* These are not real VMS status codes so assume that they are
2640           ** already UNIX status codes
2641           */
2642           unix_status = msg_no;
2643           break;
2644         }
2645     }
2646   }
2647   else {
2648     /* Translate a POSIX exit code to a UNIX exit code */
2649     if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000))  {
2650         unix_status = (msg_no & 0x07F8) >> 3;
2651     }
2652     else {
2653
2654          /* Documented traditional behavior for handling VMS child exits */
2655         /*--------------------------------------------------------------*/
2656         if (child_flag != 0) {
2657
2658              /* Success / Informational return 0 */
2659             /*----------------------------------*/
2660             if (msg_no & STS$K_SUCCESS)
2661                 return 0;
2662
2663              /* Warning returns 1 */
2664             /*-------------------*/
2665             if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2666                 return 1;
2667
2668              /* Everything else pass through the severity bits */
2669             /*------------------------------------------------*/
2670             return (msg_no & STS$M_SEVERITY);
2671         }
2672
2673          /* Normal VMS status to ERRNO mapping attempt */
2674         /*--------------------------------------------*/
2675         switch(msg_status) {
2676         /* case RMS$_EOF: */ /* End of File */
2677         case RMS$_FNF:  /* File Not Found */
2678         case RMS$_DNF:  /* Dir Not Found */
2679                 unix_status = ENOENT;
2680                 break;
2681         case RMS$_RNF:  /* Record Not Found */
2682                 unix_status = ESRCH;
2683                 break;
2684         case RMS$_DIR:
2685                 unix_status = ENOTDIR;
2686                 break;
2687         case RMS$_DEV:
2688                 unix_status = ENODEV;
2689                 break;
2690         case RMS$_IFI:
2691         case RMS$_FAC:
2692         case RMS$_ISI:
2693                 unix_status = EBADF;
2694                 break;
2695         case RMS$_FEX:
2696                 unix_status = EEXIST;
2697                 break;
2698         case RMS$_SYN:
2699         case RMS$_FNM:
2700         case LIB$_INVSTRDES:
2701         case LIB$_INVARG:
2702         case LIB$_NOSUCHSYM:
2703         case LIB$_INVSYMNAM:
2704         case DCL_IVVERB:
2705                 unix_status = EINVAL;
2706                 break;
2707         case CLI$_BUFOVF:
2708         case RMS$_RTB:
2709         case CLI$_TKNOVF:
2710         case CLI$_RSLOVF:
2711                 unix_status = E2BIG;
2712                 break;
2713         case RMS$_PRV:  /* No privilege */
2714         case RMS$_ACC:  /* ACP file access failed */
2715         case RMS$_WLK:  /* Device write locked */
2716                 unix_status = EACCES;
2717                 break;
2718         case RMS$_MKD:  /* Failed to mark for delete */
2719                 unix_status = EPERM;
2720                 break;
2721         /* case RMS$_NMF: */  /* No more files */
2722         }
2723     }
2724   }
2725
2726   return unix_status;
2727
2728
2729 /* Try to guess at what VMS error status should go with a UNIX errno
2730  * value.  This is hard to do as there could be many possible VMS
2731  * error statuses that caused the errno value to be set.
2732  */
2733
2734 int
2735 Perl_unix_status_to_vms(int unix_status)
2736 {
2737     int test_unix_status;
2738
2739      /* Trivial cases first */
2740     /*---------------------*/
2741     if (unix_status == EVMSERR)
2742         return vaxc$errno;
2743
2744      /* Is vaxc$errno sane? */
2745     /*---------------------*/
2746     test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2747     if (test_unix_status == unix_status)
2748         return vaxc$errno;
2749
2750      /* If way out of range, must be VMS code already */
2751     /*-----------------------------------------------*/
2752     if (unix_status > EVMSERR)
2753         return unix_status;
2754
2755      /* If out of range, punt */
2756     /*-----------------------*/
2757     if (unix_status > __ERRNO_MAX)
2758         return SS$_ABORT;
2759
2760
2761      /* Ok, now we have to do it the hard way. */
2762     /*----------------------------------------*/
2763     switch(unix_status) {
2764     case 0:     return SS$_NORMAL;
2765     case EPERM: return SS$_NOPRIV;
2766     case ENOENT: return SS$_NOSUCHOBJECT;
2767     case ESRCH: return SS$_UNREACHABLE;
2768     case EINTR: return SS$_ABORT;
2769     /* case EIO: */
2770     /* case ENXIO:  */
2771     case E2BIG: return SS$_BUFFEROVF;
2772     /* case ENOEXEC */
2773     case EBADF: return RMS$_IFI;
2774     case ECHILD: return SS$_NONEXPR;
2775     /* case EAGAIN */
2776     case ENOMEM: return SS$_INSFMEM;
2777     case EACCES: return SS$_FILACCERR;
2778     case EFAULT: return SS$_ACCVIO;
2779     /* case ENOTBLK */
2780     case EBUSY: return SS$_DEVOFFLINE;
2781     case EEXIST: return RMS$_FEX;
2782     /* case EXDEV */
2783     case ENODEV: return SS$_NOSUCHDEV;
2784     case ENOTDIR: return RMS$_DIR;
2785     /* case EISDIR */
2786     case EINVAL: return SS$_INVARG;
2787     /* case ENFILE */
2788     /* case EMFILE */
2789     /* case ENOTTY */
2790     /* case ETXTBSY */
2791     /* case EFBIG */
2792     case ENOSPC: return SS$_DEVICEFULL;
2793     case ESPIPE: return LIB$_INVARG;
2794     /* case EROFS: */
2795     /* case EMLINK: */
2796     /* case EPIPE: */
2797     /* case EDOM */
2798     case ERANGE: return LIB$_INVARG;
2799     /* case EWOULDBLOCK */
2800     /* case EINPROGRESS */
2801     /* case EALREADY */
2802     /* case ENOTSOCK */
2803     /* case EDESTADDRREQ */
2804     /* case EMSGSIZE */
2805     /* case EPROTOTYPE */
2806     /* case ENOPROTOOPT */
2807     /* case EPROTONOSUPPORT */
2808     /* case ESOCKTNOSUPPORT */
2809     /* case EOPNOTSUPP */
2810     /* case EPFNOSUPPORT */
2811     /* case EAFNOSUPPORT */
2812     /* case EADDRINUSE */
2813     /* case EADDRNOTAVAIL */
2814     /* case ENETDOWN */
2815     /* case ENETUNREACH */
2816     /* case ENETRESET */
2817     /* case ECONNABORTED */
2818     /* case ECONNRESET */
2819     /* case ENOBUFS */
2820     /* case EISCONN */
2821     case ENOTCONN: return SS$_CLEARED;
2822     /* case ESHUTDOWN */
2823     /* case ETOOMANYREFS */
2824     /* case ETIMEDOUT */
2825     /* case ECONNREFUSED */
2826     /* case ELOOP */
2827     /* case ENAMETOOLONG */
2828     /* case EHOSTDOWN */
2829     /* case EHOSTUNREACH */
2830     /* case ENOTEMPTY */
2831     /* case EPROCLIM */
2832     /* case EUSERS  */
2833     /* case EDQUOT  */
2834     /* case ENOMSG  */
2835     /* case EIDRM */
2836     /* case EALIGN */
2837     /* case ESTALE */
2838     /* case EREMOTE */
2839     /* case ENOLCK */
2840     /* case ENOSYS */
2841     /* case EFTYPE */
2842     /* case ECANCELED */
2843     /* case EFAIL */
2844     /* case EINPROG */
2845     case ENOTSUP:
2846         return SS$_UNSUPPORTED;
2847     /* case EDEADLK */
2848     /* case ENWAIT */
2849     /* case EILSEQ */
2850     /* case EBADCAT */
2851     /* case EBADMSG */
2852     /* case EABANDONED */
2853     default:
2854         return SS$_ABORT; /* punt */
2855     }
2856
2857
2858
2859 /* default piping mailbox size */
2860 #define PERL_BUFSIZ        8192
2861
2862
2863 static void
2864 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2865 {
2866   unsigned long int mbxbufsiz;
2867   static unsigned long int syssize = 0;
2868   unsigned long int dviitm = DVI$_DEVNAM;
2869   char csize[LNM$C_NAMLENGTH+1];
2870   int sts;
2871
2872   if (!syssize) {
2873     unsigned long syiitm = SYI$_MAXBUF;
2874     /*
2875      * Get the SYSGEN parameter MAXBUF
2876      *
2877      * If the logical 'PERL_MBX_SIZE' is defined
2878      * use the value of the logical instead of PERL_BUFSIZ, but 
2879      * keep the size between 128 and MAXBUF.
2880      *
2881      */
2882     _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2883   }
2884
2885   if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2886       mbxbufsiz = atoi(csize);
2887   } else {
2888       mbxbufsiz = PERL_BUFSIZ;
2889   }
2890   if (mbxbufsiz < 128) mbxbufsiz = 128;
2891   if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2892
2893   _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2894
2895   sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2896   _ckvmssts_noperl(sts);
2897   namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2898
2899 }  /* end of create_mbx() */
2900
2901
2902 /*{{{  my_popen and my_pclose*/
2903
2904 typedef struct _iosb           IOSB;
2905 typedef struct _iosb*         pIOSB;
2906 typedef struct _pipe           Pipe;
2907 typedef struct _pipe*         pPipe;
2908 typedef struct pipe_details    Info;
2909 typedef struct pipe_details*  pInfo;
2910 typedef struct _srqp            RQE;
2911 typedef struct _srqp*          pRQE;
2912 typedef struct _tochildbuf      CBuf;
2913 typedef struct _tochildbuf*    pCBuf;
2914
2915 struct _iosb {
2916     unsigned short status;
2917     unsigned short count;
2918     unsigned long  dvispec;
2919 };
2920
2921 #pragma member_alignment save
2922 #pragma nomember_alignment quadword
2923 struct _srqp {          /* VMS self-relative queue entry */
2924     unsigned long qptr[2];
2925 };
2926 #pragma member_alignment restore
2927 static RQE  RQE_ZERO = {0,0};
2928
2929 struct _tochildbuf {
2930     RQE             q;
2931     int             eof;
2932     unsigned short  size;
2933     char            *buf;
2934 };
2935
2936 struct _pipe {
2937     RQE            free;
2938     RQE            wait;
2939     int            fd_out;
2940     unsigned short chan_in;
2941     unsigned short chan_out;
2942     char          *buf;
2943     unsigned int   bufsize;
2944     IOSB           iosb;
2945     IOSB           iosb2;
2946     int           *pipe_done;
2947     int            retry;
2948     int            type;
2949     int            shut_on_empty;
2950     int            need_wake;
2951     pPipe         *home;
2952     pInfo          info;
2953     pCBuf          curr;
2954     pCBuf          curr2;
2955 #if defined(PERL_IMPLICIT_CONTEXT)
2956     void            *thx;           /* Either a thread or an interpreter */
2957                                     /* pointer, depending on how we're built */
2958 #endif
2959 };
2960
2961
2962 struct pipe_details
2963 {
2964     pInfo           next;
2965     PerlIO *fp;  /* file pointer to pipe mailbox */
2966     int useFILE; /* using stdio, not perlio */
2967     int pid;   /* PID of subprocess */
2968     int mode;  /* == 'r' if pipe open for reading */
2969     int done;  /* subprocess has completed */
2970     int waiting; /* waiting for completion/closure */
2971     int             closing;        /* my_pclose is closing this pipe */
2972     unsigned long   completion;     /* termination status of subprocess */
2973     pPipe           in;             /* pipe in to sub */
2974     pPipe           out;            /* pipe out of sub */
2975     pPipe           err;            /* pipe of sub's sys$error */
2976     int             in_done;        /* true when in pipe finished */
2977     int             out_done;
2978     int             err_done;
2979     unsigned short  xchan;          /* channel to debug xterm */
2980     unsigned short  xchan_valid;    /* channel is assigned */
2981 };
2982
2983 struct exit_control_block
2984 {
2985     struct exit_control_block *flink;
2986     unsigned long int (*exit_routine)(void);
2987     unsigned long int arg_count;
2988     unsigned long int *status_address;
2989     unsigned long int exit_status;
2990 }; 
2991
2992 typedef struct _closed_pipes    Xpipe;
2993 typedef struct _closed_pipes*  pXpipe;
2994
2995 struct _closed_pipes {
2996     int             pid;            /* PID of subprocess */
2997     unsigned long   completion;     /* termination status of subprocess */
2998 };
2999 #define NKEEPCLOSED 50
3000 static Xpipe closed_list[NKEEPCLOSED];
3001 static int   closed_index = 0;
3002 static int   closed_num = 0;
3003
3004 #define RETRY_DELAY     "0 ::0.20"
3005 #define MAX_RETRY              50
3006
3007 static int pipe_ef = 0;          /* first call to safe_popen inits these*/
3008 static unsigned long mypid;
3009 static unsigned long delaytime[2];
3010
3011 static pInfo open_pipes = NULL;
3012 static $DESCRIPTOR(nl_desc, "NL:");
3013
3014 #define PIPE_COMPLETION_WAIT    30  /* seconds, for EOF/FORCEX wait */
3015
3016
3017
3018 static unsigned long int
3019 pipe_exit_routine(void)
3020 {
3021     pInfo info;
3022     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3023     int sts, did_stuff, j;
3024
3025    /* 
3026     * Flush any pending i/o, but since we are in process run-down, be
3027     * careful about referencing PerlIO structures that may already have
3028     * been deallocated.  We may not even have an interpreter anymore.
3029     */
3030     info = open_pipes;
3031     while (info) {
3032         if (info->fp) {
3033 #if defined(PERL_IMPLICIT_CONTEXT)
3034            /* We need to use the Perl context of the thread that created */
3035            /* the pipe. */
3036            pTHX;
3037            if (info->err)
3038                aTHX = info->err->thx;
3039            else if (info->out)
3040                aTHX = info->out->thx;
3041            else if (info->in)
3042                aTHX = info->in->thx;
3043 #endif
3044            if (!info->useFILE
3045 #if defined(USE_ITHREADS)
3046              && my_perl
3047 #endif
3048 #ifdef USE_PERLIO
3049              && PL_perlio_fd_refcnt 
3050 #endif
3051               )
3052                PerlIO_flush(info->fp);
3053            else 
3054                fflush((FILE *)info->fp);
3055         }
3056         info = info->next;
3057     }
3058
3059     /* 
3060      next we try sending an EOF...ignore if doesn't work, make sure we
3061      don't hang
3062     */
3063     did_stuff = 0;
3064     info = open_pipes;
3065
3066     while (info) {
3067       _ckvmssts_noperl(sys$setast(0));
3068       if (info->in && !info->in->shut_on_empty) {
3069         _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3070                                  0, 0, 0, 0, 0, 0));
3071         info->waiting = 1;
3072         did_stuff = 1;
3073       }
3074       _ckvmssts_noperl(sys$setast(1));
3075       info = info->next;
3076     }
3077
3078     /* wait for EOF to have effect, up to ~ 30 sec [default] */
3079
3080     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3081         int nwait = 0;
3082
3083         info = open_pipes;
3084         while (info) {
3085           _ckvmssts_noperl(sys$setast(0));
3086           if (info->waiting && info->done) 
3087                 info->waiting = 0;
3088           nwait += info->waiting;
3089           _ckvmssts_noperl(sys$setast(1));
3090           info = info->next;
3091         }
3092         if (!nwait) break;
3093         sleep(1);  
3094     }
3095
3096     did_stuff = 0;
3097     info = open_pipes;
3098     while (info) {
3099       _ckvmssts_noperl(sys$setast(0));
3100       if (!info->done) { /* Tap them gently on the shoulder . . .*/
3101         sts = sys$forcex(&info->pid,0,&abort);
3102         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 
3103         did_stuff = 1;
3104       }
3105       _ckvmssts_noperl(sys$setast(1));
3106       info = info->next;
3107     }
3108
3109     /* again, wait for effect */
3110
3111     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3112         int nwait = 0;
3113
3114         info = open_pipes;
3115         while (info) {
3116           _ckvmssts_noperl(sys$setast(0));
3117           if (info->waiting && info->done) 
3118                 info->waiting = 0;
3119           nwait += info->waiting;
3120           _ckvmssts_noperl(sys$setast(1));
3121           info = info->next;
3122         }
3123         if (!nwait) break;
3124         sleep(1);  
3125     }
3126
3127     info = open_pipes;
3128     while (info) {
3129       _ckvmssts_noperl(sys$setast(0));
3130       if (!info->done) {  /* We tried to be nice . . . */
3131         sts = sys$delprc(&info->pid,0);
3132         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 
3133         info->done = 1;  /* sys$delprc is as done as we're going to get. */
3134       }
3135       _ckvmssts_noperl(sys$setast(1));
3136       info = info->next;
3137     }
3138
3139     while(open_pipes) {
3140
3141 #if defined(PERL_IMPLICIT_CONTEXT)
3142       /* We need to use the Perl context of the thread that created */
3143       /* the pipe. */
3144       pTHX;
3145       if (open_pipes->err)
3146           aTHX = open_pipes->err->thx;
3147       else if (open_pipes->out)
3148           aTHX = open_pipes->out->thx;
3149       else if (open_pipes->in)
3150           aTHX = open_pipes->in->thx;
3151 #endif
3152       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3153       else if (!(sts & 1)) retsts = sts;
3154     }
3155     return retsts;
3156 }
3157
3158 static struct exit_control_block pipe_exitblock = 
3159        {(struct exit_control_block *) 0,
3160         pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3161
3162 static void pipe_mbxtofd_ast(pPipe p);
3163 static void pipe_tochild1_ast(pPipe p);
3164 static void pipe_tochild2_ast(pPipe p);
3165
3166 static void
3167 popen_completion_ast(pInfo info)
3168 {
3169   pInfo i = open_pipes;
3170   int iss;
3171
3172   info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3173   closed_list[closed_index].pid = info->pid;
3174   closed_list[closed_index].completion = info->completion;
3175   closed_index++;
3176   if (closed_index == NKEEPCLOSED) 
3177     closed_index = 0;
3178   closed_num++;
3179
3180   while (i) {
3181     if (i == info) break;
3182     i = i->next;
3183   }
3184   if (!i) return;       /* unlinked, probably freed too */
3185
3186   info->done = TRUE;
3187
3188 /*
3189     Writing to subprocess ...
3190             if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3191
3192             chan_out may be waiting for "done" flag, or hung waiting
3193             for i/o completion to child...cancel the i/o.  This will
3194             put it into "snarf mode" (done but no EOF yet) that discards
3195             input.
3196
3197     Output from subprocess (stdout, stderr) needs to be flushed and
3198     shut down.   We try sending an EOF, but if the mbx is full the pipe
3199     routine should still catch the "shut_on_empty" flag, telling it to
3200     use immediate-style reads so that "mbx empty" -> EOF.
3201
3202
3203 */
3204   if (info->in && !info->in_done) {               /* only for mode=w */
3205         if (info->in->shut_on_empty && info->in->need_wake) {
3206             info->in->need_wake = FALSE;
3207             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3208         } else {
3209             _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3210         }
3211   }
3212
3213   if (info->out && !info->out_done) {             /* were we also piping output? */
3214       info->out->shut_on_empty = TRUE;
3215       iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3216       if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3217       _ckvmssts_noperl(iss);
3218   }
3219
3220   if (info->err && !info->err_done) {        /* we were piping stderr */
3221         info->err->shut_on_empty = TRUE;
3222         iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3223         if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3224         _ckvmssts_noperl(iss);
3225   }
3226   _ckvmssts_noperl(sys$setef(pipe_ef));
3227
3228 }
3229
3230 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3231 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3232 static void pipe_infromchild_ast(pPipe p);
3233
3234 /*
3235     I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3236     inside an AST routine without worrying about reentrancy and which Perl
3237     memory allocator is being used.
3238
3239     We read data and queue up the buffers, then spit them out one at a
3240     time to the output mailbox when the output mailbox is ready for one.
3241
3242 */
3243 #define INITIAL_TOCHILDQUEUE  2
3244
3245 static pPipe
3246 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3247 {
3248     pPipe p;
3249     pCBuf b;
3250     char mbx1[64], mbx2[64];
3251     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3252                                       DSC$K_CLASS_S, mbx1},
3253                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3254                                       DSC$K_CLASS_S, mbx2};
3255     unsigned int dviitm = DVI$_DEVBUFSIZ;
3256     int j, n;
3257
3258     n = sizeof(Pipe);
3259     _ckvmssts_noperl(lib$get_vm(&n, &p));
3260
3261     create_mbx(&p->chan_in , &d_mbx1);
3262     create_mbx(&p->chan_out, &d_mbx2);
3263     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3264
3265     p->buf           = 0;
3266     p->shut_on_empty = FALSE;
3267     p->need_wake     = FALSE;
3268     p->type          = 0;
3269     p->retry         = 0;
3270     p->iosb.status   = SS$_NORMAL;
3271     p->iosb2.status  = SS$_NORMAL;
3272     p->free          = RQE_ZERO;
3273     p->wait          = RQE_ZERO;
3274     p->curr          = 0;
3275     p->curr2         = 0;
3276     p->info          = 0;
3277 #ifdef PERL_IMPLICIT_CONTEXT
3278     p->thx           = aTHX;
3279 #endif
3280
3281     n = sizeof(CBuf) + p->bufsize;
3282
3283     for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3284         _ckvmssts_noperl(lib$get_vm(&n, &b));
3285         b->buf = (char *) b + sizeof(CBuf);
3286         _ckvmssts_noperl(lib$insqhi(b, &p->free));
3287     }
3288
3289     pipe_tochild2_ast(p);
3290     pipe_tochild1_ast(p);
3291     strcpy(wmbx, mbx1);
3292     strcpy(rmbx, mbx2);
3293     return p;
3294 }
3295
3296 /*  reads the MBX Perl is writing, and queues */
3297
3298 static void
3299 pipe_tochild1_ast(pPipe p)
3300 {
3301     pCBuf b = p->curr;
3302     int iss = p->iosb.status;
3303     int eof = (iss == SS$_ENDOFFILE);
3304     int sts;
3305 #ifdef PERL_IMPLICIT_CONTEXT
3306     pTHX = p->thx;
3307 #endif
3308
3309     if (p->retry) {
3310         if (eof) {
3311             p->shut_on_empty = TRUE;
3312             b->eof     = TRUE;
3313             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3314         } else  {
3315             _ckvmssts_noperl(iss);
3316         }
3317
3318         b->eof  = eof;
3319         b->size = p->iosb.count;
3320         _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3321         if (p->need_wake) {
3322             p->need_wake = FALSE;
3323             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3324         }
3325     } else {
3326         p->retry = 1;   /* initial call */
3327     }
3328
3329     if (eof) {                  /* flush the free queue, return when done */
3330         int n = sizeof(CBuf) + p->bufsize;
3331         while (1) {
3332             iss = lib$remqti(&p->free, &b);
3333             if (iss == LIB$_QUEWASEMP) return;
3334             _ckvmssts_noperl(iss);
3335             _ckvmssts_noperl(lib$free_vm(&n, &b));
3336         }
3337     }
3338
3339     iss = lib$remqti(&p->free, &b);
3340     if (iss == LIB$_QUEWASEMP) {
3341         int n = sizeof(CBuf) + p->bufsize;
3342         _ckvmssts_noperl(lib$get_vm(&n, &b));
3343         b->buf = (char *) b + sizeof(CBuf);
3344     } else {
3345        _ckvmssts_noperl(iss);
3346     }
3347
3348     p->curr = b;
3349     iss = sys$qio(0,p->chan_in,
3350              IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3351              &p->iosb,
3352              pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3353     if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3354     _ckvmssts_noperl(iss);
3355 }
3356
3357
3358 /* writes queued buffers to output, waits for each to complete before
3359    doing the next */
3360
3361 static void
3362 pipe_tochild2_ast(pPipe p)
3363 {
3364     pCBuf b = p->curr2;
3365     int iss = p->iosb2.status;
3366     int n = sizeof(CBuf) + p->bufsize;
3367     int done = (p->info && p->info->done) ||
3368               iss == SS$_CANCEL || iss == SS$_ABORT;
3369 #if defined(PERL_IMPLICIT_CONTEXT)
3370     pTHX = p->thx;
3371 #endif
3372
3373     do {
3374         if (p->type) {         /* type=1 has old buffer, dispose */
3375             if (p->shut_on_empty) {
3376                 _ckvmssts_noperl(lib$free_vm(&n, &b));
3377             } else {
3378                 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3379             }
3380             p->type = 0;
3381         }
3382
3383         iss = lib$remqti(&p->wait, &b);
3384         if (iss == LIB$_QUEWASEMP) {
3385             if (p->shut_on_empty) {
3386                 if (done) {
3387                     _ckvmssts_noperl(sys$dassgn(p->chan_out));
3388                     *p->pipe_done = TRUE;
3389                     _ckvmssts_noperl(sys$setef(pipe_ef));
3390                 } else {
3391                     _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3392                         &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3393                 }
3394                 return;
3395             }
3396             p->need_wake = TRUE;
3397             return;
3398         }
3399         _ckvmssts_noperl(iss);
3400         p->type = 1;
3401     } while (done);
3402
3403
3404     p->curr2 = b;
3405     if (b->eof) {
3406         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3407             &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3408     } else {
3409         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3410             &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3411     }
3412
3413     return;
3414
3415 }
3416
3417
3418 static pPipe
3419 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3420 {
3421     pPipe p;
3422     char mbx1[64], mbx2[64];
3423     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3424                                       DSC$K_CLASS_S, mbx1},
3425                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3426                                       DSC$K_CLASS_S, mbx2};
3427     unsigned int dviitm = DVI$_DEVBUFSIZ;
3428
3429     int n = sizeof(Pipe);
3430     _ckvmssts_noperl(lib$get_vm(&n, &p));
3431     create_mbx(&p->chan_in , &d_mbx1);
3432     create_mbx(&p->chan_out, &d_mbx2);
3433
3434     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3435     n = p->bufsize * sizeof(char);
3436     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3437     p->shut_on_empty = FALSE;
3438     p->info   = 0;
3439     p->type   = 0;
3440     p->iosb.status = SS$_NORMAL;
3441 #if defined(PERL_IMPLICIT_CONTEXT)
3442     p->thx = aTHX;
3443 #endif
3444     pipe_infromchild_ast(p);
3445
3446     strcpy(wmbx, mbx1);
3447     strcpy(rmbx, mbx2);
3448     return p;
3449 }
3450
3451 static void
3452 pipe_infromchild_ast(pPipe p)
3453 {
3454     int iss = p->iosb.status;
3455     int eof = (iss == SS$_ENDOFFILE);
3456     int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3457     int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3458 #if defined(PERL_IMPLICIT_CONTEXT)
3459     pTHX = p->thx;
3460 #endif
3461
3462     if (p->info && p->info->closing && p->chan_out)  {           /* output shutdown */
3463         _ckvmssts_noperl(sys$dassgn(p->chan_out));
3464         p->chan_out = 0;
3465     }
3466
3467     /* read completed:
3468             input shutdown if EOF from self (done or shut_on_empty)
3469             output shutdown if closing flag set (my_pclose)
3470             send data/eof from child or eof from self
3471             otherwise, re-read (snarf of data from child)
3472     */
3473
3474     if (p->type == 1) {
3475         p->type = 0;
3476         if (myeof && p->chan_in) {                  /* input shutdown */
3477             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3478             p->chan_in = 0;
3479         }
3480
3481         if (p->chan_out) {
3482             if (myeof || kideof) {      /* pass EOF to parent */
3483                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3484                                          pipe_infromchild_ast, p,
3485                                          0, 0, 0, 0, 0, 0));
3486                 return;
3487             } else if (eof) {       /* eat EOF --- fall through to read*/
3488
3489             } else {                /* transmit data */
3490                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3491                                          pipe_infromchild_ast,p,
3492                                          p->buf, p->iosb.count, 0, 0, 0, 0));
3493                 return;
3494             }
3495         }
3496     }
3497
3498     /*  everything shut? flag as done */
3499
3500     if (!p->chan_in && !p->chan_out) {
3501         *p->pipe_done = TRUE;
3502         _ckvmssts_noperl(sys$setef(pipe_ef));
3503         return;
3504     }
3505
3506     /* write completed (or read, if snarfing from child)
3507             if still have input active,
3508                queue read...immediate mode if shut_on_empty so we get EOF if empty
3509             otherwise,
3510                check if Perl reading, generate EOFs as needed
3511     */
3512
3513     if (p->type == 0) {
3514         p->type = 1;
3515         if (p->chan_in) {
3516             iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3517                           pipe_infromchild_ast,p,
3518                           p->buf, p->bufsize, 0, 0, 0, 0);
3519             if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3520             _ckvmssts_noperl(iss);
3521         } else {           /* send EOFs for extra reads */
3522             p->iosb.status = SS$_ENDOFFILE;
3523             p->iosb.dvispec = 0;
3524             _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3525                                      0, 0, 0,
3526                                      pipe_infromchild_ast, p, 0, 0, 0, 0));
3527         }
3528     }
3529 }
3530
3531 static pPipe
3532 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3533 {
3534     pPipe p;
3535     char mbx[64];
3536     unsigned long dviitm = DVI$_DEVBUFSIZ;
3537     struct stat s;
3538     struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3539                                       DSC$K_CLASS_S, mbx};
3540     int n = sizeof(Pipe);
3541
3542     /* things like terminals and mbx's don't need this filter */
3543     if (fd && fstat(fd,&s) == 0) {
3544         unsigned long devchar;
3545         char device[65];
3546         unsigned short dev_len;
3547         struct dsc$descriptor_s d_dev;
3548         char * cptr;
3549         struct item_list_3 items[3];
3550         int status;
3551         unsigned short dvi_iosb[4];
3552
3553         cptr = getname(fd, out, 1);
3554         if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3555         d_dev.dsc$a_pointer = out;
3556         d_dev.dsc$w_length = strlen(out);
3557         d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3558         d_dev.dsc$b_class = DSC$K_CLASS_S;
3559
3560         items[0].len = 4;
3561         items[0].code = DVI$_DEVCHAR;
3562         items[0].bufadr = &devchar;
3563         items[0].retadr = NULL;
3564         items[1].len = 64;
3565         items[1].code = DVI$_FULLDEVNAM;
3566         items[1].bufadr = device;
3567         items[1].retadr = &dev_len;
3568         items[2].len = 0;
3569         items[2].code = 0;
3570
3571         status = sys$getdviw
3572                 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3573         _ckvmssts_noperl(status);
3574         if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3575             device[dev_len] = 0;
3576
3577             if (!(devchar & DEV$M_DIR)) {
3578                 strcpy(out, device);
3579                 return 0;
3580             }
3581         }
3582     }
3583
3584     _ckvmssts_noperl(lib$get_vm(&n, &p));
3585     p->fd_out = dup(fd);
3586     create_mbx(&p->chan_in, &d_mbx);
3587     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3588     n = (p->bufsize+1) * sizeof(char);
3589     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3590     p->shut_on_empty = FALSE;
3591     p->retry = 0;
3592     p->info  = 0;
3593     strcpy(out, mbx);
3594
3595     _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3596                              pipe_mbxtofd_ast, p,
3597                              p->buf, p->bufsize, 0, 0, 0, 0));
3598
3599     return p;
3600 }
3601
3602 static void
3603 pipe_mbxtofd_ast(pPipe p)
3604 {
3605     int iss = p->iosb.status;
3606     int done = p->info->done;
3607     int iss2;
3608     int eof = (iss == SS$_ENDOFFILE);
3609     int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3610     int err = !(iss&1) && !eof;
3611 #if defined(PERL_IMPLICIT_CONTEXT)
3612     pTHX = p->thx;
3613 #endif
3614
3615     if (done && myeof) {               /* end piping */
3616         close(p->fd_out);
3617         sys$dassgn(p->chan_in);
3618         *p->pipe_done = TRUE;
3619         _ckvmssts_noperl(sys$setef(pipe_ef));
3620         return;
3621     }
3622
3623     if (!err && !eof) {             /* good data to send to file */
3624         p->buf[p->iosb.count] = '\n';
3625         iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3626         if (iss2 < 0) {
3627             p->retry++;
3628             if (p->retry < MAX_RETRY) {
3629                 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3630                 return;
3631             }
3632         }
3633         p->retry = 0;
3634     } else if (err) {
3635         _ckvmssts_noperl(iss);
3636     }
3637
3638
3639     iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3640           pipe_mbxtofd_ast, p,
3641           p->buf, p->bufsize, 0, 0, 0, 0);
3642     if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3643     _ckvmssts_noperl(iss);
3644 }
3645
3646
3647 typedef struct _pipeloc     PLOC;
3648 typedef struct _pipeloc*   pPLOC;
3649
3650 struct _pipeloc {
3651     pPLOC   next;
3652     char    dir[NAM$C_MAXRSS+1];
3653 };
3654 static pPLOC  head_PLOC = 0;
3655
3656 void
3657 free_pipelocs(pTHX_ void *head)
3658 {
3659     pPLOC p, pnext;
3660     pPLOC *pHead = (pPLOC *)head;
3661
3662     p = *pHead;
3663     while (p) {
3664         pnext = p->next;
3665         PerlMem_free(p);
3666         p = pnext;
3667     }
3668     *pHead = 0;
3669 }
3670
3671 static void
3672 store_pipelocs(pTHX)
3673 {
3674     int    i;
3675     pPLOC  p;
3676     AV    *av = 0;
3677     SV    *dirsv;
3678     char  *dir, *x;
3679     char  *unixdir;
3680     char  temp[NAM$C_MAXRSS+1];
3681     STRLEN n_a;
3682
3683     if (head_PLOC)  
3684         free_pipelocs(aTHX_ &head_PLOC);
3685
3686 /*  the . directory from @INC comes last */
3687
3688     p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3689     if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3690     p->next = head_PLOC;
3691     head_PLOC = p;
3692     strcpy(p->dir,"./");
3693
3694 /*  get the directory from $^X */
3695
3696     unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3697     if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3698
3699 #ifdef PERL_IMPLICIT_CONTEXT
3700     if (aTHX && PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3701 #else
3702     if (PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3703 #endif
3704         my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3705         x = strrchr(temp,']');
3706         if (x == NULL) {
3707         x = strrchr(temp,'>');
3708           if (x == NULL) {
3709             /* It could be a UNIX path */
3710             x = strrchr(temp,'/');
3711           }
3712         }
3713         if (x)
3714           x[1] = '\0';
3715         else {
3716           /* Got a bare name, so use default directory */
3717           temp[0] = '.';
3718           temp[1] = '\0';
3719         }
3720
3721         if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3722             p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3723             if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3724             p->next = head_PLOC;
3725             head_PLOC = p;
3726             my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3727         }
3728     }
3729
3730 /*  reverse order of @INC entries, skip "." since entered above */
3731
3732 #ifdef PERL_IMPLICIT_CONTEXT
3733     if (aTHX)
3734 #endif
3735     if (PL_incgv) av = GvAVn(PL_incgv);
3736
3737     for (i = 0; av && i <= AvFILL(av); i++) {
3738         dirsv = *av_fetch(av,i,TRUE);
3739
3740         if (SvROK(dirsv)) continue;
3741         dir = SvPVx(dirsv,n_a);
3742         if (strEQ(dir,".")) continue;
3743         if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3744             continue;
3745
3746         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3747         p->next = head_PLOC;
3748         head_PLOC = p;
3749         my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3750     }
3751
3752 /* most likely spot (ARCHLIB) put first in the list */
3753
3754 #ifdef ARCHLIB_EXP
3755     if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3756         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3757         if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3758         p->next = head_PLOC;
3759         head_PLOC = p;
3760         my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3761     }
3762 #endif
3763     PerlMem_free(unixdir);
3764 }
3765
3766 static I32 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective,
3767                                   const char *fname, int opts);
3768 #if !defined(PERL_IMPLICIT_CONTEXT)
3769 #define cando_by_name_int               Perl_cando_by_name_int
3770 #else
3771 #define cando_by_name_int(a,b,c,d)      Perl_cando_by_name_int(aTHX_ a,b,c,d)
3772 #endif
3773
3774 static char *
3775 find_vmspipe(pTHX)
3776 {
3777     static int   vmspipe_file_status = 0;
3778     static char  vmspipe_file[NAM$C_MAXRSS+1];
3779
3780     /* already found? Check and use ... need read+execute permission */
3781
3782     if (vmspipe_file_status == 1) {
3783         if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3784          && cando_by_name_int
3785            (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3786             return vmspipe_file;
3787         }
3788         vmspipe_file_status = 0;
3789     }
3790
3791     /* scan through stored @INC, $^X */
3792
3793     if (vmspipe_file_status == 0) {
3794         char file[NAM$C_MAXRSS+1];
3795         pPLOC  p = head_PLOC;
3796
3797         while (p) {
3798             char * exp_res;
3799             int dirlen;
3800             dirlen = my_strlcpy(file, p->dir, sizeof(file));
3801             my_strlcat(file, "vmspipe.com", sizeof(file));
3802             p = p->next;
3803
3804             exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3805             if (!exp_res) continue;
3806
3807             if (cando_by_name_int
3808                 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3809              && cando_by_name_int
3810                    (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3811                 vmspipe_file_status = 1;
3812                 return vmspipe_file;
3813             }
3814         }
3815         vmspipe_file_status = -1;   /* failed, use tempfiles */
3816     }
3817
3818     return 0;
3819 }
3820
3821 static FILE *
3822 vmspipe_tempfile(pTHX)
3823 {
3824     char file[NAM$C_MAXRSS+1];
3825     FILE *fp;
3826     static int index = 0;
3827     Stat_t s0, s1;
3828     int cmp_result;
3829
3830     /* create a tempfile */
3831
3832     /* we can't go from   W, shr=get to  R, shr=get without
3833        an intermediate vulnerable state, so don't bother trying...
3834
3835        and lib$spawn doesn't shr=put, so have to close the write
3836
3837        So... match up the creation date/time and the FID to
3838        make sure we're dealing with the same file
3839
3840     */
3841
3842     index++;
3843     if (!DECC_FILENAME_UNIX_ONLY) {
3844       sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3845       fp = fopen(file,"w");
3846       if (!fp) {
3847         sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3848         fp = fopen(file,"w");
3849         if (!fp) {
3850             sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3851             fp = fopen(file,"w");
3852         }
3853       }
3854      }
3855      else {
3856       sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3857       fp = fopen(file,"w");
3858       if (!fp) {
3859         sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3860         fp = fopen(file,"w");
3861         if (!fp) {
3862           sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3863           fp = fopen(file,"w");
3864         }
3865       }
3866     }
3867     if (!fp) return 0;  /* we're hosed */
3868
3869     fprintf(fp,"$! 'f$verify(0)'\n");
3870     fprintf(fp,"$!  ---  protect against nonstandard definitions ---\n");
3871     fprintf(fp,"$ perl_cfile  = f$environment(\"procedure\")\n");
3872     fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3873     fprintf(fp,"$ perl_on     = \"set noon\"\n");
3874     fprintf(fp,"$ perl_exit   = \"exit\"\n");
3875     fprintf(fp,"$ perl_del    = \"delete\"\n");
3876     fprintf(fp,"$ pif         = \"if\"\n");
3877     fprintf(fp,"$!  --- define i/o redirection (sys$output set by lib$spawn)\n");
3878     fprintf(fp,"$ pif perl_popen_in  .nes. \"\" then perl_define/user/name_attributes=confine sys$input  'perl_popen_in'\n");
3879     fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error  'perl_popen_err'\n");
3880     fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define      sys$output 'perl_popen_out'\n");
3881     fprintf(fp,"$!  --- build command line to get max possible length\n");
3882     fprintf(fp,"$c=perl_popen_cmd0\n"); 
3883     fprintf(fp,"$c=c+perl_popen_cmd1\n"); 
3884     fprintf(fp,"$c=c+perl_popen_cmd2\n"); 
3885     fprintf(fp,"$x=perl_popen_cmd3\n"); 
3886     fprintf(fp,"$c=c+x\n"); 
3887     fprintf(fp,"$ perl_on\n");
3888     fprintf(fp,"$ 'c'\n");
3889     fprintf(fp,"$ perl_status = $STATUS\n");
3890     fprintf(fp,"$ perl_del  'perl_cfile'\n");
3891     fprintf(fp,"$ perl_exit 'perl_status'\n");
3892     fsync(fileno(fp));
3893
3894     fgetname(fp, file, 1);
3895     fstat(fileno(fp), &s0.crtl_stat);
3896     fclose(fp);
3897
3898     if (DECC_FILENAME_UNIX_ONLY)
3899         int_tounixspec(file, file, NULL);
3900     fp = fopen(file,"r","shr=get");
3901     if (!fp) return 0;
3902     fstat(fileno(fp), &s1.crtl_stat);
3903
3904     cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3905     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
3906         fclose(fp);
3907         return 0;
3908     }
3909
3910     return fp;
3911 }
3912
3913
3914 static int
3915 vms_is_syscommand_xterm(void)
3916 {
3917     const static struct dsc$descriptor_s syscommand_dsc = 
3918       { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3919
3920     const static struct dsc$descriptor_s decwdisplay_dsc = 
3921       { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3922
3923     struct item_list_3 items[2];
3924     unsigned short dvi_iosb[4];
3925     unsigned long devchar;
3926     unsigned long devclass;
3927     int status;
3928
3929     /* Very simple check to guess if sys$command is a decterm? */
3930     /* First see if the DECW$DISPLAY: device exists */
3931     items[0].len = 4;
3932     items[0].code = DVI$_DEVCHAR;
3933     items[0].bufadr = &devchar;
3934     items[0].retadr = NULL;
3935     items[1].len = 0;
3936     items[1].code = 0;
3937
3938     status = sys$getdviw
3939         (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3940
3941     if ($VMS_STATUS_SUCCESS(status)) {
3942         status = dvi_iosb[0];
3943     }
3944
3945     if (!$VMS_STATUS_SUCCESS(status)) {
3946         SETERRNO(EVMSERR, status);
3947         return -1;
3948     }
3949
3950     /* If it does, then for now assume that we are on a workstation */
3951     /* Now verify that SYS$COMMAND is a terminal */
3952     /* for creating the debugger DECTerm */
3953
3954     items[0].len = 4;
3955     items[0].code = DVI$_DEVCLASS;
3956     items[0].bufadr = &devclass;
3957     items[0].retadr = NULL;
3958     items[1].len = 0;
3959     items[1].code = 0;
3960
3961     status = sys$getdviw
3962         (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3963
3964     if ($VMS_STATUS_SUCCESS(status)) {
3965         status = dvi_iosb[0];
3966     }
3967
3968     if (!$VMS_STATUS_SUCCESS(status)) {
3969         SETERRNO(EVMSERR, status);
3970         return -1;
3971     }
3972     else {
3973         if (devclass == DC$_TERM) {
3974             return 0;
3975         }
3976     }
3977     return -1;
3978 }
3979
3980 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3981 static PerlIO* 
3982 create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3983 {
3984     int status;
3985     int ret_stat;
3986     char * ret_char;
3987     char device_name[65];
3988     unsigned short device_name_len;
3989     struct dsc$descriptor_s customization_dsc;
3990     struct dsc$descriptor_s device_name_dsc;
3991     const char * cptr;
3992     char customization[200];
3993     char title[40];
3994     pInfo info = NULL;
3995     char mbx1[64];
3996     unsigned short p_chan;
3997     int n;
3998     unsigned short iosb[4];
3999     const char * cust_str =
4000         "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4001     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4002                                           DSC$K_CLASS_S, mbx1};
4003
4004      /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4005     /*---------------------------------------*/
4006     VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4007
4008
4009     /* Make sure that this is from the Perl debugger */
4010     ret_char = strstr(cmd," xterm ");
4011     if (ret_char == NULL)
4012         return NULL;
4013     cptr = ret_char + 7;
4014     ret_char = strstr(cmd,"tty");
4015     if (ret_char == NULL)
4016         return NULL;
4017     ret_char = strstr(cmd,"sleep");
4018     if (ret_char == NULL)
4019         return NULL;
4020
4021     if (decw_term_port == 0) {
4022         $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4023         $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4024         $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4025
4026        status = lib$find_image_symbol
4027                                (&filename1_dsc,
4028                                 &decw_term_port_dsc,
4029                                 (void *)&decw_term_port,
4030                                 NULL,
4031                                 0);
4032
4033         /* Try again with the other image name */
4034         if (!$VMS_STATUS_SUCCESS(status)) {
4035
4036            status = lib$find_image_symbol
4037                                (&filename2_dsc,
4038                                 &decw_term_port_dsc,
4039                                 (void *)&decw_term_port,
4040                                 NULL,
4041                                 0);
4042
4043         }
4044
4045     }
4046
4047
4048     /* No decw$term_port, give it up */
4049     if (!$VMS_STATUS_SUCCESS(status))
4050         return NULL;
4051
4052     /* Are we on a workstation? */
4053     /* to do: capture the rows / columns and pass their properties */
4054     ret_stat = vms_is_syscommand_xterm();
4055     if (ret_stat < 0)
4056         return NULL;
4057
4058     /* Make the title: */
4059     ret_char = strstr(cptr,"-title");
4060     if (ret_char != NULL) {
4061         while ((*cptr != 0) && (*cptr != '\"')) {
4062             cptr++;
4063         }
4064         if (*cptr == '\"')
4065             cptr++;
4066         n = 0;
4067         while ((*cptr != 0) && (*cptr != '\"')) {
4068             title[n] = *cptr;
4069             n++;
4070             if (n == 39) {
4071                 title[39] = 0;
4072                 break;
4073             }
4074             cptr++;
4075         }
4076         title[n] = 0;
4077     }
4078     else {
4079             /* Default title */
4080             strcpy(title,"Perl Debug DECTerm");
4081     }
4082     sprintf(customization, cust_str, title);
4083
4084     customization_dsc.dsc$a_pointer = customization;
4085     customization_dsc.dsc$w_length = strlen(customization);
4086     customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4087     customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4088
4089     device_name_dsc.dsc$a_pointer = device_name;
4090     device_name_dsc.dsc$w_length = sizeof device_name -1;
4091     device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4092     device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4093
4094     device_name_len = 0;
4095
4096     /* Try to create the window */
4097      status = (*decw_term_port)
4098        (NULL,
4099         NULL,
4100         &customization_dsc,
4101         &device_name_dsc,
4102         &device_name_len,
4103         NULL,
4104         NULL,
4105         NULL);
4106     if (!$VMS_STATUS_SUCCESS(status)) {
4107         SETERRNO(EVMSERR, status);
4108         return NULL;
4109     }
4110
4111     device_name[device_name_len] = '\0';
4112
4113     /* Need to set this up to look like a pipe for cleanup */
4114     n = sizeof(Info);
4115     status = lib$get_vm(&n, &info);
4116     if (!$VMS_STATUS_SUCCESS(status)) {
4117         SETERRNO(ENOMEM, status);
4118         return NULL;
4119     }
4120
4121     info->mode = *mode;
4122     info->done = FALSE;
4123     info->completion = 0;
4124     info->closing    = FALSE;
4125     info->in         = 0;
4126     info->out        = 0;
4127     info->err        = 0;
4128     info->fp         = NULL;
4129     info->useFILE    = 0;
4130     info->waiting    = 0;
4131     info->in_done    = TRUE;
4132     info->out_done   = TRUE;
4133     info->err_done   = TRUE;
4134
4135     /* Assign a channel on this so that it will persist, and not login */
4136     /* We stash this channel in the info structure for reference. */
4137     /* The created xterm self destructs when the last channel is removed */
4138     /* and it appears that perl5db.pl (perl debugger) does this routinely */
4139     /* So leave this assigned. */
4140     device_name_dsc.dsc$w_length = device_name_len;
4141     status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4142     if (!$VMS_STATUS_SUCCESS(status)) {
4143         SETERRNO(EVMSERR, status);
4144         return NULL;
4145     }
4146     info->xchan_valid = 1;
4147
4148     /* Now create a mailbox to be read by the application */
4149
4150     create_mbx(&p_chan, &d_mbx1);
4151
4152     /* write the name of the created terminal to the mailbox */
4153     status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4154             iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4155
4156     if (!$VMS_STATUS_SUCCESS(status)) {
4157         SETERRNO(EVMSERR, status);
4158         return NULL;
4159     }
4160
4161     info->fp  = PerlIO_open(mbx1, mode);
4162
4163     /* Done with this channel */
4164     sys$dassgn(p_chan);
4165
4166     /* If any errors, then clean up */
4167     if (!info->fp) {
4168         n = sizeof(Info);
4169         _ckvmssts_noperl(lib$free_vm(&n, &info));
4170         return NULL;
4171         }
4172
4173     /* All done */
4174     return info->fp;
4175 }
4176
4177 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4178
4179 static PerlIO *
4180 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4181 {
4182     static int handler_set_up = FALSE;
4183     PerlIO * ret_fp;
4184     unsigned long int sts, flags = CLI$M_NOWAIT;
4185     /* The use of a GLOBAL table (as was done previously) rendered
4186      * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4187      * environment.  Hence we've switched to LOCAL symbol table.
4188      */
4189     unsigned int table = LIB$K_CLI_LOCAL_SYM;
4190     int j, wait = 0, n;
4191     char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4192     char *in, *out, *err, mbx[512];
4193     FILE *tpipe = 0;
4194     char tfilebuf[NAM$C_MAXRSS+1];
4195     pInfo info = NULL;
4196     char cmd_sym_name[20];
4197     struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4198                                       DSC$K_CLASS_S, symbol};
4199     struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4200                                       DSC$K_CLASS_S, 0};
4201     struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4202                                       DSC$K_CLASS_S, cmd_sym_name};
4203     struct dsc$descriptor_s *vmscmd;
4204     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4205     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4206     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4207
4208     /* Check here for Xterm create request.  This means looking for
4209      * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4210      *  is possible to create an xterm.
4211      */
4212     if (*in_mode == 'r') {
4213         PerlIO * xterm_fd;
4214
4215 #if defined(PERL_IMPLICIT_CONTEXT)
4216         /* Can not fork an xterm with a NULL context */
4217         /* This probably could never happen */
4218         xterm_fd = NULL;
4219         if (aTHX != NULL)
4220 #endif
4221         xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4222         if (xterm_fd != NULL)
4223             return xterm_fd;
4224     }
4225
4226     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static vmspipe file */
4227
4228     /* once-per-program initialization...
4229        note that the SETAST calls and the dual test of pipe_ef
4230        makes sure that only the FIRST thread through here does
4231        the initialization...all other threads wait until it's
4232        done.
4233
4234        Yeah, uglier than a pthread call, it's got all the stuff inline
4235        rather than in a separate routine.
4236     */
4237
4238     if (!pipe_ef) {
4239         _ckvmssts_noperl(sys$setast(0));
4240         if (!pipe_ef) {
4241             unsigned long int pidcode = JPI$_PID;
4242             $DESCRIPTOR(d_delay, RETRY_DELAY);
4243             _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4244             _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4245             _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4246         }
4247         if (!handler_set_up) {
4248           _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4249           handler_set_up = TRUE;
4250         }
4251         _ckvmssts_noperl(sys$setast(1));
4252     }
4253
4254     /* see if we can find a VMSPIPE.COM */
4255
4256     tfilebuf[0] = '@';
4257     vmspipe = find_vmspipe(aTHX);
4258     if (vmspipe) {
4259         vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4260     } else {        /* uh, oh...we're in tempfile hell */
4261         tpipe = vmspipe_tempfile(aTHX);
4262         if (!tpipe) {       /* a fish popular in Boston */
4263             if (ckWARN(WARN_PIPE)) {
4264                 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4265             }
4266         return NULL;
4267         }
4268         fgetname(tpipe,tfilebuf+1,1);
4269         vmspipedsc.dsc$w_length  = strlen(tfilebuf);
4270     }
4271     vmspipedsc.dsc$a_pointer = tfilebuf;
4272
4273     sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4274     if (!(sts & 1)) { 
4275       switch (sts) {
4276         case RMS$_FNF:  case RMS$_DNF:
4277           set_errno(ENOENT); break;
4278         case RMS$_DIR:
4279           set_errno(ENOTDIR); break;
4280         case RMS$_DEV:
4281           set_errno(ENODEV); break;
4282         case RMS$_PRV:
4283           set_errno(EACCES); break;
4284         case RMS$_SYN:
4285           set_errno(EINVAL); break;
4286         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4287           set_errno(E2BIG); break;
4288         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4289           _ckvmssts_noperl(sts); /* fall through */
4290         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4291           set_errno(EVMSERR); 
4292       }
4293       set_vaxc_errno(sts);
4294       if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4295         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4296       }
4297       *psts = sts;
4298       return NULL; 
4299     }
4300     n = sizeof(Info);
4301     _ckvmssts_noperl(lib$get_vm(&n, &info));
4302         
4303     my_strlcpy(mode, in_mode, sizeof(mode));
4304     info->mode = *mode;
4305     info->done = FALSE;
4306     info->completion = 0;
4307     info->closing    = FALSE;
4308     info->in         = 0;
4309     info->out        = 0;
4310     info->err        = 0;
4311     info->fp         = NULL;
4312     info->useFILE    = 0;
4313     info->waiting    = 0;
4314     info->in_done    = TRUE;
4315     info->out_done   = TRUE;
4316     info->err_done   = TRUE;
4317     info->xchan      = 0;
4318     info->xchan_valid = 0;
4319
4320     in = (char *)PerlMem_malloc(VMS_MAXRSS);
4321     if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4322     out = (char *)PerlMem_malloc(VMS_MAXRSS);
4323     if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4324     err = (char *)PerlMem_malloc(VMS_MAXRSS);
4325     if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4326
4327     in[0] = out[0] = err[0] = '\0';
4328
4329     if ((p = strchr(mode,'F')) != NULL) {   /* F -> use FILE* */
4330         info->useFILE = 1;
4331         strcpy(p,p+1);
4332     }
4333     if ((p = strchr(mode,'W')) != NULL) {   /* W -> wait for completion */
4334         wait = 1;
4335         strcpy(p,p+1);
4336     }
4337
4338     if (*mode == 'r') {             /* piping from subroutine */
4339
4340         info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4341         if (info->out) {
4342             info->out->pipe_done = &info->out_done;
4343             info->out_done = FALSE;
4344             info->out->info = info;
4345         }
4346         if (!info->useFILE) {
4347             info->fp  = PerlIO_open(mbx, mode);
4348         } else {
4349             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4350             vmssetuserlnm("SYS$INPUT", mbx);
4351         }
4352
4353         if (!info->fp && info->out) {
4354             sys$cancel(info->out->chan_out);
4355         
4356             while (!info->out_done) {
4357                 int done;
4358                 _ckvmssts_noperl(sys$setast(0));
4359                 done = info->out_done;
4360                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4361                 _ckvmssts_noperl(sys$setast(1));
4362                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4363             }
4364
4365             if (info->out->buf) {
4366                 n = info->out->bufsize * sizeof(char);
4367                 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4368             }
4369             n = sizeof(Pipe);
4370             _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4371             n = sizeof(Info);
4372             _ckvmssts_noperl(lib$free_vm(&n, &info));
4373             *psts = RMS$_FNF;
4374             return NULL;
4375         }
4376
4377         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4378         if (info->err) {
4379             info->err->pipe_done = &info->err_done;
4380             info->err_done = FALSE;
4381             info->err->info = info;
4382         }
4383
4384     } else if (*mode == 'w') {      /* piping to subroutine */
4385
4386         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4387         if (info->out) {
4388             info->out->pipe_done = &info->out_done;
4389             info->out_done = FALSE;
4390             info->out->info = info;
4391         }
4392
4393         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4394         if (info->err) {
4395             info->err->pipe_done = &info->err_done;
4396             info->err_done = FALSE;
4397             info->err->info = info;
4398         }
4399
4400         info->in = pipe_tochild_setup(aTHX_ in,mbx);
4401         if (!info->useFILE) {
4402             info->fp  = PerlIO_open(mbx, mode);
4403         } else {
4404             info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4405             vmssetuserlnm("SYS$OUTPUT", mbx);
4406         }
4407
4408         if (info->in) {
4409             info->in->pipe_done = &info->in_done;
4410             info->in_done = FALSE;
4411             info->in->info = info;
4412         }
4413
4414         /* error cleanup */
4415         if (!info->fp && info->in) {
4416             info->done = TRUE;
4417             _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4418                                       0, 0, 0, 0, 0, 0, 0, 0));
4419
4420             while (!info->in_done) {
4421                 int done;
4422                 _ckvmssts_noperl(sys$setast(0));
4423                 done = info->in_done;
4424                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4425                 _ckvmssts_noperl(sys$setast(1));
4426                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4427             }
4428
4429             if (info->in->buf) {
4430                 n = info->in->bufsize * sizeof(char);
4431                 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4432             }
4433             n = sizeof(Pipe);
4434             _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4435             n = sizeof(Info);
4436             _ckvmssts_noperl(lib$free_vm(&n, &info));
4437             *psts = RMS$_FNF;
4438             return NULL;
4439         }
4440         
4441
4442     } else if (*mode == 'n') {       /* separate subprocess, no Perl i/o */
4443         /* Let the child inherit standard input, unless it's a directory. */
4444         Stat_t st;
4445         if (my_trnlnm("SYS$INPUT", in, 0)) {
4446             if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4447                 *in = '\0';
4448         }
4449
4450         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4451         if (info->out) {
4452             info->out->pipe_done = &info->out_done;
4453             info->out_done = FALSE;
4454             info->out->info = info;
4455         }
4456
4457         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4458         if (info->err) {
4459             info->err->pipe_done = &info->err_done;
4460             info->err_done = FALSE;
4461             info->err->info = info;
4462         }
4463     }
4464
4465     d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4466     _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4467
4468     d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4469     _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4470
4471     d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4472     _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4473
4474     /* Done with the names for the pipes */
4475     PerlMem_free(err);
4476     PerlMem_free(out);
4477     PerlMem_free(in);
4478
4479     p = vmscmd->dsc$a_pointer;
4480     while (*p == ' ' || *p == '\t') p++;        /* remove leading whitespace */
4481     if (*p == '$') p++;                         /* remove leading $ */
4482     while (*p == ' ' || *p == '\t') p++;
4483
4484     for (j = 0; j < 4; j++) {
4485         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4486         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4487
4488     d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4489     _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4490
4491         if (strlen(p) > MAX_DCL_SYMBOL) {
4492             p += MAX_DCL_SYMBOL;
4493         } else {
4494             p += strlen(p);
4495         }
4496     }
4497     _ckvmssts_noperl(sys$setast(0));
4498     info->next=open_pipes;  /* prepend to list */
4499     open_pipes=info;
4500     _ckvmssts_noperl(sys$setast(1));
4501     /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4502      * and SYS$COMMAND.  vmspipe.com will redefine SYS$INPUT, but we'll still
4503      * have SYS$COMMAND if we need it.
4504      */
4505     _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4506                       0, &info->pid, &info->completion,
4507                       0, popen_completion_ast,info,0,0,0));
4508
4509     /* if we were using a tempfile, close it now */
4510
4511     if (tpipe) fclose(tpipe);
4512
4513     /* once the subprocess is spawned, it has copied the symbols and
4514        we can get rid of ours */
4515
4516     for (j = 0; j < 4; j++) {
4517         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4518         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4519     _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4520     }
4521     _ckvmssts_noperl(lib$delete_symbol(&d_sym_in,  &table));
4522     _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4523     _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4524     vms_execfree(vmscmd);
4525         
4526 #ifdef PERL_IMPLICIT_CONTEXT
4527     if (aTHX) 
4528 #endif
4529     PL_forkprocess = info->pid;
4530
4531     ret_fp = info->fp;
4532     if (wait) {
4533          dSAVEDERRNO;
4534          int done = 0;
4535          while (!done) {
4536              _ckvmssts_noperl(sys$setast(0));
4537              done = info->done;
4538              if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4539              _ckvmssts_noperl(sys$setast(1));
4540              if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4541          }
4542         *psts = info->completion;
4543 /* Caller thinks it is open and tries to close it. */
4544 /* This causes some problems, as it changes the error status */
4545 /*        my_pclose(info->fp); */
4546
4547          /* If we did not have a file pointer open, then we have to */
4548          /* clean up here or eventually we will run out of something */
4549          SAVE_ERRNO;
4550          if (info->fp == NULL) {
4551              my_pclose_pinfo(aTHX_ info);
4552          }
4553          RESTORE_ERRNO;
4554
4555     } else { 
4556         *psts = info->pid;
4557     }
4558     return ret_fp;
4559 }  /* end of safe_popen */
4560
4561
4562 /*{{{  PerlIO *my_popen(char *cmd, char *mode)*/
4563 PerlIO *
4564 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4565 {
4566     int sts;
4567     TAINT_ENV();
4568     TAINT_PROPER("popen");
4569     PERL_FLUSHALL_FOR_CHILD;
4570     return safe_popen(aTHX_ cmd,mode,&sts);
4571 }
4572
4573 /*}}}*/
4574
4575
4576 /* Routine to close and cleanup a pipe info structure */
4577
4578 static I32
4579 my_pclose_pinfo(pTHX_ pInfo info) {
4580
4581     unsigned long int retsts;
4582     int done, n;
4583     pInfo next, last;
4584
4585     /* If we were writing to a subprocess, insure that someone reading from
4586      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
4587      * produce an EOF record in the mailbox.
4588      *
4589      *  well, at least sometimes it *does*, so we have to watch out for
4590      *  the first EOF closing the pipe (and DASSGN'ing the channel)...
4591      */
4592      if (info->fp) {
4593         if (!info->useFILE
4594 #if defined(USE_ITHREADS)
4595           && my_perl
4596 #endif
4597 #ifdef USE_PERLIO
4598           && PL_perlio_fd_refcnt 
4599 #endif
4600            )
4601             PerlIO_flush(info->fp);
4602         else 
4603             fflush((FILE *)info->fp);
4604     }
4605
4606     _ckvmssts(sys$setast(0));
4607      info->closing = TRUE;
4608      done = info->done && info->in_done && info->out_done && info->err_done;
4609      /* hanging on write to Perl's input? cancel it */
4610      if (info->mode == 'r' && info->out && !info->out_done) {
4611         if (info->out->chan_out) {
4612             _ckvmssts(sys$cancel(info->out->chan_out));
4613             if (!info->out->chan_in) {   /* EOF generation, need AST */
4614                 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4615             }
4616         }
4617      }
4618      if (info->in && !info->in_done && !info->in->shut_on_empty)  /* EOF if hasn't had one yet */
4619          _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4620                            0, 0, 0, 0, 0, 0));
4621     _ckvmssts(sys$setast(1));
4622     if (info->fp) {
4623      if (!info->useFILE
4624 #if defined(USE_ITHREADS)
4625          && my_perl
4626 #endif
4627 #ifdef USE_PERLIO
4628          && PL_perlio_fd_refcnt
4629 #endif
4630         )
4631         PerlIO_close(info->fp);
4632      else 
4633         fclose((FILE *)info->fp);
4634     }
4635      /*
4636         we have to wait until subprocess completes, but ALSO wait until all
4637         the i/o completes...otherwise we'll be freeing the "info" structure
4638         that the i/o ASTs could still be using...
4639      */
4640
4641      while (!done) {
4642          _ckvmssts(sys$setast(0));
4643          done = info->done && info->in_done && info->out_done && info->err_done;
4644          if (!done) _ckvmssts(sys$clref(pipe_ef));
4645          _ckvmssts(sys$setast(1));
4646          if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4647      }
4648      retsts = info->completion;
4649
4650     /* remove from list of open pipes */
4651     _ckvmssts(sys$setast(0));
4652     last = NULL;
4653     for (next = open_pipes; next != NULL; last = next, next = next->next) {
4654         if (next == info)
4655             break;
4656     }
4657
4658     if (last)
4659         last->next = info->next;
4660     else
4661         open_pipes = info->next;
4662     _ckvmssts(sys$setast(1));
4663
4664     /* free buffers and structures */
4665
4666     if (info->in) {
4667         if (info->in->buf) {
4668             n = info->in->bufsize * sizeof(char);
4669             _ckvmssts(lib$free_vm(&n, &info->in->buf));
4670         }
4671         n = sizeof(Pipe);
4672         _ckvmssts(lib$free_vm(&n, &info->in));
4673     }
4674     if (info->out) {
4675         if (info->out->buf) {
4676             n = info->out->bufsize * sizeof(char);
4677             _ckvmssts(lib$free_vm(&n, &info->out->buf));
4678         }
4679         n = sizeof(Pipe);
4680         _ckvmssts(lib$free_vm(&n, &info->out));
4681     }
4682     if (info->err) {
4683         if (info->err->buf) {
4684             n = info->err->bufsize * sizeof(char);
4685             _ckvmssts(lib$free_vm(&n, &info->err->buf));
4686         }
4687         n = sizeof(Pipe);
4688         _ckvmssts(lib$free_vm(&n, &info->err));
4689     }
4690     n = sizeof(Info);
4691     _ckvmssts(lib$free_vm(&n, &info));
4692
4693     return retsts;
4694 }
4695
4696
4697 /*{{{  I32 my_pclose(PerlIO *fp)*/
4698 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4699 {
4700     pInfo info, last = NULL;
4701     I32 ret_status;
4702     
4703     /* Fixme - need ast and mutex protection here */
4704     for (info = open_pipes; info != NULL; last = info, info = info->next)
4705         if (info->fp == fp) break;
4706
4707     if (info == NULL) {  /* no such pipe open */
4708       set_errno(ECHILD); /* quoth POSIX */
4709       set_vaxc_errno(SS$_NONEXPR);
4710       return -1;
4711     }
4712
4713     ret_status = my_pclose_pinfo(aTHX_ info);
4714
4715     return ret_status;
4716
4717 }  /* end of my_pclose() */
4718
4719   /* Roll our own prototype because we want this regardless of whether
4720    * _VMS_WAIT is defined.
4721    */
4722
4723 #ifdef __cplusplus
4724 extern "C" {
4725 #endif
4726   __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4727 #ifdef __cplusplus
4728 }
4729 #endif
4730
4731 /* sort-of waitpid; special handling of pipe clean-up for subprocesses 
4732    created with popen(); otherwise partially emulate waitpid() unless 
4733    we have a suitable one from the CRTL that came with VMS 7.2 and later.
4734    Also check processes not considered by the CRTL waitpid().
4735  */
4736 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4737 Pid_t
4738 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4739 {
4740     pInfo info;
4741     int done;
4742     int sts;
4743     int j;
4744     
4745     if (statusp) *statusp = 0;
4746     
4747     for (info = open_pipes; info != NULL; info = info->next)
4748         if (info->pid == pid) break;
4749
4750     if (info != NULL) {  /* we know about this child */
4751       while (!info->done) {
4752           _ckvmssts(sys$setast(0));
4753           done = info->done;
4754           if (!done) _ckvmssts(sys$clref(pipe_ef));
4755           _ckvmssts(sys$setast(1));
4756           if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4757       }
4758
4759       if (statusp) *statusp = info->completion;
4760       return pid;
4761     }
4762
4763     /* child that already terminated? */
4764
4765     for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4766         if (closed_list[j].pid == pid) {
4767             if (statusp) *statusp = closed_list[j].completion;
4768             return pid;
4769         }
4770     }
4771
4772     /* fall through if this child is not one of our own pipe children */
4773
4774       /* waitpid() became available in the CRTL as of VMS 7.0, but only
4775        * in 7.2 did we get a version that fills in the VMS completion
4776        * status as Perl has always tried to do.
4777        */
4778
4779       sts = __vms_waitpid( pid, statusp, flags );
4780
4781       if ( sts == 0 || !(sts == -1 && errno == ECHILD) ) 
4782          return sts;
4783
4784       /* If the real waitpid tells us the child does not exist, we 
4785        * fall through here to implement waiting for a child that 
4786        * was created by some means other than exec() (say, spawned
4787        * from DCL) or to wait for a process that is not a subprocess 
4788        * of the current process.
4789        */
4790
4791     {
4792       $DESCRIPTOR(intdsc,"0 00:00:01");
4793       unsigned long int ownercode = JPI$_OWNER, ownerpid;
4794       unsigned long int pidcode = JPI$_PID, mypid;
4795       unsigned long int interval[2];
4796       unsigned int jpi_iosb[2];
4797       struct itmlst_3 jpilist[2] = { 
4798           {sizeof(ownerpid),        JPI$_OWNER, &ownerpid,        0},
4799           {                      0,         0,                 0, 0} 
4800       };
4801
4802       if (pid <= 0) {
4803         /* Sorry folks, we don't presently implement rooting around for 
4804            the first child we can find, and we definitely don't want to
4805            pass a pid of -1 to $getjpi, where it is a wildcard operation.
4806          */
4807         set_errno(ENOTSUP); 
4808         return -1;
4809       }
4810
4811       /* Get the owner of the child so I can warn if it's not mine. If the 
4812        * process doesn't exist or I don't have the privs to look at it, 
4813        * I can go home early.
4814        */
4815       sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4816       if (sts & 1) sts = jpi_iosb[0];
4817       if (!(sts & 1)) {
4818         switch (sts) {
4819             case SS$_NONEXPR:
4820                 set_errno(ECHILD);
4821                 break;
4822             case SS$_NOPRIV:
4823                 set_errno(EACCES);
4824                 break;
4825             default:
4826                 _ckvmssts(sts);
4827         }
4828         set_vaxc_errno(sts);
4829         return -1;
4830       }
4831
4832       if (ckWARN(WARN_EXEC)) {
4833         /* remind folks they are asking for non-standard waitpid behavior */
4834         _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4835         if (ownerpid != mypid)
4836           Perl_warner(aTHX_ packWARN(WARN_EXEC),
4837                       "waitpid: process %x is not a child of process %x",
4838                       pid,mypid);
4839       }
4840
4841       /* simply check on it once a second until it's not there anymore. */
4842
4843       _ckvmssts(sys$bintim(&intdsc,interval));
4844       while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4845             _ckvmssts(sys$schdwk(0,0,interval,0));
4846             _ckvmssts(sys$hiber());
4847       }
4848       if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4849
4850       _ckvmssts(sts);
4851       return pid;
4852     }
4853 }  /* end of waitpid() */
4854 /*}}}*/
4855 /*}}}*/
4856 /*}}}*/
4857
4858 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4859 char *
4860 my_gconvert(double val, int ndig, int trail, char *buf)
4861 {
4862   static char __gcvtbuf[DBL_DIG+1];
4863   char *loc;
4864
4865   loc = buf ? buf : __gcvtbuf;
4866
4867   if (val) {
4868     if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4869     return gcvt(val,ndig,loc);
4870   }
4871   else {
4872     loc[0] = '0'; loc[1] = '\0';
4873     return loc;
4874   }
4875
4876 }
4877 /*}}}*/
4878
4879 #if !defined(NAML$C_MAXRSS)
4880 static int
4881 rms_free_search_context(struct FAB * fab)
4882 {
4883     struct NAM * nam;
4884
4885     nam = fab->fab$l_nam;
4886     nam->nam$b_nop |= NAM$M_SYNCHK;
4887     nam->nam$l_rlf = NULL;
4888     fab->fab$b_dns = 0;
4889     return sys$parse(fab, NULL, NULL);
4890 }
4891
4892 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4893 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4894 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4895 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4896 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4897 #define rms_nam_esll(nam) nam.nam$b_esl
4898 #define rms_nam_esl(nam) nam.nam$b_esl
4899 #define rms_nam_name(nam) nam.nam$l_name
4900 #define rms_nam_namel(nam) nam.nam$l_name
4901 #define rms_nam_type(nam) nam.nam$l_type
4902 #define rms_nam_typel(nam) nam.nam$l_type
4903 #define rms_nam_ver(nam) nam.nam$l_ver
4904 #define rms_nam_verl(nam) nam.nam$l_ver
4905 #define rms_nam_rsll(nam) nam.nam$b_rsl
4906 #define rms_nam_rsl(nam) nam.nam$b_rsl
4907 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4908 #define rms_set_fna(fab, nam, name, size) \
4909         { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4910 #define rms_get_fna(fab, nam) fab.fab$l_fna
4911 #define rms_set_dna(fab, nam, name, size) \
4912         { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4913 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4914 #define rms_set_esa(nam, name, size) \
4915         { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4916 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4917         { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4918 #define rms_set_rsa(nam, name, size) \
4919         { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4920 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4921         { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4922 #define rms_nam_name_type_l_size(nam) \
4923         (nam.nam$b_name + nam.nam$b_type)
4924 #else
4925 static int
4926 rms_free_search_context(struct FAB * fab)
4927 {
4928     struct NAML * nam;
4929
4930     nam = fab->fab$l_naml;
4931     nam->naml$b_nop |= NAM$M_SYNCHK;
4932     nam->naml$l_rlf = NULL;
4933     nam->naml$l_long_defname_size = 0;
4934
4935     fab->fab$b_dns = 0;
4936     return sys$parse(fab, NULL, NULL);
4937 }
4938
4939 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4940 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4941 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4942 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4943 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4944 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4945 #define rms_nam_esl(nam) nam.naml$b_esl
4946 #define rms_nam_name(nam) nam.naml$l_name
4947 #define rms_nam_namel(nam) nam.naml$l_long_name
4948 #define rms_nam_type(nam) nam.naml$l_type
4949 #define rms_nam_typel(nam) nam.naml$l_long_type
4950 #define rms_nam_ver(nam) nam.naml$l_ver
4951 #define rms_nam_verl(nam) nam.naml$l_long_ver
4952 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4953 #define rms_nam_rsl(nam) nam.naml$b_rsl
4954 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4955 #define rms_set_fna(fab, nam, name, size) \
4956         { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4957         nam.naml$l_long_filename_size = size; \
4958         nam.naml$l_long_filename = name;}
4959 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4960 #define rms_set_dna(fab, nam, name, size) \
4961         { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4962         nam.naml$l_long_defname_size = size; \
4963         nam.naml$l_long_defname = name; }
4964 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4965 #define rms_set_esa(nam, name, size) \
4966         { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4967         nam.naml$l_long_expand_alloc = size; \
4968         nam.naml$l_long_expand = name; }
4969 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4970         { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4971         nam.naml$l_long_expand = l_name; \
4972         nam.naml$l_long_expand_alloc = l_size; }
4973 #define rms_set_rsa(nam, name, size) \
4974         { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4975         nam.naml$l_long_result = name; \
4976         nam.naml$l_long_result_alloc = size; }
4977 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4978         { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4979         nam.naml$l_long_result = l_name; \
4980         nam.naml$l_long_result_alloc = l_size; }
4981 #define rms_nam_name_type_l_size(nam) \
4982         (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4983 #endif
4984
4985
4986 /* rms_erase
4987  * The CRTL for 8.3 and later can create symbolic links in any mode,
4988  * however in 8.3 the unlink/remove/delete routines will only properly handle
4989  * them if one of the PCP modes is active.
4990  */
4991 static int
4992 rms_erase(const char * vmsname)
4993 {
4994   int status;
4995   struct FAB myfab = cc$rms_fab;
4996   rms_setup_nam(mynam);
4997
4998   rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4999   rms_bind_fab_nam(myfab, mynam);
5000
5001 #ifdef NAML$M_OPEN_SPECIAL
5002   rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5003 #endif
5004
5005   status = sys$erase(&myfab, 0, 0);
5006
5007   return status;
5008 }
5009
5010
5011 static int
5012 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5013                     const struct dsc$descriptor_s * vms_dst_dsc,
5014                     unsigned long flags)
5015 {
5016     /*  VMS and UNIX handle file permissions differently and the
5017      * the same ACL trick may be needed for renaming files,
5018      * especially if they are directories.
5019      */
5020
5021    /* todo: get kill_file and rename to share common code */
5022    /* I can not find online documentation for $change_acl
5023     * it appears to be replaced by $set_security some time ago */
5024
5025     const unsigned int access_mode = 0;
5026     $DESCRIPTOR(obj_file_dsc,"FILE");
5027     char *vmsname;
5028     char *rslt;
5029     unsigned long int jpicode = JPI$_UIC;
5030     int aclsts, fndsts, rnsts = -1;
5031     unsigned int ctx = 0;
5032     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5033     struct dsc$descriptor_s * clean_dsc;
5034     
5035     struct myacedef {
5036         unsigned char myace$b_length;
5037         unsigned char myace$b_type;
5038         unsigned short int myace$w_flags;
5039         unsigned long int myace$l_access;
5040         unsigned long int myace$l_ident;
5041     } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5042              ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5043              0},
5044              oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5045
5046     struct item_list_3
5047         findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5048                       {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5049                       {0,0,0,0}},
5050         addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5051         dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5052                      {0,0,0,0}};
5053
5054
5055     /* Expand the input spec using RMS, since we do not want to put
5056      * ACLs on the target of a symbolic link */
5057     vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
5058     if (vmsname == NULL)
5059         return SS$_INSFMEM;
5060
5061     rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5062                         vmsname,
5063                         PERL_RMSEXPAND_M_SYMLINK);
5064     if (rslt == NULL) {
5065         PerlMem_free(vmsname);
5066         return SS$_INSFMEM;
5067     }
5068
5069     /* So we get our own UIC to use as a rights identifier,
5070      * and the insert an ACE at the head of the ACL which allows us
5071      * to delete the file.
5072      */
5073     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5074
5075     fildsc.dsc$w_length = strlen(vmsname);
5076     fildsc.dsc$a_pointer = vmsname;
5077     ctx = 0;
5078     newace.myace$l_ident = oldace.myace$l_ident;
5079     rnsts = SS$_ABORT;
5080
5081     /* Grab any existing ACEs with this identifier in case we fail */
5082     clean_dsc = &fildsc;
5083     aclsts = fndsts = sys$get_security(&obj_file_dsc,
5084                                &fildsc,
5085                                NULL,
5086                                OSS$M_WLOCK,
5087                                findlst,
5088                                &ctx,
5089                                &access_mode);
5090
5091     if ($VMS_STATUS_SUCCESS(fndsts)  || (fndsts == SS$_ACLEMPTY)) {
5092         /* Add the new ACE . . . */
5093
5094         /* if the sys$get_security succeeded, then ctx is valid, and the
5095          * object/file descriptors will be ignored.  But otherwise they
5096          * are needed
5097          */
5098         aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5099                                   OSS$M_RELCTX, addlst, &ctx, &access_mode);
5100         if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5101             set_errno(EVMSERR);
5102             set_vaxc_errno(aclsts);
5103             PerlMem_free(vmsname);
5104             return aclsts;
5105         }
5106
5107         rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5108                                 NULL, NULL,
5109                                 &flags,
5110                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5111
5112         if ($VMS_STATUS_SUCCESS(rnsts)) {
5113             clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5114         }
5115
5116         /* Put things back the way they were. */
5117         ctx = 0;
5118         aclsts = sys$get_security(&obj_file_dsc,
5119                                   clean_dsc,
5120                                   NULL,
5121                                   OSS$M_WLOCK,
5122                                   findlst,
5123                                   &ctx,
5124                                   &access_mode);
5125
5126         if ($VMS_STATUS_SUCCESS(aclsts)) {
5127         int sec_flags;
5128
5129             sec_flags = 0;
5130             if (!$VMS_STATUS_SUCCESS(fndsts))
5131                 sec_flags = OSS$M_RELCTX;
5132
5133             /* Get rid of the new ACE */
5134             aclsts = sys$set_security(NULL, NULL, NULL,
5135                                   sec_flags, dellst, &ctx, &access_mode);
5136
5137             /* If there was an old ACE, put it back */
5138             if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5139                 addlst[0].bufadr = &oldace;
5140                 aclsts = sys$set_security(NULL, NULL, NULL,
5141                                       OSS$M_RELCTX, addlst, &ctx, &access_mode);
5142                 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5143                     set_errno(EVMSERR);
5144                     set_vaxc_errno(aclsts);
5145                     rnsts = aclsts;
5146                 }
5147             } else {
5148             int aclsts2;
5149
5150                 /* Try to clear the lock on the ACL list */
5151                 aclsts2 = sys$set_security(NULL, NULL, NULL,
5152                                       OSS$M_RELCTX, NULL, &ctx, &access_mode);
5153
5154                 /* Rename errors are most important */
5155                 if (!$VMS_STATUS_SUCCESS(rnsts))
5156                     aclsts = rnsts;
5157                 set_errno(EVMSERR);
5158                 set_vaxc_errno(aclsts);
5159                 rnsts = aclsts;
5160             }
5161         }
5162         else {
5163             if (aclsts != SS$_ACLEMPTY)
5164                 rnsts = aclsts;
5165         }
5166     }
5167     else
5168         rnsts = fndsts;
5169
5170     PerlMem_free(vmsname);
5171     return rnsts;
5172 }
5173
5174
5175 /*{{{int rename(const char *, const char * */
5176 /* Not exactly what X/Open says to do, but doing it absolutely right
5177  * and efficiently would require a lot more work.  This should be close
5178  * enough to pass all but the most strict X/Open compliance test.
5179  */
5180 int
5181 Perl_rename(pTHX_ const char *src, const char * dst)
5182 {
5183     int retval;
5184     int pre_delete = 0;
5185     int src_sts;
5186     int dst_sts;
5187     Stat_t src_st;
5188     Stat_t dst_st;
5189
5190     /* Validate the source file */
5191     src_sts = flex_lstat(src, &src_st);
5192     if (src_sts != 0) {
5193
5194         /* No source file or other problem */
5195         return src_sts;
5196     }
5197     if (src_st.st_devnam[0] == 0)  {
5198         /* This may be possible so fail if it is seen. */
5199         errno = EIO;
5200         return -1;
5201     }
5202
5203     dst_sts = flex_lstat(dst, &dst_st);
5204     if (dst_sts == 0) {
5205
5206         if (dst_st.st_dev != src_st.st_dev) {
5207             /* Must be on the same device */
5208             errno = EXDEV;
5209             return -1;
5210         }
5211
5212         /* VMS_INO_T_COMPARE is true if the inodes are different
5213          * to match the output of memcmp
5214          */
5215
5216         if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5217             /* That was easy, the files are the same! */
5218             return 0;
5219         }
5220
5221         if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5222             /* If source is a directory, so must be dest */
5223                 errno = EISDIR;
5224                 return -1;
5225         }
5226
5227     }
5228
5229
5230     if ((dst_sts == 0) &&
5231         (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5232
5233         /* We have issues here if vms_unlink_all_versions is set
5234          * If the destination exists, and is not a directory, then
5235          * we must delete in advance.
5236          *
5237          * If the src is a directory, then we must always pre-delete
5238          * the destination.
5239          *
5240          * If we successfully delete the dst in advance, and the rename fails
5241          * X/Open requires that errno be EIO.
5242          *
5243          */
5244
5245         if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5246             int d_sts;
5247             d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5248                                      S_ISDIR(dst_st.st_mode));
5249
5250            /* Need to delete all versions ? */
5251            if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5252                 int i = 0;
5253
5254                 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5255                     d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5256                     if (d_sts != 0)
5257                         break;
5258                     i++;
5259
5260                     /* Make sure that we do not loop forever */
5261                     if (i > 32767) {
5262                         errno = EIO;
5263                         d_sts = -1;
5264                         break;
5265                     }
5266                 }
5267            }
5268
5269             if (d_sts != 0)
5270                 return d_sts;
5271
5272             /* We killed the destination, so only errno now is EIO */
5273             pre_delete = 1;
5274         }
5275     }
5276
5277     /* Originally the idea was to call the CRTL rename() and only
5278      * try the lib$rename_file if it failed.
5279      * It turns out that there are too many variants in what the
5280      * the CRTL rename might do, so only use lib$rename_file
5281      */
5282     retval = -1;
5283
5284     {
5285         /* Is the source and dest both in VMS format */
5286         /* if the source is a directory, then need to fileify */
5287         /*  and dest must be a directory or non-existent. */
5288
5289         char * vms_dst;
5290         int sts;
5291         char * ret_str;
5292         unsigned long flags;
5293         struct dsc$descriptor_s old_file_dsc;
5294         struct dsc$descriptor_s new_file_dsc;
5295
5296         /* We need to modify the src and dst depending
5297          * on if one or more of them are directories.
5298          */
5299
5300         vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5301         if (vms_dst == NULL)
5302             _ckvmssts_noperl(SS$_INSFMEM);
5303
5304         if (S_ISDIR(src_st.st_mode)) {
5305         char * ret_str;
5306         char * vms_dir_file;
5307
5308             vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5309             if (vms_dir_file == NULL)
5310                 _ckvmssts_noperl(SS$_INSFMEM);
5311
5312             /* If the dest is a directory, we must remove it */
5313             if (dst_sts == 0) {
5314                 int d_sts;
5315                 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5316                 if (d_sts != 0) {
5317                     PerlMem_free(vms_dst);
5318                     errno = EIO;
5319                     return d_sts;
5320                 }
5321
5322                 pre_delete = 1;
5323             }
5324
5325            /* The dest must be a VMS file specification */
5326            ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5327            if (ret_str == NULL) {
5328                 PerlMem_free(vms_dst);
5329                 errno = EIO;
5330                 return -1;
5331            }
5332
5333             /* The source must be a file specification */
5334             ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5335             if (ret_str == NULL) {
5336                 PerlMem_free(vms_dst);
5337                 PerlMem_free(vms_dir_file);
5338                 errno = EIO;
5339                 return -1;
5340             }
5341             PerlMem_free(vms_dst);
5342             vms_dst = vms_dir_file;
5343
5344         } else {
5345             /* File to file or file to new dir */
5346
5347             if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5348                 /* VMS pathify a dir target */
5349                 ret_str = int_tovmspath(dst, vms_dst, NULL);
5350                 if (ret_str == NULL) {
5351                     PerlMem_free(vms_dst);
5352                     errno = EIO;
5353                     return -1;
5354                 }
5355             } else {
5356                 char * v_spec, * r_spec, * d_spec, * n_spec;
5357                 char * e_spec, * vs_spec;
5358                 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5359
5360                 /* fileify a target VMS file specification */
5361                 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5362                 if (ret_str == NULL) {
5363                     PerlMem_free(vms_dst);
5364                     errno = EIO;
5365                     return -1;
5366                 }
5367
5368                 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5369                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5370                              &e_len, &vs_spec, &vs_len);
5371                 if (sts == 0) {
5372                      if (e_len == 0) {
5373                          /* Get rid of the version */
5374                          if (vs_len != 0) {
5375                              *vs_spec = '\0';
5376                          }
5377                          /* Need to specify a '.' so that the extension */
5378                          /* is not inherited */
5379                          strcat(vms_dst,".");
5380                      }
5381                 }
5382             }
5383         }
5384
5385         old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5386         old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5387         old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5388         old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5389
5390         new_file_dsc.dsc$a_pointer = vms_dst;
5391         new_file_dsc.dsc$w_length = strlen(vms_dst);
5392         new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5393         new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5394
5395         flags = 0;
5396 #if defined(NAML$C_MAXRSS)
5397         flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5398 #endif
5399
5400         sts = lib$rename_file(&old_file_dsc,
5401                               &new_file_dsc,
5402                               NULL, NULL,
5403                               &flags,
5404                               NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5405         if (!$VMS_STATUS_SUCCESS(sts)) {
5406
5407            /* We could have failed because VMS style permissions do not
5408             * permit renames that UNIX will allow.  Just like the hack
5409             * in for kill_file.
5410             */
5411            sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5412         }
5413
5414         PerlMem_free(vms_dst);
5415         if (!$VMS_STATUS_SUCCESS(sts)) {
5416             errno = EIO;
5417             return -1;
5418         }
5419         retval = 0;
5420     }
5421
5422     if (vms_unlink_all_versions) {
5423         /* Now get rid of any previous versions of the source file that
5424          * might still exist
5425          */
5426         int i = 0;
5427         dSAVEDERRNO;
5428         SAVE_ERRNO;
5429         src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5430                                    S_ISDIR(src_st.st_mode));
5431         while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5432              src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5433                                        S_ISDIR(src_st.st_mode));
5434              if (src_sts != 0)
5435                  break;
5436              i++;
5437
5438              /* Make sure that we do not loop forever */
5439              if (i > 32767) {
5440                  src_sts = -1;
5441                  break;
5442              }
5443         }
5444         RESTORE_ERRNO;
5445     }
5446
5447     /* We deleted the destination, so must force the error to be EIO */
5448     if ((retval != 0) && (pre_delete != 0))
5449         errno = EIO;
5450
5451     return retval;
5452 }
5453 /*}}}*/
5454
5455
5456 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5457 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5458  * to expand file specification.  Allows for a single default file
5459  * specification and a simple mask of options.  If outbuf is non-NULL,
5460  * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5461  * the resultant file specification is placed.  If outbuf is NULL, the
5462  * resultant file specification is placed into a static buffer.
5463  * The third argument, if non-NULL, is taken to be a default file
5464  * specification string.  The fourth argument is unused at present.
5465  * rmesexpand() returns the address of the resultant string if
5466  * successful, and NULL on error.
5467  *
5468  * New functionality for previously unused opts value:
5469  *  PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5470  *  PERL_RMSEXPAND_M_LONG - Want output in long formst
5471  *  PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5472  *  PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5473  */
5474 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5475
5476 static char *
5477 int_rmsexpand
5478    (const char *filespec,
5479     char *outbuf,
5480     const char *defspec,
5481     unsigned opts,
5482     int * fs_utf8,
5483     int * dfs_utf8)
5484 {
5485   char * ret_spec;
5486   const char * in_spec;
5487   char * spec_buf;
5488   const char * def_spec;
5489   char * vmsfspec, *vmsdefspec;
5490   char * esa;
5491   char * esal = NULL;
5492   char * outbufl;
5493   struct FAB myfab = cc$rms_fab;
5494   rms_setup_nam(mynam);
5495   STRLEN speclen;
5496   unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5497   int sts;
5498
5499   /* temp hack until UTF8 is actually implemented */
5500   if (fs_utf8 != NULL)
5501     *fs_utf8 = 0;
5502
5503   if (!filespec || !*filespec) {
5504     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5505     return NULL;
5506   }
5507
5508   vmsfspec = NULL;
5509   vmsdefspec = NULL;
5510   outbufl = NULL;
5511
5512   in_spec = filespec;
5513   isunix = 0;
5514   if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5515       char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5516       int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5517
5518       /* If this is a UNIX file spec, convert it to VMS */
5519       sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5520                            &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5521                            &e_len, &vs_spec, &vs_len);
5522       if (sts != 0) {
5523           isunix = 1;
5524           char * ret_spec;
5525
5526           vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5527           if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5528           ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5529           if (ret_spec == NULL) {
5530               PerlMem_free(vmsfspec);
5531               return NULL;
5532           }
5533           in_spec = (const char *)vmsfspec;
5534
5535           /* Unless we are forcing to VMS format, a UNIX input means
5536            * UNIX output, and that requires long names to be used
5537            */
5538           if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5539 #if defined(NAML$C_MAXRSS)
5540               opts |= PERL_RMSEXPAND_M_LONG;
5541 #else
5542               NOOP;
5543 #endif
5544           else
5545               isunix = 0;
5546       }
5547
5548   }
5549
5550   rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5551   rms_bind_fab_nam(myfab, mynam);
5552
5553   /* Process the default file specification if present */
5554   def_spec = defspec;
5555   if (defspec && *defspec) {
5556     int t_isunix;
5557     t_isunix = is_unix_filespec(defspec);
5558     if (t_isunix) {
5559       vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5560       if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5561       ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5562
5563       if (ret_spec == NULL) {
5564           /* Clean up and bail */
5565           PerlMem_free(vmsdefspec);
5566           if (vmsfspec != NULL)
5567               PerlMem_free(vmsfspec);
5568               return NULL;
5569           }
5570           def_spec = (const char *)vmsdefspec;
5571       }
5572       rms_set_dna(myfab, mynam,
5573                   (char *)def_spec, strlen(def_spec)); /* cast ok */
5574   }
5575
5576   /* Now we need the expansion buffers */
5577   esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5578   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5579 #if defined(NAML$C_MAXRSS)
5580   esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5581   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5582 #endif
5583   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5584
5585   /* If a NAML block is used RMS always writes to the long and short
5586    * addresses unless you suppress the short name.
5587    */
5588 #if defined(NAML$C_MAXRSS)
5589   outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5590   if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5591 #endif
5592    rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5593
5594 #ifdef NAM$M_NO_SHORT_UPCASE
5595   if (DECC_EFS_CASE_PRESERVE)
5596     rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5597 #endif
5598
5599    /* We may not want to follow symbolic links */
5600 #ifdef NAML$M_OPEN_SPECIAL
5601   if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5602     rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5603 #endif
5604
5605   /* First attempt to parse as an existing file */
5606   retsts = sys$parse(&myfab,0,0);
5607   if (!(retsts & STS$K_SUCCESS)) {
5608
5609     /* Could not find the file, try as syntax only if error is not fatal */
5610     rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5611     if (retsts == RMS$_DNF ||
5612         retsts == RMS$_DIR ||
5613         retsts == RMS$_DEV ||
5614         retsts == RMS$_PRV) {
5615       retsts = sys$parse(&myfab,0,0);
5616       if (retsts & STS$K_SUCCESS) goto int_expanded;
5617     }  
5618
5619      /* Still could not parse the file specification */
5620     /*----------------------------------------------*/
5621     sts = rms_free_search_context(&myfab); /* Free search context */
5622     if (vmsdefspec != NULL)
5623         PerlMem_free(vmsdefspec);
5624     if (vmsfspec != NULL)
5625         PerlMem_free(vmsfspec);
5626     if (outbufl != NULL)
5627         PerlMem_free(outbufl);
5628     PerlMem_free(esa);
5629     if (esal != NULL) 
5630         PerlMem_free(esal);
5631     set_vaxc_errno(retsts);
5632     if      (retsts == RMS$_PRV) set_errno(EACCES);
5633     else if (retsts == RMS$_DEV) set_errno(ENODEV);
5634     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5635     else                         set_errno(EVMSERR);
5636     return NULL;
5637   }
5638   retsts = sys$search(&myfab,0,0);
5639   if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5640     sts = rms_free_search_context(&myfab); /* Free search context */
5641     if (vmsdefspec != NULL)
5642         PerlMem_free(vmsdefspec);
5643     if (vmsfspec != NULL)
5644         PerlMem_free(vmsfspec);
5645     if (outbufl != NULL)
5646         PerlMem_free(outbufl);
5647     PerlMem_free(esa);
5648     if (esal != NULL) 
5649         PerlMem_free(esal);
5650     set_vaxc_errno(retsts);
5651     if      (retsts == RMS$_PRV) set_errno(EACCES);
5652     else                         set_errno(EVMSERR);
5653     return NULL;
5654   }
5655
5656   /* If the input filespec contained any lowercase characters,
5657    * downcase the result for compatibility with Unix-minded code. */
5658 int_expanded:
5659   if (!DECC_EFS_CASE_PRESERVE) {
5660     char * tbuf;
5661     for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5662       if (islower(*tbuf)) { haslower = 1; break; }
5663   }
5664
5665    /* Is a long or a short name expected */
5666   /*------------------------------------*/
5667   spec_buf = NULL;
5668 #if defined(NAML$C_MAXRSS)
5669   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5670     if (rms_nam_rsll(mynam)) {
5671         spec_buf = outbufl;
5672         speclen = rms_nam_rsll(mynam);
5673     }
5674     else {
5675         spec_buf = esal; /* Not esa */
5676         speclen = rms_nam_esll(mynam);
5677     }
5678   }
5679   else {
5680 #endif
5681     if (rms_nam_rsl(mynam)) {
5682         spec_buf = outbuf;
5683         speclen = rms_nam_rsl(mynam);
5684     }
5685     else {
5686         spec_buf = esa; /* Not esal */
5687         speclen = rms_nam_esl(mynam);
5688     }
5689 #if defined(NAML$C_MAXRSS)
5690   }
5691 #endif
5692   spec_buf[speclen] = '\0';
5693
5694   /* Trim off null fields added by $PARSE
5695    * If type > 1 char, must have been specified in original or default spec
5696    * (not true for version; $SEARCH may have added version of existing file).
5697    */
5698   trimver  = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5699   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5700     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5701              ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5702   }
5703   else {
5704     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5705              ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5706   }
5707   if (trimver || trimtype) {
5708     if (defspec && *defspec) {
5709       char *defesal = NULL;
5710       char *defesa = NULL;
5711       defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5712       if (defesa != NULL) {
5713         struct FAB deffab = cc$rms_fab;
5714 #if defined(NAML$C_MAXRSS)
5715         defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5716         if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5717 #endif
5718         rms_setup_nam(defnam);
5719      
5720         rms_bind_fab_nam(deffab, defnam);
5721
5722         /* Cast ok */ 
5723         rms_set_fna
5724             (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam)); 
5725
5726         /* RMS needs the esa/esal as a work area if wildcards are involved */
5727         rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5728
5729         rms_clear_nam_nop(defnam);
5730         rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5731 #ifdef NAM$M_NO_SHORT_UPCASE
5732         if (DECC_EFS_CASE_PRESERVE)
5733           rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5734 #endif
5735 #ifdef NAML$M_OPEN_SPECIAL
5736         if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5737           rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5738 #endif
5739         if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5740           if (trimver) {
5741              trimver  = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5742           }
5743           if (trimtype) {
5744             trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE); 
5745           }
5746         }
5747         if (defesal != NULL)
5748             PerlMem_free(defesal);
5749         PerlMem_free(defesa);
5750       } else {
5751           _ckvmssts_noperl(SS$_INSFMEM);
5752       }
5753     }
5754     if (trimver) {
5755       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5756         if (*(rms_nam_verl(mynam)) != '\"')
5757           speclen = rms_nam_verl(mynam) - spec_buf;
5758       }
5759       else {
5760         if (*(rms_nam_ver(mynam)) != '\"')
5761           speclen = rms_nam_ver(mynam) - spec_buf;
5762       }
5763     }
5764     if (trimtype) {
5765       /* If we didn't already trim version, copy down */
5766       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5767         if (speclen > rms_nam_verl(mynam) - spec_buf)
5768           memmove
5769            (rms_nam_typel(mynam),
5770             rms_nam_verl(mynam),
5771             speclen - (rms_nam_verl(mynam) - spec_buf));
5772           speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5773       }
5774       else {
5775         if (speclen > rms_nam_ver(mynam) - spec_buf)
5776           memmove
5777            (rms_nam_type(mynam),
5778             rms_nam_ver(mynam),
5779             speclen - (rms_nam_ver(mynam) - spec_buf));
5780           speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5781       }
5782     }
5783   }
5784
5785    /* Done with these copies of the input files */
5786   /*-------------------------------------------*/
5787   if (vmsfspec != NULL)
5788         PerlMem_free(vmsfspec);
5789   if (vmsdefspec != NULL)
5790         PerlMem_free(vmsdefspec);
5791
5792   /* If we just had a directory spec on input, $PARSE "helpfully"
5793    * adds an empty name and type for us */
5794 #if defined(NAML$C_MAXRSS)
5795   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5796     if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5797         rms_nam_verl(mynam)  == rms_nam_typel(mynam) + 1 &&
5798         !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5799       speclen = rms_nam_namel(mynam) - spec_buf;
5800   }
5801   else
5802 #endif
5803   {
5804     if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5805         rms_nam_ver(mynam)  == rms_nam_ver(mynam) + 1 &&
5806         !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5807       speclen = rms_nam_name(mynam) - spec_buf;
5808   }
5809
5810   /* Posix format specifications must have matching quotes */
5811   if (speclen < (VMS_MAXRSS - 1)) {
5812     if (DECC_POSIX_COMPLIANT_PATHNAMES && (spec_buf[0] == '\"')) {
5813       if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5814         spec_buf[speclen] = '\"';
5815         speclen++;
5816       }
5817     }
5818   }
5819   spec_buf[speclen] = '\0';
5820   if (haslower && !DECC_EFS_CASE_PRESERVE) __mystrtolower(spec_buf);
5821
5822   /* Have we been working with an expanded, but not resultant, spec? */
5823   /* Also, convert back to Unix syntax if necessary. */
5824   {
5825   int rsl;
5826
5827 #if defined(NAML$C_MAXRSS)
5828     if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5829       rsl = rms_nam_rsll(mynam);
5830     } else
5831 #endif
5832     {
5833       rsl = rms_nam_rsl(mynam);
5834     }
5835     if (!rsl) {
5836       /* rsl is not present, it means that spec_buf is either */
5837       /* esa or esal, and needs to be copied to outbuf */
5838       /* convert to Unix if desired */
5839       if (isunix) {
5840         ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5841       } else {
5842         /* VMS file specs are not in UTF-8 */
5843         if (fs_utf8 != NULL)
5844             *fs_utf8 = 0;
5845         my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5846         ret_spec = outbuf;
5847       }
5848     }
5849     else {
5850       /* Now spec_buf is either outbuf or outbufl */
5851       /* We need the result into outbuf */
5852       if (isunix) {
5853            /* If we need this in UNIX, then we need another buffer */
5854            /* to keep things in order */
5855            char * src;
5856            char * new_src = NULL;
5857            if (spec_buf == outbuf) {
5858                new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5859                my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5860            } else {
5861                src = spec_buf;
5862            }
5863            ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5864            if (new_src) {
5865                PerlMem_free(new_src);
5866            }
5867       } else {
5868            /* VMS file specs are not in UTF-8 */
5869            if (fs_utf8 != NULL)
5870                *fs_utf8 = 0;
5871
5872            /* Copy the buffer if needed */
5873            if (outbuf != spec_buf)
5874                my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5875            ret_spec = outbuf;
5876       }
5877     }
5878   }
5879
5880   /* Need to clean up the search context */
5881   rms_set_rsal(mynam, NULL, 0, NULL, 0);
5882   sts = rms_free_search_context(&myfab); /* Free search context */
5883
5884   /* Clean up the extra buffers */
5885   if (esal != NULL)
5886       PerlMem_free(esal);
5887   PerlMem_free(esa);
5888   if (outbufl != NULL)
5889      PerlMem_free(outbufl);
5890
5891   /* Return the result */
5892   return ret_spec;
5893 }
5894
5895 /* Common simple case - Expand an already VMS spec */
5896 static char * 
5897 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5898     opts |= PERL_RMSEXPAND_M_VMS_IN;
5899     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 
5900 }
5901
5902 /* Common simple case - Expand to a VMS spec */
5903 static char * 
5904 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5905     opts |= PERL_RMSEXPAND_M_VMS;
5906     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 
5907 }
5908
5909
5910 /* Entry point used by perl routines */
5911 static char *
5912 mp_do_rmsexpand
5913    (pTHX_ const char *filespec,
5914     char *outbuf,
5915     int ts,
5916     const char *defspec,
5917     unsigned opts,
5918     int * fs_utf8,
5919     int * dfs_utf8)
5920 {
5921     static char __rmsexpand_retbuf[VMS_MAXRSS];
5922     char * expanded, *ret_spec, *ret_buf;
5923
5924     expanded = NULL;
5925     ret_buf = outbuf;
5926     if (ret_buf == NULL) {
5927         if (ts) {
5928             Newx(expanded, VMS_MAXRSS, char);
5929             if (expanded == NULL)
5930                 _ckvmssts(SS$_INSFMEM);
5931             ret_buf = expanded;
5932         } else {
5933             ret_buf = __rmsexpand_retbuf;
5934         }
5935     }
5936
5937
5938     ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5939                              opts, fs_utf8,  dfs_utf8);
5940
5941     if (ret_spec == NULL) {
5942        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5943        if (expanded)
5944            Safefree(expanded);
5945     }
5946
5947     return ret_spec;
5948 }
5949 /*}}}*/
5950 /* External entry points */
5951 char *
5952 Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5953 {
5954     return do_rmsexpand(spec, buf, 0, def, opt, NULL, NULL);
5955 }
5956
5957 char *
5958 Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5959 {
5960     return do_rmsexpand(spec, buf, 1, def, opt, NULL, NULL);
5961 }
5962
5963 char *
5964 Perl_rmsexpand_utf8(pTHX_ const char *spec, char *buf, const char *def,
5965                     unsigned opt, int * fs_utf8, int * dfs_utf8)
5966 {
5967     return do_rmsexpand(spec, buf, 0, def, opt, fs_utf8, dfs_utf8);
5968 }
5969
5970 char *
5971 Perl_rmsexpand_utf8_ts(pTHX_ const char *spec, char *buf, const char *def,
5972                        unsigned opt, int * fs_utf8, int * dfs_utf8)
5973 {
5974     return do_rmsexpand(spec, buf, 1, def, opt, fs_utf8, dfs_utf8);
5975 }
5976
5977
5978 /*
5979 ** The following routines are provided to make life easier when
5980 ** converting among VMS-style and Unix-style directory specifications.
5981 ** All will take input specifications in either VMS or Unix syntax. On
5982 ** failure, all return NULL.  If successful, the routines listed below
5983 ** return a pointer to a buffer containing the appropriately
5984 ** reformatted spec (and, therefore, subsequent calls to that routine
5985 ** will clobber the result), while the routines of the same names with
5986 ** a _ts suffix appended will return a pointer to a mallocd string
5987 ** containing the appropriately reformatted spec.
5988 ** In all cases, only explicit syntax is altered; no check is made that
5989 ** the resulting string is valid or that the directory in question
5990 ** actually exists.
5991 **
5992 **   fileify_dirspec() - convert a directory spec into the name of the
5993 **     directory file (i.e. what you can stat() to see if it's a dir).
5994 **     The style (VMS or Unix) of the result is the same as the style
5995 **     of the parameter passed in.
5996 **   pathify_dirspec() - convert a directory spec into a path (i.e.
5997 **     what you prepend to a filename to indicate what directory it's in).
5998 **     The style (VMS or Unix) of the result is the same as the style
5999 **     of the parameter passed in.
6000 **   tounixpath() - convert a directory spec into a Unix-style path.
6001 **   tovmspath() - convert a directory spec into a VMS-style path.
6002 **   tounixspec() - convert any file spec into a Unix-style file spec.
6003 **   tovmsspec() - convert any file spec into a VMS-style spec.
6004 **   xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6005 **
6006 ** Copyright 1996 by Charles Bailey  <bailey@newman.upenn.edu>
6007 ** Permission is given to distribute this code as part of the Perl
6008 ** standard distribution under the terms of the GNU General Public
6009 ** License or the Perl Artistic License.  Copies of each may be
6010 ** found in the Perl standard distribution.
6011  */
6012
6013 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6014 static char *
6015 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6016 {
6017     unsigned long int dirlen, retlen, hasfilename = 0;
6018     char *cp1, *cp2, *lastdir;
6019     char *trndir, *vmsdir;
6020     unsigned short int trnlnm_iter_count;
6021     int sts;
6022     if (utf8_fl != NULL)
6023         *utf8_fl = 0;
6024
6025     if (!dir || !*dir) {
6026       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6027     }
6028     dirlen = strlen(dir);
6029     while (dirlen && dir[dirlen-1] == '/') --dirlen;
6030     if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6031       if (!DECC_POSIX_COMPLIANT_PATHNAMES && DECC_DISABLE_POSIX_ROOT) {
6032         dir = "/sys$disk";
6033         dirlen = 9;
6034       }
6035       else
6036         dirlen = 1;
6037     }
6038     if (dirlen > (VMS_MAXRSS - 1)) {
6039       set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6040       return NULL;
6041     }
6042     trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
6043     if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6044     if (!strpbrk(dir+1,"/]>:")  &&
6045         (!DECC_POSIX_COMPLIANT_PATHNAMES && DECC_DISABLE_POSIX_ROOT)) {
6046       strcpy(trndir,*dir == '/' ? dir + 1: dir);
6047       trnlnm_iter_count = 0;
6048       while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6049         trnlnm_iter_count++; 
6050         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6051       }
6052       dirlen = strlen(trndir);
6053     }
6054     else {
6055       memcpy(trndir, dir, dirlen);
6056       trndir[dirlen] = '\0';
6057     }
6058
6059     /* At this point we are done with *dir and use *trndir which is a
6060      * copy that can be modified.  *dir must not be modified.
6061      */
6062
6063     /* If we were handed a rooted logical name or spec, treat it like a
6064      * simple directory, so that
6065      *    $ Define myroot dev:[dir.]
6066      *    ... do_fileify_dirspec("myroot",buf,1) ...
6067      * does something useful.
6068      */
6069     if (dirlen >= 2 && strEQ(trndir+dirlen-2,".]")) {
6070       trndir[--dirlen] = '\0';
6071       trndir[dirlen-1] = ']';
6072     }
6073     if (dirlen >= 2 && strEQ(trndir+dirlen-2,".>")) {
6074       trndir[--dirlen] = '\0';
6075       trndir[dirlen-1] = '>';
6076     }
6077
6078     if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6079       /* If we've got an explicit filename, we can just shuffle the string. */
6080       if (*(cp1+1)) hasfilename = 1;
6081       /* Similarly, we can just back up a level if we've got multiple levels
6082          of explicit directories in a VMS spec which ends with directories. */
6083       else {
6084         for (cp2 = cp1; cp2 > trndir; cp2--) {
6085           if (*cp2 == '.') {
6086             if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6087 /* fix-me, can not scan EFS file specs backward like this */
6088               *cp2 = *cp1; *cp1 = '\0';
6089               hasfilename = 1;
6090               break;
6091             }
6092           }
6093           if (*cp2 == '[' || *cp2 == '<') break;
6094         }
6095       }
6096     }
6097
6098     vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
6099     if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6100     cp1 = strpbrk(trndir,"]:>");
6101     if (cp1 && *(cp1+1) == ':')   /* DECNet node spec with :: */
6102         cp1 = strpbrk(cp1+2,"]:>");
6103
6104     if (hasfilename || !cp1) { /* filename present or not VMS */
6105
6106       if (trndir[0] == '.') {
6107         if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6108           PerlMem_free(trndir);
6109           PerlMem_free(vmsdir);
6110           return int_fileify_dirspec("[]", buf, NULL);
6111         }
6112         else if (trndir[1] == '.' &&
6113                (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6114           PerlMem_free(trndir);
6115           PerlMem_free(vmsdir);
6116           return int_fileify_dirspec("[-]", buf, NULL);
6117         }
6118       }
6119       if (dirlen && trndir[dirlen-1] == '/') {    /* path ends with '/'; just add .dir;1 */
6120         dirlen -= 1;                 /* to last element */
6121         lastdir = strrchr(trndir,'/');
6122       }
6123       else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6124         /* If we have "/." or "/..", VMSify it and let the VMS code
6125          * below expand it, rather than repeating the code to handle
6126          * relative components of a filespec here */
6127         do {
6128           if (*(cp1+2) == '.') cp1++;
6129           if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6130             char * ret_chr;
6131             if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6132                 PerlMem_free(trndir);
6133                 PerlMem_free(vmsdir);
6134                 return NULL;
6135             }
6136             if (strchr(vmsdir,'/') != NULL) {
6137               /* If int_tovmsspec() returned it, it must have VMS syntax
6138                * delimiters in it, so it's a mixed VMS/Unix spec.  We take
6139                * the time to check this here only so we avoid a recursion
6140                * loop; otherwise, gigo.
6141                */
6142               PerlMem_free(trndir);
6143               PerlMem_free(vmsdir);
6144               set_errno(EINVAL);  set_vaxc_errno(RMS$_SYN);
6145               return NULL;
6146             }
6147             if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6148                 PerlMem_free(trndir);
6149                 PerlMem_free(vmsdir);
6150                 return NULL;
6151             }
6152             ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6153             PerlMem_free(trndir);
6154             PerlMem_free(vmsdir);
6155             return ret_chr;
6156           }
6157           cp1++;
6158         } while ((cp1 = strstr(cp1,"/.")) != NULL);
6159         lastdir = strrchr(trndir,'/');
6160       }
6161       else if (dirlen >= 7 && strEQ(&trndir[dirlen-7],"/000000")) {
6162         char * ret_chr;
6163         /* Ditto for specs that end in an MFD -- let the VMS code
6164          * figure out whether it's a real device or a rooted logical. */
6165
6166         /* This should not happen any more.  Allowing the fake /000000
6167          * in a UNIX pathname causes all sorts of problems when trying
6168          * to run in UNIX emulation.  So the VMS to UNIX conversions
6169          * now remove the fake /000000 directories.
6170          */
6171
6172         trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6173         if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6174             PerlMem_free(trndir);
6175             PerlMem_free(vmsdir);
6176             return NULL;
6177         }
6178         if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6179             PerlMem_free(trndir);
6180             PerlMem_free(vmsdir);
6181             return NULL;
6182         }
6183         ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6184         PerlMem_free(trndir);
6185         PerlMem_free(vmsdir);
6186         return ret_chr;
6187       }
6188       else {
6189
6190         if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6191              !(lastdir = cp1 = strrchr(trndir,']')) &&
6192              !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6193
6194         cp2 = strrchr(cp1,'.');
6195         if (cp2) {
6196             int e_len, vs_len = 0;
6197             int is_dir = 0;
6198             char * cp3;
6199             cp3 = strchr(cp2,';');
6200             e_len = strlen(cp2);
6201             if (cp3) {
6202                 vs_len = strlen(cp3);
6203                 e_len = e_len - vs_len;
6204             }
6205             is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6206             if (!is_dir) {
6207                 if (!DECC_EFS_CHARSET) {
6208                     /* If this is not EFS, then not a directory */
6209                     PerlMem_free(trndir);
6210                     PerlMem_free(vmsdir);
6211                     set_errno(ENOTDIR);
6212                     set_vaxc_errno(RMS$_DIR);
6213                     return NULL;
6214                 }
6215             } else {
6216                 /* Ok, here we have an issue, technically if a .dir shows */
6217                 /* from inside a directory, then we should treat it as */
6218                 /* xxx^.dir.dir.  But we do not have that context at this */
6219                 /* point unless this is totally restructured, so we remove */
6220                 /* The .dir for now, and fix this better later */
6221                 dirlen = cp2 - trndir;
6222             }
6223             if (DECC_EFS_CHARSET && !strchr(trndir,'/')) {
6224                 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6225                 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6226                   
6227                 for (; cp4 > cp1; cp4--) {
6228                     if (*cp4 == '.') {
6229                         if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6230                             memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6231                             *cp4 = '^';
6232                             dirlen++;
6233                         }
6234                     }
6235                 }
6236             }
6237         }
6238
6239       }
6240
6241       retlen = dirlen + 6;
6242       memcpy(buf, trndir, dirlen);
6243       buf[dirlen] = '\0';
6244
6245       /* We've picked up everything up to the directory file name.
6246          Now just add the type and version, and we're set. */
6247       if ((!DECC_EFS_CASE_PRESERVE) && vms_process_case_tolerant)
6248           strcat(buf,".dir");
6249       else
6250           strcat(buf,".DIR");
6251       if (!DECC_FILENAME_UNIX_NO_VERSION)
6252           strcat(buf,";1");
6253       PerlMem_free(trndir);
6254       PerlMem_free(vmsdir);
6255       return buf;
6256     }
6257     else {  /* VMS-style directory spec */
6258
6259       char *esa, *esal, term, *cp;
6260       char *my_esa;
6261       int my_esa_len;
6262       unsigned long int cmplen, haslower = 0;
6263       struct FAB dirfab = cc$rms_fab;
6264       rms_setup_nam(savnam);
6265       rms_setup_nam(dirnam);
6266
6267       esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6268       if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6269       esal = NULL;
6270 #if defined(NAML$C_MAXRSS)
6271       esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6272       if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6273 #endif
6274       rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6275       rms_bind_fab_nam(dirfab, dirnam);
6276       rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6277       rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6278 #ifdef NAM$M_NO_SHORT_UPCASE
6279       if (DECC_EFS_CASE_PRESERVE)
6280         rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6281 #endif
6282
6283       for (cp = trndir; *cp; cp++)
6284         if (islower(*cp)) { haslower = 1; break; }
6285       if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6286         if ((dirfab.fab$l_sts == RMS$_DIR) ||
6287             (dirfab.fab$l_sts == RMS$_DNF) ||
6288             (dirfab.fab$l_sts == RMS$_PRV)) {
6289             rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6290             sts = sys$parse(&dirfab);
6291         }
6292         if (!sts) {
6293           PerlMem_free(esa);
6294           if (esal != NULL)
6295               PerlMem_free(esal);
6296           PerlMem_free(trndir);
6297           PerlMem_free(vmsdir);
6298           set_errno(EVMSERR);
6299           set_vaxc_errno(dirfab.fab$l_sts);
6300           return NULL;
6301         }
6302       }
6303       else {
6304         savnam = dirnam;
6305         /* Does the file really exist? */
6306         if (sys$search(&dirfab)& STS$K_SUCCESS) { 
6307           /* Yes; fake the fnb bits so we'll check type below */
6308           rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6309         }
6310         else { /* No; just work with potential name */
6311           if (dirfab.fab$l_sts    == RMS$_FNF
6312               || dirfab.fab$l_sts == RMS$_DNF
6313               || dirfab.fab$l_sts == RMS$_FND)
6314                 dirnam = savnam;
6315           else { 
6316             int fab_sts;
6317             fab_sts = dirfab.fab$l_sts;
6318             sts = rms_free_search_context(&dirfab);
6319             PerlMem_free(esa);
6320             if (esal != NULL)
6321                 PerlMem_free(esal);
6322             PerlMem_free(trndir);
6323             PerlMem_free(vmsdir);
6324             set_errno(EVMSERR);  set_vaxc_errno(fab_sts);
6325             return NULL;
6326           }
6327         }
6328       }
6329
6330       /* Make sure we are using the right buffer */
6331 #if defined(NAML$C_MAXRSS)
6332       if (esal != NULL) {
6333         my_esa = esal;
6334         my_esa_len = rms_nam_esll(dirnam);
6335       } else {
6336 #endif
6337         my_esa = esa;
6338         my_esa_len = rms_nam_esl(dirnam);
6339 #if defined(NAML$C_MAXRSS)
6340       }
6341 #endif
6342       my_esa[my_esa_len] = '\0';
6343       if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6344         cp1 = strchr(my_esa,']');
6345         if (!cp1) cp1 = strchr(my_esa,'>');
6346         if (cp1) {  /* Should always be true */
6347           my_esa_len -= cp1 - my_esa - 1;
6348           memmove(my_esa, cp1 + 1, my_esa_len);
6349         }
6350       }
6351       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) {  /* Was type specified? */
6352         /* Yep; check version while we're at it, if it's there. */
6353         cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6354         if (strnNE(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6355           /* Something other than .DIR[;1].  Bzzt. */
6356           sts = rms_free_search_context(&dirfab);
6357           PerlMem_free(esa);
6358           if (esal != NULL)
6359              PerlMem_free(esal);
6360           PerlMem_free(trndir);
6361           PerlMem_free(vmsdir);
6362           set_errno(ENOTDIR);
6363           set_vaxc_errno(RMS$_DIR);
6364           return NULL;
6365         }
6366       }
6367
6368       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6369         /* They provided at least the name; we added the type, if necessary, */
6370         my_strlcpy(buf, my_esa, VMS_MAXRSS);
6371         sts = rms_free_search_context(&dirfab);
6372         PerlMem_free(trndir);
6373         PerlMem_free(esa);
6374         if (esal != NULL)
6375             PerlMem_free(esal);
6376         PerlMem_free(vmsdir);
6377         return buf;
6378       }
6379       if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6380         for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6381         *cp1 = '\0';
6382         my_esa_len -= 9;
6383       }
6384       if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6385       if (cp1 == NULL) { /* should never happen */
6386         sts = rms_free_search_context(&dirfab);
6387         PerlMem_free(trndir);
6388         PerlMem_free(esa);
6389         if (esal != NULL)
6390             PerlMem_free(esal);
6391         PerlMem_free(vmsdir);
6392         return NULL;
6393       }
6394       term = *cp1;
6395       *cp1 = '\0';
6396       retlen = strlen(my_esa);
6397       cp1 = strrchr(my_esa,'.');
6398       /* ODS-5 directory specifications can have extra "." in them. */
6399       /* Fix-me, can not scan EFS file specifications backwards */
6400       while (cp1 != NULL) {
6401         if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6402           break;
6403         else {
6404            cp1--;
6405            while ((cp1 > my_esa) && (*cp1 != '.'))
6406              cp1--;
6407         }
6408         if (cp1 == my_esa)
6409           cp1 = NULL;
6410       }
6411
6412       if ((cp1) != NULL) {
6413         /* There's more than one directory in the path.  Just roll back. */
6414         *cp1 = term;
6415         my_strlcpy(buf, my_esa, VMS_MAXRSS);
6416       }
6417       else {
6418         if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6419           /* Go back and expand rooted logical name */
6420           rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6421 #ifdef NAM$M_NO_SHORT_UPCASE
6422           if (DECC_EFS_CASE_PRESERVE)
6423             rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6424 #endif
6425           if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6426             sts = rms_free_search_context(&dirfab);
6427             PerlMem_free(esa);
6428             if (esal != NULL)
6429                 PerlMem_free(esal);
6430             PerlMem_free(trndir);
6431             PerlMem_free(vmsdir);
6432             set_errno(EVMSERR);
6433             set_vaxc_errno(dirfab.fab$l_sts);
6434             return NULL;
6435           }
6436
6437           /* This changes the length of the string of course */
6438           if (esal != NULL) {
6439               my_esa_len = rms_nam_esll(dirnam);
6440           } else {
6441               my_esa_len = rms_nam_esl(dirnam);
6442           }
6443
6444           retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6445           cp1 = strstr(my_esa,"][");
6446           if (!cp1) cp1 = strstr(my_esa,"]<");
6447           dirlen = cp1 - my_esa;
6448           memcpy(buf, my_esa, dirlen);
6449           if (strBEGINs(cp1+2,"000000]")) {
6450             buf[dirlen-1] = '\0';
6451             /* fix-me Not full ODS-5, just extra dots in directories for now */
6452             cp1 = buf + dirlen - 1;
6453             while (cp1 > buf)
6454             {
6455               if (*cp1 == '[')
6456                 break;
6457               if (*cp1 == '.') {
6458                 if (*(cp1-1) != '^')
6459                   break;
6460               }
6461               cp1--;
6462             }
6463             if (*cp1 == '.') *cp1 = ']';
6464             else {
6465               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6466               memmove(cp1+1,"000000]",7);
6467             }
6468           }
6469           else {
6470             memmove(buf+dirlen, cp1+2, retlen-dirlen);
6471             buf[retlen] = '\0';
6472             /* Convert last '.' to ']' */
6473             cp1 = buf+retlen-1;
6474             while (*cp != '[') {
6475               cp1--;
6476               if (*cp1 == '.') {
6477                 /* Do not trip on extra dots in ODS-5 directories */
6478                 if ((cp1 == buf) || (*(cp1-1) != '^'))
6479                 break;
6480               }
6481             }
6482             if (*cp1 == '.') *cp1 = ']';
6483             else {
6484               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6485               memmove(cp1+1,"000000]",7);
6486             }
6487           }
6488         }
6489         else {  /* This is a top-level dir.  Add the MFD to the path. */
6490           cp1 = strrchr(my_esa, ':');
6491           assert(cp1);
6492           memmove(buf, my_esa, cp1 - my_esa + 1);
6493           memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6494           memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6495           buf[retlen + 7] = '\0';  /* We've inserted '000000]' */
6496         }
6497       }
6498       sts = rms_free_search_context(&dirfab);
6499       /* We've set up the string up through the filename.  Add the
6500          type and version, and we're done. */
6501       strcat(buf,".DIR;1");
6502
6503       /* $PARSE may have upcased filespec, so convert output to lower
6504        * case if input contained any lowercase characters. */
6505       if (haslower && !DECC_EFS_CASE_PRESERVE) __mystrtolower(buf);
6506       PerlMem_free(trndir);
6507       PerlMem_free(esa);
6508       if (esal != NULL)
6509         PerlMem_free(esal);
6510       PerlMem_free(vmsdir);
6511       return buf;
6512     }
6513 }  /* end of int_fileify_dirspec() */
6514
6515
6516 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6517 static char *
6518 mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6519 {
6520     static char __fileify_retbuf[VMS_MAXRSS];
6521     char * fileified, *ret_spec, *ret_buf;
6522
6523     fileified = NULL;
6524     ret_buf = buf;
6525     if (ret_buf == NULL) {
6526         if (ts) {
6527             Newx(fileified, VMS_MAXRSS, char);
6528             if (fileified == NULL)
6529                 _ckvmssts(SS$_INSFMEM);
6530             ret_buf = fileified;
6531         } else {
6532             ret_buf = __fileify_retbuf;
6533         }
6534     }
6535
6536     ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6537
6538     if (ret_spec == NULL) {
6539        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6540        if (fileified)
6541            Safefree(fileified);
6542     }
6543
6544     return ret_spec;
6545 }  /* end of do_fileify_dirspec() */
6546 /*}}}*/
6547
6548 /* External entry points */
6549 char *
6550 Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6551 {
6552     return do_fileify_dirspec(dir, buf, 0, NULL);
6553 }
6554
6555 char *
6556 Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6557 {
6558     return do_fileify_dirspec(dir, buf, 1, NULL);
6559 }
6560
6561 char *
6562 Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6563 {
6564     return do_fileify_dirspec(dir, buf, 0, utf8_fl);
6565 }
6566
6567 char *
6568 Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6569 {
6570     return do_fileify_dirspec(dir, buf, 1, utf8_fl);
6571 }
6572
6573 static char * 
6574 int_pathify_dirspec_simple(const char * dir, char * buf,
6575     char * v_spec, int v_len, char * r_spec, int r_len,
6576     char * d_spec, int d_len, char * n_spec, int n_len,
6577     char * e_spec, int e_len, char * vs_spec, int vs_len)
6578 {
6579
6580     /* VMS specification - Try to do this the simple way */
6581     if ((v_len + r_len > 0) || (d_len > 0)) {
6582         int is_dir;
6583
6584         /* No name or extension component, already a directory */
6585         if ((n_len + e_len + vs_len) == 0) {
6586             strcpy(buf, dir);
6587             return buf;
6588         }
6589
6590         /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6591         /* This results from catfile() being used instead of catdir() */
6592         /* So even though it should not work, we need to allow it */
6593
6594         /* If this is .DIR;1 then do a simple conversion */
6595         is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6596         if (is_dir || (e_len == 0) && (d_len > 0)) {
6597              int len;
6598              len = v_len + r_len + d_len - 1;
6599              char dclose = d_spec[d_len - 1];
6600              memcpy(buf, dir, len);
6601              buf[len] = '.';
6602              len++;
6603              memcpy(&buf[len], n_spec, n_len);
6604              len += n_len;
6605              buf[len] = dclose;
6606              buf[len + 1] = '\0';
6607              return buf;
6608         }
6609
6610 #ifdef HAS_SYMLINK
6611         else if (d_len > 0) {
6612             /* In the olden days, a directory needed to have a .DIR */
6613             /* extension to be a valid directory, but now it could  */
6614             /* be a symbolic link */
6615             int len;
6616             len = v_len + r_len + d_len - 1;
6617             char dclose = d_spec[d_len - 1];
6618             memcpy(buf, dir, len);
6619             buf[len] = '.';
6620             len++;
6621             memcpy(&buf[len], n_spec, n_len);
6622             len += n_len;
6623             if (e_len > 0) {
6624                 if (DECC_EFS_CHARSET) {
6625                     if (e_len == 4 
6626                         && (toUPPER_A(e_spec[1]) == 'D')
6627                         && (toUPPER_A(e_spec[2]) == 'I')
6628                         && (toUPPER_A(e_spec[3]) == 'R')) {
6629
6630                         /* Corner case: directory spec with invalid version.
6631                          * Valid would have followed is_dir path above.
6632                          */
6633                         SETERRNO(ENOTDIR, RMS$_DIR);
6634                         return NULL;
6635                     }
6636                     else {
6637                         buf[len] = '^';
6638                         len++;
6639                         memcpy(&buf[len], e_spec, e_len);
6640                         len += e_len;
6641                     }
6642                 }
6643                 else {
6644                     SETERRNO(ENOTDIR, RMS$_DIR);
6645                     return NULL;
6646                 }
6647             }
6648             buf[len] = dclose;
6649             buf[len + 1] = '\0';
6650             return buf;
6651         }
6652 #else
6653         else {
6654             set_vaxc_errno(RMS$_DIR);
6655             set_errno(ENOTDIR);
6656             return NULL;
6657         }
6658 #endif
6659     }
6660     set_vaxc_errno(RMS$_DIR);
6661     set_errno(ENOTDIR);
6662     return NULL;
6663 }
6664
6665
6666 /* Internal routine to make sure or convert a directory to be in a */
6667 /* path specification.  No utf8 flag because it is not changed or used */
6668 static char *
6669 int_pathify_dirspec(const char *dir, char *buf)
6670 {
6671     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6672     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6673     char * exp_spec, *ret_spec;
6674     char * trndir;
6675     unsigned short int trnlnm_iter_count;
6676     STRLEN trnlen;
6677     int need_to_lower;
6678
6679     if (vms_debug_fileify) {
6680         if (dir == NULL)
6681             fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6682         else
6683             fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6684     }
6685
6686     /* We may need to lower case the result if we translated  */
6687     /* a logical name or got the current working directory */
6688     need_to_lower = 0;
6689
6690     if (!dir || !*dir) {
6691       set_errno(EINVAL);
6692       set_vaxc_errno(SS$_BADPARAM);
6693       return NULL;
6694     }
6695
6696     trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6697     if (trndir == NULL)
6698         _ckvmssts_noperl(SS$_INSFMEM);
6699
6700     /* If no directory specified use the current default */
6701     if (*dir)
6702         my_strlcpy(trndir, dir, VMS_MAXRSS);
6703     else {
6704         getcwd(trndir, VMS_MAXRSS - 1);
6705         need_to_lower = 1;
6706     }
6707
6708     /* now deal with bare names that could be logical names */
6709     trnlnm_iter_count = 0;
6710     while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6711            && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6712         trnlnm_iter_count++; 
6713         need_to_lower = 1;
6714         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6715             break;
6716         trnlen = strlen(trndir);
6717
6718         /* Trap simple rooted lnms, and return lnm:[000000] */
6719         if (strEQ(trndir+trnlen-2,".]")) {
6720             my_strlcpy(buf, dir, VMS_MAXRSS);
6721             strcat(buf, ":[000000]");
6722             PerlMem_free(trndir);
6723
6724             if (vms_debug_fileify) {
6725                 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6726             }
6727             return buf;
6728         }
6729     }
6730
6731     /* At this point we do not work with *dir, but the copy in  *trndir */
6732
6733     if (need_to_lower && !DECC_EFS_CASE_PRESERVE) {
6734         /* Legacy mode, lower case the returned value */
6735         __mystrtolower(trndir);
6736     }
6737
6738
6739     /* Some special cases, '..', '.' */
6740     sts = 0;
6741     if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6742        /* Force UNIX filespec */
6743        sts = 1;
6744
6745     } else {
6746         /* Is this Unix or VMS format? */
6747         sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6748                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6749                              &e_len, &vs_spec, &vs_len);
6750         if (sts == 0) {
6751
6752             /* Just a filename? */
6753             if ((v_len + r_len + d_len) == 0) {
6754
6755                 /* Now we have a problem, this could be Unix or VMS */
6756                 /* We have to guess.  .DIR usually means VMS */
6757
6758                 /* In UNIX report mode, the .DIR extension is removed */
6759                 /* if one shows up, it is for a non-directory or a directory */
6760                 /* in EFS charset mode */
6761
6762                 /* So if we are in Unix report mode, assume that this */
6763                 /* is a relative Unix directory specification */
6764
6765                 sts = 1;
6766                 if (!DECC_FILENAME_UNIX_REPORT && DECC_EFS_CHARSET) {
6767                     int is_dir;
6768                     is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6769
6770                     if (is_dir) {
6771                         /* Traditional mode, assume .DIR is directory */
6772                         buf[0] = '[';
6773                         buf[1] = '.';
6774                         memcpy(&buf[2], n_spec, n_len);
6775                         buf[n_len + 2] = ']';
6776                         buf[n_len + 3] = '\0';
6777                         PerlMem_free(trndir);
6778                         if (vms_debug_fileify) {
6779                             fprintf(stderr,
6780                                     "int_pathify_dirspec: buf = %s\n",
6781                                     buf);
6782                         }
6783                         return buf;
6784                     }
6785                 }
6786             }
6787         }
6788     }
6789     if (sts == 0) {
6790         ret_spec = int_pathify_dirspec_simple(trndir, buf,
6791             v_spec, v_len, r_spec, r_len,
6792             d_spec, d_len, n_spec, n_len,
6793             e_spec, e_len, vs_spec, vs_len);
6794
6795         if (ret_spec != NULL) {
6796             PerlMem_free(trndir);
6797             if (vms_debug_fileify) {
6798                 fprintf(stderr,
6799                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6800             }
6801             return ret_spec;
6802         }
6803
6804         /* Simple way did not work, which means that a logical name */
6805         /* was present for the directory specification.             */
6806         /* Need to use an rmsexpand variant to decode it completely */
6807         exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6808         if (exp_spec == NULL)
6809             _ckvmssts_noperl(SS$_INSFMEM);
6810
6811         ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6812         if (ret_spec != NULL) {
6813             sts = vms_split_path(exp_spec, &v_spec, &v_len,
6814                                  &r_spec, &r_len, &d_spec, &d_len,
6815                                  &n_spec, &n_len, &e_spec,
6816                                  &e_len, &vs_spec, &vs_len);
6817             if (sts == 0) {
6818                 ret_spec = int_pathify_dirspec_simple(
6819                     exp_spec, buf, v_spec, v_len, r_spec, r_len,
6820                     d_spec, d_len, n_spec, n_len,
6821                     e_spec, e_len, vs_spec, vs_len);
6822
6823                 if ((ret_spec != NULL) && (!DECC_EFS_CASE_PRESERVE)) {
6824                     /* Legacy mode, lower case the returned value */
6825                     __mystrtolower(ret_spec);
6826                 }
6827             } else {
6828                 set_vaxc_errno(RMS$_DIR);
6829                 set_errno(ENOTDIR);
6830                 ret_spec = NULL;
6831             }
6832         }
6833         PerlMem_free(exp_spec);
6834         PerlMem_free(trndir);
6835         if (vms_debug_fileify) {
6836             if (ret_spec == NULL)
6837                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6838             else
6839                 fprintf(stderr,
6840                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6841         }
6842         return ret_spec;
6843
6844     } else {
6845         /* Unix specification, Could be trivial conversion, */
6846         /* but have to deal with trailing '.dir' or extra '.' */
6847
6848         char * lastdot;
6849         char * lastslash;
6850         int is_dir;
6851         STRLEN dir_len = strlen(trndir);
6852
6853         lastslash = strrchr(trndir, '/');
6854         if (lastslash == NULL)
6855             lastslash = trndir;
6856         else
6857             lastslash++;
6858
6859         lastdot = NULL;
6860
6861         /* '..' or '.' are valid directory components */
6862         is_dir = 0;
6863         if (lastslash[0] == '.') {
6864             if (lastslash[1] == '\0') {
6865                is_dir = 1;
6866             } else if (lastslash[1] == '.') {
6867                 if (lastslash[2] == '\0') {
6868                     is_dir = 1;
6869                 } else {
6870                     /* And finally allow '...' */
6871                     if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6872                         is_dir = 1;
6873                     }
6874                 }
6875             }
6876         }
6877
6878         if (!is_dir) {
6879            lastdot = strrchr(lastslash, '.');
6880         }
6881         if (lastdot != NULL) {
6882             STRLEN e_len;
6883              /* '.dir' is discarded, and any other '.' is invalid */
6884             e_len = strlen(lastdot);
6885
6886             is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6887
6888             if (is_dir) {
6889                 dir_len = dir_len - 4;
6890             }
6891         }
6892
6893         my_strlcpy(buf, trndir, VMS_MAXRSS);
6894         if (buf[dir_len - 1] != '/') {
6895             buf[dir_len] = '/';
6896             buf[dir_len + 1] = '\0';
6897         }
6898
6899         /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6900         if (!DECC_EFS_CHARSET) {
6901              int dir_start = 0;
6902              char * str = buf;
6903              if (str[0] == '.') {
6904                  char * dots = str;
6905                  int cnt = 1;
6906                  while ((dots[cnt] == '.') && (cnt < 3))
6907                      cnt++;
6908                  if (cnt <= 3) {
6909                      if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6910                          dir_start = 1;
6911                          str += cnt;
6912                      }
6913                  }
6914              }
6915              for (; *str; ++str) {
6916                  while (*str == '/') {
6917                      dir_start = 1;
6918                      *str++;
6919                  }
6920                  if (dir_start) {
6921
6922                      /* Have to skip up to three dots which could be */
6923                      /* directories, 3 dots being a VMS extension for Perl */
6924                      char * dots = str;
6925                      int cnt = 0;
6926                      while ((dots[cnt] == '.') && (cnt < 3)) {
6927                          cnt++;
6928                      }
6929                      if (dots[cnt] == '\0')
6930                          break;
6931                      if ((cnt > 1) && (dots[cnt] != '/')) {
6932                          dir_start = 0;
6933                      } else {
6934                          str += cnt;
6935                      }
6936
6937                      /* too many dots? */
6938                      if ((cnt == 0) || (cnt > 3)) {
6939                          dir_start = 0;
6940                      }
6941                  }
6942                  if (!dir_start && (*str == '.')) {
6943                      *str = '_';
6944                  }                 
6945              }
6946         }
6947         PerlMem_free(trndir);
6948         ret_spec = buf;
6949         if (vms_debug_fileify) {
6950             if (ret_spec == NULL)
6951                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6952             else
6953                 fprintf(stderr,
6954                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6955         }
6956         return ret_spec;
6957     }
6958 }
6959
6960 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6961 static char *
6962 mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6963 {
6964     static char __pathify_retbuf[VMS_MAXRSS];
6965     char * pathified, *ret_spec, *ret_buf;
6966     
6967     pathified = NULL;
6968     ret_buf = buf;
6969     if (ret_buf == NULL) {
6970         if (ts) {
6971             Newx(pathified, VMS_MAXRSS, char);
6972             if (pathified == NULL)
6973                 _ckvmssts(SS$_INSFMEM);
6974             ret_buf = pathified;
6975         } else {
6976             ret_buf = __pathify_retbuf;
6977         }
6978     }
6979
6980     ret_spec = int_pathify_dirspec(dir, ret_buf);
6981
6982     if (ret_spec == NULL) {
6983        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6984        if (pathified)
6985            Safefree(pathified);
6986     }
6987
6988     return ret_spec;
6989
6990 }  /* end of do_pathify_dirspec() */
6991
6992
6993 /* External entry points */
6994 char *
6995 Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6996 {
6997     return do_pathify_dirspec(dir, buf, 0, NULL);
6998 }
6999
7000 char *
7001 Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7002 {
7003     return do_pathify_dirspec(dir, buf, 1, NULL);
7004 }
7005
7006 char *
7007 Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7008 {
7009     return do_pathify_dirspec(dir, buf, 0, utf8_fl);
7010 }
7011
7012 char *
7013 Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7014 {
7015     return do_pathify_dirspec(dir, buf, 1, utf8_fl);
7016 }
7017
7018 /* Internal tounixspec routine that does not use a thread context */
7019 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7020 static char *
7021 int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7022 {
7023   char *dirend, *cp1, *cp3, *tmp;
7024   const char *cp2;
7025   int dirlen;
7026   unsigned short int trnlnm_iter_count;
7027   int cmp_rslt, outchars_added;
7028   if (utf8_fl != NULL)
7029     *utf8_fl = 0;
7030
7031   if (vms_debug_fileify) {
7032       if (spec == NULL)
7033           fprintf(stderr, "int_tounixspec: spec = NULL\n");
7034       else
7035           fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7036   }
7037
7038
7039   if (spec == NULL) {
7040       set_errno(EINVAL);
7041       set_vaxc_errno(SS$_BADPARAM);
7042       return NULL;
7043   }
7044   if (strlen(spec) > (VMS_MAXRSS-1)) {
7045       set_errno(E2BIG);
7046       set_vaxc_errno(SS$_BUFFEROVF);
7047       return NULL;
7048   }
7049
7050   /* New VMS specific format needs translation
7051    * glob passes filenames with trailing '\n' and expects this preserved.
7052    */
7053   if (DECC_POSIX_COMPLIANT_PATHNAMES) {
7054     if (! strBEGINs(spec, "\"^UP^")) {
7055       char * uspec;
7056       char *tunix;
7057       int tunix_len;
7058       int nl_flag;
7059
7060       tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
7061       if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7062       tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
7063       nl_flag = 0;
7064       if (tunix[tunix_len - 1] == '\n') {
7065         tunix[tunix_len - 1] = '\"';
7066         tunix[tunix_len] = '\0';
7067         tunix_len--;
7068         nl_flag = 1;
7069       }
7070       uspec = decc$translate_vms(tunix);
7071       PerlMem_free(tunix);
7072       if ((int)uspec > 0) {
7073         my_strlcpy(rslt, uspec, VMS_MAXRSS);
7074         if (nl_flag) {
7075           strcat(rslt,"\n");
7076         }
7077         else {
7078           /* If we can not translate it, makemaker wants as-is */
7079           my_strlcpy(rslt, spec, VMS_MAXRSS);
7080         }
7081         return rslt;
7082       }
7083     }
7084   }
7085
7086   cmp_rslt = 0; /* Presume VMS */
7087   cp1 = strchr(spec, '/');
7088   if (cp1 == NULL)
7089     cmp_rslt = 0;
7090
7091     /* Look for EFS ^/ */
7092     if (DECC_EFS_CHARSET) {
7093       while (cp1 != NULL) {
7094         cp2 = cp1 - 1;
7095         if (*cp2 != '^') {
7096           /* Found illegal VMS, assume UNIX */
7097           cmp_rslt = 1;
7098           break;
7099         }
7100       cp1++;
7101       cp1 = strchr(cp1, '/');
7102     }
7103   }
7104
7105   /* Look for "." and ".." */
7106   if (DECC_FILENAME_UNIX_REPORT) {
7107     if (spec[0] == '.') {
7108       if ((spec[1] == '\0') || (spec[1] == '\n')) {
7109         cmp_rslt = 1;
7110       }
7111       else {
7112         if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7113           cmp_rslt = 1;
7114         }
7115       }
7116     }
7117   }
7118
7119   cp1 = rslt;
7120   cp2 = spec;
7121
7122   /* This is already UNIX or at least nothing VMS understands,
7123    * so all we can reasonably do is unescape extended chars.
7124    */
7125   if (cmp_rslt) {
7126     while (*cp2) {
7127         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7128         cp1 += outchars_added;
7129     }
7130     *cp1 = '\0';    
7131     if (vms_debug_fileify) {
7132         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7133     }
7134     return rslt;
7135   }
7136
7137   dirend = strrchr(spec,']');
7138   if (dirend == NULL) dirend = strrchr(spec,'>');
7139   if (dirend == NULL) dirend = strchr(spec,':');
7140   if (dirend == NULL) {
7141     while (*cp2) {
7142         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7143         cp1 += outchars_added;
7144     }
7145     *cp1 = '\0';    
7146     if (vms_debug_fileify) {
7147         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7148     }
7149     return rslt;
7150   }
7151
7152   /* Special case 1 - sys$posix_root = / */
7153   if (!DECC_DISABLE_POSIX_ROOT) {
7154     if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7155       *cp1 = '/';
7156       cp1++;
7157       cp2 = cp2 + 15;
7158       }
7159   }
7160
7161   /* Special case 2 - Convert NLA0: to /dev/null */
7162   cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7163   if (cmp_rslt == 0) {
7164     strcpy(rslt, "/dev/null");
7165     cp1 = cp1 + 9;
7166     cp2 = cp2 + 5;
7167     if (spec[6] != '\0') {
7168       cp1[9] = '/';
7169       cp1++;
7170       cp2++;
7171     }
7172   }
7173
7174    /* Also handle special case "SYS$SCRATCH:" */
7175   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7176   tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7177   if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7178   if (cmp_rslt == 0) {
7179   int islnm;
7180
7181     islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7182     if (!islnm) {
7183       strcpy(rslt, "/tmp");
7184       cp1 = cp1 + 4;
7185       cp2 = cp2 + 12;
7186       if (spec[12] != '\0') {
7187         cp1[4] = '/';
7188         cp1++;
7189         cp2++;
7190       }
7191     }
7192   }
7193
7194   if (*cp2 != '[' && *cp2 != '<') {
7195     *(cp1++) = '/';
7196   }
7197   else {  /* the VMS spec begins with directories */
7198     cp2++;
7199     if (*cp2 == ']' || *cp2 == '>') {
7200       *(cp1++) = '.';
7201       *(cp1++) = '/';
7202     }
7203     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7204       if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7205         PerlMem_free(tmp);
7206         if (vms_debug_fileify) {
7207             fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7208         }
7209         return NULL;
7210       }
7211       trnlnm_iter_count = 0;
7212       do {
7213         cp3 = tmp;
7214         while (*cp3 != ':' && *cp3) cp3++;
7215         *(cp3++) = '\0';
7216         if (strchr(cp3,']') != NULL) break;
7217         trnlnm_iter_count++; 
7218         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7219       } while (vmstrnenv(tmp,tmp,0,fildev,0));
7220       cp1 = rslt;
7221       cp3 = tmp;
7222       *(cp1++) = '/';
7223       while (*cp3) {
7224         *(cp1++) = *(cp3++);
7225         if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7226             PerlMem_free(tmp);
7227             set_errno(ENAMETOOLONG);
7228             set_vaxc_errno(SS$_BUFFEROVF);
7229             if (vms_debug_fileify) {
7230                 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7231             }
7232             return NULL; /* No room */
7233         }
7234       }
7235       *(cp1++) = '/';
7236     }
7237     if ((*cp2 == '^')) {
7238         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7239         cp1 += outchars_added;
7240     }
7241     else if ( *cp2 == '.') {
7242       if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7243         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7244         cp2 += 3;
7245       }
7246       else cp2++;
7247     }
7248   }
7249   PerlMem_free(tmp);
7250   for (; cp2 <= dirend; cp2++) {
7251     if ((*cp2 == '^')) {
7252         /* EFS file escape -- unescape it. */
7253         cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added) - 1;
7254         cp1 += outchars_added;
7255     }
7256     else if (*cp2 == ':') {
7257       *(cp1++) = '/';
7258       if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7259     }
7260     else if (*cp2 == ']' || *cp2 == '>') {
7261       if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7262     }
7263     else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7264       *(cp1++) = '/';
7265       if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7266         while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7267                *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7268         if (memEQs(cp2,7,"[000000") && (*(cp2+7) == ']' ||
7269             *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7270       }
7271       else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7272         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7273         cp2 += 2;
7274       }
7275     }
7276     else if (*cp2 == '-') {
7277       if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7278         while (*cp2 == '-') {
7279           cp2++;
7280           *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7281         }
7282         if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7283                                                          /* filespecs like */
7284           set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);   /* [fred.--foo.bar] */
7285           if (vms_debug_fileify) {
7286               fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7287           }
7288           return NULL;
7289         }
7290       }
7291       else *(cp1++) = *cp2;
7292     }
7293     else *(cp1++) = *cp2;
7294   }
7295   /* Translate the rest of the filename. */
7296   while (*cp2) {
7297       int dot_seen = 0;
7298       switch(*cp2) {
7299       /* Fixme - for compatibility with the CRTL we should be removing */
7300       /* spaces from the file specifications, but this may show that */
7301       /* some tests that were appearing to pass are not really passing */
7302       case '%':
7303           cp2++;
7304           *(cp1++) = '?';
7305           break;
7306       case '^':
7307           cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7308           cp1 += outchars_added;
7309           break;
7310       case ';':
7311           if (DECC_FILENAME_UNIX_NO_VERSION) {
7312               /* Easy, drop the version */
7313               while (*cp2)
7314                   cp2++;
7315               break;
7316           } else {
7317               /* Punt - passing the version as a dot will probably */
7318               /* break perl in weird ways, but so did passing */
7319               /* through the ; as a version.  Follow the CRTL and */
7320               /* hope for the best. */
7321               cp2++;
7322               *(cp1++) = '.';
7323           }
7324           break;
7325       case '.':
7326           if (dot_seen) {
7327               /* We will need to fix this properly later */
7328               /* As Perl may be installed on an ODS-5 volume, but not */
7329               /* have the EFS_CHARSET enabled, it still may encounter */
7330               /* filenames with extra dots in them, and a precedent got */
7331               /* set which allowed them to work, that we will uphold here */
7332               /* If extra dots are present in a name and no ^ is on them */
7333               /* VMS assumes that the first one is the extension delimiter */
7334               /* the rest have an implied ^. */
7335
7336               /* this is also a conflict as the . is also a version */
7337               /* delimiter in VMS, */
7338
7339               *(cp1++) = *(cp2++);
7340               break;
7341           }
7342           dot_seen = 1;
7343           /* This is an extension */
7344           if (DECC_READDIR_DROPDOTNOTYPE) {
7345               cp2++;
7346               if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7347                   /* Drop the dot for the extension */
7348                   break;
7349               } else {
7350                   *(cp1++) = '.';
7351               }
7352               break;
7353           }
7354       default:
7355           *(cp1++) = *(cp2++);
7356       }
7357   }
7358   *cp1 = '\0';
7359
7360   /* This still leaves /000000/ when working with a
7361    * VMS device root or concealed root.
7362    */
7363   {
7364       int ulen;
7365       char * zeros;
7366
7367       ulen = strlen(rslt);
7368
7369       /* Get rid of "000000/ in rooted filespecs */
7370       if (ulen > 7) {
7371         zeros = strstr(rslt, "/000000/");
7372         if (zeros != NULL) {
7373           int mlen;
7374           mlen = ulen - (zeros - rslt) - 7;
7375           memmove(zeros, &zeros[7], mlen);
7376           ulen = ulen - 7;
7377           rslt[ulen] = '\0';
7378         }
7379       }
7380   }
7381
7382   if (vms_debug_fileify) {
7383       fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7384   }
7385   return rslt;
7386
7387 }  /* end of int_tounixspec() */
7388
7389
7390 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7391 static char *
7392 mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7393 {
7394     static char __tounixspec_retbuf[VMS_MAXRSS];
7395     char * unixspec, *ret_spec, *ret_buf;
7396
7397     unixspec = NULL;
7398     ret_buf = buf;
7399     if (ret_buf == NULL) {
7400         if (ts) {
7401             Newx(unixspec, VMS_MAXRSS, char);
7402             if (unixspec == NULL)
7403                 _ckvmssts(SS$_INSFMEM);
7404             ret_buf = unixspec;
7405         } else {
7406             ret_buf = __tounixspec_retbuf;
7407         }
7408     }
7409
7410     ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7411
7412     if (ret_spec == NULL) {
7413        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7414        if (unixspec)
7415            Safefree(unixspec);
7416     }
7417
7418     return ret_spec;
7419
7420 }  /* end of do_tounixspec() */
7421 /*}}}*/
7422 /* External entry points */
7423 char *
7424 Perl_tounixspec(pTHX_ const char *spec, char *buf)
7425 {
7426     return do_tounixspec(spec, buf, 0, NULL);
7427 }
7428
7429 char *
7430 Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7431 {
7432     return do_tounixspec(spec,buf,1, NULL);
7433 }
7434
7435 char *
7436 Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7437 {
7438     return do_tounixspec(spec,buf,0, utf8_fl);
7439 }
7440
7441 char *
7442 Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7443 {
7444     return do_tounixspec(spec,buf,1, utf8_fl);
7445 }
7446
7447 /*
7448  This procedure is used to identify if a path is based in either
7449  the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7450  it returns the OpenVMS format directory for it.
7451
7452  It is expecting specifications of only '/' or '/xxxx/'
7453
7454  If a posix root does not exist, or 'xxxx' is not a directory
7455  in the posix root, it returns a failure.
7456
7457  FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7458
7459  It is used only internally by posix_to_vmsspec_hardway().
7460  */
7461
7462 static int
7463 posix_root_to_vms(char *vmspath, int vmspath_len,
7464                   const char *unixpath, const int * utf8_fl)
7465 {
7466   int sts;
7467   struct FAB myfab = cc$rms_fab;
7468   rms_setup_nam(mynam);
7469   struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7470   struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7471   char * esa, * esal, * rsa, * rsal;
7472   int dir_flag;
7473   int unixlen;
7474
7475   dir_flag = 0;
7476   vmspath[0] = '\0';
7477   unixlen = strlen(unixpath);
7478   if (unixlen == 0) {
7479     return RMS$_FNF;
7480   }
7481
7482 #if __CRTL_VER >= 80200000
7483   /* If not a posix spec already, convert it */
7484   if (DECC_POSIX_COMPLIANT_PATHNAMES) {
7485     if (! strBEGINs(unixpath,"\"^UP^")) {
7486       sprintf(vmspath,"\"^UP^%s\"",unixpath);
7487     }
7488     else {
7489       /* This is already a VMS specification, no conversion */
7490       unixlen--;
7491       my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7492     }
7493   }
7494   else
7495 #endif
7496   {     
7497      int path_len;
7498      int i,j;
7499
7500      /* Check to see if this is under the POSIX root */
7501      if (DECC_DISABLE_POSIX_ROOT) {
7502         return RMS$_FNF;
7503      }
7504
7505      /* Skip leading / */
7506      if (unixpath[0] == '/') {
7507         unixpath++;
7508         unixlen--;
7509      }
7510
7511
7512      strcpy(vmspath,"SYS$POSIX_ROOT:");
7513
7514      /* If this is only the / , or blank, then... */
7515      if (unixpath[0] == '\0') {
7516         /* by definition, this is the answer */
7517         return SS$_NORMAL;
7518      }
7519
7520      /* Need to look up a directory */
7521      vmspath[15] = '[';
7522      vmspath[16] = '\0';
7523
7524      /* Copy and add '^' escape characters as needed */
7525      j = 16;
7526      i = 0;
7527      while (unixpath[i] != 0) {
7528      int k;
7529
7530         j += copy_expand_unix_filename_escape
7531             (&vmspath[j], &unixpath[i], &k, utf8_fl);
7532         i += k;
7533      }
7534
7535      path_len = strlen(vmspath);
7536      if (vmspath[path_len - 1] == '/')
7537         path_len--;
7538      vmspath[path_len] = ']';
7539      path_len++;
7540      vmspath[path_len] = '\0';
7541         
7542   }
7543   vmspath[vmspath_len] = 0;
7544   if (unixpath[unixlen - 1] == '/')
7545   dir_flag = 1;
7546   esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7547   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7548   esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7549   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7550   rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7551   if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7552   rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7553   if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7554   rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7555   rms_bind_fab_nam(myfab, mynam);
7556   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7557   rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7558   if (DECC_EFS_CASE_PRESERVE)
7559     mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7560 #ifdef NAML$M_OPEN_SPECIAL
7561   mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7562 #endif
7563
7564   /* Set up the remaining naml fields */
7565   sts = sys$parse(&myfab);
7566
7567   /* It failed! Try again as a UNIX filespec */
7568   if (!(sts & 1)) {
7569     PerlMem_free(esal);
7570     PerlMem_free(esa);
7571     PerlMem_free(rsal);
7572     PerlMem_free(rsa);
7573     return sts;
7574   }
7575
7576    /* get the Device ID and the FID */
7577    sts = sys$search(&myfab);
7578
7579    /* These are no longer needed */
7580    PerlMem_free(esa);
7581    PerlMem_free(rsal);
7582    PerlMem_free(rsa);
7583
7584    /* on any failure, returned the POSIX ^UP^ filespec */
7585    if (!(sts & 1)) {
7586       PerlMem_free(esal);
7587       return sts;
7588    }
7589    specdsc.dsc$a_pointer = vmspath;
7590    specdsc.dsc$w_length = vmspath_len;
7591  
7592    dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7593    dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7594    sts = lib$fid_to_name
7595       (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7596
7597   /* on any failure, returned the POSIX ^UP^ filespec */
7598   if (!(sts & 1)) {
7599      /* This can happen if user does not have permission to read directories */
7600      if (! strBEGINs(unixpath,"\"^UP^"))
7601        sprintf(vmspath,"\"^UP^%s\"",unixpath);
7602      else
7603        my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7604   }
7605   else {
7606     vmspath[specdsc.dsc$w_length] = 0;
7607
7608     /* Are we expecting a directory? */
7609     if (dir_flag != 0) {
7610     int i;
7611     char *eptr;
7612
7613       eptr = NULL;
7614
7615       i = specdsc.dsc$w_length - 1;
7616       while (i > 0) {
7617       int zercnt;
7618         zercnt = 0;
7619         /* Version must be '1' */
7620         if (vmspath[i--] != '1')
7621           break;
7622         /* Version delimiter is one of ".;" */
7623         if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7624           break;
7625         i--;
7626         if (vmspath[i--] != 'R')
7627           break;
7628         if (vmspath[i--] != 'I')
7629           break;
7630         if (vmspath[i--] != 'D')
7631           break;
7632         if (vmspath[i--] != '.')
7633           break;
7634         eptr = &vmspath[i+1];
7635         while (i > 0) {
7636           if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7637             if (vmspath[i-1] != '^') {
7638               if (zercnt != 6) {
7639                 *eptr = vmspath[i];
7640                 eptr[1] = '\0';
7641                 vmspath[i] = '.';
7642                 break;
7643               }
7644               else {
7645                 /* Get rid of 6 imaginary zero directory filename */
7646                 vmspath[i+1] = '\0';
7647               }
7648             }
7649           }
7650           if (vmspath[i] == '0')
7651             zercnt++;
7652           else
7653             zercnt = 10;
7654           i--;
7655         }
7656         break;
7657       }
7658     }
7659   }
7660   PerlMem_free(esal);
7661   return sts;
7662 }
7663
7664 /* /dev/mumble needs to be handled special.
7665    /dev/null becomes NLA0:, And there is the potential for other stuff
7666    like /dev/tty which may need to be mapped to something.
7667 */
7668
7669 static int 
7670 slash_dev_special_to_vms(const char *unixptr, char *vmspath, int vmspath_len)
7671 {
7672     char * nextslash;
7673     int len;
7674
7675     unixptr += 4;
7676     nextslash = strchr(unixptr, '/');
7677     len = strlen(unixptr);
7678     if (nextslash != NULL)
7679         len = nextslash - unixptr;
7680     if (strEQ(unixptr, "null")) {
7681         if (vmspath_len >= 6) {
7682             strcpy(vmspath, "_NLA0:");
7683             return SS$_NORMAL;
7684         }
7685     }
7686     return 0;
7687 }
7688
7689
7690 /* The built in routines do not understand perl's special needs, so
7691     doing a manual conversion from UNIX to VMS
7692
7693     If the utf8_fl is not null and points to a non-zero value, then
7694     treat 8 bit characters as UTF-8.
7695
7696     The sequence starting with '$(' and ending with ')' will be passed
7697     through with out interpretation instead of being escaped.
7698
7699   */
7700 static int
7701 posix_to_vmsspec_hardway(char *vmspath, int vmspath_len, const char *unixpath,
7702                          int dir_flag, int * utf8_fl)
7703 {
7704
7705   char *esa;
7706   const char *unixptr;
7707   const char *unixend;
7708   char *vmsptr;
7709   const char *lastslash;
7710   const char *lastdot;
7711   int unixlen;
7712   int vmslen;
7713   int dir_start;
7714   int dir_dot;
7715   int quoted;
7716   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7717   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7718
7719   if (utf8_fl != NULL)
7720     *utf8_fl = 0;
7721
7722   unixptr = unixpath;
7723   dir_dot = 0;
7724
7725   /* Ignore leading "/" characters */
7726   while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7727     unixptr++;
7728   }
7729   unixlen = strlen(unixptr);
7730
7731   /* Do nothing with blank paths */
7732   if (unixlen == 0) {
7733     vmspath[0] = '\0';
7734     return SS$_NORMAL;
7735   }
7736
7737   quoted = 0;
7738   /* This could have a "^UP^ on the front */
7739   if (strBEGINs(unixptr,"\"^UP^")) {
7740     quoted = 1;
7741     unixptr+= 5;
7742     unixlen-= 5;
7743   }
7744
7745   lastslash = strrchr(unixptr,'/');
7746   lastdot = strrchr(unixptr,'.');
7747   unixend = strrchr(unixptr,'\"');
7748   if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7749     unixend = unixptr + unixlen;
7750   }
7751
7752   /* last dot is last dot or past end of string */
7753   if (lastdot == NULL)
7754     lastdot = unixptr + unixlen;
7755
7756   /* if no directories, set last slash to beginning of string */
7757   if (lastslash == NULL) {
7758     lastslash = unixptr;
7759   }
7760   else {
7761     /* Watch out for trailing "." after last slash, still a directory */
7762     if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7763       lastslash = unixptr + unixlen;
7764     }
7765
7766     /* Watch out for trailing ".." after last slash, still a directory */
7767     if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7768       lastslash = unixptr + unixlen;
7769     }
7770
7771     /* dots in directories are aways escaped */
7772     if (lastdot < lastslash)
7773       lastdot = unixptr + unixlen;
7774   }
7775
7776   /* if (unixptr < lastslash) then we are in a directory */
7777
7778   dir_start = 0;
7779
7780   vmsptr = vmspath;
7781   vmslen = 0;
7782
7783   /* Start with the UNIX path */
7784   if (*unixptr != '/') {
7785     /* relative paths */
7786
7787     /* If allowing logical names on relative pathnames, then handle here */
7788     if ((unixptr[0] != '.') && !DECC_DISABLE_TO_VMS_LOGNAME_TRANSLATION &&
7789         !DECC_POSIX_COMPLIANT_PATHNAMES) {
7790     char * nextslash;
7791     int seg_len;
7792     char * trn;
7793     int islnm;
7794
7795         /* Find the next slash */
7796         nextslash = strchr(unixptr,'/');
7797
7798         esa = (char *)PerlMem_malloc(vmspath_len);
7799         if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7800
7801         trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7802         if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7803
7804         if (nextslash != NULL) {
7805
7806             seg_len = nextslash - unixptr;
7807             memcpy(esa, unixptr, seg_len);
7808             esa[seg_len] = 0;
7809         }
7810         else {
7811             seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7812         }
7813         /* trnlnm(section) */
7814         islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7815
7816         if (islnm) {
7817             /* Now fix up the directory */
7818
7819             /* Split up the path to find the components */
7820             sts = vms_split_path
7821                   (trn,
7822                    &v_spec,
7823                    &v_len,
7824                    &r_spec,
7825                    &r_len,
7826                    &d_spec,
7827                    &d_len,
7828                    &n_spec,
7829                    &n_len,
7830                    &e_spec,
7831                    &e_len,
7832                    &vs_spec,
7833                    &vs_len);
7834
7835             while (sts == 0) {
7836
7837                 /* A logical name must be a directory  or the full
7838                    specification.  It is only a full specification if
7839                    it is the only component */
7840                 if ((unixptr[seg_len] == '\0') ||
7841                     (unixptr[seg_len+1] == '\0')) {
7842
7843                     /* Is a directory being required? */
7844                     if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7845                         /* Not a logical name */
7846                         break;
7847                     }
7848
7849
7850                     if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7851                         /* This must be a directory */
7852                         if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7853                             vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7854                             vmsptr[vmslen] = ':';
7855                             vmslen++;
7856                             vmsptr[vmslen] = '\0';
7857                             return SS$_NORMAL;
7858                         }
7859                     }
7860
7861                 }
7862
7863
7864                 /* must be dev/directory - ignore version */
7865                 if ((n_len + e_len) != 0)
7866                     break;
7867
7868                 /* transfer the volume */
7869                 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7870                     memcpy(vmsptr, v_spec, v_len);
7871                     vmsptr += v_len;
7872                     vmsptr[0] = '\0';
7873                     vmslen += v_len;
7874                 }
7875
7876                 /* unroot the rooted directory */
7877                 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7878                     r_spec[0] = '[';
7879                     r_spec[r_len - 1] = ']';
7880
7881                     /* This should not be there, but nothing is perfect */
7882                     if (r_len > 9) {
7883                         if (strEQ(&r_spec[1], "000000.")) {
7884                             r_spec += 7;
7885                             r_spec[7] = '[';
7886                             r_len -= 7;
7887                             if (r_len == 2)
7888                                 r_len = 0;
7889                         }
7890                     }
7891                     if (r_len > 0) {
7892                         memcpy(vmsptr, r_spec, r_len);
7893                         vmsptr += r_len;
7894                         vmslen += r_len;
7895                         vmsptr[0] = '\0';
7896                     }
7897                 }
7898                 /* Bring over the directory. */
7899                 if ((d_len > 0) &&
7900                     ((d_len + vmslen) < vmspath_len)) {
7901                     d_spec[0] = '[';
7902                     d_spec[d_len - 1] = ']';
7903                     if (d_len > 9) {
7904                         if (strEQ(&d_spec[1], "000000.")) {
7905                             d_spec += 7;
7906                             d_spec[7] = '[';
7907                             d_len -= 7;
7908                             if (d_len == 2)
7909                                 d_len = 0;
7910                         }
7911                     }
7912
7913                     if (r_len > 0) {
7914                         /* Remove the redundant root */
7915                         if (r_len > 0) {
7916                             /* remove the ][ */
7917                             vmsptr--;
7918                             vmslen--;
7919                             d_spec++;
7920                             d_len--;
7921                         }
7922                         memcpy(vmsptr, d_spec, d_len);
7923                             vmsptr += d_len;
7924                             vmslen += d_len;
7925                             vmsptr[0] = '\0';
7926                     }
7927                 }
7928                 break;
7929             }
7930         }
7931
7932         PerlMem_free(esa);
7933         PerlMem_free(trn);
7934     }
7935
7936     if (lastslash > unixptr) {
7937     int dotdir_seen;
7938
7939       /* skip leading ./ */
7940       dotdir_seen = 0;
7941       while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7942         dotdir_seen = 1;
7943         unixptr++;
7944         unixptr++;
7945       }
7946
7947       /* Are we still in a directory? */
7948       if (unixptr <= lastslash) {
7949         *vmsptr++ = '[';
7950         vmslen = 1;
7951         dir_start = 1;
7952  
7953         /* if not backing up, then it is relative forward. */
7954         if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7955               ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7956           *vmsptr++ = '.';
7957           vmslen++;
7958           dir_dot = 1;
7959           }
7960        }
7961        else {
7962          if (dotdir_seen) {
7963            /* Perl wants an empty directory here to tell the difference
7964             * between a DCL command and a filename
7965             */
7966           *vmsptr++ = '[';
7967           *vmsptr++ = ']';
7968           vmslen = 2;
7969         }
7970       }
7971     }
7972     else {
7973       /* Handle two special files . and .. */
7974       if (unixptr[0] == '.') {
7975         if (&unixptr[1] == unixend) {
7976           *vmsptr++ = '[';
7977           *vmsptr++ = ']';
7978           vmslen += 2;
7979           *vmsptr++ = '\0';
7980           return SS$_NORMAL;
7981         }
7982         if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7983           *vmsptr++ = '[';
7984           *vmsptr++ = '-';
7985           *vmsptr++ = ']';
7986           vmslen += 3;
7987           *vmsptr++ = '\0';
7988           return SS$_NORMAL;
7989         }
7990       }
7991     }
7992   }
7993   else {        /* Absolute PATH handling */
7994   int sts;
7995   char * nextslash;
7996   int seg_len;
7997     /* Need to find out where root is */
7998
7999     /* In theory, this procedure should never get an absolute POSIX pathname
8000      * that can not be found on the POSIX root.
8001      * In practice, that can not be relied on, and things will show up
8002      * here that are a VMS device name or concealed logical name instead.
8003      * So to make things work, this procedure must be tolerant.
8004      */
8005     esa = (char *)PerlMem_malloc(vmspath_len);
8006     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8007
8008     sts = SS$_NORMAL;
8009     nextslash = strchr(&unixptr[1],'/');
8010     seg_len = 0;
8011     if (nextslash != NULL) {
8012       seg_len = nextslash - &unixptr[1];
8013       my_strlcpy(vmspath, unixptr, seg_len + 2);
8014       if (memEQs(vmspath, seg_len, "dev")) {
8015             sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8016             if (sts == SS$_NORMAL)
8017                 return SS$_NORMAL;
8018       }
8019       sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8020     }
8021
8022     if ($VMS_STATUS_SUCCESS(sts)) {
8023       /* This is verified to be a real path */
8024
8025       sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8026       if ($VMS_STATUS_SUCCESS(sts)) {
8027         vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
8028         vmsptr = vmspath + vmslen;
8029         unixptr++;
8030         if (unixptr < lastslash) {
8031         char * rptr;
8032           vmsptr--;
8033           *vmsptr++ = '.';
8034           dir_start = 1;
8035           dir_dot = 1;
8036           if (vmslen > 7) {
8037             rptr = vmsptr - 7;
8038             if (strEQ(rptr,"000000.")) {
8039               vmslen -= 7;
8040               vmsptr -= 7;
8041               vmsptr[1] = '\0';
8042             } /* removing 6 zeros */
8043           } /* vmslen < 7, no 6 zeros possible */
8044         } /* Not in a directory */
8045       } /* Posix root found */
8046       else {
8047         /* No posix root, fall back to default directory */
8048         strcpy(vmspath, "SYS$DISK:[");
8049         vmsptr = &vmspath[10];
8050         vmslen = 10;
8051         if (unixptr > lastslash) {
8052            *vmsptr = ']';
8053            vmsptr++;
8054            vmslen++;
8055         }
8056         else {
8057            dir_start = 1;
8058         }
8059       }
8060     } /* end of verified real path handling */
8061     else {
8062     int add_6zero;
8063     int islnm;
8064
8065       /* Ok, we have a device or a concealed root that is not in POSIX
8066        * or we have garbage.  Make the best of it.
8067        */
8068
8069       /* Posix to VMS destroyed this, so copy it again */
8070       my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
8071       vmslen = strlen(vmspath); /* We know we're truncating. */
8072       vmsptr = &vmsptr[vmslen];
8073       islnm = 0;
8074
8075       /* Now do we need to add the fake 6 zero directory to it? */
8076       add_6zero = 1;
8077       if ((*lastslash == '/') && (nextslash < lastslash)) {
8078         /* No there is another directory */
8079         add_6zero = 0;
8080       }
8081       else {
8082       int trnend;
8083
8084         /* now we have foo:bar or foo:[000000]bar to decide from */
8085         islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8086
8087         if (!islnm && !DECC_POSIX_COMPLIANT_PATHNAMES) {
8088             if (strEQ(vmspath, "bin")) {
8089                 /* bin => SYS$SYSTEM: */
8090                 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8091             }
8092             else {
8093                 /* tmp => SYS$SCRATCH: */
8094                 if (strEQ(vmspath, "tmp")) {
8095                     islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8096                 }
8097             }
8098         }
8099
8100         trnend = islnm ? islnm - 1 : 0;
8101
8102         /* if this was a logical name, ']' or '>' must be present */
8103         /* if not a logical name, then assume a device and hope. */
8104         islnm =  trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8105
8106         /* if log name and trailing '.' then rooted - treat as device */
8107         add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8108
8109         /* Fix me, if not a logical name, a device lookup should be
8110          * done to see if the device is file structured.  If the device
8111          * is not file structured, the 6 zeros should not be put on.
8112          *
8113          * As it is, perl is occasionally looking for dev:[000000]tty.
8114          * which looks a little strange.
8115          *
8116          * Not that easy to detect as "/dev" may be file structured with
8117          * special device files.
8118          */
8119
8120         if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8121             (&nextslash[1] == unixend)) {
8122           /* No real directory present */
8123           add_6zero = 1;
8124         }
8125       }
8126
8127       /* Put the device delimiter on */
8128       *vmsptr++ = ':';
8129       vmslen++;
8130       unixptr = nextslash;
8131       unixptr++;
8132
8133       /* Start directory if needed */
8134       if (!islnm || add_6zero) {
8135         *vmsptr++ = '[';
8136         vmslen++;
8137         dir_start = 1;
8138       }
8139
8140       /* add fake 000000] if needed */
8141       if (add_6zero) {
8142         *vmsptr++ = '0';
8143         *vmsptr++ = '0';
8144         *vmsptr++ = '0';
8145         *vmsptr++ = '0';
8146         *vmsptr++ = '0';
8147         *vmsptr++ = '0';
8148         *vmsptr++ = ']';
8149         vmslen += 7;
8150         dir_start = 0;
8151       }
8152
8153     } /* non-POSIX translation */
8154     PerlMem_free(esa);
8155   } /* End of relative/absolute path handling */
8156
8157   while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8158     int dash_flag;
8159     int in_cnt;
8160     int out_cnt;
8161
8162     dash_flag = 0;
8163
8164     if (dir_start != 0) {
8165
8166       /* First characters in a directory are handled special */
8167       while ((*unixptr == '/') ||
8168              ((*unixptr == '.') &&
8169               ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8170                 (&unixptr[1]==unixend)))) {
8171       int loop_flag;
8172
8173         loop_flag = 0;
8174
8175         /* Skip redundant / in specification */
8176         while ((*unixptr == '/') && (dir_start != 0)) {
8177           loop_flag = 1;
8178           unixptr++;
8179           if (unixptr == lastslash)
8180             break;
8181         }
8182         if (unixptr == lastslash)
8183           break;
8184
8185         /* Skip redundant ./ characters */
8186         while ((*unixptr == '.') &&
8187                ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8188           loop_flag = 1;
8189           unixptr++;
8190           if (unixptr == lastslash)
8191             break;
8192           if (*unixptr == '/')
8193             unixptr++;
8194         }
8195         if (unixptr == lastslash)
8196           break;
8197
8198         /* Skip redundant ../ characters */
8199         while ((*unixptr == '.') && (unixptr[1] == '.') &&
8200              ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8201           /* Set the backing up flag */
8202           loop_flag = 1;
8203           dir_dot = 0;
8204           dash_flag = 1;
8205           *vmsptr++ = '-';
8206           vmslen++;
8207           unixptr++; /* first . */
8208           unixptr++; /* second . */
8209           if (unixptr == lastslash)
8210             break;
8211           if (*unixptr == '/') /* The slash */
8212             unixptr++;
8213         }
8214         if (unixptr == lastslash)
8215           break;
8216
8217         /* To do: Perl expects /.../ to be translated to [...] on VMS */
8218         /* Not needed when VMS is pretending to be UNIX. */
8219
8220         /* Is this loop stuck because of too many dots? */
8221         if (loop_flag == 0) {
8222           /* Exit the loop and pass the rest through */
8223           break;
8224         }
8225       }
8226
8227       /* Are we done with directories yet? */
8228       if (unixptr >= lastslash) {
8229
8230         /* Watch out for trailing dots */
8231         if (dir_dot != 0) {
8232             vmslen --;
8233             vmsptr--;
8234         }
8235         *vmsptr++ = ']';
8236         vmslen++;
8237         dash_flag = 0;
8238         dir_start = 0;
8239         if (*unixptr == '/')
8240           unixptr++;
8241       }
8242       else {
8243         /* Have we stopped backing up? */
8244         if (dash_flag) {
8245           *vmsptr++ = '.';
8246           vmslen++;
8247           dash_flag = 0;
8248           /* dir_start continues to be = 1 */
8249         }
8250         if (*unixptr == '-') {
8251           *vmsptr++ = '^';
8252           *vmsptr++ = *unixptr++;
8253           vmslen += 2;
8254           dir_start = 0;
8255
8256           /* Now are we done with directories yet? */
8257           if (unixptr >= lastslash) {
8258
8259             /* Watch out for trailing dots */
8260             if (dir_dot != 0) {
8261               vmslen --;
8262               vmsptr--;
8263             }
8264
8265             *vmsptr++ = ']';
8266             vmslen++;
8267             dash_flag = 0;
8268             dir_start = 0;
8269           }
8270         }
8271       }
8272     }
8273
8274     /* All done? */
8275     if (unixptr >= unixend)
8276       break;
8277
8278     /* Normal characters - More EFS work probably needed */
8279     dir_start = 0;
8280     dir_dot = 0;
8281
8282     switch(*unixptr) {
8283     case '/':
8284         /* remove multiple / */
8285         while (unixptr[1] == '/') {
8286            unixptr++;
8287         }
8288         if (unixptr == lastslash) {
8289           /* Watch out for trailing dots */
8290           if (dir_dot != 0) {
8291             vmslen --;
8292             vmsptr--;
8293           }
8294           *vmsptr++ = ']';
8295         }
8296         else {
8297           dir_start = 1;
8298           *vmsptr++ = '.';
8299           dir_dot = 1;
8300
8301           /* To do: Perl expects /.../ to be translated to [...] on VMS */
8302           /* Not needed when VMS is pretending to be UNIX. */
8303
8304         }
8305         dash_flag = 0;
8306         if (unixptr != unixend)
8307           unixptr++;
8308         vmslen++;
8309         break;
8310     case '.':
8311         if ((unixptr < lastdot) || (unixptr < lastslash) ||
8312             (&unixptr[1] == unixend)) {
8313           *vmsptr++ = '^';
8314           *vmsptr++ = '.';
8315           vmslen += 2;
8316           unixptr++;
8317
8318           /* trailing dot ==> '^..' on VMS */
8319           if (unixptr == unixend) {
8320             *vmsptr++ = '.';
8321             vmslen++;
8322             unixptr++;
8323           }
8324           break;
8325         }
8326
8327         *vmsptr++ = *unixptr++;
8328         vmslen ++;
8329         break;
8330     case '"':
8331         if (quoted && (&unixptr[1] == unixend)) {
8332             unixptr++;
8333             break;
8334         }
8335         in_cnt = copy_expand_unix_filename_escape
8336                 (vmsptr, unixptr, &out_cnt, utf8_fl);
8337         vmsptr += out_cnt;
8338         unixptr += in_cnt;
8339         break;
8340     case ';':
8341     case '\\':
8342     case '?':
8343     case ' ':
8344     default:
8345         in_cnt = copy_expand_unix_filename_escape
8346                 (vmsptr, unixptr, &out_cnt, utf8_fl);
8347         vmsptr += out_cnt;
8348         unixptr += in_cnt;
8349         break;
8350     }
8351   }
8352
8353   /* Make sure directory is closed */
8354   if (unixptr == lastslash) {
8355     char *vmsptr2;
8356     vmsptr2 = vmsptr - 1;
8357
8358     if (*vmsptr2 != ']') {
8359       *vmsptr2--;
8360
8361       /* directories do not end in a dot bracket */
8362       if (*vmsptr2 == '.') {
8363         vmsptr2--;
8364
8365         /* ^. is allowed */
8366         if (*vmsptr2 != '^') {
8367           vmsptr--; /* back up over the dot */
8368         }
8369       }
8370       *vmsptr++ = ']';
8371     }
8372   }
8373   else {
8374     char *vmsptr2;
8375     /* Add a trailing dot if a file with no extension */
8376     vmsptr2 = vmsptr - 1;
8377     if ((vmslen > 1) &&
8378         (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8379         (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8380         *vmsptr++ = '.';
8381         vmslen++;
8382     }
8383   }
8384
8385   *vmsptr = '\0';
8386   return SS$_NORMAL;
8387 }
8388
8389 /* A convenience macro for copying dots in filenames and escaping
8390  * them when they haven't already been escaped, with guards to
8391  * avoid checking before the start of the buffer or advancing
8392  * beyond the end of it (allowing room for the NUL terminator).
8393  */
8394 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8395     if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8396           || ((vmsefsdot) == (vmsefsbuf))) \
8397          && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8398        ) { \
8399         *((vmsefsdot)++) = '^'; \
8400     } \
8401     if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8402         *((vmsefsdot)++) = '.'; \
8403 } STMT_END
8404
8405 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8406 static char *
8407 int_tovmsspec(const char *path, char *rslt, int dir_flag, int * utf8_flag)
8408 {
8409   char *dirend;
8410   char *lastdot;
8411   char *cp1;
8412   const char *cp2;
8413   unsigned long int infront = 0, hasdir = 1;
8414   int rslt_len;
8415   int no_type_seen;
8416   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8417   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8418
8419   if (vms_debug_fileify) {
8420       if (path == NULL)
8421           fprintf(stderr, "int_tovmsspec: path = NULL\n");
8422       else
8423           fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8424   }
8425
8426   if (path == NULL) {
8427       /* If we fail, we should be setting errno */
8428       set_errno(EINVAL);
8429       set_vaxc_errno(SS$_BADPARAM);
8430       return NULL;
8431   }
8432   rslt_len = VMS_MAXRSS-1;
8433
8434   /* '.' and '..' are "[]" and "[-]" for a quick check */
8435   if (path[0] == '.') {
8436     if (path[1] == '\0') {
8437       strcpy(rslt,"[]");
8438       if (utf8_flag != NULL)
8439         *utf8_flag = 0;
8440       return rslt;
8441     }
8442     else {
8443       if (path[1] == '.' && path[2] == '\0') {
8444         strcpy(rslt,"[-]");
8445         if (utf8_flag != NULL)
8446            *utf8_flag = 0;
8447         return rslt;
8448       }
8449     }
8450   }
8451
8452    /* Posix specifications are now a native VMS format */
8453   /*--------------------------------------------------*/
8454 #if __CRTL_VER >= 80200000
8455   if (DECC_POSIX_COMPLIANT_PATHNAMES) {
8456     if (strBEGINs(path,"\"^UP^")) {
8457       posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8458       return rslt;
8459     }
8460   }
8461 #endif
8462
8463   /* This is really the only way to see if this is already in VMS format */
8464   sts = vms_split_path
8465        (path,
8466         &v_spec,
8467         &v_len,
8468         &r_spec,
8469         &r_len,
8470         &d_spec,
8471         &d_len,
8472         &n_spec,
8473         &n_len,
8474         &e_spec,
8475         &e_len,
8476         &vs_spec,
8477         &vs_len);
8478   if (sts == 0) {
8479     /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8480        replacement, because the above parse just took care of most of
8481        what is needed to do vmspath when the specification is already
8482        in VMS format.
8483
8484        And if it is not already, it is easier to do the conversion as
8485        part of this routine than to call this routine and then work on
8486        the result.
8487      */
8488
8489     /* If VMS punctuation was found, it is already VMS format */
8490     if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8491       if (utf8_flag != NULL)
8492         *utf8_flag = 0;
8493       my_strlcpy(rslt, path, VMS_MAXRSS);
8494       if (vms_debug_fileify) {
8495           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8496       }
8497       return rslt;
8498     }
8499     /* Now, what to do with trailing "." cases where there is no
8500        extension?  If this is a UNIX specification, and EFS characters
8501        are enabled, then the trailing "." should be converted to a "^.".
8502        But if this was already a VMS specification, then it should be
8503        left alone.
8504
8505        So in the case of ambiguity, leave the specification alone.
8506      */
8507
8508
8509     /* If there is a possibility of UTF8, then if any UTF8 characters
8510         are present, then they must be converted to VTF-7
8511      */
8512     if (utf8_flag != NULL)
8513       *utf8_flag = 0;
8514     my_strlcpy(rslt, path, VMS_MAXRSS);
8515     if (vms_debug_fileify) {
8516         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8517     }
8518     return rslt;
8519   }
8520
8521   dirend = strrchr(path,'/');
8522
8523   if (dirend == NULL) {
8524      /* If we get here with no Unix directory delimiters, then this is an
8525       * ambiguous file specification, such as a Unix glob specification, a
8526       * shell or make macro, or a filespec that would be valid except for
8527       * unescaped extended characters.  The safest thing if it's a macro
8528       * is to pass it through as-is.
8529       */
8530       if (strstr(path, "$(")) {
8531           my_strlcpy(rslt, path, VMS_MAXRSS);
8532           if (vms_debug_fileify) {
8533               fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8534           }
8535           return rslt;
8536       }
8537       hasdir = 0;
8538   }
8539   else if (*(dirend+1) == '.') {  /* do we have trailing "/." or "/.." or "/..."? */
8540     if (!*(dirend+2)) dirend +=2;
8541     if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8542     if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8543   }
8544
8545   cp1 = rslt;
8546   cp2 = path;
8547   lastdot = strrchr(cp2,'.');
8548   if (*cp2 == '/') {
8549     char *trndev;
8550     int islnm, rooted;
8551     STRLEN trnend;
8552
8553     while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8554     if (!*(cp2+1)) {
8555       if (DECC_DISABLE_POSIX_ROOT) {
8556         strcpy(rslt,"sys$disk:[000000]");
8557       }
8558       else {
8559         strcpy(rslt,"sys$posix_root:[000000]");
8560       }
8561       if (utf8_flag != NULL)
8562         *utf8_flag = 0;
8563       if (vms_debug_fileify) {
8564           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8565       }
8566       return rslt;
8567     }
8568     while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8569     *cp1 = '\0';
8570     trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8571     if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8572     islnm =  simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8573
8574      /* DECC special handling */
8575     if (!islnm) {
8576       if (strEQ(rslt,"bin")) {
8577         strcpy(rslt,"sys$system");
8578         cp1 = rslt + 10;
8579         *cp1 = 0;
8580         islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8581       }
8582       else if (strEQ(rslt,"tmp")) {
8583         strcpy(rslt,"sys$scratch");
8584         cp1 = rslt + 11;
8585         *cp1 = 0;
8586         islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8587       }
8588       else if (!DECC_DISABLE_POSIX_ROOT) {
8589         strcpy(rslt, "sys$posix_root");
8590         cp1 = rslt + 14;
8591         *cp1 = 0;
8592         cp2 = path;
8593         while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8594         islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8595       }
8596       else if (strEQ(rslt,"dev")) {
8597         if (strBEGINs(cp2,"/null")) {
8598           if ((cp2[5] == 0) || (cp2[5] == '/')) {
8599             strcpy(rslt,"NLA0");
8600             cp1 = rslt + 4;
8601             *cp1 = 0;
8602             cp2 = cp2 + 5;
8603             islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8604           }
8605         }
8606       }
8607     }
8608
8609     trnend = islnm ? strlen(trndev) - 1 : 0;
8610     islnm =  trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8611     rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8612     /* If the first element of the path is a logical name, determine
8613      * whether it has to be translated so we can add more directories. */
8614     if (!islnm || rooted) {
8615       *(cp1++) = ':';
8616       *(cp1++) = '[';
8617       if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8618       else cp2++;
8619     }
8620     else {
8621       if (cp2 != dirend) {
8622         my_strlcpy(rslt, trndev, VMS_MAXRSS);
8623         cp1 = rslt + trnend;
8624         if (*cp2 != 0) {
8625           *(cp1++) = '.';
8626           cp2++;
8627         }
8628       }
8629       else {
8630         if (DECC_DISABLE_POSIX_ROOT) {
8631           *(cp1++) = ':';
8632           hasdir = 0;
8633         }
8634       }
8635     }
8636     PerlMem_free(trndev);
8637   }
8638   else if (hasdir) {
8639     *(cp1++) = '[';
8640     if (*cp2 == '.') {
8641       if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8642         cp2 += 2;         /* skip over "./" - it's redundant */
8643         *(cp1++) = '.';   /* but it does indicate a relative dirspec */
8644       }
8645       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8646         *(cp1++) = '-';                                 /* "../" --> "-" */
8647         cp2 += 3;
8648       }
8649       else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8650                (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8651         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8652         if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8653         cp2 += 4;
8654       }
8655       else if ((cp2 != lastdot) || (lastdot < dirend)) {
8656         /* Escape the extra dots in EFS file specifications */
8657         *(cp1++) = '^';
8658       }
8659       if (cp2 > dirend) cp2 = dirend;
8660     }
8661     else *(cp1++) = '.';
8662   }
8663   for (; cp2 < dirend; cp2++) {
8664     if (*cp2 == '/') {
8665       if (*(cp2-1) == '/') continue;
8666       if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8667       infront = 0;
8668     }
8669     else if (!infront && *cp2 == '.') {
8670       if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8671       else if (*(cp2+1) == '/') cp2++;   /* skip over "./" - it's redundant */
8672       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8673         if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8674         else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8675         else {
8676           *(cp1++) = '-';
8677         }
8678         cp2 += 2;
8679         if (cp2 == dirend) break;
8680       }
8681       else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8682                 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8683         if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8684         *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8685         if (!*(cp2+3)) { 
8686           *(cp1++) = '.';  /* Simulate trailing '/' */
8687           cp2 += 2;  /* for loop will incr this to == dirend */
8688         }
8689         else cp2 += 3;  /* Trailing '/' was there, so skip it, too */
8690       }
8691       else {
8692         if (DECC_EFS_CHARSET == 0) {
8693           if (cp1 > rslt && *(cp1-1) == '^')
8694             cp1--;         /* remove the escape, if any */
8695           *(cp1++) = '_';  /* fix up syntax - '.' in name not allowed */
8696         }
8697         else {
8698           VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8699         }
8700       }
8701     }
8702     else {
8703       if (!infront && cp1 > rslt && *(cp1-1) == '-')  *(cp1++) = '.';
8704       if (*cp2 == '.') {
8705         if (DECC_EFS_CHARSET == 0) {
8706           if (cp1 > rslt && *(cp1-1) == '^')
8707             cp1--;         /* remove the escape, if any */
8708           *(cp1++) = '_';
8709         }
8710         else {
8711           VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8712         }
8713       }
8714       else {
8715         int out_cnt;
8716         cp2 += copy_expand_unix_filename_escape(cp1, cp2, &out_cnt, utf8_flag);
8717         cp2--; /* we're in a loop that will increment this */
8718         cp1 += out_cnt;
8719       }
8720       infront = 1;
8721     }
8722   }
8723   if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8724   if (hasdir) *(cp1++) = ']';
8725   if (*cp2 && *cp2 == '/') cp2++;  /* check in case we ended with trailing '/' */
8726   no_type_seen = 0;
8727   if (cp2 > lastdot)
8728     no_type_seen = 1;
8729   while (*cp2) {
8730     switch(*cp2) {
8731     case '?':
8732         if (DECC_EFS_CHARSET == 0)
8733           *(cp1++) = '%';
8734         else
8735           *(cp1++) = '?';
8736         cp2++;
8737         break;
8738     case ' ':
8739         if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8740             *(cp1)++ = '^';
8741         *(cp1)++ = '_';
8742         cp2++;
8743         break;
8744     case '.':
8745         if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8746             DECC_READDIR_DROPDOTNOTYPE) {
8747           VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8748           cp2++;
8749
8750           /* trailing dot ==> '^..' on VMS */
8751           if (*cp2 == '\0') {
8752             *(cp1++) = '.';
8753             no_type_seen = 0;
8754           }
8755         }
8756         else {
8757           *(cp1++) = *(cp2++);
8758           no_type_seen = 0;
8759         }
8760         break;
8761     case '$':
8762          /* This could be a macro to be passed through */
8763         *(cp1++) = *(cp2++);
8764         if (*cp2 == '(') {
8765         const char * save_cp2;
8766         char * save_cp1;
8767         int is_macro;
8768
8769             /* paranoid check */
8770             save_cp2 = cp2;
8771             save_cp1 = cp1;
8772             is_macro = 0;
8773
8774             /* Test through */
8775             *(cp1++) = *(cp2++);
8776             if (isALPHA_L1(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8777                 *(cp1++) = *(cp2++);
8778                 while (isALPHA_L1(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8779                     *(cp1++) = *(cp2++);
8780                 }
8781                 if (*cp2 == ')') {
8782                     *(cp1++) = *(cp2++);
8783                     is_macro = 1;
8784                 }
8785             }
8786             if (is_macro == 0) {
8787                 /* Not really a macro - never mind */
8788                 cp2 = save_cp2;
8789                 cp1 = save_cp1;
8790             }
8791         }
8792         break;
8793     case '\"':
8794     case '`':
8795     case '!':
8796     case '#':
8797     case '%':
8798     case '^':
8799         /* Don't escape again if following character is 
8800          * already something we escape.
8801          */
8802         if (strchr("\"`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8803             *(cp1++) = *(cp2++);
8804             break;
8805         }
8806         /* But otherwise fall through and escape it. */
8807     case '&':
8808     case '(':
8809     case ')':
8810     case '=':
8811     case '+':
8812     case '\'':
8813     case '@':
8814     case '[':
8815     case ']':
8816     case '{':
8817     case '}':
8818     case ':':
8819     case '\\':
8820     case '|':
8821     case '<':
8822     case '>':
8823         if (cp2 >= path && *(cp2-1) != '^') /* not previously escaped */
8824             *(cp1++) = '^';
8825         *(cp1++) = *(cp2++);
8826         break;
8827     case ';':
8828         /* If it doesn't look like the beginning of a version number,
8829          * or we've been promised there are no version numbers, then
8830          * escape it.
8831          */
8832         if (DECC_FILENAME_UNIX_NO_VERSION) {
8833           *(cp1++) = '^';
8834         }
8835         else {
8836           size_t all_nums = strspn(cp2+1, "0123456789");
8837           if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8838             *(cp1++) = '^';
8839         }
8840         *(cp1++) = *(cp2++);
8841         break;
8842     default:
8843         *(cp1++) = *(cp2++);
8844     }
8845   }
8846   if ((no_type_seen == 1) && DECC_READDIR_DROPDOTNOTYPE) {
8847   char *lcp1;
8848     lcp1 = cp1;
8849     lcp1--;
8850      /* Fix me for "^]", but that requires making sure that you do
8851       * not back up past the start of the filename
8852       */
8853     if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8854       *cp1++ = '.';
8855   }
8856   *cp1 = '\0';
8857
8858   if (utf8_flag != NULL)
8859     *utf8_flag = 0;
8860   if (vms_debug_fileify) {
8861       fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8862   }
8863   return rslt;
8864
8865 }  /* end of int_tovmsspec() */
8866
8867
8868 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8869 static char *
8870 mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag)
8871 {
8872     static char __tovmsspec_retbuf[VMS_MAXRSS];
8873     char * vmsspec, *ret_spec, *ret_buf;
8874
8875     vmsspec = NULL;
8876     ret_buf = buf;
8877     if (ret_buf == NULL) {
8878         if (ts) {
8879             Newx(vmsspec, VMS_MAXRSS, char);
8880             if (vmsspec == NULL)
8881                 _ckvmssts(SS$_INSFMEM);
8882             ret_buf = vmsspec;
8883         } else {
8884             ret_buf = __tovmsspec_retbuf;
8885         }
8886     }
8887
8888     ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8889
8890     if (ret_spec == NULL) {
8891        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8892        if (vmsspec)
8893            Safefree(vmsspec);
8894     }
8895
8896     return ret_spec;
8897
8898 }  /* end of mp_do_tovmsspec() */
8899 /*}}}*/
8900 /* External entry points */
8901 char *
8902 Perl_tovmsspec(pTHX_ const char *path, char *buf)
8903 {
8904     return do_tovmsspec(path, buf, 0, NULL);
8905 }
8906
8907 char *
8908 Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8909 {
8910     return do_tovmsspec(path, buf, 1, NULL);
8911 }
8912
8913 char *
8914 Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8915 {
8916     return do_tovmsspec(path, buf, 0, utf8_fl);
8917 }
8918
8919 char *
8920 Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8921 {
8922     return do_tovmsspec(path, buf, 1, utf8_fl);
8923 }
8924
8925 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8926 /* Internal routine for use with out an explicit context present */
8927 static char *
8928 int_tovmspath(const char *path, char *buf, int * utf8_fl)
8929 {
8930     char * ret_spec, *pathified;
8931
8932     if (path == NULL)
8933         return NULL;
8934
8935     pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8936     if (pathified == NULL)
8937         _ckvmssts_noperl(SS$_INSFMEM);
8938
8939     ret_spec = int_pathify_dirspec(path, pathified);
8940
8941     if (ret_spec == NULL) {
8942         PerlMem_free(pathified);
8943         return NULL;
8944     }
8945
8946     ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8947     
8948     PerlMem_free(pathified);
8949     return ret_spec;
8950
8951 }
8952
8953 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8954 static char *
8955 mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
8956 {
8957   static char __tovmspath_retbuf[VMS_MAXRSS];
8958   int vmslen;
8959   char *pathified, *vmsified, *cp;
8960
8961   if (path == NULL) return NULL;
8962   pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8963   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8964   if (int_pathify_dirspec(path, pathified) == NULL) {
8965     PerlMem_free(pathified);
8966     return NULL;
8967   }
8968
8969   vmsified = NULL;
8970   if (buf == NULL)
8971      Newx(vmsified, VMS_MAXRSS, char);
8972   if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8973     PerlMem_free(pathified);
8974     if (vmsified) Safefree(vmsified);
8975     return NULL;
8976   }
8977   PerlMem_free(pathified);
8978   if (buf) {
8979     return buf;
8980   }
8981   else if (ts) {
8982     vmslen = strlen(vmsified);
8983     Newx(cp,vmslen+1,char);
8984     memcpy(cp,vmsified,vmslen);
8985     cp[vmslen] = '\0';
8986     Safefree(vmsified);
8987     return cp;
8988   }
8989   else {
8990     my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8991     Safefree(vmsified);
8992     return __tovmspath_retbuf;
8993   }
8994
8995 }  /* end of do_tovmspath() */
8996 /*}}}*/
8997 /* External entry points */
8998 char *
8999 Perl_tovmspath(pTHX_ const char *path, char *buf)
9000 {
9001     return do_tovmspath(path, buf, 0, NULL);
9002 }
9003
9004 char *
9005 Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9006 {
9007     return do_tovmspath(path, buf, 1, NULL);
9008 }
9009
9010 char *
9011 Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9012 {
9013     return do_tovmspath(path, buf, 0, utf8_fl);
9014 }
9015
9016 char *
9017 Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9018 {
9019     return do_tovmspath(path, buf, 1, utf8_fl);
9020 }
9021
9022
9023 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9024 static char *
9025 mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
9026 {
9027   static char __tounixpath_retbuf[VMS_MAXRSS];
9028   int unixlen;
9029   char *pathified, *unixified, *cp;
9030
9031   if (path == NULL) return NULL;
9032   pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
9033   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9034   if (int_pathify_dirspec(path, pathified) == NULL) {
9035     PerlMem_free(pathified);
9036     return NULL;
9037   }
9038
9039   unixified = NULL;
9040   if (buf == NULL) {
9041       Newx(unixified, VMS_MAXRSS, char);
9042   }
9043   if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9044     PerlMem_free(pathified);
9045     if (unixified) Safefree(unixified);
9046     return NULL;
9047   }
9048   PerlMem_free(pathified);
9049   if (buf) {
9050     return buf;
9051   }
9052   else if (ts) {
9053     unixlen = strlen(unixified);
9054     Newx(cp,unixlen+1,char);
9055     memcpy(cp,unixified,unixlen);
9056     cp[unixlen] = '\0';
9057     Safefree(unixified);
9058     return cp;
9059   }
9060   else {
9061     my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
9062     Safefree(unixified);
9063     return __tounixpath_retbuf;
9064   }
9065
9066 }  /* end of do_tounixpath() */
9067 /*}}}*/
9068 /* External entry points */
9069 char *
9070 Perl_tounixpath(pTHX_ const char *path, char *buf)
9071 {
9072     return do_tounixpath(path, buf, 0, NULL);
9073 }
9074
9075 char *
9076 Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9077 {
9078     return do_tounixpath(path, buf, 1, NULL);
9079 }
9080
9081 char *
9082 Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9083 {
9084     return do_tounixpath(path, buf, 0, utf8_fl);
9085 }
9086
9087 char *
9088 Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9089 {
9090     return do_tounixpath(path, buf, 1, utf8_fl);
9091 }
9092
9093 /*
9094  * @(#)argproc.c 2.2 94/08/16   Mark Pizzolato (mark AT infocomm DOT com)
9095  *
9096  *****************************************************************************
9097  *                                                                           *
9098  *  Copyright (C) 1989-1994, 2007 by                                         *
9099  *  Mark Pizzolato - INFO COMM, Danville, California  (510) 837-5600         *
9100  *                                                                           *
9101  *  Permission is hereby granted for the reproduction of this software       *
9102  *  on condition that this copyright notice is included in source            *
9103  *  distributions of the software.  The code may be modified and             *
9104  *  distributed under the same terms as Perl itself.                         *
9105  *                                                                           *
9106  *  27-Aug-1994 Modified for inclusion in perl5                              *
9107  *              by Charles Bailey  (bailey AT newman DOT upenn DOT edu)      *
9108  *****************************************************************************
9109  */
9110
9111 /*
9112  * getredirection() is intended to aid in porting C programs
9113  * to VMS (Vax-11 C).  The native VMS environment does not support 
9114  * '>' and '<' I/O redirection, or command line wild card expansion, 
9115  * or a command line pipe mechanism using the '|' AND background 
9116  * command execution '&'.  All of these capabilities are provided to any
9117  * C program which calls this procedure as the first thing in the 
9118  * main program.
9119  * The piping mechanism will probably work with almost any 'filter' type
9120  * of program.  With suitable modification, it may useful for other
9121  * portability problems as well.
9122  *
9123  * Author:  Mark Pizzolato      (mark AT infocomm DOT com)
9124  */
9125 struct list_item
9126     {
9127     struct list_item *next;
9128     char *value;
9129     };
9130
9131 static void add_item(struct list_item **head,
9132                      struct list_item **tail,
9133                      char *value,
9134                      int *count);
9135
9136 static void mp_expand_wild_cards(pTHX_ char *item,
9137                                 struct list_item **head,
9138                                 struct list_item **tail,
9139                                 int *count);
9140
9141 static int background_process(pTHX_ int argc, char **argv);
9142
9143 static void pipe_and_fork(pTHX_ char **cmargv);
9144
9145 /*{{{ void getredirection(int *ac, char ***av)*/
9146 static void
9147 mp_getredirection(pTHX_ int *ac, char ***av)
9148 /*
9149  * Process vms redirection arg's.  Exit if any error is seen.
9150  * If getredirection() processes an argument, it is erased
9151  * from the vector.  getredirection() returns a new argc and argv value.
9152  * In the event that a background command is requested (by a trailing "&"),
9153  * this routine creates a background subprocess, and simply exits the program.
9154  *
9155  * Warning: do not try to simplify the code for vms.  The code
9156  * presupposes that getredirection() is called before any data is
9157  * read from stdin or written to stdout.
9158  *
9159  * Normal usage is as follows:
9160  *
9161  *      main(argc, argv)
9162  *      int             argc;
9163  *      char            *argv[];
9164  *      {
9165  *              getredirection(&argc, &argv);
9166  *      }
9167  */
9168 {
9169     int                 argc = *ac;     /* Argument Count         */
9170     char                **argv = *av;   /* Argument Vector        */
9171     char                *ap;            /* Argument pointer       */
9172     int                 j;              /* argv[] index           */
9173     int                 item_count = 0; /* Count of Items in List */
9174     struct list_item    *list_head = 0; /* First Item in List       */
9175     struct list_item    *list_tail;     /* Last Item in List        */
9176     char                *in = NULL;     /* Input File Name          */
9177     char                *out = NULL;    /* Output File Name         */
9178     char                *outmode = "w"; /* Mode to Open Output File */
9179     char                *err = NULL;    /* Error File Name          */
9180     char                *errmode = "w"; /* Mode to Open Error File  */
9181     int                 cmargc = 0;     /* Piped Command Arg Count  */
9182     char                **cmargv = NULL;/* Piped Command Arg Vector */
9183
9184     /*
9185      * First handle the case where the last thing on the line ends with
9186      * a '&'.  This indicates the desire for the command to be run in a
9187      * subprocess, so we satisfy that desire.
9188      */
9189     ap = argv[argc-1];
9190     if (strEQ(ap, "&"))
9191        exit(background_process(aTHX_ --argc, argv));
9192     if (*ap && '&' == ap[strlen(ap)-1])
9193         {
9194         ap[strlen(ap)-1] = '\0';
9195        exit(background_process(aTHX_ argc, argv));
9196         }
9197     /*
9198      * Now we handle the general redirection cases that involve '>', '>>',
9199      * '<', and pipes '|'.
9200      */
9201     for (j = 0; j < argc; ++j)
9202         {
9203         if (strEQ(argv[j], "<"))
9204             {
9205             if (j+1 >= argc)
9206                 {
9207                 fprintf(stderr,"No input file after < on command line");
9208                 exit(LIB$_WRONUMARG);
9209                 }
9210             in = argv[++j];
9211             continue;
9212             }
9213         if ('<' == *(ap = argv[j]))
9214             {
9215             in = 1 + ap;
9216             continue;
9217             }
9218         if (strEQ(ap, ">"))
9219             {
9220             if (j+1 >= argc)
9221                 {
9222                 fprintf(stderr,"No output file after > on command line");
9223                 exit(LIB$_WRONUMARG);
9224                 }
9225             out = argv[++j];
9226             continue;
9227             }
9228         if ('>' == *ap)
9229             {
9230             if ('>' == ap[1])
9231                 {
9232                 outmode = "a";
9233                 if ('\0' == ap[2])
9234                     out = argv[++j];
9235                 else
9236                     out = 2 + ap;
9237                 }
9238             else
9239                 out = 1 + ap;
9240             if (j >= argc)
9241                 {
9242                 fprintf(stderr,"No output file after > or >> on command line");
9243                 exit(LIB$_WRONUMARG);
9244                 }
9245             continue;
9246             }
9247         if (('2' == *ap) && ('>' == ap[1]))
9248             {
9249             if ('>' == ap[2])
9250                 {
9251                 errmode = "a";
9252                 if ('\0' == ap[3])
9253                     err = argv[++j];
9254                 else
9255                     err = 3 + ap;
9256                 }
9257             else
9258                 if ('\0' == ap[2])
9259                     err = argv[++j];
9260                 else
9261                     err = 2 + ap;
9262             if (j >= argc)
9263                 {
9264                 fprintf(stderr,"No output file after 2> or 2>> on command line");
9265                 exit(LIB$_WRONUMARG);
9266                 }
9267             continue;
9268             }
9269         if (strEQ(argv[j], "|"))
9270             {
9271             if (j+1 >= argc)
9272                 {
9273                 fprintf(stderr,"No command into which to pipe on command line");
9274                 exit(LIB$_WRONUMARG);
9275                 }
9276             cmargc = argc-(j+1);
9277             cmargv = &argv[j+1];
9278             argc = j;
9279             continue;
9280             }
9281         if ('|' == *(ap = argv[j]))
9282             {
9283             ++argv[j];
9284             cmargc = argc-j;
9285             cmargv = &argv[j];
9286             argc = j;
9287             continue;
9288             }
9289         expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9290         }
9291     /*
9292      * Allocate and fill in the new argument vector, Some Unix's terminate
9293      * the list with an extra null pointer.
9294      */
9295     argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9296     if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9297     *av = argv;
9298     for (j = 0; j < item_count; ++j, list_head = list_head->next)
9299         argv[j] = list_head->value;
9300     *ac = item_count;
9301     if (cmargv != NULL)
9302         {
9303         if (out != NULL)
9304             {
9305             fprintf(stderr,"'|' and '>' may not both be specified on command line");
9306             exit(LIB$_INVARGORD);
9307             }
9308         pipe_and_fork(aTHX_ cmargv);
9309         }
9310         
9311     /* Check for input from a pipe (mailbox) */
9312
9313     if (in == NULL && 1 == isapipe(0))
9314         {
9315         char mbxname[L_tmpnam];
9316         long int bufsize;
9317         long int dvi_item = DVI$_DEVBUFSIZ;
9318         $DESCRIPTOR(mbxnam, "");
9319         $DESCRIPTOR(mbxdevnam, "");
9320
9321         /* Input from a pipe, reopen it in binary mode to disable       */
9322         /* carriage control processing.                                 */
9323
9324         fgetname(stdin, mbxname, 1);
9325         mbxnam.dsc$a_pointer = mbxname;
9326         mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);     
9327         lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9328         mbxdevnam.dsc$a_pointer = mbxname;
9329         mbxdevnam.dsc$w_length = sizeof(mbxname);
9330         dvi_item = DVI$_DEVNAM;
9331         lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9332         mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9333         set_errno(0);
9334         set_vaxc_errno(1);
9335         freopen(mbxname, "rb", stdin);
9336         if (errno != 0)
9337             {
9338             fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9339             exit(vaxc$errno);
9340             }
9341         }
9342     if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9343         {
9344         fprintf(stderr,"Can't open input file %s as stdin",in);
9345         exit(vaxc$errno);
9346         }
9347     if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9348         {       
9349         fprintf(stderr,"Can't open output file %s as stdout",out);
9350         exit(vaxc$errno);
9351         }
9352         if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9353
9354     if (err != NULL) {
9355         if (strEQ(err, "&1")) {
9356             dup2(fileno(stdout), fileno(stderr));
9357             vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9358         } else {
9359         FILE *tmperr;
9360         if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9361             {
9362             fprintf(stderr,"Can't open error file %s as stderr",err);
9363             exit(vaxc$errno);
9364             }
9365             fclose(tmperr);
9366            if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9367                 {
9368                 exit(vaxc$errno);
9369                 }
9370             vmssetuserlnm("SYS$ERROR", err);
9371         }
9372         }
9373 #ifdef ARGPROC_DEBUG
9374     PerlIO_printf(Perl_debug_log, "Arglist:\n");
9375     for (j = 0; j < *ac;  ++j)
9376         PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9377 #endif
9378    /* Clear errors we may have hit expanding wildcards, so they don't
9379       show up in Perl's $! later */
9380    set_errno(0); set_vaxc_errno(1);
9381 }  /* end of getredirection() */
9382 /*}}}*/
9383
9384 static void
9385 add_item(struct list_item **head, struct list_item **tail, char *value, int *count)
9386 {
9387     if (*head == 0)
9388         {
9389         *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9390         if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9391         *tail = *head;
9392         }
9393     else {
9394         (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9395         if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9396         *tail = (*tail)->next;
9397         }
9398     (*tail)->value = value;
9399     ++(*count);
9400 }
9401
9402 static void 
9403 mp_expand_wild_cards(pTHX_ char *item, struct list_item **head,
9404                      struct list_item **tail, int *count)
9405 {
9406     int expcount = 0;
9407     unsigned long int context = 0;
9408     int isunix = 0;
9409     int item_len = 0;
9410     char *had_version;
9411     char *had_device;
9412     int had_directory;
9413     char *devdir,*cp;
9414     char *vmsspec;
9415     $DESCRIPTOR(filespec, "");
9416     $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9417     $DESCRIPTOR(resultspec, "");
9418     unsigned long int lff_flags = 0;
9419     int sts;
9420     int rms_sts;
9421
9422 #ifdef VMS_LONGNAME_SUPPORT
9423     lff_flags = LIB$M_FIL_LONG_NAMES;
9424 #endif
9425
9426     for (cp = item; *cp; cp++) {
9427         if (*cp == '*' || *cp == '%' || isSPACE_L1(*cp)) break;
9428         if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9429     }
9430     if (!*cp || isSPACE_L1(*cp))
9431         {
9432         add_item(head, tail, item, count);
9433         return;
9434         }
9435     else
9436         {
9437      /* "double quoted" wild card expressions pass as is */
9438      /* From DCL that means using e.g.:                  */
9439      /* perl program """perl.*"""                        */
9440      item_len = strlen(item);
9441      if ( '"' == *item && '"' == item[item_len-1] )
9442        {
9443        item++;
9444        item[item_len-2] = '\0';
9445        add_item(head, tail, item, count);
9446        return;
9447        }
9448      }
9449     resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9450     resultspec.dsc$b_class = DSC$K_CLASS_D;
9451     resultspec.dsc$a_pointer = NULL;
9452     vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9453     if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9454     if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9455       filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9456     if (!isunix || !filespec.dsc$a_pointer)
9457       filespec.dsc$a_pointer = item;
9458     filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9459     /*
9460      * Only return version specs, if the caller specified a version
9461      */
9462     had_version = strchr(item, ';');
9463     /*
9464      * Only return device and directory specs, if the caller specified either.
9465      */
9466     had_device = strchr(item, ':');
9467     had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9468     
9469     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9470                                  (&filespec, &resultspec, &context,
9471                                   &defaultspec, 0, &rms_sts, &lff_flags)))
9472         {
9473         char *string;
9474         char *c;
9475
9476         string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9477         if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9478         my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9479         if (NULL == had_version)
9480             *(strrchr(string, ';')) = '\0';
9481         if ((!had_directory) && (had_device == NULL))
9482             {
9483             if (NULL == (devdir = strrchr(string, ']')))
9484                 devdir = strrchr(string, '>');
9485             my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9486             }
9487         /*
9488          * Be consistent with what the C RTL has already done to the rest of
9489          * the argv items and lowercase all of these names.
9490          */
9491         if (!DECC_EFS_CASE_PRESERVE) {
9492             for (c = string; *c; ++c)
9493             if (isupper(*c))
9494                 *c = toLOWER_L1(*c);
9495         }
9496         if (isunix) trim_unixpath(string,item,1);
9497         add_item(head, tail, string, count);
9498         ++expcount;
9499     }
9500     PerlMem_free(vmsspec);
9501     if (sts != RMS$_NMF)
9502         {
9503         set_vaxc_errno(sts);
9504         switch (sts)
9505             {
9506             case RMS$_FNF: case RMS$_DNF:
9507                 set_errno(ENOENT); break;
9508             case RMS$_DIR:
9509                 set_errno(ENOTDIR); break;
9510             case RMS$_DEV:
9511                 set_errno(ENODEV); break;
9512             case RMS$_FNM: case RMS$_SYN:
9513                 set_errno(EINVAL); break;
9514             case RMS$_PRV:
9515                 set_errno(EACCES); break;
9516             default:
9517                 _ckvmssts_noperl(sts);
9518             }
9519         }
9520     if (expcount == 0)
9521         add_item(head, tail, item, count);
9522     _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9523     _ckvmssts_noperl(lib$find_file_end(&context));
9524 }
9525
9526
9527 static void 
9528 pipe_and_fork(pTHX_ char **cmargv)
9529 {
9530     PerlIO *fp;
9531     struct dsc$descriptor_s *vmscmd;
9532     char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9533     int sts, j, l, ismcr, quote, tquote = 0;
9534
9535     sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9536     vms_execfree(vmscmd);
9537
9538     j = l = 0;
9539     p = subcmd;
9540     q = cmargv[0];
9541     ismcr = q && toUPPER_A(*q) == 'M'     && toUPPER_A(*(q+1)) == 'C' 
9542               && toUPPER_A(*(q+2)) == 'R' && !*(q+3);
9543
9544     while (q && l < MAX_DCL_LINE_LENGTH) {
9545         if (!*q) {
9546             if (j > 0 && quote) {
9547                 *p++ = '"';
9548                 l++;
9549             }
9550             q = cmargv[++j];
9551             if (q) {
9552                 if (ismcr && j > 1) quote = 1;
9553                 tquote =  (strchr(q,' ')) != NULL || *q == '\0';
9554                 *p++ = ' ';
9555                 l++;
9556                 if (quote || tquote) {
9557                     *p++ = '"';
9558                     l++;
9559                 }
9560             }
9561         } else {
9562             if ((quote||tquote) && *q == '"') {
9563                 *p++ = '"';
9564                 l++;
9565             }
9566             *p++ = *q++;
9567             l++;
9568         }
9569     }
9570     *p = '\0';
9571
9572     fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9573     if (fp == NULL) {
9574         PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9575     }
9576 }
9577
9578 static int
9579 background_process(pTHX_ int argc, char **argv)
9580 {
9581     char command[MAX_DCL_SYMBOL + 1] = "$";
9582     $DESCRIPTOR(value, "");
9583     static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9584     static $DESCRIPTOR(null, "NLA0:");
9585     static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9586     char pidstring[80];
9587     $DESCRIPTOR(pidstr, "");
9588     int pid;
9589     unsigned long int flags = 17, one = 1, retsts;
9590     int len;
9591
9592     len = my_strlcat(command, argv[0], sizeof(command));
9593     while (--argc && (len < MAX_DCL_SYMBOL))
9594         {
9595         my_strlcat(command, " \"", sizeof(command));
9596         my_strlcat(command, *(++argv), sizeof(command));
9597         len = my_strlcat(command, "\"", sizeof(command));
9598         }
9599     value.dsc$a_pointer = command;
9600     value.dsc$w_length = strlen(value.dsc$a_pointer);
9601     _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9602     retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9603     if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9604         _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9605     }
9606     else {
9607         _ckvmssts_noperl(retsts);
9608     }
9609 #ifdef ARGPROC_DEBUG
9610     PerlIO_printf(Perl_debug_log, "%s\n", command);
9611 #endif
9612     sprintf(pidstring, "%08X", pid);
9613     PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9614     pidstr.dsc$a_pointer = pidstring;
9615     pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9616     lib$set_symbol(&pidsymbol, &pidstr);
9617     return(SS$_NORMAL);
9618 }
9619 /*}}}*/
9620 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9621
9622
9623 /* OS-specific initialization at image activation (not thread startup) */
9624 /* Older VAXC header files lack these constants */
9625 #ifndef JPI$_RIGHTS_SIZE
9626 #  define JPI$_RIGHTS_SIZE 817
9627 #endif
9628 #ifndef KGB$M_SUBSYSTEM
9629 #  define KGB$M_SUBSYSTEM 0x8
9630 #endif
9631  
9632 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9633
9634 /*{{{void vms_image_init(int *, char ***)*/
9635 void
9636 vms_image_init(int *argcp, char ***argvp)
9637 {
9638   int status;
9639   char eqv[LNM$C_NAMLENGTH+1] = "";
9640   unsigned int len, tabct = 8, tabidx = 0;
9641   unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9642   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9643   unsigned short int dummy, rlen;
9644   struct dsc$descriptor_s **tabvec;
9645 #if defined(PERL_IMPLICIT_CONTEXT)
9646   pTHX = NULL;
9647 #endif
9648   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
9649                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
9650                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9651                                  {          0,                0,    0,      0} };
9652
9653 #ifdef KILL_BY_SIGPRC
9654     Perl_csighandler_init();
9655 #endif
9656
9657   _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9658   _ckvmssts_noperl(iosb[0]);
9659   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9660     if (iprv[i]) {           /* Running image installed with privs? */
9661       _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
9662       will_taint = TRUE;
9663       break;
9664     }
9665   }
9666   /* Rights identifiers might trigger tainting as well. */
9667   if (!will_taint && (rlen || rsz)) {
9668     while (rlen < rsz) {
9669       /* We didn't get all the identifiers on the first pass.  Allocate a
9670        * buffer much larger than $GETJPI wants (rsz is size in bytes that
9671        * were needed to hold all identifiers at time of last call; we'll
9672        * allocate that many unsigned long ints), and go back and get 'em.
9673        * If it gave us less than it wanted to despite ample buffer space, 
9674        * something's broken.  Is your system missing a system identifier?
9675        */
9676       if (rsz <= jpilist[1].buflen) { 
9677          /* Perl_croak accvios when used this early in startup. */
9678          fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s", 
9679                          rsz, (unsigned long) jpilist[1].buflen,
9680                          "Check your rights database for corruption.\n");
9681          exit(SS$_ABORT);
9682       }
9683       if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9684       jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9685       if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9686       jpilist[1].buflen = rsz * sizeof(unsigned long int);
9687       _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9688       _ckvmssts_noperl(iosb[0]);
9689     }
9690     mask = (unsigned long int *)jpilist[1].bufadr;
9691     /* Check attribute flags for each identifier (2nd longword); protected
9692      * subsystem identifiers trigger tainting.
9693      */
9694     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9695       if (mask[i] & KGB$M_SUBSYSTEM) {
9696         will_taint = TRUE;
9697         break;
9698       }
9699     }
9700     if (mask != rlst) PerlMem_free(mask);
9701   }
9702
9703   /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9704    * logical, some versions of the CRTL will add a phanthom /000000/
9705    * directory.  This needs to be removed.
9706    */
9707   if (DECC_FILENAME_UNIX_REPORT) {
9708     char * zeros;
9709     int ulen;
9710     ulen = strlen(argvp[0][0]);
9711     if (ulen > 7) {
9712       zeros = strstr(argvp[0][0], "/000000/");
9713       if (zeros != NULL) {
9714         int mlen;
9715         mlen = ulen - (zeros - argvp[0][0]) - 7;
9716         memmove(zeros, &zeros[7], mlen);
9717         ulen = ulen - 7;
9718         argvp[0][0][ulen] = '\0';
9719       }
9720     }
9721     /* It also may have a trailing dot that needs to be removed otherwise
9722      * it will be converted to VMS mode incorrectly.
9723      */
9724     ulen--;
9725     if ((argvp[0][0][ulen] == '.') && (DECC_READDIR_DROPDOTNOTYPE))
9726       argvp[0][0][ulen] = '\0';
9727   }
9728
9729   /* We need to use this hack to tell Perl it should run with tainting,
9730    * since its tainting flag may be part of the PL_curinterp struct, which
9731    * hasn't been allocated when vms_image_init() is called.
9732    */
9733   if (will_taint) {
9734     char **newargv, **oldargv;
9735     oldargv = *argvp;
9736     newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9737     if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9738     newargv[0] = oldargv[0];
9739     newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9740     if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9741     strcpy(newargv[1], "-T");
9742     Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9743     (*argcp)++;
9744     newargv[*argcp] = NULL;
9745     /* We orphan the old argv, since we don't know where it's come from,
9746      * so we don't know how to free it.
9747      */
9748     *argvp = newargv;
9749   }
9750   else {  /* Did user explicitly request tainting? */
9751     int i;
9752     char *cp, **av = *argvp;
9753     for (i = 1; i < *argcp; i++) {
9754       if (*av[i] != '-') break;
9755       for (cp = av[i]+1; *cp; cp++) {
9756         if (*cp == 'T') { will_taint = 1; break; }
9757         else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9758                   strchr("DFIiMmx",*cp)) break;
9759       }
9760       if (will_taint) break;
9761     }
9762   }
9763
9764   for (tabidx = 0;
9765        len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9766        tabidx++) {
9767     if (!tabidx) {
9768       tabvec = (struct dsc$descriptor_s **)
9769             PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9770       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9771     }
9772     else if (tabidx >= tabct) {
9773       tabct += 8;
9774       tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9775       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9776     }
9777     tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9778     if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9779     tabvec[tabidx]->dsc$w_length  = len;
9780     tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
9781     tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_S;
9782     tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1);
9783     if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9784     my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9785   }
9786   if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9787
9788   getredirection(argcp,argvp);
9789 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9790   {
9791 # include <reentrancy.h>
9792   decc$set_reentrancy(C$C_MULTITHREAD);
9793   }
9794 #endif
9795   return;
9796 }
9797 /*}}}*/
9798
9799
9800 /* trim_unixpath()
9801  * Trim Unix-style prefix off filespec, so it looks like what a shell
9802  * glob expansion would return (i.e. from specified prefix on, not
9803  * full path).  Note that returned filespec is Unix-style, regardless
9804  * of whether input filespec was VMS-style or Unix-style.
9805  *
9806  * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9807  * determine prefix (both may be in VMS or Unix syntax).  opts is a bit
9808  * vector of options; at present, only bit 0 is used, and if set tells
9809  * trim unixpath to try the current default directory as a prefix when
9810  * presented with a possibly ambiguous ... wildcard.
9811  *
9812  * Returns !=0 on success, with trimmed filespec replacing contents of
9813  * fspec, and 0 on failure, with contents of fpsec unchanged.
9814  */
9815 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9816 int
9817 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9818 {
9819   char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9820   int tmplen, reslen = 0, dirs = 0;
9821
9822   if (!wildspec || !fspec) return 0;
9823
9824   unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9825   if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9826   tplate = unixwild;
9827   if (strpbrk(wildspec,"]>:") != NULL) {
9828     if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9829         PerlMem_free(unixwild);
9830         return 0;
9831     }
9832   }
9833   else {
9834     my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9835   }
9836   unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9837   if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9838   if (strpbrk(fspec,"]>:") != NULL) {
9839     if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9840         PerlMem_free(unixwild);
9841         PerlMem_free(unixified);
9842         return 0;
9843     }
9844     else base = unixified;
9845     /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9846      * check to see that final result fits into (isn't longer than) fspec */
9847     reslen = strlen(fspec);
9848   }
9849   else base = fspec;
9850
9851   /* No prefix or absolute path on wildcard, so nothing to remove */
9852   if (!*tplate || *tplate == '/') {
9853     PerlMem_free(unixwild);
9854     if (base == fspec) {
9855         PerlMem_free(unixified);
9856         return 1;
9857     }
9858     tmplen = strlen(unixified);
9859     if (tmplen > reslen) {
9860         PerlMem_free(unixified);
9861         return 0;  /* not enough space */
9862     }
9863     /* Copy unixified resultant, including trailing NUL */
9864     memmove(fspec,unixified,tmplen+1);
9865     PerlMem_free(unixified);
9866     return 1;
9867   }
9868
9869   for (end = base; *end; end++) ;  /* Find end of resultant filespec */
9870   if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9871     for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9872     for (cp1 = end ;cp1 >= base; cp1--)
9873       if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9874         { cp1++; break; }
9875     if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9876     PerlMem_free(unixified);
9877     PerlMem_free(unixwild);
9878     return 1;
9879   }
9880   else {
9881     char *tpl, *lcres;
9882     char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9883     int ells = 1, totells, segdirs, match;
9884     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9885                             resdsc =  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9886
9887     while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9888     totells = ells;
9889     for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9890     tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9891     if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9892     if (ellipsis == tplate && opts & 1) {
9893       /* Template begins with an ellipsis.  Since we can't tell how many
9894        * directory names at the front of the resultant to keep for an
9895        * arbitrary starting point, we arbitrarily choose the current
9896        * default directory as a starting point.  If it's there as a prefix,
9897        * clip it off.  If not, fall through and act as if the leading
9898        * ellipsis weren't there (i.e. return shortest possible path that
9899        * could match template).
9900        */
9901       if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9902           PerlMem_free(tpl);
9903           PerlMem_free(unixified);
9904           PerlMem_free(unixwild);
9905           return 0;
9906       }
9907       if (!DECC_EFS_CASE_PRESERVE) {
9908         for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9909           if (toLOWER_L1(*cp1) != toLOWER_L1(*cp2)) break;
9910       }
9911       segdirs = dirs - totells;  /* Min # of dirs we must have left */
9912       for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9913       if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9914         memmove(fspec,cp2+1,end - cp2);
9915         PerlMem_free(tpl);
9916         PerlMem_free(unixified);
9917         PerlMem_free(unixwild);
9918         return 1;
9919       }
9920     }
9921     /* First off, back up over constant elements at end of path */
9922     if (dirs) {
9923       for (front = end ; front >= base; front--)
9924          if (*front == '/' && !dirs--) { front++; break; }
9925     }
9926     lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9927     if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9928     for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9929          cp1++,cp2++) {
9930             if (!DECC_EFS_CASE_PRESERVE) {
9931                 *cp2 = toLOWER_L1(*cp1);  /* Make lc copy for match */
9932             }
9933             else {
9934                 *cp2 = *cp1;
9935             }
9936     }
9937     if (cp1 != '\0') {
9938         PerlMem_free(tpl);
9939         PerlMem_free(unixified);
9940         PerlMem_free(unixwild);
9941         PerlMem_free(lcres);
9942         return 0;  /* Path too long. */
9943     }
9944     lcend = cp2;
9945     *cp2 = '\0';  /* Pick up with memcpy later */
9946     lcfront = lcres + (front - base);
9947     /* Now skip over each ellipsis and try to match the path in front of it. */
9948     while (ells--) {
9949       for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9950         if (*(cp1)   == '.' && *(cp1+1) == '.' &&
9951             *(cp1+2) == '.' && *(cp1+3) == '/'    ) break;
9952       if (cp1 < tplate) break; /* template started with an ellipsis */
9953       if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9954         ellipsis = cp1; continue;
9955       }
9956       wilddsc.dsc$a_pointer = tpl;
9957       wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9958       nextell = cp1;
9959       for (segdirs = 0, cp2 = tpl;
9960            cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9961            cp1++, cp2++) {
9962          if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9963          else {
9964             if (!DECC_EFS_CASE_PRESERVE) {
9965               *cp2 = toLOWER_L1(*cp1);  /* else lowercase for match */
9966             }
9967             else {
9968               *cp2 = *cp1;  /* else preserve case for match */
9969             }
9970          }
9971          if (*cp2 == '/') segdirs++;
9972       }
9973       if (cp1 != ellipsis - 1) {
9974           PerlMem_free(tpl);
9975           PerlMem_free(unixified);
9976           PerlMem_free(unixwild);
9977           PerlMem_free(lcres);
9978           return 0; /* Path too long */
9979       }
9980       /* Back up at least as many dirs as in template before matching */
9981       for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9982         if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9983       for (match = 0; cp1 > lcres;) {
9984         resdsc.dsc$a_pointer = cp1;
9985         if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) { 
9986           match++;
9987           if (match == 1) lcfront = cp1;
9988         }
9989         for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9990       }
9991       if (!match) {
9992         PerlMem_free(tpl);
9993         PerlMem_free(unixified);
9994         PerlMem_free(unixwild);
9995         PerlMem_free(lcres);
9996         return 0;  /* Can't find prefix ??? */
9997       }
9998       if (match > 1 && opts & 1) {
9999         /* This ... wildcard could cover more than one set of dirs (i.e.
10000          * a set of similar dir names is repeated).  If the template
10001          * contains more than 1 ..., upstream elements could resolve the
10002          * ambiguity, but it's not worth a full backtracking setup here.
10003          * As a quick heuristic, clip off the current default directory
10004          * if it's present to find the trimmed spec, else use the
10005          * shortest string that this ... could cover.
10006          */
10007         char def[NAM$C_MAXRSS+1], *st;
10008
10009         if (getcwd(def, sizeof def,0) == NULL) {
10010             PerlMem_free(unixified);
10011             PerlMem_free(unixwild);
10012             PerlMem_free(lcres);
10013             PerlMem_free(tpl);
10014             return 0;
10015         }
10016         if (!DECC_EFS_CASE_PRESERVE) {
10017           for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10018             if (toLOWER_L1(*cp1) != toLOWER_L1(*cp2)) break;
10019         }
10020         segdirs = dirs - totells;  /* Min # of dirs we must have left */
10021         for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10022         if (*cp1 == '\0' && *cp2 == '/') {
10023           memmove(fspec,cp2+1,end - cp2);
10024           PerlMem_free(tpl);
10025           PerlMem_free(unixified);
10026           PerlMem_free(unixwild);
10027           PerlMem_free(lcres);
10028           return 1;
10029         }
10030         /* Nope -- stick with lcfront from above and keep going. */
10031       }
10032     }
10033     memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10034     PerlMem_free(tpl);
10035     PerlMem_free(unixified);
10036     PerlMem_free(unixwild);
10037     PerlMem_free(lcres);
10038     return 1;
10039   }
10040
10041 }  /* end of trim_unixpath() */
10042 /*}}}*/
10043
10044
10045 /*
10046  *  VMS readdir() routines.
10047  *  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10048  *
10049  *  21-Jul-1994  Charles Bailey  bailey@newman.upenn.edu
10050  *  Minor modifications to original routines.
10051  */
10052
10053 /* readdir may have been redefined by reentr.h, so make sure we get
10054  * the local version for what we do here.
10055  */
10056 #ifdef readdir
10057 # undef readdir
10058 #endif
10059 #if !defined(PERL_IMPLICIT_CONTEXT)
10060 # define readdir Perl_readdir
10061 #else
10062 # define readdir(a) Perl_readdir(aTHX_ a)
10063 #endif
10064
10065     /* Number of elements in vms_versions array */
10066 #define VERSIZE(e)      (sizeof e->vms_versions / sizeof e->vms_versions[0])
10067
10068 /*
10069  *  Open a directory, return a handle for later use.
10070  */
10071 /*{{{ DIR *opendir(char*name) */
10072 DIR *
10073 Perl_opendir(pTHX_ const char *name)
10074 {
10075     DIR *dd;
10076     char *dir;
10077     Stat_t sb;
10078
10079     Newx(dir, VMS_MAXRSS, char);
10080     if (int_tovmspath(name, dir, NULL) == NULL) {
10081       Safefree(dir);
10082       return NULL;
10083     }
10084     /* Check access before stat; otherwise stat does not
10085      * accurately report whether it's a directory.
10086      */
10087     if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
10088         && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10089       /* cando_by_name has already set errno */
10090       Safefree(dir);
10091       return NULL;
10092     }
10093     if (flex_stat(dir,&sb) == -1) return NULL;
10094     if (!S_ISDIR(sb.st_mode)) {
10095       Safefree(dir);
10096       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
10097       return NULL;
10098     }
10099     /* Get memory for the handle, and the pattern. */
10100     Newx(dd,1,DIR);
10101     Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10102
10103     /* Fill in the fields; mainly playing with the descriptor. */
10104     sprintf(dd->pattern, "%s*.*",dir);
10105     Safefree(dir);
10106     dd->context = 0;
10107     dd->count = 0;
10108     dd->flags = 0;
10109     /* By saying we want the result of readdir() in unix format, we are really
10110      * saying we want all the escapes removed, translating characters that
10111      * must be escaped in a VMS-format name to their unescaped form, which is
10112      * presumably allowed in a Unix-format name.
10113      */
10114     dd->flags = DECC_FILENAME_UNIX_REPORT ? PERL_VMSDIR_M_UNIXSPECS : 0;
10115     dd->pat.dsc$a_pointer = dd->pattern;
10116     dd->pat.dsc$w_length = strlen(dd->pattern);
10117     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10118     dd->pat.dsc$b_class = DSC$K_CLASS_S;
10119 #if defined(USE_ITHREADS)
10120     Newx(dd->mutex,1,perl_mutex);
10121     MUTEX_INIT( (perl_mutex *) dd->mutex );
10122 #else
10123     dd->mutex = NULL;
10124 #endif
10125
10126     return dd;
10127 }  /* end of opendir() */
10128 /*}}}*/
10129
10130 /*
10131  *  Set the flag to indicate we want versions or not.
10132  */
10133 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10134 void
10135 vmsreaddirversions(DIR *dd, int flag)
10136 {
10137     if (flag)
10138         dd->flags |= PERL_VMSDIR_M_VERSIONS;
10139     else
10140         dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10141 }
10142 /*}}}*/
10143
10144 /*
10145  *  Free up an opened directory.
10146  */
10147 /*{{{ void closedir(DIR *dd)*/
10148 void
10149 Perl_closedir(DIR *dd)
10150 {
10151     int sts;
10152
10153     sts = lib$find_file_end(&dd->context);
10154     Safefree(dd->pattern);
10155 #if defined(USE_ITHREADS)
10156     MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10157     Safefree(dd->mutex);
10158 #endif
10159     Safefree(dd);
10160 }
10161 /*}}}*/
10162
10163 /*
10164  *  Collect all the version numbers for the current file.
10165  */
10166 static void
10167 collectversions(pTHX_ DIR *dd)
10168 {
10169     struct dsc$descriptor_s     pat;
10170     struct dsc$descriptor_s     res;
10171     struct dirent *e;
10172     char *p, *text, *buff;
10173     int i;
10174     unsigned long context, tmpsts;
10175
10176     /* Convenient shorthand. */
10177     e = &dd->entry;
10178
10179     /* Add the version wildcard, ignoring the "*.*" put on before */
10180     i = strlen(dd->pattern);
10181     Newx(text,i + e->d_namlen + 3,char);
10182     my_strlcpy(text, dd->pattern, i + 1);
10183     sprintf(&text[i - 3], "%s;*", e->d_name);
10184
10185     /* Set up the pattern descriptor. */
10186     pat.dsc$a_pointer = text;
10187     pat.dsc$w_length = i + e->d_namlen - 1;
10188     pat.dsc$b_dtype = DSC$K_DTYPE_T;
10189     pat.dsc$b_class = DSC$K_CLASS_S;
10190
10191     /* Set up result descriptor. */
10192     Newx(buff, VMS_MAXRSS, char);
10193     res.dsc$a_pointer = buff;
10194     res.dsc$w_length = VMS_MAXRSS - 1;
10195     res.dsc$b_dtype = DSC$K_DTYPE_T;
10196     res.dsc$b_class = DSC$K_CLASS_S;
10197
10198     /* Read files, collecting versions. */
10199     for (context = 0, e->vms_verscount = 0;
10200          e->vms_verscount < VERSIZE(e);
10201          e->vms_verscount++) {
10202         unsigned long rsts;
10203         unsigned long flags = 0;
10204
10205 #ifdef VMS_LONGNAME_SUPPORT
10206         flags = LIB$M_FIL_LONG_NAMES;
10207 #endif
10208         tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10209         if (tmpsts == RMS$_NMF || context == 0) break;
10210         _ckvmssts(tmpsts);
10211         buff[VMS_MAXRSS - 1] = '\0';
10212         if ((p = strchr(buff, ';')))
10213             e->vms_versions[e->vms_verscount] = atoi(p + 1);
10214         else
10215             e->vms_versions[e->vms_verscount] = -1;
10216     }
10217
10218     _ckvmssts(lib$find_file_end(&context));
10219     Safefree(text);
10220     Safefree(buff);
10221
10222 }  /* end of collectversions() */
10223
10224 /*
10225  *  Read the next entry from the directory.
10226  */
10227 /*{{{ struct dirent *readdir(DIR *dd)*/
10228 struct dirent *
10229 Perl_readdir(pTHX_ DIR *dd)
10230 {
10231     struct dsc$descriptor_s     res;
10232     char *p, *buff;
10233     unsigned long int tmpsts;
10234     unsigned long rsts;
10235     unsigned long flags = 0;
10236     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10237     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10238
10239     /* Set up result descriptor, and get next file. */
10240     Newx(buff, VMS_MAXRSS, char);
10241     res.dsc$a_pointer = buff;
10242     res.dsc$w_length = VMS_MAXRSS - 1;
10243     res.dsc$b_dtype = DSC$K_DTYPE_T;
10244     res.dsc$b_class = DSC$K_CLASS_S;
10245
10246 #ifdef VMS_LONGNAME_SUPPORT
10247     flags = LIB$M_FIL_LONG_NAMES;
10248 #endif
10249
10250     tmpsts = lib$find_file
10251         (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10252     if (dd->context == 0)
10253         tmpsts = RMS$_NMF;  /* None left. (should be set, but make sure) */
10254
10255     if (!(tmpsts & 1)) {
10256       switch (tmpsts) {
10257         case RMS$_NMF:
10258           break;  /* no more files considered success */
10259         case RMS$_PRV:
10260           SETERRNO(EACCES, tmpsts); break;
10261         case RMS$_DEV:
10262           SETERRNO(ENODEV, tmpsts); break;
10263         case RMS$_DIR:
10264           SETERRNO(ENOTDIR, tmpsts); break;
10265         case RMS$_FNF: case RMS$_DNF:
10266           SETERRNO(ENOENT, tmpsts); break;
10267         default:
10268           SETERRNO(EVMSERR, tmpsts);
10269       }
10270       Safefree(buff);
10271       return NULL;
10272     }
10273     dd->count++;
10274     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10275     buff[res.dsc$w_length] = '\0';
10276     p = buff + res.dsc$w_length;
10277     while (--p >= buff) if (!isSPACE_L1(*p)) break;  
10278     *p = '\0';
10279     if (!DECC_EFS_CASE_PRESERVE) {
10280       for (p = buff; *p; p++) *p = toLOWER_L1(*p);
10281     }
10282
10283     /* Skip any directory component and just copy the name. */
10284     sts = vms_split_path
10285        (buff,
10286         &v_spec,
10287         &v_len,
10288         &r_spec,
10289         &r_len,
10290         &d_spec,
10291         &d_len,
10292         &n_spec,
10293         &n_len,
10294         &e_spec,
10295         &e_len,
10296         &vs_spec,
10297         &vs_len);
10298
10299     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10300
10301         /* In Unix report mode, remove the ".dir;1" from the name */
10302         /* if it is a real directory. */
10303         if (DECC_FILENAME_UNIX_REPORT && DECC_EFS_CHARSET) {
10304             if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10305                 Stat_t statbuf;
10306                 int ret_sts;
10307
10308                 ret_sts = flex_lstat(buff, &statbuf);
10309                 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10310                     e_len = 0;
10311                     e_spec[0] = 0;
10312                 }
10313             }
10314         }
10315
10316         /* Drop NULL extensions on UNIX file specification */
10317         if ((e_len == 1) && DECC_READDIR_DROPDOTNOTYPE) {
10318             e_len = 0;
10319             e_spec[0] = '\0';
10320         }
10321     }
10322
10323     memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10324     dd->entry.d_name[n_len + e_len] = '\0';
10325     dd->entry.d_namlen = n_len + e_len;
10326
10327     /* Convert the filename to UNIX format if needed */
10328     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10329
10330         /* Translate the encoded characters. */
10331         /* Fixme: Unicode handling could result in embedded 0 characters */
10332         if (strchr(dd->entry.d_name, '^') != NULL) {
10333             char new_name[256];
10334             char * q;
10335             p = dd->entry.d_name;
10336             q = new_name;
10337             while (*p != 0) {
10338                 int inchars_read, outchars_added;
10339                 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10340                 p += inchars_read;
10341                 q += outchars_added;
10342                 /* fix-me */
10343                 /* if outchars_added > 1, then this is a wide file specification */
10344                 /* Wide file specifications need to be passed in Perl */
10345                 /* counted strings apparently with a Unicode flag */
10346             }
10347             *q = 0;
10348             dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10349         }
10350     }
10351
10352     dd->entry.vms_verscount = 0;
10353     if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10354     Safefree(buff);
10355     return &dd->entry;
10356
10357 }  /* end of readdir() */
10358 /*}}}*/
10359
10360 /*
10361  *  Read the next entry from the directory -- thread-safe version.
10362  */
10363 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10364 int
10365 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10366 {
10367     int retval;
10368
10369     MUTEX_LOCK( (perl_mutex *) dd->mutex );
10370
10371     entry = readdir(dd);
10372     *result = entry;
10373     retval = ( *result == NULL ? errno : 0 );
10374
10375     MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10376
10377     return retval;
10378
10379 }  /* end of readdir_r() */
10380 /*}}}*/
10381
10382 /*
10383  *  Return something that can be used in a seekdir later.
10384  */
10385 /*{{{ long telldir(DIR *dd)*/
10386 long
10387 Perl_telldir(DIR *dd)
10388 {
10389     return dd->count;
10390 }
10391 /*}}}*/
10392
10393 /*
10394  *  Return to a spot where we used to be.  Brute force.
10395  */
10396 /*{{{ void seekdir(DIR *dd,long count)*/
10397 void
10398 Perl_seekdir(pTHX_ DIR *dd, long count)
10399 {
10400     int old_flags;
10401
10402     /* If we haven't done anything yet... */
10403     if (dd->count == 0)
10404         return;
10405
10406     /* Remember some state, and clear it. */
10407     old_flags = dd->flags;
10408     dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10409     _ckvmssts(lib$find_file_end(&dd->context));
10410     dd->context = 0;
10411
10412     /* The increment is in readdir(). */
10413     for (dd->count = 0; dd->count < count; )
10414         readdir(dd);
10415
10416     dd->flags = old_flags;
10417
10418 }  /* end of seekdir() */
10419 /*}}}*/
10420
10421 /* VMS subprocess management
10422  *
10423  * my_vfork() - just a vfork(), after setting a flag to record that
10424  * the current script is trying a Unix-style fork/exec.
10425  *
10426  * vms_do_aexec() and vms_do_exec() are called in response to the
10427  * perl 'exec' function.  If this follows a vfork call, then they
10428  * call out the regular perl routines in doio.c which do an
10429  * execvp (for those who really want to try this under VMS).
10430  * Otherwise, they do exactly what the perl docs say exec should
10431  * do - terminate the current script and invoke a new command
10432  * (See below for notes on command syntax.)
10433  *
10434  * do_aspawn() and do_spawn() implement the VMS side of the perl
10435  * 'system' function.
10436  *
10437  * Note on command arguments to perl 'exec' and 'system': When handled
10438  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10439  * are concatenated to form a DCL command string.  If the first non-numeric
10440  * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10441  * the command string is handed off to DCL directly.  Otherwise,
10442  * the first token of the command is taken as the filespec of an image
10443  * to run.  The filespec is expanded using a default type of '.EXE' and
10444  * the process defaults for device, directory, etc., and if found, the resultant
10445  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10446  * the command string as parameters.  This is perhaps a bit complicated,
10447  * but I hope it will form a happy medium between what VMS folks expect
10448  * from lib$spawn and what Unix folks expect from exec.
10449  */
10450
10451 static int vfork_called;
10452
10453 /*{{{int my_vfork(void)*/
10454 int
10455 my_vfork(void)
10456 {
10457   vfork_called++;
10458   return vfork();
10459 }
10460 /*}}}*/
10461
10462
10463 static void
10464 vms_execfree(struct dsc$descriptor_s *vmscmd) 
10465 {
10466   if (vmscmd) {
10467       if (vmscmd->dsc$a_pointer) {
10468           PerlMem_free(vmscmd->dsc$a_pointer);
10469       }
10470       PerlMem_free(vmscmd);
10471   }
10472 }
10473
10474 static char *
10475 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10476 {
10477   char *junk, *tmps = NULL, *cmd;
10478   size_t cmdlen = 0;
10479   size_t rlen;
10480   SV **idx;
10481   STRLEN n_a;
10482
10483   idx = mark;
10484   if (really) {
10485     tmps = SvPV(really,rlen);
10486     if (*tmps) {
10487       cmdlen += rlen + 1;
10488       idx++;
10489     }
10490   }
10491   
10492   for (idx++; idx <= sp; idx++) {
10493     if (*idx) {
10494       junk = SvPVx(*idx,rlen);
10495       cmdlen += rlen ? rlen + 1 : 0;
10496     }
10497   }
10498   Newx(cmd, cmdlen+1, char);
10499   SAVEFREEPV(cmd);
10500
10501   if (tmps && *tmps) {
10502     my_strlcpy(cmd, tmps, cmdlen + 1);
10503     mark++;
10504   }
10505   else *cmd = '\0';
10506   while (++mark <= sp) {
10507     if (*mark) {
10508       char *s = SvPVx(*mark,n_a);
10509       if (!*s) continue;
10510       if (*cmd) my_strlcat(cmd, " ", cmdlen+1);
10511       my_strlcat(cmd, s, cmdlen+1);
10512     }
10513   }
10514   return cmd;
10515
10516 }  /* end of setup_argstr() */
10517
10518
10519 static unsigned long int
10520 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10521                    struct dsc$descriptor_s **pvmscmd)
10522 {
10523   char * vmsspec;
10524   char * resspec;
10525   char image_name[NAM$C_MAXRSS+1];
10526   char image_argv[NAM$C_MAXRSS+1];
10527   $DESCRIPTOR(defdsc,".EXE");
10528   $DESCRIPTOR(defdsc2,".");
10529   struct dsc$descriptor_s resdsc;
10530   struct dsc$descriptor_s *vmscmd;
10531   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10532   unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10533   char *s, *rest, *cp, *wordbreak;
10534   char * cmd;
10535   int cmdlen;
10536   int isdcl;
10537
10538   vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10539   if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10540
10541   /* vmsspec is a DCL command buffer, not just a filename */
10542   vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10543   if (vmsspec == NULL)
10544       _ckvmssts_noperl(SS$_INSFMEM);
10545
10546   resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10547   if (resspec == NULL)
10548       _ckvmssts_noperl(SS$_INSFMEM);
10549
10550   /* Make a copy for modification */
10551   cmdlen = strlen(incmd);
10552   cmd = (char *)PerlMem_malloc(cmdlen+1);
10553   if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10554   my_strlcpy(cmd, incmd, cmdlen + 1);
10555   image_name[0] = 0;
10556   image_argv[0] = 0;
10557
10558   resdsc.dsc$a_pointer = resspec;
10559   resdsc.dsc$b_dtype  = DSC$K_DTYPE_T;
10560   resdsc.dsc$b_class  = DSC$K_CLASS_S;
10561   resdsc.dsc$w_length = VMS_MAXRSS - 1;
10562
10563   vmscmd->dsc$a_pointer = NULL;
10564   vmscmd->dsc$b_dtype  = DSC$K_DTYPE_T;
10565   vmscmd->dsc$b_class  = DSC$K_CLASS_S;
10566   vmscmd->dsc$w_length = 0;
10567   if (pvmscmd) *pvmscmd = vmscmd;
10568
10569   if (suggest_quote) *suggest_quote = 0;
10570
10571   if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10572     PerlMem_free(cmd);
10573     PerlMem_free(vmsspec);
10574     PerlMem_free(resspec);
10575     return CLI$_BUFOVF;                /* continuation lines currently unsupported */
10576   }
10577
10578   s = cmd;
10579
10580   while (*s && isSPACE_L1(*s)) s++;
10581
10582   if (*s == '@' || *s == '$') {
10583     vmsspec[0] = *s;  rest = s + 1;
10584     for (cp = &vmsspec[1]; *rest && isSPACE_L1(*rest); rest++,cp++) *cp = *rest;
10585   }
10586   else { cp = vmsspec; rest = s; }
10587
10588   /* If the first word is quoted, then we need to unquote it and
10589    * escape spaces within it.  We'll expand into the resspec buffer,
10590    * then copy back into the cmd buffer, expanding the latter if
10591    * necessary.
10592    */
10593   if (*rest == '"') {
10594     char *cp2;
10595     char *r = rest;
10596     bool in_quote = 0;
10597     int clen = cmdlen;
10598     int soff = s - cmd;
10599
10600     for (cp2 = resspec;
10601          *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10602          rest++) {
10603
10604       if (*rest == ' ') {    /* Escape ' ' to '^_'. */
10605         *cp2 = '^';
10606         *(++cp2) = '_';
10607         cp2++;
10608         clen++;
10609       }
10610       else if (*rest == '"') {
10611         clen--;
10612         if (in_quote) {     /* Must be closing quote. */
10613           rest++;
10614           break;
10615         }
10616         in_quote = 1;
10617       }
10618       else {
10619         *cp2 = *rest;
10620         cp2++;
10621       }
10622     }
10623     *cp2 = '\0';
10624
10625     /* Expand the command buffer if necessary. */
10626     if (clen > cmdlen) {
10627       cmd = (char *)PerlMem_realloc(cmd, clen);
10628       if (cmd == NULL)
10629         _ckvmssts_noperl(SS$_INSFMEM);
10630       /* Where we are may have changed, so recompute offsets */
10631       r = cmd + (r - s - soff);
10632       rest = cmd + (rest - s - soff);
10633       s = cmd + soff;
10634     }
10635
10636     /* Shift the non-verb portion of the command (if any) up or
10637      * down as necessary.
10638      */
10639     if (*rest)
10640       memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10641
10642     /* Copy the unquoted and escaped command verb into place. */
10643     memcpy(r, resspec, cp2 - resspec); 
10644     cmd[clen] = '\0';
10645     cmdlen = clen;
10646     rest = r;         /* Rewind for subsequent operations. */
10647   }
10648
10649   if (*rest == '.' || *rest == '/') {
10650     char *cp2;
10651     for (cp2 = resspec;
10652          *rest && !isSPACE_L1(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10653          rest++, cp2++) *cp2 = *rest;
10654     *cp2 = '\0';
10655     if (int_tovmsspec(resspec, cp, 0, NULL)) { 
10656       s = vmsspec;
10657
10658       /* When a UNIX spec with no file type is translated to VMS, */
10659       /* A trailing '.' is appended under ODS-5 rules.            */
10660       /* Here we do not want that trailing "." as it prevents     */
10661       /* Looking for a implied ".exe" type. */
10662       if (DECC_EFS_CHARSET) {
10663           int i;
10664           i = strlen(vmsspec);
10665           if (vmsspec[i-1] == '.') {
10666               vmsspec[i-1] = '\0';
10667           }
10668       }
10669
10670       if (*rest) {
10671         for (cp2 = vmsspec + strlen(vmsspec);
10672              *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10673              rest++, cp2++) *cp2 = *rest;
10674         *cp2 = '\0';
10675       }
10676     }
10677   }
10678   /* Intuit whether verb (first word of cmd) is a DCL command:
10679    *   - if first nonspace char is '@', it's a DCL indirection
10680    * otherwise
10681    *   - if verb contains a filespec separator, it's not a DCL command
10682    *   - if it doesn't, caller tells us whether to default to a DCL
10683    *     command, or to a local image unless told it's DCL (by leading '$')
10684    */
10685   if (*s == '@') {
10686       isdcl = 1;
10687       if (suggest_quote) *suggest_quote = 1;
10688   } else {
10689     char *filespec = strpbrk(s,":<[.;");
10690     rest = wordbreak = strpbrk(s," \"\t/");
10691     if (!wordbreak) wordbreak = s + strlen(s);
10692     if (*s == '$') check_img = 0;
10693     if (filespec && (filespec < wordbreak)) isdcl = 0;
10694     else isdcl = !check_img;
10695   }
10696
10697   if (!isdcl) {
10698     int rsts;
10699     imgdsc.dsc$a_pointer = s;
10700     imgdsc.dsc$w_length = wordbreak - s;
10701     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10702     if (!(retsts&1)) {
10703         _ckvmssts_noperl(lib$find_file_end(&cxt));
10704         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10705       if (!(retsts & 1) && *s == '$') {
10706         _ckvmssts_noperl(lib$find_file_end(&cxt));
10707         imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10708         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10709         if (!(retsts&1)) {
10710           _ckvmssts_noperl(lib$find_file_end(&cxt));
10711           retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10712         }
10713       }
10714     }
10715     _ckvmssts_noperl(lib$find_file_end(&cxt));
10716
10717     if (retsts & 1) {
10718       FILE *fp;
10719       s = resspec;
10720       while (*s && !isSPACE_L1(*s)) s++;
10721       *s = '\0';
10722
10723       /* check that it's really not DCL with no file extension */
10724       fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10725       if (fp) {
10726         char b[256] = {0,0,0,0};
10727         read(fileno(fp), b, 256);
10728         isdcl = isPRINT_L1(b[0]) && isPRINT_L1(b[1]) && isPRINT_L1(b[2]) && isPRINT_L1(b[3]);
10729         if (isdcl) {
10730           int shebang_len;
10731
10732           /* Check for script */
10733           shebang_len = 0;
10734           if ((b[0] == '#') && (b[1] == '!'))
10735              shebang_len = 2;
10736 #ifdef ALTERNATE_SHEBANG
10737           else {
10738             if (strEQ(b, ALTERNATE_SHEBANG)) {
10739               char * perlstr;
10740                 perlstr = strstr("perl",b);
10741                 if (perlstr == NULL)
10742                   shebang_len = 0;
10743                 else
10744                   shebang_len = strlen(ALTERNATE_SHEBANG);
10745             }
10746             else
10747               shebang_len = 0;
10748           }
10749 #endif
10750
10751           if (shebang_len > 0) {
10752           int i;
10753           int j;
10754           char tmpspec[NAM$C_MAXRSS + 1];
10755
10756             i = shebang_len;
10757              /* Image is following after white space */
10758             /*--------------------------------------*/
10759             while (isPRINT_L1(b[i]) && isSPACE_L1(b[i]))
10760                 i++;
10761
10762             j = 0;
10763             while (isPRINT_L1(b[i]) && !isSPACE_L1(b[i])) {
10764                 tmpspec[j++] = b[i++];
10765                 if (j >= NAM$C_MAXRSS)
10766                    break;
10767             }
10768             tmpspec[j] = '\0';
10769
10770              /* There may be some default parameters to the image */
10771             /*---------------------------------------------------*/
10772             j = 0;
10773             while (isPRINT_L1(b[i])) {
10774                 image_argv[j++] = b[i++];
10775                 if (j >= NAM$C_MAXRSS)
10776                    break;
10777             }
10778             while ((j > 0) && !isPRINT_L1(image_argv[j-1]))
10779                 j--;
10780             image_argv[j] = 0;
10781
10782             /* It will need to be converted to VMS format and validated */
10783             if (tmpspec[0] != '\0') {
10784               char * iname;
10785
10786                /* Try to find the exact program requested to be run */
10787               /*---------------------------------------------------*/
10788               iname = int_rmsexpand
10789                  (tmpspec, image_name, ".exe",
10790                   PERL_RMSEXPAND_M_VMS, NULL, NULL);
10791               if (iname != NULL) {
10792                 if (cando_by_name_int
10793                         (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10794                   /* MCR prefix needed */
10795                   isdcl = 0;
10796                 }
10797                 else {
10798                    /* Try again with a null type */
10799                   /*----------------------------*/
10800                   iname = int_rmsexpand
10801                     (tmpspec, image_name, ".",
10802                      PERL_RMSEXPAND_M_VMS, NULL, NULL);
10803                   if (iname != NULL) {
10804                     if (cando_by_name_int
10805                          (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10806                       /* MCR prefix needed */
10807                       isdcl = 0;
10808                     }
10809                   }
10810                 }
10811
10812                  /* Did we find the image to run the script? */
10813                 /*------------------------------------------*/
10814                 if (isdcl) {
10815                   char *tchr;
10816
10817                    /* Assume DCL or foreign command exists */
10818                   /*--------------------------------------*/
10819                   tchr = strrchr(tmpspec, '/');
10820                   if (tchr != NULL) {
10821                     tchr++;
10822                   }
10823                   else {
10824                     tchr = tmpspec;
10825                   }
10826                   my_strlcpy(image_name, tchr, sizeof(image_name));
10827                 }
10828               }
10829             }
10830           }
10831         }
10832         fclose(fp);
10833       }
10834       if (check_img && isdcl) {
10835           PerlMem_free(cmd);
10836           PerlMem_free(resspec);
10837           PerlMem_free(vmsspec);
10838           return RMS$_FNF;
10839       }
10840
10841       if (cando_by_name(S_IXUSR,0,resspec)) {
10842         vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10843         if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10844         if (!isdcl) {
10845             my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10846             if (image_name[0] != 0) {
10847                 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10848                 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10849             }
10850         } else if (image_name[0] != 0) {
10851             my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10852             my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10853         } else {
10854             my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10855         }
10856         if (suggest_quote) *suggest_quote = 1;
10857
10858         /* If there is an image name, use original command */
10859         if (image_name[0] == 0)
10860             my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10861         else {
10862             rest = cmd;
10863             while (*rest && isSPACE_L1(*rest)) rest++;
10864         }
10865
10866         if (image_argv[0] != 0) {
10867           my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10868           my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10869         }
10870         if (rest) {
10871            int rest_len;
10872            int vmscmd_len;
10873
10874            rest_len = strlen(rest);
10875            vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10876            if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10877               my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10878            else
10879              retsts = CLI$_BUFOVF;
10880         }
10881         vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10882         PerlMem_free(cmd);
10883         PerlMem_free(vmsspec);
10884         PerlMem_free(resspec);
10885         return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10886       }
10887       else
10888         retsts = RMS$_PRV;
10889     }
10890   }
10891   /* It's either a DCL command or we couldn't find a suitable image */
10892   vmscmd->dsc$w_length = strlen(cmd);
10893
10894   vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10895   my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10896
10897   PerlMem_free(cmd);
10898   PerlMem_free(resspec);
10899   PerlMem_free(vmsspec);
10900
10901   /* check if it's a symbol (for quoting purposes) */
10902   if (suggest_quote && !*suggest_quote) { 
10903     int iss;     
10904     char equiv[LNM$C_NAMLENGTH];
10905     struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10906     eqvdsc.dsc$a_pointer = equiv;
10907
10908     iss = lib$get_symbol(vmscmd,&eqvdsc);
10909     if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10910   }
10911   if (!(retsts & 1)) {
10912     /* just hand off status values likely to be due to user error */
10913     if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10914         retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10915        (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10916     else { _ckvmssts_noperl(retsts); }
10917   }
10918
10919   return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10920
10921 }  /* end of setup_cmddsc() */
10922
10923
10924 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10925 bool
10926 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10927 {
10928   bool exec_sts;
10929   char * cmd;
10930
10931   if (vfork_called) {           /* this follows a vfork - act Unixish */
10932     vfork_called--;
10933     if (vfork_called < 0) {
10934       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10935       vfork_called = 0;
10936     }
10937     else return do_aexec(really,mark,sp);
10938   }
10939                                            /* no vfork - act VMSish */
10940   if (sp > mark) {
10941     ENTER;
10942     cmd = setup_argstr(aTHX_ really,mark,sp);
10943     exec_sts = vms_do_exec(cmd);
10944     LEAVE;
10945     return exec_sts;
10946   }
10947
10948   SETERRNO(ENOENT, RMS_FNF);
10949   return FALSE;
10950 }  /* end of vms_do_aexec() */
10951 /*}}}*/
10952
10953 /* {{{bool vms_do_exec(char *cmd) */
10954 bool
10955 Perl_vms_do_exec(pTHX_ const char *cmd)
10956 {
10957   struct dsc$descriptor_s *vmscmd;
10958
10959   if (vfork_called) {             /* this follows a vfork - act Unixish */
10960     vfork_called--;
10961     if (vfork_called < 0) {
10962       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10963       vfork_called = 0;
10964     }
10965     else return do_exec(cmd);
10966   }
10967
10968   {                               /* no vfork - act VMSish */
10969     unsigned long int retsts;
10970
10971     TAINT_ENV();
10972     TAINT_PROPER("exec");
10973     if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10974       retsts = lib$do_command(vmscmd);
10975
10976     switch (retsts) {
10977       case RMS$_FNF: case RMS$_DNF:
10978         set_errno(ENOENT); break;
10979       case RMS$_DIR:
10980         set_errno(ENOTDIR); break;
10981       case RMS$_DEV:
10982         set_errno(ENODEV); break;
10983       case RMS$_PRV:
10984         set_errno(EACCES); break;
10985       case RMS$_SYN:
10986         set_errno(EINVAL); break;
10987       case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10988         set_errno(E2BIG); break;
10989       case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10990         _ckvmssts_noperl(retsts); /* fall through */
10991       default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10992         set_errno(EVMSERR); 
10993     }
10994     set_vaxc_errno(retsts);
10995     if (ckWARN(WARN_EXEC)) {
10996       Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10997              vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10998     }
10999     vms_execfree(vmscmd);
11000   }
11001
11002   return FALSE;
11003
11004 }  /* end of vms_do_exec() */
11005 /*}}}*/
11006
11007 int do_spawn2(pTHX_ const char *, int);
11008
11009 int
11010 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11011 {
11012   unsigned long int sts;
11013   char * cmd;
11014   int flags = 0;
11015
11016   if (sp > mark) {
11017
11018     /* We'll copy the (undocumented?) Win32 behavior and allow a 
11019      * numeric first argument.  But the only value we'll support
11020      * through do_aspawn is a value of 1, which means spawn without
11021      * waiting for completion -- other values are ignored.
11022      */
11023     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11024         ++mark;
11025         flags = SvIVx(*mark);
11026     }
11027
11028     if (flags && flags == 1)     /* the Win32 P_NOWAIT value */
11029         flags = CLI$M_NOWAIT;
11030     else
11031         flags = 0;
11032
11033     ENTER;
11034     cmd = setup_argstr(aTHX_ really, mark, sp);
11035     sts = do_spawn2(aTHX_ cmd, flags);
11036     LEAVE;
11037     /* pp_sys will clean up cmd */
11038     return sts;
11039   }
11040   return SS$_ABORT;
11041 }  /* end of do_aspawn() */
11042 /*}}}*/
11043
11044
11045 /* {{{int do_spawn(char* cmd) */
11046 int
11047 Perl_do_spawn(pTHX_ char* cmd)
11048 {
11049     PERL_ARGS_ASSERT_DO_SPAWN;
11050
11051     return do_spawn2(aTHX_ cmd, 0);
11052 }
11053 /*}}}*/
11054
11055 /* {{{int do_spawn_nowait(char* cmd) */
11056 int
11057 Perl_do_spawn_nowait(pTHX_ char* cmd)
11058 {
11059     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11060
11061     return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11062 }
11063 /*}}}*/
11064
11065 /* {{{int do_spawn2(char *cmd) */
11066 int
11067 do_spawn2(pTHX_ const char *cmd, int flags)
11068 {
11069   unsigned long int sts, substs;
11070
11071   TAINT_ENV();
11072   TAINT_PROPER("spawn");
11073   if (!cmd || !*cmd) {
11074     sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11075     if (!(sts & 1)) {
11076       switch (sts) {
11077         case RMS$_FNF:  case RMS$_DNF:
11078           set_errno(ENOENT); break;
11079         case RMS$_DIR:
11080           set_errno(ENOTDIR); break;
11081         case RMS$_DEV:
11082           set_errno(ENODEV); break;
11083         case RMS$_PRV:
11084           set_errno(EACCES); break;
11085         case RMS$_SYN:
11086           set_errno(EINVAL); break;
11087         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11088           set_errno(E2BIG); break;
11089         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11090           _ckvmssts_noperl(sts); /* fall through */
11091         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11092           set_errno(EVMSERR);
11093       }
11094       set_vaxc_errno(sts);
11095       if (ckWARN(WARN_EXEC)) {
11096         Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11097                     Strerror(errno));
11098       }
11099     }
11100     sts = substs;
11101   }
11102   else {
11103     char mode[3];
11104     PerlIO * fp;
11105     if (flags & CLI$M_NOWAIT)
11106         strcpy(mode, "n");
11107     else
11108         strcpy(mode, "nW");
11109     
11110     fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11111     if (fp != NULL)
11112       my_pclose(fp);
11113     /* sts will be the pid in the nowait case, so leave a
11114      * hint saying not to do any bit shifting to it.
11115      */
11116     if (flags & CLI$M_NOWAIT)
11117         PL_statusvalue = -1;
11118   }
11119   return sts;
11120 }  /* end of do_spawn2() */
11121 /*}}}*/
11122
11123
11124 static unsigned int *sockflags, sockflagsize;
11125
11126 /*
11127  * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11128  * routines found in some versions of the CRTL can't deal with sockets.
11129  * We don't shim the other file open routines since a socket isn't
11130  * likely to be opened by a name.
11131  */
11132 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11133 FILE *
11134 my_fdopen(int fd, const char *mode)
11135 {
11136   FILE *fp = fdopen(fd, mode);
11137
11138   if (fp) {
11139     unsigned int fdoff = fd / sizeof(unsigned int);
11140     Stat_t sbuf; /* native stat; we don't need flex_stat */
11141     if (!sockflagsize || fdoff > sockflagsize) {
11142       if (sockflags) Renew(     sockflags,fdoff+2,unsigned int);
11143       else           Newx  (sockflags,fdoff+2,unsigned int);
11144       memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11145       sockflagsize = fdoff + 2;
11146     }
11147     if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11148       sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11149   }
11150   return fp;
11151
11152 }
11153 /*}}}*/
11154
11155
11156 /*
11157  * Clear the corresponding bit when the (possibly) socket stream is closed.
11158  * There still a small hole: we miss an implicit close which might occur
11159  * via freopen().  >> Todo
11160  */
11161 /*{{{ int my_fclose(FILE *fp)*/
11162 int
11163 my_fclose(FILE *fp) {
11164   if (fp) {
11165     unsigned int fd = fileno(fp);
11166     unsigned int fdoff = fd / sizeof(unsigned int);
11167
11168     if (sockflagsize && fdoff < sockflagsize)
11169       sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11170   }
11171   return fclose(fp);
11172 }
11173 /*}}}*/
11174
11175
11176 /* 
11177  * A simple fwrite replacement which outputs itmsz*nitm chars without
11178  * introducing record boundaries every itmsz chars.
11179  * We are using fputs, which depends on a terminating null.  We may
11180  * well be writing binary data, so we need to accommodate not only
11181  * data with nulls sprinkled in the middle but also data with no null 
11182  * byte at the end.
11183  */
11184 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11185 int
11186 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11187 {
11188   char *cp, *end, *cpd;
11189   char *data;
11190   unsigned int fd = fileno(dest);
11191   unsigned int fdoff = fd / sizeof(unsigned int);
11192   int retval;
11193   int bufsize = itmsz * nitm + 1;
11194
11195   if (fdoff < sockflagsize &&
11196       (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11197     if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11198     return nitm;
11199   }
11200
11201   _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11202   memcpy( data, src, itmsz*nitm );
11203   data[itmsz*nitm] = '\0';
11204
11205   end = data + itmsz * nitm;
11206   retval = (int) nitm; /* on success return # items written */
11207
11208   cpd = data;
11209   while (cpd <= end) {
11210     for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11211     if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11212     if (cp < end)
11213       if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11214     cpd = cp + 1;
11215   }
11216
11217   if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11218   return retval;
11219
11220 }  /* end of my_fwrite() */
11221 /*}}}*/
11222
11223 /*{{{ int my_flush(FILE *fp)*/
11224 int
11225 Perl_my_flush(pTHX_ FILE *fp)
11226 {
11227     int res;
11228     if ((res = fflush(fp)) == 0 && fp) {
11229 #ifdef VMS_DO_SOCKETS
11230         Stat_t s;
11231         if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11232 #endif
11233             res = fsync(fileno(fp));
11234     }
11235 /*
11236  * If the flush succeeded but set end-of-file, we need to clear
11237  * the error because our caller may check ferror().  BTW, this 
11238  * probably means we just flushed an empty file.
11239  */
11240     if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11241
11242     return res;
11243 }
11244 /*}}}*/
11245
11246 /* fgetname() is not returning the correct file specifications when
11247  * decc_filename_unix_report mode is active.  So we have to have it
11248  * aways return filenames in VMS mode and convert it ourselves.
11249  */
11250
11251 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11252 char *
11253 Perl_my_fgetname(FILE *fp, char * buf) {
11254     char * retname;
11255     char * vms_name;
11256
11257     retname = fgetname(fp, buf, 1);
11258
11259     /* If we are in VMS mode, then we are done */
11260     if (!DECC_FILENAME_UNIX_REPORT || (retname == NULL)) {
11261        return retname;
11262     }
11263
11264     /* Convert this to Unix format */
11265     vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11266     my_strlcpy(vms_name, retname, VMS_MAXRSS);
11267     retname = int_tounixspec(vms_name, buf, NULL);
11268     PerlMem_free(vms_name);
11269
11270     return retname;
11271 }
11272 /*}}}*/
11273
11274 /*
11275  * Here are replacements for the following Unix routines in the VMS environment:
11276  *      getpwuid    Get information for a particular UIC or UID
11277  *      getpwnam    Get information for a named user
11278  *      getpwent    Get information for each user in the rights database
11279  *      setpwent    Reset search to the start of the rights database
11280  *      endpwent    Finish searching for users in the rights database
11281  *
11282  * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11283  * (defined in pwd.h), which contains the following fields:-
11284  *      struct passwd {
11285  *              char        *pw_name;    Username (in lower case)
11286  *              char        *pw_passwd;  Hashed password
11287  *              unsigned int pw_uid;     UIC
11288  *              unsigned int pw_gid;     UIC group  number
11289  *              char        *pw_unixdir; Default device/directory (VMS-style)
11290  *              char        *pw_gecos;   Owner name
11291  *              char        *pw_dir;     Default device/directory (Unix-style)
11292  *              char        *pw_shell;   Default CLI name (eg. DCL)
11293  *      };
11294  * If the specified user does not exist, getpwuid and getpwnam return NULL.
11295  *
11296  * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11297  * not the UIC member number (eg. what's returned by getuid()),
11298  * getpwuid() can accept either as input (if uid is specified, the caller's
11299  * UIC group is used), though it won't recognise gid=0.
11300  *
11301  * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11302  * information about other users in your group or in other groups, respectively.
11303  * If the required privilege is not available, then these routines fill only
11304  * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11305  * string).
11306  *
11307  * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11308  */
11309
11310 /* sizes of various UAF record fields */
11311 #define UAI$S_USERNAME 12
11312 #define UAI$S_IDENT    31
11313 #define UAI$S_OWNER    31
11314 #define UAI$S_DEFDEV   31
11315 #define UAI$S_DEFDIR   63
11316 #define UAI$S_DEFCLI   31
11317 #define UAI$S_PWD       8
11318
11319 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT  && \
11320                         (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11321                         (uic).uic$v_group  != UIC$K_WILD_GROUP)
11322
11323 static char __empty[]= "";
11324 static struct passwd __passwd_empty=
11325     {(char *) __empty, (char *) __empty, 0, 0,
11326      (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11327 static int contxt= 0;
11328 static struct passwd __pwdcache;
11329 static char __pw_namecache[UAI$S_IDENT+1];
11330
11331 /*
11332  * This routine does most of the work extracting the user information.
11333  */
11334 static int
11335 fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11336 {
11337     static struct {
11338         unsigned char length;
11339         char pw_gecos[UAI$S_OWNER+1];
11340     } owner;
11341     static union uicdef uic;
11342     static struct {
11343         unsigned char length;
11344         char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11345     } defdev;
11346     static struct {
11347         unsigned char length;
11348         char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11349     } defdir;
11350     static struct {
11351         unsigned char length;
11352         char pw_shell[UAI$S_DEFCLI+1];
11353     } defcli;
11354     static char pw_passwd[UAI$S_PWD+1];
11355
11356     static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11357     struct dsc$descriptor_s name_desc;
11358     unsigned long int sts;
11359
11360     static struct itmlst_3 itmlst[]= {
11361         {UAI$S_OWNER+1,    UAI$_OWNER,  &owner,    &lowner},
11362         {sizeof(uic),      UAI$_UIC,    &uic,      &luic},
11363         {UAI$S_DEFDEV+1,   UAI$_DEFDEV, &defdev,   &ldefdev},
11364         {UAI$S_DEFDIR+1,   UAI$_DEFDIR, &defdir,   &ldefdir},
11365         {UAI$S_DEFCLI+1,   UAI$_DEFCLI, &defcli,   &ldefcli},
11366         {UAI$S_PWD,        UAI$_PWD,    pw_passwd, &lpwd},
11367         {0,                0,           NULL,    NULL}};
11368
11369     name_desc.dsc$w_length=  strlen(name);
11370     name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11371     name_desc.dsc$b_class=   DSC$K_CLASS_S;
11372     name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11373
11374 /*  Note that sys$getuai returns many fields as counted strings. */
11375     sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11376     if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11377       set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11378     }
11379     else { _ckvmssts(sts); }
11380     if (!(sts & 1)) return 0;  /* out here in case _ckvmssts() doesn't abort */
11381
11382     if ((int) owner.length  < lowner)  lowner=  (int) owner.length;
11383     if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11384     if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11385     if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11386     memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11387     owner.pw_gecos[lowner]=            '\0';
11388     defdev.pw_dir[ldefdev+ldefdir]= '\0';
11389     defcli.pw_shell[ldefcli]=          '\0';
11390     if (valid_uic(uic)) {
11391         pwd->pw_uid= uic.uic$l_uic;
11392         pwd->pw_gid= uic.uic$v_group;
11393     }
11394     else
11395       Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11396     pwd->pw_passwd=  pw_passwd;
11397     pwd->pw_gecos=   owner.pw_gecos;
11398     pwd->pw_dir=     defdev.pw_dir;
11399     pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11400     pwd->pw_shell=   defcli.pw_shell;
11401     if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11402         int ldir;
11403         ldir= strlen(pwd->pw_unixdir) - 1;
11404         if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11405     }
11406     else
11407         my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11408     if (!DECC_EFS_CASE_PRESERVE)
11409         __mystrtolower(pwd->pw_unixdir);
11410     return 1;
11411 }
11412
11413 /*
11414  * Get information for a named user.
11415 */
11416 /*{{{struct passwd *getpwnam(char *name)*/
11417 struct passwd *
11418 Perl_my_getpwnam(pTHX_ const char *name)
11419 {
11420     struct dsc$descriptor_s name_desc;
11421     union uicdef uic;
11422     unsigned long int sts;
11423                                   
11424     __pwdcache = __passwd_empty;
11425     if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11426       /* We still may be able to determine pw_uid and pw_gid */
11427       name_desc.dsc$w_length=  strlen(name);
11428       name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11429       name_desc.dsc$b_class=   DSC$K_CLASS_S;
11430       name_desc.dsc$a_pointer= (char *) name;
11431       if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11432         __pwdcache.pw_uid= uic.uic$l_uic;
11433         __pwdcache.pw_gid= uic.uic$v_group;
11434       }
11435       else {
11436         if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11437           set_vaxc_errno(sts);
11438           set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11439           return NULL;
11440         }
11441         else { _ckvmssts(sts); }
11442       }
11443     }
11444     my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11445     __pwdcache.pw_name= __pw_namecache;
11446     return &__pwdcache;
11447 }  /* end of my_getpwnam() */
11448 /*}}}*/
11449
11450 /*
11451  * Get information for a particular UIC or UID.
11452  * Called by my_getpwent with uid=-1 to list all users.
11453 */
11454 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11455 struct passwd *
11456 Perl_my_getpwuid(pTHX_ Uid_t uid)
11457 {
11458     const $DESCRIPTOR(name_desc,__pw_namecache);
11459     unsigned short lname;
11460     union uicdef uic;
11461     unsigned long int status;
11462
11463     if (uid == (unsigned int) -1) {
11464       do {
11465         status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11466         if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11467           set_vaxc_errno(status);
11468           set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11469           my_endpwent();
11470           return NULL;
11471         }
11472         else { _ckvmssts(status); }
11473       } while (!valid_uic (uic));
11474     }
11475     else {
11476       uic.uic$l_uic= uid;
11477       if (!uic.uic$v_group)
11478         uic.uic$v_group= PerlProc_getgid();
11479       if (valid_uic(uic))
11480         status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11481       else status = SS$_IVIDENT;
11482       if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11483           status == RMS$_PRV) {
11484         set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11485         return NULL;
11486       }
11487       else { _ckvmssts(status); }
11488     }
11489     __pw_namecache[lname]= '\0';
11490     __mystrtolower(__pw_namecache);
11491
11492     __pwdcache = __passwd_empty;
11493     __pwdcache.pw_name = __pw_namecache;
11494
11495 /*  Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11496     The identifier's value is usually the UIC, but it doesn't have to be,
11497     so if we can, we let fillpasswd update this. */
11498     __pwdcache.pw_uid =  uic.uic$l_uic;
11499     __pwdcache.pw_gid =  uic.uic$v_group;
11500
11501     fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11502     return &__pwdcache;
11503
11504 }  /* end of my_getpwuid() */
11505 /*}}}*/
11506
11507 /*
11508  * Get information for next user.
11509 */
11510 /*{{{struct passwd *my_getpwent()*/
11511 struct passwd *
11512 Perl_my_getpwent(pTHX)
11513 {
11514     return (my_getpwuid((unsigned int) -1));
11515 }
11516 /*}}}*/
11517
11518 /*
11519  * Finish searching rights database for users.
11520 */
11521 /*{{{void my_endpwent()*/
11522 void
11523 Perl_my_endpwent(pTHX)
11524 {
11525     if (contxt) {
11526       _ckvmssts(sys$finish_rdb(&contxt));
11527       contxt= 0;
11528     }
11529 }
11530 /*}}}*/
11531
11532 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11533  * my_utime(), and flex_stat(), all of which operate on UTC unless
11534  * VMSISH_TIMES is true.
11535  */
11536 /* method used to handle UTC conversions:
11537  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
11538  */
11539 static int gmtime_emulation_type;
11540 /* number of secs to add to UTC POSIX-style time to get local time */
11541 static long int utc_offset_secs;
11542
11543 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11544  * in vmsish.h.  #undef them here so we can call the CRTL routines
11545  * directly.
11546  */
11547 #undef gmtime
11548 #undef localtime
11549 #undef time
11550
11551
11552 static time_t toutc_dst(time_t loc) {
11553   struct tm *rsltmp;
11554
11555   if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11556   loc -= utc_offset_secs;
11557   if (rsltmp->tm_isdst) loc -= 3600;
11558   return loc;
11559 }
11560 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11561        ((gmtime_emulation_type || my_time(NULL)), \
11562        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11563        ((secs) - utc_offset_secs))))
11564
11565 static time_t toloc_dst(time_t utc) {
11566   struct tm *rsltmp;
11567
11568   utc += utc_offset_secs;
11569   if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11570   if (rsltmp->tm_isdst) utc += 3600;
11571   return utc;
11572 }
11573 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11574        ((gmtime_emulation_type || my_time(NULL)), \
11575        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11576        ((secs) + utc_offset_secs))))
11577
11578 /* my_time(), my_localtime(), my_gmtime()
11579  * By default traffic in UTC time values, using CRTL gmtime() or
11580  * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11581  * Note: We need to use these functions even when the CRTL has working
11582  * UTC support, since they also handle C<use vmsish qw(times);>
11583  *
11584  * Contributed by Chuck Lane  <lane@duphy4.physics.drexel.edu>
11585  * Modified by Charles Bailey <bailey@newman.upenn.edu>
11586  */
11587
11588 /*{{{time_t my_time(time_t *timep)*/
11589 time_t
11590 Perl_my_time(pTHX_ time_t *timep)
11591 {
11592   time_t when;
11593   struct tm *tm_p;
11594
11595   if (gmtime_emulation_type == 0) {
11596     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
11597                               /* results of calls to gmtime() and localtime() */
11598                               /* for same &base */
11599
11600     gmtime_emulation_type++;
11601     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11602       char off[LNM$C_NAMLENGTH+1];;
11603
11604       gmtime_emulation_type++;
11605       if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11606         gmtime_emulation_type++;
11607         utc_offset_secs = 0;
11608         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11609       }
11610       else { utc_offset_secs = atol(off); }
11611     }
11612     else { /* We've got a working gmtime() */
11613       struct tm gmt, local;
11614
11615       gmt = *tm_p;
11616       tm_p = localtime(&base);
11617       local = *tm_p;
11618       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
11619       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11620       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
11621       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
11622     }
11623   }
11624
11625   when = time(NULL);
11626 # ifdef VMSISH_TIME
11627   if (VMSISH_TIME) when = _toloc(when);
11628 # endif
11629   if (timep != NULL) *timep = when;
11630   return when;
11631
11632 }  /* end of my_time() */
11633 /*}}}*/
11634
11635
11636 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11637 struct tm *
11638 Perl_my_gmtime(pTHX_ const time_t *timep)
11639 {
11640   time_t when;
11641   struct tm *rsltmp;
11642
11643   if (timep == NULL) {
11644     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11645     return NULL;
11646   }
11647   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
11648
11649   when = *timep;
11650 # ifdef VMSISH_TIME
11651   if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11652 #  endif
11653   return gmtime(&when);
11654 }  /* end of my_gmtime() */
11655 /*}}}*/
11656
11657
11658 /*{{{struct tm *my_localtime(const time_t *timep)*/
11659 struct tm *
11660 Perl_my_localtime(pTHX_ const time_t *timep)
11661 {
11662   time_t when;
11663
11664   if (timep == NULL) {
11665     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11666     return NULL;
11667   }
11668   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
11669   if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11670
11671   when = *timep;
11672 # ifdef VMSISH_TIME
11673   if (VMSISH_TIME) when = _toutc(when);
11674 # endif
11675   /* CRTL localtime() wants UTC as input, does tz correction itself */
11676   return localtime(&when);
11677 } /*  end of my_localtime() */
11678 /*}}}*/
11679
11680 /* Reset definitions for later calls */
11681 #define gmtime(t)    my_gmtime(t)
11682 #define localtime(t) my_localtime(t)
11683 #define time(t)      my_time(t)
11684
11685
11686 /* my_utime - update modification/access time of a file
11687  *
11688  * Only the UTC translation is home-grown. The rest is handled by the
11689  * CRTL utime(), which will take into account the relevant feature
11690  * logicals and ODS-5 volume characteristics for true access times.
11691  *
11692  */
11693
11694 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11695  *              to VMS epoch  (01-JAN-1858 00:00:00.00)
11696  * in 100 ns intervals.
11697  */
11698 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11699
11700 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11701 int
11702 Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11703 {
11704   struct utimbuf utc_utimes, *utc_utimesp;
11705
11706   if (utimes != NULL) {
11707     utc_utimes.actime = utimes->actime;
11708     utc_utimes.modtime = utimes->modtime;
11709 # ifdef VMSISH_TIME
11710     /* If input was local; convert to UTC for sys svc */
11711     if (VMSISH_TIME) {
11712       utc_utimes.actime = _toutc(utimes->actime);
11713       utc_utimes.modtime = _toutc(utimes->modtime);
11714     }
11715 # endif
11716     utc_utimesp = &utc_utimes;
11717   }
11718   else {
11719     utc_utimesp = NULL;
11720   }
11721
11722   return utime(file, utc_utimesp);
11723
11724 }  /* end of my_utime() */
11725 /*}}}*/
11726
11727 /*
11728  * flex_stat, flex_lstat, flex_fstat
11729  * basic stat, but gets it right when asked to stat
11730  * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11731  */
11732
11733 #ifndef _USE_STD_STAT
11734 /* encode_dev packs a VMS device name string into an integer to allow
11735  * simple comparisons. This can be used, for example, to check whether two
11736  * files are located on the same device, by comparing their encoded device
11737  * names. Even a string comparison would not do, because stat() reuses the
11738  * device name buffer for each call; so without encode_dev, it would be
11739  * necessary to save the buffer and use strcmp (this would mean a number of
11740  * changes to the standard Perl code, to say nothing of what a Perl script
11741  * would have to do.
11742  *
11743  * The device lock id, if it exists, should be unique (unless perhaps compared
11744  * with lock ids transferred from other nodes). We have a lock id if the disk is
11745  * mounted cluster-wide, which is when we tend to get long (host-qualified)
11746  * device names. Thus we use the lock id in preference, and only if that isn't
11747  * available, do we try to pack the device name into an integer (flagged by
11748  * the sign bit (LOCKID_MASK) being set).
11749  *
11750  * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11751  * name and its encoded form, but it seems very unlikely that we will find
11752  * two files on different disks that share the same encoded device names,
11753  * and even more remote that they will share the same file id (if the test
11754  * is to check for the same file).
11755  *
11756  * A better method might be to use sys$device_scan on the first call, and to
11757  * search for the device, returning an index into the cached array.
11758  * The number returned would be more intelligible.
11759  * This is probably not worth it, and anyway would take quite a bit longer
11760  * on the first call.
11761  */
11762 #define LOCKID_MASK 0x80000000     /* Use 0 to force device name use only */
11763 static mydev_t
11764 encode_dev (pTHX_ const char *dev)
11765 {
11766   int i;
11767   unsigned long int f;
11768   mydev_t enc;
11769   char c;
11770   const char *q;
11771
11772   if (!dev || !dev[0]) return 0;
11773
11774 #if LOCKID_MASK
11775   {
11776     struct dsc$descriptor_s dev_desc;
11777     unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11778
11779     /* For cluster-mounted disks, the disk lock identifier is unique, so we
11780        can try that first. */
11781     dev_desc.dsc$w_length =  strlen (dev);
11782     dev_desc.dsc$b_dtype =   DSC$K_DTYPE_T;
11783     dev_desc.dsc$b_class =   DSC$K_CLASS_S;
11784     dev_desc.dsc$a_pointer = (char *) dev;  /* Read only parameter */
11785     status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11786     if (!$VMS_STATUS_SUCCESS(status)) {
11787       switch (status) {
11788         case SS$_NOSUCHDEV: 
11789           SETERRNO(ENODEV, status);
11790           return 0;
11791         default: 
11792           _ckvmssts(status);
11793       }
11794     }
11795     if (lockid) return (lockid & ~LOCKID_MASK);
11796   }
11797 #endif
11798
11799   /* Otherwise we try to encode the device name */
11800   enc = 0;
11801   f = 1;
11802   i = 0;
11803   for (q = dev + strlen(dev); q--; q >= dev) {
11804     if (*q == ':')
11805         break;
11806     if (isdigit (*q))
11807       c= (*q) - '0';
11808     else if (isALPHA_A(toUPPER_A(*q)))
11809       c= toupper (*q) - 'A' + (char)10;
11810     else
11811       continue; /* Skip '$'s */
11812     i++;
11813     if (i>6) break;     /* 36^7 is too large to fit in an unsigned long int */
11814     if (i>1) f *= 36;
11815     enc += f * (unsigned long int) c;
11816   }
11817   return (enc | LOCKID_MASK);  /* May have already overflowed into bit 31 */
11818
11819 }  /* end of encode_dev() */
11820 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11821         device_no = encode_dev(aTHX_ devname)
11822 #else
11823 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11824         device_no = new_dev_no
11825 #endif
11826
11827 static int
11828 is_null_device(const char *name)
11829 {
11830   if (decc_bug_devnull != 0) {
11831     if (strBEGINs(name, "/dev/null"))
11832       return 1;
11833   }
11834     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11835        The underscore prefix, controller letter, and unit number are
11836        independently optional; for our purposes, the colon punctuation
11837        is not.  The colon can be trailed by optional directory and/or
11838        filename, but two consecutive colons indicates a nodename rather
11839        than a device.  [pr]  */
11840   if (*name == '_') ++name;
11841   if (toLOWER_L1(*name++) != 'n') return 0;
11842   if (toLOWER_L1(*name++) != 'l') return 0;
11843   if (toLOWER_L1(*name) == 'a') ++name;
11844   if (*name == '0') ++name;
11845   return (*name++ == ':') && (*name != ':');
11846 }
11847
11848 static int
11849 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11850
11851 #define flex_stat_int(a,b,c)            Perl_flex_stat_int(aTHX_ a,b,c)
11852
11853 static I32
11854 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective, const char *fname, int opts)
11855 {
11856   char usrname[L_cuserid];
11857   struct dsc$descriptor_s usrdsc =
11858          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11859   char *vmsname = NULL, *fileified = NULL;
11860   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11861   unsigned short int retlen, trnlnm_iter_count;
11862   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11863   union prvdef curprv;
11864   struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11865          {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11866          {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11867   struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11868          {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11869          {0,0,0,0}};
11870   struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11871          {0,0,0,0}};
11872   struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11873   Stat_t st;
11874   static int profile_context = -1;
11875
11876   if (!fname || !*fname) return FALSE;
11877
11878   /* Make sure we expand logical names, since sys$check_access doesn't */
11879   fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11880   if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11881   if (!strpbrk(fname,"/]>:")) {
11882       my_strlcpy(fileified, fname, VMS_MAXRSS);
11883       trnlnm_iter_count = 0;
11884       while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11885         trnlnm_iter_count++; 
11886         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11887       }
11888       fname = fileified;
11889   }
11890
11891   vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11892   if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11893   if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11894     /* Don't know if already in VMS format, so make sure */
11895     if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11896       PerlMem_free(fileified);
11897       PerlMem_free(vmsname);
11898       return FALSE;
11899     }
11900   }
11901   else {
11902     my_strlcpy(vmsname, fname, VMS_MAXRSS);
11903   }
11904
11905   /* sys$check_access needs a file spec, not a directory spec.
11906    * flex_stat now will handle a null thread context during startup.
11907    */
11908
11909   retlen = namdsc.dsc$w_length = strlen(vmsname);
11910   if (vmsname[retlen-1] == ']' 
11911       || vmsname[retlen-1] == '>' 
11912       || vmsname[retlen-1] == ':'
11913       || (!flex_stat_int(vmsname, &st, 1) &&
11914           S_ISDIR(st.st_mode))) {
11915
11916       if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11917         PerlMem_free(fileified);
11918         PerlMem_free(vmsname);
11919         return FALSE;
11920       }
11921       fname = fileified;
11922   }
11923   else {
11924       fname = vmsname;
11925   }
11926
11927   retlen = namdsc.dsc$w_length = strlen(fname);
11928   namdsc.dsc$a_pointer = (char *)fname;
11929
11930   switch (bit) {
11931     case S_IXUSR: case S_IXGRP: case S_IXOTH:
11932       access = ARM$M_EXECUTE;
11933       flags = CHP$M_READ;
11934       break;
11935     case S_IRUSR: case S_IRGRP: case S_IROTH:
11936       access = ARM$M_READ;
11937       flags = CHP$M_READ | CHP$M_USEREADALL;
11938       break;
11939     case S_IWUSR: case S_IWGRP: case S_IWOTH:
11940       access = ARM$M_WRITE;
11941       flags = CHP$M_READ | CHP$M_WRITE;
11942       break;
11943     case S_IDUSR: case S_IDGRP: case S_IDOTH:
11944       access = ARM$M_DELETE;
11945       flags = CHP$M_READ | CHP$M_WRITE;
11946       break;
11947     default:
11948       if (fileified != NULL)
11949         PerlMem_free(fileified);
11950       if (vmsname != NULL)
11951         PerlMem_free(vmsname);
11952       return FALSE;
11953   }
11954
11955   /* Before we call $check_access, create a user profile with the current
11956    * process privs since otherwise it just uses the default privs from the
11957    * UAF and might give false positives or negatives.  This only works on
11958    * VMS versions v6.0 and later since that's when sys$create_user_profile
11959    * became available.
11960    */
11961
11962   /* get current process privs and username */
11963   _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11964   _ckvmssts_noperl(iosb[0]);
11965
11966   /* find out the space required for the profile */
11967   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11968                                     &usrprodsc.dsc$w_length,&profile_context));
11969
11970   /* allocate space for the profile and get it filled in */
11971   usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
11972   if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11973   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11974                                     &usrprodsc.dsc$w_length,&profile_context));
11975
11976   /* use the profile to check access to the file; free profile & analyze results */
11977   retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11978   PerlMem_free(usrprodsc.dsc$a_pointer);
11979   if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11980
11981   if (retsts == SS$_NOPRIV      || retsts == SS$_NOSUCHOBJECT ||
11982       retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11983       retsts == RMS$_DIR        || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11984     set_vaxc_errno(retsts);
11985     if (retsts == SS$_NOPRIV) set_errno(EACCES);
11986     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11987     else set_errno(ENOENT);
11988     if (fileified != NULL)
11989       PerlMem_free(fileified);
11990     if (vmsname != NULL)
11991       PerlMem_free(vmsname);
11992     return FALSE;
11993   }
11994   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11995     if (fileified != NULL)
11996       PerlMem_free(fileified);
11997     if (vmsname != NULL)
11998       PerlMem_free(vmsname);
11999     return TRUE;
12000   }
12001   _ckvmssts_noperl(retsts);
12002
12003   if (fileified != NULL)
12004     PerlMem_free(fileified);
12005   if (vmsname != NULL)
12006     PerlMem_free(vmsname);
12007   return FALSE;  /* Should never get here */
12008
12009 }
12010
12011 /* Do the permissions in *statbufp allow some operation? */
12012 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12013  * subset of the applicable information.
12014  */
12015 bool
12016 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12017 {
12018   return cando_by_name_int
12019         (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12020 }  /* end of cando() */
12021 /*}}}*/
12022
12023
12024 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12025 I32
12026 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12027 {
12028    return cando_by_name_int(bit, effective, fname, 0);
12029
12030 }  /* end of cando_by_name() */
12031 /*}}}*/
12032
12033
12034 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12035 int
12036 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12037 {
12038   dSAVE_ERRNO; /* fstat may set this even on success */
12039   if (!fstat(fd, &statbufp->crtl_stat)) {
12040     char *cptr;
12041     char *vms_filename;
12042     vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12043     if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12044
12045     /* Save name for cando by name in VMS format */
12046     cptr = getname(fd, vms_filename, 1);
12047
12048     /* This should not happen, but just in case */
12049     if (cptr == NULL) {
12050         statbufp->st_devnam[0] = 0;
12051     }
12052     else {
12053         /* Make sure that the saved name fits in 255 characters */
12054         cptr = int_rmsexpand_vms
12055                        (vms_filename,
12056                         statbufp->st_devnam, 
12057                         0);
12058         if (cptr == NULL)
12059             statbufp->st_devnam[0] = 0;
12060     }
12061     PerlMem_free(vms_filename);
12062
12063     VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12064     VMS_DEVICE_ENCODE
12065         (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12066
12067 #   ifdef VMSISH_TIME
12068     if (VMSISH_TIME) {
12069       statbufp->st_mtime = _toloc(statbufp->st_mtime);
12070       statbufp->st_atime = _toloc(statbufp->st_atime);
12071       statbufp->st_ctime = _toloc(statbufp->st_ctime);
12072     }
12073 #   endif
12074     RESTORE_ERRNO;
12075     return 0;
12076   }
12077   return -1;
12078
12079 }  /* end of flex_fstat() */
12080 /*}}}*/
12081
12082 static int
12083 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12084 {
12085     char *temp_fspec = NULL;
12086     char *fileified = NULL;
12087     const char *save_spec;
12088     char *ret_spec;
12089     int retval = -1;
12090     char efs_hack = 0;
12091     char already_fileified = 0;
12092     dSAVEDERRNO;
12093
12094     if (!fspec) {
12095         errno = EINVAL;
12096         return retval;
12097     }
12098
12099     if (decc_bug_devnull != 0) {
12100       if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12101         memset(statbufp,0,sizeof *statbufp);
12102         VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12103         statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12104         statbufp->st_uid = 0x00010001;
12105         statbufp->st_gid = 0x0001;
12106         time((time_t *)&statbufp->st_mtime);
12107         statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12108         return 0;
12109       }
12110     }
12111
12112     SAVE_ERRNO;
12113
12114 #if __CRTL_VER >= 80200000
12115   /*
12116    * If we are in POSIX filespec mode, accept the filename as is.
12117    */
12118   if (!DECC_POSIX_COMPLIANT_PATHNAMES) {
12119 #endif
12120
12121     /* Try for a simple stat first.  If fspec contains a filename without
12122      * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12123      * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12124      * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12125      * not sea:[wine.dark]., if the latter exists.  If the intended target is
12126      * the file with null type, specify this by calling flex_stat() with
12127      * a '.' at the end of fspec.
12128      */
12129
12130     if (lstat_flag == 0)
12131         retval = stat(fspec, &statbufp->crtl_stat);
12132     else
12133         retval = lstat(fspec, &statbufp->crtl_stat);
12134
12135     if (!retval) {
12136         save_spec = fspec;
12137     }
12138     else {
12139         /* In the odd case where we have write but not read access
12140          * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12141          */
12142         fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12143         if (fileified == NULL)
12144               _ckvmssts_noperl(SS$_INSFMEM);
12145
12146         ret_spec = int_fileify_dirspec(fspec, fileified, NULL); 
12147         if (ret_spec != NULL) {
12148             if (lstat_flag == 0)
12149                 retval = stat(fileified, &statbufp->crtl_stat);
12150             else
12151                 retval = lstat(fileified, &statbufp->crtl_stat);
12152             save_spec = fileified;
12153             already_fileified = 1;
12154         }
12155     }
12156
12157     if (retval && vms_bug_stat_filename) {
12158
12159         temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12160         if (temp_fspec == NULL)
12161             _ckvmssts_noperl(SS$_INSFMEM);
12162
12163         /* We should try again as a vmsified file specification. */
12164
12165         ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12166         if (ret_spec != NULL) {
12167             if (lstat_flag == 0)
12168                 retval = stat(temp_fspec, &statbufp->crtl_stat);
12169             else
12170                 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12171             save_spec = temp_fspec;
12172         }
12173     }
12174
12175     if (retval) {
12176         /* Last chance - allow multiple dots without EFS CHARSET */
12177         /* The CRTL stat() falls down hard on multi-dot filenames in unix
12178          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12179          * enable it if it isn't already.
12180          */
12181         if (!DECC_EFS_CHARSET && (efs_charset_index > 0))
12182             decc$feature_set_value(efs_charset_index, 1, 1);
12183         if (lstat_flag == 0)
12184             retval = stat(fspec, &statbufp->crtl_stat);
12185         else
12186             retval = lstat(fspec, &statbufp->crtl_stat);
12187         save_spec = fspec;
12188         if (!DECC_EFS_CHARSET && (efs_charset_index > 0)) {
12189             decc$feature_set_value(efs_charset_index, 1, 0);
12190             efs_hack = 1;
12191         }
12192     }
12193
12194 #if __CRTL_VER >= 80200000
12195   } else {
12196     if (lstat_flag == 0)
12197       retval = stat(temp_fspec, &statbufp->crtl_stat);
12198     else
12199       retval = lstat(temp_fspec, &statbufp->crtl_stat);
12200       save_spec = temp_fspec;
12201   }
12202 #endif
12203
12204   /* As you were... */
12205   if (!DECC_EFS_CHARSET)
12206     decc$feature_set_value(efs_charset_index,1,0);
12207
12208     if (!retval) {
12209       char *cptr;
12210       int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12211
12212       /* If this is an lstat, do not follow the link */
12213       if (lstat_flag)
12214         rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12215
12216       /* If we used the efs_hack above, we must also use it here for */
12217       /* perl_cando to work */
12218       if (efs_hack && (efs_charset_index > 0)) {
12219           decc$feature_set_value(efs_charset_index, 1, 1);
12220       }
12221
12222       /* If we've got a directory, save a fileified, expanded version of it
12223        * in st_devnam.  If not a directory, just an expanded version.
12224        */
12225       if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12226           fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12227           if (fileified == NULL)
12228               _ckvmssts_noperl(SS$_INSFMEM);
12229
12230           cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12231           if (cptr != NULL)
12232               save_spec = fileified;
12233       }
12234
12235       cptr = int_rmsexpand(save_spec, 
12236                            statbufp->st_devnam,
12237                            NULL,
12238                            rmsex_flags,
12239                            0,
12240                            0);
12241
12242       if (efs_hack && (efs_charset_index > 0)) {
12243           decc$feature_set_value(efs_charset_index, 1, 0);
12244       }
12245
12246       /* Fix me: If this is NULL then stat found a file, and we could */
12247       /* not convert the specification to VMS - Should never happen */
12248       if (cptr == NULL)
12249         statbufp->st_devnam[0] = 0;
12250
12251       VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12252       VMS_DEVICE_ENCODE
12253         (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12254 #     ifdef VMSISH_TIME
12255       if (VMSISH_TIME) {
12256         statbufp->st_mtime = _toloc(statbufp->st_mtime);
12257         statbufp->st_atime = _toloc(statbufp->st_atime);
12258         statbufp->st_ctime = _toloc(statbufp->st_ctime);
12259       }
12260 #     endif
12261     }
12262     /* If we were successful, leave errno where we found it */
12263     if (retval == 0) RESTORE_ERRNO;
12264     if (temp_fspec)
12265         PerlMem_free(temp_fspec);
12266     if (fileified)
12267         PerlMem_free(fileified);
12268     return retval;
12269
12270 }  /* end of flex_stat_int() */
12271
12272
12273 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12274 int
12275 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12276 {
12277    return flex_stat_int(fspec, statbufp, 0);
12278 }
12279 /*}}}*/
12280
12281 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12282 int
12283 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12284 {
12285    return flex_stat_int(fspec, statbufp, 1);
12286 }
12287 /*}}}*/
12288
12289
12290 /*  rmscopy - copy a file using VMS RMS routines
12291  *
12292  *  Copies contents and attributes of spec_in to spec_out, except owner
12293  *  and protection information.  Name and type of spec_in are used as
12294  *  defaults for spec_out.  The third parameter specifies whether rmscopy()
12295  *  should try to propagate timestamps from the input file to the output file.
12296  *  If it is less than 0, no timestamps are preserved.  If it is 0, then
12297  *  rmscopy() will behave similarly to the DCL COPY command: timestamps are
12298  *  propagated to the output file at creation iff the output file specification
12299  *  did not contain an explicit name or type, and the revision date is always
12300  *  updated at the end of the copy operation.  If it is greater than 0, then
12301  *  it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12302  *  other than the revision date should be propagated, and bit 1 indicates
12303  *  that the revision date should be propagated.
12304  *
12305  *  Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12306  *
12307  *  Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12308  *  Incorporates, with permission, some code from EZCOPY by Tim Adye
12309  *  <T.J.Adye@rl.ac.uk>.  Permission is given to distribute this code
12310  * as part of the Perl standard distribution under the terms of the
12311  * GNU General Public License or the Perl Artistic License.  Copies
12312  * of each may be found in the Perl standard distribution.
12313  */ /* FIXME */
12314 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12315 int
12316 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12317 {
12318     char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12319          *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12320     unsigned long int sts;
12321     int dna_len;
12322     struct FAB fab_in, fab_out;
12323     struct RAB rab_in, rab_out;
12324     rms_setup_nam(nam);
12325     rms_setup_nam(nam_out);
12326     struct XABDAT xabdat;
12327     struct XABFHC xabfhc;
12328     struct XABRDT xabrdt;
12329     struct XABSUM xabsum;
12330
12331     vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12332     if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12333     vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12334     if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12335     if (!spec_in  || !*spec_in  || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12336         !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12337       PerlMem_free(vmsin);
12338       PerlMem_free(vmsout);
12339       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12340       return 0;
12341     }
12342
12343     esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12344     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12345     esal = NULL;
12346 #if defined(NAML$C_MAXRSS)
12347     esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12348     if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12349 #endif
12350     fab_in = cc$rms_fab;
12351     rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12352     fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12353     fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12354     fab_in.fab$l_fop = FAB$M_SQO;
12355     rms_bind_fab_nam(fab_in, nam);
12356     fab_in.fab$l_xab = (void *) &xabdat;
12357
12358     rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12359     if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12360     rsal = NULL;
12361 #if defined(NAML$C_MAXRSS)
12362     rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12363     if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12364 #endif
12365     rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12366     rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12367     rms_nam_esl(nam) = 0;
12368     rms_nam_rsl(nam) = 0;
12369     rms_nam_esll(nam) = 0;
12370     rms_nam_rsll(nam) = 0;
12371 #ifdef NAM$M_NO_SHORT_UPCASE
12372     if (DECC_EFS_CASE_PRESERVE)
12373         rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12374 #endif
12375
12376     xabdat = cc$rms_xabdat;        /* To get creation date */
12377     xabdat.xab$l_nxt = (void *) &xabfhc;
12378
12379     xabfhc = cc$rms_xabfhc;        /* To get record length */
12380     xabfhc.xab$l_nxt = (void *) &xabsum;
12381
12382     xabsum = cc$rms_xabsum;        /* To get key and area information */
12383
12384     if (!((sts = sys$open(&fab_in)) & 1)) {
12385       PerlMem_free(vmsin);
12386       PerlMem_free(vmsout);
12387       PerlMem_free(esa);
12388       if (esal != NULL)
12389         PerlMem_free(esal);
12390       PerlMem_free(rsa);
12391       if (rsal != NULL)
12392         PerlMem_free(rsal);
12393       set_vaxc_errno(sts);
12394       switch (sts) {
12395         case RMS$_FNF: case RMS$_DNF:
12396           set_errno(ENOENT); break;
12397         case RMS$_DIR:
12398           set_errno(ENOTDIR); break;
12399         case RMS$_DEV:
12400           set_errno(ENODEV); break;
12401         case RMS$_SYN:
12402           set_errno(EINVAL); break;
12403         case RMS$_PRV:
12404           set_errno(EACCES); break;
12405         default:
12406           set_errno(EVMSERR);
12407       }
12408       return 0;
12409     }
12410
12411     nam_out = nam;
12412     fab_out = fab_in;
12413     fab_out.fab$w_ifi = 0;
12414     fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12415     fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12416     fab_out.fab$l_fop = FAB$M_SQO;
12417     rms_bind_fab_nam(fab_out, nam_out);
12418     rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12419     dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12420     rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12421     esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12422     if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12423     rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12424     if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12425     esal_out = NULL;
12426     rsal_out = NULL;
12427 #if defined(NAML$C_MAXRSS)
12428     esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12429     if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12430     rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12431     if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12432 #endif
12433     rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12434     rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12435
12436     if (preserve_dates == 0) {  /* Act like DCL COPY */
12437       rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12438       fab_out.fab$l_xab = NULL;  /* Don't disturb data from input file */
12439       if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12440         PerlMem_free(vmsin);
12441         PerlMem_free(vmsout);
12442         PerlMem_free(esa);
12443         if (esal != NULL)
12444             PerlMem_free(esal);
12445         PerlMem_free(rsa);
12446         if (rsal != NULL)
12447             PerlMem_free(rsal);
12448         PerlMem_free(esa_out);
12449         if (esal_out != NULL)
12450             PerlMem_free(esal_out);
12451         PerlMem_free(rsa_out);
12452         if (rsal_out != NULL)
12453             PerlMem_free(rsal_out);
12454         set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12455         set_vaxc_errno(sts);
12456         return 0;
12457       }
12458       fab_out.fab$l_xab = (void *) &xabdat;
12459       if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12460         preserve_dates = 1;
12461     }
12462     if (preserve_dates < 0)   /* Clear all bits; we'll use it as a */
12463       preserve_dates =0;      /* bitmask from this point forward   */
12464
12465     if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12466     if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12467       PerlMem_free(vmsin);
12468       PerlMem_free(vmsout);
12469       PerlMem_free(esa);
12470       if (esal != NULL)
12471           PerlMem_free(esal);
12472       PerlMem_free(rsa);
12473       if (rsal != NULL)
12474           PerlMem_free(rsal);
12475       PerlMem_free(esa_out);
12476       if (esal_out != NULL)
12477           PerlMem_free(esal_out);
12478       PerlMem_free(rsa_out);
12479       if (rsal_out != NULL)
12480           PerlMem_free(rsal_out);
12481       set_vaxc_errno(sts);
12482       switch (sts) {
12483         case RMS$_DNF:
12484           set_errno(ENOENT); break;
12485         case RMS$_DIR:
12486           set_errno(ENOTDIR); break;
12487         case RMS$_DEV:
12488           set_errno(ENODEV); break;
12489         case RMS$_SYN:
12490           set_errno(EINVAL); break;
12491         case RMS$_PRV:
12492           set_errno(EACCES); break;
12493         default:
12494           set_errno(EVMSERR);
12495       }
12496       return 0;
12497     }
12498     fab_out.fab$l_fop |= FAB$M_DLT;  /* in case we have to bail out */
12499     if (preserve_dates & 2) {
12500       /* sys$close() will process xabrdt, not xabdat */
12501       xabrdt = cc$rms_xabrdt;
12502       xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12503       fab_out.fab$l_xab = (void *) &xabrdt;
12504     }
12505
12506     ubf = (char *)PerlMem_malloc(32256);
12507     if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12508     rab_in = cc$rms_rab;
12509     rab_in.rab$l_fab = &fab_in;
12510     rab_in.rab$l_rop = RAB$M_BIO;
12511     rab_in.rab$l_ubf = ubf;
12512     rab_in.rab$w_usz = 32256;
12513     if (!((sts = sys$connect(&rab_in)) & 1)) {
12514       sys$close(&fab_in); sys$close(&fab_out);
12515       PerlMem_free(vmsin);
12516       PerlMem_free(vmsout);
12517       PerlMem_free(ubf);
12518       PerlMem_free(esa);
12519       if (esal != NULL)
12520           PerlMem_free(esal);
12521       PerlMem_free(rsa);
12522       if (rsal != NULL)
12523           PerlMem_free(rsal);
12524       PerlMem_free(esa_out);
12525       if (esal_out != NULL)
12526           PerlMem_free(esal_out);
12527       PerlMem_free(rsa_out);
12528       if (rsal_out != NULL)
12529           PerlMem_free(rsal_out);
12530       set_errno(EVMSERR); set_vaxc_errno(sts);
12531       return 0;
12532     }
12533
12534     rab_out = cc$rms_rab;
12535     rab_out.rab$l_fab = &fab_out;
12536     rab_out.rab$l_rbf = ubf;
12537     if (!((sts = sys$connect(&rab_out)) & 1)) {
12538       sys$close(&fab_in); sys$close(&fab_out);
12539       PerlMem_free(vmsin);
12540       PerlMem_free(vmsout);
12541       PerlMem_free(ubf);
12542       PerlMem_free(esa);
12543       if (esal != NULL)
12544           PerlMem_free(esal);
12545       PerlMem_free(rsa);
12546       if (rsal != NULL)
12547           PerlMem_free(rsal);
12548       PerlMem_free(esa_out);
12549       if (esal_out != NULL)
12550           PerlMem_free(esal_out);
12551       PerlMem_free(rsa_out);
12552       if (rsal_out != NULL)
12553           PerlMem_free(rsal_out);
12554       set_errno(EVMSERR); set_vaxc_errno(sts);
12555       return 0;
12556     }
12557
12558     while ((sts = sys$read(&rab_in))) {  /* always true  */
12559       if (sts == RMS$_EOF) break;
12560       rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12561       if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12562         sys$close(&fab_in); sys$close(&fab_out);
12563         PerlMem_free(vmsin);
12564         PerlMem_free(vmsout);
12565         PerlMem_free(ubf);
12566         PerlMem_free(esa);
12567         if (esal != NULL)
12568             PerlMem_free(esal);
12569         PerlMem_free(rsa);
12570         if (rsal != NULL)
12571             PerlMem_free(rsal);
12572         PerlMem_free(esa_out);
12573         if (esal_out != NULL)
12574             PerlMem_free(esal_out);
12575         PerlMem_free(rsa_out);
12576         if (rsal_out != NULL)
12577             PerlMem_free(rsal_out);
12578         set_errno(EVMSERR); set_vaxc_errno(sts);
12579         return 0;
12580       }
12581     }
12582
12583
12584     fab_out.fab$l_fop &= ~FAB$M_DLT;  /* We got this far; keep the output */
12585     sys$close(&fab_in);  sys$close(&fab_out);
12586     sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12587
12588     PerlMem_free(vmsin);
12589     PerlMem_free(vmsout);
12590     PerlMem_free(ubf);
12591     PerlMem_free(esa);
12592     if (esal != NULL)
12593         PerlMem_free(esal);
12594     PerlMem_free(rsa);
12595     if (rsal != NULL)
12596         PerlMem_free(rsal);
12597     PerlMem_free(esa_out);
12598     if (esal_out != NULL)
12599         PerlMem_free(esal_out);
12600     PerlMem_free(rsa_out);
12601     if (rsal_out != NULL)
12602         PerlMem_free(rsal_out);
12603
12604     if (!(sts & 1)) {
12605       set_errno(EVMSERR); set_vaxc_errno(sts);
12606       return 0;
12607     }
12608
12609     return 1;
12610
12611 }  /* end of rmscopy() */
12612 /*}}}*/
12613
12614
12615 /***  The following glue provides 'hooks' to make some of the routines
12616  * from this file available from Perl.  These routines are sufficiently
12617  * basic, and are required sufficiently early in the build process,
12618  * that's it's nice to have them available to miniperl as well as the
12619  * full Perl, so they're set up here instead of in an extension.  The
12620  * Perl code which handles importation of these names into a given
12621  * package lives in [.VMS]Filespec.pm in @INC.
12622  */
12623
12624 void
12625 rmsexpand_fromperl(pTHX_ CV *cv)
12626 {
12627   dXSARGS;
12628   char *fspec, *defspec = NULL, *rslt;
12629   STRLEN n_a;
12630   int fs_utf8, dfs_utf8;
12631
12632   fs_utf8 = 0;
12633   dfs_utf8 = 0;
12634   if (!items || items > 2)
12635     Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12636   fspec = SvPV(ST(0),n_a);
12637   fs_utf8 = SvUTF8(ST(0));
12638   if (!fspec || !*fspec) XSRETURN_UNDEF;
12639   if (items == 2) {
12640     defspec = SvPV(ST(1),n_a);
12641     dfs_utf8 = SvUTF8(ST(1));
12642   }
12643   rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12644   ST(0) = sv_newmortal();
12645   if (rslt != NULL) {
12646     sv_usepvn(ST(0),rslt,strlen(rslt));
12647     if (fs_utf8) {
12648         SvUTF8_on(ST(0));
12649     }
12650   }
12651   XSRETURN(1);
12652 }
12653
12654 void
12655 vmsify_fromperl(pTHX_ CV *cv)
12656 {
12657   dXSARGS;
12658   char *vmsified;
12659   STRLEN n_a;
12660   int utf8_fl;
12661
12662   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12663   utf8_fl = SvUTF8(ST(0));
12664   vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12665   ST(0) = sv_newmortal();
12666   if (vmsified != NULL) {
12667     sv_usepvn(ST(0),vmsified,strlen(vmsified));
12668     if (utf8_fl) {
12669         SvUTF8_on(ST(0));
12670     }
12671   }
12672   XSRETURN(1);
12673 }
12674
12675 void
12676 unixify_fromperl(pTHX_ CV *cv)
12677 {
12678   dXSARGS;
12679   char *unixified;
12680   STRLEN n_a;
12681   int utf8_fl;
12682
12683   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12684   utf8_fl = SvUTF8(ST(0));
12685   unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12686   ST(0) = sv_newmortal();
12687   if (unixified != NULL) {
12688     sv_usepvn(ST(0),unixified,strlen(unixified));
12689     if (utf8_fl) {
12690         SvUTF8_on(ST(0));
12691     }
12692   }
12693   XSRETURN(1);
12694 }
12695
12696 void
12697 fileify_fromperl(pTHX_ CV *cv)
12698 {
12699   dXSARGS;
12700   char *fileified;
12701   STRLEN n_a;
12702   int utf8_fl;
12703
12704   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12705   utf8_fl = SvUTF8(ST(0));
12706   fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12707   ST(0) = sv_newmortal();
12708   if (fileified != NULL) {
12709     sv_usepvn(ST(0),fileified,strlen(fileified));
12710     if (utf8_fl) {
12711         SvUTF8_on(ST(0));
12712     }
12713   }
12714   XSRETURN(1);
12715 }
12716
12717 void
12718 pathify_fromperl(pTHX_ CV *cv)
12719 {
12720   dXSARGS;
12721   char *pathified;
12722   STRLEN n_a;
12723   int utf8_fl;
12724
12725   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12726   utf8_fl = SvUTF8(ST(0));
12727   pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12728   ST(0) = sv_newmortal();
12729   if (pathified != NULL) {
12730     sv_usepvn(ST(0),pathified,strlen(pathified));
12731     if (utf8_fl) {
12732         SvUTF8_on(ST(0));
12733     }
12734   }
12735   XSRETURN(1);
12736 }
12737
12738 void
12739 vmspath_fromperl(pTHX_ CV *cv)
12740 {
12741   dXSARGS;
12742   char *vmspath;
12743   STRLEN n_a;
12744   int utf8_fl;
12745
12746   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12747   utf8_fl = SvUTF8(ST(0));
12748   vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12749   ST(0) = sv_newmortal();
12750   if (vmspath != NULL) {
12751     sv_usepvn(ST(0),vmspath,strlen(vmspath));
12752     if (utf8_fl) {
12753         SvUTF8_on(ST(0));
12754     }
12755   }
12756   XSRETURN(1);
12757 }
12758
12759 void
12760 unixpath_fromperl(pTHX_ CV *cv)
12761 {
12762   dXSARGS;
12763   char *unixpath;
12764   STRLEN n_a;
12765   int utf8_fl;
12766
12767   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12768   utf8_fl = SvUTF8(ST(0));
12769   unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12770   ST(0) = sv_newmortal();
12771   if (unixpath != NULL) {
12772     sv_usepvn(ST(0),unixpath,strlen(unixpath));
12773     if (utf8_fl) {
12774         SvUTF8_on(ST(0));
12775     }
12776   }
12777   XSRETURN(1);
12778 }
12779
12780 void
12781 candelete_fromperl(pTHX_ CV *cv)
12782 {
12783   dXSARGS;
12784   char *fspec, *fsp;
12785   SV *mysv;
12786   IO *io;
12787   STRLEN n_a;
12788
12789   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12790
12791   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12792   Newx(fspec, VMS_MAXRSS, char);
12793   if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12794   if (isGV_with_GP(mysv)) {
12795     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12796       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12797       ST(0) = &PL_sv_no;
12798       Safefree(fspec);
12799       XSRETURN(1);
12800     }
12801     fsp = fspec;
12802   }
12803   else {
12804     if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12805       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12806       ST(0) = &PL_sv_no;
12807       Safefree(fspec);
12808       XSRETURN(1);
12809     }
12810   }
12811
12812   ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12813   Safefree(fspec);
12814   XSRETURN(1);
12815 }
12816
12817 void
12818 rmscopy_fromperl(pTHX_ CV *cv)
12819 {
12820   dXSARGS;
12821   char *inspec, *outspec, *inp, *outp;
12822   int date_flag;
12823   SV *mysv;
12824   IO *io;
12825   STRLEN n_a;
12826
12827   if (items < 2 || items > 3)
12828     Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12829
12830   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12831   Newx(inspec, VMS_MAXRSS, char);
12832   if (isGV_with_GP(mysv)) {
12833     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12834       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12835       ST(0) = sv_2mortal(newSViv(0));
12836       Safefree(inspec);
12837       XSRETURN(1);
12838     }
12839     inp = inspec;
12840   }
12841   else {
12842     if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12843       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12844       ST(0) = sv_2mortal(newSViv(0));
12845       Safefree(inspec);
12846       XSRETURN(1);
12847     }
12848   }
12849   mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12850   Newx(outspec, VMS_MAXRSS, char);
12851   if (isGV_with_GP(mysv)) {
12852     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12853       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12854       ST(0) = sv_2mortal(newSViv(0));
12855       Safefree(inspec);
12856       Safefree(outspec);
12857       XSRETURN(1);
12858     }
12859     outp = outspec;
12860   }
12861   else {
12862     if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12863       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12864       ST(0) = sv_2mortal(newSViv(0));
12865       Safefree(inspec);
12866       Safefree(outspec);
12867       XSRETURN(1);
12868     }
12869   }
12870   date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12871
12872   ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12873   Safefree(inspec);
12874   Safefree(outspec);
12875   XSRETURN(1);
12876 }
12877
12878 /* The mod2fname is limited to shorter filenames by design, so it should
12879  * not be modified to support longer EFS pathnames
12880  */
12881 void
12882 mod2fname(pTHX_ CV *cv)
12883 {
12884   dXSARGS;
12885   char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12886        workbuff[NAM$C_MAXRSS*1 + 1];
12887   SSize_t counter, num_entries;
12888   /* ODS-5 ups this, but we want to be consistent, so... */
12889   int max_name_len = 39;
12890   AV *in_array = (AV *)SvRV(ST(0));
12891
12892   num_entries = av_tindex(in_array);
12893
12894   /* All the names start with PL_. */
12895   strcpy(ultimate_name, "PL_");
12896
12897   /* Clean up our working buffer */
12898   Zero(work_name, sizeof(work_name), char);
12899
12900   /* Run through the entries and build up a working name */
12901   for(counter = 0; counter <= num_entries; counter++) {
12902     /* If it's not the first name then tack on a __ */
12903     if (counter) {
12904       my_strlcat(work_name, "__", sizeof(work_name));
12905     }
12906     my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12907   }
12908
12909   /* Check to see if we actually have to bother...*/
12910   if (strlen(work_name) + 3 <= max_name_len) {
12911     my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12912   } else {
12913     /* It's too darned big, so we need to go strip. We use the same */
12914     /* algorithm as xsubpp does. First, strip out doubled __ */
12915     char *source, *dest, last;
12916     dest = workbuff;
12917     last = 0;
12918     for (source = work_name; *source; source++) {
12919       if (last == *source && last == '_') {
12920         continue;
12921       }
12922       *dest++ = *source;
12923       last = *source;
12924     }
12925     /* Go put it back */
12926     my_strlcpy(work_name, workbuff, sizeof(work_name));
12927     /* Is it still too big? */
12928     if (strlen(work_name) + 3 > max_name_len) {
12929       /* Strip duplicate letters */
12930       last = 0;
12931       dest = workbuff;
12932       for (source = work_name; *source; source++) {
12933         if (last == toUPPER_A(*source)) {
12934         continue;
12935         }
12936         *dest++ = *source;
12937         last = toUPPER_A(*source);
12938       }
12939       my_strlcpy(work_name, workbuff, sizeof(work_name));
12940     }
12941
12942     /* Is it *still* too big? */
12943     if (strlen(work_name) + 3 > max_name_len) {
12944       /* Too bad, we truncate */
12945       work_name[max_name_len - 2] = 0;
12946     }
12947     my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12948   }
12949
12950   /* Okay, return it */
12951   ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12952   XSRETURN(1);
12953 }
12954
12955 void
12956 hushexit_fromperl(pTHX_ CV *cv)
12957 {
12958     dXSARGS;
12959
12960     if (items > 0) {
12961         VMSISH_HUSHED = SvTRUE(ST(0));
12962     }
12963     ST(0) = boolSV(VMSISH_HUSHED);
12964     XSRETURN(1);
12965 }
12966
12967
12968 PerlIO * 
12969 Perl_vms_start_glob(pTHX_ SV *tmpglob, IO *io)
12970 {
12971     PerlIO *fp;
12972     struct vs_str_st *rslt;
12973     char *vmsspec;
12974     char *rstr;
12975     char *begin, *cp;
12976     $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
12977     PerlIO *tmpfp;
12978     STRLEN i;
12979     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12980     struct dsc$descriptor_vs rsdsc;
12981     unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
12982     unsigned long hasver = 0, isunix = 0;
12983     unsigned long int lff_flags = 0;
12984     int rms_sts;
12985     int vms_old_glob = 1;
12986
12987     if (!SvOK(tmpglob)) {
12988         SETERRNO(ENOENT,RMS$_FNF);
12989         return NULL;
12990     }
12991
12992     vms_old_glob = !DECC_FILENAME_UNIX_REPORT;
12993
12994 #ifdef VMS_LONGNAME_SUPPORT
12995     lff_flags = LIB$M_FIL_LONG_NAMES;
12996 #endif
12997     /* The Newx macro will not allow me to assign a smaller array
12998      * to the rslt pointer, so we will assign it to the begin char pointer
12999      * and then copy the value into the rslt pointer.
13000      */
13001     Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13002     rslt = (struct vs_str_st *)begin;
13003     rslt->length = 0;
13004     rstr = &rslt->str[0];
13005     rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13006     rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13007     rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13008     rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13009
13010     Newx(vmsspec, VMS_MAXRSS, char);
13011
13012         /* We could find out if there's an explicit dev/dir or version
13013            by peeking into lib$find_file's internal context at
13014            ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13015            but that's unsupported, so I don't want to do it now and
13016            have it bite someone in the future. */
13017         /* Fix-me: vms_split_path() is the only way to do this, the
13018            existing method will fail with many legal EFS or UNIX specifications
13019          */
13020
13021     cp = SvPV(tmpglob,i);
13022
13023     for (; i; i--) {
13024         if (cp[i] == ';') hasver = 1;
13025         if (cp[i] == '.') {
13026             if (sts) hasver = 1;
13027             else sts = 1;
13028         }
13029         if (cp[i] == '/') {
13030             hasdir = isunix = 1;
13031             break;
13032         }
13033         if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13034             hasdir = 1;
13035             break;
13036         }
13037     }
13038
13039     /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13040     if ((hasdir == 0) && DECC_FILENAME_UNIX_REPORT) {
13041         isunix = 1;
13042     }
13043
13044     if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13045         char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13046         int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13047         int wildstar = 0;
13048         int wildquery = 0;
13049         int found = 0;
13050         Stat_t st;
13051         int stat_sts;
13052         stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13053         if (!stat_sts && S_ISDIR(st.st_mode)) {
13054             char * vms_dir;
13055             const char * fname;
13056             STRLEN fname_len;
13057
13058             /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13059             /* path delimiter of ':>]', if so, then the old behavior has */
13060             /* obviously been specifically requested */
13061
13062             fname = SvPVX_const(tmpglob);
13063             fname_len = strlen(fname);
13064             vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13065             if (vms_old_glob || (vms_dir != NULL)) {
13066                 wilddsc.dsc$a_pointer = tovmspath_utf8(
13067                                             SvPVX(tmpglob),vmsspec,NULL);
13068                 ok = (wilddsc.dsc$a_pointer != NULL);
13069                 /* maybe passed 'foo' rather than '[.foo]', thus not
13070                    detected above */
13071                 hasdir = 1; 
13072             } else {
13073                 /* Operate just on the directory, the special stat/fstat for */
13074                 /* leaves the fileified  specification in the st_devnam */
13075                 /* member. */
13076                 wilddsc.dsc$a_pointer = st.st_devnam;
13077                 ok = 1;
13078             }
13079         }
13080         else {
13081             wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13082             ok = (wilddsc.dsc$a_pointer != NULL);
13083         }
13084         if (ok)
13085             wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13086
13087         /* If not extended character set, replace ? with % */
13088         /* With extended character set, ? is a wildcard single character */
13089         for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13090             if (*cp == '?') {
13091                 wildquery = 1;
13092                 if (!DECC_EFS_CHARSET)
13093                     *cp = '%';
13094             } else if (*cp == '%') {
13095                 wildquery = 1;
13096             } else if (*cp == '*') {
13097                 wildstar = 1;
13098             }
13099         }
13100
13101         if (ok) {
13102             wv_sts = vms_split_path(
13103                 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13104                 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13105                 &wvs_spec, &wvs_len);
13106         } else {
13107             wn_spec = NULL;
13108             wn_len = 0;
13109             we_spec = NULL;
13110             we_len = 0;
13111         }
13112
13113         sts = SS$_NORMAL;
13114         while (ok && $VMS_STATUS_SUCCESS(sts)) {
13115          char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13116          int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13117          int valid_find;
13118
13119             valid_find = 0;
13120             sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13121                                 &dfltdsc,NULL,&rms_sts,&lff_flags);
13122             if (!$VMS_STATUS_SUCCESS(sts))
13123                 break;
13124
13125             /* with varying string, 1st word of buffer contains result length */
13126             rstr[rslt->length] = '\0';
13127
13128              /* Find where all the components are */
13129              v_sts = vms_split_path
13130                        (rstr,
13131                         &v_spec,
13132                         &v_len,
13133                         &r_spec,
13134                         &r_len,
13135                         &d_spec,
13136                         &d_len,
13137                         &n_spec,
13138                         &n_len,
13139                         &e_spec,
13140                         &e_len,
13141                         &vs_spec,
13142                         &vs_len);
13143
13144             /* If no version on input, truncate the version on output */
13145             if (!hasver && (vs_len > 0)) {
13146                 *vs_spec = '\0';
13147                 vs_len = 0;
13148             }
13149
13150             if (isunix) {
13151
13152                 /* In Unix report mode, remove the ".dir;1" from the name */
13153                 /* if it is a real directory */
13154                 if (DECC_FILENAME_UNIX_REPORT && DECC_EFS_CHARSET) {
13155                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13156                         Stat_t statbuf;
13157                         int ret_sts;
13158
13159                         ret_sts = flex_lstat(rstr, &statbuf);
13160                         if ((ret_sts == 0) &&
13161                             S_ISDIR(statbuf.st_mode)) {
13162                             e_len = 0;
13163                             e_spec[0] = 0;
13164                         }
13165                     }
13166                 }
13167
13168                 /* No version & a null extension on UNIX handling */
13169                 if ((e_len == 1) && DECC_READDIR_DROPDOTNOTYPE) {
13170                     e_len = 0;
13171                     *e_spec = '\0';
13172                 }
13173             }
13174
13175             if (!DECC_EFS_CASE_PRESERVE) {
13176                 for (cp = rstr; *cp; cp++) *cp = toLOWER_L1(*cp);
13177             }
13178
13179             /* Find File treats a Null extension as return all extensions */
13180             /* This is contrary to Perl expectations */
13181
13182             if (wildstar || wildquery || vms_old_glob) {
13183                 /* really need to see if the returned file name matched */
13184                 /* but for now will assume that it matches */
13185                 valid_find = 1;
13186             } else {
13187                 /* Exact Match requested */
13188                 /* How are directories handled? - like a file */
13189                 if ((e_len == we_len) && (n_len == wn_len)) {
13190                     int t1;
13191                     t1 = e_len;
13192                     if (t1 > 0)
13193                         t1 = strncmp(e_spec, we_spec, e_len);
13194                     if (t1 == 0) {
13195                        t1 = n_len;
13196                        if (t1 > 0)
13197                            t1 = strncmp(n_spec, we_spec, n_len);
13198                        if (t1 == 0)
13199                            valid_find = 1;
13200                     }
13201                 }
13202             }
13203
13204             if (valid_find) {
13205                 found++;
13206
13207                 if (hasdir) {
13208                     if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13209                     begin = rstr;
13210                 }
13211                 else {
13212                     /* Start with the name */
13213                     begin = n_spec;
13214                 }
13215                 strcat(begin,"\n");
13216                 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13217             }
13218         }
13219         if (cxt) (void)lib$find_file_end(&cxt);
13220
13221         if (!found) {
13222             /* Be POSIXish: return the input pattern when no matches */
13223             my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13224             strcat(rstr,"\n");
13225             ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13226         }
13227
13228         if (ok && sts != RMS$_NMF &&
13229             sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13230         if (!ok) {
13231             if (!(sts & 1)) {
13232                 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13233             }
13234             PerlIO_close(tmpfp);
13235             fp = NULL;
13236         }
13237         else {
13238             PerlIO_rewind(tmpfp);
13239             IoTYPE(io) = IoTYPE_RDONLY;
13240             IoIFP(io) = fp = tmpfp;
13241             IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
13242         }
13243     }
13244     Safefree(vmsspec);
13245     Safefree(rslt);
13246     return fp;
13247 }
13248
13249
13250 static char *
13251 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13252                    int *utf8_fl);
13253
13254 void
13255 unixrealpath_fromperl(pTHX_ CV *cv)
13256 {
13257     dXSARGS;
13258     char *fspec, *rslt_spec, *rslt;
13259     STRLEN n_a;
13260
13261     if (!items || items != 1)
13262         Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13263
13264     fspec = SvPV(ST(0),n_a);
13265     if (!fspec || !*fspec) XSRETURN_UNDEF;
13266
13267     Newx(rslt_spec, VMS_MAXRSS + 1, char);
13268     rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13269
13270     ST(0) = sv_newmortal();
13271     if (rslt != NULL)
13272         sv_usepvn(ST(0),rslt,strlen(rslt));
13273     else
13274         Safefree(rslt_spec);
13275         XSRETURN(1);
13276 }
13277
13278 static char *
13279 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13280                    int *utf8_fl);
13281
13282 void
13283 vmsrealpath_fromperl(pTHX_ CV *cv)
13284 {
13285     dXSARGS;
13286     char *fspec, *rslt_spec, *rslt;
13287     STRLEN n_a;
13288
13289     if (!items || items != 1)
13290         Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13291
13292     fspec = SvPV(ST(0),n_a);
13293     if (!fspec || !*fspec) XSRETURN_UNDEF;
13294
13295     Newx(rslt_spec, VMS_MAXRSS + 1, char);
13296     rslt = do_vms_realname(fspec, rslt_spec, NULL);
13297
13298     ST(0) = sv_newmortal();
13299     if (rslt != NULL)
13300         sv_usepvn(ST(0),rslt,strlen(rslt));
13301     else
13302         Safefree(rslt_spec);
13303         XSRETURN(1);
13304 }
13305
13306 #ifdef HAS_SYMLINK
13307 /*
13308  * A thin wrapper around decc$symlink to make sure we follow the 
13309  * standard and do not create a symlink with a zero-length name,
13310  * and convert the target to Unix format, as the CRTL can't handle
13311  * targets in VMS format.
13312  */
13313 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13314 int
13315 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13316 {
13317     int sts;
13318     char * utarget;
13319
13320     if (!link_name || !*link_name) {
13321       SETERRNO(ENOENT, SS$_NOSUCHFILE);
13322       return -1;
13323     }
13324
13325     utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13326     /* An untranslatable filename should be passed through. */
13327     (void) int_tounixspec(contents, utarget, NULL);
13328     sts = symlink(utarget, link_name);
13329     PerlMem_free(utarget);
13330     return sts;
13331 }
13332 /*}}}*/
13333
13334 #endif /* HAS_SYMLINK */
13335
13336 int do_vms_case_tolerant(void);
13337
13338 void
13339 case_tolerant_process_fromperl(pTHX_ CV *cv)
13340 {
13341   dXSARGS;
13342   ST(0) = boolSV(do_vms_case_tolerant());
13343   XSRETURN(1);
13344 }
13345
13346 #ifdef USE_ITHREADS
13347
13348 void  
13349 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, 
13350                           struct interp_intern *dst)
13351 {
13352     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13353
13354     memcpy(dst,src,sizeof(struct interp_intern));
13355 }
13356
13357 #endif
13358
13359 void  
13360 Perl_sys_intern_clear(pTHX)
13361 {
13362 }
13363
13364 void  
13365 Perl_sys_intern_init(pTHX)
13366 {
13367     unsigned int ix = RAND_MAX;
13368     double x;
13369
13370     VMSISH_HUSHED = 0;
13371
13372     MY_POSIX_EXIT = vms_posix_exit;
13373
13374     x = (float)ix;
13375     MY_INV_RAND_MAX = 1./x;
13376 }
13377
13378 void
13379 init_os_extras(void)
13380 {
13381   dTHX;
13382   char* file = __FILE__;
13383   if (DECC_DISABLE_TO_VMS_LOGNAME_TRANSLATION) {
13384     no_translate_barewords = TRUE;
13385   } else {
13386     no_translate_barewords = FALSE;
13387   }
13388
13389   newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13390   newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13391   newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13392   newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13393   newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13394   newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13395   newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13396   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13397   newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13398   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13399   newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13400   newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13401   newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13402   newXSproto("VMS::Filespec::case_tolerant_process",
13403       case_tolerant_process_fromperl,file,"");
13404
13405   store_pipelocs(aTHX);         /* will redo any earlier attempts */
13406
13407   return;
13408 }
13409   
13410 #if __CRTL_VER == 80200000
13411 /* This missed getting in to the DECC SDK for 8.2 */
13412 char *realpath(const char *file_name, char * resolved_name, ...);
13413 #endif
13414
13415 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13416 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13417  * The perl fallback routine to provide realpath() is not as efficient
13418  * on OpenVMS.
13419  */
13420
13421 #ifdef __cplusplus
13422 extern "C" {
13423 #endif
13424
13425 /* Hack, use old stat() as fastest way of getting ino_t and device */
13426 int decc$stat(const char *name, void * statbuf);
13427 #if __CRTL_VER >= 80200000
13428 int decc$lstat(const char *name, void * statbuf);
13429 #else
13430 #define decc$lstat decc$stat
13431 #endif
13432
13433 #ifdef __cplusplus
13434 }
13435 #endif
13436
13437
13438 /* Realpath is fragile.  In 8.3 it does not work if the feature
13439  * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13440  * links are implemented in RMS, not the CRTL. It also can fail if the 
13441  * user does not have read/execute access to some of the directories.
13442  * So in order for Do What I Mean mode to work, if realpath() fails,
13443  * fall back to looking up the filename by the device name and FID.
13444  */
13445
13446 int vms_fid_to_name(char * outname, int outlen,
13447                     const char * name, int lstat_flag, mode_t * mode)
13448 {
13449 #pragma message save
13450 #pragma message disable MISALGNDSTRCT
13451 #pragma message disable MISALGNDMEM
13452 #pragma member_alignment save
13453 #pragma nomember_alignment
13454     struct statbuf_t {
13455         char       * st_dev;
13456         unsigned short st_ino[3];
13457         unsigned short old_st_mode;
13458         unsigned long  padl[30];  /* plenty of room */
13459     } statbuf;
13460 #pragma message restore
13461 #pragma member_alignment restore
13462
13463     int sts;
13464     struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13465     struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13466     char *fileified;
13467     char *temp_fspec;
13468     char *ret_spec;
13469
13470     /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13471      * unexpected answers
13472      */
13473
13474     fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13475     if (fileified == NULL)
13476         _ckvmssts_noperl(SS$_INSFMEM);
13477      
13478     temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13479     if (temp_fspec == NULL)
13480         _ckvmssts_noperl(SS$_INSFMEM);
13481
13482     sts = -1;
13483     /* First need to try as a directory */
13484     ret_spec = int_tovmspath(name, temp_fspec, NULL);
13485     if (ret_spec != NULL) {
13486         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL); 
13487         if (ret_spec != NULL) {
13488             if (lstat_flag == 0)
13489                 sts = decc$stat(fileified, &statbuf);
13490             else
13491                 sts = decc$lstat(fileified, &statbuf);
13492         }
13493     }
13494
13495     /* Then as a VMS file spec */
13496     if (sts != 0) {
13497         ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13498         if (ret_spec != NULL) {
13499             if (lstat_flag == 0) {
13500                 sts = decc$stat(temp_fspec, &statbuf);
13501             } else {
13502                 sts = decc$lstat(temp_fspec, &statbuf);
13503             }
13504         }
13505     }
13506
13507     if (sts) {
13508         /* Next try - allow multiple dots with out EFS CHARSET */
13509         /* The CRTL stat() falls down hard on multi-dot filenames in unix
13510          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13511          * enable it if it isn't already.
13512          */
13513         if (!DECC_EFS_CHARSET && (efs_charset_index > 0))
13514             decc$feature_set_value(efs_charset_index, 1, 1);
13515         ret_spec = int_tovmspath(name, temp_fspec, NULL);
13516         if (lstat_flag == 0) {
13517             sts = decc$stat(name, &statbuf);
13518         } else {
13519             sts = decc$lstat(name, &statbuf);
13520         }
13521         if (!DECC_EFS_CHARSET && (efs_charset_index > 0))
13522             decc$feature_set_value(efs_charset_index, 1, 0);
13523     }
13524
13525
13526     /* and then because the Perl Unix to VMS conversion is not perfect */
13527     /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13528     /* characters from filenames so we need to try it as-is */
13529     if (sts) {
13530         if (lstat_flag == 0) {
13531             sts = decc$stat(name, &statbuf);
13532         } else {
13533             sts = decc$lstat(name, &statbuf);
13534         }
13535     }
13536
13537     if (sts == 0) {
13538         int vms_sts;
13539
13540         dvidsc.dsc$a_pointer=statbuf.st_dev;
13541         dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13542
13543         specdsc.dsc$a_pointer = outname;
13544         specdsc.dsc$w_length = outlen-1;
13545
13546         vms_sts = lib$fid_to_name
13547             (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13548         if ($VMS_STATUS_SUCCESS(vms_sts)) {
13549             outname[specdsc.dsc$w_length] = 0;
13550
13551             /* Return the mode */
13552             if (mode) {
13553                 *mode = statbuf.old_st_mode;
13554             }
13555         }
13556     }
13557     PerlMem_free(temp_fspec);
13558     PerlMem_free(fileified);
13559     return sts;
13560 }
13561
13562
13563
13564 static char *
13565 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13566                    int *utf8_fl)
13567 {
13568     char * rslt = NULL;
13569
13570 #ifdef HAS_SYMLINK
13571     if (DECC_POSIX_COMPLIANT_PATHNAMES) {
13572         /* realpath currently only works if posix compliant pathnames are
13573          * enabled.  It may start working when they are not, but in that
13574          * case we still want the fallback behavior for backwards compatibility
13575          */
13576         rslt = realpath(filespec, outbuf);
13577     }
13578 #endif
13579
13580     if (rslt == NULL) {
13581         char * vms_spec;
13582         char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13583         int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13584         mode_t my_mode;
13585
13586         /* Fall back to fid_to_name */
13587
13588         Newx(vms_spec, VMS_MAXRSS + 1, char);
13589
13590         sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13591         if (sts == 0) {
13592
13593
13594             /* Now need to trim the version off */
13595             sts = vms_split_path
13596                   (vms_spec,
13597                    &v_spec,
13598                    &v_len,
13599                    &r_spec,
13600                    &r_len,
13601                    &d_spec,
13602                    &d_len,
13603                    &n_spec,
13604                    &n_len,
13605                    &e_spec,
13606                    &e_len,
13607                    &vs_spec,
13608                    &vs_len);
13609
13610
13611                 if (sts == 0) {
13612                     int haslower = 0;
13613                     const char *cp;
13614
13615                     /* Trim off the version */
13616                     int file_len = v_len + r_len + d_len + n_len + e_len;
13617                     vms_spec[file_len] = 0;
13618
13619                     /* Trim off the .DIR if this is a directory */
13620                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13621                         if (S_ISDIR(my_mode)) {
13622                             e_len = 0;
13623                             e_spec[0] = 0;
13624                         }
13625                     }
13626
13627                     /* Drop NULL extensions on UNIX file specification */
13628                     if ((e_len == 1) && DECC_READDIR_DROPDOTNOTYPE) {
13629                         e_len = 0;
13630                         e_spec[0] = '\0';
13631                     }
13632
13633                     /* The result is expected to be in UNIX format */
13634                     rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13635
13636                     /* Downcase if input had any lower case letters and 
13637                      * case preservation is not in effect. 
13638                      */
13639                     if (!DECC_EFS_CASE_PRESERVE) {
13640                         for (cp = filespec; *cp; cp++)
13641                             if (islower(*cp)) { haslower = 1; break; }
13642
13643                         if (haslower) __mystrtolower(rslt);
13644                     }
13645                 }
13646         } else {
13647
13648             /* Now for some hacks to deal with backwards and forward */
13649             /* compatibility */
13650             if (!DECC_EFS_CHARSET) {
13651
13652                 /* 1. ODS-2 mode wants to do a syntax only translation */
13653                 rslt = int_rmsexpand(filespec, outbuf,
13654                                     NULL, 0, NULL, utf8_fl);
13655
13656             } else {
13657                 if (DECC_FILENAME_UNIX_REPORT) {
13658                     char * dir_name;
13659                     char * vms_dir_name;
13660                     char * file_name;
13661
13662                     /* 2. ODS-5 / UNIX report mode should return a failure */
13663                     /*    if the parent directory also does not exist */
13664                     /*    Otherwise, get the real path for the parent */
13665                     /*    and add the child to it. */
13666
13667                     /* basename / dirname only available for VMS 7.0+ */
13668                     /* So we may need to implement them as common routines */
13669
13670                     Newx(dir_name, VMS_MAXRSS + 1, char);
13671                     Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13672                     dir_name[0] = '\0';
13673                     file_name = NULL;
13674
13675                     /* First try a VMS parse */
13676                     sts = vms_split_path
13677                           (filespec,
13678                            &v_spec,
13679                            &v_len,
13680                            &r_spec,
13681                            &r_len,
13682                            &d_spec,
13683                            &d_len,
13684                            &n_spec,
13685                            &n_len,
13686                            &e_spec,
13687                            &e_len,
13688                            &vs_spec,
13689                            &vs_len);
13690
13691                     if (sts == 0) {
13692                         /* This is VMS */
13693
13694                         int dir_len = v_len + r_len + d_len + n_len;
13695                         if (dir_len > 0) {
13696                            memcpy(dir_name, filespec, dir_len);
13697                            dir_name[dir_len] = '\0';
13698                            file_name = (char *)&filespec[dir_len + 1];
13699                         }
13700                     } else {
13701                         /* This must be UNIX */
13702                         char * tchar;
13703
13704                         tchar = strrchr(filespec, '/');
13705
13706                         if (tchar != NULL) {
13707                             int dir_len = tchar - filespec;
13708                             memcpy(dir_name, filespec, dir_len);
13709                             dir_name[dir_len] = '\0';
13710                             file_name = (char *) &filespec[dir_len + 1];
13711                         }
13712                     }
13713
13714                     /* Dir name is defaulted */
13715                     if (dir_name[0] == 0) {
13716                         dir_name[0] = '.';
13717                         dir_name[1] = '\0';
13718                     }
13719
13720                     /* Need realpath for the directory */
13721                     sts = vms_fid_to_name(vms_dir_name,
13722                                           VMS_MAXRSS + 1,
13723                                           dir_name, 0, NULL);
13724
13725                     if (sts == 0) {
13726                         /* Now need to pathify it. */
13727                         char *tdir = int_pathify_dirspec(vms_dir_name,
13728                                                          outbuf);
13729
13730                         /* And now add the original filespec to it */
13731                         if (file_name != NULL) {
13732                             my_strlcat(outbuf, file_name, VMS_MAXRSS);
13733                         }
13734                         return outbuf;
13735                     }
13736                     Safefree(vms_dir_name);
13737                     Safefree(dir_name);
13738                 }
13739             }
13740         }
13741         Safefree(vms_spec);
13742     }
13743     return rslt;
13744 }
13745
13746 static char *
13747 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13748                    int *utf8_fl)
13749 {
13750     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13751     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13752
13753     /* Fall back to fid_to_name */
13754
13755     sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13756     if (sts != 0) {
13757         return NULL;
13758     }
13759     else {
13760
13761
13762         /* Now need to trim the version off */
13763         sts = vms_split_path
13764                   (outbuf,
13765                    &v_spec,
13766                    &v_len,
13767                    &r_spec,
13768                    &r_len,
13769                    &d_spec,
13770                    &d_len,
13771                    &n_spec,
13772                    &n_len,
13773                    &e_spec,
13774                    &e_len,
13775                    &vs_spec,
13776                    &vs_len);
13777
13778
13779         if (sts == 0) {
13780             int haslower = 0;
13781             const char *cp;
13782
13783             /* Trim off the version */
13784             int file_len = v_len + r_len + d_len + n_len + e_len;
13785             outbuf[file_len] = 0;
13786
13787             /* Downcase if input had any lower case letters and 
13788              * case preservation is not in effect. 
13789              */
13790             if (!DECC_EFS_CASE_PRESERVE) {
13791                 for (cp = filespec; *cp; cp++)
13792                     if (islower(*cp)) { haslower = 1; break; }
13793
13794                 if (haslower) __mystrtolower(outbuf);
13795             }
13796         }
13797     }
13798     return outbuf;
13799 }
13800
13801
13802 /*}}}*/
13803 /* External entry points */
13804 char *
13805 Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13806 {
13807     return do_vms_realpath(filespec, outbuf, utf8_fl);
13808 }
13809
13810 char *
13811 Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13812 {
13813     return do_vms_realname(filespec, outbuf, utf8_fl);
13814 }
13815
13816 /* case_tolerant */
13817
13818 /*{{{int do_vms_case_tolerant(void)*/
13819 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13820  * controlled by a process setting.
13821  */
13822 int
13823 do_vms_case_tolerant(void)
13824 {
13825     return vms_process_case_tolerant;
13826 }
13827 /*}}}*/
13828 /* External entry points */
13829 int
13830 Perl_vms_case_tolerant(void)
13831 {
13832     return do_vms_case_tolerant();
13833 }
13834
13835  /* Start of DECC RTL Feature handling */
13836
13837 static int
13838 set_feature_default(const char *name, int value)
13839 {
13840     int status;
13841     int index;
13842     char val_str[10];
13843
13844     /* If the feature has been explicitly disabled in the environment,
13845      * then don't enable it here.
13846      */
13847     if (value > 0) {
13848         status = simple_trnlnm(name, val_str, sizeof(val_str));
13849         if (status) {
13850             val_str[0] = toUPPER_A(val_str[0]);
13851             if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13852                 return 0;
13853         }
13854     }
13855
13856     index = decc$feature_get_index(name);
13857
13858     status = decc$feature_set_value(index, 1, value);
13859     if (index == -1 || (status == -1)) {
13860       return -1;
13861     }
13862
13863     status = decc$feature_get_value(index, 1);
13864     if (status != value) {
13865       return -1;
13866     }
13867
13868     /* Various things may check for an environment setting
13869      * rather than the feature directly, so set that too.
13870      */
13871     vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13872
13873     return 0;
13874 }
13875
13876
13877 /* C RTL Feature settings */
13878
13879 #if defined(__DECC) || defined(__DECCXX)
13880
13881 #ifdef __cplusplus 
13882 extern "C" { 
13883 #endif 
13884  
13885 extern void
13886 vmsperl_set_features(void)
13887 {
13888     int status, initial;
13889     int s;
13890     char val_str[LNM$C_NAMLENGTH+1];
13891 #if defined(JPI$_CASE_LOOKUP_PERM)
13892     const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13893     const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13894     unsigned long case_perm;
13895     unsigned long case_image;
13896 #endif
13897
13898     /* Allow an exception to bring Perl into the VMS debugger */
13899     vms_debug_on_exception = 0;
13900     status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13901     if (status) {
13902        val_str[0] = toUPPER_A(val_str[0]);
13903        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13904          vms_debug_on_exception = 1;
13905        else
13906          vms_debug_on_exception = 0;
13907     }
13908
13909     /* Debug unix/vms file translation routines */
13910     vms_debug_fileify = 0;
13911     status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13912     if (status) {
13913         val_str[0] = toUPPER_A(val_str[0]);
13914         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13915             vms_debug_fileify = 1;
13916         else
13917             vms_debug_fileify = 0;
13918     }
13919
13920
13921     /* Historically PERL has been doing vmsify / stat differently than */
13922     /* the CRTL.  In particular, under some conditions the CRTL will   */
13923     /* remove some illegal characters like spaces from filenames       */
13924     /* resulting in some differences.  The stat()/lstat() wrapper has  */
13925     /* been reporting such file names as invalid and fails to stat them */
13926     /* fixing this bug so that stat()/lstat() accept these like the     */
13927     /* CRTL does will result in several tests failing.                  */
13928     /* This should really be fixed, but for now, set up a feature to    */
13929     /* enable it so that the impact can be studied.                     */
13930     vms_bug_stat_filename = 0;
13931     status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13932     if (status) {
13933         val_str[0] = toUPPER_A(val_str[0]);
13934         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13935             vms_bug_stat_filename = 1;
13936         else
13937             vms_bug_stat_filename = 0;
13938     }
13939
13940
13941     /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13942     vms_vtf7_filenames = 0;
13943     status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13944     if (status) {
13945        val_str[0] = toUPPER_A(val_str[0]);
13946        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13947          vms_vtf7_filenames = 1;
13948        else
13949          vms_vtf7_filenames = 0;
13950     }
13951
13952     /* unlink all versions on unlink() or rename() */
13953     vms_unlink_all_versions = 0;
13954     status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13955     if (status) {
13956        val_str[0] = toUPPER_A(val_str[0]);
13957        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13958          vms_unlink_all_versions = 1;
13959        else
13960          vms_unlink_all_versions = 0;
13961     }
13962
13963     /* The path separator in PERL5LIB is '|' unless running under a Unix shell. */
13964     PL_perllib_sep = '|';
13965
13966     /* Detect running under GNV Bash or other UNIX like shell */
13967     gnv_unix_shell = 0;
13968     status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13969     if (status) {
13970          gnv_unix_shell = 1;
13971          set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13972          set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13973          set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13974          set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13975          vms_unlink_all_versions = 1;
13976          vms_posix_exit = 1;
13977          /* Reverse default ordering of PERL_ENV_TABLES. */
13978          defenv[0] = &crtlenvdsc;
13979          defenv[1] = &fildevdsc;
13980          PL_perllib_sep = ':';
13981     }
13982     /* Some reasonable defaults that are not CRTL defaults */
13983     set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
13984     set_feature_default("DECC$ARGV_PARSE_STYLE", 1);     /* Requires extended parse. */
13985     set_feature_default("DECC$EFS_CHARSET", 1);
13986
13987    /* If POSIX root doesn't exist or nothing has set it explicitly, we disable it,
13988     * which confusingly means enabling the feature.  For some reason only the default
13989     * -- not current -- value can be set, so we cannot use the confusingly-named
13990     * set_feature_default function, which sets the current value.
13991     */
13992     s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
13993     disable_posix_root_index = s;
13994
13995     status = simple_trnlnm("SYS$POSIX_ROOT", val_str, LNM$C_NAMLENGTH);
13996     initial = decc$feature_get_value(disable_posix_root_index, __FEATURE_MODE_INIT_STATE);
13997     if (!status || !initial) {
13998         decc$feature_set_value(disable_posix_root_index, 0, 1);
13999     }
14000
14001     /* hacks to see if known bugs are still present for testing */
14002
14003     /* PCP mode requires creating /dev/null special device file */
14004     decc_bug_devnull = 0;
14005     status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14006     if (status) {
14007        val_str[0] = toUPPER_A(val_str[0]);
14008        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14009           decc_bug_devnull = 1;
14010        else
14011           decc_bug_devnull = 0;
14012     }
14013
14014     s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14015     disable_to_vms_logname_translation_index = s;
14016
14017     s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14018     efs_case_preserve_index = s;
14019
14020     s = decc$feature_get_index("DECC$EFS_CHARSET");
14021     efs_charset_index = s;
14022
14023     s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14024     filename_unix_report_index = s;
14025
14026     s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14027     filename_unix_only_index = s;
14028
14029     s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14030     filename_unix_no_version_index = s;
14031
14032     s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14033     readdir_dropdotnotype_index = s;
14034
14035 #if __CRTL_VER >= 80200000
14036     s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14037     posix_compliant_pathnames_index = s;
14038 #endif
14039
14040 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND)
14041
14042      /* Report true case tolerance */
14043     /*----------------------------*/
14044     status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14045     if (!$VMS_STATUS_SUCCESS(status))
14046         case_perm = PPROP$K_CASE_BLIND;
14047     status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14048     if (!$VMS_STATUS_SUCCESS(status))
14049         case_image = PPROP$K_CASE_BLIND;
14050     if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14051         (case_image == PPROP$K_CASE_SENSITIVE))
14052         vms_process_case_tolerant = 0;
14053
14054 #endif
14055
14056     /* USE POSIX/DCL Exit codes - Recommended, but needs to default to  */
14057     /* for strict backward compatibility */
14058     status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14059     if (status) {
14060        val_str[0] = toUPPER_A(val_str[0]);
14061        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14062          vms_posix_exit = 1;
14063        else
14064          vms_posix_exit = 0;
14065     }
14066 }
14067
14068 /* Use 32-bit pointers because that's what the image activator
14069  * assumes for the LIB$INITIALZE psect.
14070  */ 
14071 #if __INITIAL_POINTER_SIZE 
14072 #pragma pointer_size save 
14073 #pragma pointer_size 32 
14074 #endif 
14075  
14076 /* Create a reference to the LIB$INITIALIZE function. */ 
14077 extern void LIB$INITIALIZE(void); 
14078 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE; 
14079  
14080 /* Create an array of pointers to the init functions in the special 
14081  * LIB$INITIALIZE section. In our case, the array only has one entry.
14082  */ 
14083 #pragma extern_model save 
14084 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long 
14085 extern void (* const vmsperl_unused_global_2[])() = 
14086
14087    vmsperl_set_features,
14088 }; 
14089 #pragma extern_model restore 
14090  
14091 #if __INITIAL_POINTER_SIZE 
14092 #pragma pointer_size restore 
14093 #endif 
14094  
14095 #ifdef __cplusplus 
14096
14097 #endif
14098
14099 #endif /* defined(__DECC) || defined(__DECCXX) */
14100 /*  End of vms.c */