This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
rebump Hash::Util::FieldHash from 1.03_01 to 1.04
[perl5.git] / vms / vms.c
1 /*    vms.c
2  *
3  *    VMS-specific routines for perl5
4  *
5  *    Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6  *    2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
7  *
8  *    You may distribute under the terms of either the GNU General Public
9  *    License or the Artistic License, as specified in the README file.
10  *
11  *    Please see Changes*.* or the Perl Repository Browser for revision history.
12  */
13
14 /*
15  *   Yet small as was their hunted band
16  *   still fell and fearless was each hand,
17  *   and strong deeds they wrought yet oft,
18  *   and loved the woods, whose ways more soft
19  *   them seemed than thralls of that black throne
20  *   to live and languish in halls of stone.
21  *        "The Lay of Leithian", Canto II, lines 135-40
22  *
23  *     [p.162 of _The Lays of Beleriand_]
24  */
25  
26 #include <acedef.h>
27 #include <acldef.h>
28 #include <armdef.h>
29 #include <atrdef.h>
30 #include <chpdef.h>
31 #include <clidef.h>
32 #include <climsgdef.h>
33 #include <dcdef.h>
34 #include <descrip.h>
35 #include <devdef.h>
36 #include <dvidef.h>
37 #include <fibdef.h>
38 #include <float.h>
39 #include <fscndef.h>
40 #include <iodef.h>
41 #include <jpidef.h>
42 #include <kgbdef.h>
43 #include <libclidef.h>
44 #include <libdef.h>
45 #include <lib$routines.h>
46 #include <lnmdef.h>
47 #include <msgdef.h>
48 #include <ossdef.h>
49 #if __CRTL_VER >= 70301000 && !defined(__VAX)
50 #include <ppropdef.h>
51 #endif
52 #include <prvdef.h>
53 #include <psldef.h>
54 #include <rms.h>
55 #include <shrdef.h>
56 #include <ssdef.h>
57 #include <starlet.h>
58 #include <strdef.h>
59 #include <str$routines.h>
60 #include <syidef.h>
61 #include <uaidef.h>
62 #include <uicdef.h>
63 #include <stsdef.h>
64 #include <rmsdef.h>
65 #include <smgdef.h>
66 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
67 #include <efndef.h>
68 #define NO_EFN EFN$C_ENF
69 #else
70 #define NO_EFN 0;
71 #endif
72
73 #if  __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
74 int   decc$feature_get_index(const char *name);
75 char* decc$feature_get_name(int index);
76 int   decc$feature_get_value(int index, int mode);
77 int   decc$feature_set_value(int index, int mode, int value);
78 #else
79 #include <unixlib.h>
80 #endif
81
82 #pragma member_alignment save
83 #pragma nomember_alignment longword
84 struct item_list_3 {
85         unsigned short len;
86         unsigned short code;
87         void * bufadr;
88         unsigned short * retadr;
89 };
90 #pragma member_alignment restore
91
92 /* More specific prototype than in starlet_c.h makes programming errors
93    more visible.
94  */
95 #ifdef sys$getdviw
96 #undef sys$getdviw
97 int sys$getdviw
98        (unsigned long efn,
99         unsigned short chan,
100         const struct dsc$descriptor_s * devnam,
101         const struct item_list_3 * itmlst,
102         void * iosb,
103         void * (astadr)(unsigned long),
104         void * astprm,
105         void * nullarg);
106 #endif
107
108 #ifdef sys$get_security
109 #undef sys$get_security
110 int sys$get_security
111        (const struct dsc$descriptor_s * clsnam,
112         const struct dsc$descriptor_s * objnam,
113         const unsigned int *objhan,
114         unsigned int flags,
115         const struct item_list_3 * itmlst,
116         unsigned int * contxt,
117         const unsigned int * acmode);
118 #endif
119
120 #ifdef sys$set_security
121 #undef sys$set_security
122 int sys$set_security
123        (const struct dsc$descriptor_s * clsnam,
124         const struct dsc$descriptor_s * objnam,
125         const unsigned int *objhan,
126         unsigned int flags,
127         const struct item_list_3 * itmlst,
128         unsigned int * contxt,
129         const unsigned int * acmode);
130 #endif
131
132 #ifdef lib$find_image_symbol
133 #undef lib$find_image_symbol
134 int lib$find_image_symbol
135        (const struct dsc$descriptor_s * imgname,
136         const struct dsc$descriptor_s * symname,
137         void * symval,
138         const struct dsc$descriptor_s * defspec,
139         unsigned long flag);
140 #endif
141
142 #ifdef lib$rename_file
143 #undef lib$rename_file
144 int lib$rename_file
145        (const struct dsc$descriptor_s * old_file_dsc,
146         const struct dsc$descriptor_s * new_file_dsc,
147         const struct dsc$descriptor_s * default_file_dsc,
148         const struct dsc$descriptor_s * related_file_dsc,
149         const unsigned long * flags,
150         void * (success)(const struct dsc$descriptor_s * old_dsc,
151                          const struct dsc$descriptor_s * new_dsc,
152                          const void *),
153         void * (error)(const struct dsc$descriptor_s * old_dsc,
154                        const struct dsc$descriptor_s * new_dsc,
155                        const int * rms_sts,
156                        const int * rms_stv,
157                        const int * error_src,
158                        const void * usr_arg),
159         int (confirm)(const struct dsc$descriptor_s * old_dsc,
160                       const struct dsc$descriptor_s * new_dsc,
161                       const void * old_fab,
162                       const void * usr_arg),
163         void * user_arg,
164         struct dsc$descriptor_s * old_result_name_dsc,
165         struct dsc$descriptor_s * new_result_name_dsc,
166         unsigned long * file_scan_context);
167 #endif
168
169 #if __CRTL_VER >= 70300000 && !defined(__VAX)
170
171 static int set_feature_default(const char *name, int value)
172 {
173     int status;
174     int index;
175
176     index = decc$feature_get_index(name);
177
178     status = decc$feature_set_value(index, 1, value);
179     if (index == -1 || (status == -1)) {
180       return -1;
181     }
182
183     status = decc$feature_get_value(index, 1);
184     if (status != value) {
185       return -1;
186     }
187
188 return 0;
189 }
190 #endif
191
192 /* Older versions of ssdef.h don't have these */
193 #ifndef SS$_INVFILFOROP
194 #  define SS$_INVFILFOROP 3930
195 #endif
196 #ifndef SS$_NOSUCHOBJECT
197 #  define SS$_NOSUCHOBJECT 2696
198 #endif
199
200 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
201 #define PERLIO_NOT_STDIO 0 
202
203 /* Don't replace system definitions of vfork, getenv, lstat, and stat, 
204  * code below needs to get to the underlying CRTL routines. */
205 #define DONT_MASK_RTL_CALLS
206 #include "EXTERN.h"
207 #include "perl.h"
208 #include "XSUB.h"
209 /* Anticipating future expansion in lexical warnings . . . */
210 #ifndef WARN_INTERNAL
211 #  define WARN_INTERNAL WARN_MISC
212 #endif
213
214 #ifdef VMS_LONGNAME_SUPPORT
215 #include <libfildef.h>
216 #endif
217
218 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
219 #  define RTL_USES_UTC 1
220 #endif
221
222 #if !defined(__VAX) && __CRTL_VER >= 80200000
223 #ifdef lstat
224 #undef lstat
225 #endif
226 #else
227 #ifdef lstat
228 #undef lstat
229 #endif
230 #define lstat(_x, _y) stat(_x, _y)
231 #endif
232
233 /* Routine to create a decterm for use with the Perl debugger */
234 /* No headers, this information was found in the Programming Concepts Manual */
235
236 static int (*decw_term_port)
237    (const struct dsc$descriptor_s * display,
238     const struct dsc$descriptor_s * setup_file,
239     const struct dsc$descriptor_s * customization,
240     struct dsc$descriptor_s * result_device_name,
241     unsigned short * result_device_name_length,
242     void * controller,
243     void * char_buffer,
244     void * char_change_buffer) = 0;
245
246 /* gcc's header files don't #define direct access macros
247  * corresponding to VAXC's variant structs */
248 #ifdef __GNUC__
249 #  define uic$v_format uic$r_uic_form.uic$v_format
250 #  define uic$v_group uic$r_uic_form.uic$v_group
251 #  define uic$v_member uic$r_uic_form.uic$v_member
252 #  define prv$v_bypass  prv$r_prvdef_bits0.prv$v_bypass
253 #  define prv$v_grpprv  prv$r_prvdef_bits0.prv$v_grpprv
254 #  define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
255 #  define prv$v_sysprv  prv$r_prvdef_bits0.prv$v_sysprv
256 #endif
257
258 #if defined(NEED_AN_H_ERRNO)
259 dEXT int h_errno;
260 #endif
261
262 #ifdef __DECC
263 #pragma message disable pragma
264 #pragma member_alignment save
265 #pragma nomember_alignment longword
266 #pragma message save
267 #pragma message disable misalgndmem
268 #endif
269 struct itmlst_3 {
270   unsigned short int buflen;
271   unsigned short int itmcode;
272   void *bufadr;
273   unsigned short int *retlen;
274 };
275
276 struct filescan_itmlst_2 {
277     unsigned short length;
278     unsigned short itmcode;
279     char * component;
280 };
281
282 struct vs_str_st {
283     unsigned short length;
284     char str[65536];
285 };
286
287 #ifdef __DECC
288 #pragma message restore
289 #pragma member_alignment restore
290 #endif
291
292 #define do_fileify_dirspec(a,b,c,d)     mp_do_fileify_dirspec(aTHX_ a,b,c,d)
293 #define do_pathify_dirspec(a,b,c,d)     mp_do_pathify_dirspec(aTHX_ a,b,c,d)
294 #define do_tovmsspec(a,b,c,d)           mp_do_tovmsspec(aTHX_ a,b,c,0,d)
295 #define do_tovmspath(a,b,c,d)           mp_do_tovmspath(aTHX_ a,b,c,d)
296 #define do_rmsexpand(a,b,c,d,e,f,g)     mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
297 #define do_vms_realpath(a,b,c)          mp_do_vms_realpath(aTHX_ a,b,c)
298 #define do_vms_realname(a,b,c)          mp_do_vms_realname(aTHX_ a,b,c)
299 #define do_tounixspec(a,b,c,d)          mp_do_tounixspec(aTHX_ a,b,c,d)
300 #define do_tounixpath(a,b,c,d)          mp_do_tounixpath(aTHX_ a,b,c,d)
301 #define do_vms_case_tolerant(a)         mp_do_vms_case_tolerant(a)
302 #define expand_wild_cards(a,b,c,d)      mp_expand_wild_cards(aTHX_ a,b,c,d)
303 #define getredirection(a,b)             mp_getredirection(aTHX_ a,b)
304
305 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
306 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
307 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
308 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
309
310 static char *  int_rmsexpand_vms(
311     const char * filespec, char * outbuf, unsigned opts);
312 static char * int_rmsexpand_tovms(
313     const char * filespec, char * outbuf, unsigned opts);
314 static char *int_tovmsspec
315    (const char *path, char *buf, int dir_flag, int * utf8_flag);
316 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
317 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
318 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
319
320 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
321 #define PERL_LNM_MAX_ALLOWED_INDEX 127
322
323 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
324  * depending on the facility.  SHOW LOGICAL does 10, so we'll imitate that for
325  * the Perl facility.
326  */
327 #define PERL_LNM_MAX_ITER 10
328
329   /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
330 #if __CRTL_VER >= 70302000 && !defined(__VAX)
331 #define MAX_DCL_SYMBOL          (8192)
332 #define MAX_DCL_LINE_LENGTH     (4096 - 4)
333 #else
334 #define MAX_DCL_SYMBOL          (1024)
335 #define MAX_DCL_LINE_LENGTH     (1024 - 4)
336 #endif
337
338 static char *__mystrtolower(char *str)
339 {
340   if (str) for (; *str; ++str) *str= tolower(*str);
341   return str;
342 }
343
344 static struct dsc$descriptor_s fildevdsc = 
345   { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
346 static struct dsc$descriptor_s crtlenvdsc = 
347   { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
348 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
349 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
350 static struct dsc$descriptor_s **env_tables = defenv;
351 static bool will_taint = FALSE;  /* tainting active, but no PL_curinterp yet */
352
353 /* True if we shouldn't treat barewords as logicals during directory */
354 /* munching */ 
355 static int no_translate_barewords;
356
357 #ifndef RTL_USES_UTC
358 static int tz_updated = 1;
359 #endif
360
361 /* DECC Features that may need to affect how Perl interprets
362  * displays filename information
363  */
364 static int decc_disable_to_vms_logname_translation = 1;
365 static int decc_disable_posix_root = 1;
366 int decc_efs_case_preserve = 0;
367 static int decc_efs_charset = 0;
368 static int decc_efs_charset_index = -1;
369 static int decc_filename_unix_no_version = 0;
370 static int decc_filename_unix_only = 0;
371 int decc_filename_unix_report = 0;
372 int decc_posix_compliant_pathnames = 0;
373 int decc_readdir_dropdotnotype = 0;
374 static int vms_process_case_tolerant = 1;
375 int vms_vtf7_filenames = 0;
376 int gnv_unix_shell = 0;
377 static int vms_unlink_all_versions = 0;
378 static int vms_posix_exit = 0;
379
380 /* bug workarounds if needed */
381 int decc_bug_devnull = 1;
382 int decc_dir_barename = 0;
383 int vms_bug_stat_filename = 0;
384
385 static int vms_debug_on_exception = 0;
386 static int vms_debug_fileify = 0;
387
388 /* Simple logical name translation */
389 static int simple_trnlnm
390    (const char * logname,
391     char * value,
392     int value_len)
393 {
394     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
395     const unsigned long attr = LNM$M_CASE_BLIND;
396     struct dsc$descriptor_s name_dsc;
397     int status;
398     unsigned short result;
399     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
400                                 {0, 0, 0, 0}};
401
402     name_dsc.dsc$w_length = strlen(logname);
403     name_dsc.dsc$a_pointer = (char *)logname;
404     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
405     name_dsc.dsc$b_class = DSC$K_CLASS_S;
406
407     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
408
409     if ($VMS_STATUS_SUCCESS(status)) {
410
411          /* Null terminate and return the string */
412         /*--------------------------------------*/
413         value[result] = 0;
414         return result;
415     }
416
417     return 0;
418 }
419
420
421 /* Is this a UNIX file specification?
422  *   No longer a simple check with EFS file specs
423  *   For now, not a full check, but need to
424  *   handle POSIX ^UP^ specifications
425  *   Fixing to handle ^/ cases would require
426  *   changes to many other conversion routines.
427  */
428
429 static int is_unix_filespec(const char *path)
430 {
431 int ret_val;
432 const char * pch1;
433
434     ret_val = 0;
435     if (strncmp(path,"\"^UP^",5) != 0) {
436         pch1 = strchr(path, '/');
437         if (pch1 != NULL)
438             ret_val = 1;
439         else {
440
441             /* If the user wants UNIX files, "." needs to be treated as in UNIX */
442             if (decc_filename_unix_report || decc_filename_unix_only) {
443             if (strcmp(path,".") == 0)
444                 ret_val = 1;
445             }
446         }
447     }
448     return ret_val;
449 }
450
451 /* This routine converts a UCS-2 character to be VTF-7 encoded.
452  */
453
454 static void ucs2_to_vtf7
455    (char *outspec,
456     unsigned long ucs2_char,
457     int * output_cnt)
458 {
459 unsigned char * ucs_ptr;
460 int hex;
461
462     ucs_ptr = (unsigned char *)&ucs2_char;
463
464     outspec[0] = '^';
465     outspec[1] = 'U';
466     hex = (ucs_ptr[1] >> 4) & 0xf;
467     if (hex < 0xA)
468         outspec[2] = hex + '0';
469     else
470         outspec[2] = (hex - 9) + 'A';
471     hex = ucs_ptr[1] & 0xF;
472     if (hex < 0xA)
473         outspec[3] = hex + '0';
474     else {
475         outspec[3] = (hex - 9) + 'A';
476     }
477     hex = (ucs_ptr[0] >> 4) & 0xf;
478     if (hex < 0xA)
479         outspec[4] = hex + '0';
480     else
481         outspec[4] = (hex - 9) + 'A';
482     hex = ucs_ptr[1] & 0xF;
483     if (hex < 0xA)
484         outspec[5] = hex + '0';
485     else {
486         outspec[5] = (hex - 9) + 'A';
487     }
488     *output_cnt = 6;
489 }
490
491
492 /* This handles the conversion of a UNIX extended character set to a ^
493  * escaped VMS character.
494  * in a UNIX file specification.
495  *
496  * The output count variable contains the number of characters added
497  * to the output string.
498  *
499  * The return value is the number of characters read from the input string
500  */
501 static int copy_expand_unix_filename_escape
502   (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
503 {
504 int count;
505 int scnt;
506 int utf8_flag;
507
508     utf8_flag = 0;
509     if (utf8_fl)
510       utf8_flag = *utf8_fl;
511
512     count = 0;
513     *output_cnt = 0;
514     if (*inspec >= 0x80) {
515         if (utf8_fl && vms_vtf7_filenames) {
516         unsigned long ucs_char;
517
518             ucs_char = 0;
519
520             if ((*inspec & 0xE0) == 0xC0) {
521                 /* 2 byte Unicode */
522                 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
523                 if (ucs_char >= 0x80) {
524                     ucs2_to_vtf7(outspec, ucs_char, output_cnt);
525                     return 2;
526                 }
527             } else if ((*inspec & 0xF0) == 0xE0) {
528                 /* 3 byte Unicode */
529                 ucs_char = ((inspec[0] & 0xF) << 12) + 
530                    ((inspec[1] & 0x3f) << 6) +
531                    (inspec[2] & 0x3f);
532                 if (ucs_char >= 0x800) {
533                     ucs2_to_vtf7(outspec, ucs_char, output_cnt);
534                     return 3;
535                 }
536
537 #if 0 /* I do not see longer sequences supported by OpenVMS */
538       /* Maybe some one can fix this later */
539             } else if ((*inspec & 0xF8) == 0xF0) {
540                 /* 4 byte Unicode */
541                 /* UCS-4 to UCS-2 */
542             } else if ((*inspec & 0xFC) == 0xF8) {
543                 /* 5 byte Unicode */
544                 /* UCS-4 to UCS-2 */
545             } else if ((*inspec & 0xFE) == 0xFC) {
546                 /* 6 byte Unicode */
547                 /* UCS-4 to UCS-2 */
548 #endif
549             }
550         }
551
552         /* High bit set, but not a Unicode character! */
553
554         /* Non printing DECMCS or ISO Latin-1 character? */
555         if (*inspec <= 0x9F) {
556         int hex;
557             outspec[0] = '^';
558             outspec++;
559             hex = (*inspec >> 4) & 0xF;
560             if (hex < 0xA)
561                 outspec[1] = hex + '0';
562             else {
563                 outspec[1] = (hex - 9) + 'A';
564             }
565             hex = *inspec & 0xF;
566             if (hex < 0xA)
567                 outspec[2] = hex + '0';
568             else {
569                 outspec[2] = (hex - 9) + 'A';
570             }
571             *output_cnt = 3;
572             return 1;
573         } else if (*inspec == 0xA0) {
574             outspec[0] = '^';
575             outspec[1] = 'A';
576             outspec[2] = '0';
577             *output_cnt = 3;
578             return 1;
579         } else if (*inspec == 0xFF) {
580             outspec[0] = '^';
581             outspec[1] = 'F';
582             outspec[2] = 'F';
583             *output_cnt = 3;
584             return 1;
585         }
586         *outspec = *inspec;
587         *output_cnt = 1;
588         return 1;
589     }
590
591     /* Is this a macro that needs to be passed through?
592      * Macros start with $( and an alpha character, followed
593      * by a string of alpha numeric characters ending with a )
594      * If this does not match, then encode it as ODS-5.
595      */
596     if ((inspec[0] == '$') && (inspec[1] == '(')) {
597     int tcnt;
598
599         if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
600             tcnt = 3;
601             outspec[0] = inspec[0];
602             outspec[1] = inspec[1];
603             outspec[2] = inspec[2];
604
605             while(isalnum(inspec[tcnt]) ||
606                   (inspec[2] == '.') || (inspec[2] == '_')) {
607                 outspec[tcnt] = inspec[tcnt];
608                 tcnt++;
609             }
610             if (inspec[tcnt] == ')') {
611                 outspec[tcnt] = inspec[tcnt];
612                 tcnt++;
613                 *output_cnt = tcnt;
614                 return tcnt;
615             }
616         }
617     }
618
619     switch (*inspec) {
620     case 0x7f:
621         outspec[0] = '^';
622         outspec[1] = '7';
623         outspec[2] = 'F';
624         *output_cnt = 3;
625         return 1;
626         break;
627     case '?':
628         if (decc_efs_charset == 0)
629           outspec[0] = '%';
630         else
631           outspec[0] = '?';
632         *output_cnt = 1;
633         return 1;
634         break;
635     case '.':
636     case '~':
637     case '!':
638     case '#':
639     case '&':
640     case '\'':
641     case '`':
642     case '(':
643     case ')':
644     case '+':
645     case '@':
646     case '{':
647     case '}':
648     case ',':
649     case ';':
650     case '[':
651     case ']':
652     case '%':
653     case '^':
654     case '\\':
655         /* Don't escape again if following character is 
656          * already something we escape.
657          */
658         if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
659             *outspec = *inspec;
660             *output_cnt = 1;
661             return 1;
662             break;
663         }
664         /* But otherwise fall through and escape it. */
665     case '=':
666         /* Assume that this is to be escaped */
667         outspec[0] = '^';
668         outspec[1] = *inspec;
669         *output_cnt = 2;
670         return 1;
671         break;
672     case ' ': /* space */
673         /* Assume that this is to be escaped */
674         outspec[0] = '^';
675         outspec[1] = '_';
676         *output_cnt = 2;
677         return 1;
678         break;
679     default:
680         *outspec = *inspec;
681         *output_cnt = 1;
682         return 1;
683         break;
684     }
685 }
686
687
688 /* This handles the expansion of a '^' prefix to the proper character
689  * in a UNIX file specification.
690  *
691  * The output count variable contains the number of characters added
692  * to the output string.
693  *
694  * The return value is the number of characters read from the input
695  * string
696  */
697 static int copy_expand_vms_filename_escape
698   (char *outspec, const char *inspec, int *output_cnt)
699 {
700 int count;
701 int scnt;
702
703     count = 0;
704     *output_cnt = 0;
705     if (*inspec == '^') {
706         inspec++;
707         switch (*inspec) {
708         /* Spaces and non-trailing dots should just be passed through, 
709          * but eat the escape character.
710          */
711         case '.':
712             *outspec = *inspec;
713             count += 2;
714             (*output_cnt)++;
715             break;
716         case '_': /* space */
717             *outspec = ' ';
718             count += 2;
719             (*output_cnt)++;
720             break;
721         case '^':
722             /* Hmm.  Better leave the escape escaped. */
723             outspec[0] = '^';
724             outspec[1] = '^';
725             count += 2;
726             (*output_cnt) += 2;
727             break;
728         case 'U': /* Unicode - FIX-ME this is wrong. */
729             inspec++;
730             count++;
731             scnt = strspn(inspec, "0123456789ABCDEFabcdef");
732             if (scnt == 4) {
733                 unsigned int c1, c2;
734                 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
735                 outspec[0] == c1 & 0xff;
736                 outspec[1] == c2 & 0xff;
737                 if (scnt > 1) {
738                     (*output_cnt) += 2;
739                     count += 4;
740                 }
741             }
742             else {
743                 /* Error - do best we can to continue */
744                 *outspec = 'U';
745                 outspec++;
746                 (*output_cnt++);
747                 *outspec = *inspec;
748                 count++;
749                 (*output_cnt++);
750             }
751             break;
752         default:
753             scnt = strspn(inspec, "0123456789ABCDEFabcdef");
754             if (scnt == 2) {
755                 /* Hex encoded */
756                 unsigned int c1;
757                 scnt = sscanf(inspec, "%2x", &c1);
758                 outspec[0] = c1 & 0xff;
759                 if (scnt > 0) {
760                     (*output_cnt++);
761                     count += 2;
762                 }
763             }
764             else {
765                 *outspec = *inspec;
766                 count++;
767                 (*output_cnt++);
768             }
769         }
770     }
771     else {
772         *outspec = *inspec;
773         count++;
774         (*output_cnt)++;
775     }
776     return count;
777 }
778
779 #ifdef sys$filescan
780 #undef sys$filescan
781 int sys$filescan
782    (const struct dsc$descriptor_s * srcstr,
783     struct filescan_itmlst_2 * valuelist,
784     unsigned long * fldflags,
785     struct dsc$descriptor_s *auxout,
786     unsigned short * retlen);
787 #endif
788
789 /* vms_split_path - Verify that the input file specification is a
790  * VMS format file specification, and provide pointers to the components of
791  * it.  With EFS format filenames, this is virtually the only way to
792  * parse a VMS path specification into components.
793  *
794  * If the sum of the components do not add up to the length of the
795  * string, then the passed file specification is probably a UNIX style
796  * path.
797  */
798 static int vms_split_path
799    (const char * path,
800     char * * volume,
801     int * vol_len,
802     char * * root,
803     int * root_len,
804     char * * dir,
805     int * dir_len,
806     char * * name,
807     int * name_len,
808     char * * ext,
809     int * ext_len,
810     char * * version,
811     int * ver_len)
812 {
813 struct dsc$descriptor path_desc;
814 int status;
815 unsigned long flags;
816 int ret_stat;
817 struct filescan_itmlst_2 item_list[9];
818 const int filespec = 0;
819 const int nodespec = 1;
820 const int devspec = 2;
821 const int rootspec = 3;
822 const int dirspec = 4;
823 const int namespec = 5;
824 const int typespec = 6;
825 const int verspec = 7;
826
827     /* Assume the worst for an easy exit */
828     ret_stat = -1;
829     *volume = NULL;
830     *vol_len = 0;
831     *root = NULL;
832     *root_len = 0;
833     *dir = NULL;
834     *dir_len;
835     *name = NULL;
836     *name_len = 0;
837     *ext = NULL;
838     *ext_len = 0;
839     *version = NULL;
840     *ver_len = 0;
841
842     path_desc.dsc$a_pointer = (char *)path; /* cast ok */
843     path_desc.dsc$w_length = strlen(path);
844     path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
845     path_desc.dsc$b_class = DSC$K_CLASS_S;
846
847     /* Get the total length, if it is shorter than the string passed
848      * then this was probably not a VMS formatted file specification
849      */
850     item_list[filespec].itmcode = FSCN$_FILESPEC;
851     item_list[filespec].length = 0;
852     item_list[filespec].component = NULL;
853
854     /* If the node is present, then it gets considered as part of the
855      * volume name to hopefully make things simple.
856      */
857     item_list[nodespec].itmcode = FSCN$_NODE;
858     item_list[nodespec].length = 0;
859     item_list[nodespec].component = NULL;
860
861     item_list[devspec].itmcode = FSCN$_DEVICE;
862     item_list[devspec].length = 0;
863     item_list[devspec].component = NULL;
864
865     /* root is a special case,  adding it to either the directory or
866      * the device components will probalby complicate things for the
867      * callers of this routine, so leave it separate.
868      */
869     item_list[rootspec].itmcode = FSCN$_ROOT;
870     item_list[rootspec].length = 0;
871     item_list[rootspec].component = NULL;
872
873     item_list[dirspec].itmcode = FSCN$_DIRECTORY;
874     item_list[dirspec].length = 0;
875     item_list[dirspec].component = NULL;
876
877     item_list[namespec].itmcode = FSCN$_NAME;
878     item_list[namespec].length = 0;
879     item_list[namespec].component = NULL;
880
881     item_list[typespec].itmcode = FSCN$_TYPE;
882     item_list[typespec].length = 0;
883     item_list[typespec].component = NULL;
884
885     item_list[verspec].itmcode = FSCN$_VERSION;
886     item_list[verspec].length = 0;
887     item_list[verspec].component = NULL;
888
889     item_list[8].itmcode = 0;
890     item_list[8].length = 0;
891     item_list[8].component = NULL;
892
893     status = sys$filescan
894        ((const struct dsc$descriptor_s *)&path_desc, item_list,
895         &flags, NULL, NULL);
896     _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
897
898     /* If we parsed it successfully these two lengths should be the same */
899     if (path_desc.dsc$w_length != item_list[filespec].length)
900         return ret_stat;
901
902     /* If we got here, then it is a VMS file specification */
903     ret_stat = 0;
904
905     /* set the volume name */
906     if (item_list[nodespec].length > 0) {
907         *volume = item_list[nodespec].component;
908         *vol_len = item_list[nodespec].length + item_list[devspec].length;
909     }
910     else {
911         *volume = item_list[devspec].component;
912         *vol_len = item_list[devspec].length;
913     }
914
915     *root = item_list[rootspec].component;
916     *root_len = item_list[rootspec].length;
917
918     *dir = item_list[dirspec].component;
919     *dir_len = item_list[dirspec].length;
920
921     /* Now fun with versions and EFS file specifications
922      * The parser can not tell the difference when a "." is a version
923      * delimiter or a part of the file specification.
924      */
925     if ((decc_efs_charset) && 
926         (item_list[verspec].length > 0) &&
927         (item_list[verspec].component[0] == '.')) {
928         *name = item_list[namespec].component;
929         *name_len = item_list[namespec].length + item_list[typespec].length;
930         *ext = item_list[verspec].component;
931         *ext_len = item_list[verspec].length;
932         *version = NULL;
933         *ver_len = 0;
934     }
935     else {
936         *name = item_list[namespec].component;
937         *name_len = item_list[namespec].length;
938         *ext = item_list[typespec].component;
939         *ext_len = item_list[typespec].length;
940         *version = item_list[verspec].component;
941         *ver_len = item_list[verspec].length;
942     }
943     return ret_stat;
944 }
945
946 /* Routine to determine if the file specification ends with .dir */
947 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
948
949     /* e_len must be 4, and version must be <= 2 characters */
950     if (e_len != 4 || vs_len > 2)
951         return 0;
952
953     /* If a version number is present, it needs to be one */
954     if ((vs_len == 2) && (vs_spec[1] != '1'))
955         return 0;
956
957     /* Look for the DIR on the extension */
958     if (vms_process_case_tolerant) {
959         if ((toupper(e_spec[1]) == 'D') &&
960             (toupper(e_spec[2]) == 'I') &&
961             (toupper(e_spec[3]) == 'R')) {
962             return 1;
963         }
964     } else {
965         /* Directory extensions are supposed to be in upper case only */
966         /* I would not be surprised if this rule can not be enforced */
967         /* if and when someone fully debugs the case sensitive mode */
968         if ((e_spec[1] == 'D') &&
969             (e_spec[2] == 'I') &&
970             (e_spec[3] == 'R')) {
971             return 1;
972         }
973     }
974     return 0;
975 }
976
977
978 /* my_maxidx
979  * Routine to retrieve the maximum equivalence index for an input
980  * logical name.  Some calls to this routine have no knowledge if
981  * the variable is a logical or not.  So on error we return a max
982  * index of zero.
983  */
984 /*{{{int my_maxidx(const char *lnm) */
985 static int
986 my_maxidx(const char *lnm)
987 {
988     int status;
989     int midx;
990     int attr = LNM$M_CASE_BLIND;
991     struct dsc$descriptor lnmdsc;
992     struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
993                                 {0, 0, 0, 0}};
994
995     lnmdsc.dsc$w_length = strlen(lnm);
996     lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
997     lnmdsc.dsc$b_class = DSC$K_CLASS_S;
998     lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
999
1000     status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
1001     if ((status & 1) == 0)
1002        midx = 0;
1003
1004     return (midx);
1005 }
1006 /*}}}*/
1007
1008 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
1009 int
1010 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
1011   struct dsc$descriptor_s **tabvec, unsigned long int flags)
1012 {
1013     const char *cp1;
1014     char uplnm[LNM$C_NAMLENGTH+1], *cp2;
1015     unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
1016     unsigned long int retsts, attr = LNM$M_CASE_BLIND;
1017     int midx;
1018     unsigned char acmode;
1019     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1020                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1021     struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
1022                                  {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
1023                                  {0, 0, 0, 0}};
1024     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1025 #if defined(PERL_IMPLICIT_CONTEXT)
1026     pTHX = NULL;
1027     if (PL_curinterp) {
1028       aTHX = PERL_GET_INTERP;
1029     } else {
1030       aTHX = NULL;
1031     }
1032 #endif
1033
1034     if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
1035       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
1036     }
1037     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1038       *cp2 = _toupper(*cp1);
1039       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1040         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1041         return 0;
1042       }
1043     }
1044     lnmdsc.dsc$w_length = cp1 - lnm;
1045     lnmdsc.dsc$a_pointer = uplnm;
1046     uplnm[lnmdsc.dsc$w_length] = '\0';
1047     secure = flags & PERL__TRNENV_SECURE;
1048     acmode = secure ? PSL$C_EXEC : PSL$C_USER;
1049     if (!tabvec || !*tabvec) tabvec = env_tables;
1050
1051     for (curtab = 0; tabvec[curtab]; curtab++) {
1052       if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1053         if (!ivenv && !secure) {
1054           char *eq, *end;
1055           int i;
1056           if (!environ) {
1057             ivenv = 1; 
1058 #if defined(PERL_IMPLICIT_CONTEXT)
1059             if (aTHX == NULL) {
1060                 fprintf(stderr,
1061                     "Can't read CRTL environ\n");
1062             } else
1063 #endif
1064                 Perl_warn(aTHX_ "Can't read CRTL environ\n");
1065             continue;
1066           }
1067           retsts = SS$_NOLOGNAM;
1068           for (i = 0; environ[i]; i++) { 
1069             if ((eq = strchr(environ[i],'=')) && 
1070                 lnmdsc.dsc$w_length == (eq - environ[i]) &&
1071                 !strncmp(environ[i],uplnm,eq - environ[i])) {
1072               eq++;
1073               for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
1074               if (!eqvlen) continue;
1075               retsts = SS$_NORMAL;
1076               break;
1077             }
1078           }
1079           if (retsts != SS$_NOLOGNAM) break;
1080         }
1081       }
1082       else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1083                !str$case_blind_compare(&tmpdsc,&clisym)) {
1084         if (!ivsym && !secure) {
1085           unsigned short int deflen = LNM$C_NAMLENGTH;
1086           struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1087           /* dynamic dsc to accomodate possible long value */
1088           _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
1089           retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1090           if (retsts & 1) { 
1091             if (eqvlen > MAX_DCL_SYMBOL) {
1092               set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1093               eqvlen = MAX_DCL_SYMBOL;
1094               /* Special hack--we might be called before the interpreter's */
1095               /* fully initialized, in which case either thr or PL_curcop */
1096               /* might be bogus. We have to check, since ckWARN needs them */
1097               /* both to be valid if running threaded */
1098 #if defined(PERL_IMPLICIT_CONTEXT)
1099               if (aTHX == NULL) {
1100                   fprintf(stderr,
1101                      "Value of CLI symbol \"%s\" too long",lnm);
1102               } else
1103 #endif
1104                 if (ckWARN(WARN_MISC)) {
1105                   Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1106                 }
1107             }
1108             strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1109           }
1110           _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1111           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1112           if (retsts == LIB$_NOSUCHSYM) continue;
1113           break;
1114         }
1115       }
1116       else if (!ivlnm) {
1117         if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1118           midx = my_maxidx(lnm);
1119           for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1120             lnmlst[1].bufadr = cp2;
1121             eqvlen = 0;
1122             retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1123             if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1124             if (retsts == SS$_NOLOGNAM) break;
1125             /* PPFs have a prefix */
1126             if (
1127 #if INTSIZE == 4
1128                  *((int *)uplnm) == *((int *)"SYS$")                    &&
1129 #endif
1130                  eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00        &&
1131                  ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT"))  ||
1132                    (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT"))   ||
1133                    (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR"))   ||
1134                    (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) )  ) {
1135               memmove(eqv,eqv+4,eqvlen-4);
1136               eqvlen -= 4;
1137             }
1138             cp2 += eqvlen;
1139             *cp2 = '\0';
1140           }
1141           if ((retsts == SS$_IVLOGNAM) ||
1142               (retsts == SS$_NOLOGNAM)) { continue; }
1143         }
1144         else {
1145           retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1146           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1147           if (retsts == SS$_NOLOGNAM) continue;
1148           eqv[eqvlen] = '\0';
1149         }
1150         eqvlen = strlen(eqv);
1151         break;
1152       }
1153     }
1154     if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1155     else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1156              retsts == SS$_IVLOGNAM   || retsts == SS$_IVLOGTAB   ||
1157              retsts == SS$_NOLOGNAM) {
1158       set_errno(EINVAL);  set_vaxc_errno(retsts);
1159     }
1160     else _ckvmssts_noperl(retsts);
1161     return 0;
1162 }  /* end of vmstrnenv */
1163 /*}}}*/
1164
1165 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1166 /* Define as a function so we can access statics. */
1167 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1168 {
1169     int flags = 0;
1170
1171 #if defined(PERL_IMPLICIT_CONTEXT)
1172     if (aTHX != NULL)
1173 #endif
1174 #ifdef SECURE_INTERNAL_GETENV
1175         flags = (PL_curinterp ? PL_tainting : will_taint) ?
1176                  PERL__TRNENV_SECURE : 0;
1177 #endif
1178
1179     return vmstrnenv(lnm, eqv, idx, fildev, flags);
1180 }
1181 /*}}}*/
1182
1183 /* my_getenv
1184  * Note: Uses Perl temp to store result so char * can be returned to
1185  * caller; this pointer will be invalidated at next Perl statement
1186  * transition.
1187  * We define this as a function rather than a macro in terms of my_getenv_len()
1188  * so that it'll work when PL_curinterp is undefined (and we therefore can't
1189  * allocate SVs).
1190  */
1191 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1192 char *
1193 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1194 {
1195     const char *cp1;
1196     static char *__my_getenv_eqv = NULL;
1197     char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1198     unsigned long int idx = 0;
1199     int trnsuccess, success, secure, saverr, savvmserr;
1200     int midx, flags;
1201     SV *tmpsv;
1202
1203     midx = my_maxidx(lnm) + 1;
1204
1205     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1206       /* Set up a temporary buffer for the return value; Perl will
1207        * clean it up at the next statement transition */
1208       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1209       if (!tmpsv) return NULL;
1210       eqv = SvPVX(tmpsv);
1211     }
1212     else {
1213       /* Assume no interpreter ==> single thread */
1214       if (__my_getenv_eqv != NULL) {
1215         Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1216       }
1217       else {
1218         Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1219       }
1220       eqv = __my_getenv_eqv;  
1221     }
1222
1223     for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1224     if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1225       int len;
1226       getcwd(eqv,LNM$C_NAMLENGTH);
1227
1228       len = strlen(eqv);
1229
1230       /* Get rid of "000000/ in rooted filespecs */
1231       if (len > 7) {
1232         char * zeros;
1233         zeros = strstr(eqv, "/000000/");
1234         if (zeros != NULL) {
1235           int mlen;
1236           mlen = len - (zeros - eqv) - 7;
1237           memmove(zeros, &zeros[7], mlen);
1238           len = len - 7;
1239           eqv[len] = '\0';
1240         }
1241       }
1242       return eqv;
1243     }
1244     else {
1245       /* Impose security constraints only if tainting */
1246       if (sys) {
1247         /* Impose security constraints only if tainting */
1248         secure = PL_curinterp ? PL_tainting : will_taint;
1249         saverr = errno;  savvmserr = vaxc$errno;
1250       }
1251       else {
1252         secure = 0;
1253       }
1254
1255       flags = 
1256 #ifdef SECURE_INTERNAL_GETENV
1257               secure ? PERL__TRNENV_SECURE : 0
1258 #else
1259               0
1260 #endif
1261       ;
1262
1263       /* For the getenv interface we combine all the equivalence names
1264        * of a search list logical into one value to acquire a maximum
1265        * value length of 255*128 (assuming %ENV is using logicals).
1266        */
1267       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1268
1269       /* If the name contains a semicolon-delimited index, parse it
1270        * off and make sure we only retrieve the equivalence name for 
1271        * that index.  */
1272       if ((cp2 = strchr(lnm,';')) != NULL) {
1273         strcpy(uplnm,lnm);
1274         uplnm[cp2-lnm] = '\0';
1275         idx = strtoul(cp2+1,NULL,0);
1276         lnm = uplnm;
1277         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1278       }
1279
1280       success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1281
1282       /* Discard NOLOGNAM on internal calls since we're often looking
1283        * for an optional name, and this "error" often shows up as the
1284        * (bogus) exit status for a die() call later on.  */
1285       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1286       return success ? eqv : NULL;
1287     }
1288
1289 }  /* end of my_getenv() */
1290 /*}}}*/
1291
1292
1293 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1294 char *
1295 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1296 {
1297     const char *cp1;
1298     char *buf, *cp2;
1299     unsigned long idx = 0;
1300     int midx, flags;
1301     static char *__my_getenv_len_eqv = NULL;
1302     int secure, saverr, savvmserr;
1303     SV *tmpsv;
1304     
1305     midx = my_maxidx(lnm) + 1;
1306
1307     if (PL_curinterp) {  /* Perl interpreter running -- may be threaded */
1308       /* Set up a temporary buffer for the return value; Perl will
1309        * clean it up at the next statement transition */
1310       tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1311       if (!tmpsv) return NULL;
1312       buf = SvPVX(tmpsv);
1313     }
1314     else {
1315       /* Assume no interpreter ==> single thread */
1316       if (__my_getenv_len_eqv != NULL) {
1317         Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1318       }
1319       else {
1320         Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1321       }
1322       buf = __my_getenv_len_eqv;  
1323     }
1324
1325     for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1326     if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1327     char * zeros;
1328
1329       getcwd(buf,LNM$C_NAMLENGTH);
1330       *len = strlen(buf);
1331
1332       /* Get rid of "000000/ in rooted filespecs */
1333       if (*len > 7) {
1334       zeros = strstr(buf, "/000000/");
1335       if (zeros != NULL) {
1336         int mlen;
1337         mlen = *len - (zeros - buf) - 7;
1338         memmove(zeros, &zeros[7], mlen);
1339         *len = *len - 7;
1340         buf[*len] = '\0';
1341         }
1342       }
1343       return buf;
1344     }
1345     else {
1346       if (sys) {
1347         /* Impose security constraints only if tainting */
1348         secure = PL_curinterp ? PL_tainting : will_taint;
1349         saverr = errno;  savvmserr = vaxc$errno;
1350       }
1351       else {
1352         secure = 0;
1353       }
1354
1355       flags = 
1356 #ifdef SECURE_INTERNAL_GETENV
1357               secure ? PERL__TRNENV_SECURE : 0
1358 #else
1359               0
1360 #endif
1361       ;
1362
1363       flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1364
1365       if ((cp2 = strchr(lnm,';')) != NULL) {
1366         strcpy(buf,lnm);
1367         buf[cp2-lnm] = '\0';
1368         idx = strtoul(cp2+1,NULL,0);
1369         lnm = buf;
1370         flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1371       }
1372
1373       *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1374
1375       /* Get rid of "000000/ in rooted filespecs */
1376       if (*len > 7) {
1377       char * zeros;
1378         zeros = strstr(buf, "/000000/");
1379         if (zeros != NULL) {
1380           int mlen;
1381           mlen = *len - (zeros - buf) - 7;
1382           memmove(zeros, &zeros[7], mlen);
1383           *len = *len - 7;
1384           buf[*len] = '\0';
1385         }
1386       }
1387
1388       /* Discard NOLOGNAM on internal calls since we're often looking
1389        * for an optional name, and this "error" often shows up as the
1390        * (bogus) exit status for a die() call later on.  */
1391       if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1392       return *len ? buf : NULL;
1393     }
1394
1395 }  /* end of my_getenv_len() */
1396 /*}}}*/
1397
1398 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1399
1400 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1401
1402 /*{{{ void prime_env_iter() */
1403 void
1404 prime_env_iter(void)
1405 /* Fill the %ENV associative array with all logical names we can
1406  * find, in preparation for iterating over it.
1407  */
1408 {
1409   static int primed = 0;
1410   HV *seenhv = NULL, *envhv;
1411   SV *sv = NULL;
1412   char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1413   unsigned short int chan;
1414 #ifndef CLI$M_TRUSTED
1415 #  define CLI$M_TRUSTED 0x40  /* Missing from VAXC headers */
1416 #endif
1417   unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1418   unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1419   long int i;
1420   bool have_sym = FALSE, have_lnm = FALSE;
1421   struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1422   $DESCRIPTOR(cmddsc,cmd);    $DESCRIPTOR(nldsc,"_NLA0:");
1423   $DESCRIPTOR(clidsc,"DCL");  $DESCRIPTOR(clitabdsc,"DCLTABLES");
1424   $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1425   $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam); 
1426 #if defined(PERL_IMPLICIT_CONTEXT)
1427   pTHX;
1428 #endif
1429 #if defined(USE_ITHREADS)
1430   static perl_mutex primenv_mutex;
1431   MUTEX_INIT(&primenv_mutex);
1432 #endif
1433
1434 #if defined(PERL_IMPLICIT_CONTEXT)
1435     /* We jump through these hoops because we can be called at */
1436     /* platform-specific initialization time, which is before anything is */
1437     /* set up--we can't even do a plain dTHX since that relies on the */
1438     /* interpreter structure to be initialized */
1439     if (PL_curinterp) {
1440       aTHX = PERL_GET_INTERP;
1441     } else {
1442       /* we never get here because the NULL pointer will cause the */
1443       /* several of the routines called by this routine to access violate */
1444
1445       /* This routine is only called by hv.c/hv_iterinit which has a */
1446       /* context, so the real fix may be to pass it through instead of */
1447       /* the hoops above */
1448       aTHX = NULL;
1449     }
1450 #endif
1451
1452   if (primed || !PL_envgv) return;
1453   MUTEX_LOCK(&primenv_mutex);
1454   if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1455   envhv = GvHVn(PL_envgv);
1456   /* Perform a dummy fetch as an lval to insure that the hash table is
1457    * set up.  Otherwise, the hv_store() will turn into a nullop. */
1458   (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1459
1460   for (i = 0; env_tables[i]; i++) {
1461      if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1462          !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1463      if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1464   }
1465   if (have_sym || have_lnm) {
1466     long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1467     _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1468     _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1469     _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1470   }
1471
1472   for (i--; i >= 0; i--) {
1473     if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1474       char *start;
1475       int j;
1476       for (j = 0; environ[j]; j++) { 
1477         if (!(start = strchr(environ[j],'='))) {
1478           if (ckWARN(WARN_INTERNAL)) 
1479             Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1480         }
1481         else {
1482           start++;
1483           sv = newSVpv(start,0);
1484           SvTAINTED_on(sv);
1485           (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1486         }
1487       }
1488       continue;
1489     }
1490     else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1491              !str$case_blind_compare(&tmpdsc,&clisym)) {
1492       strcpy(cmd,"Show Symbol/Global *");
1493       cmddsc.dsc$w_length = 20;
1494       if (env_tables[i]->dsc$w_length == 12 &&
1495           (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1496           !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local  *");
1497       flags = defflags | CLI$M_NOLOGNAM;
1498     }
1499     else {
1500       strcpy(cmd,"Show Logical *");
1501       if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1502         strcat(cmd," /Table=");
1503         strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1504         cmddsc.dsc$w_length = strlen(cmd);
1505       }
1506       else cmddsc.dsc$w_length = 14;  /* N.B. We test this below */
1507       flags = defflags | CLI$M_NOCLISYM;
1508     }
1509     
1510     /* Create a new subprocess to execute each command, to exclude the
1511      * remote possibility that someone could subvert a mbx or file used
1512      * to write multiple commands to a single subprocess.
1513      */
1514     do {
1515       retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1516                          0,&riseandshine,0,0,&clidsc,&clitabdsc);
1517       flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1518       defflags &= ~CLI$M_TRUSTED;
1519     } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1520     _ckvmssts(retsts);
1521     if (!buf) Newx(buf,mbxbufsiz + 1,char);
1522     if (seenhv) SvREFCNT_dec(seenhv);
1523     seenhv = newHV();
1524     while (1) {
1525       char *cp1, *cp2, *key;
1526       unsigned long int sts, iosb[2], retlen, keylen;
1527       register U32 hash;
1528
1529       sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1530       if (sts & 1) sts = iosb[0] & 0xffff;
1531       if (sts == SS$_ENDOFFILE) {
1532         int wakect = 0;
1533         while (substs == 0) { sys$hiber(); wakect++;}
1534         if (wakect > 1) sys$wake(0,0);  /* Stole someone else's wake */
1535         _ckvmssts(substs);
1536         break;
1537       }
1538       _ckvmssts(sts);
1539       retlen = iosb[0] >> 16;      
1540       if (!retlen) continue;  /* blank line */
1541       buf[retlen] = '\0';
1542       if (iosb[1] != subpid) {
1543         if (iosb[1]) {
1544           Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1545         }
1546         continue;
1547       }
1548       if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1549         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1550
1551       for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1552       if (*cp1 == '(' || /* Logical name table name */
1553           *cp1 == '='    /* Next eqv of searchlist  */) continue;
1554       if (*cp1 == '"') cp1++;
1555       for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1556       key = cp1;  keylen = cp2 - cp1;
1557       if (keylen && hv_exists(seenhv,key,keylen)) continue;
1558       while (*cp2 && *cp2 != '=') cp2++;
1559       while (*cp2 && *cp2 == '=') cp2++;
1560       while (*cp2 && *cp2 == ' ') cp2++;
1561       if (*cp2 == '"') {  /* String translation; may embed "" */
1562         for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1563         cp2++;  cp1--; /* Skip "" surrounding translation */
1564       }
1565       else {  /* Numeric translation */
1566         for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1567         cp1--;  /* stop on last non-space char */
1568       }
1569       if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1570         Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1571         continue;
1572       }
1573       PERL_HASH(hash,key,keylen);
1574
1575       if (cp1 == cp2 && *cp2 == '.') {
1576         /* A single dot usually means an unprintable character, such as a null
1577          * to indicate a zero-length value.  Get the actual value to make sure.
1578          */
1579         char lnm[LNM$C_NAMLENGTH+1];
1580         char eqv[MAX_DCL_SYMBOL+1];
1581         int trnlen;
1582         strncpy(lnm, key, keylen);
1583         trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1584         sv = newSVpvn(eqv, strlen(eqv));
1585       }
1586       else {
1587         sv = newSVpvn(cp2,cp1 - cp2 + 1);
1588       }
1589
1590       SvTAINTED_on(sv);
1591       hv_store(envhv,key,keylen,sv,hash);
1592       hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1593     }
1594     if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1595       /* get the PPFs for this process, not the subprocess */
1596       const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1597       char eqv[LNM$C_NAMLENGTH+1];
1598       int trnlen, i;
1599       for (i = 0; ppfs[i]; i++) {
1600         trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1601         sv = newSVpv(eqv,trnlen);
1602         SvTAINTED_on(sv);
1603         hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1604       }
1605     }
1606   }
1607   primed = 1;
1608   if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1609   if (buf) Safefree(buf);
1610   if (seenhv) SvREFCNT_dec(seenhv);
1611   MUTEX_UNLOCK(&primenv_mutex);
1612   return;
1613
1614 }  /* end of prime_env_iter */
1615 /*}}}*/
1616
1617
1618 /*{{{ int  vmssetenv(const char *lnm, const char *eqv)*/
1619 /* Define or delete an element in the same "environment" as
1620  * vmstrnenv().  If an element is to be deleted, it's removed from
1621  * the first place it's found.  If it's to be set, it's set in the
1622  * place designated by the first element of the table vector.
1623  * Like setenv() returns 0 for success, non-zero on error.
1624  */
1625 int
1626 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1627 {
1628     const char *cp1;
1629     char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1630     unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1631     int nseg = 0, j;
1632     unsigned long int retsts, usermode = PSL$C_USER;
1633     struct itmlst_3 *ile, *ilist;
1634     struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1635                             eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1636                             tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1637     $DESCRIPTOR(crtlenv,"CRTL_ENV");  $DESCRIPTOR(clisym,"CLISYM");
1638     $DESCRIPTOR(local,"_LOCAL");
1639
1640     if (!lnm) {
1641         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1642         return SS$_IVLOGNAM;
1643     }
1644
1645     for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1646       *cp2 = _toupper(*cp1);
1647       if (cp1 - lnm > LNM$C_NAMLENGTH) {
1648         set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1649         return SS$_IVLOGNAM;
1650       }
1651     }
1652     lnmdsc.dsc$w_length = cp1 - lnm;
1653     if (!tabvec || !*tabvec) tabvec = env_tables;
1654
1655     if (!eqv) {  /* we're deleting n element */
1656       for (curtab = 0; tabvec[curtab]; curtab++) {
1657         if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1658         int i;
1659           for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1660             if ((cp1 = strchr(environ[i],'=')) && 
1661                 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1662                 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1663 #ifdef HAS_SETENV
1664               return setenv(lnm,"",1) ? vaxc$errno : 0;
1665             }
1666           }
1667           ivenv = 1; retsts = SS$_NOLOGNAM;
1668 #else
1669               if (ckWARN(WARN_INTERNAL))
1670                 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1671               ivenv = 1; retsts = SS$_NOSUCHPGM;
1672               break;
1673             }
1674           }
1675 #endif
1676         }
1677         else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1678                  !str$case_blind_compare(&tmpdsc,&clisym)) {
1679           unsigned int symtype;
1680           if (tabvec[curtab]->dsc$w_length == 12 &&
1681               (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1682               !str$case_blind_compare(&tmpdsc,&local)) 
1683             symtype = LIB$K_CLI_LOCAL_SYM;
1684           else symtype = LIB$K_CLI_GLOBAL_SYM;
1685           retsts = lib$delete_symbol(&lnmdsc,&symtype);
1686           if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1687           if (retsts == LIB$_NOSUCHSYM) continue;
1688           break;
1689         }
1690         else if (!ivlnm) {
1691           retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1692           if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1693           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1694           retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1695           if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1696         }
1697       }
1698     }
1699     else {  /* we're defining a value */
1700       if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1701 #ifdef HAS_SETENV
1702         return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1703 #else
1704         if (ckWARN(WARN_INTERNAL))
1705           Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1706         retsts = SS$_NOSUCHPGM;
1707 #endif
1708       }
1709       else {
1710         eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1711         eqvdsc.dsc$w_length  = strlen(eqv);
1712         if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1713             !str$case_blind_compare(&tmpdsc,&clisym)) {
1714           unsigned int symtype;
1715           if (tabvec[0]->dsc$w_length == 12 &&
1716               (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1717                !str$case_blind_compare(&tmpdsc,&local)) 
1718             symtype = LIB$K_CLI_LOCAL_SYM;
1719           else symtype = LIB$K_CLI_GLOBAL_SYM;
1720           retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1721         }
1722         else {
1723           if (!*eqv) eqvdsc.dsc$w_length = 1;
1724           if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1725
1726             nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1727             if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1728               Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1729                           lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1730               eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1731               nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1732             }
1733
1734             Newx(ilist,nseg+1,struct itmlst_3);
1735             ile = ilist;
1736             if (!ile) {
1737               set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1738               return SS$_INSFMEM;
1739             }
1740             memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1741
1742             for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1743               ile->itmcode = LNM$_STRING;
1744               ile->bufadr = c;
1745               if ((j+1) == nseg) {
1746                 ile->buflen = strlen(c);
1747                 /* in case we are truncating one that's too long */
1748                 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1749               }
1750               else {
1751                 ile->buflen = LNM$C_NAMLENGTH;
1752               }
1753             }
1754
1755             retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1756             Safefree (ilist);
1757           }
1758           else {
1759             retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1760           }
1761         }
1762       }
1763     }
1764     if (!(retsts & 1)) {
1765       switch (retsts) {
1766         case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1767         case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1768           set_errno(EVMSERR); break;
1769         case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM: 
1770         case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1771           set_errno(EINVAL); break;
1772         case SS$_NOPRIV:
1773           set_errno(EACCES); break;
1774         default:
1775           _ckvmssts(retsts);
1776           set_errno(EVMSERR);
1777        }
1778        set_vaxc_errno(retsts);
1779        return (int) retsts || 44; /* retsts should never be 0, but just in case */
1780     }
1781     else {
1782       /* We reset error values on success because Perl does an hv_fetch()
1783        * before each hv_store(), and if the thing we're setting didn't
1784        * previously exist, we've got a leftover error message.  (Of course,
1785        * this fails in the face of
1786        *    $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1787        * in that the error reported in $! isn't spurious, 
1788        * but it's right more often than not.)
1789        */
1790       set_errno(0); set_vaxc_errno(retsts);
1791       return 0;
1792     }
1793
1794 }  /* end of vmssetenv() */
1795 /*}}}*/
1796
1797 /*{{{ void  my_setenv(const char *lnm, const char *eqv)*/
1798 /* This has to be a function since there's a prototype for it in proto.h */
1799 void
1800 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1801 {
1802     if (lnm && *lnm) {
1803       int len = strlen(lnm);
1804       if  (len == 7) {
1805         char uplnm[8];
1806         int i;
1807         for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1808         if (!strcmp(uplnm,"DEFAULT")) {
1809           if (eqv && *eqv) my_chdir(eqv);
1810           return;
1811         }
1812     } 
1813 #ifndef RTL_USES_UTC
1814     if (len == 6 || len == 2) {
1815       char uplnm[7];
1816       int i;
1817       for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1818       uplnm[len] = '\0';
1819       if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1820       if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1821     }
1822 #endif
1823   }
1824   (void) vmssetenv(lnm,eqv,NULL);
1825 }
1826 /*}}}*/
1827
1828 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1829 /*  vmssetuserlnm
1830  *  sets a user-mode logical in the process logical name table
1831  *  used for redirection of sys$error
1832  *
1833  *  Fix-me: The pTHX is not needed for this routine, however doio.c
1834  *          is calling it with one instead of using a macro.
1835  *          A macro needs to be added to vmsish.h and doio.c updated to use it.
1836  *
1837  */
1838 void
1839 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1840 {
1841     $DESCRIPTOR(d_tab, "LNM$PROCESS");
1842     struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1843     unsigned long int iss, attr = LNM$M_CONFINE;
1844     unsigned char acmode = PSL$C_USER;
1845     struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1846                                  {0, 0, 0, 0}};
1847     d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1848     d_name.dsc$w_length = strlen(name);
1849
1850     lnmlst[0].buflen = strlen(eqv);
1851     lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1852
1853     iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1854     if (!(iss&1)) lib$signal(iss);
1855 }
1856 /*}}}*/
1857
1858
1859 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1860 /* my_crypt - VMS password hashing
1861  * my_crypt() provides an interface compatible with the Unix crypt()
1862  * C library function, and uses sys$hash_password() to perform VMS
1863  * password hashing.  The quadword hashed password value is returned
1864  * as a NUL-terminated 8 character string.  my_crypt() does not change
1865  * the case of its string arguments; in order to match the behavior
1866  * of LOGINOUT et al., alphabetic characters in both arguments must
1867  *  be upcased by the caller.
1868  *
1869  * - fix me to call ACM services when available
1870  */
1871 char *
1872 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1873 {
1874 #   ifndef UAI$C_PREFERRED_ALGORITHM
1875 #     define UAI$C_PREFERRED_ALGORITHM 127
1876 #   endif
1877     unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1878     unsigned short int salt = 0;
1879     unsigned long int sts;
1880     struct const_dsc {
1881         unsigned short int dsc$w_length;
1882         unsigned char      dsc$b_type;
1883         unsigned char      dsc$b_class;
1884         const char *       dsc$a_pointer;
1885     }  usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1886        txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1887     struct itmlst_3 uailst[3] = {
1888         { sizeof alg,  UAI$_ENCRYPT, &alg, 0},
1889         { sizeof salt, UAI$_SALT,    &salt, 0},
1890         { 0,           0,            NULL,  NULL}};
1891     static char hash[9];
1892
1893     usrdsc.dsc$w_length = strlen(usrname);
1894     usrdsc.dsc$a_pointer = usrname;
1895     if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1896       switch (sts) {
1897         case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1898           set_errno(EACCES);
1899           break;
1900         case RMS$_RNF:
1901           set_errno(ESRCH);  /* There isn't a Unix no-such-user error */
1902           break;
1903         default:
1904           set_errno(EVMSERR);
1905       }
1906       set_vaxc_errno(sts);
1907       if (sts != RMS$_RNF) return NULL;
1908     }
1909
1910     txtdsc.dsc$w_length = strlen(textpasswd);
1911     txtdsc.dsc$a_pointer = textpasswd;
1912     if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1913       set_errno(EVMSERR);  set_vaxc_errno(sts);  return NULL;
1914     }
1915
1916     return (char *) hash;
1917
1918 }  /* end of my_crypt() */
1919 /*}}}*/
1920
1921
1922 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1923 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1924 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1925
1926 /* fixup barenames that are directories for internal use.
1927  * There have been problems with the consistent handling of UNIX
1928  * style directory names when routines are presented with a name that
1929  * has no directory delimitors at all.  So this routine will eventually
1930  * fix the issue.
1931  */
1932 static char * fixup_bare_dirnames(const char * name)
1933 {
1934   if (decc_disable_to_vms_logname_translation) {
1935 /* fix me */
1936   }
1937   return NULL;
1938 }
1939
1940 /* 8.3, remove() is now broken on symbolic links */
1941 static int rms_erase(const char * vmsname);
1942
1943
1944 /* mp_do_kill_file
1945  * A little hack to get around a bug in some implemenation of remove()
1946  * that do not know how to delete a directory
1947  *
1948  * Delete any file to which user has control access, regardless of whether
1949  * delete access is explicitly allowed.
1950  * Limitations: User must have write access to parent directory.
1951  *              Does not block signals or ASTs; if interrupted in midstream
1952  *              may leave file with an altered ACL.
1953  * HANDLE WITH CARE!
1954  */
1955 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1956 static int
1957 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1958 {
1959     char *vmsname;
1960     char *rslt;
1961     unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1962     unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1963     struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1964     struct myacedef {
1965       unsigned char myace$b_length;
1966       unsigned char myace$b_type;
1967       unsigned short int myace$w_flags;
1968       unsigned long int myace$l_access;
1969       unsigned long int myace$l_ident;
1970     } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1971                  ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1972       oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1973      struct itmlst_3
1974        findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1975                      {sizeof oldace, ACL$C_READACE,   &oldace, 0},{0,0,0,0}},
1976        addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1977        dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1978        lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1979        ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1980
1981     /* Expand the input spec using RMS, since the CRTL remove() and
1982      * system services won't do this by themselves, so we may miss
1983      * a file "hiding" behind a logical name or search list. */
1984     vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1985     if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1986
1987     rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1988     if (rslt == NULL) {
1989         PerlMem_free(vmsname);
1990         return -1;
1991       }
1992
1993     /* Erase the file */
1994     rmsts = rms_erase(vmsname);
1995
1996     /* Did it succeed */
1997     if ($VMS_STATUS_SUCCESS(rmsts)) {
1998         PerlMem_free(vmsname);
1999         return 0;
2000       }
2001
2002     /* If not, can changing protections help? */
2003     if (rmsts != RMS$_PRV) {
2004       set_vaxc_errno(rmsts);
2005       PerlMem_free(vmsname);
2006       return -1;
2007     }
2008
2009     /* No, so we get our own UIC to use as a rights identifier,
2010      * and the insert an ACE at the head of the ACL which allows us
2011      * to delete the file.
2012      */
2013     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
2014     fildsc.dsc$w_length = strlen(vmsname);
2015     fildsc.dsc$a_pointer = vmsname;
2016     cxt = 0;
2017     newace.myace$l_ident = oldace.myace$l_ident;
2018     rmsts = -1;
2019     if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
2020       switch (aclsts) {
2021         case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
2022           set_errno(ENOENT); break;
2023         case RMS$_DIR:
2024           set_errno(ENOTDIR); break;
2025         case RMS$_DEV:
2026           set_errno(ENODEV); break;
2027         case RMS$_SYN: case SS$_INVFILFOROP:
2028           set_errno(EINVAL); break;
2029         case RMS$_PRV:
2030           set_errno(EACCES); break;
2031         default:
2032           _ckvmssts_noperl(aclsts);
2033       }
2034       set_vaxc_errno(aclsts);
2035       PerlMem_free(vmsname);
2036       return -1;
2037     }
2038     /* Grab any existing ACEs with this identifier in case we fail */
2039     aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2040     if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2041                     || fndsts == SS$_NOMOREACE ) {
2042       /* Add the new ACE . . . */
2043       if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2044         goto yourroom;
2045
2046       rmsts = rms_erase(vmsname);
2047       if ($VMS_STATUS_SUCCESS(rmsts)) {
2048         rmsts = 0;
2049         }
2050         else {
2051         rmsts = -1;
2052         /* We blew it - dir with files in it, no write priv for
2053          * parent directory, etc.  Put things back the way they were. */
2054         if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2055           goto yourroom;
2056         if (fndsts & 1) {
2057           addlst[0].bufadr = &oldace;
2058           if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2059             goto yourroom;
2060         }
2061       }
2062     }
2063
2064     yourroom:
2065     fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2066     /* We just deleted it, so of course it's not there.  Some versions of
2067      * VMS seem to return success on the unlock operation anyhow (after all
2068      * the unlock is successful), but others don't.
2069      */
2070     if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2071     if (aclsts & 1) aclsts = fndsts;
2072     if (!(aclsts & 1)) {
2073       set_errno(EVMSERR);
2074       set_vaxc_errno(aclsts);
2075     }
2076
2077     PerlMem_free(vmsname);
2078     return rmsts;
2079
2080 }  /* end of kill_file() */
2081 /*}}}*/
2082
2083
2084 /*{{{int do_rmdir(char *name)*/
2085 int
2086 Perl_do_rmdir(pTHX_ const char *name)
2087 {
2088     char * dirfile;
2089     int retval;
2090     Stat_t st;
2091
2092     /* lstat returns a VMS fileified specification of the name */
2093     /* that is looked up, and also lets verifies that this is a directory */
2094
2095     retval = flex_lstat(name, &st);
2096     if (retval != 0) {
2097         char * ret_spec;
2098
2099         /* Due to a historical feature, flex_stat/lstat can not see some */
2100         /* Unix format file names that the rest of the CRTL can see */
2101         /* Fixing that feature will cause some perl tests to fail */
2102         /* So try this one more time. */
2103
2104         retval = lstat(name, &st.crtl_stat);
2105         if (retval != 0)
2106             return -1;
2107
2108         /* force it to a file spec for the kill file to work. */
2109         ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
2110         if (ret_spec == NULL) {
2111             errno = EIO;
2112             return -1;
2113         }
2114     }
2115
2116     if (!S_ISDIR(st.st_mode)) {
2117         errno = ENOTDIR;
2118         retval = -1;
2119     }
2120     else {
2121         dirfile = st.st_devnam;
2122
2123         /* It may be possible for flex_stat to find a file and vmsify() to */
2124         /* fail with ODS-2 specifications.  mp_do_kill_file can not deal */
2125         /* with that case, so fail it */
2126         if (dirfile[0] == 0) {
2127             errno = EIO;
2128             return -1;
2129         }
2130
2131         retval = mp_do_kill_file(aTHX_ dirfile, 1);
2132     }
2133
2134     return retval;
2135
2136 }  /* end of do_rmdir */
2137 /*}}}*/
2138
2139 /* kill_file
2140  * Delete any file to which user has control access, regardless of whether
2141  * delete access is explicitly allowed.
2142  * Limitations: User must have write access to parent directory.
2143  *              Does not block signals or ASTs; if interrupted in midstream
2144  *              may leave file with an altered ACL.
2145  * HANDLE WITH CARE!
2146  */
2147 /*{{{int kill_file(char *name)*/
2148 int
2149 Perl_kill_file(pTHX_ const char *name)
2150 {
2151     char * vmsfile;
2152     Stat_t st;
2153     int rmsts;
2154
2155     /* Convert the filename to VMS format and see if it is a directory */
2156     /* flex_lstat returns a vmsified file specification */
2157     rmsts = flex_lstat(name, &st);
2158     if (rmsts != 0) {
2159
2160         /* Due to a historical feature, flex_stat/lstat can not see some */
2161         /* Unix format file names that the rest of the CRTL can see when */
2162         /* ODS-2 file specifications are in use. */
2163         /* Fixing that feature will cause some perl tests to fail */
2164         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2165         st.st_mode = 0;
2166         vmsfile = (char *) name; /* cast ok */
2167
2168     } else {
2169         vmsfile = st.st_devnam;
2170         if (vmsfile[0] == 0) {
2171             /* It may be possible for flex_stat to find a file and vmsify() */
2172             /* to fail with ODS-2 specifications.  mp_do_kill_file can not */
2173             /* deal with that case, so fail it */
2174             errno = EIO;
2175             return -1;
2176         }
2177     }
2178
2179     /* Remove() is allowed to delete directories, according to the X/Open
2180      * specifications.
2181      * This may need special handling to work with the ACL hacks.
2182      */
2183     if (S_ISDIR(st.st_mode)) {
2184         rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2185         return rmsts;
2186     }
2187
2188     rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2189
2190     /* Need to delete all versions ? */
2191     if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2192         int i = 0;
2193
2194         /* Just use lstat() here as do not need st_dev */
2195         /* and we know that the file is in VMS format or that */
2196         /* because of a historical bug, flex_stat can not see the file */
2197         while (lstat(vmsfile, (stat_t *)&st) == 0) {
2198             rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2199             if (rmsts != 0)
2200                 break;
2201             i++;
2202
2203             /* Make sure that we do not loop forever */
2204             if (i > 32767) {
2205                 errno = EIO;
2206                 rmsts = -1;
2207                 break;
2208             }
2209         }
2210     }
2211
2212     return rmsts;
2213
2214 }  /* end of kill_file() */
2215 /*}}}*/
2216
2217
2218 /*{{{int my_mkdir(char *,Mode_t)*/
2219 int
2220 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2221 {
2222   STRLEN dirlen = strlen(dir);
2223
2224   /* zero length string sometimes gives ACCVIO */
2225   if (dirlen == 0) return -1;
2226
2227   /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2228    * null file name/type.  However, it's commonplace under Unix,
2229    * so we'll allow it for a gain in portability.
2230    */
2231   if (dir[dirlen-1] == '/') {
2232     char *newdir = savepvn(dir,dirlen-1);
2233     int ret = mkdir(newdir,mode);
2234     Safefree(newdir);
2235     return ret;
2236   }
2237   else return mkdir(dir,mode);
2238 }  /* end of my_mkdir */
2239 /*}}}*/
2240
2241 /*{{{int my_chdir(char *)*/
2242 int
2243 Perl_my_chdir(pTHX_ const char *dir)
2244 {
2245   STRLEN dirlen = strlen(dir);
2246
2247   /* zero length string sometimes gives ACCVIO */
2248   if (dirlen == 0) return -1;
2249   const char *dir1;
2250
2251   /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2252    * This does not work if DECC$EFS_CHARSET is active.  Hack it here
2253    * so that existing scripts do not need to be changed.
2254    */
2255   dir1 = dir;
2256   while ((dirlen > 0) && (*dir1 == ' ')) {
2257     dir1++;
2258     dirlen--;
2259   }
2260
2261   /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2262    * that implies
2263    * null file name/type.  However, it's commonplace under Unix,
2264    * so we'll allow it for a gain in portability.
2265    *
2266    *  '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2267    */
2268   if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2269       char *newdir;
2270       int ret;
2271       newdir = PerlMem_malloc(dirlen);
2272       if (newdir ==NULL)
2273           _ckvmssts_noperl(SS$_INSFMEM);
2274       strncpy(newdir, dir1, dirlen-1);
2275       newdir[dirlen-1] = '\0';
2276       ret = chdir(newdir);
2277       PerlMem_free(newdir);
2278       return ret;
2279   }
2280   else return chdir(dir1);
2281 }  /* end of my_chdir */
2282 /*}}}*/
2283
2284
2285 /*{{{int my_chmod(char *, mode_t)*/
2286 int
2287 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2288 {
2289   Stat_t st;
2290   int ret = -1;
2291   char * changefile;
2292   STRLEN speclen = strlen(file_spec);
2293
2294   /* zero length string sometimes gives ACCVIO */
2295   if (speclen == 0) return -1;
2296
2297   /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2298    * that implies null file name/type.  However, it's commonplace under Unix,
2299    * so we'll allow it for a gain in portability.
2300    *
2301    * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2302    * in VMS file.dir notation.
2303    */
2304   changefile = (char *) file_spec; /* cast ok */
2305   ret = flex_lstat(file_spec, &st);
2306   if (ret != 0) {
2307
2308         /* Due to a historical feature, flex_stat/lstat can not see some */
2309         /* Unix format file names that the rest of the CRTL can see when */
2310         /* ODS-2 file specifications are in use. */
2311         /* Fixing that feature will cause some perl tests to fail */
2312         /* [.lib.ExtUtils.t]Manifest.t is one of them */
2313         st.st_mode = 0;
2314
2315   } else {
2316       /* It may be possible to get here with nothing in st_devname */
2317       /* chmod still may work though */
2318       if (st.st_devnam[0] != 0) {
2319           changefile = st.st_devnam;
2320       }
2321   }
2322   ret = chmod(changefile, mode);
2323   return ret;
2324 }  /* end of my_chmod */
2325 /*}}}*/
2326
2327
2328 /*{{{FILE *my_tmpfile()*/
2329 FILE *
2330 my_tmpfile(void)
2331 {
2332   FILE *fp;
2333   char *cp;
2334
2335   if ((fp = tmpfile())) return fp;
2336
2337   cp = PerlMem_malloc(L_tmpnam+24);
2338   if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2339
2340   if (decc_filename_unix_only == 0)
2341     strcpy(cp,"Sys$Scratch:");
2342   else
2343     strcpy(cp,"/tmp/");
2344   tmpnam(cp+strlen(cp));
2345   strcat(cp,".Perltmp");
2346   fp = fopen(cp,"w+","fop=dlt");
2347   PerlMem_free(cp);
2348   return fp;
2349 }
2350 /*}}}*/
2351
2352
2353 #ifndef HOMEGROWN_POSIX_SIGNALS
2354 /*
2355  * The C RTL's sigaction fails to check for invalid signal numbers so we 
2356  * help it out a bit.  The docs are correct, but the actual routine doesn't
2357  * do what the docs say it will.
2358  */
2359 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2360 int
2361 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act, 
2362                    struct sigaction* oact)
2363 {
2364   if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2365         SETERRNO(EINVAL, SS$_INVARG);
2366         return -1;
2367   }
2368   return sigaction(sig, act, oact);
2369 }
2370 /*}}}*/
2371 #endif
2372
2373 #ifdef KILL_BY_SIGPRC
2374 #include <errnodef.h>
2375
2376 /* We implement our own kill() using the undocumented system service
2377    sys$sigprc for one of two reasons:
2378
2379    1.) If the kill() in an older CRTL uses sys$forcex, causing the
2380    target process to do a sys$exit, which usually can't be handled 
2381    gracefully...certainly not by Perl and the %SIG{} mechanism.
2382
2383    2.) If the kill() in the CRTL can't be called from a signal
2384    handler without disappearing into the ether, i.e., the signal
2385    it purportedly sends is never trapped. Still true as of VMS 7.3.
2386
2387    sys$sigprc has the same parameters as sys$forcex, but throws an exception
2388    in the target process rather than calling sys$exit.
2389
2390    Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2391    on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2392    provide.  On VMS 7.0+ this is taken care of by doing sys$sigprc
2393    with condition codes C$_SIG0+nsig*8, catching the exception on the 
2394    target process and resignaling with appropriate arguments.
2395
2396    But we don't have that VMS 7.0+ exception handler, so if you
2397    Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS.  Oh well.
2398
2399    Also note that SIGTERM is listed in the docs as being "unimplemented",
2400    yet always seems to be signaled with a VMS condition code of 4 (and
2401    correctly handled for that code).  So we hardwire it in.
2402
2403    Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2404    number to see if it's valid.  So Perl_my_kill(pid,0) returns -1 rather
2405    than signalling with an unrecognized (and unhandled by CRTL) code.
2406 */
2407
2408 #define _MY_SIG_MAX 28
2409
2410 static unsigned int
2411 Perl_sig_to_vmscondition_int(int sig)
2412 {
2413     static unsigned int sig_code[_MY_SIG_MAX+1] = 
2414     {
2415         0,                  /*  0 ZERO     */
2416         SS$_HANGUP,         /*  1 SIGHUP   */
2417         SS$_CONTROLC,       /*  2 SIGINT   */
2418         SS$_CONTROLY,       /*  3 SIGQUIT  */
2419         SS$_RADRMOD,        /*  4 SIGILL   */
2420         SS$_BREAK,          /*  5 SIGTRAP  */
2421         SS$_OPCCUS,         /*  6 SIGABRT  */
2422         SS$_COMPAT,         /*  7 SIGEMT   */
2423 #ifdef __VAX                      
2424         SS$_FLTOVF,         /*  8 SIGFPE VAX */
2425 #else                             
2426         SS$_HPARITH,        /*  8 SIGFPE AXP */
2427 #endif                            
2428         SS$_ABORT,          /*  9 SIGKILL  */
2429         SS$_ACCVIO,         /* 10 SIGBUS   */
2430         SS$_ACCVIO,         /* 11 SIGSEGV  */
2431         SS$_BADPARAM,       /* 12 SIGSYS   */
2432         SS$_NOMBX,          /* 13 SIGPIPE  */
2433         SS$_ASTFLT,         /* 14 SIGALRM  */
2434         4,                  /* 15 SIGTERM  */
2435         0,                  /* 16 SIGUSR1  */
2436         0,                  /* 17 SIGUSR2  */
2437         0,                  /* 18 */
2438         0,                  /* 19 */
2439         0,                  /* 20 SIGCHLD  */
2440         0,                  /* 21 SIGCONT  */
2441         0,                  /* 22 SIGSTOP  */
2442         0,                  /* 23 SIGTSTP  */
2443         0,                  /* 24 SIGTTIN  */
2444         0,                  /* 25 SIGTTOU  */
2445         0,                  /* 26 */
2446         0,                  /* 27 */
2447         0                   /* 28 SIGWINCH  */
2448     };
2449
2450 #if __VMS_VER >= 60200000
2451     static int initted = 0;
2452     if (!initted) {
2453         initted = 1;
2454         sig_code[16] = C$_SIGUSR1;
2455         sig_code[17] = C$_SIGUSR2;
2456 #if __CRTL_VER >= 70000000
2457         sig_code[20] = C$_SIGCHLD;
2458 #endif
2459 #if __CRTL_VER >= 70300000
2460         sig_code[28] = C$_SIGWINCH;
2461 #endif
2462     }
2463 #endif
2464
2465     if (sig < _SIG_MIN) return 0;
2466     if (sig > _MY_SIG_MAX) return 0;
2467     return sig_code[sig];
2468 }
2469
2470 unsigned int
2471 Perl_sig_to_vmscondition(int sig)
2472 {
2473 #ifdef SS$_DEBUG
2474     if (vms_debug_on_exception != 0)
2475         lib$signal(SS$_DEBUG);
2476 #endif
2477     return Perl_sig_to_vmscondition_int(sig);
2478 }
2479
2480
2481 int
2482 Perl_my_kill(int pid, int sig)
2483 {
2484     dTHX;
2485     int iss;
2486     unsigned int code;
2487     int sys$sigprc(unsigned int *pidadr,
2488                      struct dsc$descriptor_s *prcname,
2489                      unsigned int code);
2490
2491      /* sig 0 means validate the PID */
2492     /*------------------------------*/
2493     if (sig == 0) {
2494         const unsigned long int jpicode = JPI$_PID;
2495         pid_t ret_pid;
2496         int status;
2497         status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2498         if ($VMS_STATUS_SUCCESS(status))
2499            return 0;
2500         switch (status) {
2501         case SS$_NOSUCHNODE:
2502         case SS$_UNREACHABLE:
2503         case SS$_NONEXPR:
2504            errno = ESRCH;
2505            break;
2506         case SS$_NOPRIV:
2507            errno = EPERM;
2508            break;
2509         default:
2510            errno = EVMSERR;
2511         }
2512         vaxc$errno=status;
2513         return -1;
2514     }
2515
2516     code = Perl_sig_to_vmscondition_int(sig);
2517
2518     if (!code) {
2519         SETERRNO(EINVAL, SS$_BADPARAM);
2520         return -1;
2521     }
2522
2523     /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2524      * signals are to be sent to multiple processes.
2525      *  pid = 0 - all processes in group except ones that the system exempts
2526      *  pid = -1 - all processes except ones that the system exempts
2527      *  pid = -n - all processes in group (abs(n)) except ... 
2528      * For now, just report as not supported.
2529      */
2530
2531     if (pid <= 0) {
2532         SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2533         return -1;
2534     }
2535
2536     iss = sys$sigprc((unsigned int *)&pid,0,code);
2537     if (iss&1) return 0;
2538
2539     switch (iss) {
2540       case SS$_NOPRIV:
2541         set_errno(EPERM);  break;
2542       case SS$_NONEXPR:  
2543       case SS$_NOSUCHNODE:
2544       case SS$_UNREACHABLE:
2545         set_errno(ESRCH);  break;
2546       case SS$_INSFMEM:
2547         set_errno(ENOMEM); break;
2548       default:
2549         _ckvmssts_noperl(iss);
2550         set_errno(EVMSERR);
2551     } 
2552     set_vaxc_errno(iss);
2553  
2554     return -1;
2555 }
2556 #endif
2557
2558 /* Routine to convert a VMS status code to a UNIX status code.
2559 ** More tricky than it appears because of conflicting conventions with
2560 ** existing code.
2561 **
2562 ** VMS status codes are a bit mask, with the least significant bit set for
2563 ** success.
2564 **
2565 ** Special UNIX status of EVMSERR indicates that no translation is currently
2566 ** available, and programs should check the VMS status code.
2567 **
2568 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2569 ** decoding.
2570 */
2571
2572 #ifndef C_FACILITY_NO
2573 #define C_FACILITY_NO 0x350000
2574 #endif
2575 #ifndef DCL_IVVERB
2576 #define DCL_IVVERB 0x38090
2577 #endif
2578
2579 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2580 {
2581 int facility;
2582 int fac_sp;
2583 int msg_no;
2584 int msg_status;
2585 int unix_status;
2586
2587   /* Assume the best or the worst */
2588   if (vms_status & STS$M_SUCCESS)
2589     unix_status = 0;
2590   else
2591     unix_status = EVMSERR;
2592
2593   msg_status = vms_status & ~STS$M_CONTROL;
2594
2595   facility = vms_status & STS$M_FAC_NO;
2596   fac_sp = vms_status & STS$M_FAC_SP;
2597   msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2598
2599   if (((facility == 0) || (fac_sp == 0))  && (child_flag == 0)) {
2600     switch(msg_no) {
2601     case SS$_NORMAL:
2602         unix_status = 0;
2603         break;
2604     case SS$_ACCVIO:
2605         unix_status = EFAULT;
2606         break;
2607     case SS$_DEVOFFLINE:
2608         unix_status = EBUSY;
2609         break;
2610     case SS$_CLEARED:
2611         unix_status = ENOTCONN;
2612         break;
2613     case SS$_IVCHAN:
2614     case SS$_IVLOGNAM:
2615     case SS$_BADPARAM:
2616     case SS$_IVLOGTAB:
2617     case SS$_NOLOGNAM:
2618     case SS$_NOLOGTAB:
2619     case SS$_INVFILFOROP:
2620     case SS$_INVARG:
2621     case SS$_NOSUCHID:
2622     case SS$_IVIDENT:
2623         unix_status = EINVAL;
2624         break;
2625     case SS$_UNSUPPORTED:
2626         unix_status = ENOTSUP;
2627         break;
2628     case SS$_FILACCERR:
2629     case SS$_NOGRPPRV:
2630     case SS$_NOSYSPRV:
2631         unix_status = EACCES;
2632         break;
2633     case SS$_DEVICEFULL:
2634         unix_status = ENOSPC;
2635         break;
2636     case SS$_NOSUCHDEV:
2637         unix_status = ENODEV;
2638         break;
2639     case SS$_NOSUCHFILE:
2640     case SS$_NOSUCHOBJECT:
2641         unix_status = ENOENT;
2642         break;
2643     case SS$_ABORT:                                 /* Fatal case */
2644     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2645     case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2646         unix_status = EINTR;
2647         break;
2648     case SS$_BUFFEROVF:
2649         unix_status = E2BIG;
2650         break;
2651     case SS$_INSFMEM:
2652         unix_status = ENOMEM;
2653         break;
2654     case SS$_NOPRIV:
2655         unix_status = EPERM;
2656         break;
2657     case SS$_NOSUCHNODE:
2658     case SS$_UNREACHABLE:
2659         unix_status = ESRCH;
2660         break;
2661     case SS$_NONEXPR:
2662         unix_status = ECHILD;
2663         break;
2664     default:
2665         if ((facility == 0) && (msg_no < 8)) {
2666           /* These are not real VMS status codes so assume that they are
2667           ** already UNIX status codes
2668           */
2669           unix_status = msg_no;
2670           break;
2671         }
2672     }
2673   }
2674   else {
2675     /* Translate a POSIX exit code to a UNIX exit code */
2676     if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000))  {
2677         unix_status = (msg_no & 0x07F8) >> 3;
2678     }
2679     else {
2680
2681          /* Documented traditional behavior for handling VMS child exits */
2682         /*--------------------------------------------------------------*/
2683         if (child_flag != 0) {
2684
2685              /* Success / Informational return 0 */
2686             /*----------------------------------*/
2687             if (msg_no & STS$K_SUCCESS)
2688                 return 0;
2689
2690              /* Warning returns 1 */
2691             /*-------------------*/
2692             if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2693                 return 1;
2694
2695              /* Everything else pass through the severity bits */
2696             /*------------------------------------------------*/
2697             return (msg_no & STS$M_SEVERITY);
2698         }
2699
2700          /* Normal VMS status to ERRNO mapping attempt */
2701         /*--------------------------------------------*/
2702         switch(msg_status) {
2703         /* case RMS$_EOF: */ /* End of File */
2704         case RMS$_FNF:  /* File Not Found */
2705         case RMS$_DNF:  /* Dir Not Found */
2706                 unix_status = ENOENT;
2707                 break;
2708         case RMS$_RNF:  /* Record Not Found */
2709                 unix_status = ESRCH;
2710                 break;
2711         case RMS$_DIR:
2712                 unix_status = ENOTDIR;
2713                 break;
2714         case RMS$_DEV:
2715                 unix_status = ENODEV;
2716                 break;
2717         case RMS$_IFI:
2718         case RMS$_FAC:
2719         case RMS$_ISI:
2720                 unix_status = EBADF;
2721                 break;
2722         case RMS$_FEX:
2723                 unix_status = EEXIST;
2724                 break;
2725         case RMS$_SYN:
2726         case RMS$_FNM:
2727         case LIB$_INVSTRDES:
2728         case LIB$_INVARG:
2729         case LIB$_NOSUCHSYM:
2730         case LIB$_INVSYMNAM:
2731         case DCL_IVVERB:
2732                 unix_status = EINVAL;
2733                 break;
2734         case CLI$_BUFOVF:
2735         case RMS$_RTB:
2736         case CLI$_TKNOVF:
2737         case CLI$_RSLOVF:
2738                 unix_status = E2BIG;
2739                 break;
2740         case RMS$_PRV:  /* No privilege */
2741         case RMS$_ACC:  /* ACP file access failed */
2742         case RMS$_WLK:  /* Device write locked */
2743                 unix_status = EACCES;
2744                 break;
2745         case RMS$_MKD:  /* Failed to mark for delete */
2746                 unix_status = EPERM;
2747                 break;
2748         /* case RMS$_NMF: */  /* No more files */
2749         }
2750     }
2751   }
2752
2753   return unix_status;
2754
2755
2756 /* Try to guess at what VMS error status should go with a UNIX errno
2757  * value.  This is hard to do as there could be many possible VMS
2758  * error statuses that caused the errno value to be set.
2759  */
2760
2761 int Perl_unix_status_to_vms(int unix_status)
2762 {
2763 int test_unix_status;
2764
2765      /* Trivial cases first */
2766     /*---------------------*/
2767     if (unix_status == EVMSERR)
2768         return vaxc$errno;
2769
2770      /* Is vaxc$errno sane? */
2771     /*---------------------*/
2772     test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2773     if (test_unix_status == unix_status)
2774         return vaxc$errno;
2775
2776      /* If way out of range, must be VMS code already */
2777     /*-----------------------------------------------*/
2778     if (unix_status > EVMSERR)
2779         return unix_status;
2780
2781      /* If out of range, punt */
2782     /*-----------------------*/
2783     if (unix_status > __ERRNO_MAX)
2784         return SS$_ABORT;
2785
2786
2787      /* Ok, now we have to do it the hard way. */
2788     /*----------------------------------------*/
2789     switch(unix_status) {
2790     case 0:     return SS$_NORMAL;
2791     case EPERM: return SS$_NOPRIV;
2792     case ENOENT: return SS$_NOSUCHOBJECT;
2793     case ESRCH: return SS$_UNREACHABLE;
2794     case EINTR: return SS$_ABORT;
2795     /* case EIO: */
2796     /* case ENXIO:  */
2797     case E2BIG: return SS$_BUFFEROVF;
2798     /* case ENOEXEC */
2799     case EBADF: return RMS$_IFI;
2800     case ECHILD: return SS$_NONEXPR;
2801     /* case EAGAIN */
2802     case ENOMEM: return SS$_INSFMEM;
2803     case EACCES: return SS$_FILACCERR;
2804     case EFAULT: return SS$_ACCVIO;
2805     /* case ENOTBLK */
2806     case EBUSY: return SS$_DEVOFFLINE;
2807     case EEXIST: return RMS$_FEX;
2808     /* case EXDEV */
2809     case ENODEV: return SS$_NOSUCHDEV;
2810     case ENOTDIR: return RMS$_DIR;
2811     /* case EISDIR */
2812     case EINVAL: return SS$_INVARG;
2813     /* case ENFILE */
2814     /* case EMFILE */
2815     /* case ENOTTY */
2816     /* case ETXTBSY */
2817     /* case EFBIG */
2818     case ENOSPC: return SS$_DEVICEFULL;
2819     case ESPIPE: return LIB$_INVARG;
2820     /* case EROFS: */
2821     /* case EMLINK: */
2822     /* case EPIPE: */
2823     /* case EDOM */
2824     case ERANGE: return LIB$_INVARG;
2825     /* case EWOULDBLOCK */
2826     /* case EINPROGRESS */
2827     /* case EALREADY */
2828     /* case ENOTSOCK */
2829     /* case EDESTADDRREQ */
2830     /* case EMSGSIZE */
2831     /* case EPROTOTYPE */
2832     /* case ENOPROTOOPT */
2833     /* case EPROTONOSUPPORT */
2834     /* case ESOCKTNOSUPPORT */
2835     /* case EOPNOTSUPP */
2836     /* case EPFNOSUPPORT */
2837     /* case EAFNOSUPPORT */
2838     /* case EADDRINUSE */
2839     /* case EADDRNOTAVAIL */
2840     /* case ENETDOWN */
2841     /* case ENETUNREACH */
2842     /* case ENETRESET */
2843     /* case ECONNABORTED */
2844     /* case ECONNRESET */
2845     /* case ENOBUFS */
2846     /* case EISCONN */
2847     case ENOTCONN: return SS$_CLEARED;
2848     /* case ESHUTDOWN */
2849     /* case ETOOMANYREFS */
2850     /* case ETIMEDOUT */
2851     /* case ECONNREFUSED */
2852     /* case ELOOP */
2853     /* case ENAMETOOLONG */
2854     /* case EHOSTDOWN */
2855     /* case EHOSTUNREACH */
2856     /* case ENOTEMPTY */
2857     /* case EPROCLIM */
2858     /* case EUSERS  */
2859     /* case EDQUOT  */
2860     /* case ENOMSG  */
2861     /* case EIDRM */
2862     /* case EALIGN */
2863     /* case ESTALE */
2864     /* case EREMOTE */
2865     /* case ENOLCK */
2866     /* case ENOSYS */
2867     /* case EFTYPE */
2868     /* case ECANCELED */
2869     /* case EFAIL */
2870     /* case EINPROG */
2871     case ENOTSUP:
2872         return SS$_UNSUPPORTED;
2873     /* case EDEADLK */
2874     /* case ENWAIT */
2875     /* case EILSEQ */
2876     /* case EBADCAT */
2877     /* case EBADMSG */
2878     /* case EABANDONED */
2879     default:
2880         return SS$_ABORT; /* punt */
2881     }
2882
2883   return SS$_ABORT; /* Should not get here */
2884
2885
2886
2887 /* default piping mailbox size */
2888 #define PERL_BUFSIZ        512
2889
2890
2891 static void
2892 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2893 {
2894   unsigned long int mbxbufsiz;
2895   static unsigned long int syssize = 0;
2896   unsigned long int dviitm = DVI$_DEVNAM;
2897   char csize[LNM$C_NAMLENGTH+1];
2898   int sts;
2899
2900   if (!syssize) {
2901     unsigned long syiitm = SYI$_MAXBUF;
2902     /*
2903      * Get the SYSGEN parameter MAXBUF
2904      *
2905      * If the logical 'PERL_MBX_SIZE' is defined
2906      * use the value of the logical instead of PERL_BUFSIZ, but 
2907      * keep the size between 128 and MAXBUF.
2908      *
2909      */
2910     _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2911   }
2912
2913   if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2914       mbxbufsiz = atoi(csize);
2915   } else {
2916       mbxbufsiz = PERL_BUFSIZ;
2917   }
2918   if (mbxbufsiz < 128) mbxbufsiz = 128;
2919   if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2920
2921   _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2922
2923   sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2924   _ckvmssts_noperl(sts);
2925   namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2926
2927 }  /* end of create_mbx() */
2928
2929
2930 /*{{{  my_popen and my_pclose*/
2931
2932 typedef struct _iosb           IOSB;
2933 typedef struct _iosb*         pIOSB;
2934 typedef struct _pipe           Pipe;
2935 typedef struct _pipe*         pPipe;
2936 typedef struct pipe_details    Info;
2937 typedef struct pipe_details*  pInfo;
2938 typedef struct _srqp            RQE;
2939 typedef struct _srqp*          pRQE;
2940 typedef struct _tochildbuf      CBuf;
2941 typedef struct _tochildbuf*    pCBuf;
2942
2943 struct _iosb {
2944     unsigned short status;
2945     unsigned short count;
2946     unsigned long  dvispec;
2947 };
2948
2949 #pragma member_alignment save
2950 #pragma nomember_alignment quadword
2951 struct _srqp {          /* VMS self-relative queue entry */
2952     unsigned long qptr[2];
2953 };
2954 #pragma member_alignment restore
2955 static RQE  RQE_ZERO = {0,0};
2956
2957 struct _tochildbuf {
2958     RQE             q;
2959     int             eof;
2960     unsigned short  size;
2961     char            *buf;
2962 };
2963
2964 struct _pipe {
2965     RQE            free;
2966     RQE            wait;
2967     int            fd_out;
2968     unsigned short chan_in;
2969     unsigned short chan_out;
2970     char          *buf;
2971     unsigned int   bufsize;
2972     IOSB           iosb;
2973     IOSB           iosb2;
2974     int           *pipe_done;
2975     int            retry;
2976     int            type;
2977     int            shut_on_empty;
2978     int            need_wake;
2979     pPipe         *home;
2980     pInfo          info;
2981     pCBuf          curr;
2982     pCBuf          curr2;
2983 #if defined(PERL_IMPLICIT_CONTEXT)
2984     void            *thx;           /* Either a thread or an interpreter */
2985                                     /* pointer, depending on how we're built */
2986 #endif
2987 };
2988
2989
2990 struct pipe_details
2991 {
2992     pInfo           next;
2993     PerlIO *fp;  /* file pointer to pipe mailbox */
2994     int useFILE; /* using stdio, not perlio */
2995     int pid;   /* PID of subprocess */
2996     int mode;  /* == 'r' if pipe open for reading */
2997     int done;  /* subprocess has completed */
2998     int waiting; /* waiting for completion/closure */
2999     int             closing;        /* my_pclose is closing this pipe */
3000     unsigned long   completion;     /* termination status of subprocess */
3001     pPipe           in;             /* pipe in to sub */
3002     pPipe           out;            /* pipe out of sub */
3003     pPipe           err;            /* pipe of sub's sys$error */
3004     int             in_done;        /* true when in pipe finished */
3005     int             out_done;
3006     int             err_done;
3007     unsigned short  xchan;          /* channel to debug xterm */
3008     unsigned short  xchan_valid;    /* channel is assigned */
3009 };
3010
3011 struct exit_control_block
3012 {
3013     struct exit_control_block *flink;
3014     unsigned long int   (*exit_routine)();
3015     unsigned long int arg_count;
3016     unsigned long int *status_address;
3017     unsigned long int exit_status;
3018 }; 
3019
3020 typedef struct _closed_pipes    Xpipe;
3021 typedef struct _closed_pipes*  pXpipe;
3022
3023 struct _closed_pipes {
3024     int             pid;            /* PID of subprocess */
3025     unsigned long   completion;     /* termination status of subprocess */
3026 };
3027 #define NKEEPCLOSED 50
3028 static Xpipe closed_list[NKEEPCLOSED];
3029 static int   closed_index = 0;
3030 static int   closed_num = 0;
3031
3032 #define RETRY_DELAY     "0 ::0.20"
3033 #define MAX_RETRY              50
3034
3035 static int pipe_ef = 0;          /* first call to safe_popen inits these*/
3036 static unsigned long mypid;
3037 static unsigned long delaytime[2];
3038
3039 static pInfo open_pipes = NULL;
3040 static $DESCRIPTOR(nl_desc, "NL:");
3041
3042 #define PIPE_COMPLETION_WAIT    30  /* seconds, for EOF/FORCEX wait */
3043
3044
3045
3046 static unsigned long int
3047 pipe_exit_routine()
3048 {
3049     pInfo info;
3050     unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3051     int sts, did_stuff, need_eof, j;
3052
3053    /* 
3054     * Flush any pending i/o, but since we are in process run-down, be
3055     * careful about referencing PerlIO structures that may already have
3056     * been deallocated.  We may not even have an interpreter anymore.
3057     */
3058     info = open_pipes;
3059     while (info) {
3060         if (info->fp) {
3061 #if defined(PERL_IMPLICIT_CONTEXT)
3062            /* We need to use the Perl context of the thread that created */
3063            /* the pipe. */
3064            pTHX;
3065            if (info->err)
3066                aTHX = info->err->thx;
3067            else if (info->out)
3068                aTHX = info->out->thx;
3069            else if (info->in)
3070                aTHX = info->in->thx;
3071 #endif
3072            if (!info->useFILE
3073 #if defined(USE_ITHREADS)
3074              && my_perl
3075 #endif
3076              && PL_perlio_fd_refcnt) 
3077                PerlIO_flush(info->fp);
3078            else 
3079                fflush((FILE *)info->fp);
3080         }
3081         info = info->next;
3082     }
3083
3084     /* 
3085      next we try sending an EOF...ignore if doesn't work, make sure we
3086      don't hang
3087     */
3088     did_stuff = 0;
3089     info = open_pipes;
3090
3091     while (info) {
3092       int need_eof;
3093       _ckvmssts_noperl(sys$setast(0));
3094       if (info->in && !info->in->shut_on_empty) {
3095         _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3096                                  0, 0, 0, 0, 0, 0));
3097         info->waiting = 1;
3098         did_stuff = 1;
3099       }
3100       _ckvmssts_noperl(sys$setast(1));
3101       info = info->next;
3102     }
3103
3104     /* wait for EOF to have effect, up to ~ 30 sec [default] */
3105
3106     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3107         int nwait = 0;
3108
3109         info = open_pipes;
3110         while (info) {
3111           _ckvmssts_noperl(sys$setast(0));
3112           if (info->waiting && info->done) 
3113                 info->waiting = 0;
3114           nwait += info->waiting;
3115           _ckvmssts_noperl(sys$setast(1));
3116           info = info->next;
3117         }
3118         if (!nwait) break;
3119         sleep(1);  
3120     }
3121
3122     did_stuff = 0;
3123     info = open_pipes;
3124     while (info) {
3125       _ckvmssts_noperl(sys$setast(0));
3126       if (!info->done) { /* Tap them gently on the shoulder . . .*/
3127         sts = sys$forcex(&info->pid,0,&abort);
3128         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 
3129         did_stuff = 1;
3130       }
3131       _ckvmssts_noperl(sys$setast(1));
3132       info = info->next;
3133     }
3134
3135     /* again, wait for effect */
3136
3137     for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3138         int nwait = 0;
3139
3140         info = open_pipes;
3141         while (info) {
3142           _ckvmssts_noperl(sys$setast(0));
3143           if (info->waiting && info->done) 
3144                 info->waiting = 0;
3145           nwait += info->waiting;
3146           _ckvmssts_noperl(sys$setast(1));
3147           info = info->next;
3148         }
3149         if (!nwait) break;
3150         sleep(1);  
3151     }
3152
3153     info = open_pipes;
3154     while (info) {
3155       _ckvmssts_noperl(sys$setast(0));
3156       if (!info->done) {  /* We tried to be nice . . . */
3157         sts = sys$delprc(&info->pid,0);
3158         if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts); 
3159         info->done = 1;  /* sys$delprc is as done as we're going to get. */
3160       }
3161       _ckvmssts_noperl(sys$setast(1));
3162       info = info->next;
3163     }
3164
3165     while(open_pipes) {
3166
3167 #if defined(PERL_IMPLICIT_CONTEXT)
3168       /* We need to use the Perl context of the thread that created */
3169       /* the pipe. */
3170       pTHX;
3171       if (open_pipes->err)
3172           aTHX = open_pipes->err->thx;
3173       else if (open_pipes->out)
3174           aTHX = open_pipes->out->thx;
3175       else if (open_pipes->in)
3176           aTHX = open_pipes->in->thx;
3177 #endif
3178       if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3179       else if (!(sts & 1)) retsts = sts;
3180     }
3181     return retsts;
3182 }
3183
3184 static struct exit_control_block pipe_exitblock = 
3185        {(struct exit_control_block *) 0,
3186         pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3187
3188 static void pipe_mbxtofd_ast(pPipe p);
3189 static void pipe_tochild1_ast(pPipe p);
3190 static void pipe_tochild2_ast(pPipe p);
3191
3192 static void
3193 popen_completion_ast(pInfo info)
3194 {
3195   pInfo i = open_pipes;
3196   int iss;
3197   int sts;
3198   pXpipe x;
3199
3200   info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3201   closed_list[closed_index].pid = info->pid;
3202   closed_list[closed_index].completion = info->completion;
3203   closed_index++;
3204   if (closed_index == NKEEPCLOSED) 
3205     closed_index = 0;
3206   closed_num++;
3207
3208   while (i) {
3209     if (i == info) break;
3210     i = i->next;
3211   }
3212   if (!i) return;       /* unlinked, probably freed too */
3213
3214   info->done = TRUE;
3215
3216 /*
3217     Writing to subprocess ...
3218             if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3219
3220             chan_out may be waiting for "done" flag, or hung waiting
3221             for i/o completion to child...cancel the i/o.  This will
3222             put it into "snarf mode" (done but no EOF yet) that discards
3223             input.
3224
3225     Output from subprocess (stdout, stderr) needs to be flushed and
3226     shut down.   We try sending an EOF, but if the mbx is full the pipe
3227     routine should still catch the "shut_on_empty" flag, telling it to
3228     use immediate-style reads so that "mbx empty" -> EOF.
3229
3230
3231 */
3232   if (info->in && !info->in_done) {               /* only for mode=w */
3233         if (info->in->shut_on_empty && info->in->need_wake) {
3234             info->in->need_wake = FALSE;
3235             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3236         } else {
3237             _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3238         }
3239   }
3240
3241   if (info->out && !info->out_done) {             /* were we also piping output? */
3242       info->out->shut_on_empty = TRUE;
3243       iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3244       if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3245       _ckvmssts_noperl(iss);
3246   }
3247
3248   if (info->err && !info->err_done) {        /* we were piping stderr */
3249         info->err->shut_on_empty = TRUE;
3250         iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3251         if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3252         _ckvmssts_noperl(iss);
3253   }
3254   _ckvmssts_noperl(sys$setef(pipe_ef));
3255
3256 }
3257
3258 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3259 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3260
3261 /*
3262     we actually differ from vmstrnenv since we use this to
3263     get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3264     are pointing to the same thing
3265 */
3266
3267 static unsigned short
3268 popen_translate(pTHX_ char *logical, char *result)
3269 {
3270     int iss;
3271     $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3272     $DESCRIPTOR(d_log,"");
3273     struct _il3 {
3274         unsigned short length;
3275         unsigned short code;
3276         char *         buffer_addr;
3277         unsigned short *retlenaddr;
3278     } itmlst[2];
3279     unsigned short l, ifi;
3280
3281     d_log.dsc$a_pointer = logical;
3282     d_log.dsc$w_length  = strlen(logical);
3283
3284     itmlst[0].code = LNM$_STRING;
3285     itmlst[0].length = 255;
3286     itmlst[0].buffer_addr = result;
3287     itmlst[0].retlenaddr = &l;
3288
3289     itmlst[1].code = 0;
3290     itmlst[1].length = 0;
3291     itmlst[1].buffer_addr = 0;
3292     itmlst[1].retlenaddr = 0;
3293
3294     iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3295     if (iss == SS$_NOLOGNAM) {
3296         iss = SS$_NORMAL;
3297         l = 0;
3298     }
3299     if (!(iss&1)) lib$signal(iss);
3300     result[l] = '\0';
3301 /*
3302     logicals for PPFs have a 4 byte prefix  ESC+NUL+(RMS IFI)
3303     strip it off and return the ifi, if any
3304 */
3305     ifi  = 0;
3306     if (result[0] == 0x1b && result[1] == 0x00) {
3307         memmove(&ifi,result+2,2);
3308         strcpy(result,result+4);
3309     }
3310     return ifi;     /* this is the RMS internal file id */
3311 }
3312
3313 static void pipe_infromchild_ast(pPipe p);
3314
3315 /*
3316     I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3317     inside an AST routine without worrying about reentrancy and which Perl
3318     memory allocator is being used.
3319
3320     We read data and queue up the buffers, then spit them out one at a
3321     time to the output mailbox when the output mailbox is ready for one.
3322
3323 */
3324 #define INITIAL_TOCHILDQUEUE  2
3325
3326 static pPipe
3327 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3328 {
3329     pPipe p;
3330     pCBuf b;
3331     char mbx1[64], mbx2[64];
3332     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3333                                       DSC$K_CLASS_S, mbx1},
3334                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3335                                       DSC$K_CLASS_S, mbx2};
3336     unsigned int dviitm = DVI$_DEVBUFSIZ;
3337     int j, n;
3338
3339     n = sizeof(Pipe);
3340     _ckvmssts_noperl(lib$get_vm(&n, &p));
3341
3342     create_mbx(&p->chan_in , &d_mbx1);
3343     create_mbx(&p->chan_out, &d_mbx2);
3344     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3345
3346     p->buf           = 0;
3347     p->shut_on_empty = FALSE;
3348     p->need_wake     = FALSE;
3349     p->type          = 0;
3350     p->retry         = 0;
3351     p->iosb.status   = SS$_NORMAL;
3352     p->iosb2.status  = SS$_NORMAL;
3353     p->free          = RQE_ZERO;
3354     p->wait          = RQE_ZERO;
3355     p->curr          = 0;
3356     p->curr2         = 0;
3357     p->info          = 0;
3358 #ifdef PERL_IMPLICIT_CONTEXT
3359     p->thx           = aTHX;
3360 #endif
3361
3362     n = sizeof(CBuf) + p->bufsize;
3363
3364     for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3365         _ckvmssts_noperl(lib$get_vm(&n, &b));
3366         b->buf = (char *) b + sizeof(CBuf);
3367         _ckvmssts_noperl(lib$insqhi(b, &p->free));
3368     }
3369
3370     pipe_tochild2_ast(p);
3371     pipe_tochild1_ast(p);
3372     strcpy(wmbx, mbx1);
3373     strcpy(rmbx, mbx2);
3374     return p;
3375 }
3376
3377 /*  reads the MBX Perl is writing, and queues */
3378
3379 static void
3380 pipe_tochild1_ast(pPipe p)
3381 {
3382     pCBuf b = p->curr;
3383     int iss = p->iosb.status;
3384     int eof = (iss == SS$_ENDOFFILE);
3385     int sts;
3386 #ifdef PERL_IMPLICIT_CONTEXT
3387     pTHX = p->thx;
3388 #endif
3389
3390     if (p->retry) {
3391         if (eof) {
3392             p->shut_on_empty = TRUE;
3393             b->eof     = TRUE;
3394             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3395         } else  {
3396             _ckvmssts_noperl(iss);
3397         }
3398
3399         b->eof  = eof;
3400         b->size = p->iosb.count;
3401         _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3402         if (p->need_wake) {
3403             p->need_wake = FALSE;
3404             _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3405         }
3406     } else {
3407         p->retry = 1;   /* initial call */
3408     }
3409
3410     if (eof) {                  /* flush the free queue, return when done */
3411         int n = sizeof(CBuf) + p->bufsize;
3412         while (1) {
3413             iss = lib$remqti(&p->free, &b);
3414             if (iss == LIB$_QUEWASEMP) return;
3415             _ckvmssts_noperl(iss);
3416             _ckvmssts_noperl(lib$free_vm(&n, &b));
3417         }
3418     }
3419
3420     iss = lib$remqti(&p->free, &b);
3421     if (iss == LIB$_QUEWASEMP) {
3422         int n = sizeof(CBuf) + p->bufsize;
3423         _ckvmssts_noperl(lib$get_vm(&n, &b));
3424         b->buf = (char *) b + sizeof(CBuf);
3425     } else {
3426        _ckvmssts_noperl(iss);
3427     }
3428
3429     p->curr = b;
3430     iss = sys$qio(0,p->chan_in,
3431              IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3432              &p->iosb,
3433              pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3434     if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3435     _ckvmssts_noperl(iss);
3436 }
3437
3438
3439 /* writes queued buffers to output, waits for each to complete before
3440    doing the next */
3441
3442 static void
3443 pipe_tochild2_ast(pPipe p)
3444 {
3445     pCBuf b = p->curr2;
3446     int iss = p->iosb2.status;
3447     int n = sizeof(CBuf) + p->bufsize;
3448     int done = (p->info && p->info->done) ||
3449               iss == SS$_CANCEL || iss == SS$_ABORT;
3450 #if defined(PERL_IMPLICIT_CONTEXT)
3451     pTHX = p->thx;
3452 #endif
3453
3454     do {
3455         if (p->type) {         /* type=1 has old buffer, dispose */
3456             if (p->shut_on_empty) {
3457                 _ckvmssts_noperl(lib$free_vm(&n, &b));
3458             } else {
3459                 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3460             }
3461             p->type = 0;
3462         }
3463
3464         iss = lib$remqti(&p->wait, &b);
3465         if (iss == LIB$_QUEWASEMP) {
3466             if (p->shut_on_empty) {
3467                 if (done) {
3468                     _ckvmssts_noperl(sys$dassgn(p->chan_out));
3469                     *p->pipe_done = TRUE;
3470                     _ckvmssts_noperl(sys$setef(pipe_ef));
3471                 } else {
3472                     _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3473                         &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3474                 }
3475                 return;
3476             }
3477             p->need_wake = TRUE;
3478             return;
3479         }
3480         _ckvmssts_noperl(iss);
3481         p->type = 1;
3482     } while (done);
3483
3484
3485     p->curr2 = b;
3486     if (b->eof) {
3487         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3488             &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3489     } else {
3490         _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3491             &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3492     }
3493
3494     return;
3495
3496 }
3497
3498
3499 static pPipe
3500 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3501 {
3502     pPipe p;
3503     char mbx1[64], mbx2[64];
3504     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3505                                       DSC$K_CLASS_S, mbx1},
3506                             d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3507                                       DSC$K_CLASS_S, mbx2};
3508     unsigned int dviitm = DVI$_DEVBUFSIZ;
3509
3510     int n = sizeof(Pipe);
3511     _ckvmssts_noperl(lib$get_vm(&n, &p));
3512     create_mbx(&p->chan_in , &d_mbx1);
3513     create_mbx(&p->chan_out, &d_mbx2);
3514
3515     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3516     n = p->bufsize * sizeof(char);
3517     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3518     p->shut_on_empty = FALSE;
3519     p->info   = 0;
3520     p->type   = 0;
3521     p->iosb.status = SS$_NORMAL;
3522 #if defined(PERL_IMPLICIT_CONTEXT)
3523     p->thx = aTHX;
3524 #endif
3525     pipe_infromchild_ast(p);
3526
3527     strcpy(wmbx, mbx1);
3528     strcpy(rmbx, mbx2);
3529     return p;
3530 }
3531
3532 static void
3533 pipe_infromchild_ast(pPipe p)
3534 {
3535     int iss = p->iosb.status;
3536     int eof = (iss == SS$_ENDOFFILE);
3537     int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3538     int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3539 #if defined(PERL_IMPLICIT_CONTEXT)
3540     pTHX = p->thx;
3541 #endif
3542
3543     if (p->info && p->info->closing && p->chan_out)  {           /* output shutdown */
3544         _ckvmssts_noperl(sys$dassgn(p->chan_out));
3545         p->chan_out = 0;
3546     }
3547
3548     /* read completed:
3549             input shutdown if EOF from self (done or shut_on_empty)
3550             output shutdown if closing flag set (my_pclose)
3551             send data/eof from child or eof from self
3552             otherwise, re-read (snarf of data from child)
3553     */
3554
3555     if (p->type == 1) {
3556         p->type = 0;
3557         if (myeof && p->chan_in) {                  /* input shutdown */
3558             _ckvmssts_noperl(sys$dassgn(p->chan_in));
3559             p->chan_in = 0;
3560         }
3561
3562         if (p->chan_out) {
3563             if (myeof || kideof) {      /* pass EOF to parent */
3564                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3565                                          pipe_infromchild_ast, p,
3566                                          0, 0, 0, 0, 0, 0));
3567                 return;
3568             } else if (eof) {       /* eat EOF --- fall through to read*/
3569
3570             } else {                /* transmit data */
3571                 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3572                                          pipe_infromchild_ast,p,
3573                                          p->buf, p->iosb.count, 0, 0, 0, 0));
3574                 return;
3575             }
3576         }
3577     }
3578
3579     /*  everything shut? flag as done */
3580
3581     if (!p->chan_in && !p->chan_out) {
3582         *p->pipe_done = TRUE;
3583         _ckvmssts_noperl(sys$setef(pipe_ef));
3584         return;
3585     }
3586
3587     /* write completed (or read, if snarfing from child)
3588             if still have input active,
3589                queue read...immediate mode if shut_on_empty so we get EOF if empty
3590             otherwise,
3591                check if Perl reading, generate EOFs as needed
3592     */
3593
3594     if (p->type == 0) {
3595         p->type = 1;
3596         if (p->chan_in) {
3597             iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3598                           pipe_infromchild_ast,p,
3599                           p->buf, p->bufsize, 0, 0, 0, 0);
3600             if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3601             _ckvmssts_noperl(iss);
3602         } else {           /* send EOFs for extra reads */
3603             p->iosb.status = SS$_ENDOFFILE;
3604             p->iosb.dvispec = 0;
3605             _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3606                                      0, 0, 0,
3607                                      pipe_infromchild_ast, p, 0, 0, 0, 0));
3608         }
3609     }
3610 }
3611
3612 static pPipe
3613 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3614 {
3615     pPipe p;
3616     char mbx[64];
3617     unsigned long dviitm = DVI$_DEVBUFSIZ;
3618     struct stat s;
3619     struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3620                                       DSC$K_CLASS_S, mbx};
3621     int n = sizeof(Pipe);
3622
3623     /* things like terminals and mbx's don't need this filter */
3624     if (fd && fstat(fd,&s) == 0) {
3625         unsigned long dviitm = DVI$_DEVCHAR, devchar;
3626         char device[65];
3627         unsigned short dev_len;
3628         struct dsc$descriptor_s d_dev;
3629         char * cptr;
3630         struct item_list_3 items[3];
3631         int status;
3632         unsigned short dvi_iosb[4];
3633
3634         cptr = getname(fd, out, 1);
3635         if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3636         d_dev.dsc$a_pointer = out;
3637         d_dev.dsc$w_length = strlen(out);
3638         d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3639         d_dev.dsc$b_class = DSC$K_CLASS_S;
3640
3641         items[0].len = 4;
3642         items[0].code = DVI$_DEVCHAR;
3643         items[0].bufadr = &devchar;
3644         items[0].retadr = NULL;
3645         items[1].len = 64;
3646         items[1].code = DVI$_FULLDEVNAM;
3647         items[1].bufadr = device;
3648         items[1].retadr = &dev_len;
3649         items[2].len = 0;
3650         items[2].code = 0;
3651
3652         status = sys$getdviw
3653                 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3654         _ckvmssts_noperl(status);
3655         if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3656             device[dev_len] = 0;
3657
3658             if (!(devchar & DEV$M_DIR)) {
3659                 strcpy(out, device);
3660                 return 0;
3661             }
3662         }
3663     }
3664
3665     _ckvmssts_noperl(lib$get_vm(&n, &p));
3666     p->fd_out = dup(fd);
3667     create_mbx(&p->chan_in, &d_mbx);
3668     _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3669     n = (p->bufsize+1) * sizeof(char);
3670     _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3671     p->shut_on_empty = FALSE;
3672     p->retry = 0;
3673     p->info  = 0;
3674     strcpy(out, mbx);
3675
3676     _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3677                              pipe_mbxtofd_ast, p,
3678                              p->buf, p->bufsize, 0, 0, 0, 0));
3679
3680     return p;
3681 }
3682
3683 static void
3684 pipe_mbxtofd_ast(pPipe p)
3685 {
3686     int iss = p->iosb.status;
3687     int done = p->info->done;
3688     int iss2;
3689     int eof = (iss == SS$_ENDOFFILE);
3690     int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3691     int err = !(iss&1) && !eof;
3692 #if defined(PERL_IMPLICIT_CONTEXT)
3693     pTHX = p->thx;
3694 #endif
3695
3696     if (done && myeof) {               /* end piping */
3697         close(p->fd_out);
3698         sys$dassgn(p->chan_in);
3699         *p->pipe_done = TRUE;
3700         _ckvmssts_noperl(sys$setef(pipe_ef));
3701         return;
3702     }
3703
3704     if (!err && !eof) {             /* good data to send to file */
3705         p->buf[p->iosb.count] = '\n';
3706         iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3707         if (iss2 < 0) {
3708             p->retry++;
3709             if (p->retry < MAX_RETRY) {
3710                 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3711                 return;
3712             }
3713         }
3714         p->retry = 0;
3715     } else if (err) {
3716         _ckvmssts_noperl(iss);
3717     }
3718
3719
3720     iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3721           pipe_mbxtofd_ast, p,
3722           p->buf, p->bufsize, 0, 0, 0, 0);
3723     if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3724     _ckvmssts_noperl(iss);
3725 }
3726
3727
3728 typedef struct _pipeloc     PLOC;
3729 typedef struct _pipeloc*   pPLOC;
3730
3731 struct _pipeloc {
3732     pPLOC   next;
3733     char    dir[NAM$C_MAXRSS+1];
3734 };
3735 static pPLOC  head_PLOC = 0;
3736
3737 void
3738 free_pipelocs(pTHX_ void *head)
3739 {
3740     pPLOC p, pnext;
3741     pPLOC *pHead = (pPLOC *)head;
3742
3743     p = *pHead;
3744     while (p) {
3745         pnext = p->next;
3746         PerlMem_free(p);
3747         p = pnext;
3748     }
3749     *pHead = 0;
3750 }
3751
3752 static void
3753 store_pipelocs(pTHX)
3754 {
3755     int    i;
3756     pPLOC  p;
3757     AV    *av = 0;
3758     SV    *dirsv;
3759     GV    *gv;
3760     char  *dir, *x;
3761     char  *unixdir;
3762     char  temp[NAM$C_MAXRSS+1];
3763     STRLEN n_a;
3764
3765     if (head_PLOC)  
3766         free_pipelocs(aTHX_ &head_PLOC);
3767
3768 /*  the . directory from @INC comes last */
3769
3770     p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3771     if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3772     p->next = head_PLOC;
3773     head_PLOC = p;
3774     strcpy(p->dir,"./");
3775
3776 /*  get the directory from $^X */
3777
3778     unixdir = PerlMem_malloc(VMS_MAXRSS);
3779     if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3780
3781 #ifdef PERL_IMPLICIT_CONTEXT
3782     if (aTHX && PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3783 #else
3784     if (PL_origargv && PL_origargv[0]) {    /* maybe nul if embedded Perl */
3785 #endif
3786         strcpy(temp, PL_origargv[0]);
3787         x = strrchr(temp,']');
3788         if (x == NULL) {
3789         x = strrchr(temp,'>');
3790           if (x == NULL) {
3791             /* It could be a UNIX path */
3792             x = strrchr(temp,'/');
3793           }
3794         }
3795         if (x)
3796           x[1] = '\0';
3797         else {
3798           /* Got a bare name, so use default directory */
3799           temp[0] = '.';
3800           temp[1] = '\0';
3801         }
3802
3803         if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3804             p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3805             if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3806             p->next = head_PLOC;
3807             head_PLOC = p;
3808             strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3809             p->dir[NAM$C_MAXRSS] = '\0';
3810         }
3811     }
3812
3813 /*  reverse order of @INC entries, skip "." since entered above */
3814
3815 #ifdef PERL_IMPLICIT_CONTEXT
3816     if (aTHX)
3817 #endif
3818     if (PL_incgv) av = GvAVn(PL_incgv);
3819
3820     for (i = 0; av && i <= AvFILL(av); i++) {
3821         dirsv = *av_fetch(av,i,TRUE);
3822
3823         if (SvROK(dirsv)) continue;
3824         dir = SvPVx(dirsv,n_a);
3825         if (strcmp(dir,".") == 0) continue;
3826         if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3827             continue;
3828
3829         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3830         p->next = head_PLOC;
3831         head_PLOC = p;
3832         strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3833         p->dir[NAM$C_MAXRSS] = '\0';
3834     }
3835
3836 /* most likely spot (ARCHLIB) put first in the list */
3837
3838 #ifdef ARCHLIB_EXP
3839     if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3840         p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3841         if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3842         p->next = head_PLOC;
3843         head_PLOC = p;
3844         strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3845         p->dir[NAM$C_MAXRSS] = '\0';
3846     }
3847 #endif
3848     PerlMem_free(unixdir);
3849 }
3850
3851 static I32
3852 Perl_cando_by_name_int
3853    (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3854 #if !defined(PERL_IMPLICIT_CONTEXT)
3855 #define cando_by_name_int               Perl_cando_by_name_int
3856 #else
3857 #define cando_by_name_int(a,b,c,d)      Perl_cando_by_name_int(aTHX_ a,b,c,d)
3858 #endif
3859
3860 static char *
3861 find_vmspipe(pTHX)
3862 {
3863     static int   vmspipe_file_status = 0;
3864     static char  vmspipe_file[NAM$C_MAXRSS+1];
3865
3866     /* already found? Check and use ... need read+execute permission */
3867
3868     if (vmspipe_file_status == 1) {
3869         if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3870          && cando_by_name_int
3871            (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3872             return vmspipe_file;
3873         }
3874         vmspipe_file_status = 0;
3875     }
3876
3877     /* scan through stored @INC, $^X */
3878
3879     if (vmspipe_file_status == 0) {
3880         char file[NAM$C_MAXRSS+1];
3881         pPLOC  p = head_PLOC;
3882
3883         while (p) {
3884             char * exp_res;
3885             int dirlen;
3886             strcpy(file, p->dir);
3887             dirlen = strlen(file);
3888             strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3889             file[NAM$C_MAXRSS] = '\0';
3890             p = p->next;
3891
3892             exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3893             if (!exp_res) continue;
3894
3895             if (cando_by_name_int
3896                 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3897              && cando_by_name_int
3898                    (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3899                 vmspipe_file_status = 1;
3900                 return vmspipe_file;
3901             }
3902         }
3903         vmspipe_file_status = -1;   /* failed, use tempfiles */
3904     }
3905
3906     return 0;
3907 }
3908
3909 static FILE *
3910 vmspipe_tempfile(pTHX)
3911 {
3912     char file[NAM$C_MAXRSS+1];
3913     FILE *fp;
3914     static int index = 0;
3915     Stat_t s0, s1;
3916     int cmp_result;
3917
3918     /* create a tempfile */
3919
3920     /* we can't go from   W, shr=get to  R, shr=get without
3921        an intermediate vulnerable state, so don't bother trying...
3922
3923        and lib$spawn doesn't shr=put, so have to close the write
3924
3925        So... match up the creation date/time and the FID to
3926        make sure we're dealing with the same file
3927
3928     */
3929
3930     index++;
3931     if (!decc_filename_unix_only) {
3932       sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3933       fp = fopen(file,"w");
3934       if (!fp) {
3935         sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3936         fp = fopen(file,"w");
3937         if (!fp) {
3938             sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3939             fp = fopen(file,"w");
3940         }
3941       }
3942      }
3943      else {
3944       sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3945       fp = fopen(file,"w");
3946       if (!fp) {
3947         sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3948         fp = fopen(file,"w");
3949         if (!fp) {
3950           sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3951           fp = fopen(file,"w");
3952         }
3953       }
3954     }
3955     if (!fp) return 0;  /* we're hosed */
3956
3957     fprintf(fp,"$! 'f$verify(0)'\n");
3958     fprintf(fp,"$!  ---  protect against nonstandard definitions ---\n");
3959     fprintf(fp,"$ perl_cfile  = f$environment(\"procedure\")\n");
3960     fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3961     fprintf(fp,"$ perl_on     = \"set noon\"\n");
3962     fprintf(fp,"$ perl_exit   = \"exit\"\n");
3963     fprintf(fp,"$ perl_del    = \"delete\"\n");
3964     fprintf(fp,"$ pif         = \"if\"\n");
3965     fprintf(fp,"$!  --- define i/o redirection (sys$output set by lib$spawn)\n");
3966     fprintf(fp,"$ pif perl_popen_in  .nes. \"\" then perl_define/user/name_attributes=confine sys$input  'perl_popen_in'\n");
3967     fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error  'perl_popen_err'\n");
3968     fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define      sys$output 'perl_popen_out'\n");
3969     fprintf(fp,"$!  --- build command line to get max possible length\n");
3970     fprintf(fp,"$c=perl_popen_cmd0\n"); 
3971     fprintf(fp,"$c=c+perl_popen_cmd1\n"); 
3972     fprintf(fp,"$c=c+perl_popen_cmd2\n"); 
3973     fprintf(fp,"$x=perl_popen_cmd3\n"); 
3974     fprintf(fp,"$c=c+x\n"); 
3975     fprintf(fp,"$ perl_on\n");
3976     fprintf(fp,"$ 'c'\n");
3977     fprintf(fp,"$ perl_status = $STATUS\n");
3978     fprintf(fp,"$ perl_del  'perl_cfile'\n");
3979     fprintf(fp,"$ perl_exit 'perl_status'\n");
3980     fsync(fileno(fp));
3981
3982     fgetname(fp, file, 1);
3983     fstat(fileno(fp), &s0.crtl_stat);
3984     fclose(fp);
3985
3986     if (decc_filename_unix_only)
3987         int_tounixspec(file, file, NULL);
3988     fp = fopen(file,"r","shr=get");
3989     if (!fp) return 0;
3990     fstat(fileno(fp), &s1.crtl_stat);
3991
3992     cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3993     if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime))  {
3994         fclose(fp);
3995         return 0;
3996     }
3997
3998     return fp;
3999 }
4000
4001
4002 static int vms_is_syscommand_xterm(void)
4003 {
4004     const static struct dsc$descriptor_s syscommand_dsc = 
4005       { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
4006
4007     const static struct dsc$descriptor_s decwdisplay_dsc = 
4008       { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
4009
4010     struct item_list_3 items[2];
4011     unsigned short dvi_iosb[4];
4012     unsigned long devchar;
4013     unsigned long devclass;
4014     int status;
4015
4016     /* Very simple check to guess if sys$command is a decterm? */
4017     /* First see if the DECW$DISPLAY: device exists */
4018     items[0].len = 4;
4019     items[0].code = DVI$_DEVCHAR;
4020     items[0].bufadr = &devchar;
4021     items[0].retadr = NULL;
4022     items[1].len = 0;
4023     items[1].code = 0;
4024
4025     status = sys$getdviw
4026         (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
4027
4028     if ($VMS_STATUS_SUCCESS(status)) {
4029         status = dvi_iosb[0];
4030     }
4031
4032     if (!$VMS_STATUS_SUCCESS(status)) {
4033         SETERRNO(EVMSERR, status);
4034         return -1;
4035     }
4036
4037     /* If it does, then for now assume that we are on a workstation */
4038     /* Now verify that SYS$COMMAND is a terminal */
4039     /* for creating the debugger DECTerm */
4040
4041     items[0].len = 4;
4042     items[0].code = DVI$_DEVCLASS;
4043     items[0].bufadr = &devclass;
4044     items[0].retadr = NULL;
4045     items[1].len = 0;
4046     items[1].code = 0;
4047
4048     status = sys$getdviw
4049         (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
4050
4051     if ($VMS_STATUS_SUCCESS(status)) {
4052         status = dvi_iosb[0];
4053     }
4054
4055     if (!$VMS_STATUS_SUCCESS(status)) {
4056         SETERRNO(EVMSERR, status);
4057         return -1;
4058     }
4059     else {
4060         if (devclass == DC$_TERM) {
4061             return 0;
4062         }
4063     }
4064     return -1;
4065 }
4066
4067 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
4068 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
4069 {
4070     int status;
4071     int ret_stat;
4072     char * ret_char;
4073     char device_name[65];
4074     unsigned short device_name_len;
4075     struct dsc$descriptor_s customization_dsc;
4076     struct dsc$descriptor_s device_name_dsc;
4077     const char * cptr;
4078     char * tptr;
4079     char customization[200];
4080     char title[40];
4081     pInfo info = NULL;
4082     char mbx1[64];
4083     unsigned short p_chan;
4084     int n;
4085     unsigned short iosb[4];
4086     struct item_list_3 items[2];
4087     const char * cust_str =
4088         "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4089     struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4090                                           DSC$K_CLASS_S, mbx1};
4091
4092      /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4093     /*---------------------------------------*/
4094     VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4095
4096
4097     /* Make sure that this is from the Perl debugger */
4098     ret_char = strstr(cmd," xterm ");
4099     if (ret_char == NULL)
4100         return NULL;
4101     cptr = ret_char + 7;
4102     ret_char = strstr(cmd,"tty");
4103     if (ret_char == NULL)
4104         return NULL;
4105     ret_char = strstr(cmd,"sleep");
4106     if (ret_char == NULL)
4107         return NULL;
4108
4109     if (decw_term_port == 0) {
4110         $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4111         $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4112         $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4113
4114        status = lib$find_image_symbol
4115                                (&filename1_dsc,
4116                                 &decw_term_port_dsc,
4117                                 (void *)&decw_term_port,
4118                                 NULL,
4119                                 0);
4120
4121         /* Try again with the other image name */
4122         if (!$VMS_STATUS_SUCCESS(status)) {
4123
4124            status = lib$find_image_symbol
4125                                (&filename2_dsc,
4126                                 &decw_term_port_dsc,
4127                                 (void *)&decw_term_port,
4128                                 NULL,
4129                                 0);
4130
4131         }
4132
4133     }
4134
4135
4136     /* No decw$term_port, give it up */
4137     if (!$VMS_STATUS_SUCCESS(status))
4138         return NULL;
4139
4140     /* Are we on a workstation? */
4141     /* to do: capture the rows / columns and pass their properties */
4142     ret_stat = vms_is_syscommand_xterm();
4143     if (ret_stat < 0)
4144         return NULL;
4145
4146     /* Make the title: */
4147     ret_char = strstr(cptr,"-title");
4148     if (ret_char != NULL) {
4149         while ((*cptr != 0) && (*cptr != '\"')) {
4150             cptr++;
4151         }
4152         if (*cptr == '\"')
4153             cptr++;
4154         n = 0;
4155         while ((*cptr != 0) && (*cptr != '\"')) {
4156             title[n] = *cptr;
4157             n++;
4158             if (n == 39) {
4159                 title[39] == 0;
4160                 break;
4161             }
4162             cptr++;
4163         }
4164         title[n] = 0;
4165     }
4166     else {
4167             /* Default title */
4168             strcpy(title,"Perl Debug DECTerm");
4169     }
4170     sprintf(customization, cust_str, title);
4171
4172     customization_dsc.dsc$a_pointer = customization;
4173     customization_dsc.dsc$w_length = strlen(customization);
4174     customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4175     customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4176
4177     device_name_dsc.dsc$a_pointer = device_name;
4178     device_name_dsc.dsc$w_length = sizeof device_name -1;
4179     device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4180     device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4181
4182     device_name_len = 0;
4183
4184     /* Try to create the window */
4185      status = (*decw_term_port)
4186        (NULL,
4187         NULL,
4188         &customization_dsc,
4189         &device_name_dsc,
4190         &device_name_len,
4191         NULL,
4192         NULL,
4193         NULL);
4194     if (!$VMS_STATUS_SUCCESS(status)) {
4195         SETERRNO(EVMSERR, status);
4196         return NULL;
4197     }
4198
4199     device_name[device_name_len] = '\0';
4200
4201     /* Need to set this up to look like a pipe for cleanup */
4202     n = sizeof(Info);
4203     status = lib$get_vm(&n, &info);
4204     if (!$VMS_STATUS_SUCCESS(status)) {
4205         SETERRNO(ENOMEM, status);
4206         return NULL;
4207     }
4208
4209     info->mode = *mode;
4210     info->done = FALSE;
4211     info->completion = 0;
4212     info->closing    = FALSE;
4213     info->in         = 0;
4214     info->out        = 0;
4215     info->err        = 0;
4216     info->fp         = NULL;
4217     info->useFILE    = 0;
4218     info->waiting    = 0;
4219     info->in_done    = TRUE;
4220     info->out_done   = TRUE;
4221     info->err_done   = TRUE;
4222
4223     /* Assign a channel on this so that it will persist, and not login */
4224     /* We stash this channel in the info structure for reference. */
4225     /* The created xterm self destructs when the last channel is removed */
4226     /* and it appears that perl5db.pl (perl debugger) does this routinely */
4227     /* So leave this assigned. */
4228     device_name_dsc.dsc$w_length = device_name_len;
4229     status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4230     if (!$VMS_STATUS_SUCCESS(status)) {
4231         SETERRNO(EVMSERR, status);
4232         return NULL;
4233     }
4234     info->xchan_valid = 1;
4235
4236     /* Now create a mailbox to be read by the application */
4237
4238     create_mbx(&p_chan, &d_mbx1);
4239
4240     /* write the name of the created terminal to the mailbox */
4241     status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4242             iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4243
4244     if (!$VMS_STATUS_SUCCESS(status)) {
4245         SETERRNO(EVMSERR, status);
4246         return NULL;
4247     }
4248
4249     info->fp  = PerlIO_open(mbx1, mode);
4250
4251     /* Done with this channel */
4252     sys$dassgn(p_chan);
4253
4254     /* If any errors, then clean up */
4255     if (!info->fp) {
4256         n = sizeof(Info);
4257         _ckvmssts_noperl(lib$free_vm(&n, &info));
4258         return NULL;
4259         }
4260
4261     /* All done */
4262     return info->fp;
4263 }
4264
4265 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4266
4267 static PerlIO *
4268 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4269 {
4270     static int handler_set_up = FALSE;
4271     PerlIO * ret_fp;
4272     unsigned long int sts, flags = CLI$M_NOWAIT;
4273     /* The use of a GLOBAL table (as was done previously) rendered
4274      * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4275      * environment.  Hence we've switched to LOCAL symbol table.
4276      */
4277     unsigned int table = LIB$K_CLI_LOCAL_SYM;
4278     int j, wait = 0, n;
4279     char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4280     char *in, *out, *err, mbx[512];
4281     FILE *tpipe = 0;
4282     char tfilebuf[NAM$C_MAXRSS+1];
4283     pInfo info = NULL;
4284     char cmd_sym_name[20];
4285     struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4286                                       DSC$K_CLASS_S, symbol};
4287     struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4288                                       DSC$K_CLASS_S, 0};
4289     struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4290                                       DSC$K_CLASS_S, cmd_sym_name};
4291     struct dsc$descriptor_s *vmscmd;
4292     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4293     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4294     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4295
4296     /* Check here for Xterm create request.  This means looking for
4297      * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4298      *  is possible to create an xterm.
4299      */
4300     if (*in_mode == 'r') {
4301         PerlIO * xterm_fd;
4302
4303 #if defined(PERL_IMPLICIT_CONTEXT)
4304         /* Can not fork an xterm with a NULL context */
4305         /* This probably could never happen */
4306         xterm_fd = NULL;
4307         if (aTHX != NULL)
4308 #endif
4309         xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4310         if (xterm_fd != NULL)
4311             return xterm_fd;
4312     }
4313
4314     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static vmspipe file */
4315
4316     /* once-per-program initialization...
4317        note that the SETAST calls and the dual test of pipe_ef
4318        makes sure that only the FIRST thread through here does
4319        the initialization...all other threads wait until it's
4320        done.
4321
4322        Yeah, uglier than a pthread call, it's got all the stuff inline
4323        rather than in a separate routine.
4324     */
4325
4326     if (!pipe_ef) {
4327         _ckvmssts_noperl(sys$setast(0));
4328         if (!pipe_ef) {
4329             unsigned long int pidcode = JPI$_PID;
4330             $DESCRIPTOR(d_delay, RETRY_DELAY);
4331             _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4332             _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4333             _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4334         }
4335         if (!handler_set_up) {
4336           _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4337           handler_set_up = TRUE;
4338         }
4339         _ckvmssts_noperl(sys$setast(1));
4340     }
4341
4342     /* see if we can find a VMSPIPE.COM */
4343
4344     tfilebuf[0] = '@';
4345     vmspipe = find_vmspipe(aTHX);
4346     if (vmspipe) {
4347         strcpy(tfilebuf+1,vmspipe);
4348     } else {        /* uh, oh...we're in tempfile hell */
4349         tpipe = vmspipe_tempfile(aTHX);
4350         if (!tpipe) {       /* a fish popular in Boston */
4351             if (ckWARN(WARN_PIPE)) {
4352                 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4353             }
4354         return NULL;
4355         }
4356         fgetname(tpipe,tfilebuf+1,1);
4357     }
4358     vmspipedsc.dsc$a_pointer = tfilebuf;
4359     vmspipedsc.dsc$w_length  = strlen(tfilebuf);
4360
4361     sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4362     if (!(sts & 1)) { 
4363       switch (sts) {
4364         case RMS$_FNF:  case RMS$_DNF:
4365           set_errno(ENOENT); break;
4366         case RMS$_DIR:
4367           set_errno(ENOTDIR); break;
4368         case RMS$_DEV:
4369           set_errno(ENODEV); break;
4370         case RMS$_PRV:
4371           set_errno(EACCES); break;
4372         case RMS$_SYN:
4373           set_errno(EINVAL); break;
4374         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4375           set_errno(E2BIG); break;
4376         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4377           _ckvmssts_noperl(sts); /* fall through */
4378         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4379           set_errno(EVMSERR); 
4380       }
4381       set_vaxc_errno(sts);
4382       if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4383         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4384       }
4385       *psts = sts;
4386       return NULL; 
4387     }
4388     n = sizeof(Info);
4389     _ckvmssts_noperl(lib$get_vm(&n, &info));
4390         
4391     strcpy(mode,in_mode);
4392     info->mode = *mode;
4393     info->done = FALSE;
4394     info->completion = 0;
4395     info->closing    = FALSE;
4396     info->in         = 0;
4397     info->out        = 0;
4398     info->err        = 0;
4399     info->fp         = NULL;
4400     info->useFILE    = 0;
4401     info->waiting    = 0;
4402     info->in_done    = TRUE;
4403     info->out_done   = TRUE;
4404     info->err_done   = TRUE;
4405     info->xchan      = 0;
4406     info->xchan_valid = 0;
4407
4408     in = PerlMem_malloc(VMS_MAXRSS);
4409     if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4410     out = PerlMem_malloc(VMS_MAXRSS);
4411     if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4412     err = PerlMem_malloc(VMS_MAXRSS);
4413     if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4414
4415     in[0] = out[0] = err[0] = '\0';
4416
4417     if ((p = strchr(mode,'F')) != NULL) {   /* F -> use FILE* */
4418         info->useFILE = 1;
4419         strcpy(p,p+1);
4420     }
4421     if ((p = strchr(mode,'W')) != NULL) {   /* W -> wait for completion */
4422         wait = 1;
4423         strcpy(p,p+1);
4424     }
4425
4426     if (*mode == 'r') {             /* piping from subroutine */
4427
4428         info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4429         if (info->out) {
4430             info->out->pipe_done = &info->out_done;
4431             info->out_done = FALSE;
4432             info->out->info = info;
4433         }
4434         if (!info->useFILE) {
4435             info->fp  = PerlIO_open(mbx, mode);
4436         } else {
4437             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4438             Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4439         }
4440
4441         if (!info->fp && info->out) {
4442             sys$cancel(info->out->chan_out);
4443         
4444             while (!info->out_done) {
4445                 int done;
4446                 _ckvmssts_noperl(sys$setast(0));
4447                 done = info->out_done;
4448                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4449                 _ckvmssts_noperl(sys$setast(1));
4450                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4451             }
4452
4453             if (info->out->buf) {
4454                 n = info->out->bufsize * sizeof(char);
4455                 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4456             }
4457             n = sizeof(Pipe);
4458             _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4459             n = sizeof(Info);
4460             _ckvmssts_noperl(lib$free_vm(&n, &info));
4461             *psts = RMS$_FNF;
4462             return NULL;
4463         }
4464
4465         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4466         if (info->err) {
4467             info->err->pipe_done = &info->err_done;
4468             info->err_done = FALSE;
4469             info->err->info = info;
4470         }
4471
4472     } else if (*mode == 'w') {      /* piping to subroutine */
4473
4474         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4475         if (info->out) {
4476             info->out->pipe_done = &info->out_done;
4477             info->out_done = FALSE;
4478             info->out->info = info;
4479         }
4480
4481         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4482         if (info->err) {
4483             info->err->pipe_done = &info->err_done;
4484             info->err_done = FALSE;
4485             info->err->info = info;
4486         }
4487
4488         info->in = pipe_tochild_setup(aTHX_ in,mbx);
4489         if (!info->useFILE) {
4490             info->fp  = PerlIO_open(mbx, mode);
4491         } else {
4492             info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4493             Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4494         }
4495
4496         if (info->in) {
4497             info->in->pipe_done = &info->in_done;
4498             info->in_done = FALSE;
4499             info->in->info = info;
4500         }
4501
4502         /* error cleanup */
4503         if (!info->fp && info->in) {
4504             info->done = TRUE;
4505             _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4506                                       0, 0, 0, 0, 0, 0, 0, 0));
4507
4508             while (!info->in_done) {
4509                 int done;
4510                 _ckvmssts_noperl(sys$setast(0));
4511                 done = info->in_done;
4512                 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4513                 _ckvmssts_noperl(sys$setast(1));
4514                 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4515             }
4516
4517             if (info->in->buf) {
4518                 n = info->in->bufsize * sizeof(char);
4519                 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4520             }
4521             n = sizeof(Pipe);
4522             _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4523             n = sizeof(Info);
4524             _ckvmssts_noperl(lib$free_vm(&n, &info));
4525             *psts = RMS$_FNF;
4526             return NULL;
4527         }
4528         
4529
4530     } else if (*mode == 'n') {       /* separate subprocess, no Perl i/o */
4531         info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4532         if (info->out) {
4533             info->out->pipe_done = &info->out_done;
4534             info->out_done = FALSE;
4535             info->out->info = info;
4536         }
4537
4538         info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4539         if (info->err) {
4540             info->err->pipe_done = &info->err_done;
4541             info->err_done = FALSE;
4542             info->err->info = info;
4543         }
4544     }
4545
4546     symbol[MAX_DCL_SYMBOL] = '\0';
4547
4548     strncpy(symbol, in, MAX_DCL_SYMBOL);
4549     d_symbol.dsc$w_length = strlen(symbol);
4550     _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4551
4552     strncpy(symbol, err, MAX_DCL_SYMBOL);
4553     d_symbol.dsc$w_length = strlen(symbol);
4554     _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4555
4556     strncpy(symbol, out, MAX_DCL_SYMBOL);
4557     d_symbol.dsc$w_length = strlen(symbol);
4558     _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4559
4560     /* Done with the names for the pipes */
4561     PerlMem_free(err);
4562     PerlMem_free(out);
4563     PerlMem_free(in);
4564
4565     p = vmscmd->dsc$a_pointer;
4566     while (*p == ' ' || *p == '\t') p++;        /* remove leading whitespace */
4567     if (*p == '$') p++;                         /* remove leading $ */
4568     while (*p == ' ' || *p == '\t') p++;
4569
4570     for (j = 0; j < 4; j++) {
4571         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4572         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4573
4574     strncpy(symbol, p, MAX_DCL_SYMBOL);
4575     d_symbol.dsc$w_length = strlen(symbol);
4576     _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4577
4578         if (strlen(p) > MAX_DCL_SYMBOL) {
4579             p += MAX_DCL_SYMBOL;
4580         } else {
4581             p += strlen(p);
4582         }
4583     }
4584     _ckvmssts_noperl(sys$setast(0));
4585     info->next=open_pipes;  /* prepend to list */
4586     open_pipes=info;
4587     _ckvmssts_noperl(sys$setast(1));
4588     /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4589      * and SYS$COMMAND.  vmspipe.com will redefine SYS$INPUT, but we'll still
4590      * have SYS$COMMAND if we need it.
4591      */
4592     _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4593                       0, &info->pid, &info->completion,
4594                       0, popen_completion_ast,info,0,0,0));
4595
4596     /* if we were using a tempfile, close it now */
4597
4598     if (tpipe) fclose(tpipe);
4599
4600     /* once the subprocess is spawned, it has copied the symbols and
4601        we can get rid of ours */
4602
4603     for (j = 0; j < 4; j++) {
4604         sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4605         d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4606     _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4607     }
4608     _ckvmssts_noperl(lib$delete_symbol(&d_sym_in,  &table));
4609     _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4610     _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4611     vms_execfree(vmscmd);
4612         
4613 #ifdef PERL_IMPLICIT_CONTEXT
4614     if (aTHX) 
4615 #endif
4616     PL_forkprocess = info->pid;
4617
4618     ret_fp = info->fp;
4619     if (wait) {
4620          dSAVEDERRNO;
4621          int done = 0;
4622          while (!done) {
4623              _ckvmssts_noperl(sys$setast(0));
4624              done = info->done;
4625              if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4626              _ckvmssts_noperl(sys$setast(1));
4627              if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4628          }
4629         *psts = info->completion;
4630 /* Caller thinks it is open and tries to close it. */
4631 /* This causes some problems, as it changes the error status */
4632 /*        my_pclose(info->fp); */
4633
4634          /* If we did not have a file pointer open, then we have to */
4635          /* clean up here or eventually we will run out of something */
4636          SAVE_ERRNO;
4637          if (info->fp == NULL) {
4638              my_pclose_pinfo(aTHX_ info);
4639          }
4640          RESTORE_ERRNO;
4641
4642     } else { 
4643         *psts = info->pid;
4644     }
4645     return ret_fp;
4646 }  /* end of safe_popen */
4647
4648
4649 /*{{{  PerlIO *my_popen(char *cmd, char *mode)*/
4650 PerlIO *
4651 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4652 {
4653     int sts;
4654     TAINT_ENV();
4655     TAINT_PROPER("popen");
4656     PERL_FLUSHALL_FOR_CHILD;
4657     return safe_popen(aTHX_ cmd,mode,&sts);
4658 }
4659
4660 /*}}}*/
4661
4662
4663 /* Routine to close and cleanup a pipe info structure */
4664
4665 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4666
4667     unsigned long int retsts;
4668     int done, iss, n;
4669     int status;
4670     pInfo next, last;
4671
4672     /* If we were writing to a subprocess, insure that someone reading from
4673      * the mailbox gets an EOF.  It looks like a simple fclose() doesn't
4674      * produce an EOF record in the mailbox.
4675      *
4676      *  well, at least sometimes it *does*, so we have to watch out for
4677      *  the first EOF closing the pipe (and DASSGN'ing the channel)...
4678      */
4679      if (info->fp) {
4680         if (!info->useFILE
4681 #if defined(USE_ITHREADS)
4682           && my_perl
4683 #endif
4684           && PL_perlio_fd_refcnt) 
4685             PerlIO_flush(info->fp);
4686         else 
4687             fflush((FILE *)info->fp);
4688     }
4689
4690     _ckvmssts(sys$setast(0));
4691      info->closing = TRUE;
4692      done = info->done && info->in_done && info->out_done && info->err_done;
4693      /* hanging on write to Perl's input? cancel it */
4694      if (info->mode == 'r' && info->out && !info->out_done) {
4695         if (info->out->chan_out) {
4696             _ckvmssts(sys$cancel(info->out->chan_out));
4697             if (!info->out->chan_in) {   /* EOF generation, need AST */
4698                 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4699             }
4700         }
4701      }
4702      if (info->in && !info->in_done && !info->in->shut_on_empty)  /* EOF if hasn't had one yet */
4703          _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4704                            0, 0, 0, 0, 0, 0));
4705     _ckvmssts(sys$setast(1));
4706     if (info->fp) {
4707      if (!info->useFILE
4708 #if defined(USE_ITHREADS)
4709          && my_perl
4710 #endif
4711          && PL_perlio_fd_refcnt) 
4712         PerlIO_close(info->fp);
4713      else 
4714         fclose((FILE *)info->fp);
4715     }
4716      /*
4717         we have to wait until subprocess completes, but ALSO wait until all
4718         the i/o completes...otherwise we'll be freeing the "info" structure
4719         that the i/o ASTs could still be using...
4720      */
4721
4722      while (!done) {
4723          _ckvmssts(sys$setast(0));
4724          done = info->done && info->in_done && info->out_done && info->err_done;
4725          if (!done) _ckvmssts(sys$clref(pipe_ef));
4726          _ckvmssts(sys$setast(1));
4727          if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4728      }
4729      retsts = info->completion;
4730
4731     /* remove from list of open pipes */
4732     _ckvmssts(sys$setast(0));
4733     last = NULL;
4734     for (next = open_pipes; next != NULL; last = next, next = next->next) {
4735         if (next == info)
4736             break;
4737     }
4738
4739     if (last)
4740         last->next = info->next;
4741     else
4742         open_pipes = info->next;
4743     _ckvmssts(sys$setast(1));
4744
4745     /* free buffers and structures */
4746
4747     if (info->in) {
4748         if (info->in->buf) {
4749             n = info->in->bufsize * sizeof(char);
4750             _ckvmssts(lib$free_vm(&n, &info->in->buf));
4751         }
4752         n = sizeof(Pipe);
4753         _ckvmssts(lib$free_vm(&n, &info->in));
4754     }
4755     if (info->out) {
4756         if (info->out->buf) {
4757             n = info->out->bufsize * sizeof(char);
4758             _ckvmssts(lib$free_vm(&n, &info->out->buf));
4759         }
4760         n = sizeof(Pipe);
4761         _ckvmssts(lib$free_vm(&n, &info->out));
4762     }
4763     if (info->err) {
4764         if (info->err->buf) {
4765             n = info->err->bufsize * sizeof(char);
4766             _ckvmssts(lib$free_vm(&n, &info->err->buf));
4767         }
4768         n = sizeof(Pipe);
4769         _ckvmssts(lib$free_vm(&n, &info->err));
4770     }
4771     n = sizeof(Info);
4772     _ckvmssts(lib$free_vm(&n, &info));
4773
4774     return retsts;
4775 }
4776
4777
4778 /*{{{  I32 my_pclose(PerlIO *fp)*/
4779 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4780 {
4781     pInfo info, last = NULL;
4782     I32 ret_status;
4783     
4784     /* Fixme - need ast and mutex protection here */
4785     for (info = open_pipes; info != NULL; last = info, info = info->next)
4786         if (info->fp == fp) break;
4787
4788     if (info == NULL) {  /* no such pipe open */
4789       set_errno(ECHILD); /* quoth POSIX */
4790       set_vaxc_errno(SS$_NONEXPR);
4791       return -1;
4792     }
4793
4794     ret_status = my_pclose_pinfo(aTHX_ info);
4795
4796     return ret_status;
4797
4798 }  /* end of my_pclose() */
4799
4800 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4801   /* Roll our own prototype because we want this regardless of whether
4802    * _VMS_WAIT is defined.
4803    */
4804   __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4805 #endif
4806 /* sort-of waitpid; special handling of pipe clean-up for subprocesses 
4807    created with popen(); otherwise partially emulate waitpid() unless 
4808    we have a suitable one from the CRTL that came with VMS 7.2 and later.
4809    Also check processes not considered by the CRTL waitpid().
4810  */
4811 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4812 Pid_t
4813 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4814 {
4815     pInfo info;
4816     int done;
4817     int sts;
4818     int j;
4819     
4820     if (statusp) *statusp = 0;
4821     
4822     for (info = open_pipes; info != NULL; info = info->next)
4823         if (info->pid == pid) break;
4824
4825     if (info != NULL) {  /* we know about this child */
4826       while (!info->done) {
4827           _ckvmssts(sys$setast(0));
4828           done = info->done;
4829           if (!done) _ckvmssts(sys$clref(pipe_ef));
4830           _ckvmssts(sys$setast(1));
4831           if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4832       }
4833
4834       if (statusp) *statusp = info->completion;
4835       return pid;
4836     }
4837
4838     /* child that already terminated? */
4839
4840     for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4841         if (closed_list[j].pid == pid) {
4842             if (statusp) *statusp = closed_list[j].completion;
4843             return pid;
4844         }
4845     }
4846
4847     /* fall through if this child is not one of our own pipe children */
4848
4849 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4850
4851       /* waitpid() became available in the CRTL as of VMS 7.0, but only
4852        * in 7.2 did we get a version that fills in the VMS completion
4853        * status as Perl has always tried to do.
4854        */
4855
4856       sts = __vms_waitpid( pid, statusp, flags );
4857
4858       if ( sts == 0 || !(sts == -1 && errno == ECHILD) ) 
4859          return sts;
4860
4861       /* If the real waitpid tells us the child does not exist, we 
4862        * fall through here to implement waiting for a child that 
4863        * was created by some means other than exec() (say, spawned
4864        * from DCL) or to wait for a process that is not a subprocess 
4865        * of the current process.
4866        */
4867
4868 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4869
4870     {
4871       $DESCRIPTOR(intdsc,"0 00:00:01");
4872       unsigned long int ownercode = JPI$_OWNER, ownerpid;
4873       unsigned long int pidcode = JPI$_PID, mypid;
4874       unsigned long int interval[2];
4875       unsigned int jpi_iosb[2];
4876       struct itmlst_3 jpilist[2] = { 
4877           {sizeof(ownerpid),        JPI$_OWNER, &ownerpid,        0},
4878           {                      0,         0,                 0, 0} 
4879       };
4880
4881       if (pid <= 0) {
4882         /* Sorry folks, we don't presently implement rooting around for 
4883            the first child we can find, and we definitely don't want to
4884            pass a pid of -1 to $getjpi, where it is a wildcard operation.
4885          */
4886         set_errno(ENOTSUP); 
4887         return -1;
4888       }
4889
4890       /* Get the owner of the child so I can warn if it's not mine. If the 
4891        * process doesn't exist or I don't have the privs to look at it, 
4892        * I can go home early.
4893        */
4894       sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4895       if (sts & 1) sts = jpi_iosb[0];
4896       if (!(sts & 1)) {
4897         switch (sts) {
4898             case SS$_NONEXPR:
4899                 set_errno(ECHILD);
4900                 break;
4901             case SS$_NOPRIV:
4902                 set_errno(EACCES);
4903                 break;
4904             default:
4905                 _ckvmssts(sts);
4906         }
4907         set_vaxc_errno(sts);
4908         return -1;
4909       }
4910
4911       if (ckWARN(WARN_EXEC)) {
4912         /* remind folks they are asking for non-standard waitpid behavior */
4913         _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4914         if (ownerpid != mypid)
4915           Perl_warner(aTHX_ packWARN(WARN_EXEC),
4916                       "waitpid: process %x is not a child of process %x",
4917                       pid,mypid);
4918       }
4919
4920       /* simply check on it once a second until it's not there anymore. */
4921
4922       _ckvmssts(sys$bintim(&intdsc,interval));
4923       while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4924             _ckvmssts(sys$schdwk(0,0,interval,0));
4925             _ckvmssts(sys$hiber());
4926       }
4927       if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4928
4929       _ckvmssts(sts);
4930       return pid;
4931     }
4932 }  /* end of waitpid() */
4933 /*}}}*/
4934 /*}}}*/
4935 /*}}}*/
4936
4937 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4938 char *
4939 my_gconvert(double val, int ndig, int trail, char *buf)
4940 {
4941   static char __gcvtbuf[DBL_DIG+1];
4942   char *loc;
4943
4944   loc = buf ? buf : __gcvtbuf;
4945
4946 #ifndef __DECC  /* VAXCRTL gcvt uses E format for numbers < 1 */
4947   if (val < 1) {
4948     sprintf(loc,"%.*g",ndig,val);
4949     return loc;
4950   }
4951 #endif
4952
4953   if (val) {
4954     if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4955     return gcvt(val,ndig,loc);
4956   }
4957   else {
4958     loc[0] = '0'; loc[1] = '\0';
4959     return loc;
4960   }
4961
4962 }
4963 /*}}}*/
4964
4965 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4966 static int rms_free_search_context(struct FAB * fab)
4967 {
4968 struct NAM * nam;
4969
4970     nam = fab->fab$l_nam;
4971     nam->nam$b_nop |= NAM$M_SYNCHK;
4972     nam->nam$l_rlf = NULL;
4973     fab->fab$b_dns = 0;
4974     return sys$parse(fab, NULL, NULL);
4975 }
4976
4977 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4978 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4979 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4980 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4981 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4982 #define rms_nam_esll(nam) nam.nam$b_esl
4983 #define rms_nam_esl(nam) nam.nam$b_esl
4984 #define rms_nam_name(nam) nam.nam$l_name
4985 #define rms_nam_namel(nam) nam.nam$l_name
4986 #define rms_nam_type(nam) nam.nam$l_type
4987 #define rms_nam_typel(nam) nam.nam$l_type
4988 #define rms_nam_ver(nam) nam.nam$l_ver
4989 #define rms_nam_verl(nam) nam.nam$l_ver
4990 #define rms_nam_rsll(nam) nam.nam$b_rsl
4991 #define rms_nam_rsl(nam) nam.nam$b_rsl
4992 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4993 #define rms_set_fna(fab, nam, name, size) \
4994         { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4995 #define rms_get_fna(fab, nam) fab.fab$l_fna
4996 #define rms_set_dna(fab, nam, name, size) \
4997         { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4998 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4999 #define rms_set_esa(nam, name, size) \
5000         { nam.nam$b_ess = size; nam.nam$l_esa = name; }
5001 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5002         { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
5003 #define rms_set_rsa(nam, name, size) \
5004         { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
5005 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5006         { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
5007 #define rms_nam_name_type_l_size(nam) \
5008         (nam.nam$b_name + nam.nam$b_type)
5009 #else
5010 static int rms_free_search_context(struct FAB * fab)
5011 {
5012 struct NAML * nam;
5013
5014     nam = fab->fab$l_naml;
5015     nam->naml$b_nop |= NAM$M_SYNCHK;
5016     nam->naml$l_rlf = NULL;
5017     nam->naml$l_long_defname_size = 0;
5018
5019     fab->fab$b_dns = 0;
5020     return sys$parse(fab, NULL, NULL);
5021 }
5022
5023 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
5024 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
5025 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
5026 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
5027 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
5028 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
5029 #define rms_nam_esl(nam) nam.naml$b_esl
5030 #define rms_nam_name(nam) nam.naml$l_name
5031 #define rms_nam_namel(nam) nam.naml$l_long_name
5032 #define rms_nam_type(nam) nam.naml$l_type
5033 #define rms_nam_typel(nam) nam.naml$l_long_type
5034 #define rms_nam_ver(nam) nam.naml$l_ver
5035 #define rms_nam_verl(nam) nam.naml$l_long_ver
5036 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
5037 #define rms_nam_rsl(nam) nam.naml$b_rsl
5038 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
5039 #define rms_set_fna(fab, nam, name, size) \
5040         { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
5041         nam.naml$l_long_filename_size = size; \
5042         nam.naml$l_long_filename = name;}
5043 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
5044 #define rms_set_dna(fab, nam, name, size) \
5045         { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
5046         nam.naml$l_long_defname_size = size; \
5047         nam.naml$l_long_defname = name; }
5048 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
5049 #define rms_set_esa(nam, name, size) \
5050         { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
5051         nam.naml$l_long_expand_alloc = size; \
5052         nam.naml$l_long_expand = name; }
5053 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5054         { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
5055         nam.naml$l_long_expand = l_name; \
5056         nam.naml$l_long_expand_alloc = l_size; }
5057 #define rms_set_rsa(nam, name, size) \
5058         { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
5059         nam.naml$l_long_result = name; \
5060         nam.naml$l_long_result_alloc = size; }
5061 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5062         { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
5063         nam.naml$l_long_result = l_name; \
5064         nam.naml$l_long_result_alloc = l_size; }
5065 #define rms_nam_name_type_l_size(nam) \
5066         (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
5067 #endif
5068
5069
5070 /* rms_erase
5071  * The CRTL for 8.3 and later can create symbolic links in any mode,
5072  * however in 8.3 the unlink/remove/delete routines will only properly handle
5073  * them if one of the PCP modes is active.
5074  */
5075 static int rms_erase(const char * vmsname)
5076 {
5077   int status;
5078   struct FAB myfab = cc$rms_fab;
5079   rms_setup_nam(mynam);
5080
5081   rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
5082   rms_bind_fab_nam(myfab, mynam);
5083
5084 #ifdef NAML$M_OPEN_SPECIAL
5085   rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5086 #endif
5087
5088   status = sys$erase(&myfab, 0, 0);
5089
5090   return status;
5091 }
5092
5093
5094 static int
5095 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5096                     const struct dsc$descriptor_s * vms_dst_dsc,
5097                     unsigned long flags)
5098 {
5099     /*  VMS and UNIX handle file permissions differently and the
5100      * the same ACL trick may be needed for renaming files,
5101      * especially if they are directories.
5102      */
5103
5104    /* todo: get kill_file and rename to share common code */
5105    /* I can not find online documentation for $change_acl
5106     * it appears to be replaced by $set_security some time ago */
5107
5108 const unsigned int access_mode = 0;
5109 $DESCRIPTOR(obj_file_dsc,"FILE");
5110 char *vmsname;
5111 char *rslt;
5112 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5113 int aclsts, fndsts, rnsts = -1;
5114 unsigned int ctx = 0;
5115 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5116 struct dsc$descriptor_s * clean_dsc;
5117
5118 struct myacedef {
5119     unsigned char myace$b_length;
5120     unsigned char myace$b_type;
5121     unsigned short int myace$w_flags;
5122     unsigned long int myace$l_access;
5123     unsigned long int myace$l_ident;
5124 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5125              ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5126              0},
5127              oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5128
5129 struct item_list_3
5130         findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5131                       {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5132                       {0,0,0,0}},
5133         addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5134         dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5135                      {0,0,0,0}};
5136
5137
5138     /* Expand the input spec using RMS, since we do not want to put
5139      * ACLs on the target of a symbolic link */
5140     vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5141     if (vmsname == NULL)
5142         return SS$_INSFMEM;
5143
5144     rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5145                         vmsname,
5146                         PERL_RMSEXPAND_M_SYMLINK);
5147     if (rslt == NULL) {
5148         PerlMem_free(vmsname);
5149         return SS$_INSFMEM;
5150     }
5151
5152     /* So we get our own UIC to use as a rights identifier,
5153      * and the insert an ACE at the head of the ACL which allows us
5154      * to delete the file.
5155      */
5156     _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5157
5158     fildsc.dsc$w_length = strlen(vmsname);
5159     fildsc.dsc$a_pointer = vmsname;
5160     ctx = 0;
5161     newace.myace$l_ident = oldace.myace$l_ident;
5162     rnsts = SS$_ABORT;
5163
5164     /* Grab any existing ACEs with this identifier in case we fail */
5165     clean_dsc = &fildsc;
5166     aclsts = fndsts = sys$get_security(&obj_file_dsc,
5167                                &fildsc,
5168                                NULL,
5169                                OSS$M_WLOCK,
5170                                findlst,
5171                                &ctx,
5172                                &access_mode);
5173
5174     if ($VMS_STATUS_SUCCESS(fndsts)  || (fndsts == SS$_ACLEMPTY)) {
5175         /* Add the new ACE . . . */
5176
5177         /* if the sys$get_security succeeded, then ctx is valid, and the
5178          * object/file descriptors will be ignored.  But otherwise they
5179          * are needed
5180          */
5181         aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5182                                   OSS$M_RELCTX, addlst, &ctx, &access_mode);
5183         if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5184             set_errno(EVMSERR);
5185             set_vaxc_errno(aclsts);
5186             PerlMem_free(vmsname);
5187             return aclsts;
5188         }
5189
5190         rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5191                                 NULL, NULL,
5192                                 &flags,
5193                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5194
5195         if ($VMS_STATUS_SUCCESS(rnsts)) {
5196             clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5197         }
5198
5199         /* Put things back the way they were. */
5200         ctx = 0;
5201         aclsts = sys$get_security(&obj_file_dsc,
5202                                   clean_dsc,
5203                                   NULL,
5204                                   OSS$M_WLOCK,
5205                                   findlst,
5206                                   &ctx,
5207                                   &access_mode);
5208
5209         if ($VMS_STATUS_SUCCESS(aclsts)) {
5210         int sec_flags;
5211
5212             sec_flags = 0;
5213             if (!$VMS_STATUS_SUCCESS(fndsts))
5214                 sec_flags = OSS$M_RELCTX;
5215
5216             /* Get rid of the new ACE */
5217             aclsts = sys$set_security(NULL, NULL, NULL,
5218                                   sec_flags, dellst, &ctx, &access_mode);
5219
5220             /* If there was an old ACE, put it back */
5221             if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5222                 addlst[0].bufadr = &oldace;
5223                 aclsts = sys$set_security(NULL, NULL, NULL,
5224                                       OSS$M_RELCTX, addlst, &ctx, &access_mode);
5225                 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5226                     set_errno(EVMSERR);
5227                     set_vaxc_errno(aclsts);
5228                     rnsts = aclsts;
5229                 }
5230             } else {
5231             int aclsts2;
5232
5233                 /* Try to clear the lock on the ACL list */
5234                 aclsts2 = sys$set_security(NULL, NULL, NULL,
5235                                       OSS$M_RELCTX, NULL, &ctx, &access_mode);
5236
5237                 /* Rename errors are most important */
5238                 if (!$VMS_STATUS_SUCCESS(rnsts))
5239                     aclsts = rnsts;
5240                 set_errno(EVMSERR);
5241                 set_vaxc_errno(aclsts);
5242                 rnsts = aclsts;
5243             }
5244         }
5245         else {
5246             if (aclsts != SS$_ACLEMPTY)
5247                 rnsts = aclsts;
5248         }
5249     }
5250     else
5251         rnsts = fndsts;
5252
5253     PerlMem_free(vmsname);
5254     return rnsts;
5255 }
5256
5257
5258 /*{{{int rename(const char *, const char * */
5259 /* Not exactly what X/Open says to do, but doing it absolutely right
5260  * and efficiently would require a lot more work.  This should be close
5261  * enough to pass all but the most strict X/Open compliance test.
5262  */
5263 int
5264 Perl_rename(pTHX_ const char *src, const char * dst)
5265 {
5266 int retval;
5267 int pre_delete = 0;
5268 int src_sts;
5269 int dst_sts;
5270 Stat_t src_st;
5271 Stat_t dst_st;
5272
5273     /* Validate the source file */
5274     src_sts = flex_lstat(src, &src_st);
5275     if (src_sts != 0) {
5276
5277         /* No source file or other problem */
5278         return src_sts;
5279     }
5280     if (src_st.st_devnam[0] == 0)  {
5281         /* This may be possible so fail if it is seen. */
5282         errno = EIO;
5283         return -1;
5284     }
5285
5286     dst_sts = flex_lstat(dst, &dst_st);
5287     if (dst_sts == 0) {
5288
5289         if (dst_st.st_dev != src_st.st_dev) {
5290             /* Must be on the same device */
5291             errno = EXDEV;
5292             return -1;
5293         }
5294
5295         /* VMS_INO_T_COMPARE is true if the inodes are different
5296          * to match the output of memcmp
5297          */
5298
5299         if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5300             /* That was easy, the files are the same! */
5301             return 0;
5302         }
5303
5304         if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5305             /* If source is a directory, so must be dest */
5306                 errno = EISDIR;
5307                 return -1;
5308         }
5309
5310     }
5311
5312
5313     if ((dst_sts == 0) &&
5314         (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5315
5316         /* We have issues here if vms_unlink_all_versions is set
5317          * If the destination exists, and is not a directory, then
5318          * we must delete in advance.
5319          *
5320          * If the src is a directory, then we must always pre-delete
5321          * the destination.
5322          *
5323          * If we successfully delete the dst in advance, and the rename fails
5324          * X/Open requires that errno be EIO.
5325          *
5326          */
5327
5328         if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5329             int d_sts;
5330             d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5331                                      S_ISDIR(dst_st.st_mode));
5332
5333            /* Need to delete all versions ? */
5334            if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5335                 int i = 0;
5336
5337                 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5338                     d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5339                     if (d_sts != 0)
5340                         break;
5341                     i++;
5342
5343                     /* Make sure that we do not loop forever */
5344                     if (i > 32767) {
5345                         errno = EIO;
5346                         d_sts = -1;
5347                         break;
5348                     }
5349                 }
5350            }
5351
5352             if (d_sts != 0)
5353                 return d_sts;
5354
5355             /* We killed the destination, so only errno now is EIO */
5356             pre_delete = 1;
5357         }
5358     }
5359
5360     /* Originally the idea was to call the CRTL rename() and only
5361      * try the lib$rename_file if it failed.
5362      * It turns out that there are too many variants in what the
5363      * the CRTL rename might do, so only use lib$rename_file
5364      */
5365     retval = -1;
5366
5367     {
5368         /* Is the source and dest both in VMS format */
5369         /* if the source is a directory, then need to fileify */
5370         /*  and dest must be a directory or non-existant. */
5371
5372         char * vms_dst;
5373         int sts;
5374         char * ret_str;
5375         unsigned long flags;
5376         struct dsc$descriptor_s old_file_dsc;
5377         struct dsc$descriptor_s new_file_dsc;
5378
5379         /* We need to modify the src and dst depending
5380          * on if one or more of them are directories.
5381          */
5382
5383         vms_dst = PerlMem_malloc(VMS_MAXRSS);
5384         if (vms_dst == NULL)
5385             _ckvmssts_noperl(SS$_INSFMEM);
5386
5387         if (S_ISDIR(src_st.st_mode)) {
5388         char * ret_str;
5389         char * vms_dir_file;
5390
5391             vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5392             if (vms_dir_file == NULL)
5393                 _ckvmssts_noperl(SS$_INSFMEM);
5394
5395             /* If the dest is a directory, we must remove it
5396             if (dst_sts == 0) {
5397                 int d_sts;
5398                 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5399                 if (d_sts != 0) {
5400                     PerlMem_free(vms_dst);
5401                     errno = EIO;
5402                     return sts;
5403                 }
5404
5405                 pre_delete = 1;
5406             }
5407
5408            /* The dest must be a VMS file specification */
5409            ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5410            if (ret_str == NULL) {
5411                 PerlMem_free(vms_dst);
5412                 errno = EIO;
5413                 return -1;
5414            }
5415
5416             /* The source must be a file specification */
5417             vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5418             if (vms_dir_file == NULL)
5419                 _ckvmssts_noperl(SS$_INSFMEM);
5420
5421             ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5422             if (ret_str == NULL) {
5423                 PerlMem_free(vms_dst);
5424                 PerlMem_free(vms_dir_file);
5425                 errno = EIO;
5426                 return -1;
5427             }
5428             PerlMem_free(vms_dst);
5429             vms_dst = vms_dir_file;
5430
5431         } else {
5432             /* File to file or file to new dir */
5433
5434             if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5435                 /* VMS pathify a dir target */
5436                 ret_str = int_tovmspath(dst, vms_dst, NULL);
5437                 if (ret_str == NULL) {
5438                     PerlMem_free(vms_dst);
5439                     errno = EIO;
5440                     return -1;
5441                 }
5442             } else {
5443                 char * v_spec, * r_spec, * d_spec, * n_spec;
5444                 char * e_spec, * vs_spec;
5445                 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5446
5447                 /* fileify a target VMS file specification */
5448                 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5449                 if (ret_str == NULL) {
5450                     PerlMem_free(vms_dst);
5451                     errno = EIO;
5452                     return -1;
5453                 }
5454
5455                 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5456                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5457                              &e_len, &vs_spec, &vs_len);
5458                 if (sts == 0) {
5459                      if (e_len == 0) {
5460                          /* Get rid of the version */
5461                          if (vs_len != 0) {
5462                              *vs_spec = '\0';
5463                          }
5464                          /* Need to specify a '.' so that the extension */
5465                          /* is not inherited */
5466                          strcat(vms_dst,".");
5467                      }
5468                 }
5469             }
5470         }
5471
5472         old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5473         old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5474         old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5475         old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5476
5477         new_file_dsc.dsc$a_pointer = vms_dst;
5478         new_file_dsc.dsc$w_length = strlen(vms_dst);
5479         new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5480         new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5481
5482         flags = 0;
5483 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5484         flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5485 #endif
5486
5487         sts = lib$rename_file(&old_file_dsc,
5488                               &new_file_dsc,
5489                               NULL, NULL,
5490                               &flags,
5491                               NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5492         if (!$VMS_STATUS_SUCCESS(sts)) {
5493
5494            /* We could have failed because VMS style permissions do not
5495             * permit renames that UNIX will allow.  Just like the hack
5496             * in for kill_file.
5497             */
5498            sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5499         }
5500
5501         PerlMem_free(vms_dst);
5502         if (!$VMS_STATUS_SUCCESS(sts)) {
5503             errno = EIO;
5504             return -1;
5505         }
5506         retval = 0;
5507     }
5508
5509     if (vms_unlink_all_versions) {
5510         /* Now get rid of any previous versions of the source file that
5511          * might still exist
5512          */
5513         int i = 0;
5514         dSAVEDERRNO;
5515         SAVE_ERRNO;
5516         src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5517                                    S_ISDIR(src_st.st_mode));
5518         while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5519              src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5520                                        S_ISDIR(src_st.st_mode));
5521              if (src_sts != 0)
5522                  break;
5523              i++;
5524
5525              /* Make sure that we do not loop forever */
5526              if (i > 32767) {
5527                  src_sts = -1;
5528                  break;
5529              }
5530         }
5531         RESTORE_ERRNO;
5532     }
5533
5534     /* We deleted the destination, so must force the error to be EIO */
5535     if ((retval != 0) && (pre_delete != 0))
5536         errno = EIO;
5537
5538     return retval;
5539 }
5540 /*}}}*/
5541
5542
5543 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5544 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5545  * to expand file specification.  Allows for a single default file
5546  * specification and a simple mask of options.  If outbuf is non-NULL,
5547  * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5548  * the resultant file specification is placed.  If outbuf is NULL, the
5549  * resultant file specification is placed into a static buffer.
5550  * The third argument, if non-NULL, is taken to be a default file
5551  * specification string.  The fourth argument is unused at present.
5552  * rmesexpand() returns the address of the resultant string if
5553  * successful, and NULL on error.
5554  *
5555  * New functionality for previously unused opts value:
5556  *  PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5557  *  PERL_RMSEXPAND_M_LONG - Want output in long formst
5558  *  PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5559  *  PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5560  */
5561 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5562
5563 static char *
5564 int_rmsexpand
5565    (const char *filespec,
5566     char *outbuf,
5567     const char *defspec,
5568     unsigned opts,
5569     int * fs_utf8,
5570     int * dfs_utf8)
5571 {
5572   char * ret_spec;
5573   const char * in_spec;
5574   char * spec_buf;
5575   const char * def_spec;
5576   char * vmsfspec, *vmsdefspec;
5577   char * esa;
5578   char * esal = NULL;
5579   char * outbufl;
5580   struct FAB myfab = cc$rms_fab;
5581   rms_setup_nam(mynam);
5582   STRLEN speclen;
5583   unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5584   int sts;
5585
5586   /* temp hack until UTF8 is actually implemented */
5587   if (fs_utf8 != NULL)
5588     *fs_utf8 = 0;
5589
5590   if (!filespec || !*filespec) {
5591     set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5592     return NULL;
5593   }
5594
5595   vmsfspec = NULL;
5596   vmsdefspec = NULL;
5597   outbufl = NULL;
5598
5599   in_spec = filespec;
5600   isunix = 0;
5601   if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5602       char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5603       int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5604
5605       /* If this is a UNIX file spec, convert it to VMS */
5606       sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5607                            &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5608                            &e_len, &vs_spec, &vs_len);
5609       if (sts != 0) {
5610           isunix = 1;
5611           char * ret_spec;
5612
5613           vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5614           if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5615           ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5616           if (ret_spec == NULL) {
5617               PerlMem_free(vmsfspec);
5618               return NULL;
5619           }
5620           in_spec = (const char *)vmsfspec;
5621
5622           /* Unless we are forcing to VMS format, a UNIX input means
5623            * UNIX output, and that requires long names to be used
5624            */
5625           if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5626 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5627               opts |= PERL_RMSEXPAND_M_LONG;
5628 #else
5629               NOOP;
5630 #endif
5631           else
5632               isunix = 0;
5633       }
5634
5635   }
5636
5637   rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5638   rms_bind_fab_nam(myfab, mynam);
5639
5640   /* Process the default file specification if present */
5641   def_spec = defspec;
5642   if (defspec && *defspec) {
5643     int t_isunix;
5644     t_isunix = is_unix_filespec(defspec);
5645     if (t_isunix) {
5646       vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5647       if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5648       ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5649
5650       if (ret_spec == NULL) {
5651           /* Clean up and bail */
5652           PerlMem_free(vmsdefspec);
5653           if (vmsfspec != NULL)
5654               PerlMem_free(vmsfspec);
5655               return NULL;
5656           }
5657           def_spec = (const char *)vmsdefspec;
5658       }
5659       rms_set_dna(myfab, mynam,
5660                   (char *)def_spec, strlen(def_spec)); /* cast ok */
5661   }
5662
5663   /* Now we need the expansion buffers */
5664   esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5665   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5666 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5667   esal = PerlMem_malloc(VMS_MAXRSS);
5668   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5669 #endif
5670   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5671
5672   /* If a NAML block is used RMS always writes to the long and short
5673    * addresses unless you suppress the short name.
5674    */
5675 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5676   outbufl = PerlMem_malloc(VMS_MAXRSS);
5677   if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5678 #endif
5679    rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5680
5681 #ifdef NAM$M_NO_SHORT_UPCASE
5682   if (decc_efs_case_preserve)
5683     rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5684 #endif
5685
5686    /* We may not want to follow symbolic links */
5687 #ifdef NAML$M_OPEN_SPECIAL
5688   if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5689     rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5690 #endif
5691
5692   /* First attempt to parse as an existing file */
5693   retsts = sys$parse(&myfab,0,0);
5694   if (!(retsts & STS$K_SUCCESS)) {
5695
5696     /* Could not find the file, try as syntax only if error is not fatal */
5697     rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5698     if (retsts == RMS$_DNF ||
5699         retsts == RMS$_DIR ||
5700         retsts == RMS$_DEV ||
5701         retsts == RMS$_PRV) {
5702       retsts = sys$parse(&myfab,0,0);
5703       if (retsts & STS$K_SUCCESS) goto int_expanded;
5704     }  
5705
5706      /* Still could not parse the file specification */
5707     /*----------------------------------------------*/
5708     sts = rms_free_search_context(&myfab); /* Free search context */
5709     if (vmsdefspec != NULL)
5710         PerlMem_free(vmsdefspec);
5711     if (vmsfspec != NULL)
5712         PerlMem_free(vmsfspec);
5713     if (outbufl != NULL)
5714         PerlMem_free(outbufl);
5715     PerlMem_free(esa);
5716     if (esal != NULL) 
5717         PerlMem_free(esal);
5718     set_vaxc_errno(retsts);
5719     if      (retsts == RMS$_PRV) set_errno(EACCES);
5720     else if (retsts == RMS$_DEV) set_errno(ENODEV);
5721     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5722     else                         set_errno(EVMSERR);
5723     return NULL;
5724   }
5725   retsts = sys$search(&myfab,0,0);
5726   if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5727     sts = rms_free_search_context(&myfab); /* Free search context */
5728     if (vmsdefspec != NULL)
5729         PerlMem_free(vmsdefspec);
5730     if (vmsfspec != NULL)
5731         PerlMem_free(vmsfspec);
5732     if (outbufl != NULL)
5733         PerlMem_free(outbufl);
5734     PerlMem_free(esa);
5735     if (esal != NULL) 
5736         PerlMem_free(esal);
5737     set_vaxc_errno(retsts);
5738     if      (retsts == RMS$_PRV) set_errno(EACCES);
5739     else                         set_errno(EVMSERR);
5740     return NULL;
5741   }
5742
5743   /* If the input filespec contained any lowercase characters,
5744    * downcase the result for compatibility with Unix-minded code. */
5745 int_expanded:
5746   if (!decc_efs_case_preserve) {
5747     char * tbuf;
5748     for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5749       if (islower(*tbuf)) { haslower = 1; break; }
5750   }
5751
5752    /* Is a long or a short name expected */
5753   /*------------------------------------*/
5754   spec_buf = NULL;
5755 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5756   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5757     if (rms_nam_rsll(mynam)) {
5758         spec_buf = outbufl;
5759         speclen = rms_nam_rsll(mynam);
5760     }
5761     else {
5762         spec_buf = esal; /* Not esa */
5763         speclen = rms_nam_esll(mynam);
5764     }
5765   }
5766   else {
5767 #endif
5768     if (rms_nam_rsl(mynam)) {
5769         spec_buf = outbuf;
5770         speclen = rms_nam_rsl(mynam);
5771     }
5772     else {
5773         spec_buf = esa; /* Not esal */
5774         speclen = rms_nam_esl(mynam);
5775     }
5776 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5777   }
5778 #endif
5779   spec_buf[speclen] = '\0';
5780
5781   /* Trim off null fields added by $PARSE
5782    * If type > 1 char, must have been specified in original or default spec
5783    * (not true for version; $SEARCH may have added version of existing file).
5784    */
5785   trimver  = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5786   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5787     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5788              ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5789   }
5790   else {
5791     trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5792              ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5793   }
5794   if (trimver || trimtype) {
5795     if (defspec && *defspec) {
5796       char *defesal = NULL;
5797       char *defesa = NULL;
5798       defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5799       if (defesa != NULL) {
5800         struct FAB deffab = cc$rms_fab;
5801 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5802         defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5803         if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5804 #endif
5805         rms_setup_nam(defnam);
5806      
5807         rms_bind_fab_nam(deffab, defnam);
5808
5809         /* Cast ok */ 
5810         rms_set_fna
5811             (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam)); 
5812
5813         /* RMS needs the esa/esal as a work area if wildcards are involved */
5814         rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5815
5816         rms_clear_nam_nop(defnam);
5817         rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5818 #ifdef NAM$M_NO_SHORT_UPCASE
5819         if (decc_efs_case_preserve)
5820           rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5821 #endif
5822 #ifdef NAML$M_OPEN_SPECIAL
5823         if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5824           rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5825 #endif
5826         if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5827           if (trimver) {
5828              trimver  = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5829           }
5830           if (trimtype) {
5831             trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE); 
5832           }
5833         }
5834         if (defesal != NULL)
5835             PerlMem_free(defesal);
5836         PerlMem_free(defesa);
5837       } else {
5838           _ckvmssts_noperl(SS$_INSFMEM);
5839       }
5840     }
5841     if (trimver) {
5842       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5843         if (*(rms_nam_verl(mynam)) != '\"')
5844           speclen = rms_nam_verl(mynam) - spec_buf;
5845       }
5846       else {
5847         if (*(rms_nam_ver(mynam)) != '\"')
5848           speclen = rms_nam_ver(mynam) - spec_buf;
5849       }
5850     }
5851     if (trimtype) {
5852       /* If we didn't already trim version, copy down */
5853       if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5854         if (speclen > rms_nam_verl(mynam) - spec_buf)
5855           memmove
5856            (rms_nam_typel(mynam),
5857             rms_nam_verl(mynam),
5858             speclen - (rms_nam_verl(mynam) - spec_buf));
5859           speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5860       }
5861       else {
5862         if (speclen > rms_nam_ver(mynam) - spec_buf)
5863           memmove
5864            (rms_nam_type(mynam),
5865             rms_nam_ver(mynam),
5866             speclen - (rms_nam_ver(mynam) - spec_buf));
5867           speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5868       }
5869     }
5870   }
5871
5872    /* Done with these copies of the input files */
5873   /*-------------------------------------------*/
5874   if (vmsfspec != NULL)
5875         PerlMem_free(vmsfspec);
5876   if (vmsdefspec != NULL)
5877         PerlMem_free(vmsdefspec);
5878
5879   /* If we just had a directory spec on input, $PARSE "helpfully"
5880    * adds an empty name and type for us */
5881 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5882   if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5883     if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5884         rms_nam_verl(mynam)  == rms_nam_typel(mynam) + 1 &&
5885         !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5886       speclen = rms_nam_namel(mynam) - spec_buf;
5887   }
5888   else
5889 #endif
5890   {
5891     if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5892         rms_nam_ver(mynam)  == rms_nam_ver(mynam) + 1 &&
5893         !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5894       speclen = rms_nam_name(mynam) - spec_buf;
5895   }
5896
5897   /* Posix format specifications must have matching quotes */
5898   if (speclen < (VMS_MAXRSS - 1)) {
5899     if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5900       if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5901         spec_buf[speclen] = '\"';
5902         speclen++;
5903       }
5904     }
5905   }
5906   spec_buf[speclen] = '\0';
5907   if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5908
5909   /* Have we been working with an expanded, but not resultant, spec? */
5910   /* Also, convert back to Unix syntax if necessary. */
5911   {
5912   int rsl;
5913
5914 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5915     if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5916       rsl = rms_nam_rsll(mynam);
5917     } else
5918 #endif
5919     {
5920       rsl = rms_nam_rsl(mynam);
5921     }
5922     if (!rsl) {
5923       /* rsl is not present, it means that spec_buf is either */
5924       /* esa or esal, and needs to be copied to outbuf */
5925       /* convert to Unix if desired */
5926       if (isunix) {
5927         ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5928       } else {
5929         /* VMS file specs are not in UTF-8 */
5930         if (fs_utf8 != NULL)
5931             *fs_utf8 = 0;
5932         strcpy(outbuf, spec_buf);
5933         ret_spec = outbuf;
5934       }
5935     }
5936     else {
5937       /* Now spec_buf is either outbuf or outbufl */
5938       /* We need the result into outbuf */
5939       if (isunix) {
5940            /* If we need this in UNIX, then we need another buffer */
5941            /* to keep things in order */
5942            char * src;
5943            char * new_src = NULL;
5944            if (spec_buf == outbuf) {
5945                new_src = PerlMem_malloc(VMS_MAXRSS);
5946                strcpy(new_src, spec_buf);
5947            } else {
5948                src = spec_buf;
5949            }
5950            ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5951            if (new_src) {
5952                PerlMem_free(new_src);
5953            }
5954       } else {
5955            /* VMS file specs are not in UTF-8 */
5956            if (fs_utf8 != NULL)
5957                *fs_utf8 = 0;
5958
5959            /* Copy the buffer if needed */
5960            if (outbuf != spec_buf)
5961                strcpy(outbuf, spec_buf);
5962            ret_spec = outbuf;
5963       }
5964     }
5965   }
5966
5967   /* Need to clean up the search context */
5968   rms_set_rsal(mynam, NULL, 0, NULL, 0);
5969   sts = rms_free_search_context(&myfab); /* Free search context */
5970
5971   /* Clean up the extra buffers */
5972   if (esal != NULL)
5973       PerlMem_free(esal);
5974   PerlMem_free(esa);
5975   if (outbufl != NULL)
5976      PerlMem_free(outbufl);
5977
5978   /* Return the result */
5979   return ret_spec;
5980 }
5981
5982 /* Common simple case - Expand an already VMS spec */
5983 static char * 
5984 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5985     opts |= PERL_RMSEXPAND_M_VMS_IN;
5986     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 
5987 }
5988
5989 /* Common simple case - Expand to a VMS spec */
5990 static char * 
5991 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5992     opts |= PERL_RMSEXPAND_M_VMS;
5993     return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL); 
5994 }
5995
5996
5997 /* Entry point used by perl routines */
5998 static char *
5999 mp_do_rmsexpand
6000    (pTHX_ const char *filespec,
6001     char *outbuf,
6002     int ts,
6003     const char *defspec,
6004     unsigned opts,
6005     int * fs_utf8,
6006     int * dfs_utf8)
6007 {
6008     static char __rmsexpand_retbuf[VMS_MAXRSS];
6009     char * expanded, *ret_spec, *ret_buf;
6010
6011     expanded = NULL;
6012     ret_buf = outbuf;
6013     if (ret_buf == NULL) {
6014         if (ts) {
6015             Newx(expanded, VMS_MAXRSS, char);
6016             if (expanded == NULL)
6017                 _ckvmssts(SS$_INSFMEM);
6018             ret_buf = expanded;
6019         } else {
6020             ret_buf = __rmsexpand_retbuf;
6021         }
6022     }
6023
6024
6025     ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
6026                              opts, fs_utf8,  dfs_utf8);
6027
6028     if (ret_spec == NULL) {
6029        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6030        if (expanded)
6031            Safefree(expanded);
6032     }
6033
6034     return ret_spec;
6035 }
6036 /*}}}*/
6037 /* External entry points */
6038 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6039 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
6040 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6041 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
6042 char *Perl_rmsexpand_utf8
6043   (pTHX_ const char *spec, char *buf, const char *def,
6044    unsigned opt, int * fs_utf8, int * dfs_utf8)
6045 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
6046 char *Perl_rmsexpand_utf8_ts
6047   (pTHX_ const char *spec, char *buf, const char *def,
6048    unsigned opt, int * fs_utf8, int * dfs_utf8)
6049 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
6050
6051
6052 /*
6053 ** The following routines are provided to make life easier when
6054 ** converting among VMS-style and Unix-style directory specifications.
6055 ** All will take input specifications in either VMS or Unix syntax. On
6056 ** failure, all return NULL.  If successful, the routines listed below
6057 ** return a pointer to a buffer containing the appropriately
6058 ** reformatted spec (and, therefore, subsequent calls to that routine
6059 ** will clobber the result), while the routines of the same names with
6060 ** a _ts suffix appended will return a pointer to a mallocd string
6061 ** containing the appropriately reformatted spec.
6062 ** In all cases, only explicit syntax is altered; no check is made that
6063 ** the resulting string is valid or that the directory in question
6064 ** actually exists.
6065 **
6066 **   fileify_dirspec() - convert a directory spec into the name of the
6067 **     directory file (i.e. what you can stat() to see if it's a dir).
6068 **     The style (VMS or Unix) of the result is the same as the style
6069 **     of the parameter passed in.
6070 **   pathify_dirspec() - convert a directory spec into a path (i.e.
6071 **     what you prepend to a filename to indicate what directory it's in).
6072 **     The style (VMS or Unix) of the result is the same as the style
6073 **     of the parameter passed in.
6074 **   tounixpath() - convert a directory spec into a Unix-style path.
6075 **   tovmspath() - convert a directory spec into a VMS-style path.
6076 **   tounixspec() - convert any file spec into a Unix-style file spec.
6077 **   tovmsspec() - convert any file spec into a VMS-style spec.
6078 **   xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6079 **
6080 ** Copyright 1996 by Charles Bailey  <bailey@newman.upenn.edu>
6081 ** Permission is given to distribute this code as part of the Perl
6082 ** standard distribution under the terms of the GNU General Public
6083 ** License or the Perl Artistic License.  Copies of each may be
6084 ** found in the Perl standard distribution.
6085  */
6086
6087 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6088 static char *
6089 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6090 {
6091     unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
6092     char *cp1, *cp2, *lastdir;
6093     char *trndir, *vmsdir;
6094     unsigned short int trnlnm_iter_count;
6095     int is_vms = 0;
6096     int is_unix = 0;
6097     int sts;
6098     if (utf8_fl != NULL)
6099         *utf8_fl = 0;
6100
6101     if (!dir || !*dir) {
6102       set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6103     }
6104     dirlen = strlen(dir);
6105     while (dirlen && dir[dirlen-1] == '/') --dirlen;
6106     if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6107       if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6108         dir = "/sys$disk";
6109         dirlen = 9;
6110       }
6111       else
6112         dirlen = 1;
6113     }
6114     if (dirlen > (VMS_MAXRSS - 1)) {
6115       set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6116       return NULL;
6117     }
6118     trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6119     if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6120     if (!strpbrk(dir+1,"/]>:")  &&
6121         (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6122       strcpy(trndir,*dir == '/' ? dir + 1: dir);
6123       trnlnm_iter_count = 0;
6124       while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6125         trnlnm_iter_count++; 
6126         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6127       }
6128       dirlen = strlen(trndir);
6129     }
6130     else {
6131       strncpy(trndir,dir,dirlen);
6132       trndir[dirlen] = '\0';
6133     }
6134
6135     /* At this point we are done with *dir and use *trndir which is a
6136      * copy that can be modified.  *dir must not be modified.
6137      */
6138
6139     /* If we were handed a rooted logical name or spec, treat it like a
6140      * simple directory, so that
6141      *    $ Define myroot dev:[dir.]
6142      *    ... do_fileify_dirspec("myroot",buf,1) ...
6143      * does something useful.
6144      */
6145     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6146       trndir[--dirlen] = '\0';
6147       trndir[dirlen-1] = ']';
6148     }
6149     if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6150       trndir[--dirlen] = '\0';
6151       trndir[dirlen-1] = '>';
6152     }
6153
6154     if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6155       /* If we've got an explicit filename, we can just shuffle the string. */
6156       if (*(cp1+1)) hasfilename = 1;
6157       /* Similarly, we can just back up a level if we've got multiple levels
6158          of explicit directories in a VMS spec which ends with directories. */
6159       else {
6160         for (cp2 = cp1; cp2 > trndir; cp2--) {
6161           if (*cp2 == '.') {
6162             if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6163 /* fix-me, can not scan EFS file specs backward like this */
6164               *cp2 = *cp1; *cp1 = '\0';
6165               hasfilename = 1;
6166               break;
6167             }
6168           }
6169           if (*cp2 == '[' || *cp2 == '<') break;
6170         }
6171       }
6172     }
6173
6174     vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6175     if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6176     cp1 = strpbrk(trndir,"]:>");
6177     if (hasfilename || !cp1) { /* filename present or not VMS */
6178
6179       if (decc_efs_charset && !cp1) {
6180
6181           /* EFS handling for UNIX mode */
6182
6183           /* Just remove the trailing '/' and we should be done */
6184           STRLEN trndir_len;
6185           trndir_len = strlen(trndir);
6186
6187           if (trndir_len > 1) {
6188               trndir_len--;
6189               if (trndir[trndir_len] == '/') {
6190                   trndir[trndir_len] = '\0';
6191               }
6192           }
6193           strcpy(buf, trndir);
6194           PerlMem_free(trndir);
6195           PerlMem_free(vmsdir);
6196           return buf;
6197       }
6198
6199       /* For non-EFS mode, this is left for backwards compatibility */
6200       /* For EFS mode, this is only done for VMS format filespecs as */
6201       /* Perl programs generally have problems when a UNIX format spec */
6202       /* returns a VMS format spec */
6203       if (trndir[0] == '.') {
6204         if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6205           PerlMem_free(trndir);
6206           PerlMem_free(vmsdir);
6207           return int_fileify_dirspec("[]", buf, NULL);
6208         }
6209         else if (trndir[1] == '.' &&
6210                (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6211           PerlMem_free(trndir);
6212           PerlMem_free(vmsdir);
6213           return int_fileify_dirspec("[-]", buf, NULL);
6214         }
6215       }
6216       if (dirlen && trndir[dirlen-1] == '/') {    /* path ends with '/'; just add .dir;1 */
6217         dirlen -= 1;                 /* to last element */
6218         lastdir = strrchr(trndir,'/');
6219       }
6220       else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6221         /* If we have "/." or "/..", VMSify it and let the VMS code
6222          * below expand it, rather than repeating the code to handle
6223          * relative components of a filespec here */
6224         do {
6225           if (*(cp1+2) == '.') cp1++;
6226           if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6227             char * ret_chr;
6228             if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6229                 PerlMem_free(trndir);
6230                 PerlMem_free(vmsdir);
6231                 return NULL;
6232             }
6233             if (strchr(vmsdir,'/') != NULL) {
6234               /* If int_tovmsspec() returned it, it must have VMS syntax
6235                * delimiters in it, so it's a mixed VMS/Unix spec.  We take
6236                * the time to check this here only so we avoid a recursion
6237                * loop; otherwise, gigo.
6238                */
6239               PerlMem_free(trndir);
6240               PerlMem_free(vmsdir);
6241               set_errno(EINVAL);  set_vaxc_errno(RMS$_SYN);
6242               return NULL;
6243             }
6244             if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6245                 PerlMem_free(trndir);
6246                 PerlMem_free(vmsdir);
6247                 return NULL;
6248             }
6249             ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6250             PerlMem_free(trndir);
6251             PerlMem_free(vmsdir);
6252             return ret_chr;
6253           }
6254           cp1++;
6255         } while ((cp1 = strstr(cp1,"/.")) != NULL);
6256         lastdir = strrchr(trndir,'/');
6257       }
6258       else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6259         char * ret_chr;
6260         /* Ditto for specs that end in an MFD -- let the VMS code
6261          * figure out whether it's a real device or a rooted logical. */
6262
6263         /* This should not happen any more.  Allowing the fake /000000
6264          * in a UNIX pathname causes all sorts of problems when trying
6265          * to run in UNIX emulation.  So the VMS to UNIX conversions
6266          * now remove the fake /000000 directories.
6267          */
6268
6269         trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6270         if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6271             PerlMem_free(trndir);
6272             PerlMem_free(vmsdir);
6273             return NULL;
6274         }
6275         if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6276             PerlMem_free(trndir);
6277             PerlMem_free(vmsdir);
6278             return NULL;
6279         }
6280         ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6281         PerlMem_free(trndir);
6282         PerlMem_free(vmsdir);
6283         return ret_chr;
6284       }
6285       else {
6286
6287         if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6288              !(lastdir = cp1 = strrchr(trndir,']')) &&
6289              !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6290
6291         cp2 = strrchr(cp1,'.');
6292         if (cp2) {
6293             int e_len, vs_len = 0;
6294             int is_dir = 0;
6295             char * cp3;
6296             cp3 = strchr(cp2,';');
6297             e_len = strlen(cp2);
6298             if (cp3) {
6299                 vs_len = strlen(cp3);
6300                 e_len = e_len - vs_len;
6301             }
6302             is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6303             if (!is_dir) {
6304                 if (!decc_efs_charset) {
6305                     /* If this is not EFS, then not a directory */
6306                     PerlMem_free(trndir);
6307                     PerlMem_free(vmsdir);
6308                     set_errno(ENOTDIR);
6309                     set_vaxc_errno(RMS$_DIR);
6310                     return NULL;
6311                 }
6312             } else {
6313                 /* Ok, here we have an issue, technically if a .dir shows */
6314                 /* from inside a directory, then we should treat it as */
6315                 /* xxx^.dir.dir.  But we do not have that context at this */
6316                 /* point unless this is totally restructured, so we remove */
6317                 /* The .dir for now, and fix this better later */
6318                 dirlen = cp2 - trndir;
6319             }
6320         }
6321
6322       }
6323
6324       retlen = dirlen + 6;
6325       memcpy(buf, trndir, dirlen);
6326       buf[dirlen] = '\0';
6327
6328       /* We've picked up everything up to the directory file name.
6329          Now just add the type and version, and we're set. */
6330
6331       /* We should only add type for VMS syntax, but historically Perl
6332          has added it for UNIX style also */
6333
6334       /* Fix me - we should not be using the same routine for VMS and
6335          UNIX format files.  Things are too tangled so we need to lookup
6336          what syntax the output is */
6337
6338       is_unix = 0;
6339       is_vms = 0;
6340       lastdir = strrchr(trndir,'/');
6341       if (lastdir) {
6342           is_unix = 1;
6343       } else {
6344           lastdir = strpbrk(trndir,"]:>");
6345           if (lastdir) {
6346               is_vms = 1;
6347           }
6348       }
6349
6350       if ((is_vms == 0) && (is_unix == 0)) {
6351           /* We still do not  know? */
6352           is_unix = decc_filename_unix_report;
6353           if (is_unix == 0)
6354               is_vms = 1;
6355       }
6356
6357       if ((is_unix && !decc_efs_charset) || is_vms) {
6358
6359            /* It is a bug to add a .dir to a UNIX format directory spec */
6360            /* However Perl on VMS may have programs that expect this so */
6361            /* If not using EFS character specifications allow it. */
6362
6363            if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6364                /* Traditionally Perl expects filenames in lower case */
6365                strcat(buf, ".dir");
6366            } else {
6367                /* VMS expects the .DIR to be in upper case */
6368                strcat(buf, ".DIR");
6369            }
6370
6371            /* It is also a bug to put a VMS format version on a UNIX file */
6372            /* specification.  Perl self tests are looking for this */
6373            if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6374                strcat(buf, ";1");
6375       }
6376       PerlMem_free(trndir);
6377       PerlMem_free(vmsdir);
6378       return buf;
6379     }
6380     else {  /* VMS-style directory spec */
6381
6382       char *esa, *esal, term, *cp;
6383       char *my_esa;
6384       int my_esa_len;
6385       unsigned long int sts, cmplen, haslower = 0;
6386       unsigned int nam_fnb;
6387       char * nam_type;
6388       struct FAB dirfab = cc$rms_fab;
6389       rms_setup_nam(savnam);
6390       rms_setup_nam(dirnam);
6391
6392       esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6393       if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6394       esal = NULL;
6395 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6396       esal = PerlMem_malloc(VMS_MAXRSS);
6397       if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6398 #endif
6399       rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6400       rms_bind_fab_nam(dirfab, dirnam);
6401       rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6402       rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6403 #ifdef NAM$M_NO_SHORT_UPCASE
6404       if (decc_efs_case_preserve)
6405         rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6406 #endif
6407
6408       for (cp = trndir; *cp; cp++)
6409         if (islower(*cp)) { haslower = 1; break; }
6410       if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6411         if ((dirfab.fab$l_sts == RMS$_DIR) ||
6412             (dirfab.fab$l_sts == RMS$_DNF) ||
6413             (dirfab.fab$l_sts == RMS$_PRV)) {
6414             rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6415             sts = sys$parse(&dirfab);
6416         }
6417         if (!sts) {
6418           PerlMem_free(esa);
6419           if (esal != NULL)
6420               PerlMem_free(esal);
6421           PerlMem_free(trndir);
6422           PerlMem_free(vmsdir);
6423           set_errno(EVMSERR);
6424           set_vaxc_errno(dirfab.fab$l_sts);
6425           return NULL;
6426         }
6427       }
6428       else {
6429         savnam = dirnam;
6430         /* Does the file really exist? */
6431         if (sys$search(&dirfab)& STS$K_SUCCESS) { 
6432           /* Yes; fake the fnb bits so we'll check type below */
6433           rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6434         }
6435         else { /* No; just work with potential name */
6436           if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6437           else { 
6438             int fab_sts;
6439             fab_sts = dirfab.fab$l_sts;
6440             sts = rms_free_search_context(&dirfab);
6441             PerlMem_free(esa);
6442             if (esal != NULL)
6443                 PerlMem_free(esal);
6444             PerlMem_free(trndir);
6445             PerlMem_free(vmsdir);
6446             set_errno(EVMSERR);  set_vaxc_errno(fab_sts);
6447             return NULL;
6448           }
6449         }
6450       }
6451
6452       /* Make sure we are using the right buffer */
6453 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6454       if (esal != NULL) {
6455         my_esa = esal;
6456         my_esa_len = rms_nam_esll(dirnam);
6457       } else {
6458 #endif
6459         my_esa = esa;
6460         my_esa_len = rms_nam_esl(dirnam);
6461 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6462       }
6463 #endif
6464       my_esa[my_esa_len] = '\0';
6465       if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6466         cp1 = strchr(my_esa,']');
6467         if (!cp1) cp1 = strchr(my_esa,'>');
6468         if (cp1) {  /* Should always be true */
6469           my_esa_len -= cp1 - my_esa - 1;
6470           memmove(my_esa, cp1 + 1, my_esa_len);
6471         }
6472       }
6473       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) {  /* Was type specified? */
6474         /* Yep; check version while we're at it, if it's there. */
6475         cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6476         if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) { 
6477           /* Something other than .DIR[;1].  Bzzt. */
6478           sts = rms_free_search_context(&dirfab);
6479           PerlMem_free(esa);
6480           if (esal != NULL)
6481              PerlMem_free(esal);
6482           PerlMem_free(trndir);
6483           PerlMem_free(vmsdir);
6484           set_errno(ENOTDIR);
6485           set_vaxc_errno(RMS$_DIR);
6486           return NULL;
6487         }
6488       }
6489
6490       if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6491         /* They provided at least the name; we added the type, if necessary, */
6492         strcpy(buf, my_esa);
6493         sts = rms_free_search_context(&dirfab);
6494         PerlMem_free(trndir);
6495         PerlMem_free(esa);
6496         if (esal != NULL)
6497             PerlMem_free(esal);
6498         PerlMem_free(vmsdir);
6499         return buf;
6500       }
6501       if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6502         for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6503         *cp1 = '\0';
6504         my_esa_len -= 9;
6505       }
6506       if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6507       if (cp1 == NULL) { /* should never happen */
6508         sts = rms_free_search_context(&dirfab);
6509         PerlMem_free(trndir);
6510         PerlMem_free(esa);
6511         if (esal != NULL)
6512             PerlMem_free(esal);
6513         PerlMem_free(vmsdir);
6514         return NULL;
6515       }
6516       term = *cp1;
6517       *cp1 = '\0';
6518       retlen = strlen(my_esa);
6519       cp1 = strrchr(my_esa,'.');
6520       /* ODS-5 directory specifications can have extra "." in them. */
6521       /* Fix-me, can not scan EFS file specifications backwards */
6522       while (cp1 != NULL) {
6523         if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6524           break;
6525         else {
6526            cp1--;
6527            while ((cp1 > my_esa) && (*cp1 != '.'))
6528              cp1--;
6529         }
6530         if (cp1 == my_esa)
6531           cp1 = NULL;
6532       }
6533
6534       if ((cp1) != NULL) {
6535         /* There's more than one directory in the path.  Just roll back. */
6536         *cp1 = term;
6537         strcpy(buf, my_esa);
6538       }
6539       else {
6540         if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6541           /* Go back and expand rooted logical name */
6542           rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6543 #ifdef NAM$M_NO_SHORT_UPCASE
6544           if (decc_efs_case_preserve)
6545             rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6546 #endif
6547           if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6548             sts = rms_free_search_context(&dirfab);
6549             PerlMem_free(esa);
6550             if (esal != NULL)
6551                 PerlMem_free(esal);
6552             PerlMem_free(trndir);
6553             PerlMem_free(vmsdir);
6554             set_errno(EVMSERR);
6555             set_vaxc_errno(dirfab.fab$l_sts);
6556             return NULL;
6557           }
6558
6559           /* This changes the length of the string of course */
6560           if (esal != NULL) {
6561               my_esa_len = rms_nam_esll(dirnam);
6562           } else {
6563               my_esa_len = rms_nam_esl(dirnam);
6564           }
6565
6566           retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6567           cp1 = strstr(my_esa,"][");
6568           if (!cp1) cp1 = strstr(my_esa,"]<");
6569           dirlen = cp1 - my_esa;
6570           memcpy(buf, my_esa, dirlen);
6571           if (!strncmp(cp1+2,"000000]",7)) {
6572             buf[dirlen-1] = '\0';
6573             /* fix-me Not full ODS-5, just extra dots in directories for now */
6574             cp1 = buf + dirlen - 1;
6575             while (cp1 > buf)
6576             {
6577               if (*cp1 == '[')
6578                 break;
6579               if (*cp1 == '.') {
6580                 if (*(cp1-1) != '^')
6581                   break;
6582               }
6583               cp1--;
6584             }
6585             if (*cp1 == '.') *cp1 = ']';
6586             else {
6587               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6588               memmove(cp1+1,"000000]",7);
6589             }
6590           }
6591           else {
6592             memmove(buf+dirlen, cp1+2, retlen-dirlen);
6593             buf[retlen] = '\0';
6594             /* Convert last '.' to ']' */
6595             cp1 = buf+retlen-1;
6596             while (*cp != '[') {
6597               cp1--;
6598               if (*cp1 == '.') {
6599                 /* Do not trip on extra dots in ODS-5 directories */
6600                 if ((cp1 == buf) || (*(cp1-1) != '^'))
6601                 break;
6602               }
6603             }
6604             if (*cp1 == '.') *cp1 = ']';
6605             else {
6606               memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6607               memmove(cp1+1,"000000]",7);
6608             }
6609           }
6610         }
6611         else {  /* This is a top-level dir.  Add the MFD to the path. */
6612           cp1 = my_esa;
6613           cp2 = buf;
6614           while ((*cp1 != ':')  && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6615           strcpy(cp2,":[000000]");
6616           cp1 += 2;
6617           strcpy(cp2+9,cp1);
6618         }
6619       }
6620       sts = rms_free_search_context(&dirfab);
6621       /* We've set up the string up through the filename.  Add the
6622          type and version, and we're done. */
6623       strcat(buf,".DIR;1");
6624
6625       /* $PARSE may have upcased filespec, so convert output to lower
6626        * case if input contained any lowercase characters. */
6627       if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6628       PerlMem_free(trndir);
6629       PerlMem_free(esa);
6630       if (esal != NULL)
6631         PerlMem_free(esal);
6632       PerlMem_free(vmsdir);
6633       return buf;
6634     }
6635 }  /* end of int_fileify_dirspec() */
6636
6637
6638 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6639 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6640 {
6641     static char __fileify_retbuf[VMS_MAXRSS];
6642     char * fileified, *ret_spec, *ret_buf;
6643
6644     fileified = NULL;
6645     ret_buf = buf;
6646     if (ret_buf == NULL) {
6647         if (ts) {
6648             Newx(fileified, VMS_MAXRSS, char);
6649             if (fileified == NULL)
6650                 _ckvmssts(SS$_INSFMEM);
6651             ret_buf = fileified;
6652         } else {
6653             ret_buf = __fileify_retbuf;
6654         }
6655     }
6656
6657     ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6658
6659     if (ret_spec == NULL) {
6660        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6661        if (fileified)
6662            Safefree(fileified);
6663     }
6664
6665     return ret_spec;
6666 }  /* end of do_fileify_dirspec() */
6667 /*}}}*/
6668
6669 /* External entry points */
6670 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6671 { return do_fileify_dirspec(dir,buf,0,NULL); }
6672 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6673 { return do_fileify_dirspec(dir,buf,1,NULL); }
6674 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6675 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6676 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6677 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6678
6679 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6680     char * v_spec, int v_len, char * r_spec, int r_len,
6681     char * d_spec, int d_len, char * n_spec, int n_len,
6682     char * e_spec, int e_len, char * vs_spec, int vs_len) {
6683
6684     /* VMS specification - Try to do this the simple way */
6685     if ((v_len + r_len > 0) || (d_len > 0)) {
6686         int is_dir;
6687
6688         /* No name or extension component, already a directory */
6689         if ((n_len + e_len + vs_len) == 0) {
6690             strcpy(buf, dir);
6691             return buf;
6692         }
6693
6694         /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6695         /* This results from catfile() being used instead of catdir() */
6696         /* So even though it should not work, we need to allow it */
6697
6698         /* If this is .DIR;1 then do a simple conversion */
6699         is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6700         if (is_dir || (e_len == 0) && (d_len > 0)) {
6701              int len;
6702              len = v_len + r_len + d_len - 1;
6703              char dclose = d_spec[d_len - 1];
6704              strncpy(buf, dir, len);
6705              buf[len] = '.';
6706              len++;
6707              strncpy(&buf[len], n_spec, n_len);
6708              len += n_len;
6709              buf[len] = dclose;
6710              buf[len + 1] = '\0';
6711              return buf;
6712         }
6713
6714 #ifdef HAS_SYMLINK
6715         else if (d_len > 0) {
6716             /* In the olden days, a directory needed to have a .DIR */
6717             /* extension to be a valid directory, but now it could  */
6718             /* be a symbolic link */
6719             int len;
6720             len = v_len + r_len + d_len - 1;
6721             char dclose = d_spec[d_len - 1];
6722             strncpy(buf, dir, len);
6723             buf[len] = '.';
6724             len++;
6725             strncpy(&buf[len], n_spec, n_len);
6726             len += n_len;
6727             if (e_len > 0) {
6728                 if (decc_efs_charset) {
6729                     buf[len] = '^';
6730                     len++;
6731                     strncpy(&buf[len], e_spec, e_len);
6732                     len += e_len;
6733                 } else {
6734                     set_vaxc_errno(RMS$_DIR);
6735                     set_errno(ENOTDIR);
6736                     return NULL;
6737                 }
6738             }
6739             buf[len] = dclose;
6740             buf[len + 1] = '\0';
6741             return buf;
6742         }
6743 #else
6744         else {
6745             set_vaxc_errno(RMS$_DIR);
6746             set_errno(ENOTDIR);
6747             return NULL;
6748         }
6749 #endif
6750     }
6751     set_vaxc_errno(RMS$_DIR);
6752     set_errno(ENOTDIR);
6753     return NULL;
6754 }
6755
6756
6757 /* Internal routine to make sure or convert a directory to be in a */
6758 /* path specification.  No utf8 flag because it is not changed or used */
6759 static char *int_pathify_dirspec(const char *dir, char *buf)
6760 {
6761     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6762     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6763     char * exp_spec, *ret_spec;
6764     char * trndir;
6765     unsigned short int trnlnm_iter_count;
6766     STRLEN trnlen;
6767     int need_to_lower;
6768
6769     if (vms_debug_fileify) {
6770         if (dir == NULL)
6771             fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6772         else
6773             fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6774     }
6775
6776     /* We may need to lower case the result if we translated  */
6777     /* a logical name or got the current working directory */
6778     need_to_lower = 0;
6779
6780     if (!dir || !*dir) {
6781       set_errno(EINVAL);
6782       set_vaxc_errno(SS$_BADPARAM);
6783       return NULL;
6784     }
6785
6786     trndir = PerlMem_malloc(VMS_MAXRSS);
6787     if (trndir == NULL)
6788         _ckvmssts_noperl(SS$_INSFMEM);
6789
6790     /* If no directory specified use the current default */
6791     if (*dir)
6792         strcpy(trndir, dir);
6793     else {
6794         getcwd(trndir, VMS_MAXRSS - 1);
6795         need_to_lower = 1;
6796     }
6797
6798     /* now deal with bare names that could be logical names */
6799     trnlnm_iter_count = 0;
6800     while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6801            && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6802         trnlnm_iter_count++; 
6803         need_to_lower = 1;
6804         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6805             break;
6806         trnlen = strlen(trndir);
6807
6808         /* Trap simple rooted lnms, and return lnm:[000000] */
6809         if (!strcmp(trndir+trnlen-2,".]")) {
6810             strcpy(buf, dir);
6811             strcat(buf, ":[000000]");
6812             PerlMem_free(trndir);
6813
6814             if (vms_debug_fileify) {
6815                 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6816             }
6817             return buf;
6818         }
6819     }
6820
6821     /* At this point we do not work with *dir, but the copy in  *trndir */
6822
6823     if (need_to_lower && !decc_efs_case_preserve) {
6824         /* Legacy mode, lower case the returned value */
6825         __mystrtolower(trndir);
6826     }
6827
6828
6829     /* Some special cases, '..', '.' */
6830     sts = 0;
6831     if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6832        /* Force UNIX filespec */
6833        sts = 1;
6834
6835     } else {
6836         /* Is this Unix or VMS format? */
6837         sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6838                              &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6839                              &e_len, &vs_spec, &vs_len);
6840         if (sts == 0) {
6841
6842             /* Just a filename? */
6843             if ((v_len + r_len + d_len) == 0) {
6844
6845                 /* Now we have a problem, this could be Unix or VMS */
6846                 /* We have to guess.  .DIR usually means VMS */
6847
6848                 /* In UNIX report mode, the .DIR extension is removed */
6849                 /* if one shows up, it is for a non-directory or a directory */
6850                 /* in EFS charset mode */
6851
6852                 /* So if we are in Unix report mode, assume that this */
6853                 /* is a relative Unix directory specification */
6854
6855                 sts = 1;
6856                 if (!decc_filename_unix_report && decc_efs_charset) {
6857                     int is_dir;
6858                     is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6859
6860                     if (is_dir) {
6861                         /* Traditional mode, assume .DIR is directory */
6862                         buf[0] = '[';
6863                         buf[1] = '.';
6864                         strncpy(&buf[2], n_spec, n_len);
6865                         buf[n_len + 2] = ']';
6866                         buf[n_len + 3] = '\0';
6867                         PerlMem_free(trndir);
6868                         if (vms_debug_fileify) {
6869                             fprintf(stderr,
6870                                     "int_pathify_dirspec: buf = %s\n",
6871                                     buf);
6872                         }
6873                         return buf;
6874                     }
6875                 }
6876             }
6877         }
6878     }
6879     if (sts == 0) {
6880         ret_spec = int_pathify_dirspec_simple(trndir, buf,
6881             v_spec, v_len, r_spec, r_len,
6882             d_spec, d_len, n_spec, n_len,
6883             e_spec, e_len, vs_spec, vs_len);
6884
6885         if (ret_spec != NULL) {
6886             PerlMem_free(trndir);
6887             if (vms_debug_fileify) {
6888                 fprintf(stderr,
6889                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6890             }
6891             return ret_spec;
6892         }
6893
6894         /* Simple way did not work, which means that a logical name */
6895         /* was present for the directory specification.             */
6896         /* Need to use an rmsexpand variant to decode it completely */
6897         exp_spec = PerlMem_malloc(VMS_MAXRSS);
6898         if (exp_spec == NULL)
6899             _ckvmssts_noperl(SS$_INSFMEM);
6900
6901         ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6902         if (ret_spec != NULL) {
6903             sts = vms_split_path(exp_spec, &v_spec, &v_len,
6904                                  &r_spec, &r_len, &d_spec, &d_len,
6905                                  &n_spec, &n_len, &e_spec,
6906                                  &e_len, &vs_spec, &vs_len);
6907             if (sts == 0) {
6908                 ret_spec = int_pathify_dirspec_simple(
6909                     exp_spec, buf, v_spec, v_len, r_spec, r_len,
6910                     d_spec, d_len, n_spec, n_len,
6911                     e_spec, e_len, vs_spec, vs_len);
6912
6913                 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6914                     /* Legacy mode, lower case the returned value */
6915                     __mystrtolower(ret_spec);
6916                 }
6917             } else {
6918                 set_vaxc_errno(RMS$_DIR);
6919                 set_errno(ENOTDIR);
6920                 ret_spec = NULL;
6921             }
6922         }
6923         PerlMem_free(exp_spec);
6924         PerlMem_free(trndir);
6925         if (vms_debug_fileify) {
6926             if (ret_spec == NULL)
6927                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6928             else
6929                 fprintf(stderr,
6930                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6931         }
6932         return ret_spec;
6933
6934     } else {
6935         /* Unix specification, Could be trivial conversion */
6936         STRLEN dir_len;
6937         dir_len = strlen(trndir);
6938
6939         /* If the extended file character set is in effect */
6940         /* then pathify is simple */
6941
6942         if (!decc_efs_charset) {
6943             /* Have to deal with traiing '.dir' or extra '.' */
6944             /* that should not be there in legacy mode, but is */
6945
6946             char * lastdot;
6947             char * lastslash;
6948             int is_dir;
6949
6950             lastslash = strrchr(trndir, '/');
6951             if (lastslash == NULL)
6952                 lastslash = trndir;
6953             else
6954                 lastslash++;
6955
6956             lastdot = NULL;
6957
6958             /* '..' or '.' are valid directory components */
6959             is_dir = 0;
6960             if (lastslash[0] == '.') {
6961                 if (lastslash[1] == '\0') {
6962                    is_dir = 1;
6963                 } else if (lastslash[1] == '.') {
6964                     if (lastslash[2] == '\0') {
6965                         is_dir = 1;
6966                     } else {
6967                         /* And finally allow '...' */
6968                         if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6969                             is_dir = 1;
6970                         }
6971                     }
6972                 }
6973             }
6974
6975             if (!is_dir) {
6976                lastdot = strrchr(lastslash, '.');
6977             }
6978             if (lastdot != NULL) {
6979                 STRLEN e_len;
6980
6981                 /* '.dir' is discarded, and any other '.' is invalid */
6982                 e_len = strlen(lastdot);
6983
6984                 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6985
6986                 if (is_dir) {
6987                     dir_len = dir_len - 4;
6988
6989                 }
6990             }
6991         }
6992
6993         strcpy(buf, trndir);
6994         if (buf[dir_len - 1] != '/') {
6995             buf[dir_len] = '/';
6996             buf[dir_len + 1] = '\0';
6997         }
6998
6999         /* Under ODS-2 rules, '.' becomes '_', so fix it up */
7000         if (!decc_efs_charset) {
7001              int dir_start = 0;
7002              char * str = buf;
7003              if (str[0] == '.') {
7004                  char * dots = str;
7005                  int cnt = 1;
7006                  while ((dots[cnt] == '.') && (cnt < 3))
7007                      cnt++;
7008                  if (cnt <= 3) {
7009                      if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
7010                          dir_start = 1;
7011                          str += cnt;
7012                      }
7013                  }
7014              }
7015              for (; *str; ++str) {
7016                  while (*str == '/') {
7017                      dir_start = 1;
7018                      *str++;
7019                  }
7020                  if (dir_start) {
7021
7022                      /* Have to skip up to three dots which could be */
7023                      /* directories, 3 dots being a VMS extension for Perl */
7024                      char * dots = str;
7025                      int cnt = 0;
7026                      while ((dots[cnt] == '.') && (cnt < 3)) {
7027                          cnt++;
7028                      }
7029                      if (dots[cnt] == '\0')
7030                          break;
7031                      if ((cnt > 1) && (dots[cnt] != '/')) {
7032                          dir_start = 0;
7033                      } else {
7034                          str += cnt;
7035                      }
7036
7037                      /* too many dots? */
7038                      if ((cnt == 0) || (cnt > 3)) {
7039                          dir_start = 0;
7040                      }
7041                  }
7042                  if (!dir_start && (*str == '.')) {
7043                      *str = '_';
7044                  }                 
7045              }
7046         }
7047         PerlMem_free(trndir);
7048         ret_spec = buf;
7049         if (vms_debug_fileify) {
7050             if (ret_spec == NULL)
7051                 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
7052             else
7053                 fprintf(stderr,
7054                         "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
7055         }
7056         return ret_spec;
7057     }
7058 }
7059
7060 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
7061 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
7062 {
7063     static char __pathify_retbuf[VMS_MAXRSS];
7064     char * pathified, *ret_spec, *ret_buf;
7065     
7066     pathified = NULL;
7067     ret_buf = buf;
7068     if (ret_buf == NULL) {
7069         if (ts) {
7070             Newx(pathified, VMS_MAXRSS, char);
7071             if (pathified == NULL)
7072                 _ckvmssts(SS$_INSFMEM);
7073             ret_buf = pathified;
7074         } else {
7075             ret_buf = __pathify_retbuf;
7076         }
7077     }
7078
7079     ret_spec = int_pathify_dirspec(dir, ret_buf);
7080
7081     if (ret_spec == NULL) {
7082        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7083        if (pathified)
7084            Safefree(pathified);
7085     }
7086
7087     return ret_spec;
7088
7089 }  /* end of do_pathify_dirspec() */
7090
7091
7092 /* External entry points */
7093 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
7094 { return do_pathify_dirspec(dir,buf,0,NULL); }
7095 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7096 { return do_pathify_dirspec(dir,buf,1,NULL); }
7097 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7098 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7099 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7100 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
7101
7102 /* Internal tounixspec routine that does not use a thread context */
7103 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7104 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7105 {
7106   char *dirend, *cp1, *cp3, *tmp;
7107   const char *cp2;
7108   int devlen, dirlen, retlen = VMS_MAXRSS;
7109   int expand = 1; /* guarantee room for leading and trailing slashes */
7110   unsigned short int trnlnm_iter_count;
7111   int cmp_rslt;
7112   if (utf8_fl != NULL)
7113     *utf8_fl = 0;
7114
7115   if (vms_debug_fileify) {
7116       if (spec == NULL)
7117           fprintf(stderr, "int_tounixspec: spec = NULL\n");
7118       else
7119           fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7120   }
7121
7122
7123   if (spec == NULL) {
7124       set_errno(EINVAL);
7125       set_vaxc_errno(SS$_BADPARAM);
7126       return NULL;
7127   }
7128   if (strlen(spec) > (VMS_MAXRSS-1)) {
7129       set_errno(E2BIG);
7130       set_vaxc_errno(SS$_BUFFEROVF);
7131       return NULL;
7132   }
7133
7134   /* New VMS specific format needs translation
7135    * glob passes filenames with trailing '\n' and expects this preserved.
7136    */
7137   if (decc_posix_compliant_pathnames) {
7138     if (strncmp(spec, "\"^UP^", 5) == 0) {
7139       char * uspec;
7140       char *tunix;
7141       int tunix_len;
7142       int nl_flag;
7143
7144       tunix = PerlMem_malloc(VMS_MAXRSS);
7145       if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7146       strcpy(tunix, spec);
7147       tunix_len = strlen(tunix);
7148       nl_flag = 0;
7149       if (tunix[tunix_len - 1] == '\n') {
7150         tunix[tunix_len - 1] = '\"';
7151         tunix[tunix_len] = '\0';
7152         tunix_len--;
7153         nl_flag = 1;
7154       }
7155       uspec = decc$translate_vms(tunix);
7156       PerlMem_free(tunix);
7157       if ((int)uspec > 0) {
7158         strcpy(rslt,uspec);
7159         if (nl_flag) {
7160           strcat(rslt,"\n");
7161         }
7162         else {
7163           /* If we can not translate it, makemaker wants as-is */
7164           strcpy(rslt, spec);
7165         }
7166         return rslt;
7167       }
7168     }
7169   }
7170
7171   cmp_rslt = 0; /* Presume VMS */
7172   cp1 = strchr(spec, '/');
7173   if (cp1 == NULL)
7174     cmp_rslt = 0;
7175
7176     /* Look for EFS ^/ */
7177     if (decc_efs_charset) {
7178       while (cp1 != NULL) {
7179         cp2 = cp1 - 1;
7180         if (*cp2 != '^') {
7181           /* Found illegal VMS, assume UNIX */
7182           cmp_rslt = 1;
7183           break;
7184         }
7185       cp1++;
7186       cp1 = strchr(cp1, '/');
7187     }
7188   }
7189
7190   /* Look for "." and ".." */
7191   if (decc_filename_unix_report) {
7192     if (spec[0] == '.') {
7193       if ((spec[1] == '\0') || (spec[1] == '\n')) {
7194         cmp_rslt = 1;
7195       }
7196       else {
7197         if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7198           cmp_rslt = 1;
7199         }
7200       }
7201     }
7202   }
7203   /* This is already UNIX or at least nothing VMS understands */
7204   if (cmp_rslt) {
7205     strcpy(rslt,spec);
7206     if (vms_debug_fileify) {
7207         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7208     }
7209     return rslt;
7210   }
7211
7212   cp1 = rslt;
7213   cp2 = spec;
7214   dirend = strrchr(spec,']');
7215   if (dirend == NULL) dirend = strrchr(spec,'>');
7216   if (dirend == NULL) dirend = strchr(spec,':');
7217   if (dirend == NULL) {
7218     strcpy(rslt,spec);
7219     if (vms_debug_fileify) {
7220         fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7221     }
7222     return rslt;
7223   }
7224
7225   /* Special case 1 - sys$posix_root = / */
7226 #if __CRTL_VER >= 70000000
7227   if (!decc_disable_posix_root) {
7228     if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7229       *cp1 = '/';
7230       cp1++;
7231       cp2 = cp2 + 15;
7232       }
7233   }
7234 #endif
7235
7236   /* Special case 2 - Convert NLA0: to /dev/null */
7237 #if __CRTL_VER < 70000000
7238   cmp_rslt = strncmp(spec,"NLA0:", 5);
7239   if (cmp_rslt != 0)
7240      cmp_rslt = strncmp(spec,"nla0:", 5);
7241 #else
7242   cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7243 #endif
7244   if (cmp_rslt == 0) {
7245     strcpy(rslt, "/dev/null");
7246     cp1 = cp1 + 9;
7247     cp2 = cp2 + 5;
7248     if (spec[6] != '\0') {
7249       cp1[9] == '/';
7250       cp1++;
7251       cp2++;
7252     }
7253   }
7254
7255    /* Also handle special case "SYS$SCRATCH:" */
7256 #if __CRTL_VER < 70000000
7257   cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7258   if (cmp_rslt != 0)
7259      cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7260 #else
7261   cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7262 #endif
7263   tmp = PerlMem_malloc(VMS_MAXRSS);
7264   if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7265   if (cmp_rslt == 0) {
7266   int islnm;
7267
7268     islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7269     if (!islnm) {
7270       strcpy(rslt, "/tmp");
7271       cp1 = cp1 + 4;
7272       cp2 = cp2 + 12;
7273       if (spec[12] != '\0') {
7274         cp1[4] == '/';
7275         cp1++;
7276         cp2++;
7277       }
7278     }
7279   }
7280
7281   if (*cp2 != '[' && *cp2 != '<') {
7282     *(cp1++) = '/';
7283   }
7284   else {  /* the VMS spec begins with directories */
7285     cp2++;
7286     if (*cp2 == ']' || *cp2 == '>') {
7287       *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7288       PerlMem_free(tmp);
7289       return rslt;
7290     }
7291     else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7292       if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7293         PerlMem_free(tmp);
7294         if (vms_debug_fileify) {
7295             fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7296         }
7297         return NULL;
7298       }
7299       trnlnm_iter_count = 0;
7300       do {
7301         cp3 = tmp;
7302         while (*cp3 != ':' && *cp3) cp3++;
7303         *(cp3++) = '\0';
7304         if (strchr(cp3,']') != NULL) break;
7305         trnlnm_iter_count++; 
7306         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7307       } while (vmstrnenv(tmp,tmp,0,fildev,0));
7308       cp1 = rslt;
7309       cp3 = tmp;
7310       *(cp1++) = '/';
7311       while (*cp3) {
7312         *(cp1++) = *(cp3++);
7313         if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7314             PerlMem_free(tmp);
7315             set_errno(ENAMETOOLONG);
7316             set_vaxc_errno(SS$_BUFFEROVF);
7317             if (vms_debug_fileify) {
7318                 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7319             }
7320             return NULL; /* No room */
7321         }
7322       }
7323       *(cp1++) = '/';
7324     }
7325     if ((*cp2 == '^')) {
7326         /* EFS file escape, pass the next character as is */
7327         /* Fix me: HEX encoding for Unicode not implemented */
7328         cp2++;
7329     }
7330     else if ( *cp2 == '.') {
7331       if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7332         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7333         cp2 += 3;
7334       }
7335       else cp2++;
7336     }
7337   }
7338   PerlMem_free(tmp);
7339   for (; cp2 <= dirend; cp2++) {
7340     if ((*cp2 == '^')) {
7341         /* EFS file escape, pass the next character as is */
7342         /* Fix me: HEX encoding for Unicode not implemented */
7343         *(cp1++) = *(++cp2);
7344         /* An escaped dot stays as is -- don't convert to slash */
7345         if (*cp2 == '.') cp2++;
7346     }
7347     if (*cp2 == ':') {
7348       *(cp1++) = '/';
7349       if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7350     }
7351     else if (*cp2 == ']' || *cp2 == '>') {
7352       if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7353     }
7354     else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7355       *(cp1++) = '/';
7356       if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7357         while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7358                *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7359         if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7360             *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7361       }
7362       else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7363         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7364         cp2 += 2;
7365       }
7366     }
7367     else if (*cp2 == '-') {
7368       if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7369         while (*cp2 == '-') {
7370           cp2++;
7371           *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7372         }
7373         if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7374                                                          /* filespecs like */
7375           set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);   /* [fred.--foo.bar] */
7376           if (vms_debug_fileify) {
7377               fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7378           }
7379           return NULL;
7380         }
7381       }
7382       else *(cp1++) = *cp2;
7383     }
7384     else *(cp1++) = *cp2;
7385   }
7386   /* Translate the rest of the filename. */
7387   while (*cp2) {
7388       int dot_seen;
7389       dot_seen = 0;
7390       switch(*cp2) {
7391       /* Fixme - for compatibility with the CRTL we should be removing */
7392       /* spaces from the file specifications, but this may show that */
7393       /* some tests that were appearing to pass are not really passing */
7394       case '%':
7395           cp2++;
7396           *(cp1++) = '?';
7397           break;
7398       case '^':
7399           /* Fix me hex expansions not implemented */
7400           cp2++;  /* '^.' --> '.' and other. */
7401           if (*cp2) {
7402               if (*cp2 == '_') {
7403                   cp2++;
7404                   *(cp1++) = ' ';
7405               } else {
7406                   *(cp1++) = *(cp2++);
7407               }
7408           }
7409           break;
7410       case ';':
7411           if (decc_filename_unix_no_version) {
7412               /* Easy, drop the version */
7413               while (*cp2)
7414                   cp2++;
7415               break;
7416           } else {
7417               /* Punt - passing the version as a dot will probably */
7418               /* break perl in weird ways, but so did passing */
7419               /* through the ; as a version.  Follow the CRTL and */
7420               /* hope for the best. */
7421               cp2++;
7422               *(cp1++) = '.';
7423           }
7424           break;
7425       case '.':
7426           if (dot_seen) {
7427               /* We will need to fix this properly later */
7428               /* As Perl may be installed on an ODS-5 volume, but not */
7429               /* have the EFS_CHARSET enabled, it still may encounter */
7430               /* filenames with extra dots in them, and a precedent got */
7431               /* set which allowed them to work, that we will uphold here */
7432               /* If extra dots are present in a name and no ^ is on them */
7433               /* VMS assumes that the first one is the extension delimiter */
7434               /* the rest have an implied ^. */
7435
7436               /* this is also a conflict as the . is also a version */
7437               /* delimiter in VMS, */
7438
7439               *(cp1++) = *(cp2++);
7440               break;
7441           }
7442           dot_seen = 1;
7443           /* This is an extension */
7444           if (decc_readdir_dropdotnotype) {
7445               cp2++;
7446               if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7447                   /* Drop the dot for the extension */
7448                   break;
7449               } else {
7450                   *(cp1++) = '.';
7451               }
7452               break;
7453           }
7454       default:
7455           *(cp1++) = *(cp2++);
7456       }
7457   }
7458   *cp1 = '\0';
7459
7460   /* This still leaves /000000/ when working with a
7461    * VMS device root or concealed root.
7462    */
7463   {
7464   int ulen;
7465   char * zeros;
7466
7467       ulen = strlen(rslt);
7468
7469       /* Get rid of "000000/ in rooted filespecs */
7470       if (ulen > 7) {
7471         zeros = strstr(rslt, "/000000/");
7472         if (zeros != NULL) {
7473           int mlen;
7474           mlen = ulen - (zeros - rslt) - 7;
7475           memmove(zeros, &zeros[7], mlen);
7476           ulen = ulen - 7;
7477           rslt[ulen] = '\0';
7478         }
7479       }
7480   }
7481
7482   if (vms_debug_fileify) {
7483       fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7484   }
7485   return rslt;
7486
7487 }  /* end of int_tounixspec() */
7488
7489
7490 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7491 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7492 {
7493     static char __tounixspec_retbuf[VMS_MAXRSS];
7494     char * unixspec, *ret_spec, *ret_buf;
7495
7496     unixspec = NULL;
7497     ret_buf = buf;
7498     if (ret_buf == NULL) {
7499         if (ts) {
7500             Newx(unixspec, VMS_MAXRSS, char);
7501             if (unixspec == NULL)
7502                 _ckvmssts(SS$_INSFMEM);
7503             ret_buf = unixspec;
7504         } else {
7505             ret_buf = __tounixspec_retbuf;
7506         }
7507     }
7508
7509     ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7510
7511     if (ret_spec == NULL) {
7512        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7513        if (unixspec)
7514            Safefree(unixspec);
7515     }
7516
7517     return ret_spec;
7518
7519 }  /* end of do_tounixspec() */
7520 /*}}}*/
7521 /* External entry points */
7522 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7523   { return do_tounixspec(spec,buf,0, NULL); }
7524 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7525   { return do_tounixspec(spec,buf,1, NULL); }
7526 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7527   { return do_tounixspec(spec,buf,0, utf8_fl); }
7528 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7529   { return do_tounixspec(spec,buf,1, utf8_fl); }
7530
7531 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7532
7533 /*
7534  This procedure is used to identify if a path is based in either
7535  the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7536  it returns the OpenVMS format directory for it.
7537
7538  It is expecting specifications of only '/' or '/xxxx/'
7539
7540  If a posix root does not exist, or 'xxxx' is not a directory
7541  in the posix root, it returns a failure.
7542
7543  FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7544
7545  It is used only internally by posix_to_vmsspec_hardway().
7546  */
7547
7548 static int posix_root_to_vms
7549   (char *vmspath, int vmspath_len,
7550    const char *unixpath,
7551    const int * utf8_fl)
7552 {
7553 int sts;
7554 struct FAB myfab = cc$rms_fab;
7555 rms_setup_nam(mynam);
7556 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7557 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7558 char * esa, * esal, * rsa, * rsal;
7559 char *vms_delim;
7560 int dir_flag;
7561 int unixlen;
7562
7563     dir_flag = 0;
7564     vmspath[0] = '\0';
7565     unixlen = strlen(unixpath);
7566     if (unixlen == 0) {
7567       return RMS$_FNF;
7568     }
7569
7570 #if __CRTL_VER >= 80200000
7571   /* If not a posix spec already, convert it */
7572   if (decc_posix_compliant_pathnames) {
7573     if (strncmp(unixpath,"\"^UP^",5) != 0) {
7574       sprintf(vmspath,"\"^UP^%s\"",unixpath);
7575     }
7576     else {
7577       /* This is already a VMS specification, no conversion */
7578       unixlen--;
7579       strncpy(vmspath,unixpath, vmspath_len);
7580     }
7581   }
7582   else
7583 #endif
7584   {     
7585   int path_len;
7586   int i,j;
7587
7588      /* Check to see if this is under the POSIX root */
7589      if (decc_disable_posix_root) {
7590         return RMS$_FNF;
7591      }
7592
7593      /* Skip leading / */
7594      if (unixpath[0] == '/') {
7595         unixpath++;
7596         unixlen--;
7597      }
7598
7599
7600      strcpy(vmspath,"SYS$POSIX_ROOT:");
7601
7602      /* If this is only the / , or blank, then... */
7603      if (unixpath[0] == '\0') {
7604         /* by definition, this is the answer */
7605         return SS$_NORMAL;
7606      }
7607
7608      /* Need to look up a directory */
7609      vmspath[15] = '[';
7610      vmspath[16] = '\0';
7611
7612      /* Copy and add '^' escape characters as needed */
7613      j = 16;
7614      i = 0;
7615      while (unixpath[i] != 0) {
7616      int k;
7617
7618         j += copy_expand_unix_filename_escape
7619             (&vmspath[j], &unixpath[i], &k, utf8_fl);
7620         i += k;
7621      }
7622
7623      path_len = strlen(vmspath);
7624      if (vmspath[path_len - 1] == '/')
7625         path_len--;
7626      vmspath[path_len] = ']';
7627      path_len++;
7628      vmspath[path_len] = '\0';
7629         
7630   }
7631   vmspath[vmspath_len] = 0;
7632   if (unixpath[unixlen - 1] == '/')
7633   dir_flag = 1;
7634   esal = PerlMem_malloc(VMS_MAXRSS);
7635   if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7636   esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7637   if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7638   rsal = PerlMem_malloc(VMS_MAXRSS);
7639   if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7640   rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7641   if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7642   rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7643   rms_bind_fab_nam(myfab, mynam);
7644   rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7645   rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7646   if (decc_efs_case_preserve)
7647     mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7648 #ifdef NAML$M_OPEN_SPECIAL
7649   mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7650 #endif
7651
7652   /* Set up the remaining naml fields */
7653   sts = sys$parse(&myfab);
7654
7655   /* It failed! Try again as a UNIX filespec */
7656   if (!(sts & 1)) {
7657     PerlMem_free(esal);
7658     PerlMem_free(esa);
7659     PerlMem_free(rsal);
7660     PerlMem_free(rsa);
7661     return sts;
7662   }
7663
7664    /* get the Device ID and the FID */
7665    sts = sys$search(&myfab);
7666
7667    /* These are no longer needed */
7668    PerlMem_free(esa);
7669    PerlMem_free(rsal);
7670    PerlMem_free(rsa);
7671
7672    /* on any failure, returned the POSIX ^UP^ filespec */
7673    if (!(sts & 1)) {
7674       PerlMem_free(esal);
7675       return sts;
7676    }
7677    specdsc.dsc$a_pointer = vmspath;
7678    specdsc.dsc$w_length = vmspath_len;
7679  
7680    dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7681    dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7682    sts = lib$fid_to_name
7683       (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7684
7685   /* on any failure, returned the POSIX ^UP^ filespec */
7686   if (!(sts & 1)) {
7687      /* This can happen if user does not have permission to read directories */
7688      if (strncmp(unixpath,"\"^UP^",5) != 0)
7689        sprintf(vmspath,"\"^UP^%s\"",unixpath);
7690      else
7691        strcpy(vmspath, unixpath);
7692   }
7693   else {
7694     vmspath[specdsc.dsc$w_length] = 0;
7695
7696     /* Are we expecting a directory? */
7697     if (dir_flag != 0) {
7698     int i;
7699     char *eptr;
7700
7701       eptr = NULL;
7702
7703       i = specdsc.dsc$w_length - 1;
7704       while (i > 0) {
7705       int zercnt;
7706         zercnt = 0;
7707         /* Version must be '1' */
7708         if (vmspath[i--] != '1')
7709           break;
7710         /* Version delimiter is one of ".;" */
7711         if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7712           break;
7713         i--;
7714         if (vmspath[i--] != 'R')
7715           break;
7716         if (vmspath[i--] != 'I')
7717           break;
7718         if (vmspath[i--] != 'D')
7719           break;
7720         if (vmspath[i--] != '.')
7721           break;
7722         eptr = &vmspath[i+1];
7723         while (i > 0) {
7724           if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7725             if (vmspath[i-1] != '^') {
7726               if (zercnt != 6) {
7727                 *eptr = vmspath[i];
7728                 eptr[1] = '\0';
7729                 vmspath[i] = '.';
7730                 break;
7731               }
7732               else {
7733                 /* Get rid of 6 imaginary zero directory filename */
7734                 vmspath[i+1] = '\0';
7735               }
7736             }
7737           }
7738           if (vmspath[i] == '0')
7739             zercnt++;
7740           else
7741             zercnt = 10;
7742           i--;
7743         }
7744         break;
7745       }
7746     }
7747   }
7748   PerlMem_free(esal);
7749   return sts;
7750 }
7751
7752 /* /dev/mumble needs to be handled special.
7753    /dev/null becomes NLA0:, And there is the potential for other stuff
7754    like /dev/tty which may need to be mapped to something.
7755 */
7756
7757 static int 
7758 slash_dev_special_to_vms
7759    (const char * unixptr,
7760     char * vmspath,
7761     int vmspath_len)
7762 {
7763 char * nextslash;
7764 int len;
7765 int cmp;
7766 int islnm;
7767
7768     unixptr += 4;
7769     nextslash = strchr(unixptr, '/');
7770     len = strlen(unixptr);
7771     if (nextslash != NULL)
7772         len = nextslash - unixptr;
7773     cmp = strncmp("null", unixptr, 5);
7774     if (cmp == 0) {
7775         if (vmspath_len >= 6) {
7776             strcpy(vmspath, "_NLA0:");
7777             return SS$_NORMAL;
7778         }
7779     }
7780 }
7781
7782
7783 /* The built in routines do not understand perl's special needs, so
7784     doing a manual conversion from UNIX to VMS
7785
7786     If the utf8_fl is not null and points to a non-zero value, then
7787     treat 8 bit characters as UTF-8.
7788
7789     The sequence starting with '$(' and ending with ')' will be passed
7790     through with out interpretation instead of being escaped.
7791
7792   */
7793 static int posix_to_vmsspec_hardway
7794   (char *vmspath, int vmspath_len,
7795    const char *unixpath,
7796    int dir_flag,
7797    int * utf8_fl) {
7798
7799 char *esa;
7800 const char *unixptr;
7801 const char *unixend;
7802 char *vmsptr;
7803 const char *lastslash;
7804 const char *lastdot;
7805 int unixlen;
7806 int vmslen;
7807 int dir_start;
7808 int dir_dot;
7809 int quoted;
7810 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7811 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7812
7813   if (utf8_fl != NULL)
7814     *utf8_fl = 0;
7815
7816   unixptr = unixpath;
7817   dir_dot = 0;
7818
7819   /* Ignore leading "/" characters */
7820   while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7821     unixptr++;
7822   }
7823   unixlen = strlen(unixptr);
7824
7825   /* Do nothing with blank paths */
7826   if (unixlen == 0) {
7827     vmspath[0] = '\0';
7828     return SS$_NORMAL;
7829   }
7830
7831   quoted = 0;
7832   /* This could have a "^UP^ on the front */
7833   if (strncmp(unixptr,"\"^UP^",5) == 0) {
7834     quoted = 1;
7835     unixptr+= 5;
7836     unixlen-= 5;
7837   }
7838
7839   lastslash = strrchr(unixptr,'/');
7840   lastdot = strrchr(unixptr,'.');
7841   unixend = strrchr(unixptr,'\"');
7842   if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7843     unixend = unixptr + unixlen;
7844   }
7845
7846   /* last dot is last dot or past end of string */
7847   if (lastdot == NULL)
7848     lastdot = unixptr + unixlen;
7849
7850   /* if no directories, set last slash to beginning of string */
7851   if (lastslash == NULL) {
7852     lastslash = unixptr;
7853   }
7854   else {
7855     /* Watch out for trailing "." after last slash, still a directory */
7856     if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7857       lastslash = unixptr + unixlen;
7858     }
7859
7860     /* Watch out for traiing ".." after last slash, still a directory */
7861     if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7862       lastslash = unixptr + unixlen;
7863     }
7864
7865     /* dots in directories are aways escaped */
7866     if (lastdot < lastslash)
7867       lastdot = unixptr + unixlen;
7868   }
7869
7870   /* if (unixptr < lastslash) then we are in a directory */
7871
7872   dir_start = 0;
7873
7874   vmsptr = vmspath;
7875   vmslen = 0;
7876
7877   /* Start with the UNIX path */
7878   if (*unixptr != '/') {
7879     /* relative paths */
7880
7881     /* If allowing logical names on relative pathnames, then handle here */
7882     if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7883         !decc_posix_compliant_pathnames) {
7884     char * nextslash;
7885     int seg_len;
7886     char * trn;
7887     int islnm;
7888
7889         /* Find the next slash */
7890         nextslash = strchr(unixptr,'/');
7891
7892         esa = PerlMem_malloc(vmspath_len);
7893         if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7894
7895         trn = PerlMem_malloc(VMS_MAXRSS);
7896         if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7897
7898         if (nextslash != NULL) {
7899
7900             seg_len = nextslash - unixptr;
7901             strncpy(esa, unixptr, seg_len);
7902             esa[seg_len] = 0;
7903         }
7904         else {
7905             strcpy(esa, unixptr);
7906             seg_len = strlen(unixptr);
7907         }
7908         /* trnlnm(section) */
7909         islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7910
7911         if (islnm) {
7912             /* Now fix up the directory */
7913
7914             /* Split up the path to find the components */
7915             sts = vms_split_path
7916                   (trn,
7917                    &v_spec,
7918                    &v_len,
7919                    &r_spec,
7920                    &r_len,
7921                    &d_spec,
7922                    &d_len,
7923                    &n_spec,
7924                    &n_len,
7925                    &e_spec,
7926                    &e_len,
7927                    &vs_spec,
7928                    &vs_len);
7929
7930             while (sts == 0) {
7931             char * strt;
7932             int cmp;
7933
7934                 /* A logical name must be a directory  or the full
7935                    specification.  It is only a full specification if
7936                    it is the only component */
7937                 if ((unixptr[seg_len] == '\0') ||
7938                     (unixptr[seg_len+1] == '\0')) {
7939
7940                     /* Is a directory being required? */
7941                     if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7942                         /* Not a logical name */
7943                         break;
7944                     }
7945
7946
7947                     if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7948                         /* This must be a directory */
7949                         if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7950                             strcpy(vmsptr, esa);
7951                             vmslen=strlen(vmsptr);
7952                             vmsptr[vmslen] = ':';
7953                             vmslen++;
7954                             vmsptr[vmslen] = '\0';
7955                             return SS$_NORMAL;
7956                         }
7957                     }
7958
7959                 }
7960
7961
7962                 /* must be dev/directory - ignore version */
7963                 if ((n_len + e_len) != 0)
7964                     break;
7965
7966                 /* transfer the volume */
7967                 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7968                     strncpy(vmsptr, v_spec, v_len);
7969                     vmsptr += v_len;
7970                     vmsptr[0] = '\0';
7971                     vmslen += v_len;
7972                 }
7973
7974                 /* unroot the rooted directory */
7975                 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7976                     r_spec[0] = '[';
7977                     r_spec[r_len - 1] = ']';
7978
7979                     /* This should not be there, but nothing is perfect */
7980                     if (r_len > 9) {
7981                         cmp = strcmp(&r_spec[1], "000000.");
7982                         if (cmp == 0) {
7983                             r_spec += 7;
7984                             r_spec[7] = '[';
7985                             r_len -= 7;
7986                             if (r_len == 2)
7987                                 r_len = 0;
7988                         }
7989                     }
7990                     if (r_len > 0) {
7991                         strncpy(vmsptr, r_spec, r_len);
7992                         vmsptr += r_len;
7993                         vmslen += r_len;
7994                         vmsptr[0] = '\0';
7995                     }
7996                 }
7997                 /* Bring over the directory. */
7998                 if ((d_len > 0) &&
7999                     ((d_len + vmslen) < vmspath_len)) {
8000                     d_spec[0] = '[';
8001                     d_spec[d_len - 1] = ']';
8002                     if (d_len > 9) {
8003                         cmp = strcmp(&d_spec[1], "000000.");
8004                         if (cmp == 0) {
8005                             d_spec += 7;
8006                             d_spec[7] = '[';
8007                             d_len -= 7;
8008                             if (d_len == 2)
8009                                 d_len = 0;
8010                         }
8011                     }
8012
8013                     if (r_len > 0) {
8014                         /* Remove the redundant root */
8015                         if (r_len > 0) {
8016                             /* remove the ][ */
8017                             vmsptr--;
8018                             vmslen--;
8019                             d_spec++;
8020                             d_len--;
8021                         }
8022                         strncpy(vmsptr, d_spec, d_len);
8023                             vmsptr += d_len;
8024                             vmslen += d_len;
8025                             vmsptr[0] = '\0';
8026                     }
8027                 }
8028                 break;
8029             }
8030         }
8031
8032         PerlMem_free(esa);
8033         PerlMem_free(trn);
8034     }
8035
8036     if (lastslash > unixptr) {
8037     int dotdir_seen;
8038
8039       /* skip leading ./ */
8040       dotdir_seen = 0;
8041       while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
8042         dotdir_seen = 1;
8043         unixptr++;
8044         unixptr++;
8045       }
8046
8047       /* Are we still in a directory? */
8048       if (unixptr <= lastslash) {
8049         *vmsptr++ = '[';
8050         vmslen = 1;
8051         dir_start = 1;
8052  
8053         /* if not backing up, then it is relative forward. */
8054         if (!((*unixptr == '.') && (unixptr[1] == '.') &&
8055               ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
8056           *vmsptr++ = '.';
8057           vmslen++;
8058           dir_dot = 1;
8059           }
8060        }
8061        else {
8062          if (dotdir_seen) {
8063            /* Perl wants an empty directory here to tell the difference
8064             * between a DCL commmand and a filename
8065             */
8066           *vmsptr++ = '[';
8067           *vmsptr++ = ']';
8068           vmslen = 2;
8069         }
8070       }
8071     }
8072     else {
8073       /* Handle two special files . and .. */
8074       if (unixptr[0] == '.') {
8075         if (&unixptr[1] == unixend) {
8076           *vmsptr++ = '[';
8077           *vmsptr++ = ']';
8078           vmslen += 2;
8079           *vmsptr++ = '\0';
8080           return SS$_NORMAL;
8081         }
8082         if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
8083           *vmsptr++ = '[';
8084           *vmsptr++ = '-';
8085           *vmsptr++ = ']';
8086           vmslen += 3;
8087           *vmsptr++ = '\0';
8088           return SS$_NORMAL;
8089         }
8090       }
8091     }
8092   }
8093   else {        /* Absolute PATH handling */
8094   int sts;
8095   char * nextslash;
8096   int seg_len;
8097     /* Need to find out where root is */
8098
8099     /* In theory, this procedure should never get an absolute POSIX pathname
8100      * that can not be found on the POSIX root.
8101      * In practice, that can not be relied on, and things will show up
8102      * here that are a VMS device name or concealed logical name instead.
8103      * So to make things work, this procedure must be tolerant.
8104      */
8105     esa = PerlMem_malloc(vmspath_len);
8106     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8107
8108     sts = SS$_NORMAL;
8109     nextslash = strchr(&unixptr[1],'/');
8110     seg_len = 0;
8111     if (nextslash != NULL) {
8112     int cmp;
8113       seg_len = nextslash - &unixptr[1];
8114       strncpy(vmspath, unixptr, seg_len + 1);
8115       vmspath[seg_len+1] = 0;
8116       cmp = 1;
8117       if (seg_len == 3) {
8118         cmp = strncmp(vmspath, "dev", 4);
8119         if (cmp == 0) {
8120             sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8121             if (sts = SS$_NORMAL)
8122                 return SS$_NORMAL;
8123         }
8124       }
8125       sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8126     }
8127
8128     if ($VMS_STATUS_SUCCESS(sts)) {
8129       /* This is verified to be a real path */
8130
8131       sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8132       if ($VMS_STATUS_SUCCESS(sts)) {
8133         strcpy(vmspath, esa);
8134         vmslen = strlen(vmspath);
8135         vmsptr = vmspath + vmslen;
8136         unixptr++;
8137         if (unixptr < lastslash) {
8138         char * rptr;
8139           vmsptr--;
8140           *vmsptr++ = '.';
8141           dir_start = 1;
8142           dir_dot = 1;
8143           if (vmslen > 7) {
8144           int cmp;
8145             rptr = vmsptr - 7;
8146             cmp = strcmp(rptr,"000000.");
8147             if (cmp == 0) {
8148               vmslen -= 7;
8149               vmsptr -= 7;
8150               vmsptr[1] = '\0';
8151             } /* removing 6 zeros */
8152           } /* vmslen < 7, no 6 zeros possible */
8153         } /* Not in a directory */
8154       } /* Posix root found */
8155       else {
8156         /* No posix root, fall back to default directory */
8157         strcpy(vmspath, "SYS$DISK:[");
8158         vmsptr = &vmspath[10];
8159         vmslen = 10;
8160         if (unixptr > lastslash) {
8161            *vmsptr = ']';
8162            vmsptr++;
8163            vmslen++;
8164         }
8165         else {
8166            dir_start = 1;
8167         }
8168       }
8169     } /* end of verified real path handling */
8170     else {
8171     int add_6zero;
8172     int islnm;
8173
8174       /* Ok, we have a device or a concealed root that is not in POSIX
8175        * or we have garbage.  Make the best of it.
8176        */
8177
8178       /* Posix to VMS destroyed this, so copy it again */
8179       strncpy(vmspath, &unixptr[1], seg_len);
8180       vmspath[seg_len] = 0;
8181       vmslen = seg_len;
8182       vmsptr = &vmsptr[vmslen];
8183       islnm = 0;
8184
8185       /* Now do we need to add the fake 6 zero directory to it? */
8186       add_6zero = 1;
8187       if ((*lastslash == '/') && (nextslash < lastslash)) {
8188         /* No there is another directory */
8189         add_6zero = 0;
8190       }
8191       else {
8192       int trnend;
8193       int cmp;
8194
8195         /* now we have foo:bar or foo:[000000]bar to decide from */
8196         islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8197
8198         if (!islnm && !decc_posix_compliant_pathnames) {
8199
8200             cmp = strncmp("bin", vmspath, 4);
8201             if (cmp == 0) {
8202                 /* bin => SYS$SYSTEM: */
8203                 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8204             }
8205             else {
8206                 /* tmp => SYS$SCRATCH: */
8207                 cmp = strncmp("tmp", vmspath, 4);
8208                 if (cmp == 0) {
8209                     islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8210                 }
8211             }
8212         }
8213
8214         trnend = islnm ? islnm - 1 : 0;
8215
8216         /* if this was a logical name, ']' or '>' must be present */
8217         /* if not a logical name, then assume a device and hope. */
8218         islnm =  trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8219
8220         /* if log name and trailing '.' then rooted - treat as device */
8221         add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8222
8223         /* Fix me, if not a logical name, a device lookup should be
8224          * done to see if the device is file structured.  If the device
8225          * is not file structured, the 6 zeros should not be put on.
8226          *
8227          * As it is, perl is occasionally looking for dev:[000000]tty.
8228          * which looks a little strange.
8229          *
8230          * Not that easy to detect as "/dev" may be file structured with
8231          * special device files.
8232          */
8233
8234         if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8235             (&nextslash[1] == unixend)) {
8236           /* No real directory present */
8237           add_6zero = 1;
8238         }
8239       }
8240
8241       /* Put the device delimiter on */
8242       *vmsptr++ = ':';
8243       vmslen++;
8244       unixptr = nextslash;
8245       unixptr++;
8246
8247       /* Start directory if needed */
8248       if (!islnm || add_6zero) {
8249         *vmsptr++ = '[';
8250         vmslen++;
8251         dir_start = 1;
8252       }
8253
8254       /* add fake 000000] if needed */
8255       if (add_6zero) {
8256         *vmsptr++ = '0';
8257         *vmsptr++ = '0';
8258         *vmsptr++ = '0';
8259         *vmsptr++ = '0';
8260         *vmsptr++ = '0';
8261         *vmsptr++ = '0';
8262         *vmsptr++ = ']';
8263         vmslen += 7;
8264         dir_start = 0;
8265       }
8266
8267     } /* non-POSIX translation */
8268     PerlMem_free(esa);
8269   } /* End of relative/absolute path handling */
8270
8271   while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8272   int dash_flag;
8273   int in_cnt;
8274   int out_cnt;
8275
8276     dash_flag = 0;
8277
8278     if (dir_start != 0) {
8279
8280       /* First characters in a directory are handled special */
8281       while ((*unixptr == '/') ||
8282              ((*unixptr == '.') &&
8283               ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8284                 (&unixptr[1]==unixend)))) {
8285       int loop_flag;
8286
8287         loop_flag = 0;
8288
8289         /* Skip redundant / in specification */
8290         while ((*unixptr == '/') && (dir_start != 0)) {
8291           loop_flag = 1;
8292           unixptr++;
8293           if (unixptr == lastslash)
8294             break;
8295         }
8296         if (unixptr == lastslash)
8297           break;
8298
8299         /* Skip redundant ./ characters */
8300         while ((*unixptr == '.') &&
8301                ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8302           loop_flag = 1;
8303           unixptr++;
8304           if (unixptr == lastslash)
8305             break;
8306           if (*unixptr == '/')
8307             unixptr++;
8308         }
8309         if (unixptr == lastslash)
8310           break;
8311
8312         /* Skip redundant ../ characters */
8313         while ((*unixptr == '.') && (unixptr[1] == '.') &&
8314              ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8315           /* Set the backing up flag */
8316           loop_flag = 1;
8317           dir_dot = 0;
8318           dash_flag = 1;
8319           *vmsptr++ = '-';
8320           vmslen++;
8321           unixptr++; /* first . */
8322           unixptr++; /* second . */
8323           if (unixptr == lastslash)
8324             break;
8325           if (*unixptr == '/') /* The slash */
8326             unixptr++;
8327         }
8328         if (unixptr == lastslash)
8329           break;
8330
8331         /* To do: Perl expects /.../ to be translated to [...] on VMS */
8332         /* Not needed when VMS is pretending to be UNIX. */
8333
8334         /* Is this loop stuck because of too many dots? */
8335         if (loop_flag == 0) {
8336           /* Exit the loop and pass the rest through */
8337           break;
8338         }
8339       }
8340
8341       /* Are we done with directories yet? */
8342       if (unixptr >= lastslash) {
8343
8344         /* Watch out for trailing dots */
8345         if (dir_dot != 0) {
8346             vmslen --;
8347             vmsptr--;
8348         }
8349         *vmsptr++ = ']';
8350         vmslen++;
8351         dash_flag = 0;
8352         dir_start = 0;
8353         if (*unixptr == '/')
8354           unixptr++;
8355       }
8356       else {
8357         /* Have we stopped backing up? */
8358         if (dash_flag) {
8359           *vmsptr++ = '.';
8360           vmslen++;
8361           dash_flag = 0;
8362           /* dir_start continues to be = 1 */
8363         }
8364         if (*unixptr == '-') {
8365           *vmsptr++ = '^';
8366           *vmsptr++ = *unixptr++;
8367           vmslen += 2;
8368           dir_start = 0;
8369
8370           /* Now are we done with directories yet? */
8371           if (unixptr >= lastslash) {
8372
8373             /* Watch out for trailing dots */
8374             if (dir_dot != 0) {
8375               vmslen --;
8376               vmsptr--;
8377             }
8378
8379             *vmsptr++ = ']';
8380             vmslen++;
8381             dash_flag = 0;
8382             dir_start = 0;
8383           }
8384         }
8385       }
8386     }
8387
8388     /* All done? */
8389     if (unixptr >= unixend)
8390       break;
8391
8392     /* Normal characters - More EFS work probably needed */
8393     dir_start = 0;
8394     dir_dot = 0;
8395
8396     switch(*unixptr) {
8397     case '/':
8398         /* remove multiple / */
8399         while (unixptr[1] == '/') {
8400            unixptr++;
8401         }
8402         if (unixptr == lastslash) {
8403           /* Watch out for trailing dots */
8404           if (dir_dot != 0) {
8405             vmslen --;
8406             vmsptr--;
8407           }
8408           *vmsptr++ = ']';
8409         }
8410         else {
8411           dir_start = 1;
8412           *vmsptr++ = '.';
8413           dir_dot = 1;
8414
8415           /* To do: Perl expects /.../ to be translated to [...] on VMS */
8416           /* Not needed when VMS is pretending to be UNIX. */
8417
8418         }
8419         dash_flag = 0;
8420         if (unixptr != unixend)
8421           unixptr++;
8422         vmslen++;
8423         break;
8424     case '.':
8425         if ((unixptr < lastdot) || (unixptr < lastslash) ||
8426             (&unixptr[1] == unixend)) {
8427           *vmsptr++ = '^';
8428           *vmsptr++ = '.';
8429           vmslen += 2;
8430           unixptr++;
8431
8432           /* trailing dot ==> '^..' on VMS */
8433           if (unixptr == unixend) {
8434             *vmsptr++ = '.';
8435             vmslen++;
8436             unixptr++;
8437           }
8438           break;
8439         }
8440
8441         *vmsptr++ = *unixptr++;
8442         vmslen ++;
8443         break;
8444     case '"':
8445         if (quoted && (&unixptr[1] == unixend)) {
8446             unixptr++;
8447             break;
8448         }
8449         in_cnt = copy_expand_unix_filename_escape
8450                 (vmsptr, unixptr, &out_cnt, utf8_fl);
8451         vmsptr += out_cnt;
8452         unixptr += in_cnt;
8453         break;
8454     case '~':
8455     case ';':
8456     case '\\':
8457     case '?':
8458     case ' ':
8459     default:
8460         in_cnt = copy_expand_unix_filename_escape
8461                 (vmsptr, unixptr, &out_cnt, utf8_fl);
8462         vmsptr += out_cnt;
8463         unixptr += in_cnt;
8464         break;
8465     }
8466   }
8467
8468   /* Make sure directory is closed */
8469   if (unixptr == lastslash) {
8470     char *vmsptr2;
8471     vmsptr2 = vmsptr - 1;
8472
8473     if (*vmsptr2 != ']') {
8474       *vmsptr2--;
8475
8476       /* directories do not end in a dot bracket */
8477       if (*vmsptr2 == '.') {
8478         vmsptr2--;
8479
8480         /* ^. is allowed */
8481         if (*vmsptr2 != '^') {
8482           vmsptr--; /* back up over the dot */
8483         }
8484       }
8485       *vmsptr++ = ']';
8486     }
8487   }
8488   else {
8489     char *vmsptr2;
8490     /* Add a trailing dot if a file with no extension */
8491     vmsptr2 = vmsptr - 1;
8492     if ((vmslen > 1) &&
8493         (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8494         (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8495         *vmsptr++ = '.';
8496         vmslen++;
8497     }
8498   }
8499
8500   *vmsptr = '\0';
8501   return SS$_NORMAL;
8502 }
8503 #endif
8504
8505  /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8506 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8507 {
8508 char * result;
8509 int utf8_flag;
8510
8511    /* If a UTF8 flag is being passed, honor it */
8512    utf8_flag = 0;
8513    if (utf8_fl != NULL) {
8514      utf8_flag = *utf8_fl;
8515     *utf8_fl = 0;
8516    }
8517
8518    if (utf8_flag) {
8519      /* If there is a possibility of UTF8, then if any UTF8 characters
8520         are present, then they must be converted to VTF-7
8521       */
8522      result = strcpy(rslt, path); /* FIX-ME */
8523    }
8524    else
8525      result = strcpy(rslt, path);
8526
8527    return result;
8528 }
8529
8530
8531
8532 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8533 static char *int_tovmsspec
8534    (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8535   char *dirend;
8536   char *lastdot;
8537   char *vms_delim;
8538   register char *cp1;
8539   const char *cp2;
8540   unsigned long int infront = 0, hasdir = 1;
8541   int rslt_len;
8542   int no_type_seen;
8543   char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8544   int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8545
8546   if (vms_debug_fileify) {
8547       if (path == NULL)
8548           fprintf(stderr, "int_tovmsspec: path = NULL\n");
8549       else
8550           fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8551   }
8552
8553   if (path == NULL) {
8554       /* If we fail, we should be setting errno */
8555       set_errno(EINVAL);
8556       set_vaxc_errno(SS$_BADPARAM);
8557       return NULL;
8558   }
8559   rslt_len = VMS_MAXRSS-1;
8560
8561   /* '.' and '..' are "[]" and "[-]" for a quick check */
8562   if (path[0] == '.') {
8563     if (path[1] == '\0') {
8564       strcpy(rslt,"[]");
8565       if (utf8_flag != NULL)
8566         *utf8_flag = 0;
8567       return rslt;
8568     }
8569     else {
8570       if (path[1] == '.' && path[2] == '\0') {
8571         strcpy(rslt,"[-]");
8572         if (utf8_flag != NULL)
8573            *utf8_flag = 0;
8574         return rslt;
8575       }
8576     }
8577   }
8578
8579    /* Posix specifications are now a native VMS format */
8580   /*--------------------------------------------------*/
8581 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8582   if (decc_posix_compliant_pathnames) {
8583     if (strncmp(path,"\"^UP^",5) == 0) {
8584       posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8585       return rslt;
8586     }
8587   }
8588 #endif
8589
8590   /* This is really the only way to see if this is already in VMS format */
8591   sts = vms_split_path
8592        (path,
8593         &v_spec,
8594         &v_len,
8595         &r_spec,
8596         &r_len,
8597         &d_spec,
8598         &d_len,
8599         &n_spec,
8600         &n_len,
8601         &e_spec,
8602         &e_len,
8603         &vs_spec,
8604         &vs_len);
8605   if (sts == 0) {
8606     /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8607        replacement, because the above parse just took care of most of
8608        what is needed to do vmspath when the specification is already
8609        in VMS format.
8610
8611        And if it is not already, it is easier to do the conversion as
8612        part of this routine than to call this routine and then work on
8613        the result.
8614      */
8615
8616     /* If VMS punctuation was found, it is already VMS format */
8617     if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8618       if (utf8_flag != NULL)
8619         *utf8_flag = 0;
8620       strcpy(rslt, path);
8621       if (vms_debug_fileify) {
8622           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8623       }
8624       return rslt;
8625     }
8626     /* Now, what to do with trailing "." cases where there is no
8627        extension?  If this is a UNIX specification, and EFS characters
8628        are enabled, then the trailing "." should be converted to a "^.".
8629        But if this was already a VMS specification, then it should be
8630        left alone.
8631
8632        So in the case of ambiguity, leave the specification alone.
8633      */
8634
8635
8636     /* If there is a possibility of UTF8, then if any UTF8 characters
8637         are present, then they must be converted to VTF-7
8638      */
8639     if (utf8_flag != NULL)
8640       *utf8_flag = 0;
8641     strcpy(rslt, path);
8642     if (vms_debug_fileify) {
8643         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8644     }
8645     return rslt;
8646   }
8647
8648   dirend = strrchr(path,'/');
8649
8650   if (dirend == NULL) {
8651      char *macro_start;
8652      int has_macro;
8653
8654      /* If we get here with no UNIX directory delimiters, then this is
8655         not a complete file specification, either garbage a UNIX glob
8656         specification that can not be converted to a VMS wildcard, or
8657         it a UNIX shell macro.  MakeMaker wants shell macros passed
8658         through AS-IS,
8659
8660         utf8 flag setting needs to be preserved.
8661       */
8662       hasdir = 0;
8663
8664       has_macro = 0;
8665       macro_start = strchr(path,'$');
8666       if (macro_start != NULL) {
8667           if (macro_start[1] == '(') {
8668               has_macro = 1;
8669           }
8670       }
8671       if ((decc_efs_charset == 0) || (has_macro)) {
8672           strcpy(rslt, path);
8673           if (vms_debug_fileify) {
8674               fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8675           }
8676           return rslt;
8677       }
8678   }
8679
8680 /* If EFS charset mode active, handle the conversion */
8681 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8682   if (decc_efs_charset) {
8683     posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8684     if (vms_debug_fileify) {
8685         fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8686     }
8687     return rslt;
8688   }
8689 #endif
8690
8691   if (*(dirend+1) == '.') {  /* do we have trailing "/." or "/.." or "/..."? */
8692     if (!*(dirend+2)) dirend +=2;
8693     if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8694     if (decc_efs_charset == 0) {
8695       if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8696     }
8697   }
8698
8699   cp1 = rslt;
8700   cp2 = path;
8701   lastdot = strrchr(cp2,'.');
8702   if (*cp2 == '/') {
8703     char *trndev;
8704     int islnm, rooted;
8705     STRLEN trnend;
8706
8707     while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8708     if (!*(cp2+1)) {
8709       if (decc_disable_posix_root) {
8710         strcpy(rslt,"sys$disk:[000000]");
8711       }
8712       else {
8713         strcpy(rslt,"sys$posix_root:[000000]");
8714       }
8715       if (utf8_flag != NULL)
8716         *utf8_flag = 0;
8717       if (vms_debug_fileify) {
8718           fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8719       }
8720       return rslt;
8721     }
8722     while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8723     *cp1 = '\0';
8724     trndev = PerlMem_malloc(VMS_MAXRSS);
8725     if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8726     islnm =  simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8727
8728      /* DECC special handling */
8729     if (!islnm) {
8730       if (strcmp(rslt,"bin") == 0) {
8731         strcpy(rslt,"sys$system");
8732         cp1 = rslt + 10;
8733         *cp1 = 0;
8734         islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8735       }
8736       else if (strcmp(rslt,"tmp") == 0) {
8737         strcpy(rslt,"sys$scratch");
8738         cp1 = rslt + 11;
8739         *cp1 = 0;
8740         islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8741       }
8742       else if (!decc_disable_posix_root) {
8743         strcpy(rslt, "sys$posix_root");
8744         cp1 = rslt + 14;
8745         *cp1 = 0;
8746         cp2 = path;
8747         while (*(cp2+1) == '/') cp2++;  /* Skip multiple /s */
8748         islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8749       }
8750       else if (strcmp(rslt,"dev") == 0) {
8751         if (strncmp(cp2,"/null", 5) == 0) {
8752           if ((cp2[5] == 0) || (cp2[5] == '/')) {
8753             strcpy(rslt,"NLA0");
8754             cp1 = rslt + 4;
8755             *cp1 = 0;
8756             cp2 = cp2 + 5;
8757             islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8758           }
8759         }
8760       }
8761     }
8762
8763     trnend = islnm ? strlen(trndev) - 1 : 0;
8764     islnm =  trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8765     rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8766     /* If the first element of the path is a logical name, determine
8767      * whether it has to be translated so we can add more directories. */
8768     if (!islnm || rooted) {
8769       *(cp1++) = ':';
8770       *(cp1++) = '[';
8771       if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8772       else cp2++;
8773     }
8774     else {
8775       if (cp2 != dirend) {
8776         strcpy(rslt,trndev);
8777         cp1 = rslt + trnend;
8778         if (*cp2 != 0) {
8779           *(cp1++) = '.';
8780           cp2++;
8781         }
8782       }
8783       else {
8784         if (decc_disable_posix_root) {
8785           *(cp1++) = ':';
8786           hasdir = 0;
8787         }
8788       }
8789     }
8790     PerlMem_free(trndev);
8791   }
8792   else {
8793     *(cp1++) = '[';
8794     if (*cp2 == '.') {
8795       if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8796         cp2 += 2;         /* skip over "./" - it's redundant */
8797         *(cp1++) = '.';   /* but it does indicate a relative dirspec */
8798       }
8799       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8800         *(cp1++) = '-';                                 /* "../" --> "-" */
8801         cp2 += 3;
8802       }
8803       else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8804                (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8805         *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8806         if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8807         cp2 += 4;
8808       }
8809       else if ((cp2 != lastdot) || (lastdot < dirend)) {
8810         /* Escape the extra dots in EFS file specifications */
8811         *(cp1++) = '^';
8812       }
8813       if (cp2 > dirend) cp2 = dirend;
8814     }
8815     else *(cp1++) = '.';
8816   }
8817   for (; cp2 < dirend; cp2++) {
8818     if (*cp2 == '/') {
8819       if (*(cp2-1) == '/') continue;
8820       if (*(cp1-1) != '.') *(cp1++) = '.';
8821       infront = 0;
8822     }
8823     else if (!infront && *cp2 == '.') {
8824       if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8825       else if (*(cp2+1) == '/') cp2++;   /* skip over "./" - it's redundant */
8826       else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8827         if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8828         else if (*(cp1-2) == '[') *(cp1-1) = '-';
8829         else {  /* back up over previous directory name */
8830           cp1--;
8831           while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8832           if (*(cp1-1) == '[') {
8833             memcpy(cp1,"000000.",7);
8834             cp1 += 7;
8835           }
8836         }
8837         cp2 += 2;
8838         if (cp2 == dirend) break;
8839       }
8840       else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8841                 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8842         if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8843         *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8844         if (!*(cp2+3)) { 
8845           *(cp1++) = '.';  /* Simulate trailing '/' */
8846           cp2 += 2;  /* for loop will incr this to == dirend */
8847         }
8848         else cp2 += 3;  /* Trailing '/' was there, so skip it, too */
8849       }
8850       else {
8851         if (decc_efs_charset == 0)
8852           *(cp1++) = '_';  /* fix up syntax - '.' in name not allowed */
8853         else {
8854           *(cp1++) = '^';  /* fix up syntax - '.' in name is allowed */
8855           *(cp1++) = '.';
8856         }
8857       }
8858     }
8859     else {
8860       if (!infront && *(cp1-1) == '-')  *(cp1++) = '.';
8861       if (*cp2 == '.') {
8862         if (decc_efs_charset == 0)
8863           *(cp1++) = '_';
8864         else {
8865           *(cp1++) = '^';
8866           *(cp1++) = '.';
8867         }
8868       }
8869       else                  *(cp1++) =  *cp2;
8870       infront = 1;
8871     }
8872   }
8873   if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8874   if (hasdir) *(cp1++) = ']';
8875   if (*cp2) cp2++;  /* check in case we ended with trailing '..' */
8876   /* fixme for ODS5 */
8877   no_type_seen = 0;
8878   if (cp2 > lastdot)
8879     no_type_seen = 1;
8880   while (*cp2) {
8881     switch(*cp2) {
8882     case '?':
8883         if (decc_efs_charset == 0)
8884           *(cp1++) = '%';
8885         else
8886           *(cp1++) = '?';
8887         cp2++;
8888     case ' ':
8889         *(cp1)++ = '^';
8890         *(cp1)++ = '_';
8891         cp2++;
8892         break;
8893     case '.':
8894         if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8895             decc_readdir_dropdotnotype) {
8896           *(cp1)++ = '^';
8897           *(cp1)++ = '.';
8898           cp2++;
8899
8900           /* trailing dot ==> '^..' on VMS */
8901           if (*cp2 == '\0') {
8902             *(cp1++) = '.';
8903             no_type_seen = 0;
8904           }
8905         }
8906         else {
8907           *(cp1++) = *(cp2++);
8908           no_type_seen = 0;
8909         }
8910         break;
8911     case '$':
8912          /* This could be a macro to be passed through */
8913         *(cp1++) = *(cp2++);
8914         if (*cp2 == '(') {
8915         const char * save_cp2;
8916         char * save_cp1;
8917         int is_macro;
8918
8919             /* paranoid check */
8920             save_cp2 = cp2;
8921             save_cp1 = cp1;
8922             is_macro = 0;
8923
8924             /* Test through */
8925             *(cp1++) = *(cp2++);
8926             if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8927                 *(cp1++) = *(cp2++);
8928                 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8929                     *(cp1++) = *(cp2++);
8930                 }
8931                 if (*cp2 == ')') {
8932                     *(cp1++) = *(cp2++);
8933                     is_macro = 1;
8934                 }
8935             }
8936             if (is_macro == 0) {
8937                 /* Not really a macro - never mind */
8938                 cp2 = save_cp2;
8939                 cp1 = save_cp1;
8940             }
8941         }
8942         break;
8943     case '\"':
8944     case '~':
8945     case '`':
8946     case '!':
8947     case '#':
8948     case '%':
8949     case '^':
8950         /* Don't escape again if following character is 
8951          * already something we escape.
8952          */
8953         if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8954             *(cp1++) = *(cp2++);
8955             break;
8956         }
8957         /* But otherwise fall through and escape it. */
8958     case '&':
8959     case '(':
8960     case ')':
8961     case '=':
8962     case '+':
8963     case '\'':
8964     case '@':
8965     case '[':
8966     case ']':
8967     case '{':
8968     case '}':
8969     case ':':
8970     case '\\':
8971     case '|':
8972     case '<':
8973     case '>':
8974         *(cp1++) = '^';
8975         *(cp1++) = *(cp2++);
8976         break;
8977     case ';':
8978         /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8979          * which is wrong.  UNIX notation should be ".dir." unless
8980          * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8981          * changing this behavior could break more things at this time.
8982          * efs character set effectively does not allow "." to be a version
8983          * delimiter as a further complication about changing this.
8984          */
8985         if (decc_filename_unix_report != 0) {
8986           *(cp1++) = '^';
8987         }
8988         *(cp1++) = *(cp2++);
8989         break;
8990     default:
8991         *(cp1++) = *(cp2++);
8992     }
8993   }
8994   if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8995   char *lcp1;
8996     lcp1 = cp1;
8997     lcp1--;
8998      /* Fix me for "^]", but that requires making sure that you do
8999       * not back up past the start of the filename
9000       */
9001     if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
9002       *cp1++ = '.';
9003   }
9004   *cp1 = '\0';
9005
9006   if (utf8_flag != NULL)
9007     *utf8_flag = 0;
9008   if (vms_debug_fileify) {
9009       fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
9010   }
9011   return rslt;
9012
9013 }  /* end of int_tovmsspec() */
9014
9015
9016 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
9017 static char *mp_do_tovmsspec
9018    (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
9019   static char __tovmsspec_retbuf[VMS_MAXRSS];
9020     char * vmsspec, *ret_spec, *ret_buf;
9021
9022     vmsspec = NULL;
9023     ret_buf = buf;
9024     if (ret_buf == NULL) {
9025         if (ts) {
9026             Newx(vmsspec, VMS_MAXRSS, char);
9027             if (vmsspec == NULL)
9028                 _ckvmssts(SS$_INSFMEM);
9029             ret_buf = vmsspec;
9030         } else {
9031             ret_buf = __tovmsspec_retbuf;
9032         }
9033     }
9034
9035     ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
9036
9037     if (ret_spec == NULL) {
9038        /* Cleanup on isle 5, if this is thread specific we need to deallocate */
9039        if (vmsspec)
9040            Safefree(vmsspec);
9041     }
9042
9043     return ret_spec;
9044
9045 }  /* end of mp_do_tovmsspec() */
9046 /*}}}*/
9047 /* External entry points */
9048 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
9049   { return do_tovmsspec(path,buf,0,NULL); }
9050 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
9051   { return do_tovmsspec(path,buf,1,NULL); }
9052 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9053   { return do_tovmsspec(path,buf,0,utf8_fl); }
9054 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9055   { return do_tovmsspec(path,buf,1,utf8_fl); }
9056
9057 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
9058 /* Internal routine for use with out an explict context present */
9059 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
9060
9061     char * ret_spec, *pathified;
9062
9063     if (path == NULL)
9064         return NULL;
9065
9066     pathified = PerlMem_malloc(VMS_MAXRSS);
9067     if (pathified == NULL)
9068         _ckvmssts_noperl(SS$_INSFMEM);
9069
9070     ret_spec = int_pathify_dirspec(path, pathified);
9071
9072     if (ret_spec == NULL) {
9073         PerlMem_free(pathified);
9074         return NULL;
9075     }
9076
9077     ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
9078     
9079     PerlMem_free(pathified);
9080     return ret_spec;
9081
9082 }
9083
9084 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9085 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9086   static char __tovmspath_retbuf[VMS_MAXRSS];
9087   int vmslen;
9088   char *pathified, *vmsified, *cp;
9089
9090   if (path == NULL) return NULL;
9091   pathified = PerlMem_malloc(VMS_MAXRSS);
9092   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9093   if (int_pathify_dirspec(path, pathified) == NULL) {
9094     PerlMem_free(pathified);
9095     return NULL;
9096   }
9097
9098   vmsified = NULL;
9099   if (buf == NULL)
9100      Newx(vmsified, VMS_MAXRSS, char);
9101   if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
9102     PerlMem_free(pathified);
9103     if (vmsified) Safefree(vmsified);
9104     return NULL;
9105   }
9106   PerlMem_free(pathified);
9107   if (buf) {
9108     return buf;
9109   }
9110   else if (ts) {
9111     vmslen = strlen(vmsified);
9112     Newx(cp,vmslen+1,char);
9113     memcpy(cp,vmsified,vmslen);
9114     cp[vmslen] = '\0';
9115     Safefree(vmsified);
9116     return cp;
9117   }
9118   else {
9119     strcpy(__tovmspath_retbuf,vmsified);
9120     Safefree(vmsified);
9121     return __tovmspath_retbuf;
9122   }
9123
9124 }  /* end of do_tovmspath() */
9125 /*}}}*/
9126 /* External entry points */
9127 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9128   { return do_tovmspath(path,buf,0, NULL); }
9129 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9130   { return do_tovmspath(path,buf,1, NULL); }
9131 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl) 
9132   { return do_tovmspath(path,buf,0,utf8_fl); }
9133 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9134   { return do_tovmspath(path,buf,1,utf8_fl); }
9135
9136
9137 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9138 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9139   static char __tounixpath_retbuf[VMS_MAXRSS];
9140   int unixlen;
9141   char *pathified, *unixified, *cp;
9142
9143   if (path == NULL) return NULL;
9144   pathified = PerlMem_malloc(VMS_MAXRSS);
9145   if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9146   if (int_pathify_dirspec(path, pathified) == NULL) {
9147     PerlMem_free(pathified);
9148     return NULL;
9149   }
9150
9151   unixified = NULL;
9152   if (buf == NULL) {
9153       Newx(unixified, VMS_MAXRSS, char);
9154   }
9155   if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9156     PerlMem_free(pathified);
9157     if (unixified) Safefree(unixified);
9158     return NULL;
9159   }
9160   PerlMem_free(pathified);
9161   if (buf) {
9162     return buf;
9163   }
9164   else if (ts) {
9165     unixlen = strlen(unixified);
9166     Newx(cp,unixlen+1,char);
9167     memcpy(cp,unixified,unixlen);
9168     cp[unixlen] = '\0';
9169     Safefree(unixified);
9170     return cp;
9171   }
9172   else {
9173     strcpy(__tounixpath_retbuf,unixified);
9174     Safefree(unixified);
9175     return __tounixpath_retbuf;
9176   }
9177
9178 }  /* end of do_tounixpath() */
9179 /*}}}*/
9180 /* External entry points */
9181 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9182   { return do_tounixpath(path,buf,0,NULL); }
9183 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9184   { return do_tounixpath(path,buf,1,NULL); }
9185 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9186   { return do_tounixpath(path,buf,0,utf8_fl); }
9187 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9188   { return do_tounixpath(path,buf,1,utf8_fl); }
9189
9190 /*
9191  * @(#)argproc.c 2.2 94/08/16   Mark Pizzolato (mark AT infocomm DOT com)
9192  *
9193  *****************************************************************************
9194  *                                                                           *
9195  *  Copyright (C) 1989-1994, 2007 by                                         *
9196  *  Mark Pizzolato - INFO COMM, Danville, California  (510) 837-5600         *
9197  *                                                                           *
9198  *  Permission is hereby granted for the reproduction of this software       *
9199  *  on condition that this copyright notice is included in source            *
9200  *  distributions of the software.  The code may be modified and             *
9201  *  distributed under the same terms as Perl itself.                         *
9202  *                                                                           *
9203  *  27-Aug-1994 Modified for inclusion in perl5                              *
9204  *              by Charles Bailey  (bailey AT newman DOT upenn DOT edu)      *
9205  *****************************************************************************
9206  */
9207
9208 /*
9209  * getredirection() is intended to aid in porting C programs
9210  * to VMS (Vax-11 C).  The native VMS environment does not support 
9211  * '>' and '<' I/O redirection, or command line wild card expansion, 
9212  * or a command line pipe mechanism using the '|' AND background 
9213  * command execution '&'.  All of these capabilities are provided to any
9214  * C program which calls this procedure as the first thing in the 
9215  * main program.
9216  * The piping mechanism will probably work with almost any 'filter' type
9217  * of program.  With suitable modification, it may useful for other
9218  * portability problems as well.
9219  *
9220  * Author:  Mark Pizzolato      (mark AT infocomm DOT com)
9221  */
9222 struct list_item
9223     {
9224     struct list_item *next;
9225     char *value;
9226     };
9227
9228 static void add_item(struct list_item **head,
9229                      struct list_item **tail,
9230                      char *value,
9231                      int *count);
9232
9233 static void mp_expand_wild_cards(pTHX_ char *item,
9234                                 struct list_item **head,
9235                                 struct list_item **tail,
9236                                 int *count);
9237
9238 static int background_process(pTHX_ int argc, char **argv);
9239
9240 static void pipe_and_fork(pTHX_ char **cmargv);
9241
9242 /*{{{ void getredirection(int *ac, char ***av)*/
9243 static void
9244 mp_getredirection(pTHX_ int *ac, char ***av)
9245 /*
9246  * Process vms redirection arg's.  Exit if any error is seen.
9247  * If getredirection() processes an argument, it is erased
9248  * from the vector.  getredirection() returns a new argc and argv value.
9249  * In the event that a background command is requested (by a trailing "&"),
9250  * this routine creates a background subprocess, and simply exits the program.
9251  *
9252  * Warning: do not try to simplify the code for vms.  The code
9253  * presupposes that getredirection() is called before any data is
9254  * read from stdin or written to stdout.
9255  *
9256  * Normal usage is as follows:
9257  *
9258  *      main(argc, argv)
9259  *      int             argc;
9260  *      char            *argv[];
9261  *      {
9262  *              getredirection(&argc, &argv);
9263  *      }
9264  */
9265 {
9266     int                 argc = *ac;     /* Argument Count         */
9267     char                **argv = *av;   /* Argument Vector        */
9268     char                *ap;            /* Argument pointer       */
9269     int                 j;              /* argv[] index           */
9270     int                 item_count = 0; /* Count of Items in List */
9271     struct list_item    *list_head = 0; /* First Item in List       */
9272     struct list_item    *list_tail;     /* Last Item in List        */
9273     char                *in = NULL;     /* Input File Name          */
9274     char                *out = NULL;    /* Output File Name         */
9275     char                *outmode = "w"; /* Mode to Open Output File */
9276     char                *err = NULL;    /* Error File Name          */
9277     char                *errmode = "w"; /* Mode to Open Error File  */
9278     int                 cmargc = 0;     /* Piped Command Arg Count  */
9279     char                **cmargv = NULL;/* Piped Command Arg Vector */
9280
9281     /*
9282      * First handle the case where the last thing on the line ends with
9283      * a '&'.  This indicates the desire for the command to be run in a
9284      * subprocess, so we satisfy that desire.
9285      */
9286     ap = argv[argc-1];
9287     if (0 == strcmp("&", ap))
9288        exit(background_process(aTHX_ --argc, argv));
9289     if (*ap && '&' == ap[strlen(ap)-1])
9290         {
9291         ap[strlen(ap)-1] = '\0';
9292        exit(background_process(aTHX_ argc, argv));
9293         }
9294     /*
9295      * Now we handle the general redirection cases that involve '>', '>>',
9296      * '<', and pipes '|'.
9297      */
9298     for (j = 0; j < argc; ++j)
9299         {
9300         if (0 == strcmp("<", argv[j]))
9301             {
9302             if (j+1 >= argc)
9303                 {
9304                 fprintf(stderr,"No input file after < on command line");
9305                 exit(LIB$_WRONUMARG);
9306                 }
9307             in = argv[++j];
9308             continue;
9309             }
9310         if ('<' == *(ap = argv[j]))
9311             {
9312             in = 1 + ap;
9313             continue;
9314             }
9315         if (0 == strcmp(">", ap))
9316             {
9317             if (j+1 >= argc)
9318                 {
9319                 fprintf(stderr,"No output file after > on command line");
9320                 exit(LIB$_WRONUMARG);
9321                 }
9322             out = argv[++j];
9323             continue;
9324             }
9325         if ('>' == *ap)
9326             {
9327             if ('>' == ap[1])
9328                 {
9329                 outmode = "a";
9330                 if ('\0' == ap[2])
9331                     out = argv[++j];
9332                 else
9333                     out = 2 + ap;
9334                 }
9335             else
9336                 out = 1 + ap;
9337             if (j >= argc)
9338                 {
9339                 fprintf(stderr,"No output file after > or >> on command line");
9340                 exit(LIB$_WRONUMARG);
9341                 }
9342             continue;
9343             }
9344         if (('2' == *ap) && ('>' == ap[1]))
9345             {
9346             if ('>' == ap[2])
9347                 {
9348                 errmode = "a";
9349                 if ('\0' == ap[3])
9350                     err = argv[++j];
9351                 else
9352                     err = 3 + ap;
9353                 }
9354             else
9355                 if ('\0' == ap[2])
9356                     err = argv[++j];
9357                 else
9358                     err = 2 + ap;
9359             if (j >= argc)
9360                 {
9361                 fprintf(stderr,"No output file after 2> or 2>> on command line");
9362                 exit(LIB$_WRONUMARG);
9363                 }
9364             continue;
9365             }
9366         if (0 == strcmp("|", argv[j]))
9367             {
9368             if (j+1 >= argc)
9369                 {
9370                 fprintf(stderr,"No command into which to pipe on command line");
9371                 exit(LIB$_WRONUMARG);
9372                 }
9373             cmargc = argc-(j+1);
9374             cmargv = &argv[j+1];
9375             argc = j;
9376             continue;
9377             }
9378         if ('|' == *(ap = argv[j]))
9379             {
9380             ++argv[j];
9381             cmargc = argc-j;
9382             cmargv = &argv[j];
9383             argc = j;
9384             continue;
9385             }
9386         expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9387         }
9388     /*
9389      * Allocate and fill in the new argument vector, Some Unix's terminate
9390      * the list with an extra null pointer.
9391      */
9392     argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9393     if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9394     *av = argv;
9395     for (j = 0; j < item_count; ++j, list_head = list_head->next)
9396         argv[j] = list_head->value;
9397     *ac = item_count;
9398     if (cmargv != NULL)
9399         {
9400         if (out != NULL)
9401             {
9402             fprintf(stderr,"'|' and '>' may not both be specified on command line");
9403             exit(LIB$_INVARGORD);
9404             }
9405         pipe_and_fork(aTHX_ cmargv);
9406         }
9407         
9408     /* Check for input from a pipe (mailbox) */
9409
9410     if (in == NULL && 1 == isapipe(0))
9411         {
9412         char mbxname[L_tmpnam];
9413         long int bufsize;
9414         long int dvi_item = DVI$_DEVBUFSIZ;
9415         $DESCRIPTOR(mbxnam, "");
9416         $DESCRIPTOR(mbxdevnam, "");
9417
9418         /* Input from a pipe, reopen it in binary mode to disable       */
9419         /* carriage control processing.                                 */
9420
9421         fgetname(stdin, mbxname, 1);
9422         mbxnam.dsc$a_pointer = mbxname;
9423         mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);     
9424         lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9425         mbxdevnam.dsc$a_pointer = mbxname;
9426         mbxdevnam.dsc$w_length = sizeof(mbxname);
9427         dvi_item = DVI$_DEVNAM;
9428         lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9429         mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9430         set_errno(0);
9431         set_vaxc_errno(1);
9432         freopen(mbxname, "rb", stdin);
9433         if (errno != 0)
9434             {
9435             fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9436             exit(vaxc$errno);
9437             }
9438         }
9439     if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9440         {
9441         fprintf(stderr,"Can't open input file %s as stdin",in);
9442         exit(vaxc$errno);
9443         }
9444     if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9445         {       
9446         fprintf(stderr,"Can't open output file %s as stdout",out);
9447         exit(vaxc$errno);
9448         }
9449         if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9450
9451     if (err != NULL) {
9452         if (strcmp(err,"&1") == 0) {
9453             dup2(fileno(stdout), fileno(stderr));
9454             Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9455         } else {
9456         FILE *tmperr;
9457         if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9458             {
9459             fprintf(stderr,"Can't open error file %s as stderr",err);
9460             exit(vaxc$errno);
9461             }
9462             fclose(tmperr);
9463            if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9464                 {
9465                 exit(vaxc$errno);
9466                 }
9467             Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9468         }
9469         }
9470 #ifdef ARGPROC_DEBUG
9471     PerlIO_printf(Perl_debug_log, "Arglist:\n");
9472     for (j = 0; j < *ac;  ++j)
9473         PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9474 #endif
9475    /* Clear errors we may have hit expanding wildcards, so they don't
9476       show up in Perl's $! later */
9477    set_errno(0); set_vaxc_errno(1);
9478 }  /* end of getredirection() */
9479 /*}}}*/
9480
9481 static void add_item(struct list_item **head,
9482                      struct list_item **tail,
9483                      char *value,
9484                      int *count)
9485 {
9486     if (*head == 0)
9487         {
9488         *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9489         if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9490         *tail = *head;
9491         }
9492     else {
9493         (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9494         if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9495         *tail = (*tail)->next;
9496         }
9497     (*tail)->value = value;
9498     ++(*count);
9499 }
9500
9501 static void mp_expand_wild_cards(pTHX_ char *item,
9502                               struct list_item **head,
9503                               struct list_item **tail,
9504                               int *count)
9505 {
9506 int expcount = 0;
9507 unsigned long int context = 0;
9508 int isunix = 0;
9509 int item_len = 0;
9510 char *had_version;
9511 char *had_device;
9512 int had_directory;
9513 char *devdir,*cp;
9514 char *vmsspec;
9515 $DESCRIPTOR(filespec, "");
9516 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9517 $DESCRIPTOR(resultspec, "");
9518 unsigned long int lff_flags = 0;
9519 int sts;
9520 int rms_sts;
9521
9522 #ifdef VMS_LONGNAME_SUPPORT
9523     lff_flags = LIB$M_FIL_LONG_NAMES;
9524 #endif
9525
9526     for (cp = item; *cp; cp++) {
9527         if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9528         if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9529     }
9530     if (!*cp || isspace(*cp))
9531         {
9532         add_item(head, tail, item, count);
9533         return;
9534         }
9535     else
9536         {
9537      /* "double quoted" wild card expressions pass as is */
9538      /* From DCL that means using e.g.:                  */
9539      /* perl program """perl.*"""                        */
9540      item_len = strlen(item);
9541      if ( '"' == *item && '"' == item[item_len-1] )
9542        {
9543        item++;
9544        item[item_len-2] = '\0';
9545        add_item(head, tail, item, count);
9546        return;
9547        }
9548      }
9549     resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9550     resultspec.dsc$b_class = DSC$K_CLASS_D;
9551     resultspec.dsc$a_pointer = NULL;
9552     vmsspec = PerlMem_malloc(VMS_MAXRSS);
9553     if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9554     if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9555       filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9556     if (!isunix || !filespec.dsc$a_pointer)
9557       filespec.dsc$a_pointer = item;
9558     filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9559     /*
9560      * Only return version specs, if the caller specified a version
9561      */
9562     had_version = strchr(item, ';');
9563     /*
9564      * Only return device and directory specs, if the caller specifed either.
9565      */
9566     had_device = strchr(item, ':');
9567     had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9568     
9569     while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9570                                  (&filespec, &resultspec, &context,
9571                                   &defaultspec, 0, &rms_sts, &lff_flags)))
9572         {
9573         char *string;
9574         char *c;
9575
9576         string = PerlMem_malloc(resultspec.dsc$w_length+1);
9577         if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9578         strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9579         string[resultspec.dsc$w_length] = '\0';
9580         if (NULL == had_version)
9581             *(strrchr(string, ';')) = '\0';
9582         if ((!had_directory) && (had_device == NULL))
9583             {
9584             if (NULL == (devdir = strrchr(string, ']')))
9585                 devdir = strrchr(string, '>');
9586             strcpy(string, devdir + 1);
9587             }
9588         /*
9589          * Be consistent with what the C RTL has already done to the rest of
9590          * the argv items and lowercase all of these names.
9591          */
9592         if (!decc_efs_case_preserve) {
9593             for (c = string; *c; ++c)
9594             if (isupper(*c))
9595                 *c = tolower(*c);
9596         }
9597         if (isunix) trim_unixpath(string,item,1);
9598         add_item(head, tail, string, count);
9599         ++expcount;
9600     }
9601     PerlMem_free(vmsspec);
9602     if (sts != RMS$_NMF)
9603         {
9604         set_vaxc_errno(sts);
9605         switch (sts)
9606             {
9607             case RMS$_FNF: case RMS$_DNF:
9608                 set_errno(ENOENT); break;
9609             case RMS$_DIR:
9610                 set_errno(ENOTDIR); break;
9611             case RMS$_DEV:
9612                 set_errno(ENODEV); break;
9613             case RMS$_FNM: case RMS$_SYN:
9614                 set_errno(EINVAL); break;
9615             case RMS$_PRV:
9616                 set_errno(EACCES); break;
9617             default:
9618                 _ckvmssts_noperl(sts);
9619             }
9620         }
9621     if (expcount == 0)
9622         add_item(head, tail, item, count);
9623     _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9624     _ckvmssts_noperl(lib$find_file_end(&context));
9625 }
9626
9627 static int child_st[2];/* Event Flag set when child process completes   */
9628
9629 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox                */
9630
9631 static unsigned long int exit_handler(int *status)
9632 {
9633 short iosb[4];
9634
9635     if (0 == child_st[0])
9636         {
9637 #ifdef ARGPROC_DEBUG
9638         PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9639 #endif
9640         fflush(stdout);     /* Have to flush pipe for binary data to    */
9641                             /* terminate properly -- <tp@mccall.com>    */
9642         sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9643         sys$dassgn(child_chan);
9644         fclose(stdout);
9645         sys$synch(0, child_st);
9646         }
9647     return(1);
9648 }
9649
9650 static void sig_child(int chan)
9651 {
9652 #ifdef ARGPROC_DEBUG
9653     PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9654 #endif
9655     if (child_st[0] == 0)
9656         child_st[0] = 1;
9657 }
9658
9659 static struct exit_control_block exit_block =
9660     {
9661     0,
9662     exit_handler,
9663     1,
9664     &exit_block.exit_status,
9665     0
9666     };
9667
9668 static void 
9669 pipe_and_fork(pTHX_ char **cmargv)
9670 {
9671     PerlIO *fp;
9672     struct dsc$descriptor_s *vmscmd;
9673     char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9674     int sts, j, l, ismcr, quote, tquote = 0;
9675
9676     sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9677     vms_execfree(vmscmd);
9678
9679     j = l = 0;
9680     p = subcmd;
9681     q = cmargv[0];
9682     ismcr = q && toupper(*q) == 'M'     && toupper(*(q+1)) == 'C' 
9683               && toupper(*(q+2)) == 'R' && !*(q+3);
9684
9685     while (q && l < MAX_DCL_LINE_LENGTH) {
9686         if (!*q) {
9687             if (j > 0 && quote) {
9688                 *p++ = '"';
9689                 l++;
9690             }
9691             q = cmargv[++j];
9692             if (q) {
9693                 if (ismcr && j > 1) quote = 1;
9694                 tquote =  (strchr(q,' ')) != NULL || *q == '\0';
9695                 *p++ = ' ';
9696                 l++;
9697                 if (quote || tquote) {
9698                     *p++ = '"';
9699                     l++;
9700                 }
9701             }
9702         } else {
9703             if ((quote||tquote) && *q == '"') {
9704                 *p++ = '"';
9705                 l++;
9706             }
9707             *p++ = *q++;
9708             l++;
9709         }
9710     }
9711     *p = '\0';
9712
9713     fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9714     if (fp == NULL) {
9715         PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9716     }
9717 }
9718
9719 static int background_process(pTHX_ int argc, char **argv)
9720 {
9721 char command[MAX_DCL_SYMBOL + 1] = "$";
9722 $DESCRIPTOR(value, "");
9723 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9724 static $DESCRIPTOR(null, "NLA0:");
9725 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9726 char pidstring[80];
9727 $DESCRIPTOR(pidstr, "");
9728 int pid;
9729 unsigned long int flags = 17, one = 1, retsts;
9730 int len;
9731
9732     strcat(command, argv[0]);
9733     len = strlen(command);
9734     while (--argc && (len < MAX_DCL_SYMBOL))
9735         {
9736         strcat(command, " \"");
9737         strcat(command, *(++argv));
9738         strcat(command, "\"");
9739         len = strlen(command);
9740         }
9741     value.dsc$a_pointer = command;
9742     value.dsc$w_length = strlen(value.dsc$a_pointer);
9743     _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9744     retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9745     if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9746         _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9747     }
9748     else {
9749         _ckvmssts_noperl(retsts);
9750     }
9751 #ifdef ARGPROC_DEBUG
9752     PerlIO_printf(Perl_debug_log, "%s\n", command);
9753 #endif
9754     sprintf(pidstring, "%08X", pid);
9755     PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9756     pidstr.dsc$a_pointer = pidstring;
9757     pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9758     lib$set_symbol(&pidsymbol, &pidstr);
9759     return(SS$_NORMAL);
9760 }
9761 /*}}}*/
9762 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9763
9764
9765 /* OS-specific initialization at image activation (not thread startup) */
9766 /* Older VAXC header files lack these constants */
9767 #ifndef JPI$_RIGHTS_SIZE
9768 #  define JPI$_RIGHTS_SIZE 817
9769 #endif
9770 #ifndef KGB$M_SUBSYSTEM
9771 #  define KGB$M_SUBSYSTEM 0x8
9772 #endif
9773  
9774 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9775
9776 /*{{{void vms_image_init(int *, char ***)*/
9777 void
9778 vms_image_init(int *argcp, char ***argvp)
9779 {
9780   int status;
9781   char eqv[LNM$C_NAMLENGTH+1] = "";
9782   unsigned int len, tabct = 8, tabidx = 0;
9783   unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9784   unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9785   unsigned short int dummy, rlen;
9786   struct dsc$descriptor_s **tabvec;
9787 #if defined(PERL_IMPLICIT_CONTEXT)
9788   pTHX = NULL;
9789 #endif
9790   struct itmlst_3 jpilist[4] = { {sizeof iprv,    JPI$_IMAGPRIV, iprv, &dummy},
9791                                  {sizeof rlst,  JPI$_RIGHTSLIST, rlst,  &rlen},
9792                                  { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9793                                  {          0,                0,    0,      0} };
9794
9795 #ifdef KILL_BY_SIGPRC
9796     Perl_csighandler_init();
9797 #endif
9798
9799 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9800     /* This was moved from the pre-image init handler because on threaded */
9801     /* Perl it was always returning 0 for the default value. */
9802     status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9803     if (status > 0) {
9804         int s;
9805         s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9806         if (s > 0) {
9807             int initial;
9808             initial = decc$feature_get_value(s, 4);
9809             if (initial > 0) {
9810                 /* initial is: 0 if nothing has set the feature */
9811                 /*            -1 if initialized to default */
9812                 /*             1 if set by logical name */
9813                 /*             2 if set by decc$feature_set_value */
9814                 decc_disable_posix_root = decc$feature_get_value(s, 1);
9815
9816                 /* If the value is not valid, force the feature off */
9817                 if (decc_disable_posix_root < 0) {
9818                     decc$feature_set_value(s, 1, 1);
9819                     decc_disable_posix_root = 1;
9820                 }
9821             }
9822             else {
9823                 /* Nothing has asked for it explicitly, so use our own default. */
9824                 decc_disable_posix_root = 1;
9825                 decc$feature_set_value(s, 1, 1);
9826             }
9827         }
9828     }
9829 #endif
9830
9831   _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9832   _ckvmssts_noperl(iosb[0]);
9833   for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9834     if (iprv[i]) {           /* Running image installed with privs? */
9835       _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL));       /* Turn 'em off. */
9836       will_taint = TRUE;
9837       break;
9838     }
9839   }
9840   /* Rights identifiers might trigger tainting as well. */
9841   if (!will_taint && (rlen || rsz)) {
9842     while (rlen < rsz) {
9843       /* We didn't get all the identifiers on the first pass.  Allocate a
9844        * buffer much larger than $GETJPI wants (rsz is size in bytes that
9845        * were needed to hold all identifiers at time of last call; we'll
9846        * allocate that many unsigned long ints), and go back and get 'em.
9847        * If it gave us less than it wanted to despite ample buffer space, 
9848        * something's broken.  Is your system missing a system identifier?
9849        */
9850       if (rsz <= jpilist[1].buflen) { 
9851          /* Perl_croak accvios when used this early in startup. */
9852          fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s", 
9853                          rsz, (unsigned long) jpilist[1].buflen,
9854                          "Check your rights database for corruption.\n");
9855          exit(SS$_ABORT);
9856       }
9857       if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9858       jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9859       if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9860       jpilist[1].buflen = rsz * sizeof(unsigned long int);
9861       _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9862       _ckvmssts_noperl(iosb[0]);
9863     }
9864     mask = jpilist[1].bufadr;
9865     /* Check attribute flags for each identifier (2nd longword); protected
9866      * subsystem identifiers trigger tainting.
9867      */
9868     for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9869       if (mask[i] & KGB$M_SUBSYSTEM) {
9870         will_taint = TRUE;
9871         break;
9872       }
9873     }
9874     if (mask != rlst) PerlMem_free(mask);
9875   }
9876
9877   /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9878    * logical, some versions of the CRTL will add a phanthom /000000/
9879    * directory.  This needs to be removed.
9880    */
9881   if (decc_filename_unix_report) {
9882   char * zeros;
9883   int ulen;
9884     ulen = strlen(argvp[0][0]);
9885     if (ulen > 7) {
9886       zeros = strstr(argvp[0][0], "/000000/");
9887       if (zeros != NULL) {
9888         int mlen;
9889         mlen = ulen - (zeros - argvp[0][0]) - 7;
9890         memmove(zeros, &zeros[7], mlen);
9891         ulen = ulen - 7;
9892         argvp[0][0][ulen] = '\0';
9893       }
9894     }
9895     /* It also may have a trailing dot that needs to be removed otherwise
9896      * it will be converted to VMS mode incorrectly.
9897      */
9898     ulen--;
9899     if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9900       argvp[0][0][ulen] = '\0';
9901   }
9902
9903   /* We need to use this hack to tell Perl it should run with tainting,
9904    * since its tainting flag may be part of the PL_curinterp struct, which
9905    * hasn't been allocated when vms_image_init() is called.
9906    */
9907   if (will_taint) {
9908     char **newargv, **oldargv;
9909     oldargv = *argvp;
9910     newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9911     if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9912     newargv[0] = oldargv[0];
9913     newargv[1] = PerlMem_malloc(3 * sizeof(char));
9914     if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9915     strcpy(newargv[1], "-T");
9916     Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9917     (*argcp)++;
9918     newargv[*argcp] = NULL;
9919     /* We orphan the old argv, since we don't know where it's come from,
9920      * so we don't know how to free it.
9921      */
9922     *argvp = newargv;
9923   }
9924   else {  /* Did user explicitly request tainting? */
9925     int i;
9926     char *cp, **av = *argvp;
9927     for (i = 1; i < *argcp; i++) {
9928       if (*av[i] != '-') break;
9929       for (cp = av[i]+1; *cp; cp++) {
9930         if (*cp == 'T') { will_taint = 1; break; }
9931         else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9932                   strchr("DFIiMmx",*cp)) break;
9933       }
9934       if (will_taint) break;
9935     }
9936   }
9937
9938   for (tabidx = 0;
9939        len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9940        tabidx++) {
9941     if (!tabidx) {
9942       tabvec = (struct dsc$descriptor_s **)
9943             PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9944       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9945     }
9946     else if (tabidx >= tabct) {
9947       tabct += 8;
9948       tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9949       if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9950     }
9951     tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9952     if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9953     tabvec[tabidx]->dsc$w_length  = 0;
9954     tabvec[tabidx]->dsc$b_dtype   = DSC$K_DTYPE_T;
9955     tabvec[tabidx]->dsc$b_class   = DSC$K_CLASS_D;
9956     tabvec[tabidx]->dsc$a_pointer = NULL;
9957     _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9958   }
9959   if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9960
9961   getredirection(argcp,argvp);
9962 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9963   {
9964 # include <reentrancy.h>
9965   decc$set_reentrancy(C$C_MULTITHREAD);
9966   }
9967 #endif
9968   return;
9969 }
9970 /*}}}*/
9971
9972
9973 /* trim_unixpath()
9974  * Trim Unix-style prefix off filespec, so it looks like what a shell
9975  * glob expansion would return (i.e. from specified prefix on, not
9976  * full path).  Note that returned filespec is Unix-style, regardless
9977  * of whether input filespec was VMS-style or Unix-style.
9978  *
9979  * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9980  * determine prefix (both may be in VMS or Unix syntax).  opts is a bit
9981  * vector of options; at present, only bit 0 is used, and if set tells
9982  * trim unixpath to try the current default directory as a prefix when
9983  * presented with a possibly ambiguous ... wildcard.
9984  *
9985  * Returns !=0 on success, with trimmed filespec replacing contents of
9986  * fspec, and 0 on failure, with contents of fpsec unchanged.
9987  */
9988 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9989 int
9990 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9991 {
9992   char *unixified, *unixwild,
9993        *template, *base, *end, *cp1, *cp2;
9994   register int tmplen, reslen = 0, dirs = 0;
9995
9996   if (!wildspec || !fspec) return 0;
9997
9998   unixwild = PerlMem_malloc(VMS_MAXRSS);
9999   if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10000   template = unixwild;
10001   if (strpbrk(wildspec,"]>:") != NULL) {
10002     if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
10003         PerlMem_free(unixwild);
10004         return 0;
10005     }
10006   }
10007   else {
10008     strncpy(unixwild, wildspec, VMS_MAXRSS-1);
10009     unixwild[VMS_MAXRSS-1] = 0;
10010   }
10011   unixified = PerlMem_malloc(VMS_MAXRSS);
10012   if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10013   if (strpbrk(fspec,"]>:") != NULL) {
10014     if (int_tounixspec(fspec, unixified, NULL) == NULL) {
10015         PerlMem_free(unixwild);
10016         PerlMem_free(unixified);
10017         return 0;
10018     }
10019     else base = unixified;
10020     /* reslen != 0 ==> we had to unixify resultant filespec, so we must
10021      * check to see that final result fits into (isn't longer than) fspec */
10022     reslen = strlen(fspec);
10023   }
10024   else base = fspec;
10025
10026   /* No prefix or absolute path on wildcard, so nothing to remove */
10027   if (!*template || *template == '/') {
10028     PerlMem_free(unixwild);
10029     if (base == fspec) {
10030         PerlMem_free(unixified);
10031         return 1;
10032     }
10033     tmplen = strlen(unixified);
10034     if (tmplen > reslen) {
10035         PerlMem_free(unixified);
10036         return 0;  /* not enough space */
10037     }
10038     /* Copy unixified resultant, including trailing NUL */
10039     memmove(fspec,unixified,tmplen+1);
10040     PerlMem_free(unixified);
10041     return 1;
10042   }
10043
10044   for (end = base; *end; end++) ;  /* Find end of resultant filespec */
10045   if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
10046     for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
10047     for (cp1 = end ;cp1 >= base; cp1--)
10048       if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
10049         { cp1++; break; }
10050     if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
10051     PerlMem_free(unixified);
10052     PerlMem_free(unixwild);
10053     return 1;
10054   }
10055   else {
10056     char *tpl, *lcres;
10057     char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
10058     int ells = 1, totells, segdirs, match;
10059     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
10060                             resdsc =  {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10061
10062     while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
10063     totells = ells;
10064     for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
10065     tpl = PerlMem_malloc(VMS_MAXRSS);
10066     if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10067     if (ellipsis == template && opts & 1) {
10068       /* Template begins with an ellipsis.  Since we can't tell how many
10069        * directory names at the front of the resultant to keep for an
10070        * arbitrary starting point, we arbitrarily choose the current
10071        * default directory as a starting point.  If it's there as a prefix,
10072        * clip it off.  If not, fall through and act as if the leading
10073        * ellipsis weren't there (i.e. return shortest possible path that
10074        * could match template).
10075        */
10076       if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
10077           PerlMem_free(tpl);
10078           PerlMem_free(unixified);
10079           PerlMem_free(unixwild);
10080           return 0;
10081       }
10082       if (!decc_efs_case_preserve) {
10083         for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10084           if (_tolower(*cp1) != _tolower(*cp2)) break;
10085       }
10086       segdirs = dirs - totells;  /* Min # of dirs we must have left */
10087       for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
10088       if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
10089         memmove(fspec,cp2+1,end - cp2);
10090         PerlMem_free(tpl);
10091         PerlMem_free(unixified);
10092         PerlMem_free(unixwild);
10093         return 1;
10094       }
10095     }
10096     /* First off, back up over constant elements at end of path */
10097     if (dirs) {
10098       for (front = end ; front >= base; front--)
10099          if (*front == '/' && !dirs--) { front++; break; }
10100     }
10101     lcres = PerlMem_malloc(VMS_MAXRSS);
10102     if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10103     for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10104          cp1++,cp2++) {
10105             if (!decc_efs_case_preserve) {
10106                 *cp2 = _tolower(*cp1);  /* Make lc copy for match */
10107             }
10108             else {
10109                 *cp2 = *cp1;
10110             }
10111     }
10112     if (cp1 != '\0') {
10113         PerlMem_free(tpl);
10114         PerlMem_free(unixified);
10115         PerlMem_free(unixwild);
10116         PerlMem_free(lcres);
10117         return 0;  /* Path too long. */
10118     }
10119     lcend = cp2;
10120     *cp2 = '\0';  /* Pick up with memcpy later */
10121     lcfront = lcres + (front - base);
10122     /* Now skip over each ellipsis and try to match the path in front of it. */
10123     while (ells--) {
10124       for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10125         if (*(cp1)   == '.' && *(cp1+1) == '.' &&
10126             *(cp1+2) == '.' && *(cp1+3) == '/'    ) break;
10127       if (cp1 < template) break; /* template started with an ellipsis */
10128       if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10129         ellipsis = cp1; continue;
10130       }
10131       wilddsc.dsc$a_pointer = tpl;
10132       wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10133       nextell = cp1;
10134       for (segdirs = 0, cp2 = tpl;
10135            cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10136            cp1++, cp2++) {
10137          if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10138          else {
10139             if (!decc_efs_case_preserve) {
10140               *cp2 = _tolower(*cp1);  /* else lowercase for match */
10141             }
10142             else {
10143               *cp2 = *cp1;  /* else preserve case for match */
10144             }
10145          }
10146          if (*cp2 == '/') segdirs++;
10147       }
10148       if (cp1 != ellipsis - 1) {
10149           PerlMem_free(tpl);
10150           PerlMem_free(unixified);
10151           PerlMem_free(unixwild);
10152           PerlMem_free(lcres);
10153           return 0; /* Path too long */
10154       }
10155       /* Back up at least as many dirs as in template before matching */
10156       for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10157         if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10158       for (match = 0; cp1 > lcres;) {
10159         resdsc.dsc$a_pointer = cp1;
10160         if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) { 
10161           match++;
10162           if (match == 1) lcfront = cp1;
10163         }
10164         for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10165       }
10166       if (!match) {
10167         PerlMem_free(tpl);
10168         PerlMem_free(unixified);
10169         PerlMem_free(unixwild);
10170         PerlMem_free(lcres);
10171         return 0;  /* Can't find prefix ??? */
10172       }
10173       if (match > 1 && opts & 1) {
10174         /* This ... wildcard could cover more than one set of dirs (i.e.
10175          * a set of similar dir names is repeated).  If the template
10176          * contains more than 1 ..., upstream elements could resolve the
10177          * ambiguity, but it's not worth a full backtracking setup here.
10178          * As a quick heuristic, clip off the current default directory
10179          * if it's present to find the trimmed spec, else use the
10180          * shortest string that this ... could cover.
10181          */
10182         char def[NAM$C_MAXRSS+1], *st;
10183
10184         if (getcwd(def, sizeof def,0) == NULL) {
10185             PerlMem_free(unixified);
10186             PerlMem_free(unixwild);
10187             PerlMem_free(lcres);
10188             PerlMem_free(tpl);
10189             return 0;
10190         }
10191         if (!decc_efs_case_preserve) {
10192           for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10193             if (_tolower(*cp1) != _tolower(*cp2)) break;
10194         }
10195         segdirs = dirs - totells;  /* Min # of dirs we must have left */
10196         for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10197         if (*cp1 == '\0' && *cp2 == '/') {
10198           memmove(fspec,cp2+1,end - cp2);
10199           PerlMem_free(tpl);
10200           PerlMem_free(unixified);
10201           PerlMem_free(unixwild);
10202           PerlMem_free(lcres);
10203           return 1;
10204         }
10205         /* Nope -- stick with lcfront from above and keep going. */
10206       }
10207     }
10208     memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10209     PerlMem_free(tpl);
10210     PerlMem_free(unixified);
10211     PerlMem_free(unixwild);
10212     PerlMem_free(lcres);
10213     return 1;
10214     ellipsis = nextell;
10215   }
10216
10217 }  /* end of trim_unixpath() */
10218 /*}}}*/
10219
10220
10221 /*
10222  *  VMS readdir() routines.
10223  *  Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10224  *
10225  *  21-Jul-1994  Charles Bailey  bailey@newman.upenn.edu
10226  *  Minor modifications to original routines.
10227  */
10228
10229 /* readdir may have been redefined by reentr.h, so make sure we get
10230  * the local version for what we do here.
10231  */
10232 #ifdef readdir
10233 # undef readdir
10234 #endif
10235 #if !defined(PERL_IMPLICIT_CONTEXT)
10236 # define readdir Perl_readdir
10237 #else
10238 # define readdir(a) Perl_readdir(aTHX_ a)
10239 #endif
10240
10241     /* Number of elements in vms_versions array */
10242 #define VERSIZE(e)      (sizeof e->vms_versions / sizeof e->vms_versions[0])
10243
10244 /*
10245  *  Open a directory, return a handle for later use.
10246  */
10247 /*{{{ DIR *opendir(char*name) */
10248 DIR *
10249 Perl_opendir(pTHX_ const char *name)
10250 {
10251     DIR *dd;
10252     char *dir;
10253     Stat_t sb;
10254
10255     Newx(dir, VMS_MAXRSS, char);
10256     if (int_tovmspath(name, dir, NULL) == NULL) {
10257       Safefree(dir);
10258       return NULL;
10259     }
10260     /* Check access before stat; otherwise stat does not
10261      * accurately report whether it's a directory.
10262      */
10263     if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10264       /* cando_by_name has already set errno */
10265       Safefree(dir);
10266       return NULL;
10267     }
10268     if (flex_stat(dir,&sb) == -1) return NULL;
10269     if (!S_ISDIR(sb.st_mode)) {
10270       Safefree(dir);
10271       set_errno(ENOTDIR);  set_vaxc_errno(RMS$_DIR);
10272       return NULL;
10273     }
10274     /* Get memory for the handle, and the pattern. */
10275     Newx(dd,1,DIR);
10276     Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10277
10278     /* Fill in the fields; mainly playing with the descriptor. */
10279     sprintf(dd->pattern, "%s*.*",dir);
10280     Safefree(dir);
10281     dd->context = 0;
10282     dd->count = 0;
10283     dd->flags = 0;
10284     /* By saying we always want the result of readdir() in unix format, we 
10285      * are really saying we want all the escapes removed.  Otherwise the caller,
10286      * having no way to know whether it's already in VMS format, might send it
10287      * through tovmsspec again, thus double escaping.
10288      */
10289     dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10290     dd->pat.dsc$a_pointer = dd->pattern;
10291     dd->pat.dsc$w_length = strlen(dd->pattern);
10292     dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10293     dd->pat.dsc$b_class = DSC$K_CLASS_S;
10294 #if defined(USE_ITHREADS)
10295     Newx(dd->mutex,1,perl_mutex);
10296     MUTEX_INIT( (perl_mutex *) dd->mutex );
10297 #else
10298     dd->mutex = NULL;
10299 #endif
10300
10301     return dd;
10302 }  /* end of opendir() */
10303 /*}}}*/
10304
10305 /*
10306  *  Set the flag to indicate we want versions or not.
10307  */
10308 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10309 void
10310 vmsreaddirversions(DIR *dd, int flag)
10311 {
10312     if (flag)
10313         dd->flags |= PERL_VMSDIR_M_VERSIONS;
10314     else
10315         dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10316 }
10317 /*}}}*/
10318
10319 /*
10320  *  Free up an opened directory.
10321  */
10322 /*{{{ void closedir(DIR *dd)*/
10323 void
10324 Perl_closedir(DIR *dd)
10325 {
10326     int sts;
10327
10328     sts = lib$find_file_end(&dd->context);
10329     Safefree(dd->pattern);
10330 #if defined(USE_ITHREADS)
10331     MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10332     Safefree(dd->mutex);
10333 #endif
10334     Safefree(dd);
10335 }
10336 /*}}}*/
10337
10338 /*
10339  *  Collect all the version numbers for the current file.
10340  */
10341 static void
10342 collectversions(pTHX_ DIR *dd)
10343 {
10344     struct dsc$descriptor_s     pat;
10345     struct dsc$descriptor_s     res;
10346     struct dirent *e;
10347     char *p, *text, *buff;
10348     int i;
10349     unsigned long context, tmpsts;
10350
10351     /* Convenient shorthand. */
10352     e = &dd->entry;
10353
10354     /* Add the version wildcard, ignoring the "*.*" put on before */
10355     i = strlen(dd->pattern);
10356     Newx(text,i + e->d_namlen + 3,char);
10357     strcpy(text, dd->pattern);
10358     sprintf(&text[i - 3], "%s;*", e->d_name);
10359
10360     /* Set up the pattern descriptor. */
10361     pat.dsc$a_pointer = text;
10362     pat.dsc$w_length = i + e->d_namlen - 1;
10363     pat.dsc$b_dtype = DSC$K_DTYPE_T;
10364     pat.dsc$b_class = DSC$K_CLASS_S;
10365
10366     /* Set up result descriptor. */
10367     Newx(buff, VMS_MAXRSS, char);
10368     res.dsc$a_pointer = buff;
10369     res.dsc$w_length = VMS_MAXRSS - 1;
10370     res.dsc$b_dtype = DSC$K_DTYPE_T;
10371     res.dsc$b_class = DSC$K_CLASS_S;
10372
10373     /* Read files, collecting versions. */
10374     for (context = 0, e->vms_verscount = 0;
10375          e->vms_verscount < VERSIZE(e);
10376          e->vms_verscount++) {
10377         unsigned long rsts;
10378         unsigned long flags = 0;
10379
10380 #ifdef VMS_LONGNAME_SUPPORT
10381         flags = LIB$M_FIL_LONG_NAMES;
10382 #endif
10383         tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10384         if (tmpsts == RMS$_NMF || context == 0) break;
10385         _ckvmssts(tmpsts);
10386         buff[VMS_MAXRSS - 1] = '\0';
10387         if ((p = strchr(buff, ';')))
10388             e->vms_versions[e->vms_verscount] = atoi(p + 1);
10389         else
10390             e->vms_versions[e->vms_verscount] = -1;
10391     }
10392
10393     _ckvmssts(lib$find_file_end(&context));
10394     Safefree(text);
10395     Safefree(buff);
10396
10397 }  /* end of collectversions() */
10398
10399 /*
10400  *  Read the next entry from the directory.
10401  */
10402 /*{{{ struct dirent *readdir(DIR *dd)*/
10403 struct dirent *
10404 Perl_readdir(pTHX_ DIR *dd)
10405 {
10406     struct dsc$descriptor_s     res;
10407     char *p, *buff;
10408     unsigned long int tmpsts;
10409     unsigned long rsts;
10410     unsigned long flags = 0;
10411     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10412     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10413
10414     /* Set up result descriptor, and get next file. */
10415     Newx(buff, VMS_MAXRSS, char);
10416     res.dsc$a_pointer = buff;
10417     res.dsc$w_length = VMS_MAXRSS - 1;
10418     res.dsc$b_dtype = DSC$K_DTYPE_T;
10419     res.dsc$b_class = DSC$K_CLASS_S;
10420
10421 #ifdef VMS_LONGNAME_SUPPORT
10422     flags = LIB$M_FIL_LONG_NAMES;
10423 #endif
10424
10425     tmpsts = lib$find_file
10426         (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10427     if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL;  /* None left. */
10428     if (!(tmpsts & 1)) {
10429       set_vaxc_errno(tmpsts);
10430       switch (tmpsts) {
10431         case RMS$_PRV:
10432           set_errno(EACCES); break;
10433         case RMS$_DEV:
10434           set_errno(ENODEV); break;
10435         case RMS$_DIR:
10436           set_errno(ENOTDIR); break;
10437         case RMS$_FNF: case RMS$_DNF:
10438           set_errno(ENOENT); break;
10439         default:
10440           set_errno(EVMSERR);
10441       }
10442       Safefree(buff);
10443       return NULL;
10444     }
10445     dd->count++;
10446     /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10447     buff[res.dsc$w_length] = '\0';
10448     p = buff + res.dsc$w_length;
10449     while (--p >= buff) if (!isspace(*p)) break;  
10450     *p = '\0';
10451     if (!decc_efs_case_preserve) {
10452       for (p = buff; *p; p++) *p = _tolower(*p);
10453     }
10454
10455     /* Skip any directory component and just copy the name. */
10456     sts = vms_split_path
10457        (buff,
10458         &v_spec,
10459         &v_len,
10460         &r_spec,
10461         &r_len,
10462         &d_spec,
10463         &d_len,
10464         &n_spec,
10465         &n_len,
10466         &e_spec,
10467         &e_len,
10468         &vs_spec,
10469         &vs_len);
10470
10471     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10472
10473         /* In Unix report mode, remove the ".dir;1" from the name */
10474         /* if it is a real directory. */
10475         if (decc_filename_unix_report || decc_efs_charset) {
10476             if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10477                 Stat_t statbuf;
10478                 int ret_sts;
10479
10480                 ret_sts = flex_lstat(buff, &statbuf);
10481                 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10482                     e_len = 0;
10483                     e_spec[0] = 0;
10484                 }
10485             }
10486         }
10487
10488         /* Drop NULL extensions on UNIX file specification */
10489         if ((e_len == 1) && decc_readdir_dropdotnotype) {
10490             e_len = 0;
10491             e_spec[0] = '\0';
10492         }
10493     }
10494
10495     strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10496     dd->entry.d_name[n_len + e_len] = '\0';
10497     dd->entry.d_namlen = strlen(dd->entry.d_name);
10498
10499     /* Convert the filename to UNIX format if needed */
10500     if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10501
10502         /* Translate the encoded characters. */
10503         /* Fixme: Unicode handling could result in embedded 0 characters */
10504         if (strchr(dd->entry.d_name, '^') != NULL) {
10505             char new_name[256];
10506             char * q;
10507             p = dd->entry.d_name;
10508             q = new_name;
10509             while (*p != 0) {
10510                 int inchars_read, outchars_added;
10511                 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10512                 p += inchars_read;
10513                 q += outchars_added;
10514                 /* fix-me */
10515                 /* if outchars_added > 1, then this is a wide file specification */
10516                 /* Wide file specifications need to be passed in Perl */
10517                 /* counted strings apparently with a Unicode flag */
10518             }
10519             *q = 0;
10520             strcpy(dd->entry.d_name, new_name);
10521             dd->entry.d_namlen = strlen(dd->entry.d_name);
10522         }
10523     }
10524
10525     dd->entry.vms_verscount = 0;
10526     if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10527     Safefree(buff);
10528     return &dd->entry;
10529
10530 }  /* end of readdir() */
10531 /*}}}*/
10532
10533 /*
10534  *  Read the next entry from the directory -- thread-safe version.
10535  */
10536 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10537 int
10538 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10539 {
10540     int retval;
10541
10542     MUTEX_LOCK( (perl_mutex *) dd->mutex );
10543
10544     entry = readdir(dd);
10545     *result = entry;
10546     retval = ( *result == NULL ? errno : 0 );
10547
10548     MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10549
10550     return retval;
10551
10552 }  /* end of readdir_r() */
10553 /*}}}*/
10554
10555 /*
10556  *  Return something that can be used in a seekdir later.
10557  */
10558 /*{{{ long telldir(DIR *dd)*/
10559 long
10560 Perl_telldir(DIR *dd)
10561 {
10562     return dd->count;
10563 }
10564 /*}}}*/
10565
10566 /*
10567  *  Return to a spot where we used to be.  Brute force.
10568  */
10569 /*{{{ void seekdir(DIR *dd,long count)*/
10570 void
10571 Perl_seekdir(pTHX_ DIR *dd, long count)
10572 {
10573     int old_flags;
10574
10575     /* If we haven't done anything yet... */
10576     if (dd->count == 0)
10577         return;
10578
10579     /* Remember some state, and clear it. */
10580     old_flags = dd->flags;
10581     dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10582     _ckvmssts(lib$find_file_end(&dd->context));
10583     dd->context = 0;
10584
10585     /* The increment is in readdir(). */
10586     for (dd->count = 0; dd->count < count; )
10587         readdir(dd);
10588
10589     dd->flags = old_flags;
10590
10591 }  /* end of seekdir() */
10592 /*}}}*/
10593
10594 /* VMS subprocess management
10595  *
10596  * my_vfork() - just a vfork(), after setting a flag to record that
10597  * the current script is trying a Unix-style fork/exec.
10598  *
10599  * vms_do_aexec() and vms_do_exec() are called in response to the
10600  * perl 'exec' function.  If this follows a vfork call, then they
10601  * call out the regular perl routines in doio.c which do an
10602  * execvp (for those who really want to try this under VMS).
10603  * Otherwise, they do exactly what the perl docs say exec should
10604  * do - terminate the current script and invoke a new command
10605  * (See below for notes on command syntax.)
10606  *
10607  * do_aspawn() and do_spawn() implement the VMS side of the perl
10608  * 'system' function.
10609  *
10610  * Note on command arguments to perl 'exec' and 'system': When handled
10611  * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10612  * are concatenated to form a DCL command string.  If the first non-numeric
10613  * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10614  * the command string is handed off to DCL directly.  Otherwise,
10615  * the first token of the command is taken as the filespec of an image
10616  * to run.  The filespec is expanded using a default type of '.EXE' and
10617  * the process defaults for device, directory, etc., and if found, the resultant
10618  * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10619  * the command string as parameters.  This is perhaps a bit complicated,
10620  * but I hope it will form a happy medium between what VMS folks expect
10621  * from lib$spawn and what Unix folks expect from exec.
10622  */
10623
10624 static int vfork_called;
10625
10626 /*{{{int my_vfork()*/
10627 int
10628 my_vfork()
10629 {
10630   vfork_called++;
10631   return vfork();
10632 }
10633 /*}}}*/
10634
10635
10636 static void
10637 vms_execfree(struct dsc$descriptor_s *vmscmd) 
10638 {
10639   if (vmscmd) {
10640       if (vmscmd->dsc$a_pointer) {
10641           PerlMem_free(vmscmd->dsc$a_pointer);
10642       }
10643       PerlMem_free(vmscmd);
10644   }
10645 }
10646
10647 static char *
10648 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10649 {
10650   char *junk, *tmps = NULL;
10651   register size_t cmdlen = 0;
10652   size_t rlen;
10653   register SV **idx;
10654   STRLEN n_a;
10655
10656   idx = mark;
10657   if (really) {
10658     tmps = SvPV(really,rlen);
10659     if (*tmps) {
10660       cmdlen += rlen + 1;
10661       idx++;
10662     }
10663   }
10664   
10665   for (idx++; idx <= sp; idx++) {
10666     if (*idx) {
10667       junk = SvPVx(*idx,rlen);
10668       cmdlen += rlen ? rlen + 1 : 0;
10669     }
10670   }
10671   Newx(PL_Cmd, cmdlen+1, char);
10672
10673   if (tmps && *tmps) {
10674     strcpy(PL_Cmd,tmps);
10675     mark++;
10676   }
10677   else *PL_Cmd = '\0';
10678   while (++mark <= sp) {
10679     if (*mark) {
10680       char *s = SvPVx(*mark,n_a);
10681       if (!*s) continue;
10682       if (*PL_Cmd) strcat(PL_Cmd," ");
10683       strcat(PL_Cmd,s);
10684     }
10685   }
10686   return PL_Cmd;
10687
10688 }  /* end of setup_argstr() */
10689
10690
10691 static unsigned long int
10692 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10693                    struct dsc$descriptor_s **pvmscmd)
10694 {
10695   char * vmsspec;
10696   char * resspec;
10697   char image_name[NAM$C_MAXRSS+1];
10698   char image_argv[NAM$C_MAXRSS+1];
10699   $DESCRIPTOR(defdsc,".EXE");
10700   $DESCRIPTOR(defdsc2,".");
10701   struct dsc$descriptor_s resdsc;
10702   struct dsc$descriptor_s *vmscmd;
10703   struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10704   unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10705   register char *s, *rest, *cp, *wordbreak;
10706   char * cmd;
10707   int cmdlen;
10708   register int isdcl;
10709
10710   vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10711   if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10712
10713   /* vmsspec is a DCL command buffer, not just a filename */
10714   vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10715   if (vmsspec == NULL)
10716       _ckvmssts_noperl(SS$_INSFMEM);
10717
10718   resspec = PerlMem_malloc(VMS_MAXRSS);
10719   if (resspec == NULL)
10720       _ckvmssts_noperl(SS$_INSFMEM);
10721
10722   /* Make a copy for modification */
10723   cmdlen = strlen(incmd);
10724   cmd = PerlMem_malloc(cmdlen+1);
10725   if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10726   strncpy(cmd, incmd, cmdlen);
10727   cmd[cmdlen] = 0;
10728   image_name[0] = 0;
10729   image_argv[0] = 0;
10730
10731   resdsc.dsc$a_pointer = resspec;
10732   resdsc.dsc$b_dtype  = DSC$K_DTYPE_T;
10733   resdsc.dsc$b_class  = DSC$K_CLASS_S;
10734   resdsc.dsc$w_length = VMS_MAXRSS - 1;
10735
10736   vmscmd->dsc$a_pointer = NULL;
10737   vmscmd->dsc$b_dtype  = DSC$K_DTYPE_T;
10738   vmscmd->dsc$b_class  = DSC$K_CLASS_S;
10739   vmscmd->dsc$w_length = 0;
10740   if (pvmscmd) *pvmscmd = vmscmd;
10741
10742   if (suggest_quote) *suggest_quote = 0;
10743
10744   if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10745     PerlMem_free(cmd);
10746     PerlMem_free(vmsspec);
10747     PerlMem_free(resspec);
10748     return CLI$_BUFOVF;                /* continuation lines currently unsupported */
10749   }
10750
10751   s = cmd;
10752
10753   while (*s && isspace(*s)) s++;
10754
10755   if (*s == '@' || *s == '$') {
10756     vmsspec[0] = *s;  rest = s + 1;
10757     for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10758   }
10759   else { cp = vmsspec; rest = s; }
10760   if (*rest == '.' || *rest == '/') {
10761     char *cp2;
10762     for (cp2 = resspec;
10763          *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10764          rest++, cp2++) *cp2 = *rest;
10765     *cp2 = '\0';
10766     if (int_tovmsspec(resspec, cp, 0, NULL)) { 
10767       s = vmsspec;
10768
10769       /* When a UNIX spec with no file type is translated to VMS, */
10770       /* A trailing '.' is appended under ODS-5 rules.            */
10771       /* Here we do not want that trailing "." as it prevents     */
10772       /* Looking for a implied ".exe" type. */
10773       if (decc_efs_charset) {
10774           int i;
10775           i = strlen(vmsspec);
10776           if (vmsspec[i-1] == '.') {
10777               vmsspec[i-1] = '\0';
10778           }
10779       }
10780
10781       if (*rest) {
10782         for (cp2 = vmsspec + strlen(vmsspec);
10783              *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10784              rest++, cp2++) *cp2 = *rest;
10785         *cp2 = '\0';
10786       }
10787     }
10788   }
10789   /* Intuit whether verb (first word of cmd) is a DCL command:
10790    *   - if first nonspace char is '@', it's a DCL indirection
10791    * otherwise
10792    *   - if verb contains a filespec separator, it's not a DCL command
10793    *   - if it doesn't, caller tells us whether to default to a DCL
10794    *     command, or to a local image unless told it's DCL (by leading '$')
10795    */
10796   if (*s == '@') {
10797       isdcl = 1;
10798       if (suggest_quote) *suggest_quote = 1;
10799   } else {
10800     register char *filespec = strpbrk(s,":<[.;");
10801     rest = wordbreak = strpbrk(s," \"\t/");
10802     if (!wordbreak) wordbreak = s + strlen(s);
10803     if (*s == '$') check_img = 0;
10804     if (filespec && (filespec < wordbreak)) isdcl = 0;
10805     else isdcl = !check_img;
10806   }
10807
10808   if (!isdcl) {
10809     int rsts;
10810     imgdsc.dsc$a_pointer = s;
10811     imgdsc.dsc$w_length = wordbreak - s;
10812     retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10813     if (!(retsts&1)) {
10814         _ckvmssts_noperl(lib$find_file_end(&cxt));
10815         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10816       if (!(retsts & 1) && *s == '$') {
10817         _ckvmssts_noperl(lib$find_file_end(&cxt));
10818         imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10819         retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10820         if (!(retsts&1)) {
10821           _ckvmssts_noperl(lib$find_file_end(&cxt));
10822           retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10823         }
10824       }
10825     }
10826     _ckvmssts_noperl(lib$find_file_end(&cxt));
10827
10828     if (retsts & 1) {
10829       FILE *fp;
10830       s = resspec;
10831       while (*s && !isspace(*s)) s++;
10832       *s = '\0';
10833
10834       /* check that it's really not DCL with no file extension */
10835       fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10836       if (fp) {
10837         char b[256] = {0,0,0,0};
10838         read(fileno(fp), b, 256);
10839         isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10840         if (isdcl) {
10841           int shebang_len;
10842
10843           /* Check for script */
10844           shebang_len = 0;
10845           if ((b[0] == '#') && (b[1] == '!'))
10846              shebang_len = 2;
10847 #ifdef ALTERNATE_SHEBANG
10848           else {
10849             shebang_len = strlen(ALTERNATE_SHEBANG);
10850             if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10851               char * perlstr;
10852                 perlstr = strstr("perl",b);
10853                 if (perlstr == NULL)
10854                   shebang_len = 0;
10855             }
10856             else
10857               shebang_len = 0;
10858           }
10859 #endif
10860
10861           if (shebang_len > 0) {
10862           int i;
10863           int j;
10864           char tmpspec[NAM$C_MAXRSS + 1];
10865
10866             i = shebang_len;
10867              /* Image is following after white space */
10868             /*--------------------------------------*/
10869             while (isprint(b[i]) && isspace(b[i]))
10870                 i++;
10871
10872             j = 0;
10873             while (isprint(b[i]) && !isspace(b[i])) {
10874                 tmpspec[j++] = b[i++];
10875                 if (j >= NAM$C_MAXRSS)
10876                    break;
10877             }
10878             tmpspec[j] = '\0';
10879
10880              /* There may be some default parameters to the image */
10881             /*---------------------------------------------------*/
10882             j = 0;
10883             while (isprint(b[i])) {
10884                 image_argv[j++] = b[i++];
10885                 if (j >= NAM$C_MAXRSS)
10886                    break;
10887             }
10888             while ((j > 0) && !isprint(image_argv[j-1]))
10889                 j--;
10890             image_argv[j] = 0;
10891
10892             /* It will need to be converted to VMS format and validated */
10893             if (tmpspec[0] != '\0') {
10894               char * iname;
10895
10896                /* Try to find the exact program requested to be run */
10897               /*---------------------------------------------------*/
10898               iname = int_rmsexpand
10899                  (tmpspec, image_name, ".exe",
10900                   PERL_RMSEXPAND_M_VMS, NULL, NULL);
10901               if (iname != NULL) {
10902                 if (cando_by_name_int
10903                         (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10904                   /* MCR prefix needed */
10905                   isdcl = 0;
10906                 }
10907                 else {
10908                    /* Try again with a null type */
10909                   /*----------------------------*/
10910                   iname = int_rmsexpand
10911                     (tmpspec, image_name, ".",
10912                      PERL_RMSEXPAND_M_VMS, NULL, NULL);
10913                   if (iname != NULL) {
10914                     if (cando_by_name_int
10915                          (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10916                       /* MCR prefix needed */
10917                       isdcl = 0;
10918                     }
10919                   }
10920                 }
10921
10922                  /* Did we find the image to run the script? */
10923                 /*------------------------------------------*/
10924                 if (isdcl) {
10925                   char *tchr;
10926
10927                    /* Assume DCL or foreign command exists */
10928                   /*--------------------------------------*/
10929                   tchr = strrchr(tmpspec, '/');
10930                   if (tchr != NULL) {
10931                     tchr++;
10932                   }
10933                   else {
10934                     tchr = tmpspec;
10935                   }
10936                   strcpy(image_name, tchr);
10937                 }
10938               }
10939             }
10940           }
10941         }
10942         fclose(fp);
10943       }
10944       if (check_img && isdcl) {
10945           PerlMem_free(cmd);
10946           PerlMem_free(resspec);
10947           PerlMem_free(vmsspec);
10948           return RMS$_FNF;
10949       }
10950
10951       if (cando_by_name(S_IXUSR,0,resspec)) {
10952         vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10953         if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10954         if (!isdcl) {
10955             strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10956             if (image_name[0] != 0) {
10957                 strcat(vmscmd->dsc$a_pointer, image_name);
10958                 strcat(vmscmd->dsc$a_pointer, " ");
10959             }
10960         } else if (image_name[0] != 0) {
10961             strcpy(vmscmd->dsc$a_pointer, image_name);
10962             strcat(vmscmd->dsc$a_pointer, " ");
10963         } else {
10964             strcpy(vmscmd->dsc$a_pointer,"@");
10965         }
10966         if (suggest_quote) *suggest_quote = 1;
10967
10968         /* If there is an image name, use original command */
10969         if (image_name[0] == 0)
10970             strcat(vmscmd->dsc$a_pointer,resspec);
10971         else {
10972             rest = cmd;
10973             while (*rest && isspace(*rest)) rest++;
10974         }
10975
10976         if (image_argv[0] != 0) {
10977           strcat(vmscmd->dsc$a_pointer,image_argv);
10978           strcat(vmscmd->dsc$a_pointer, " ");
10979         }
10980         if (rest) {
10981            int rest_len;
10982            int vmscmd_len;
10983
10984            rest_len = strlen(rest);
10985            vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10986            if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10987               strcat(vmscmd->dsc$a_pointer,rest);
10988            else
10989              retsts = CLI$_BUFOVF;
10990         }
10991         vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10992         PerlMem_free(cmd);
10993         PerlMem_free(vmsspec);
10994         PerlMem_free(resspec);
10995         return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10996       }
10997       else
10998         retsts = RMS$_PRV;
10999     }
11000   }
11001   /* It's either a DCL command or we couldn't find a suitable image */
11002   vmscmd->dsc$w_length = strlen(cmd);
11003
11004   vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
11005   strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
11006   vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
11007
11008   PerlMem_free(cmd);
11009   PerlMem_free(resspec);
11010   PerlMem_free(vmsspec);
11011
11012   /* check if it's a symbol (for quoting purposes) */
11013   if (suggest_quote && !*suggest_quote) { 
11014     int iss;     
11015     char equiv[LNM$C_NAMLENGTH];
11016     struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11017     eqvdsc.dsc$a_pointer = equiv;
11018
11019     iss = lib$get_symbol(vmscmd,&eqvdsc);
11020     if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
11021   }
11022   if (!(retsts & 1)) {
11023     /* just hand off status values likely to be due to user error */
11024     if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
11025         retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
11026        (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
11027     else { _ckvmssts_noperl(retsts); }
11028   }
11029
11030   return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
11031
11032 }  /* end of setup_cmddsc() */
11033
11034
11035 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
11036 bool
11037 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
11038 {
11039 bool exec_sts;
11040 char * cmd;
11041
11042   if (sp > mark) {
11043     if (vfork_called) {           /* this follows a vfork - act Unixish */
11044       vfork_called--;
11045       if (vfork_called < 0) {
11046         Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11047         vfork_called = 0;
11048       }
11049       else return do_aexec(really,mark,sp);
11050     }
11051                                            /* no vfork - act VMSish */
11052     cmd = setup_argstr(aTHX_ really,mark,sp);
11053     exec_sts = vms_do_exec(cmd);
11054     Safefree(cmd);  /* Clean up from setup_argstr() */
11055     return exec_sts;
11056   }
11057
11058   return FALSE;
11059 }  /* end of vms_do_aexec() */
11060 /*}}}*/
11061
11062 /* {{{bool vms_do_exec(char *cmd) */
11063 bool
11064 Perl_vms_do_exec(pTHX_ const char *cmd)
11065 {
11066   struct dsc$descriptor_s *vmscmd;
11067
11068   if (vfork_called) {             /* this follows a vfork - act Unixish */
11069     vfork_called--;
11070     if (vfork_called < 0) {
11071       Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11072       vfork_called = 0;
11073     }
11074     else return do_exec(cmd);
11075   }
11076
11077   {                               /* no vfork - act VMSish */
11078     unsigned long int retsts;
11079
11080     TAINT_ENV();
11081     TAINT_PROPER("exec");
11082     if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11083       retsts = lib$do_command(vmscmd);
11084
11085     switch (retsts) {
11086       case RMS$_FNF: case RMS$_DNF:
11087         set_errno(ENOENT); break;
11088       case RMS$_DIR:
11089         set_errno(ENOTDIR); break;
11090       case RMS$_DEV:
11091         set_errno(ENODEV); break;
11092       case RMS$_PRV:
11093         set_errno(EACCES); break;
11094       case RMS$_SYN:
11095         set_errno(EINVAL); break;
11096       case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11097         set_errno(E2BIG); break;
11098       case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11099         _ckvmssts_noperl(retsts); /* fall through */
11100       default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11101         set_errno(EVMSERR); 
11102     }
11103     set_vaxc_errno(retsts);
11104     if (ckWARN(WARN_EXEC)) {
11105       Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11106              vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11107     }
11108     vms_execfree(vmscmd);
11109   }
11110
11111   return FALSE;
11112
11113 }  /* end of vms_do_exec() */
11114 /*}}}*/
11115
11116 int do_spawn2(pTHX_ const char *, int);
11117
11118 int
11119 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11120 {
11121 unsigned long int sts;
11122 char * cmd;
11123 int flags = 0;
11124
11125   if (sp > mark) {
11126
11127     /* We'll copy the (undocumented?) Win32 behavior and allow a 
11128      * numeric first argument.  But the only value we'll support
11129      * through do_aspawn is a value of 1, which means spawn without
11130      * waiting for completion -- other values are ignored.
11131      */
11132     if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11133         ++mark;
11134         flags = SvIVx(*mark);
11135     }
11136
11137     if (flags && flags == 1)     /* the Win32 P_NOWAIT value */
11138         flags = CLI$M_NOWAIT;
11139     else
11140         flags = 0;
11141
11142     cmd = setup_argstr(aTHX_ really, mark, sp);
11143     sts = do_spawn2(aTHX_ cmd, flags);
11144     /* pp_sys will clean up cmd */
11145     return sts;
11146   }
11147   return SS$_ABORT;
11148 }  /* end of do_aspawn() */
11149 /*}}}*/
11150
11151
11152 /* {{{int do_spawn(char* cmd) */
11153 int
11154 Perl_do_spawn(pTHX_ char* cmd)
11155 {
11156     PERL_ARGS_ASSERT_DO_SPAWN;
11157
11158     return do_spawn2(aTHX_ cmd, 0);
11159 }
11160 /*}}}*/
11161
11162 /* {{{int do_spawn_nowait(char* cmd) */
11163 int
11164 Perl_do_spawn_nowait(pTHX_ char* cmd)
11165 {
11166     PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11167
11168     return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11169 }
11170 /*}}}*/
11171
11172 /* {{{int do_spawn2(char *cmd) */
11173 int
11174 do_spawn2(pTHX_ const char *cmd, int flags)
11175 {
11176   unsigned long int sts, substs;
11177
11178   /* The caller of this routine expects to Safefree(PL_Cmd) */
11179   Newx(PL_Cmd,10,char);
11180
11181   TAINT_ENV();
11182   TAINT_PROPER("spawn");
11183   if (!cmd || !*cmd) {
11184     sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11185     if (!(sts & 1)) {
11186       switch (sts) {
11187         case RMS$_FNF:  case RMS$_DNF:
11188           set_errno(ENOENT); break;
11189         case RMS$_DIR:
11190           set_errno(ENOTDIR); break;
11191         case RMS$_DEV:
11192           set_errno(ENODEV); break;
11193         case RMS$_PRV:
11194           set_errno(EACCES); break;
11195         case RMS$_SYN:
11196           set_errno(EINVAL); break;
11197         case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11198           set_errno(E2BIG); break;
11199         case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11200           _ckvmssts_noperl(sts); /* fall through */
11201         default:  /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11202           set_errno(EVMSERR);
11203       }
11204       set_vaxc_errno(sts);
11205       if (ckWARN(WARN_EXEC)) {
11206         Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11207                     Strerror(errno));
11208       }
11209     }
11210     sts = substs;
11211   }
11212   else {
11213     char mode[3];
11214     PerlIO * fp;
11215     if (flags & CLI$M_NOWAIT)
11216         strcpy(mode, "n");
11217     else
11218         strcpy(mode, "nW");
11219     
11220     fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11221     if (fp != NULL)
11222       my_pclose(fp);
11223     /* sts will be the pid in the nowait case */
11224   }
11225   return sts;
11226 }  /* end of do_spawn2() */
11227 /*}}}*/
11228
11229
11230 static unsigned int *sockflags, sockflagsize;
11231
11232 /*
11233  * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11234  * routines found in some versions of the CRTL can't deal with sockets.
11235  * We don't shim the other file open routines since a socket isn't
11236  * likely to be opened by a name.
11237  */
11238 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11239 FILE *my_fdopen(int fd, const char *mode)
11240 {
11241   FILE *fp = fdopen(fd, mode);
11242
11243   if (fp) {
11244     unsigned int fdoff = fd / sizeof(unsigned int);
11245     Stat_t sbuf; /* native stat; we don't need flex_stat */
11246     if (!sockflagsize || fdoff > sockflagsize) {
11247       if (sockflags) Renew(     sockflags,fdoff+2,unsigned int);
11248       else           Newx  (sockflags,fdoff+2,unsigned int);
11249       memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11250       sockflagsize = fdoff + 2;
11251     }
11252     if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11253       sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11254   }
11255   return fp;
11256
11257 }
11258 /*}}}*/
11259
11260
11261 /*
11262  * Clear the corresponding bit when the (possibly) socket stream is closed.
11263  * There still a small hole: we miss an implicit close which might occur
11264  * via freopen().  >> Todo
11265  */
11266 /*{{{ int my_fclose(FILE *fp)*/
11267 int my_fclose(FILE *fp) {
11268   if (fp) {
11269     unsigned int fd = fileno(fp);
11270     unsigned int fdoff = fd / sizeof(unsigned int);
11271
11272     if (sockflagsize && fdoff < sockflagsize)
11273       sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11274   }
11275   return fclose(fp);
11276 }
11277 /*}}}*/
11278
11279
11280 /* 
11281  * A simple fwrite replacement which outputs itmsz*nitm chars without
11282  * introducing record boundaries every itmsz chars.
11283  * We are using fputs, which depends on a terminating null.  We may
11284  * well be writing binary data, so we need to accommodate not only
11285  * data with nulls sprinkled in the middle but also data with no null 
11286  * byte at the end.
11287  */
11288 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11289 int
11290 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11291 {
11292   register char *cp, *end, *cpd, *data;
11293   register unsigned int fd = fileno(dest);
11294   register unsigned int fdoff = fd / sizeof(unsigned int);
11295   int retval;
11296   int bufsize = itmsz * nitm + 1;
11297
11298   if (fdoff < sockflagsize &&
11299       (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11300     if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11301     return nitm;
11302   }
11303
11304   _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11305   memcpy( data, src, itmsz*nitm );
11306   data[itmsz*nitm] = '\0';
11307
11308   end = data + itmsz * nitm;
11309   retval = (int) nitm; /* on success return # items written */
11310
11311   cpd = data;
11312   while (cpd <= end) {
11313     for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11314     if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11315     if (cp < end)
11316       if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11317     cpd = cp + 1;
11318   }
11319
11320   if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11321   return retval;
11322
11323 }  /* end of my_fwrite() */
11324 /*}}}*/
11325
11326 /*{{{ int my_flush(FILE *fp)*/
11327 int
11328 Perl_my_flush(pTHX_ FILE *fp)
11329 {
11330     int res;
11331     if ((res = fflush(fp)) == 0 && fp) {
11332 #ifdef VMS_DO_SOCKETS
11333         Stat_t s;
11334         if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11335 #endif
11336             res = fsync(fileno(fp));
11337     }
11338 /*
11339  * If the flush succeeded but set end-of-file, we need to clear
11340  * the error because our caller may check ferror().  BTW, this 
11341  * probably means we just flushed an empty file.
11342  */
11343     if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11344
11345     return res;
11346 }
11347 /*}}}*/
11348
11349 /* fgetname() is not returning the correct file specifications when
11350  * decc_filename_unix_report mode is active.  So we have to have it
11351  * aways return filenames in VMS mode and convert it ourselves.
11352  */
11353
11354 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11355 char *
11356 Perl_my_fgetname(FILE *fp, char * buf) {
11357     char * retname;
11358     char * vms_name;
11359
11360     retname = fgetname(fp, buf, 1);
11361
11362     /* If we are in VMS mode, then we are done */
11363     if (!decc_filename_unix_report || (retname == NULL)) {
11364        return retname;
11365     }
11366
11367     /* Convert this to Unix format */
11368     vms_name = PerlMem_malloc(VMS_MAXRSS + 1);
11369     strcpy(vms_name, retname);
11370     retname = int_tounixspec(vms_name, buf, NULL);
11371     PerlMem_free(vms_name);
11372
11373     return retname;
11374 }
11375 /*}}}*/
11376
11377 /*
11378  * Here are replacements for the following Unix routines in the VMS environment:
11379  *      getpwuid    Get information for a particular UIC or UID
11380  *      getpwnam    Get information for a named user
11381  *      getpwent    Get information for each user in the rights database
11382  *      setpwent    Reset search to the start of the rights database
11383  *      endpwent    Finish searching for users in the rights database
11384  *
11385  * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11386  * (defined in pwd.h), which contains the following fields:-
11387  *      struct passwd {
11388  *              char        *pw_name;    Username (in lower case)
11389  *              char        *pw_passwd;  Hashed password
11390  *              unsigned int pw_uid;     UIC
11391  *              unsigned int pw_gid;     UIC group  number
11392  *              char        *pw_unixdir; Default device/directory (VMS-style)
11393  *              char        *pw_gecos;   Owner name
11394  *              char        *pw_dir;     Default device/directory (Unix-style)
11395  *              char        *pw_shell;   Default CLI name (eg. DCL)
11396  *      };
11397  * If the specified user does not exist, getpwuid and getpwnam return NULL.
11398  *
11399  * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11400  * not the UIC member number (eg. what's returned by getuid()),
11401  * getpwuid() can accept either as input (if uid is specified, the caller's
11402  * UIC group is used), though it won't recognise gid=0.
11403  *
11404  * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11405  * information about other users in your group or in other groups, respectively.
11406  * If the required privilege is not available, then these routines fill only
11407  * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11408  * string).
11409  *
11410  * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11411  */
11412
11413 /* sizes of various UAF record fields */
11414 #define UAI$S_USERNAME 12
11415 #define UAI$S_IDENT    31
11416 #define UAI$S_OWNER    31
11417 #define UAI$S_DEFDEV   31
11418 #define UAI$S_DEFDIR   63
11419 #define UAI$S_DEFCLI   31
11420 #define UAI$S_PWD       8
11421
11422 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT  && \
11423                         (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11424                         (uic).uic$v_group  != UIC$K_WILD_GROUP)
11425
11426 static char __empty[]= "";
11427 static struct passwd __passwd_empty=
11428     {(char *) __empty, (char *) __empty, 0, 0,
11429      (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11430 static int contxt= 0;
11431 static struct passwd __pwdcache;
11432 static char __pw_namecache[UAI$S_IDENT+1];
11433
11434 /*
11435  * This routine does most of the work extracting the user information.
11436  */
11437 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11438 {
11439     static struct {
11440         unsigned char length;
11441         char pw_gecos[UAI$S_OWNER+1];
11442     } owner;
11443     static union uicdef uic;
11444     static struct {
11445         unsigned char length;
11446         char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11447     } defdev;
11448     static struct {
11449         unsigned char length;
11450         char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11451     } defdir;
11452     static struct {
11453         unsigned char length;
11454         char pw_shell[UAI$S_DEFCLI+1];
11455     } defcli;
11456     static char pw_passwd[UAI$S_PWD+1];
11457
11458     static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11459     struct dsc$descriptor_s name_desc;
11460     unsigned long int sts;
11461
11462     static struct itmlst_3 itmlst[]= {
11463         {UAI$S_OWNER+1,    UAI$_OWNER,  &owner,    &lowner},
11464         {sizeof(uic),      UAI$_UIC,    &uic,      &luic},
11465         {UAI$S_DEFDEV+1,   UAI$_DEFDEV, &defdev,   &ldefdev},
11466         {UAI$S_DEFDIR+1,   UAI$_DEFDIR, &defdir,   &ldefdir},
11467         {UAI$S_DEFCLI+1,   UAI$_DEFCLI, &defcli,   &ldefcli},
11468         {UAI$S_PWD,        UAI$_PWD,    pw_passwd, &lpwd},
11469         {0,                0,           NULL,    NULL}};
11470
11471     name_desc.dsc$w_length=  strlen(name);
11472     name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11473     name_desc.dsc$b_class=   DSC$K_CLASS_S;
11474     name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11475
11476 /*  Note that sys$getuai returns many fields as counted strings. */
11477     sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11478     if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11479       set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11480     }
11481     else { _ckvmssts(sts); }
11482     if (!(sts & 1)) return 0;  /* out here in case _ckvmssts() doesn't abort */
11483
11484     if ((int) owner.length  < lowner)  lowner=  (int) owner.length;
11485     if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11486     if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11487     if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11488     memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11489     owner.pw_gecos[lowner]=            '\0';
11490     defdev.pw_dir[ldefdev+ldefdir]= '\0';
11491     defcli.pw_shell[ldefcli]=          '\0';
11492     if (valid_uic(uic)) {
11493         pwd->pw_uid= uic.uic$l_uic;
11494         pwd->pw_gid= uic.uic$v_group;
11495     }
11496     else
11497       Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11498     pwd->pw_passwd=  pw_passwd;
11499     pwd->pw_gecos=   owner.pw_gecos;
11500     pwd->pw_dir=     defdev.pw_dir;
11501     pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11502     pwd->pw_shell=   defcli.pw_shell;
11503     if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11504         int ldir;
11505         ldir= strlen(pwd->pw_unixdir) - 1;
11506         if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11507     }
11508     else
11509         strcpy(pwd->pw_unixdir, pwd->pw_dir);
11510     if (!decc_efs_case_preserve)
11511         __mystrtolower(pwd->pw_unixdir);
11512     return 1;
11513 }
11514
11515 /*
11516  * Get information for a named user.
11517 */
11518 /*{{{struct passwd *getpwnam(char *name)*/
11519 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11520 {
11521     struct dsc$descriptor_s name_desc;
11522     union uicdef uic;
11523     unsigned long int status, sts;
11524                                   
11525     __pwdcache = __passwd_empty;
11526     if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11527       /* We still may be able to determine pw_uid and pw_gid */
11528       name_desc.dsc$w_length=  strlen(name);
11529       name_desc.dsc$b_dtype=   DSC$K_DTYPE_T;
11530       name_desc.dsc$b_class=   DSC$K_CLASS_S;
11531       name_desc.dsc$a_pointer= (char *) name;
11532       if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11533         __pwdcache.pw_uid= uic.uic$l_uic;
11534         __pwdcache.pw_gid= uic.uic$v_group;
11535       }
11536       else {
11537         if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11538           set_vaxc_errno(sts);
11539           set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11540           return NULL;
11541         }
11542         else { _ckvmssts(sts); }
11543       }
11544     }
11545     strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11546     __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11547     __pwdcache.pw_name= __pw_namecache;
11548     return &__pwdcache;
11549 }  /* end of my_getpwnam() */
11550 /*}}}*/
11551
11552 /*
11553  * Get information for a particular UIC or UID.
11554  * Called by my_getpwent with uid=-1 to list all users.
11555 */
11556 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11557 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11558 {
11559     const $DESCRIPTOR(name_desc,__pw_namecache);
11560     unsigned short lname;
11561     union uicdef uic;
11562     unsigned long int status;
11563
11564     if (uid == (unsigned int) -1) {
11565       do {
11566         status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11567         if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11568           set_vaxc_errno(status);
11569           set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11570           my_endpwent();
11571           return NULL;
11572         }
11573         else { _ckvmssts(status); }
11574       } while (!valid_uic (uic));
11575     }
11576     else {
11577       uic.uic$l_uic= uid;
11578       if (!uic.uic$v_group)
11579         uic.uic$v_group= PerlProc_getgid();
11580       if (valid_uic(uic))
11581         status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11582       else status = SS$_IVIDENT;
11583       if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11584           status == RMS$_PRV) {
11585         set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11586         return NULL;
11587       }
11588       else { _ckvmssts(status); }
11589     }
11590     __pw_namecache[lname]= '\0';
11591     __mystrtolower(__pw_namecache);
11592
11593     __pwdcache = __passwd_empty;
11594     __pwdcache.pw_name = __pw_namecache;
11595
11596 /*  Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11597     The identifier's value is usually the UIC, but it doesn't have to be,
11598     so if we can, we let fillpasswd update this. */
11599     __pwdcache.pw_uid =  uic.uic$l_uic;
11600     __pwdcache.pw_gid =  uic.uic$v_group;
11601
11602     fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11603     return &__pwdcache;
11604
11605 }  /* end of my_getpwuid() */
11606 /*}}}*/
11607
11608 /*
11609  * Get information for next user.
11610 */
11611 /*{{{struct passwd *my_getpwent()*/
11612 struct passwd *Perl_my_getpwent(pTHX)
11613 {
11614     return (my_getpwuid((unsigned int) -1));
11615 }
11616 /*}}}*/
11617
11618 /*
11619  * Finish searching rights database for users.
11620 */
11621 /*{{{void my_endpwent()*/
11622 void Perl_my_endpwent(pTHX)
11623 {
11624     if (contxt) {
11625       _ckvmssts(sys$finish_rdb(&contxt));
11626       contxt= 0;
11627     }
11628 }
11629 /*}}}*/
11630
11631 #ifdef HOMEGROWN_POSIX_SIGNALS
11632   /* Signal handling routines, pulled into the core from POSIX.xs.
11633    *
11634    * We need these for threads, so they've been rolled into the core,
11635    * rather than left in POSIX.xs.
11636    *
11637    * (DRS, Oct 23, 1997)
11638    */
11639
11640   /* sigset_t is atomic under VMS, so these routines are easy */
11641 /*{{{int my_sigemptyset(sigset_t *) */
11642 int my_sigemptyset(sigset_t *set) {
11643     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11644     *set = 0; return 0;
11645 }
11646 /*}}}*/
11647
11648
11649 /*{{{int my_sigfillset(sigset_t *)*/
11650 int my_sigfillset(sigset_t *set) {
11651     int i;
11652     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11653     for (i = 0; i < NSIG; i++) *set |= (1 << i);
11654     return 0;
11655 }
11656 /*}}}*/
11657
11658
11659 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11660 int my_sigaddset(sigset_t *set, int sig) {
11661     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11662     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11663     *set |= (1 << (sig - 1));
11664     return 0;
11665 }
11666 /*}}}*/
11667
11668
11669 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11670 int my_sigdelset(sigset_t *set, int sig) {
11671     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11672     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11673     *set &= ~(1 << (sig - 1));
11674     return 0;
11675 }
11676 /*}}}*/
11677
11678
11679 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11680 int my_sigismember(sigset_t *set, int sig) {
11681     if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11682     if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11683     return *set & (1 << (sig - 1));
11684 }
11685 /*}}}*/
11686
11687
11688 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11689 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11690     sigset_t tempmask;
11691
11692     /* If set and oset are both null, then things are badly wrong. Bail out. */
11693     if ((oset == NULL) && (set == NULL)) {
11694       set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11695       return -1;
11696     }
11697
11698     /* If set's null, then we're just handling a fetch. */
11699     if (set == NULL) {
11700         tempmask = sigblock(0);
11701     }
11702     else {
11703       switch (how) {
11704       case SIG_SETMASK:
11705         tempmask = sigsetmask(*set);
11706         break;
11707       case SIG_BLOCK:
11708         tempmask = sigblock(*set);
11709         break;
11710       case SIG_UNBLOCK:
11711         tempmask = sigblock(0);
11712         sigsetmask(*oset & ~tempmask);
11713         break;
11714       default:
11715         set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11716         return -1;
11717       }
11718     }
11719
11720     /* Did they pass us an oset? If so, stick our holding mask into it */
11721     if (oset)
11722       *oset = tempmask;
11723   
11724     return 0;
11725 }
11726 /*}}}*/
11727 #endif  /* HOMEGROWN_POSIX_SIGNALS */
11728
11729
11730 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11731  * my_utime(), and flex_stat(), all of which operate on UTC unless
11732  * VMSISH_TIMES is true.
11733  */
11734 /* method used to handle UTC conversions:
11735  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
11736  */
11737 static int gmtime_emulation_type;
11738 /* number of secs to add to UTC POSIX-style time to get local time */
11739 static long int utc_offset_secs;
11740
11741 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11742  * in vmsish.h.  #undef them here so we can call the CRTL routines
11743  * directly.
11744  */
11745 #undef gmtime
11746 #undef localtime
11747 #undef time
11748
11749
11750 /*
11751  * DEC C previous to 6.0 corrupts the behavior of the /prefix
11752  * qualifier with the extern prefix pragma.  This provisional
11753  * hack circumvents this prefix pragma problem in previous 
11754  * precompilers.
11755  */
11756 #if defined(__VMS_VER) && __VMS_VER >= 70000000 
11757 #  if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11758 #    pragma __extern_prefix save
11759 #    pragma __extern_prefix ""  /* set to empty to prevent prefixing */
11760 #    define gmtime decc$__utctz_gmtime
11761 #    define localtime decc$__utctz_localtime
11762 #    define time decc$__utc_time
11763 #    pragma __extern_prefix restore
11764
11765      struct tm *gmtime(), *localtime();   
11766
11767 #  endif
11768 #endif
11769
11770
11771 static time_t toutc_dst(time_t loc) {
11772   struct tm *rsltmp;
11773
11774   if ((rsltmp = localtime(&loc)) == NULL) return -1;
11775   loc -= utc_offset_secs;
11776   if (rsltmp->tm_isdst) loc -= 3600;
11777   return loc;
11778 }
11779 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11780        ((gmtime_emulation_type || my_time(NULL)), \
11781        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11782        ((secs) - utc_offset_secs))))
11783
11784 static time_t toloc_dst(time_t utc) {
11785   struct tm *rsltmp;
11786
11787   utc += utc_offset_secs;
11788   if ((rsltmp = localtime(&utc)) == NULL) return -1;
11789   if (rsltmp->tm_isdst) utc += 3600;
11790   return utc;
11791 }
11792 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
11793        ((gmtime_emulation_type || my_time(NULL)), \
11794        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11795        ((secs) + utc_offset_secs))))
11796
11797 #ifndef RTL_USES_UTC
11798 /*
11799   
11800     ucx$tz = "EST5EDT4,M4.1.0,M10.5.0"  typical 
11801         DST starts on 1st sun of april      at 02:00  std time
11802             ends on last sun of october     at 02:00  dst time
11803     see the UCX management command reference, SET CONFIG TIMEZONE
11804     for formatting info.
11805
11806     No, it's not as general as it should be, but then again, NOTHING
11807     will handle UK times in a sensible way. 
11808 */
11809
11810
11811 /* 
11812     parse the DST start/end info:
11813     (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11814 */
11815
11816 static char *
11817 tz_parse_startend(char *s, struct tm *w, int *past)
11818 {
11819     int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11820     int ly, dozjd, d, m, n, hour, min, sec, j, k;
11821     time_t g;
11822
11823     if (!s)    return 0;
11824     if (!w) return 0;
11825     if (!past) return 0;
11826
11827     ly = 0;
11828     if (w->tm_year % 4        == 0) ly = 1;
11829     if (w->tm_year % 100      == 0) ly = 0;
11830     if (w->tm_year+1900 % 400 == 0) ly = 1;
11831     if (ly) dinm[1]++;
11832
11833     dozjd = isdigit(*s);
11834     if (*s == 'J' || *s == 'j' || dozjd) {
11835         if (!dozjd && !isdigit(*++s)) return 0;
11836         d = *s++ - '0';
11837         if (isdigit(*s)) {
11838             d = d*10 + *s++ - '0';
11839             if (isdigit(*s)) {
11840                 d = d*10 + *s++ - '0';
11841             }
11842         }
11843         if (d == 0) return 0;
11844         if (d > 366) return 0;
11845         d--;
11846         if (!dozjd && d > 58 && ly) d++;  /* after 28 feb */
11847         g = d * 86400;
11848         dozjd = 1;
11849     } else if (*s == 'M' || *s == 'm') {
11850         if (!isdigit(*++s)) return 0;
11851         m = *s++ - '0';
11852         if (isdigit(*s)) m = 10*m + *s++ - '0';
11853         if (*s != '.') return 0;
11854         if (!isdigit(*++s)) return 0;
11855         n = *s++ - '0';
11856         if (n < 1 || n > 5) return 0;
11857         if (*s != '.') return 0;
11858         if (!isdigit(*++s)) return 0;
11859         d = *s++ - '0';
11860         if (d > 6) return 0;
11861     }
11862
11863     if (*s == '/') {
11864         if (!isdigit(*++s)) return 0;
11865         hour = *s++ - '0';
11866         if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11867         if (*s == ':') {
11868             if (!isdigit(*++s)) return 0;
11869             min = *s++ - '0';
11870             if (isdigit(*s)) min = 10*min + *s++ - '0';
11871             if (*s == ':') {
11872                 if (!isdigit(*++s)) return 0;
11873                 sec = *s++ - '0';
11874                 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11875             }
11876         }
11877     } else {
11878         hour = 2;
11879         min = 0;
11880         sec = 0;
11881     }
11882
11883     if (dozjd) {
11884         if (w->tm_yday < d) goto before;
11885         if (w->tm_yday > d) goto after;
11886     } else {
11887         if (w->tm_mon+1 < m) goto before;
11888         if (w->tm_mon+1 > m) goto after;
11889
11890         j = (42 + w->tm_wday - w->tm_mday)%7;   /*dow of mday 0 */
11891         k = d - j; /* mday of first d */
11892         if (k <= 0) k += 7;
11893         k += 7 * ((n>4?4:n)-1);  /* mday of n'th d */
11894         if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11895         if (w->tm_mday < k) goto before;
11896         if (w->tm_mday > k) goto after;
11897     }
11898
11899     if (w->tm_hour < hour) goto before;
11900     if (w->tm_hour > hour) goto after;
11901     if (w->tm_min  < min)  goto before;
11902     if (w->tm_min  > min)  goto after;
11903     if (w->tm_sec  < sec)  goto before;
11904     goto after;
11905
11906 before:
11907     *past = 0;
11908     return s;
11909 after:
11910     *past = 1;
11911     return s;
11912 }
11913
11914
11915
11916
11917 /*  parse the offset:   (+|-)hh[:mm[:ss]]  */
11918
11919 static char *
11920 tz_parse_offset(char *s, int *offset)
11921 {
11922     int hour = 0, min = 0, sec = 0;
11923     int neg = 0;
11924     if (!s) return 0;
11925     if (!offset) return 0;
11926
11927     if (*s == '-') {neg++; s++;}
11928     if (*s == '+') s++;
11929     if (!isdigit(*s)) return 0;
11930     hour = *s++ - '0';
11931     if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11932     if (hour > 24) return 0;
11933     if (*s == ':') {
11934         if (!isdigit(*++s)) return 0;
11935         min = *s++ - '0';
11936         if (isdigit(*s)) min = min*10 + (*s++ - '0');
11937         if (min > 59) return 0;
11938         if (*s == ':') {
11939             if (!isdigit(*++s)) return 0;
11940             sec = *s++ - '0';
11941             if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11942             if (sec > 59) return 0;
11943         }
11944     }
11945
11946     *offset = (hour*60+min)*60 + sec;
11947     if (neg) *offset = -*offset;
11948     return s;
11949 }
11950
11951 /*
11952     input time is w, whatever type of time the CRTL localtime() uses.
11953     sets dst, the zone, and the gmtoff (seconds)
11954
11955     caches the value of TZ and UCX$TZ env variables; note that 
11956     my_setenv looks for these and sets a flag if they're changed
11957     for efficiency. 
11958
11959     We have to watch out for the "australian" case (dst starts in
11960     october, ends in april)...flagged by "reverse" and checked by
11961     scanning through the months of the previous year.
11962
11963 */
11964
11965 static int
11966 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11967 {
11968     time_t when;
11969     struct tm *w2;
11970     char *s,*s2;
11971     char *dstzone, *tz, *s_start, *s_end;
11972     int std_off, dst_off, isdst;
11973     int y, dststart, dstend;
11974     static char envtz[1025];  /* longer than any logical, symbol, ... */
11975     static char ucxtz[1025];
11976     static char reversed = 0;
11977
11978     if (!w) return 0;
11979
11980     if (tz_updated) {
11981         tz_updated = 0;
11982         reversed = -1;  /* flag need to check  */
11983         envtz[0] = ucxtz[0] = '\0';
11984         tz = my_getenv("TZ",0);
11985         if (tz) strcpy(envtz, tz);
11986         tz = my_getenv("UCX$TZ",0);
11987         if (tz) strcpy(ucxtz, tz);
11988         if (!envtz[0] && !ucxtz[0]) return 0;  /* we give up */
11989     }
11990     tz = envtz;
11991     if (!*tz) tz = ucxtz;
11992
11993     s = tz;
11994     while (isalpha(*s)) s++;
11995     s = tz_parse_offset(s, &std_off);
11996     if (!s) return 0;
11997     if (!*s) {                  /* no DST, hurray we're done! */
11998         isdst = 0;
11999         goto done;
12000     }
12001
12002     dstzone = s;
12003     while (isalpha(*s)) s++;
12004     s2 = tz_parse_offset(s, &dst_off);
12005     if (s2) {
12006         s = s2;
12007     } else {
12008         dst_off = std_off - 3600;
12009     }
12010
12011     if (!*s) {      /* default dst start/end?? */
12012         if (tz != ucxtz) {          /* if TZ tells zone only, UCX$TZ tells rule */
12013             s = strchr(ucxtz,',');
12014         }
12015         if (!s || !*s) s = ",M4.1.0,M10.5.0";   /* we know we do dst, default rule */
12016     }
12017     if (*s != ',') return 0;
12018
12019     when = *w;
12020     when = _toutc(when);      /* convert to utc */
12021     when = when - std_off;    /* convert to pseudolocal time*/
12022
12023     w2 = localtime(&when);
12024     y = w2->tm_year;
12025     s_start = s+1;
12026     s = tz_parse_startend(s_start,w2,&dststart);
12027     if (!s) return 0;
12028     if (*s != ',') return 0;
12029
12030     when = *w;
12031     when = _toutc(when);      /* convert to utc */
12032     when = when - dst_off;    /* convert to pseudolocal time*/
12033     w2 = localtime(&when);
12034     if (w2->tm_year != y) {   /* spans a year, just check one time */
12035         when += dst_off - std_off;
12036         w2 = localtime(&when);
12037     }
12038     s_end = s+1;
12039     s = tz_parse_startend(s_end,w2,&dstend);
12040     if (!s) return 0;
12041
12042     if (reversed == -1) {  /* need to check if start later than end */
12043         int j, ds, de;
12044
12045         when = *w;
12046         if (when < 2*365*86400) {
12047             when += 2*365*86400;
12048         } else {
12049             when -= 365*86400;
12050         }
12051         w2 =localtime(&when);
12052         when = when + (15 - w2->tm_yday) * 86400;   /* jan 15 */
12053
12054         for (j = 0; j < 12; j++) {
12055             w2 =localtime(&when);
12056             tz_parse_startend(s_start,w2,&ds);
12057             tz_parse_startend(s_end,w2,&de);
12058             if (ds != de) break;
12059             when += 30*86400;
12060         }
12061         reversed = 0;
12062         if (de && !ds) reversed = 1;
12063     }
12064
12065     isdst = dststart && !dstend;
12066     if (reversed) isdst = dststart  || !dstend;
12067
12068 done:
12069     if (dst)    *dst = isdst;
12070     if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
12071     if (isdst)  tz = dstzone;
12072     if (zone) {
12073         while(isalpha(*tz))  *zone++ = *tz++;
12074         *zone = '\0';
12075     }
12076     return 1;
12077 }
12078
12079 #endif /* !RTL_USES_UTC */
12080
12081 /* my_time(), my_localtime(), my_gmtime()
12082  * By default traffic in UTC time values, using CRTL gmtime() or
12083  * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
12084  * Note: We need to use these functions even when the CRTL has working
12085  * UTC support, since they also handle C<use vmsish qw(times);>
12086  *
12087  * Contributed by Chuck Lane  <lane@duphy4.physics.drexel.edu>
12088  * Modified by Charles Bailey <bailey@newman.upenn.edu>
12089  */
12090
12091 /*{{{time_t my_time(time_t *timep)*/
12092 time_t Perl_my_time(pTHX_ time_t *timep)
12093 {
12094   time_t when;
12095   struct tm *tm_p;
12096
12097   if (gmtime_emulation_type == 0) {
12098     int dstnow;
12099     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
12100                               /* results of calls to gmtime() and localtime() */
12101                               /* for same &base */
12102
12103     gmtime_emulation_type++;
12104     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
12105       char off[LNM$C_NAMLENGTH+1];;
12106
12107       gmtime_emulation_type++;
12108       if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
12109         gmtime_emulation_type++;
12110         utc_offset_secs = 0;
12111         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
12112       }
12113       else { utc_offset_secs = atol(off); }
12114     }
12115     else { /* We've got a working gmtime() */
12116       struct tm gmt, local;
12117
12118       gmt = *tm_p;
12119       tm_p = localtime(&base);
12120       local = *tm_p;
12121       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
12122       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12123       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
12124       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
12125     }
12126   }
12127
12128   when = time(NULL);
12129 # ifdef VMSISH_TIME
12130 # ifdef RTL_USES_UTC
12131   if (VMSISH_TIME) when = _toloc(when);
12132 # else
12133   if (!VMSISH_TIME) when = _toutc(when);
12134 # endif
12135 # endif
12136   if (timep != NULL) *timep = when;
12137   return when;
12138
12139 }  /* end of my_time() */
12140 /*}}}*/
12141
12142
12143 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12144 struct tm *
12145 Perl_my_gmtime(pTHX_ const time_t *timep)
12146 {
12147   char *p;
12148   time_t when;
12149   struct tm *rsltmp;
12150
12151   if (timep == NULL) {
12152     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12153     return NULL;
12154   }
12155   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
12156
12157   when = *timep;
12158 # ifdef VMSISH_TIME
12159   if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12160 #  endif
12161 # ifdef RTL_USES_UTC  /* this implies that the CRTL has a working gmtime() */
12162   return gmtime(&when);
12163 # else
12164   /* CRTL localtime() wants local time as input, so does no tz correction */
12165   rsltmp = localtime(&when);
12166   if (rsltmp) rsltmp->tm_isdst = 0;  /* We already took DST into account */
12167   return rsltmp;
12168 #endif
12169 }  /* end of my_gmtime() */
12170 /*}}}*/
12171
12172
12173 /*{{{struct tm *my_localtime(const time_t *timep)*/
12174 struct tm *
12175 Perl_my_localtime(pTHX_ const time_t *timep)
12176 {
12177   time_t when, whenutc;
12178   struct tm *rsltmp;
12179   int dst, offset;
12180
12181   if (timep == NULL) {
12182     set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12183     return NULL;
12184   }
12185   if (*timep == 0) gmtime_emulation_type = 0;  /* possibly reset TZ */
12186   if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12187
12188   when = *timep;
12189 # ifdef RTL_USES_UTC
12190 # ifdef VMSISH_TIME
12191   if (VMSISH_TIME) when = _toutc(when);
12192 # endif
12193   /* CRTL localtime() wants UTC as input, does tz correction itself */
12194   return localtime(&when);
12195   
12196 # else /* !RTL_USES_UTC */
12197   whenutc = when;
12198 # ifdef VMSISH_TIME
12199   if (!VMSISH_TIME) when = _toloc(whenutc);  /*  input was UTC */
12200   if (VMSISH_TIME) whenutc = _toutc(when);   /*  input was truelocal */
12201 # endif
12202   dst = -1;
12203 #ifndef RTL_USES_UTC
12204   if (tz_parse(aTHX_ &when, &dst, 0, &offset)) {   /* truelocal determines DST*/
12205       when = whenutc - offset;                   /* pseudolocal time*/
12206   }
12207 # endif
12208   /* CRTL localtime() wants local time as input, so does no tz correction */
12209   rsltmp = localtime(&when);
12210   if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12211   return rsltmp;
12212 # endif
12213
12214 } /*  end of my_localtime() */
12215 /*}}}*/
12216
12217 /* Reset definitions for later calls */
12218 #define gmtime(t)    my_gmtime(t)
12219 #define localtime(t) my_localtime(t)
12220 #define time(t)      my_time(t)
12221
12222
12223 /* my_utime - update modification/access time of a file
12224  *
12225  * VMS 7.3 and later implementation
12226  * Only the UTC translation is home-grown. The rest is handled by the
12227  * CRTL utime(), which will take into account the relevant feature
12228  * logicals and ODS-5 volume characteristics for true access times.
12229  *
12230  * pre VMS 7.3 implementation:
12231  * The calling sequence is identical to POSIX utime(), but under
12232  * VMS with ODS-2, only the modification time is changed; ODS-2 does
12233  * not maintain access times.  Restrictions differ from the POSIX
12234  * definition in that the time can be changed as long as the
12235  * caller has permission to execute the necessary IO$_MODIFY $QIO;
12236  * no separate checks are made to insure that the caller is the
12237  * owner of the file or has special privs enabled.
12238  * Code here is based on Joe Meadows' FILE utility.
12239  *
12240  */
12241
12242 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12243  *              to VMS epoch  (01-JAN-1858 00:00:00.00)
12244  * in 100 ns intervals.
12245  */
12246 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12247
12248 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12249 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12250 {
12251 #if __CRTL_VER >= 70300000
12252   struct utimbuf utc_utimes, *utc_utimesp;
12253
12254   if (utimes != NULL) {
12255     utc_utimes.actime = utimes->actime;
12256     utc_utimes.modtime = utimes->modtime;
12257 # ifdef VMSISH_TIME
12258     /* If input was local; convert to UTC for sys svc */
12259     if (VMSISH_TIME) {
12260       utc_utimes.actime = _toutc(utimes->actime);
12261       utc_utimes.modtime = _toutc(utimes->modtime);
12262     }
12263 # endif
12264     utc_utimesp = &utc_utimes;
12265   }
12266   else {
12267     utc_utimesp = NULL;
12268   }
12269
12270   return utime(file, utc_utimesp);
12271
12272 #else /* __CRTL_VER < 70300000 */
12273
12274   register int i;
12275   int sts;
12276   long int bintime[2], len = 2, lowbit, unixtime,
12277            secscale = 10000000; /* seconds --> 100 ns intervals */
12278   unsigned long int chan, iosb[2], retsts;
12279   char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12280   struct FAB myfab = cc$rms_fab;
12281   struct NAM mynam = cc$rms_nam;
12282 #if defined (__DECC) && defined (__VAX)
12283   /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12284    * at least through VMS V6.1, which causes a type-conversion warning.
12285    */
12286 #  pragma message save
12287 #  pragma message disable cvtdiftypes
12288 #endif
12289   struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12290   struct fibdef myfib;
12291 #if defined (__DECC) && defined (__VAX)
12292   /* This should be right after the declaration of myatr, but due
12293    * to a bug in VAX DEC C, this takes effect a statement early.
12294    */
12295 #  pragma message restore
12296 #endif
12297   /* cast ok for read only parameter */
12298   struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12299                         devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12300                         fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12301         
12302   if (file == NULL || *file == '\0') {
12303     SETERRNO(ENOENT, LIB$_INVARG);
12304     return -1;
12305   }
12306
12307   /* Convert to VMS format ensuring that it will fit in 255 characters */
12308   if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12309       SETERRNO(ENOENT, LIB$_INVARG);
12310       return -1;
12311   }
12312   if (utimes != NULL) {
12313     /* Convert Unix time    (seconds since 01-JAN-1970 00:00:00.00)
12314      * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12315      * Since time_t is unsigned long int, and lib$emul takes a signed long int
12316      * as input, we force the sign bit to be clear by shifting unixtime right
12317      * one bit, then multiplying by an extra factor of 2 in lib$emul().
12318      */
12319     lowbit = (utimes->modtime & 1) ? secscale : 0;
12320     unixtime = (long int) utimes->modtime;
12321 #   ifdef VMSISH_TIME
12322     /* If input was UTC; convert to local for sys svc */
12323     if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12324 #   endif
12325     unixtime >>= 1;  secscale <<= 1;
12326     retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12327     if (!(retsts & 1)) {
12328       SETERRNO(EVMSERR, retsts);
12329       return -1;
12330     }
12331     retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12332     if (!(retsts & 1)) {
12333       SETERRNO(EVMSERR, retsts);
12334       return -1;
12335     }
12336   }
12337   else {
12338     /* Just get the current time in VMS format directly */
12339     retsts = sys$gettim(bintime);
12340     if (!(retsts & 1)) {
12341       SETERRNO(EVMSERR, retsts);
12342       return -1;
12343     }
12344   }
12345
12346   myfab.fab$l_fna = vmsspec;
12347   myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12348   myfab.fab$l_nam = &mynam;
12349   mynam.nam$l_esa = esa;
12350   mynam.nam$b_ess = (unsigned char) sizeof esa;
12351   mynam.nam$l_rsa = rsa;
12352   mynam.nam$b_rss = (unsigned char) sizeof rsa;
12353   if (decc_efs_case_preserve)
12354       mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12355
12356   /* Look for the file to be affected, letting RMS parse the file
12357    * specification for us as well.  I have set errno using only
12358    * values documented in the utime() man page for VMS POSIX.
12359    */
12360   retsts = sys$parse(&myfab,0,0);
12361   if (!(retsts & 1)) {
12362     set_vaxc_errno(retsts);
12363     if      (retsts == RMS$_PRV) set_errno(EACCES);
12364     else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12365     else                         set_errno(EVMSERR);
12366     return -1;
12367   }
12368   retsts = sys$search(&myfab,0,0);
12369   if (!(retsts & 1)) {
12370     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12371     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12372     set_vaxc_errno(retsts);
12373     if      (retsts == RMS$_PRV) set_errno(EACCES);
12374     else if (retsts == RMS$_FNF) set_errno(ENOENT);
12375     else                         set_errno(EVMSERR);
12376     return -1;
12377   }
12378
12379   devdsc.dsc$w_length = mynam.nam$b_dev;
12380   /* cast ok for read only parameter */
12381   devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12382
12383   retsts = sys$assign(&devdsc,&chan,0,0);
12384   if (!(retsts & 1)) {
12385     mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12386     myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12387     set_vaxc_errno(retsts);
12388     if      (retsts == SS$_IVDEVNAM)   set_errno(ENOTDIR);
12389     else if (retsts == SS$_NOPRIV)     set_errno(EACCES);
12390     else if (retsts == SS$_NOSUCHDEV)  set_errno(ENOTDIR);
12391     else                               set_errno(EVMSERR);
12392     return -1;
12393   }
12394
12395   fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12396   fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12397
12398   memset((void *) &myfib, 0, sizeof myfib);
12399 #if defined(__DECC) || defined(__DECCXX)
12400   for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12401   for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12402   /* This prevents the revision time of the file being reset to the current
12403    * time as a result of our IO$_MODIFY $QIO. */
12404   myfib.fib$l_acctl = FIB$M_NORECORD;
12405 #else
12406   for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12407   for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12408   myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12409 #endif
12410   retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12411   mynam.nam$b_nop |= NAM$M_SYNCHK;  mynam.nam$l_rlf = NULL;
12412   myfab.fab$b_dns = 0;  sts = sys$parse(&myfab,0,0);
12413   _ckvmssts(sys$dassgn(chan));
12414   if (retsts & 1) retsts = iosb[0];
12415   if (!(retsts & 1)) {
12416     set_vaxc_errno(retsts);
12417     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12418     else                      set_errno(EVMSERR);
12419     return -1;
12420   }
12421
12422   return 0;
12423
12424 #endif /* #if __CRTL_VER >= 70300000 */
12425
12426 }  /* end of my_utime() */
12427 /*}}}*/
12428
12429 /*
12430  * flex_stat, flex_lstat, flex_fstat
12431  * basic stat, but gets it right when asked to stat
12432  * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12433  */
12434
12435 #ifndef _USE_STD_STAT
12436 /* encode_dev packs a VMS device name string into an integer to allow
12437  * simple comparisons. This can be used, for example, to check whether two
12438  * files are located on the same device, by comparing their encoded device
12439  * names. Even a string comparison would not do, because stat() reuses the
12440  * device name buffer for each call; so without encode_dev, it would be
12441  * necessary to save the buffer and use strcmp (this would mean a number of
12442  * changes to the standard Perl code, to say nothing of what a Perl script
12443  * would have to do.
12444  *
12445  * The device lock id, if it exists, should be unique (unless perhaps compared
12446  * with lock ids transferred from other nodes). We have a lock id if the disk is
12447  * mounted cluster-wide, which is when we tend to get long (host-qualified)
12448  * device names. Thus we use the lock id in preference, and only if that isn't
12449  * available, do we try to pack the device name into an integer (flagged by
12450  * the sign bit (LOCKID_MASK) being set).
12451  *
12452  * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12453  * name and its encoded form, but it seems very unlikely that we will find
12454  * two files on different disks that share the same encoded device names,
12455  * and even more remote that they will share the same file id (if the test
12456  * is to check for the same file).
12457  *
12458  * A better method might be to use sys$device_scan on the first call, and to
12459  * search for the device, returning an index into the cached array.
12460  * The number returned would be more intelligible.
12461  * This is probably not worth it, and anyway would take quite a bit longer
12462  * on the first call.
12463  */
12464 #define LOCKID_MASK 0x80000000     /* Use 0 to force device name use only */
12465 static mydev_t encode_dev (pTHX_ const char *dev)
12466 {
12467   int i;
12468   unsigned long int f;
12469   mydev_t enc;
12470   char c;
12471   const char *q;
12472
12473   if (!dev || !dev[0]) return 0;
12474
12475 #if LOCKID_MASK
12476   {
12477     struct dsc$descriptor_s dev_desc;
12478     unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12479
12480     /* For cluster-mounted disks, the disk lock identifier is unique, so we
12481        can try that first. */
12482     dev_desc.dsc$w_length =  strlen (dev);
12483     dev_desc.dsc$b_dtype =   DSC$K_DTYPE_T;
12484     dev_desc.dsc$b_class =   DSC$K_CLASS_S;
12485     dev_desc.dsc$a_pointer = (char *) dev;  /* Read only parameter */
12486     status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12487     if (!$VMS_STATUS_SUCCESS(status)) {
12488       switch (status) {
12489         case SS$_NOSUCHDEV: 
12490           SETERRNO(ENODEV, status);
12491           return 0;
12492         default: 
12493           _ckvmssts(status);
12494       }
12495     }
12496     if (lockid) return (lockid & ~LOCKID_MASK);
12497   }
12498 #endif
12499
12500   /* Otherwise we try to encode the device name */
12501   enc = 0;
12502   f = 1;
12503   i = 0;
12504   for (q = dev + strlen(dev); q--; q >= dev) {
12505     if (*q == ':')
12506         break;
12507     if (isdigit (*q))
12508       c= (*q) - '0';
12509     else if (isalpha (toupper (*q)))
12510       c= toupper (*q) - 'A' + (char)10;
12511     else
12512       continue; /* Skip '$'s */
12513     i++;
12514     if (i>6) break;     /* 36^7 is too large to fit in an unsigned long int */
12515     if (i>1) f *= 36;
12516     enc += f * (unsigned long int) c;
12517   }
12518   return (enc | LOCKID_MASK);  /* May have already overflowed into bit 31 */
12519
12520 }  /* end of encode_dev() */
12521 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12522         device_no = encode_dev(aTHX_ devname)
12523 #else
12524 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12525         device_no = new_dev_no
12526 #endif
12527
12528 static int
12529 is_null_device(name)
12530     const char *name;
12531 {
12532   if (decc_bug_devnull != 0) {
12533     if (strncmp("/dev/null", name, 9) == 0)
12534       return 1;
12535   }
12536     /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12537        The underscore prefix, controller letter, and unit number are
12538        independently optional; for our purposes, the colon punctuation
12539        is not.  The colon can be trailed by optional directory and/or
12540        filename, but two consecutive colons indicates a nodename rather
12541        than a device.  [pr]  */
12542   if (*name == '_') ++name;
12543   if (tolower(*name++) != 'n') return 0;
12544   if (tolower(*name++) != 'l') return 0;
12545   if (tolower(*name) == 'a') ++name;
12546   if (*name == '0') ++name;
12547   return (*name++ == ':') && (*name != ':');
12548 }
12549
12550 static int
12551 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12552
12553 #define flex_stat_int(a,b,c)            Perl_flex_stat_int(aTHX_ a,b,c)
12554
12555 static I32
12556 Perl_cando_by_name_int
12557    (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12558 {
12559   char usrname[L_cuserid];
12560   struct dsc$descriptor_s usrdsc =
12561          {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12562   char *vmsname = NULL, *fileified = NULL;
12563   unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12564   unsigned short int retlen, trnlnm_iter_count;
12565   struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12566   union prvdef curprv;
12567   struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12568          {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12569          {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12570   struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12571          {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12572          {0,0,0,0}};
12573   struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12574          {0,0,0,0}};
12575   struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12576   Stat_t st;
12577   static int profile_context = -1;
12578
12579   if (!fname || !*fname) return FALSE;
12580
12581   /* Make sure we expand logical names, since sys$check_access doesn't */
12582   fileified = PerlMem_malloc(VMS_MAXRSS);
12583   if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12584   if (!strpbrk(fname,"/]>:")) {
12585       strcpy(fileified,fname);
12586       trnlnm_iter_count = 0;
12587       while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12588         trnlnm_iter_count++; 
12589         if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12590       }
12591       fname = fileified;
12592   }
12593
12594   vmsname = PerlMem_malloc(VMS_MAXRSS);
12595   if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12596   if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12597     /* Don't know if already in VMS format, so make sure */
12598     if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12599       PerlMem_free(fileified);
12600       PerlMem_free(vmsname);
12601       return FALSE;
12602     }
12603   }
12604   else {
12605     strcpy(vmsname,fname);
12606   }
12607
12608   /* sys$check_access needs a file spec, not a directory spec.
12609    * flex_stat now will handle a null thread context during startup.
12610    */
12611
12612   retlen = namdsc.dsc$w_length = strlen(vmsname);
12613   if (vmsname[retlen-1] == ']' 
12614       || vmsname[retlen-1] == '>' 
12615       || vmsname[retlen-1] == ':'
12616       || (!flex_stat_int(vmsname, &st, 1) &&
12617           S_ISDIR(st.st_mode))) {
12618
12619       if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12620         PerlMem_free(fileified);
12621         PerlMem_free(vmsname);
12622         return FALSE;
12623       }
12624       fname = fileified;
12625   }
12626   else {
12627       fname = vmsname;
12628   }
12629
12630   retlen = namdsc.dsc$w_length = strlen(fname);
12631   namdsc.dsc$a_pointer = (char *)fname;
12632
12633   switch (bit) {
12634     case S_IXUSR: case S_IXGRP: case S_IXOTH:
12635       access = ARM$M_EXECUTE;
12636       flags = CHP$M_READ;
12637       break;
12638     case S_IRUSR: case S_IRGRP: case S_IROTH:
12639       access = ARM$M_READ;
12640       flags = CHP$M_READ | CHP$M_USEREADALL;
12641       break;
12642     case S_IWUSR: case S_IWGRP: case S_IWOTH:
12643       access = ARM$M_WRITE;
12644       flags = CHP$M_READ | CHP$M_WRITE;
12645       break;
12646     case S_IDUSR: case S_IDGRP: case S_IDOTH:
12647       access = ARM$M_DELETE;
12648       flags = CHP$M_READ | CHP$M_WRITE;
12649       break;
12650     default:
12651       if (fileified != NULL)
12652         PerlMem_free(fileified);
12653       if (vmsname != NULL)
12654         PerlMem_free(vmsname);
12655       return FALSE;
12656   }
12657
12658   /* Before we call $check_access, create a user profile with the current
12659    * process privs since otherwise it just uses the default privs from the
12660    * UAF and might give false positives or negatives.  This only works on
12661    * VMS versions v6.0 and later since that's when sys$create_user_profile
12662    * became available.
12663    */
12664
12665   /* get current process privs and username */
12666   _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12667   _ckvmssts_noperl(iosb[0]);
12668
12669 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12670
12671   /* find out the space required for the profile */
12672   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12673                                     &usrprodsc.dsc$w_length,&profile_context));
12674
12675   /* allocate space for the profile and get it filled in */
12676   usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12677   if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12678   _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12679                                     &usrprodsc.dsc$w_length,&profile_context));
12680
12681   /* use the profile to check access to the file; free profile & analyze results */
12682   retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12683   PerlMem_free(usrprodsc.dsc$a_pointer);
12684   if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12685
12686 #else
12687
12688   retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12689
12690 #endif
12691
12692   if (retsts == SS$_NOPRIV      || retsts == SS$_NOSUCHOBJECT ||
12693       retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12694       retsts == RMS$_DIR        || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12695     set_vaxc_errno(retsts);
12696     if (retsts == SS$_NOPRIV) set_errno(EACCES);
12697     else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12698     else set_errno(ENOENT);
12699     if (fileified != NULL)
12700       PerlMem_free(fileified);
12701     if (vmsname != NULL)
12702       PerlMem_free(vmsname);
12703     return FALSE;
12704   }
12705   if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12706     if (fileified != NULL)
12707       PerlMem_free(fileified);
12708     if (vmsname != NULL)
12709       PerlMem_free(vmsname);
12710     return TRUE;
12711   }
12712   _ckvmssts_noperl(retsts);
12713
12714   if (fileified != NULL)
12715     PerlMem_free(fileified);
12716   if (vmsname != NULL)
12717     PerlMem_free(vmsname);
12718   return FALSE;  /* Should never get here */
12719
12720 }
12721
12722 /* Do the permissions allow some operation?  Assumes PL_statcache already set. */
12723 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12724  * subset of the applicable information.
12725  */
12726 bool
12727 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12728 {
12729   return cando_by_name_int
12730         (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12731 }  /* end of cando() */
12732 /*}}}*/
12733
12734
12735 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12736 I32
12737 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12738 {
12739    return cando_by_name_int(bit, effective, fname, 0);
12740
12741 }  /* end of cando_by_name() */
12742 /*}}}*/
12743
12744
12745 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12746 int
12747 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12748 {
12749   if (!fstat(fd, &statbufp->crtl_stat)) {
12750     char *cptr;
12751     char *vms_filename;
12752     vms_filename = PerlMem_malloc(VMS_MAXRSS);
12753     if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12754
12755     /* Save name for cando by name in VMS format */
12756     cptr = getname(fd, vms_filename, 1);
12757
12758     /* This should not happen, but just in case */
12759     if (cptr == NULL) {
12760         statbufp->st_devnam[0] = 0;
12761     }
12762     else {
12763         /* Make sure that the saved name fits in 255 characters */
12764         cptr = int_rmsexpand_vms
12765                        (vms_filename,
12766                         statbufp->st_devnam, 
12767                         0);
12768         if (cptr == NULL)
12769             statbufp->st_devnam[0] = 0;
12770     }
12771     PerlMem_free(vms_filename);
12772
12773     VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12774     VMS_DEVICE_ENCODE
12775         (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12776
12777 #   ifdef RTL_USES_UTC
12778 #   ifdef VMSISH_TIME
12779     if (VMSISH_TIME) {
12780       statbufp->st_mtime = _toloc(statbufp->st_mtime);
12781       statbufp->st_atime = _toloc(statbufp->st_atime);
12782       statbufp->st_ctime = _toloc(statbufp->st_ctime);
12783     }
12784 #   endif
12785 #   else
12786 #   ifdef VMSISH_TIME
12787     if (!VMSISH_TIME) { /* Return UTC instead of local time */
12788 #   else
12789     if (1) {
12790 #   endif
12791       statbufp->st_mtime = _toutc(statbufp->st_mtime);
12792       statbufp->st_atime = _toutc(statbufp->st_atime);
12793       statbufp->st_ctime = _toutc(statbufp->st_ctime);
12794     }
12795 #endif
12796     return 0;
12797   }
12798   return -1;
12799
12800 }  /* end of flex_fstat() */
12801 /*}}}*/
12802
12803 static int
12804 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12805 {
12806     char *fileified;
12807     char *temp_fspec;
12808     const char *save_spec;
12809     char *ret_spec;
12810     int retval = -1;
12811     int efs_hack = 0;
12812     dSAVEDERRNO;
12813
12814     if (!fspec) {
12815         errno = EINVAL;
12816         return retval;
12817     }
12818
12819     if (decc_bug_devnull != 0) {
12820       if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12821         memset(statbufp,0,sizeof *statbufp);
12822         VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12823         statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12824         statbufp->st_uid = 0x00010001;
12825         statbufp->st_gid = 0x0001;
12826         time((time_t *)&statbufp->st_mtime);
12827         statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12828         return 0;
12829       }
12830     }
12831
12832     /* Try for a directory name first.  If fspec contains a filename without
12833      * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12834      * and sea:[wine.dark]water. exist, we prefer the directory here.
12835      * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12836      * not sea:[wine.dark]., if the latter exists.  If the intended target is
12837      * the file with null type, specify this by calling flex_stat() with
12838      * a '.' at the end of fspec.
12839      *
12840      * If we are in Posix filespec mode, accept the filename as is.
12841      */
12842
12843
12844     fileified = PerlMem_malloc(VMS_MAXRSS);
12845     if (fileified == NULL)
12846         _ckvmssts_noperl(SS$_INSFMEM);
12847      
12848     temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12849     if (temp_fspec == NULL)
12850         _ckvmssts_noperl(SS$_INSFMEM);
12851
12852     strcpy(temp_fspec, fspec);
12853
12854     SAVE_ERRNO;
12855
12856 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12857   if (decc_posix_compliant_pathnames == 0) {
12858 #endif
12859
12860     /* We may be able to optimize this, but in order for fileify_dirspec to
12861      * always return a usuable answer, we have to call vmspath first to
12862      * make sure that it is in VMS directory format, as stat/lstat on 8.3
12863      * can not handle directories in unix format that it does not have read
12864      * access to.  Vmspath handles the case where a bare name which could be
12865      * a logical name gets passed.
12866      */ 
12867     ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12868     if (ret_spec != NULL) {
12869         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL); 
12870         if (ret_spec != NULL) {
12871             if (lstat_flag == 0)
12872                 retval = stat(fileified, &statbufp->crtl_stat);
12873             else
12874                 retval = lstat(fileified, &statbufp->crtl_stat);
12875             save_spec = fileified;
12876         }
12877     }
12878
12879     if (retval && vms_bug_stat_filename) {
12880
12881         /* We should try again as a vmsified file specification */
12882         /* However Perl traditionally has not done this, which  */
12883         /* causes problems with existing tests */
12884
12885         ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12886         if (ret_spec != NULL) {
12887             if (lstat_flag == 0)
12888                 retval = stat(temp_fspec, &statbufp->crtl_stat);
12889             else
12890                 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12891             save_spec = temp_fspec;
12892         }
12893     }
12894
12895     if (retval) {
12896         /* Last chance - allow multiple dots with out EFS CHARSET */
12897         /* The CRTL stat() falls down hard on multi-dot filenames in unix
12898          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12899          * enable it if it isn't already.
12900          */
12901 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12902         if (!decc_efs_charset && (decc_efs_charset_index > 0))
12903             decc$feature_set_value(decc_efs_charset_index, 1, 1); 
12904 #endif
12905         if (lstat_flag == 0)
12906             retval = stat(fspec, &statbufp->crtl_stat);
12907         else
12908             retval = lstat(fspec, &statbufp->crtl_stat);
12909         save_spec = fspec;
12910 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12911         if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12912             decc$feature_set_value(decc_efs_charset_index, 1, 0); 
12913             efs_hack = 1;
12914         }
12915 #endif
12916     }
12917
12918 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12919   } else {
12920     if (lstat_flag == 0)
12921       retval = stat(temp_fspec, &statbufp->crtl_stat);
12922     else
12923       retval = lstat(temp_fspec, &statbufp->crtl_stat);
12924       save_spec = temp_fspec;
12925   }
12926 #endif
12927
12928 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12929   /* As you were... */
12930   if (!decc_efs_charset)
12931     decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0); 
12932 #endif
12933
12934     if (!retval) {
12935     char * cptr;
12936     int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12937
12938       /* If this is an lstat, do not follow the link */
12939       if (lstat_flag)
12940         rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12941
12942 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12943       /* If we used the efs_hack above, we must also use it here for */
12944       /* perl_cando to work */
12945       if (efs_hack && (decc_efs_charset_index > 0)) {
12946           decc$feature_set_value(decc_efs_charset_index, 1, 1);
12947       }
12948 #endif
12949       cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12950 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12951       if (efs_hack && (decc_efs_charset_index > 0)) {
12952           decc$feature_set_value(decc_efs_charset, 1, 0);
12953       }
12954 #endif
12955
12956       /* Fix me: If this is NULL then stat found a file, and we could */
12957       /* not convert the specification to VMS - Should never happen */
12958       if (cptr == NULL)
12959         statbufp->st_devnam[0] = 0;
12960
12961       VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12962       VMS_DEVICE_ENCODE
12963         (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12964 #     ifdef RTL_USES_UTC
12965 #     ifdef VMSISH_TIME
12966       if (VMSISH_TIME) {
12967         statbufp->st_mtime = _toloc(statbufp->st_mtime);
12968         statbufp->st_atime = _toloc(statbufp->st_atime);
12969         statbufp->st_ctime = _toloc(statbufp->st_ctime);
12970       }
12971 #     endif
12972 #     else
12973 #     ifdef VMSISH_TIME
12974       if (!VMSISH_TIME) { /* Return UTC instead of local time */
12975 #     else
12976       if (1) {
12977 #     endif
12978         statbufp->st_mtime = _toutc(statbufp->st_mtime);
12979         statbufp->st_atime = _toutc(statbufp->st_atime);
12980         statbufp->st_ctime = _toutc(statbufp->st_ctime);
12981       }
12982 #     endif
12983     }
12984     /* If we were successful, leave errno where we found it */
12985     if (retval == 0) RESTORE_ERRNO;
12986     return retval;
12987
12988 }  /* end of flex_stat_int() */
12989
12990
12991 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12992 int
12993 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12994 {
12995    return flex_stat_int(fspec, statbufp, 0);
12996 }
12997 /*}}}*/
12998
12999 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
13000 int
13001 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
13002 {
13003    return flex_stat_int(fspec, statbufp, 1);
13004 }
13005 /*}}}*/
13006
13007
13008 /*{{{char *my_getlogin()*/
13009 /* VMS cuserid == Unix getlogin, except calling sequence */
13010 char *
13011 my_getlogin(void)
13012 {
13013     static char user[L_cuserid];
13014     return cuserid(user);
13015 }
13016 /*}}}*/
13017
13018
13019 /*  rmscopy - copy a file using VMS RMS routines
13020  *
13021  *  Copies contents and attributes of spec_in to spec_out, except owner
13022  *  and protection information.  Name and type of spec_in are used as
13023  *  defaults for spec_out.  The third parameter specifies whether rmscopy()
13024  *  should try to propagate timestamps from the input file to the output file.
13025  *  If it is less than 0, no timestamps are preserved.  If it is 0, then
13026  *  rmscopy() will behave similarly to the DCL COPY command: timestamps are
13027  *  propagated to the output file at creation iff the output file specification
13028  *  did not contain an explicit name or type, and the revision date is always
13029  *  updated at the end of the copy operation.  If it is greater than 0, then
13030  *  it is interpreted as a bitmask, in which bit 0 indicates that timestamps
13031  *  other than the revision date should be propagated, and bit 1 indicates
13032  *  that the revision date should be propagated.
13033  *
13034  *  Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
13035  *
13036  *  Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
13037  *  Incorporates, with permission, some code from EZCOPY by Tim Adye
13038  *  <T.J.Adye@rl.ac.uk>.  Permission is given to distribute this code
13039  * as part of the Perl standard distribution under the terms of the
13040  * GNU General Public License or the Perl Artistic License.  Copies
13041  * of each may be found in the Perl standard distribution.
13042  */ /* FIXME */
13043 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
13044 int
13045 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
13046 {
13047     char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
13048          *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
13049     unsigned long int i, sts, sts2;
13050     int dna_len;
13051     struct FAB fab_in, fab_out;
13052     struct RAB rab_in, rab_out;
13053     rms_setup_nam(nam);
13054     rms_setup_nam(nam_out);
13055     struct XABDAT xabdat;
13056     struct XABFHC xabfhc;
13057     struct XABRDT xabrdt;
13058     struct XABSUM xabsum;
13059
13060     vmsin = PerlMem_malloc(VMS_MAXRSS);
13061     if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13062     vmsout = PerlMem_malloc(VMS_MAXRSS);
13063     if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13064     if (!spec_in  || !*spec_in  || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
13065         !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
13066       PerlMem_free(vmsin);
13067       PerlMem_free(vmsout);
13068       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13069       return 0;
13070     }
13071
13072     esa = PerlMem_malloc(VMS_MAXRSS);
13073     if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13074     esal = NULL;
13075 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13076     esal = PerlMem_malloc(VMS_MAXRSS);
13077     if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13078 #endif
13079     fab_in = cc$rms_fab;
13080     rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
13081     fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
13082     fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
13083     fab_in.fab$l_fop = FAB$M_SQO;
13084     rms_bind_fab_nam(fab_in, nam);
13085     fab_in.fab$l_xab = (void *) &xabdat;
13086
13087     rsa = PerlMem_malloc(VMS_MAXRSS);
13088     if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13089     rsal = NULL;
13090 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13091     rsal = PerlMem_malloc(VMS_MAXRSS);
13092     if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13093 #endif
13094     rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
13095     rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
13096     rms_nam_esl(nam) = 0;
13097     rms_nam_rsl(nam) = 0;
13098     rms_nam_esll(nam) = 0;
13099     rms_nam_rsll(nam) = 0;
13100 #ifdef NAM$M_NO_SHORT_UPCASE
13101     if (decc_efs_case_preserve)
13102         rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
13103 #endif
13104
13105     xabdat = cc$rms_xabdat;        /* To get creation date */
13106     xabdat.xab$l_nxt = (void *) &xabfhc;
13107
13108     xabfhc = cc$rms_xabfhc;        /* To get record length */
13109     xabfhc.xab$l_nxt = (void *) &xabsum;
13110
13111     xabsum = cc$rms_xabsum;        /* To get key and area information */
13112
13113     if (!((sts = sys$open(&fab_in)) & 1)) {
13114       PerlMem_free(vmsin);
13115       PerlMem_free(vmsout);
13116       PerlMem_free(esa);
13117       if (esal != NULL)
13118         PerlMem_free(esal);
13119       PerlMem_free(rsa);
13120       if (rsal != NULL)
13121         PerlMem_free(rsal);
13122       set_vaxc_errno(sts);
13123       switch (sts) {
13124         case RMS$_FNF: case RMS$_DNF:
13125           set_errno(ENOENT); break;
13126         case RMS$_DIR:
13127           set_errno(ENOTDIR); break;
13128         case RMS$_DEV:
13129           set_errno(ENODEV); break;
13130         case RMS$_SYN:
13131           set_errno(EINVAL); break;
13132         case RMS$_PRV:
13133           set_errno(EACCES); break;
13134         default:
13135           set_errno(EVMSERR);
13136       }
13137       return 0;
13138     }
13139
13140     nam_out = nam;
13141     fab_out = fab_in;
13142     fab_out.fab$w_ifi = 0;
13143     fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13144     fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13145     fab_out.fab$l_fop = FAB$M_SQO;
13146     rms_bind_fab_nam(fab_out, nam_out);
13147     rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13148     dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13149     rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13150     esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13151     if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13152     rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13153     if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13154     esal_out = NULL;
13155     rsal_out = NULL;
13156 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13157     esal_out = PerlMem_malloc(VMS_MAXRSS);
13158     if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13159     rsal_out = PerlMem_malloc(VMS_MAXRSS);
13160     if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13161 #endif
13162     rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13163     rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13164
13165     if (preserve_dates == 0) {  /* Act like DCL COPY */
13166       rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13167       fab_out.fab$l_xab = NULL;  /* Don't disturb data from input file */
13168       if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13169         PerlMem_free(vmsin);
13170         PerlMem_free(vmsout);
13171         PerlMem_free(esa);
13172         if (esal != NULL)
13173             PerlMem_free(esal);
13174         PerlMem_free(rsa);
13175         if (rsal != NULL)
13176             PerlMem_free(rsal);
13177         PerlMem_free(esa_out);
13178         if (esal_out != NULL)
13179             PerlMem_free(esal_out);
13180         PerlMem_free(rsa_out);
13181         if (rsal_out != NULL)
13182             PerlMem_free(rsal_out);
13183         set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13184         set_vaxc_errno(sts);
13185         return 0;
13186       }
13187       fab_out.fab$l_xab = (void *) &xabdat;
13188       if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13189         preserve_dates = 1;
13190     }
13191     if (preserve_dates < 0)   /* Clear all bits; we'll use it as a */
13192       preserve_dates =0;      /* bitmask from this point forward   */
13193
13194     if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13195     if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13196       PerlMem_free(vmsin);
13197       PerlMem_free(vmsout);
13198       PerlMem_free(esa);
13199       if (esal != NULL)
13200           PerlMem_free(esal);
13201       PerlMem_free(rsa);
13202       if (rsal != NULL)
13203           PerlMem_free(rsal);
13204       PerlMem_free(esa_out);
13205       if (esal_out != NULL)
13206           PerlMem_free(esal_out);
13207       PerlMem_free(rsa_out);
13208       if (rsal_out != NULL)
13209           PerlMem_free(rsal_out);
13210       set_vaxc_errno(sts);
13211       switch (sts) {
13212         case RMS$_DNF:
13213           set_errno(ENOENT); break;
13214         case RMS$_DIR:
13215           set_errno(ENOTDIR); break;
13216         case RMS$_DEV:
13217           set_errno(ENODEV); break;
13218         case RMS$_SYN:
13219           set_errno(EINVAL); break;
13220         case RMS$_PRV:
13221           set_errno(EACCES); break;
13222         default:
13223           set_errno(EVMSERR);
13224       }
13225       return 0;
13226     }
13227     fab_out.fab$l_fop |= FAB$M_DLT;  /* in case we have to bail out */
13228     if (preserve_dates & 2) {
13229       /* sys$close() will process xabrdt, not xabdat */
13230       xabrdt = cc$rms_xabrdt;
13231 #ifndef __GNUC__
13232       xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13233 #else
13234       /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13235        * is unsigned long[2], while DECC & VAXC use a struct */
13236       memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13237 #endif
13238       fab_out.fab$l_xab = (void *) &xabrdt;
13239     }
13240
13241     ubf = PerlMem_malloc(32256);
13242     if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13243     rab_in = cc$rms_rab;
13244     rab_in.rab$l_fab = &fab_in;
13245     rab_in.rab$l_rop = RAB$M_BIO;
13246     rab_in.rab$l_ubf = ubf;
13247     rab_in.rab$w_usz = 32256;
13248     if (!((sts = sys$connect(&rab_in)) & 1)) {
13249       sys$close(&fab_in); sys$close(&fab_out);
13250       PerlMem_free(vmsin);
13251       PerlMem_free(vmsout);
13252       PerlMem_free(ubf);
13253       PerlMem_free(esa);
13254       if (esal != NULL)
13255           PerlMem_free(esal);
13256       PerlMem_free(rsa);
13257       if (rsal != NULL)
13258           PerlMem_free(rsal);
13259       PerlMem_free(esa_out);
13260       if (esal_out != NULL)
13261           PerlMem_free(esal_out);
13262       PerlMem_free(rsa_out);
13263       if (rsal_out != NULL)
13264           PerlMem_free(rsal_out);
13265       set_errno(EVMSERR); set_vaxc_errno(sts);
13266       return 0;
13267     }
13268
13269     rab_out = cc$rms_rab;
13270     rab_out.rab$l_fab = &fab_out;
13271     rab_out.rab$l_rbf = ubf;
13272     if (!((sts = sys$connect(&rab_out)) & 1)) {
13273       sys$close(&fab_in); sys$close(&fab_out);
13274       PerlMem_free(vmsin);
13275       PerlMem_free(vmsout);
13276       PerlMem_free(ubf);
13277       PerlMem_free(esa);
13278       if (esal != NULL)
13279           PerlMem_free(esal);
13280       PerlMem_free(rsa);
13281       if (rsal != NULL)
13282           PerlMem_free(rsal);
13283       PerlMem_free(esa_out);
13284       if (esal_out != NULL)
13285           PerlMem_free(esal_out);
13286       PerlMem_free(rsa_out);
13287       if (rsal_out != NULL)
13288           PerlMem_free(rsal_out);
13289       set_errno(EVMSERR); set_vaxc_errno(sts);
13290       return 0;
13291     }
13292
13293     while ((sts = sys$read(&rab_in))) {  /* always true  */
13294       if (sts == RMS$_EOF) break;
13295       rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13296       if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13297         sys$close(&fab_in); sys$close(&fab_out);
13298         PerlMem_free(vmsin);
13299         PerlMem_free(vmsout);
13300         PerlMem_free(ubf);
13301         PerlMem_free(esa);
13302         if (esal != NULL)
13303             PerlMem_free(esal);
13304         PerlMem_free(rsa);
13305         if (rsal != NULL)
13306             PerlMem_free(rsal);
13307         PerlMem_free(esa_out);
13308         if (esal_out != NULL)
13309             PerlMem_free(esal_out);
13310         PerlMem_free(rsa_out);
13311         if (rsal_out != NULL)
13312             PerlMem_free(rsal_out);
13313         set_errno(EVMSERR); set_vaxc_errno(sts);
13314         return 0;
13315       }
13316     }
13317
13318
13319     fab_out.fab$l_fop &= ~FAB$M_DLT;  /* We got this far; keep the output */
13320     sys$close(&fab_in);  sys$close(&fab_out);
13321     sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13322
13323     PerlMem_free(vmsin);
13324     PerlMem_free(vmsout);
13325     PerlMem_free(ubf);
13326     PerlMem_free(esa);
13327     if (esal != NULL)
13328         PerlMem_free(esal);
13329     PerlMem_free(rsa);
13330     if (rsal != NULL)
13331         PerlMem_free(rsal);
13332     PerlMem_free(esa_out);
13333     if (esal_out != NULL)
13334         PerlMem_free(esal_out);
13335     PerlMem_free(rsa_out);
13336     if (rsal_out != NULL)
13337         PerlMem_free(rsal_out);
13338
13339     if (!(sts & 1)) {
13340       set_errno(EVMSERR); set_vaxc_errno(sts);
13341       return 0;
13342     }
13343
13344     return 1;
13345
13346 }  /* end of rmscopy() */
13347 /*}}}*/
13348
13349
13350 /***  The following glue provides 'hooks' to make some of the routines
13351  * from this file available from Perl.  These routines are sufficiently
13352  * basic, and are required sufficiently early in the build process,
13353  * that's it's nice to have them available to miniperl as well as the
13354  * full Perl, so they're set up here instead of in an extension.  The
13355  * Perl code which handles importation of these names into a given
13356  * package lives in [.VMS]Filespec.pm in @INC.
13357  */
13358
13359 void
13360 rmsexpand_fromperl(pTHX_ CV *cv)
13361 {
13362   dXSARGS;
13363   char *fspec, *defspec = NULL, *rslt;
13364   STRLEN n_a;
13365   int fs_utf8, dfs_utf8;
13366
13367   fs_utf8 = 0;
13368   dfs_utf8 = 0;
13369   if (!items || items > 2)
13370     Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13371   fspec = SvPV(ST(0),n_a);
13372   fs_utf8 = SvUTF8(ST(0));
13373   if (!fspec || !*fspec) XSRETURN_UNDEF;
13374   if (items == 2) {
13375     defspec = SvPV(ST(1),n_a);
13376     dfs_utf8 = SvUTF8(ST(1));
13377   }
13378   rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13379   ST(0) = sv_newmortal();
13380   if (rslt != NULL) {
13381     sv_usepvn(ST(0),rslt,strlen(rslt));
13382     if (fs_utf8) {
13383         SvUTF8_on(ST(0));
13384     }
13385   }
13386   XSRETURN(1);
13387 }
13388
13389 void
13390 vmsify_fromperl(pTHX_ CV *cv)
13391 {
13392   dXSARGS;
13393   char *vmsified;
13394   STRLEN n_a;
13395   int utf8_fl;
13396
13397   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13398   utf8_fl = SvUTF8(ST(0));
13399   vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13400   ST(0) = sv_newmortal();
13401   if (vmsified != NULL) {
13402     sv_usepvn(ST(0),vmsified,strlen(vmsified));
13403     if (utf8_fl) {
13404         SvUTF8_on(ST(0));
13405     }
13406   }
13407   XSRETURN(1);
13408 }
13409
13410 void
13411 unixify_fromperl(pTHX_ CV *cv)
13412 {
13413   dXSARGS;
13414   char *unixified;
13415   STRLEN n_a;
13416   int utf8_fl;
13417
13418   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13419   utf8_fl = SvUTF8(ST(0));
13420   unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13421   ST(0) = sv_newmortal();
13422   if (unixified != NULL) {
13423     sv_usepvn(ST(0),unixified,strlen(unixified));
13424     if (utf8_fl) {
13425         SvUTF8_on(ST(0));
13426     }
13427   }
13428   XSRETURN(1);
13429 }
13430
13431 void
13432 fileify_fromperl(pTHX_ CV *cv)
13433 {
13434   dXSARGS;
13435   char *fileified;
13436   STRLEN n_a;
13437   int utf8_fl;
13438
13439   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13440   utf8_fl = SvUTF8(ST(0));
13441   fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13442   ST(0) = sv_newmortal();
13443   if (fileified != NULL) {
13444     sv_usepvn(ST(0),fileified,strlen(fileified));
13445     if (utf8_fl) {
13446         SvUTF8_on(ST(0));
13447     }
13448   }
13449   XSRETURN(1);
13450 }
13451
13452 void
13453 pathify_fromperl(pTHX_ CV *cv)
13454 {
13455   dXSARGS;
13456   char *pathified;
13457   STRLEN n_a;
13458   int utf8_fl;
13459
13460   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13461   utf8_fl = SvUTF8(ST(0));
13462   pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13463   ST(0) = sv_newmortal();
13464   if (pathified != NULL) {
13465     sv_usepvn(ST(0),pathified,strlen(pathified));
13466     if (utf8_fl) {
13467         SvUTF8_on(ST(0));
13468     }
13469   }
13470   XSRETURN(1);
13471 }
13472
13473 void
13474 vmspath_fromperl(pTHX_ CV *cv)
13475 {
13476   dXSARGS;
13477   char *vmspath;
13478   STRLEN n_a;
13479   int utf8_fl;
13480
13481   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13482   utf8_fl = SvUTF8(ST(0));
13483   vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13484   ST(0) = sv_newmortal();
13485   if (vmspath != NULL) {
13486     sv_usepvn(ST(0),vmspath,strlen(vmspath));
13487     if (utf8_fl) {
13488         SvUTF8_on(ST(0));
13489     }
13490   }
13491   XSRETURN(1);
13492 }
13493
13494 void
13495 unixpath_fromperl(pTHX_ CV *cv)
13496 {
13497   dXSARGS;
13498   char *unixpath;
13499   STRLEN n_a;
13500   int utf8_fl;
13501
13502   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13503   utf8_fl = SvUTF8(ST(0));
13504   unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13505   ST(0) = sv_newmortal();
13506   if (unixpath != NULL) {
13507     sv_usepvn(ST(0),unixpath,strlen(unixpath));
13508     if (utf8_fl) {
13509         SvUTF8_on(ST(0));
13510     }
13511   }
13512   XSRETURN(1);
13513 }
13514
13515 void
13516 candelete_fromperl(pTHX_ CV *cv)
13517 {
13518   dXSARGS;
13519   char *fspec, *fsp;
13520   SV *mysv;
13521   IO *io;
13522   STRLEN n_a;
13523
13524   if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13525
13526   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13527   Newx(fspec, VMS_MAXRSS, char);
13528   if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13529   if (SvTYPE(mysv) == SVt_PVGV) {
13530     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13531       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13532       ST(0) = &PL_sv_no;
13533       Safefree(fspec);
13534       XSRETURN(1);
13535     }
13536     fsp = fspec;
13537   }
13538   else {
13539     if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13540       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13541       ST(0) = &PL_sv_no;
13542       Safefree(fspec);
13543       XSRETURN(1);
13544     }
13545   }
13546
13547   ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13548   Safefree(fspec);
13549   XSRETURN(1);
13550 }
13551
13552 void
13553 rmscopy_fromperl(pTHX_ CV *cv)
13554 {
13555   dXSARGS;
13556   char *inspec, *outspec, *inp, *outp;
13557   int date_flag;
13558   struct dsc$descriptor indsc  = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13559                         outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13560   unsigned long int sts;
13561   SV *mysv;
13562   IO *io;
13563   STRLEN n_a;
13564
13565   if (items < 2 || items > 3)
13566     Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13567
13568   mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13569   Newx(inspec, VMS_MAXRSS, char);
13570   if (SvTYPE(mysv) == SVt_PVGV) {
13571     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13572       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13573       ST(0) = &PL_sv_no;
13574       Safefree(inspec);
13575       XSRETURN(1);
13576     }
13577     inp = inspec;
13578   }
13579   else {
13580     if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13581       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13582       ST(0) = &PL_sv_no;
13583       Safefree(inspec);
13584       XSRETURN(1);
13585     }
13586   }
13587   mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13588   Newx(outspec, VMS_MAXRSS, char);
13589   if (SvTYPE(mysv) == SVt_PVGV) {
13590     if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13591       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13592       ST(0) = &PL_sv_no;
13593       Safefree(inspec);
13594       Safefree(outspec);
13595       XSRETURN(1);
13596     }
13597     outp = outspec;
13598   }
13599   else {
13600     if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13601       set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13602       ST(0) = &PL_sv_no;
13603       Safefree(inspec);
13604       Safefree(outspec);
13605       XSRETURN(1);
13606     }
13607   }
13608   date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13609
13610   ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13611   Safefree(inspec);
13612   Safefree(outspec);
13613   XSRETURN(1);
13614 }
13615
13616 /* The mod2fname is limited to shorter filenames by design, so it should
13617  * not be modified to support longer EFS pathnames
13618  */
13619 void
13620 mod2fname(pTHX_ CV *cv)
13621 {
13622   dXSARGS;
13623   char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13624        workbuff[NAM$C_MAXRSS*1 + 1];
13625   int total_namelen = 3, counter, num_entries;
13626   /* ODS-5 ups this, but we want to be consistent, so... */
13627   int max_name_len = 39;
13628   AV *in_array = (AV *)SvRV(ST(0));
13629
13630   num_entries = av_len(in_array);
13631
13632   /* All the names start with PL_. */
13633   strcpy(ultimate_name, "PL_");
13634
13635   /* Clean up our working buffer */
13636   Zero(work_name, sizeof(work_name), char);
13637
13638   /* Run through the entries and build up a working name */
13639   for(counter = 0; counter <= num_entries; counter++) {
13640     /* If it's not the first name then tack on a __ */
13641     if (counter) {
13642       strcat(work_name, "__");
13643     }
13644     strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13645   }
13646
13647   /* Check to see if we actually have to bother...*/
13648   if (strlen(work_name) + 3 <= max_name_len) {
13649     strcat(ultimate_name, work_name);
13650   } else {
13651     /* It's too darned big, so we need to go strip. We use the same */
13652     /* algorithm as xsubpp does. First, strip out doubled __ */
13653     char *source, *dest, last;
13654     dest = workbuff;
13655     last = 0;
13656     for (source = work_name; *source; source++) {
13657       if (last == *source && last == '_') {
13658         continue;
13659       }
13660       *dest++ = *source;
13661       last = *source;
13662     }
13663     /* Go put it back */
13664     strcpy(work_name, workbuff);
13665     /* Is it still too big? */
13666     if (strlen(work_name) + 3 > max_name_len) {
13667       /* Strip duplicate letters */
13668       last = 0;
13669       dest = workbuff;
13670       for (source = work_name; *source; source++) {
13671         if (last == toupper(*source)) {
13672         continue;
13673         }
13674         *dest++ = *source;
13675         last = toupper(*source);
13676       }
13677       strcpy(work_name, workbuff);
13678     }
13679
13680     /* Is it *still* too big? */
13681     if (strlen(work_name) + 3 > max_name_len) {
13682       /* Too bad, we truncate */
13683       work_name[max_name_len - 2] = 0;
13684     }
13685     strcat(ultimate_name, work_name);
13686   }
13687
13688   /* Okay, return it */
13689   ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13690   XSRETURN(1);
13691 }
13692
13693 void
13694 hushexit_fromperl(pTHX_ CV *cv)
13695 {
13696     dXSARGS;
13697
13698     if (items > 0) {
13699         VMSISH_HUSHED = SvTRUE(ST(0));
13700     }
13701     ST(0) = boolSV(VMSISH_HUSHED);
13702     XSRETURN(1);
13703 }
13704
13705
13706 PerlIO * 
13707 Perl_vms_start_glob
13708    (pTHX_ SV *tmpglob,
13709     IO *io)
13710 {
13711     PerlIO *fp;
13712     struct vs_str_st *rslt;
13713     char *vmsspec;
13714     char *rstr;
13715     char *begin, *cp;
13716     $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13717     PerlIO *tmpfp;
13718     STRLEN i;
13719     struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13720     struct dsc$descriptor_vs rsdsc;
13721     unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13722     unsigned long hasver = 0, isunix = 0;
13723     unsigned long int lff_flags = 0;
13724     int rms_sts;
13725     int vms_old_glob = 1;
13726
13727     if (!SvOK(tmpglob)) {
13728         SETERRNO(ENOENT,RMS$_FNF);
13729         return NULL;
13730     }
13731
13732     vms_old_glob = !decc_filename_unix_report;
13733
13734 #ifdef VMS_LONGNAME_SUPPORT
13735     lff_flags = LIB$M_FIL_LONG_NAMES;
13736 #endif
13737     /* The Newx macro will not allow me to assign a smaller array
13738      * to the rslt pointer, so we will assign it to the begin char pointer
13739      * and then copy the value into the rslt pointer.
13740      */
13741     Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13742     rslt = (struct vs_str_st *)begin;
13743     rslt->length = 0;
13744     rstr = &rslt->str[0];
13745     rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13746     rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13747     rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13748     rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13749
13750     Newx(vmsspec, VMS_MAXRSS, char);
13751
13752         /* We could find out if there's an explicit dev/dir or version
13753            by peeking into lib$find_file's internal context at
13754            ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13755            but that's unsupported, so I don't want to do it now and
13756            have it bite someone in the future. */
13757         /* Fix-me: vms_split_path() is the only way to do this, the
13758            existing method will fail with many legal EFS or UNIX specifications
13759          */
13760
13761     cp = SvPV(tmpglob,i);
13762
13763     for (; i; i--) {
13764         if (cp[i] == ';') hasver = 1;
13765         if (cp[i] == '.') {
13766             if (sts) hasver = 1;
13767             else sts = 1;
13768         }
13769         if (cp[i] == '/') {
13770             hasdir = isunix = 1;
13771             break;
13772         }
13773         if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13774             hasdir = 1;
13775             break;
13776         }
13777     }
13778
13779     /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13780     if ((hasdir == 0) && decc_filename_unix_report) {
13781         isunix = 1;
13782     }
13783
13784     if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13785         char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13786         int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13787         int wildstar = 0;
13788         int wildquery = 0;
13789         int found = 0;
13790         Stat_t st;
13791         int stat_sts;
13792         stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13793         if (!stat_sts && S_ISDIR(st.st_mode)) {
13794             char * vms_dir;
13795             const char * fname;
13796             STRLEN fname_len;
13797
13798             /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13799             /* path delimiter of ':>]', if so, then the old behavior has */
13800             /* obviously been specificially requested */
13801
13802             fname = SvPVX_const(tmpglob);
13803             fname_len = strlen(fname);
13804             vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13805             if (vms_old_glob || (vms_dir != NULL)) {
13806                 wilddsc.dsc$a_pointer = tovmspath_utf8(
13807                                             SvPVX(tmpglob),vmsspec,NULL);
13808                 ok = (wilddsc.dsc$a_pointer != NULL);
13809                 /* maybe passed 'foo' rather than '[.foo]', thus not
13810                    detected above */
13811                 hasdir = 1; 
13812             } else {
13813                 /* Operate just on the directory, the special stat/fstat for */
13814                 /* leaves the fileified  specification in the st_devnam */
13815                 /* member. */
13816                 wilddsc.dsc$a_pointer = st.st_devnam;
13817                 ok = 1;
13818             }
13819         }
13820         else {
13821             wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13822             ok = (wilddsc.dsc$a_pointer != NULL);
13823         }
13824         if (ok)
13825             wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13826
13827         /* If not extended character set, replace ? with % */
13828         /* With extended character set, ? is a wildcard single character */
13829         for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13830             if (*cp == '?') {
13831                 wildquery = 1;
13832                 if (!decc_efs_case_preserve)
13833                     *cp = '%';
13834             } else if (*cp == '%') {
13835                 wildquery = 1;
13836             } else if (*cp == '*') {
13837                 wildstar = 1;
13838             }
13839         }
13840
13841         if (ok) {
13842             wv_sts = vms_split_path(
13843                 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13844                 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13845                 &wvs_spec, &wvs_len);
13846         } else {
13847             wn_spec = NULL;
13848             wn_len = 0;
13849             we_spec = NULL;
13850             we_len = 0;
13851         }
13852
13853         sts = SS$_NORMAL;
13854         while (ok && $VMS_STATUS_SUCCESS(sts)) {
13855          char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13856          int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13857          int valid_find;
13858
13859             valid_find = 0;
13860             sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13861                                 &dfltdsc,NULL,&rms_sts,&lff_flags);
13862             if (!$VMS_STATUS_SUCCESS(sts))
13863                 break;
13864
13865             /* with varying string, 1st word of buffer contains result length */
13866             rstr[rslt->length] = '\0';
13867
13868              /* Find where all the components are */
13869              v_sts = vms_split_path
13870                        (rstr,
13871                         &v_spec,
13872                         &v_len,
13873                         &r_spec,
13874                         &r_len,
13875                         &d_spec,
13876                         &d_len,
13877                         &n_spec,
13878                         &n_len,
13879                         &e_spec,
13880                         &e_len,
13881                         &vs_spec,
13882                         &vs_len);
13883
13884             /* If no version on input, truncate the version on output */
13885             if (!hasver && (vs_len > 0)) {
13886                 *vs_spec = '\0';
13887                 vs_len = 0;
13888             }
13889
13890             if (isunix) {
13891
13892                 /* In Unix report mode, remove the ".dir;1" from the name */
13893                 /* if it is a real directory */
13894                 if (decc_filename_unix_report || decc_efs_charset) {
13895                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13896                         Stat_t statbuf;
13897                         int ret_sts;
13898
13899                         ret_sts = flex_lstat(rstr, &statbuf);
13900                         if ((ret_sts == 0) &&
13901                             S_ISDIR(statbuf.st_mode)) {
13902                             e_len = 0;
13903                             e_spec[0] = 0;
13904                         }
13905                     }
13906                 }
13907
13908                 /* No version & a null extension on UNIX handling */
13909                 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13910                     e_len = 0;
13911                     *e_spec = '\0';
13912                 }
13913             }
13914
13915             if (!decc_efs_case_preserve) {
13916                 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13917             }
13918
13919             /* Find File treats a Null extension as return all extensions */
13920             /* This is contrary to Perl expectations */
13921
13922             if (wildstar || wildquery || vms_old_glob) {
13923                 /* really need to see if the returned file name matched */
13924                 /* but for now will assume that it matches */
13925                 valid_find = 1;
13926             } else {
13927                 /* Exact Match requested */
13928                 /* How are directories handled? - like a file */
13929                 if ((e_len == we_len) && (n_len == wn_len)) {
13930                     int t1;
13931                     t1 = e_len;
13932                     if (t1 > 0)
13933                         t1 = strncmp(e_spec, we_spec, e_len);
13934                     if (t1 == 0) {
13935                        t1 = n_len;
13936                        if (t1 > 0)
13937                            t1 = strncmp(n_spec, we_spec, n_len);
13938                        if (t1 == 0)
13939                            valid_find = 1;
13940                     }
13941                 }
13942             }
13943
13944             if (valid_find) {
13945                 found++;
13946
13947                 if (hasdir) {
13948                     if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13949                     begin = rstr;
13950                 }
13951                 else {
13952                     /* Start with the name */
13953                     begin = n_spec;
13954                 }
13955                 strcat(begin,"\n");
13956                 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13957             }
13958         }
13959         if (cxt) (void)lib$find_file_end(&cxt);
13960
13961         if (!found) {
13962             /* Be POSIXish: return the input pattern when no matches */
13963             strcpy(rstr,SvPVX(tmpglob));
13964             strcat(rstr,"\n");
13965             ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13966         }
13967
13968         if (ok && sts != RMS$_NMF &&
13969             sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13970         if (!ok) {
13971             if (!(sts & 1)) {
13972                 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13973             }
13974             PerlIO_close(tmpfp);
13975             fp = NULL;
13976         }
13977         else {
13978             PerlIO_rewind(tmpfp);
13979             IoTYPE(io) = IoTYPE_RDONLY;
13980             IoIFP(io) = fp = tmpfp;
13981             IoFLAGS(io) &= ~IOf_UNTAINT;  /* maybe redundant */
13982         }
13983     }
13984     Safefree(vmsspec);
13985     Safefree(rslt);
13986     return fp;
13987 }
13988
13989
13990 static char *
13991 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13992                    int *utf8_fl);
13993
13994 void
13995 unixrealpath_fromperl(pTHX_ CV *cv)
13996 {
13997     dXSARGS;
13998     char *fspec, *rslt_spec, *rslt;
13999     STRLEN n_a;
14000
14001     if (!items || items != 1)
14002         Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
14003
14004     fspec = SvPV(ST(0),n_a);
14005     if (!fspec || !*fspec) XSRETURN_UNDEF;
14006
14007     Newx(rslt_spec, VMS_MAXRSS + 1, char);
14008     rslt = do_vms_realpath(fspec, rslt_spec, NULL);
14009
14010     ST(0) = sv_newmortal();
14011     if (rslt != NULL)
14012         sv_usepvn(ST(0),rslt,strlen(rslt));
14013     else
14014         Safefree(rslt_spec);
14015         XSRETURN(1);
14016 }
14017
14018 static char *
14019 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
14020                    int *utf8_fl);
14021
14022 void
14023 vmsrealpath_fromperl(pTHX_ CV *cv)
14024 {
14025     dXSARGS;
14026     char *fspec, *rslt_spec, *rslt;
14027     STRLEN n_a;
14028
14029     if (!items || items != 1)
14030         Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
14031
14032     fspec = SvPV(ST(0),n_a);
14033     if (!fspec || !*fspec) XSRETURN_UNDEF;
14034
14035     Newx(rslt_spec, VMS_MAXRSS + 1, char);
14036     rslt = do_vms_realname(fspec, rslt_spec, NULL);
14037
14038     ST(0) = sv_newmortal();
14039     if (rslt != NULL)
14040         sv_usepvn(ST(0),rslt,strlen(rslt));
14041     else
14042         Safefree(rslt_spec);
14043         XSRETURN(1);
14044 }
14045
14046 #ifdef HAS_SYMLINK
14047 /*
14048  * A thin wrapper around decc$symlink to make sure we follow the 
14049  * standard and do not create a symlink with a zero-length name.
14050  *
14051  * Also in ODS-2 mode, existing tests assume that the link target
14052  * will be converted to UNIX format.
14053  */
14054 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
14055 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
14056   if (!link_name || !*link_name) {
14057     SETERRNO(ENOENT, SS$_NOSUCHFILE);
14058     return -1;
14059   }
14060
14061   if (decc_efs_charset) {
14062       return symlink(contents, link_name);
14063   } else {
14064       int sts;
14065       char * utarget;
14066
14067       /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
14068       /* because in order to work, the symlink target must be in UNIX format */
14069
14070       /* As symbolic links can hold things other than files, we will only do */
14071       /* the conversion in in ODS-2 mode */
14072
14073       utarget = PerlMem_malloc(VMS_MAXRSS + 1);
14074       if (int_tounixspec(contents, utarget, NULL) == NULL) {
14075
14076           /* This should not fail, as an untranslatable filename */
14077           /* should be passed through */
14078           utarget = (char *)contents;
14079       }
14080       sts = symlink(utarget, link_name);
14081       PerlMem_free(utarget);
14082       return sts;
14083   }
14084
14085 }
14086 /*}}}*/
14087
14088 #endif /* HAS_SYMLINK */
14089
14090 int do_vms_case_tolerant(void);
14091
14092 void
14093 case_tolerant_process_fromperl(pTHX_ CV *cv)
14094 {
14095   dXSARGS;
14096   ST(0) = boolSV(do_vms_case_tolerant());
14097   XSRETURN(1);
14098 }
14099
14100 #ifdef USE_ITHREADS
14101
14102 void  
14103 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, 
14104                           struct interp_intern *dst)
14105 {
14106     PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14107
14108     memcpy(dst,src,sizeof(struct interp_intern));
14109 }
14110
14111 #endif
14112
14113 void  
14114 Perl_sys_intern_clear(pTHX)
14115 {
14116 }
14117
14118 void  
14119 Perl_sys_intern_init(pTHX)
14120 {
14121     unsigned int ix = RAND_MAX;
14122     double x;
14123
14124     VMSISH_HUSHED = 0;
14125
14126     MY_POSIX_EXIT = vms_posix_exit;
14127
14128     x = (float)ix;
14129     MY_INV_RAND_MAX = 1./x;
14130 }
14131
14132 void
14133 init_os_extras(void)
14134 {
14135   dTHX;
14136   char* file = __FILE__;
14137   if (decc_disable_to_vms_logname_translation) {
14138     no_translate_barewords = TRUE;
14139   } else {
14140     no_translate_barewords = FALSE;
14141   }
14142
14143   newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14144   newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14145   newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14146   newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14147   newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14148   newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14149   newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14150   newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14151   newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14152   newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14153   newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14154   newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14155   newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14156   newXSproto("VMS::Filespec::case_tolerant_process",
14157       case_tolerant_process_fromperl,file,"");
14158
14159   store_pipelocs(aTHX);         /* will redo any earlier attempts */
14160
14161   return;
14162 }
14163   
14164 #if __CRTL_VER == 80200000
14165 /* This missed getting in to the DECC SDK for 8.2 */
14166 char *realpath(const char *file_name, char * resolved_name, ...);
14167 #endif
14168
14169 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14170 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14171  * The perl fallback routine to provide realpath() is not as efficient
14172  * on OpenVMS.
14173  */
14174
14175 /* Hack, use old stat() as fastest way of getting ino_t and device */
14176 int decc$stat(const char *name, void * statbuf);
14177 #if !defined(__VAX) && __CRTL_VER >= 80200000
14178 int decc$lstat(const char *name, void * statbuf);
14179 #else
14180 #define decc$lstat decc$stat
14181 #endif
14182
14183
14184 /* Realpath is fragile.  In 8.3 it does not work if the feature
14185  * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14186  * links are implemented in RMS, not the CRTL. It also can fail if the 
14187  * user does not have read/execute access to some of the directories.
14188  * So in order for Do What I Mean mode to work, if realpath() fails,
14189  * fall back to looking up the filename by the device name and FID.
14190  */
14191
14192 int vms_fid_to_name(char * outname, int outlen,
14193                     const char * name, int lstat_flag, mode_t * mode)
14194 {
14195 #pragma message save
14196 #pragma message disable MISALGNDSTRCT
14197 #pragma message disable MISALGNDMEM
14198 #pragma member_alignment save
14199 #pragma nomember_alignment
14200 struct statbuf_t {
14201     char           * st_dev;
14202     unsigned short st_ino[3];
14203     unsigned short old_st_mode;
14204     unsigned long  padl[30];  /* plenty of room */
14205 } statbuf;
14206 #pragma message restore
14207 #pragma member_alignment restore
14208
14209     int sts;
14210     struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14211     struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14212     char *fileified;
14213     char *temp_fspec;
14214     char *ret_spec;
14215
14216     /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14217      * unexpected answers
14218      */
14219
14220     fileified = PerlMem_malloc(VMS_MAXRSS);
14221     if (fileified == NULL)
14222         _ckvmssts_noperl(SS$_INSFMEM);
14223      
14224     temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14225     if (temp_fspec == NULL)
14226         _ckvmssts_noperl(SS$_INSFMEM);
14227
14228     sts = -1;
14229     /* First need to try as a directory */
14230     ret_spec = int_tovmspath(name, temp_fspec, NULL);
14231     if (ret_spec != NULL) {
14232         ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL); 
14233         if (ret_spec != NULL) {
14234             if (lstat_flag == 0)
14235                 sts = decc$stat(fileified, &statbuf);
14236             else
14237                 sts = decc$lstat(fileified, &statbuf);
14238         }
14239     }
14240
14241     /* Then as a VMS file spec */
14242     if (sts != 0) {
14243         ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14244         if (ret_spec != NULL) {
14245             if (lstat_flag == 0) {
14246                 sts = decc$stat(temp_fspec, &statbuf);
14247             } else {
14248                 sts = decc$lstat(temp_fspec, &statbuf);
14249             }
14250         }
14251     }
14252
14253     if (sts) {
14254         /* Next try - allow multiple dots with out EFS CHARSET */
14255         /* The CRTL stat() falls down hard on multi-dot filenames in unix
14256          * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14257          * enable it if it isn't already.
14258          */
14259 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14260         if (!decc_efs_charset && (decc_efs_charset_index > 0))
14261             decc$feature_set_value(decc_efs_charset_index, 1, 1); 
14262 #endif
14263         ret_spec = int_tovmspath(name, temp_fspec, NULL);
14264         if (lstat_flag == 0) {
14265             sts = decc$stat(name, &statbuf);
14266         } else {
14267             sts = decc$lstat(name, &statbuf);
14268         }
14269 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14270         if (!decc_efs_charset && (decc_efs_charset_index > 0))
14271             decc$feature_set_value(decc_efs_charset_index, 1, 0); 
14272 #endif
14273     }
14274
14275
14276     /* and then because the Perl Unix to VMS conversion is not perfect */
14277     /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14278     /* characters from filenames so we need to try it as-is */
14279     if (sts) {
14280         if (lstat_flag == 0) {
14281             sts = decc$stat(name, &statbuf);
14282         } else {
14283             sts = decc$lstat(name, &statbuf);
14284         }
14285     }
14286
14287     if (sts == 0) {
14288         int vms_sts;
14289
14290         dvidsc.dsc$a_pointer=statbuf.st_dev;
14291         dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14292
14293         specdsc.dsc$a_pointer = outname;
14294         specdsc.dsc$w_length = outlen-1;
14295
14296         vms_sts = lib$fid_to_name
14297             (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14298         if ($VMS_STATUS_SUCCESS(vms_sts)) {
14299             outname[specdsc.dsc$w_length] = 0;
14300
14301             /* Return the mode */
14302             if (mode) {
14303                 *mode = statbuf.old_st_mode;
14304             }
14305             return 0;
14306         }
14307     }
14308     return sts;
14309 }
14310
14311
14312
14313 static char *
14314 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14315                    int *utf8_fl)
14316 {
14317     char * rslt = NULL;
14318
14319 #ifdef HAS_SYMLINK
14320     if (decc_posix_compliant_pathnames > 0 ) {
14321         /* realpath currently only works if posix compliant pathnames are
14322          * enabled.  It may start working when they are not, but in that
14323          * case we still want the fallback behavior for backwards compatibility
14324          */
14325         rslt = realpath(filespec, outbuf);
14326     }
14327 #endif
14328
14329     if (rslt == NULL) {
14330         char * vms_spec;
14331         char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14332         int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14333         int file_len;
14334         mode_t my_mode;
14335
14336         /* Fall back to fid_to_name */
14337
14338         Newx(vms_spec, VMS_MAXRSS + 1, char);
14339
14340         sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14341         if (sts == 0) {
14342
14343
14344             /* Now need to trim the version off */
14345             sts = vms_split_path
14346                   (vms_spec,
14347                    &v_spec,
14348                    &v_len,
14349                    &r_spec,
14350                    &r_len,
14351                    &d_spec,
14352                    &d_len,
14353                    &n_spec,
14354                    &n_len,
14355                    &e_spec,
14356                    &e_len,
14357                    &vs_spec,
14358                    &vs_len);
14359
14360
14361                 if (sts == 0) {
14362                     int haslower = 0;
14363                     const char *cp;
14364
14365                     /* Trim off the version */
14366                     int file_len = v_len + r_len + d_len + n_len + e_len;
14367                     vms_spec[file_len] = 0;
14368
14369                     /* Trim off the .DIR if this is a directory */
14370                     if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
14371                         if (S_ISDIR(my_mode)) {
14372                             e_len = 0;
14373                             e_spec[0] = 0;
14374                         }
14375                     }
14376
14377                     /* Drop NULL extensions on UNIX file specification */
14378                     if ((e_len == 1) && decc_readdir_dropdotnotype) {
14379                         e_len = 0;
14380                         e_spec[0] = '\0';
14381                     }
14382
14383                     /* The result is expected to be in UNIX format */
14384                     rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14385
14386                     /* Downcase if input had any lower case letters and 
14387                      * case preservation is not in effect. 
14388                      */
14389                     if (!decc_efs_case_preserve) {
14390                         for (cp = filespec; *cp; cp++)
14391                             if (islower(*cp)) { haslower = 1; break; }
14392
14393                         if (haslower) __mystrtolower(rslt);
14394                     }
14395                 }
14396         } else {
14397
14398             /* Now for some hacks to deal with backwards and forward */
14399             /* compatibilty */
14400             if (!decc_efs_charset) {
14401
14402                 /* 1. ODS-2 mode wants to do a syntax only translation */
14403                 rslt = int_rmsexpand(filespec, outbuf,
14404                                     NULL, 0, NULL, utf8_fl);
14405
14406             } else {
14407                 if (decc_filename_unix_report) {
14408                     char * dir_name;
14409                     char * vms_dir_name;
14410                     char * file_name;
14411
14412                     /* 2. ODS-5 / UNIX report mode should return a failure */
14413                     /*    if the parent directory also does not exist */
14414                     /*    Otherwise, get the real path for the parent */
14415                     /*    and add the child to it.
14416
14417                     /* basename / dirname only available for VMS 7.0+ */
14418                     /* So we may need to implement them as common routines */
14419
14420                     Newx(dir_name, VMS_MAXRSS + 1, char);
14421                     Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14422                     dir_name[0] = '\0';
14423                     file_name = NULL;
14424
14425                     /* First try a VMS parse */
14426                     sts = vms_split_path
14427                           (filespec,
14428                            &v_spec,
14429                            &v_len,
14430                            &r_spec,
14431                            &r_len,
14432                            &d_spec,
14433                            &d_len,
14434                            &n_spec,
14435                            &n_len,
14436                            &e_spec,
14437                            &e_len,
14438                            &vs_spec,
14439                            &vs_len);
14440
14441                     if (sts == 0) {
14442                         /* This is VMS */
14443
14444                         int dir_len = v_len + r_len + d_len + n_len;
14445                         if (dir_len > 0) {
14446                            strncpy(dir_name, filespec, dir_len);
14447                            dir_name[dir_len] = '\0';
14448                            file_name = (char *)&filespec[dir_len + 1];
14449                         }
14450                     } else {
14451                         /* This must be UNIX */
14452                         char * tchar;
14453
14454                         tchar = strrchr(filespec, '/');
14455
14456                         if (tchar != NULL) {
14457                             int dir_len = tchar - filespec;
14458                             strncpy(dir_name, filespec, dir_len);
14459                             dir_name[dir_len] = '\0';
14460                             file_name = (char *) &filespec[dir_len + 1];
14461                         }
14462                     }
14463
14464                     /* Dir name is defaulted */
14465                     if (dir_name[0] == 0) {
14466                         dir_name[0] = '.';
14467                         dir_name[1] = '\0';
14468                     }
14469
14470                     /* Need realpath for the directory */
14471                     sts = vms_fid_to_name(vms_dir_name,
14472                                           VMS_MAXRSS + 1,
14473                                           dir_name, 0, NULL);
14474
14475                     if (sts == 0) {
14476                         /* Now need to pathify it.
14477                         char *tdir = int_pathify_dirspec(vms_dir_name,
14478                                                          outbuf);
14479
14480                         /* And now add the original filespec to it */
14481                         if (file_name != NULL) {
14482                             strcat(outbuf, file_name);
14483                         }
14484                         return outbuf;
14485                     }
14486                     Safefree(vms_dir_name);
14487                     Safefree(dir_name);
14488                 }
14489             }
14490         }
14491         Safefree(vms_spec);
14492     }
14493     return rslt;
14494 }
14495
14496 static char *
14497 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14498                    int *utf8_fl)
14499 {
14500     char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14501     int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14502     int file_len;
14503
14504     /* Fall back to fid_to_name */
14505
14506     sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14507     if (sts != 0) {
14508         return NULL;
14509     }
14510     else {
14511
14512
14513         /* Now need to trim the version off */
14514         sts = vms_split_path
14515                   (outbuf,
14516                    &v_spec,
14517                    &v_len,
14518                    &r_spec,
14519                    &r_len,
14520                    &d_spec,
14521                    &d_len,
14522                    &n_spec,
14523                    &n_len,
14524                    &e_spec,
14525                    &e_len,
14526                    &vs_spec,
14527                    &vs_len);
14528
14529
14530         if (sts == 0) {
14531             int haslower = 0;
14532             const char *cp;
14533
14534             /* Trim off the version */
14535             int file_len = v_len + r_len + d_len + n_len + e_len;
14536             outbuf[file_len] = 0;
14537
14538             /* Downcase if input had any lower case letters and 
14539              * case preservation is not in effect. 
14540              */
14541             if (!decc_efs_case_preserve) {
14542                 for (cp = filespec; *cp; cp++)
14543                     if (islower(*cp)) { haslower = 1; break; }
14544
14545                 if (haslower) __mystrtolower(outbuf);
14546             }
14547         }
14548     }
14549     return outbuf;
14550 }
14551
14552
14553 /*}}}*/
14554 /* External entry points */
14555 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14556 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14557
14558 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14559 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14560
14561 /* case_tolerant */
14562
14563 /*{{{int do_vms_case_tolerant(void)*/
14564 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14565  * controlled by a process setting.
14566  */
14567 int do_vms_case_tolerant(void)
14568 {
14569     return vms_process_case_tolerant;
14570 }
14571 /*}}}*/
14572 /* External entry points */
14573 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14574 int Perl_vms_case_tolerant(void)
14575 { return do_vms_case_tolerant(); }
14576 #else
14577 int Perl_vms_case_tolerant(void)
14578 { return vms_process_case_tolerant; }
14579 #endif
14580
14581
14582  /* Start of DECC RTL Feature handling */
14583
14584 static int sys_trnlnm
14585    (const char * logname,
14586     char * value,
14587     int value_len)
14588 {
14589     const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14590     const unsigned long attr = LNM$M_CASE_BLIND;
14591     struct dsc$descriptor_s name_dsc;
14592     int status;
14593     unsigned short result;
14594     struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14595                                 {0, 0, 0, 0}};
14596
14597     name_dsc.dsc$w_length = strlen(logname);
14598     name_dsc.dsc$a_pointer = (char *)logname;
14599     name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14600     name_dsc.dsc$b_class = DSC$K_CLASS_S;
14601
14602     status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14603
14604     if ($VMS_STATUS_SUCCESS(status)) {
14605
14606          /* Null terminate and return the string */
14607         /*--------------------------------------*/
14608         value[result] = 0;
14609     }
14610
14611     return status;
14612 }
14613
14614 static int sys_crelnm
14615    (const char * logname,
14616     const char * value)
14617 {
14618     int ret_val;
14619     const char * proc_table = "LNM$PROCESS_TABLE";
14620     struct dsc$descriptor_s proc_table_dsc;
14621     struct dsc$descriptor_s logname_dsc;
14622     struct itmlst_3 item_list[2];
14623
14624     proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14625     proc_table_dsc.dsc$w_length = strlen(proc_table);
14626     proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14627     proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14628
14629     logname_dsc.dsc$a_pointer = (char *) logname;
14630     logname_dsc.dsc$w_length = strlen(logname);
14631     logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14632     logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14633
14634     item_list[0].buflen = strlen(value);
14635     item_list[0].itmcode = LNM$_STRING;
14636     item_list[0].bufadr = (char *)value;
14637     item_list[0].retlen = NULL;
14638
14639     item_list[1].buflen = 0;
14640     item_list[1].itmcode = 0;
14641
14642     ret_val = sys$crelnm
14643                        (NULL,
14644                         (const struct dsc$descriptor_s *)&proc_table_dsc,
14645                         (const struct dsc$descriptor_s *)&logname_dsc,
14646                         NULL,
14647                         (const struct item_list_3 *) item_list);
14648
14649     return ret_val;
14650 }
14651
14652 /* C RTL Feature settings */
14653
14654 static int set_features
14655    (int (* init_coroutine)(int *, int *, void *),  /* Needs casts if used */
14656     int (* cli_routine)(void),  /* Not documented */
14657     void *image_info)           /* Not documented */
14658 {
14659     int status;
14660     int s;
14661     char* str;
14662     char val_str[10];
14663 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14664     const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14665     const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14666     unsigned long case_perm;
14667     unsigned long case_image;
14668 #endif
14669
14670     /* Allow an exception to bring Perl into the VMS debugger */
14671     vms_debug_on_exception = 0;
14672     status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14673     if ($VMS_STATUS_SUCCESS(status)) {
14674        val_str[0] = _toupper(val_str[0]);
14675        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14676          vms_debug_on_exception = 1;
14677        else
14678          vms_debug_on_exception = 0;
14679     }
14680
14681     /* Debug unix/vms file translation routines */
14682     vms_debug_fileify = 0;
14683     status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14684     if ($VMS_STATUS_SUCCESS(status)) {
14685         val_str[0] = _toupper(val_str[0]);
14686         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14687             vms_debug_fileify = 1;
14688         else
14689             vms_debug_fileify = 0;
14690     }
14691
14692
14693     /* Historically PERL has been doing vmsify / stat differently than */
14694     /* the CRTL.  In particular, under some conditions the CRTL will   */
14695     /* remove some illegal characters like spaces from filenames       */
14696     /* resulting in some differences.  The stat()/lstat() wrapper has  */
14697     /* been reporting such file names as invalid and fails to stat them */
14698     /* fixing this bug so that stat()/lstat() accept these like the     */
14699     /* CRTL does will result in several tests failing.                  */
14700     /* This should really be fixed, but for now, set up a feature to    */
14701     /* enable it so that the impact can be studied.                     */
14702     vms_bug_stat_filename = 0;
14703     status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14704     if ($VMS_STATUS_SUCCESS(status)) {
14705         val_str[0] = _toupper(val_str[0]);
14706         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14707             vms_bug_stat_filename = 1;
14708         else
14709             vms_bug_stat_filename = 0;
14710     }
14711
14712
14713     /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14714     vms_vtf7_filenames = 0;
14715     status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14716     if ($VMS_STATUS_SUCCESS(status)) {
14717        val_str[0] = _toupper(val_str[0]);
14718        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14719          vms_vtf7_filenames = 1;
14720        else
14721          vms_vtf7_filenames = 0;
14722     }
14723
14724     /* unlink all versions on unlink() or rename() */
14725     vms_unlink_all_versions = 0;
14726     status = sys_trnlnm
14727         ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14728     if ($VMS_STATUS_SUCCESS(status)) {
14729        val_str[0] = _toupper(val_str[0]);
14730        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14731          vms_unlink_all_versions = 1;
14732        else
14733          vms_unlink_all_versions = 0;
14734     }
14735
14736     /* Dectect running under GNV Bash or other UNIX like shell */
14737 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14738     gnv_unix_shell = 0;
14739     status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14740     if ($VMS_STATUS_SUCCESS(status)) {
14741          gnv_unix_shell = 1;
14742          set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14743          set_feature_default("DECC$EFS_CHARSET", 1);
14744          set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14745          set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14746          set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14747          set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14748          vms_unlink_all_versions = 1;
14749          vms_posix_exit = 1;
14750     }
14751 #endif
14752
14753     /* hacks to see if known bugs are still present for testing */
14754
14755     /* PCP mode requires creating /dev/null special device file */
14756     decc_bug_devnull = 0;
14757     status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14758     if ($VMS_STATUS_SUCCESS(status)) {
14759        val_str[0] = _toupper(val_str[0]);
14760        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14761           decc_bug_devnull = 1;
14762        else
14763           decc_bug_devnull = 0;
14764     }
14765
14766     /* UNIX directory names with no paths are broken in a lot of places */
14767     decc_dir_barename = 1;
14768     status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14769     if ($VMS_STATUS_SUCCESS(status)) {
14770       val_str[0] = _toupper(val_str[0]);
14771       if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14772         decc_dir_barename = 1;
14773       else
14774         decc_dir_barename = 0;
14775     }
14776
14777 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14778     s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14779     if (s >= 0) {
14780         decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14781         if (decc_disable_to_vms_logname_translation < 0)
14782             decc_disable_to_vms_logname_translation = 0;
14783     }
14784
14785     s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14786     if (s >= 0) {
14787         decc_efs_case_preserve = decc$feature_get_value(s, 1);
14788         if (decc_efs_case_preserve < 0)
14789             decc_efs_case_preserve = 0;
14790     }
14791
14792     s = decc$feature_get_index("DECC$EFS_CHARSET");
14793     decc_efs_charset_index = s;
14794     if (s >= 0) {
14795         decc_efs_charset = decc$feature_get_value(s, 1);
14796         if (decc_efs_charset < 0)
14797             decc_efs_charset = 0;
14798     }
14799
14800     s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14801     if (s >= 0) {
14802         decc_filename_unix_report = decc$feature_get_value(s, 1);
14803         if (decc_filename_unix_report > 0) {
14804             decc_filename_unix_report = 1;
14805             vms_posix_exit = 1;
14806         }
14807         else
14808             decc_filename_unix_report = 0;
14809     }
14810
14811     s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14812     if (s >= 0) {
14813         decc_filename_unix_only = decc$feature_get_value(s, 1);
14814         if (decc_filename_unix_only > 0) {
14815             decc_filename_unix_only = 1;
14816         }
14817         else {
14818             decc_filename_unix_only = 0;
14819         }
14820     }
14821
14822     s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14823     if (s >= 0) {
14824         decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14825         if (decc_filename_unix_no_version < 0)
14826             decc_filename_unix_no_version = 0;
14827     }
14828
14829     s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14830     if (s >= 0) {
14831         decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14832         if (decc_readdir_dropdotnotype < 0)
14833             decc_readdir_dropdotnotype = 0;
14834     }
14835
14836 #if __CRTL_VER >= 80200000
14837     s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14838     if (s >= 0) {
14839         decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14840         if (decc_posix_compliant_pathnames < 0)
14841             decc_posix_compliant_pathnames = 0;
14842         if (decc_posix_compliant_pathnames > 4)
14843             decc_posix_compliant_pathnames = 0;
14844     }
14845
14846 #endif
14847 #else
14848     status = sys_trnlnm
14849         ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14850     if ($VMS_STATUS_SUCCESS(status)) {
14851         val_str[0] = _toupper(val_str[0]);
14852         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14853            decc_disable_to_vms_logname_translation = 1;
14854         }
14855     }
14856
14857 #ifndef __VAX
14858     status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14859     if ($VMS_STATUS_SUCCESS(status)) {
14860         val_str[0] = _toupper(val_str[0]);
14861         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14862            decc_efs_case_preserve = 1;
14863         }
14864     }
14865 #endif
14866
14867     status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14868     if ($VMS_STATUS_SUCCESS(status)) {
14869         val_str[0] = _toupper(val_str[0]);
14870         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14871            decc_filename_unix_report = 1;
14872         }
14873     }
14874     status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14875     if ($VMS_STATUS_SUCCESS(status)) {
14876         val_str[0] = _toupper(val_str[0]);
14877         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14878            decc_filename_unix_only = 1;
14879            decc_filename_unix_report = 1;
14880         }
14881     }
14882     status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14883     if ($VMS_STATUS_SUCCESS(status)) {
14884         val_str[0] = _toupper(val_str[0]);
14885         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14886            decc_filename_unix_no_version = 1;
14887         }
14888     }
14889     status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14890     if ($VMS_STATUS_SUCCESS(status)) {
14891         val_str[0] = _toupper(val_str[0]);
14892         if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14893            decc_readdir_dropdotnotype = 1;
14894         }
14895     }
14896 #endif
14897
14898 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14899
14900      /* Report true case tolerance */
14901     /*----------------------------*/
14902     status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14903     if (!$VMS_STATUS_SUCCESS(status))
14904         case_perm = PPROP$K_CASE_BLIND;
14905     status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14906     if (!$VMS_STATUS_SUCCESS(status))
14907         case_image = PPROP$K_CASE_BLIND;
14908     if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14909         (case_image == PPROP$K_CASE_SENSITIVE))
14910         vms_process_case_tolerant = 0;
14911
14912 #endif
14913
14914     /* USE POSIX/DCL Exit codes - Recommended, but needs to default to  */
14915     /* for strict backward compatibilty */
14916     status = sys_trnlnm
14917         ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14918     if ($VMS_STATUS_SUCCESS(status)) {
14919        val_str[0] = _toupper(val_str[0]);
14920        if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14921          vms_posix_exit = 1;
14922        else
14923          vms_posix_exit = 0;
14924     }
14925
14926
14927     /* CRTL can be initialized past this point, but not before. */
14928 /*    DECC$CRTL_INIT(); */
14929
14930     return SS$_NORMAL;
14931 }
14932
14933 #ifdef __DECC
14934 #pragma nostandard
14935 #pragma extern_model save
14936 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14937         const __align (LONGWORD) int spare[8] = {0};
14938
14939 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14940 #if __DECC_VER >= 60560002
14941 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14942 #else
14943 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14944 #endif
14945 #endif /* __DECC */
14946
14947 const long vms_cc_features = (const long)set_features;
14948
14949 /*
14950 ** Force a reference to LIB$INITIALIZE to ensure it
14951 ** exists in the image.
14952 */
14953 int lib$initialize(void);
14954 #ifdef __DECC
14955 #pragma extern_model strict_refdef
14956 #endif
14957     int lib_init_ref = (int) lib$initialize;
14958
14959 #ifdef __DECC
14960 #pragma extern_model restore
14961 #pragma standard
14962 #endif
14963
14964 /*  End of vms.c */