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
CommitLineData
b429d381 1/* vms.c
a0d0e21e 2 *
82dd182c 3 * VMS-specific routines for perl5
748a9306 4 *
82dd182c
CB
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.
a0d0e21e
LW
12 */
13
7c884029 14/*
4ac71550
TC
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
7c884029 22 *
4ac71550 23 * [p.162 of _The Lays of Beleriand_]
7c884029
CB
24 */
25
a0d0e21e
LW
26#include <acedef.h>
27#include <acldef.h>
28#include <armdef.h>
748a9306 29#include <atrdef.h>
a0d0e21e 30#include <chpdef.h>
8fde5078 31#include <clidef.h>
a3e9d8c9 32#include <climsgdef.h>
cd1191f1 33#include <dcdef.h>
a0d0e21e 34#include <descrip.h>
22d4bb9c 35#include <devdef.h>
a0d0e21e 36#include <dvidef.h>
748a9306 37#include <fibdef.h>
a0d0e21e
LW
38#include <float.h>
39#include <fscndef.h>
40#include <iodef.h>
41#include <jpidef.h>
61bb5906 42#include <kgbdef.h>
f675dbe5 43#include <libclidef.h>
a0d0e21e
LW
44#include <libdef.h>
45#include <lib$routines.h>
46#include <lnmdef.h>
aeb5cf3c 47#include <msgdef.h>
4fdf8f88 48#include <ossdef.h>
f7ddb74a
JM
49#if __CRTL_VER >= 70301000 && !defined(__VAX)
50#include <ppropdef.h>
51#endif
748a9306 52#include <prvdef.h>
a0d0e21e
LW
53#include <psldef.h>
54#include <rms.h>
55#include <shrdef.h>
56#include <ssdef.h>
57#include <starlet.h>
f86702cc 58#include <strdef.h>
59#include <str$routines.h>
a0d0e21e 60#include <syidef.h>
748a9306
LW
61#include <uaidef.h>
62#include <uicdef.h>
2fbb330f
JM
63#include <stsdef.h>
64#include <rmsdef.h>
cd1191f1 65#include <smgdef.h>
cfcfe586
JM
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
a0d0e21e 72
f7ddb74a
JM
73#if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
74int decc$feature_get_index(const char *name);
75char* decc$feature_get_name(int index);
76int decc$feature_get_value(int index, int mode);
77int decc$feature_set_value(int index, int mode, int value);
78#else
79#include <unixlib.h>
80#endif
81
cfcfe586
JM
82#pragma member_alignment save
83#pragma nomember_alignment longword
84struct 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
cfcfe586
JM
97int 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);
7566800d 106#endif
cfcfe586 107
4fdf8f88
JM
108#ifdef sys$get_security
109#undef sys$get_security
110int 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
122int 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
8cb5d3d5
JM
132#ifdef lib$find_image_symbol
133#undef lib$find_image_symbol
134int 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);
4fdf8f88 140#endif
8cb5d3d5 141
4fdf8f88
JM
142#ifdef lib$rename_file
143#undef lib$rename_file
144int 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);
8cb5d3d5
JM
167#endif
168
7a7fd8e0 169#if __CRTL_VER >= 70300000 && !defined(__VAX)
f7ddb74a
JM
170
171static 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
188return 0;
189}
190#endif
f7ddb74a 191
740ce14c 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
b7ae7a0d 197# define SS$_NOSUCHOBJECT 2696
198#endif
199
a15cef0c
CB
200/* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
201#define PERLIO_NOT_STDIO 0
202
2497a41f 203/* Don't replace system definitions of vfork, getenv, lstat, and stat,
aa689395 204 * code below needs to get to the underlying CRTL routines. */
205#define DONT_MASK_RTL_CALLS
a0d0e21e
LW
206#include "EXTERN.h"
207#include "perl.h"
748a9306 208#include "XSUB.h"
3eeba6fb
CB
209/* Anticipating future expansion in lexical warnings . . . */
210#ifndef WARN_INTERNAL
211# define WARN_INTERNAL WARN_MISC
212#endif
a0d0e21e 213
988c775c
JM
214#ifdef VMS_LONGNAME_SUPPORT
215#include <libfildef.h>
216#endif
217
22d4bb9c
CB
218#if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
219# define RTL_USES_UTC 1
220#endif
221
58472d87
CB
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
5f1992ed
CB
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
8cb5d3d5 236static int (*decw_term_port)
5f1992ed
CB
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,
8cb5d3d5 244 void * char_change_buffer) = 0;
22d4bb9c 245
c07a80fd 246/* gcc's header files don't #define direct access macros
247 * corresponding to VAXC's variant structs */
248#ifdef __GNUC__
482b294c 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
c07a80fd 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
c645ec3f
GS
258#if defined(NEED_AN_H_ERRNO)
259dEXT int h_errno;
260#endif
c07a80fd 261
f7ddb74a
JM
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
a0d0e21e
LW
269struct itmlst_3 {
270 unsigned short int buflen;
271 unsigned short int itmcode;
272 void *bufadr;
748a9306 273 unsigned short int *retlen;
a0d0e21e 274};
657054d4
JM
275
276struct filescan_itmlst_2 {
277 unsigned short length;
278 unsigned short itmcode;
279 char * component;
280};
281
dca5a913
JM
282struct vs_str_st {
283 unsigned short length;
284 char str[65536];
285};
286
f7ddb74a
JM
287#ifdef __DECC
288#pragma message restore
289#pragma member_alignment restore
290#endif
a0d0e21e 291
360732b5
JM
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)
b1a8dcd7 298#define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
360732b5
JM
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)
f7ddb74a 301#define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
4b19af01
CB
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
360732b5
JM
305static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
306static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
307static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
308static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
f7ddb74a 309
6fb6c614
JM
310static char * int_rmsexpand_vms(
311 const char * filespec, char * outbuf, unsigned opts);
312static char * int_rmsexpand_tovms(
313 const char * filespec, char * outbuf, unsigned opts);
df278665
JM
314static char *int_tovmsspec
315 (const char *path, char *buf, int dir_flag, int * utf8_flag);
a979ce91 316static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
0e5ce2c7 317static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
4846f1d7 318static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
df278665 319
0e06870b
CB
320/* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
321#define PERL_LNM_MAX_ALLOWED_INDEX 127
322
2d9f3838
CB
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
2497a41f
JM
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
ff7adb52 337
01b8edb6 338static char *__mystrtolower(char *str)
339{
340 if (str) for (; *str; ++str) *str= tolower(*str);
341 return str;
342}
343
f675dbe5
CB
344static struct dsc$descriptor_s fildevdsc =
345 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
346static struct dsc$descriptor_s crtlenvdsc =
347 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
348static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
349static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
350static struct dsc$descriptor_s **env_tables = defenv;
351static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
352
93948341
CB
353/* True if we shouldn't treat barewords as logicals during directory */
354/* munching */
355static int no_translate_barewords;
356
22d4bb9c
CB
357#ifndef RTL_USES_UTC
358static int tz_updated = 1;
359#endif
360
f7ddb74a
JM
361/* DECC Features that may need to affect how Perl interprets
362 * displays filename information
363 */
364static int decc_disable_to_vms_logname_translation = 1;
365static int decc_disable_posix_root = 1;
366int decc_efs_case_preserve = 0;
367static int decc_efs_charset = 0;
b53f3677 368static int decc_efs_charset_index = -1;
f7ddb74a
JM
369static int decc_filename_unix_no_version = 0;
370static int decc_filename_unix_only = 0;
371int decc_filename_unix_report = 0;
372int decc_posix_compliant_pathnames = 0;
373int decc_readdir_dropdotnotype = 0;
374static int vms_process_case_tolerant = 1;
360732b5
JM
375int vms_vtf7_filenames = 0;
376int gnv_unix_shell = 0;
e0e5e8d6 377static int vms_unlink_all_versions = 0;
1a3aec58 378static int vms_posix_exit = 0;
f7ddb74a 379
2497a41f 380/* bug workarounds if needed */
682e4b71 381int decc_bug_devnull = 1;
2497a41f 382int decc_dir_barename = 0;
b53f3677 383int vms_bug_stat_filename = 0;
2497a41f 384
9c1171d1 385static int vms_debug_on_exception = 0;
b53f3677
JM
386static int vms_debug_fileify = 0;
387
388/* Simple logical name translation */
389static 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
9c1171d1 420
f7ddb74a
JM
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
657054d4 429static int is_unix_filespec(const char *path)
f7ddb74a
JM
430{
431int ret_val;
432const 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
360732b5
JM
451/* This routine converts a UCS-2 character to be VTF-7 encoded.
452 */
453
454static void ucs2_to_vtf7
455 (char *outspec,
456 unsigned long ucs2_char,
457 int * output_cnt)
458{
459unsigned char * ucs_ptr;
460int 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 */
501static int copy_expand_unix_filename_escape
502 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
503{
504int count;
505int scnt;
506int 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
38a44b82 552 /* High bit set, but not a Unicode character! */
360732b5
JM
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 '^':
449de3c2 654 case '\\':
adc11f0b
CB
655 /* Don't escape again if following character is
656 * already something we escape.
657 */
449de3c2 658 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
adc11f0b
CB
659 *outspec = *inspec;
660 *output_cnt = 1;
661 return 1;
662 break;
663 }
664 /* But otherwise fall through and escape it. */
360732b5
JM
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
657054d4
JM
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 */
697static int copy_expand_vms_filename_escape
698 (char *outspec, const char *inspec, int *output_cnt)
699{
700int count;
701int scnt;
702
703 count = 0;
704 *output_cnt = 0;
705 if (*inspec == '^') {
706 inspec++;
707 switch (*inspec) {
adc11f0b
CB
708 /* Spaces and non-trailing dots should just be passed through,
709 * but eat the escape character.
710 */
657054d4 711 case '.':
657054d4 712 *outspec = *inspec;
adc11f0b
CB
713 count += 2;
714 (*output_cnt)++;
657054d4
JM
715 break;
716 case '_': /* space */
717 *outspec = ' ';
adc11f0b 718 count += 2;
657054d4
JM
719 (*output_cnt)++;
720 break;
adc11f0b
CB
721 case '^':
722 /* Hmm. Better leave the escape escaped. */
723 outspec[0] = '^';
724 outspec[1] = '^';
725 count += 2;
726 (*output_cnt) += 2;
727 break;
360732b5 728 case 'U': /* Unicode - FIX-ME this is wrong. */
657054d4
JM
729 inspec++;
730 count++;
731 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
732 if (scnt == 4) {
2f4077ca
JM
733 unsigned int c1, c2;
734 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
735 outspec[0] == c1 & 0xff;
736 outspec[1] == c2 & 0xff;
657054d4
JM
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 */
2f4077ca
JM
756 unsigned int c1;
757 scnt = sscanf(inspec, "%2x", &c1);
758 outspec[0] = c1 & 0xff;
657054d4
JM
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
7566800d
CB
779#ifdef sys$filescan
780#undef sys$filescan
781int sys$filescan
657054d4
JM
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);
7566800d 787#endif
657054d4
JM
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 */
798static int vms_split_path
360732b5 799 (const char * path,
dca5a913 800 char * * volume,
657054d4 801 int * vol_len,
dca5a913 802 char * * root,
657054d4 803 int * root_len,
dca5a913 804 char * * dir,
657054d4 805 int * dir_len,
dca5a913 806 char * * name,
657054d4 807 int * name_len,
dca5a913 808 char * * ext,
657054d4 809 int * ext_len,
dca5a913 810 char * * version,
657054d4
JM
811 int * ver_len)
812{
813struct dsc$descriptor path_desc;
814int status;
815unsigned long flags;
816int ret_stat;
817struct filescan_itmlst_2 item_list[9];
818const int filespec = 0;
819const int nodespec = 1;
820const int devspec = 2;
821const int rootspec = 3;
822const int dirspec = 4;
823const int namespec = 5;
824const int typespec = 6;
825const 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
7566800d 893 status = sys$filescan
657054d4
JM
894 ((const struct dsc$descriptor_s *)&path_desc, item_list,
895 &flags, NULL, NULL);
360732b5 896 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
657054d4
JM
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
df278665
JM
946/* Routine to determine if the file specification ends with .dir */
947static 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
f7ddb74a 977
fa537f88
CB
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 */
f7ddb74a 984/*{{{int my_maxidx(const char *lnm) */
fa537f88 985static int
f7ddb74a 986my_maxidx(const char *lnm)
fa537f88
CB
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;
f7ddb74a 998 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
fa537f88
CB
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
f675dbe5 1008/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
c07a80fd 1009int
fd8cd3a3 1010Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
f675dbe5 1011 struct dsc$descriptor_s **tabvec, unsigned long int flags)
748a9306 1012{
f7ddb74a
JM
1013 const char *cp1;
1014 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
f675dbe5 1015 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
748a9306 1016 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
fa537f88 1017 int midx;
f675dbe5
CB
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},
748a9306 1023 {0, 0, 0, 0}};
f675dbe5 1024 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
fd8cd3a3
DS
1025#if defined(PERL_IMPLICIT_CONTEXT)
1026 pTHX = NULL;
fd8cd3a3
DS
1027 if (PL_curinterp) {
1028 aTHX = PERL_GET_INTERP;
cc077a9f 1029 } else {
fd8cd3a3 1030 aTHX = NULL;
cc077a9f
HM
1031 }
1032#endif
748a9306 1033
fa537f88 1034 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
b7ae7a0d 1035 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
1036 }
f7ddb74a 1037 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
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;
fd7385b9 1046 uplnm[lnmdsc.dsc$w_length] = '\0';
f675dbe5
CB
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;
ebd4d70b
JM
1058#if defined(PERL_IMPLICIT_CONTEXT)
1059 if (aTHX == NULL) {
1060 fprintf(stderr,
873f5ddf 1061 "Can't read CRTL environ\n");
ebd4d70b
JM
1062 } else
1063#endif
1064 Perl_warn(aTHX_ "Can't read CRTL environ\n");
f675dbe5
CB
1065 continue;
1066 }
1067 retsts = SS$_NOLOGNAM;
1068 for (i = 0; environ[i]; i++) {
1069 if ((eq = strchr(environ[i],'=')) &&
299d126a 1070 lnmdsc.dsc$w_length == (eq - environ[i]) &&
f675dbe5
CB
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 */
ebd4d70b 1088 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
f675dbe5
CB
1089 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1090 if (retsts & 1) {
2497a41f 1091 if (eqvlen > MAX_DCL_SYMBOL) {
f675dbe5 1092 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
2497a41f 1093 eqvlen = MAX_DCL_SYMBOL;
cc077a9f
HM
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 */
8a646e0b
JM
1098#if defined(PERL_IMPLICIT_CONTEXT)
1099 if (aTHX == NULL) {
1100 fprintf(stderr,
873f5ddf 1101 "Value of CLI symbol \"%s\" too long",lnm);
8a646e0b
JM
1102 } else
1103#endif
cc077a9f 1104 if (ckWARN(WARN_MISC)) {
f98bc0c6 1105 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
cc077a9f 1106 }
f675dbe5
CB
1107 }
1108 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1109 }
ebd4d70b 1110 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
f675dbe5
CB
1111 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1112 if (retsts == LIB$_NOSUCHSYM) continue;
1113 break;
1114 }
1115 }
1116 else if (!ivlnm) {
843027b0 1117 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
f7ddb74a
JM
1118 midx = my_maxidx(lnm);
1119 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1120 lnmlst[1].bufadr = cp2;
fa537f88
CB
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 (
fd7385b9 1127#if INTSIZE == 4
fa537f88 1128 *((int *)uplnm) == *((int *)"SYS$") &&
fd7385b9 1129#endif
fa537f88
CB
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")) ) ) {
18a3d61e 1135 memmove(eqv,eqv+4,eqvlen-4);
fa537f88
CB
1136 eqvlen -= 4;
1137 }
f7ddb74a
JM
1138 cp2 += eqvlen;
1139 *cp2 = '\0';
fa537f88
CB
1140 }
1141 if ((retsts == SS$_IVLOGNAM) ||
1142 (retsts == SS$_NOLOGNAM)) { continue; }
fd7385b9 1143 }
fa537f88 1144 else {
fa537f88
CB
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);
f675dbe5
CB
1151 break;
1152 }
c07a80fd 1153 }
f675dbe5
CB
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);
748a9306 1159 }
ebd4d70b 1160 else _ckvmssts_noperl(retsts);
f675dbe5
CB
1161 return 0;
1162} /* end of vmstrnenv */
1163/*}}}*/
c07a80fd 1164
f675dbe5
CB
1165/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1166/* Define as a function so we can access statics. */
4b19af01 1167int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
f675dbe5 1168{
8a646e0b
JM
1169 int flags = 0;
1170
1171#if defined(PERL_IMPLICIT_CONTEXT)
1172 if (aTHX != NULL)
1173#endif
f675dbe5 1174#ifdef SECURE_INTERNAL_GETENV
8a646e0b
JM
1175 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1176 PERL__TRNENV_SECURE : 0;
f675dbe5 1177#endif
8a646e0b
JM
1178
1179 return vmstrnenv(lnm, eqv, idx, fildev, flags);
f675dbe5
CB
1180}
1181/*}}}*/
a0d0e21e
LW
1182
1183/* my_getenv
61bb5906
CB
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.
a6c40364 1187 * We define this as a function rather than a macro in terms of my_getenv_len()
f675dbe5
CB
1188 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1189 * allocate SVs).
a0d0e21e 1190 */
f675dbe5 1191/*{{{ char *my_getenv(const char *lnm, bool sys)*/
a0d0e21e 1192char *
5c84aa53 1193Perl_my_getenv(pTHX_ const char *lnm, bool sys)
a0d0e21e 1194{
f7ddb74a 1195 const char *cp1;
fa537f88 1196 static char *__my_getenv_eqv = NULL;
f7ddb74a 1197 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
c07a80fd 1198 unsigned long int idx = 0;
bc10a425 1199 int trnsuccess, success, secure, saverr, savvmserr;
843027b0 1200 int midx, flags;
61bb5906 1201 SV *tmpsv;
a0d0e21e 1202
f7ddb74a 1203 midx = my_maxidx(lnm) + 1;
fa537f88 1204
6b88bc9c 1205 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
61bb5906
CB
1206 /* Set up a temporary buffer for the return value; Perl will
1207 * clean it up at the next statement transition */
fa537f88 1208 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
61bb5906
CB
1209 if (!tmpsv) return NULL;
1210 eqv = SvPVX(tmpsv);
1211 }
fa537f88
CB
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 {
a02a5408 1218 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1219 }
1220 eqv = __my_getenv_eqv;
1221 }
1222
f7ddb74a 1223 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1224 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
2497a41f 1225 int len;
61bb5906 1226 getcwd(eqv,LNM$C_NAMLENGTH);
2497a41f
JM
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 }
61bb5906 1242 return eqv;
748a9306 1243 }
a0d0e21e 1244 else {
2512681b 1245 /* Impose security constraints only if tainting */
bc10a425
CB
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 }
843027b0
CB
1251 else {
1252 secure = 0;
1253 }
1254
1255 flags =
f675dbe5 1256#ifdef SECURE_INTERNAL_GETENV
843027b0 1257 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1258#else
843027b0 1259 0
f675dbe5 1260#endif
843027b0
CB
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
bc10a425
CB
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);
4e205ed6 1286 return success ? eqv : NULL;
a0d0e21e 1287 }
a0d0e21e
LW
1288
1289} /* end of my_getenv() */
1290/*}}}*/
1291
f675dbe5 1292
a6c40364
GS
1293/*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1294char *
fd8cd3a3 1295Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
f675dbe5 1296{
f7ddb74a
JM
1297 const char *cp1;
1298 char *buf, *cp2;
a6c40364 1299 unsigned long idx = 0;
843027b0 1300 int midx, flags;
fa537f88 1301 static char *__my_getenv_len_eqv = NULL;
bc10a425 1302 int secure, saverr, savvmserr;
cc077a9f
HM
1303 SV *tmpsv;
1304
f7ddb74a 1305 midx = my_maxidx(lnm) + 1;
fa537f88 1306
cc077a9f
HM
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 */
fa537f88 1310 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
cc077a9f
HM
1311 if (!tmpsv) return NULL;
1312 buf = SvPVX(tmpsv);
1313 }
fa537f88
CB
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 {
a02a5408 1320 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1321 }
1322 buf = __my_getenv_len_eqv;
1323 }
1324
f7ddb74a 1325 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1326 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
f7ddb74a
JM
1327 char * zeros;
1328
f675dbe5 1329 getcwd(buf,LNM$C_NAMLENGTH);
a6c40364 1330 *len = strlen(buf);
f7ddb74a
JM
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 }
a6c40364 1343 return buf;
f675dbe5
CB
1344 }
1345 else {
bc10a425
CB
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 }
843027b0
CB
1351 else {
1352 secure = 0;
1353 }
1354
1355 flags =
f675dbe5 1356#ifdef SECURE_INTERNAL_GETENV
843027b0 1357 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1358#else
843027b0 1359 0
f675dbe5 1360#endif
843027b0
CB
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
f7ddb74a
JM
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
bc10a425
CB
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);
4e205ed6 1392 return *len ? buf : NULL;
f675dbe5
CB
1393 }
1394
a6c40364 1395} /* end of my_getenv_len() */
f675dbe5
CB
1396/*}}}*/
1397
8a646e0b 1398static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
8fde5078
CB
1399
1400static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1e422769 1401
740ce14c 1402/*{{{ void prime_env_iter() */
1403void
1404prime_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{
17f28c40 1409 static int primed = 0;
3eeba6fb 1410 HV *seenhv = NULL, *envhv;
22be8b3c 1411 SV *sv = NULL;
4e205ed6 1412 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
8fde5078
CB
1413 unsigned short int chan;
1414#ifndef CLI$M_TRUSTED
1415# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1416#endif
f675dbe5
CB
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);
fd8cd3a3
DS
1426#if defined(PERL_IMPLICIT_CONTEXT)
1427 pTHX;
1428#endif
3db8f154 1429#if defined(USE_ITHREADS)
b2b3adea
HM
1430 static perl_mutex primenv_mutex;
1431 MUTEX_INIT(&primenv_mutex);
61bb5906 1432#endif
740ce14c 1433
fd8cd3a3
DS
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 */
fd8cd3a3
DS
1439 if (PL_curinterp) {
1440 aTHX = PERL_GET_INTERP;
1441 } else {
ebd4d70b
JM
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 */
fd8cd3a3
DS
1448 aTHX = NULL;
1449 }
1450#endif
fd8cd3a3 1451
3eeba6fb 1452 if (primed || !PL_envgv) return;
61bb5906
CB
1453 MUTEX_LOCK(&primenv_mutex);
1454 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
3eeba6fb 1455 envhv = GvHVn(PL_envgv);
740ce14c 1456 /* Perform a dummy fetch as an lval to insure that the hash table is
8fde5078 1457 * set up. Otherwise, the hv_store() will turn into a nullop. */
740ce14c 1458 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
740ce14c 1459
f675dbe5
CB
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;
f02a1854 1463 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
8fde5078 1464 }
f675dbe5
CB
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));
61bb5906 1470 }
f675dbe5
CB
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],'='))) {
3eeba6fb 1478 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1479 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
f675dbe5
CB
1480 }
1481 else {
1482 start++;
22be8b3c
CB
1483 sv = newSVpv(start,0);
1484 SvTAINTED_on(sv);
1485 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
f675dbe5
CB
1486 }
1487 }
1488 continue;
740ce14c 1489 }
f675dbe5
CB
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);
a02a5408 1521 if (!buf) Newx(buf,mbxbufsiz + 1,char);
f675dbe5
CB
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]) {
5c84aa53 1544 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
f675dbe5
CB
1545 }
1546 continue;
1547 }
3eeba6fb 1548 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
f98bc0c6 1549 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
f675dbe5
CB
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++;
1f47e8e2
CB
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)) {
f98bc0c6 1570 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
edc7bc49
CB
1571 continue;
1572 }
5afd6d42 1573 PERL_HASH(hash,key,keylen);
ff79d39d
CB
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];
2497a41f 1580 char eqv[MAX_DCL_SYMBOL+1];
0faef845 1581 int trnlen;
ff79d39d 1582 strncpy(lnm, key, keylen);
0faef845 1583 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
ff79d39d
CB
1584 sv = newSVpvn(eqv, strlen(eqv));
1585 }
1586 else {
1587 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1588 }
1589
22be8b3c
CB
1590 SvTAINTED_on(sv);
1591 hv_store(envhv,key,keylen,sv,hash);
f675dbe5 1592 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
edc7bc49 1593 }
f675dbe5
CB
1594 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1595 /* get the PPFs for this process, not the subprocess */
f7ddb74a 1596 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
f675dbe5
CB
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);
22be8b3c
CB
1601 sv = newSVpv(eqv,trnlen);
1602 SvTAINTED_on(sv);
1603 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
f675dbe5 1604 }
740ce14c 1605 }
1606 }
f675dbe5
CB
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
740ce14c 1614} /* end of prime_env_iter */
1615/*}}}*/
740ce14c 1616
f675dbe5 1617
2c590a56 1618/*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
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.
3eeba6fb 1623 * Like setenv() returns 0 for success, non-zero on error.
a0d0e21e 1624 */
f675dbe5 1625int
2c590a56 1626Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
a0d0e21e 1627{
f7ddb74a
JM
1628 const char *cp1;
1629 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
f675dbe5 1630 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
fa537f88 1631 int nseg = 0, j;
a0d0e21e 1632 unsigned long int retsts, usermode = PSL$C_USER;
fa537f88 1633 struct itmlst_3 *ile, *ilist;
a0d0e21e 1634 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
f675dbe5
CB
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
ed253963
CB
1640 if (!lnm) {
1641 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1642 return SS$_IVLOGNAM;
1643 }
1644
f7ddb74a 1645 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
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 }
a0d0e21e 1652 lnmdsc.dsc$w_length = cp1 - lnm;
f675dbe5
CB
1653 if (!tabvec || !*tabvec) tabvec = env_tables;
1654
3eeba6fb 1655 if (!eqv) { /* we're deleting n element */
f675dbe5
CB
1656 for (curtab = 0; tabvec[curtab]; curtab++) {
1657 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1658 int i;
299d126a 1659 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
f675dbe5 1660 if ((cp1 = strchr(environ[i],'=')) &&
299d126a 1661 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
f675dbe5 1662 !strncmp(environ[i],lnm,cp1 - environ[i])) {
3eeba6fb 1663#ifdef HAS_SETENV
0e06870b 1664 return setenv(lnm,"",1) ? vaxc$errno : 0;
f675dbe5
CB
1665 }
1666 }
1667 ivenv = 1; retsts = SS$_NOLOGNAM;
1668#else
3eeba6fb 1669 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1670 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
3eeba6fb
CB
1671 ivenv = 1; retsts = SS$_NOSUCHPGM;
1672 break;
1673 }
1674 }
f675dbe5
CB
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);
3eeba6fb
CB
1686 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1687 if (retsts == LIB$_NOSUCHSYM) continue;
f675dbe5
CB
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 }
a0d0e21e
LW
1697 }
1698 }
f675dbe5
CB
1699 else { /* we're defining a value */
1700 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1701#ifdef HAS_SETENV
3eeba6fb 1702 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
f675dbe5 1703#else
3eeba6fb 1704 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1705 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
f675dbe5
CB
1706 retsts = SS$_NOSUCHPGM;
1707#endif
1708 }
1709 else {
f7ddb74a 1710 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
f675dbe5
CB
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 }
3eeba6fb
CB
1722 else {
1723 if (!*eqv) eqvdsc.dsc$w_length = 1;
a1dfe751 1724 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
fa537f88
CB
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
a02a5408 1734 Newx(ilist,nseg+1,struct itmlst_3);
fa537f88
CB
1735 ile = ilist;
1736 if (!ile) {
1737 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1738 return SS$_INSFMEM;
a1dfe751 1739 }
fa537f88
CB
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);
a1dfe751 1760 }
3eeba6fb 1761 }
f675dbe5
CB
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:
7d2497bf 1773 set_errno(EACCES); break;
f675dbe5
CB
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 */
a0d0e21e 1780 }
3eeba6fb
CB
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 */
f675dbe5
CB
1790 set_errno(0); set_vaxc_errno(retsts);
1791 return 0;
1792 }
1793
1794} /* end of vmssetenv() */
1795/*}}}*/
a0d0e21e 1796
2c590a56 1797/*{{{ void my_setenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1798/* This has to be a function since there's a prototype for it in proto.h */
1799void
2c590a56 1800Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
f675dbe5 1801{
bc10a425
CB
1802 if (lnm && *lnm) {
1803 int len = strlen(lnm);
1804 if (len == 7) {
1805 char uplnm[8];
22d4bb9c
CB
1806 int i;
1807 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
bc10a425 1808 if (!strcmp(uplnm,"DEFAULT")) {
7ded3206 1809 if (eqv && *eqv) my_chdir(eqv);
bc10a425
CB
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;
22d4bb9c
CB
1821 }
1822#endif
1823 }
f675dbe5
CB
1824 (void) vmssetenv(lnm,eqv,NULL);
1825}
a0d0e21e
LW
1826/*}}}*/
1827
27c67b75 1828/*{{{static void vmssetuserlnm(char *name, char *eqv); */
0e06870b
CB
1829/* vmssetuserlnm
1830 * sets a user-mode logical in the process logical name table
1831 * used for redirection of sys$error
4d9538c1
JM
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 *
0e06870b
CB
1837 */
1838void
2fbb330f 1839Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
0e06870b
CB
1840{
1841 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1842 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
2d5e9e5d 1843 unsigned long int iss, attr = LNM$M_CONFINE;
0e06870b
CB
1844 unsigned char acmode = PSL$C_USER;
1845 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1846 {0, 0, 0, 0}};
2fbb330f 1847 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
0e06870b
CB
1848 d_name.dsc$w_length = strlen(name);
1849
1850 lnmlst[0].buflen = strlen(eqv);
2fbb330f 1851 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
0e06870b
CB
1852
1853 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1854 if (!(iss&1)) lib$signal(iss);
1855}
1856/*}}}*/
c07a80fd 1857
f675dbe5 1858
c07a80fd 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.
2497a41f
JM
1868 *
1869 * - fix me to call ACM services when available
c07a80fd 1870 */
1871char *
fd8cd3a3 1872Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
c07a80fd 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) {
f282b18d 1897 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
c07a80fd 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
360732b5
JM
1922static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1923static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1924static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
a0d0e21e 1925
2497a41f
JM
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 */
1932static 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
e0e5e8d6
JM
1940/* 8.3, remove() is now broken on symbolic links */
1941static int rms_erase(const char * vmsname);
1942
1943
2497a41f
JM
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)*/
1956static int
1957mp_do_kill_file(pTHX_ const char *name, int dirflag)
1958{
e0e5e8d6
JM
1959 char *vmsname;
1960 char *rslt;
2497a41f
JM
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. */
c5375c28 1984 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
ebd4d70b 1985 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 1986
6fb6c614 1987 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
e0e5e8d6 1988 if (rslt == NULL) {
c5375c28 1989 PerlMem_free(vmsname);
2497a41f
JM
1990 return -1;
1991 }
c5375c28 1992
e0e5e8d6
JM
1993 /* Erase the file */
1994 rmsts = rms_erase(vmsname);
2497a41f 1995
e0e5e8d6
JM
1996 /* Did it succeed */
1997 if ($VMS_STATUS_SUCCESS(rmsts)) {
1998 PerlMem_free(vmsname);
1999 return 0;
2497a41f
JM
2000 }
2001
2002 /* If not, can changing protections help? */
e0e5e8d6
JM
2003 if (rmsts != RMS$_PRV) {
2004 set_vaxc_errno(rmsts);
2005 PerlMem_free(vmsname);
2497a41f
JM
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 */
ebd4d70b 2013 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
e0e5e8d6
JM
2014 fildsc.dsc$w_length = strlen(vmsname);
2015 fildsc.dsc$a_pointer = vmsname;
2497a41f
JM
2016 cxt = 0;
2017 newace.myace$l_ident = oldace.myace$l_ident;
e0e5e8d6 2018 rmsts = -1;
2497a41f
JM
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:
ebd4d70b 2032 _ckvmssts_noperl(aclsts);
2497a41f
JM
2033 }
2034 set_vaxc_errno(aclsts);
e0e5e8d6 2035 PerlMem_free(vmsname);
2497a41f
JM
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
e0e5e8d6
JM
2046 rmsts = rms_erase(vmsname);
2047 if ($VMS_STATUS_SUCCESS(rmsts)) {
2048 rmsts = 0;
2497a41f
JM
2049 }
2050 else {
e0e5e8d6 2051 rmsts = -1;
2497a41f
JM
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);
2497a41f
JM
2075 }
2076
e0e5e8d6 2077 PerlMem_free(vmsname);
2497a41f
JM
2078 return rmsts;
2079
2080} /* end of kill_file() */
2081/*}}}*/
2082
2083
a0d0e21e
LW
2084/*{{{int do_rmdir(char *name)*/
2085int
b8ffc8df 2086Perl_do_rmdir(pTHX_ const char *name)
a0d0e21e 2087{
e0e5e8d6 2088 char * dirfile;
a0d0e21e 2089 int retval;
61bb5906 2090 Stat_t st;
a0d0e21e 2091
d94c5a78
JM
2092 /* lstat returns a VMS fileified specification of the name */
2093 /* that is looked up, and also lets verifies that this is a directory */
e0e5e8d6 2094
46c05374 2095 retval = flex_lstat(name, &st);
d94c5a78
JM
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 }
e0e5e8d6 2114 }
d94c5a78
JM
2115
2116 if (!S_ISDIR(st.st_mode)) {
e0e5e8d6
JM
2117 errno = ENOTDIR;
2118 retval = -1;
2119 }
d94c5a78
JM
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
e0e5e8d6 2131 retval = mp_do_kill_file(aTHX_ dirfile, 1);
d94c5a78 2132 }
e0e5e8d6 2133
a0d0e21e
LW
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)*/
2148int
b8ffc8df 2149Perl_kill_file(pTHX_ const char *name)
a0d0e21e 2150{
d94c5a78 2151 char * vmsfile;
e0e5e8d6
JM
2152 Stat_t st;
2153 int rmsts;
a0d0e21e 2154
d94c5a78
JM
2155 /* Convert the filename to VMS format and see if it is a directory */
2156 /* flex_lstat returns a vmsified file specification */
46c05374 2157 rmsts = flex_lstat(name, &st);
d94c5a78
JM
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.
a0d0e21e 2182 */
d94c5a78
JM
2183 if (S_ISDIR(st.st_mode)) {
2184 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2185 return rmsts;
a0d0e21e
LW
2186 }
2187
d94c5a78
JM
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 }
a0d0e21e
LW
2211
2212 return rmsts;
2213
2214} /* end of kill_file() */
2215/*}}}*/
2216
8cc95fdb 2217
84902520 2218/*{{{int my_mkdir(char *,Mode_t)*/
8cc95fdb 2219int
b8ffc8df 2220Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
8cc95fdb 2221{
2222 STRLEN dirlen = strlen(dir);
2223
a2a90019
CB
2224 /* zero length string sometimes gives ACCVIO */
2225 if (dirlen == 0) return -1;
2226
8cc95fdb 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
ee8c7f54
CB
2241/*{{{int my_chdir(char *)*/
2242int
b8ffc8df 2243Perl_my_chdir(pTHX_ const char *dir)
ee8c7f54
CB
2244{
2245 STRLEN dirlen = strlen(dir);
ee8c7f54
CB
2246
2247 /* zero length string sometimes gives ACCVIO */
2248 if (dirlen == 0) return -1;
f7ddb74a
JM
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 }
ee8c7f54
CB
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.
f7ddb74a 2265 *
4d9538c1 2266 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
ee8c7f54 2267 */
f7ddb74a 2268 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
4d9538c1
JM
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;
ee8c7f54 2279 }
dca5a913 2280 else return chdir(dir1);
ee8c7f54
CB
2281} /* end of my_chdir */
2282/*}}}*/
8cc95fdb 2283
674d6c38 2284
f1db9cda
JM
2285/*{{{int my_chmod(char *, mode_t)*/
2286int
2287Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2288{
4d9538c1
JM
2289 Stat_t st;
2290 int ret = -1;
2291 char * changefile;
f1db9cda
JM
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 */
4d9538c1
JM
2304 changefile = (char *) file_spec; /* cast ok */
2305 ret = flex_lstat(file_spec, &st);
2306 if (ret != 0) {
f1db9cda 2307
4d9538c1
JM
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;
f1db9cda 2314
4d9538c1
JM
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 }
f1db9cda 2321 }
4d9538c1
JM
2322 ret = chmod(changefile, mode);
2323 return ret;
f1db9cda
JM
2324} /* end of my_chmod */
2325/*}}}*/
2326
2327
674d6c38
CB
2328/*{{{FILE *my_tmpfile()*/
2329FILE *
2330my_tmpfile(void)
2331{
2332 FILE *fp;
2333 char *cp;
674d6c38
CB
2334
2335 if ((fp = tmpfile())) return fp;
2336
c5375c28
JM
2337 cp = PerlMem_malloc(L_tmpnam+24);
2338 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2339
2497a41f
JM
2340 if (decc_filename_unix_only == 0)
2341 strcpy(cp,"Sys$Scratch:");
2342 else
2343 strcpy(cp,"/tmp/");
674d6c38
CB
2344 tmpnam(cp+strlen(cp));
2345 strcat(cp,".Perltmp");
2346 fp = fopen(cp,"w+","fop=dlt");
c5375c28 2347 PerlMem_free(cp);
674d6c38
CB
2348 return fp;
2349}
2350/*}}}*/
2351
5c2d7af2
CB
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*);*/
2360int
2361Perl_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
f2610a60
CL
2373#ifdef KILL_BY_SIGPRC
2374#include <errnodef.h>
2375
05c058bc
CB
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
f2610a60
CL
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
05c058bc
CB
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
f2610a60
CL
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
fe1de8ce 2408#define _MY_SIG_MAX 28
f2610a60 2409
9c1171d1
JM
2410static unsigned int
2411Perl_sig_to_vmscondition_int(int sig)
f2610a60 2412{
2e34cc90 2413 static unsigned int sig_code[_MY_SIG_MAX+1] =
f2610a60
CL
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 */
fe1de8ce
CB
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 */
f2610a60
CL
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;
fe1de8ce
CB
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
f2610a60
CL
2462 }
2463#endif
2464
2e34cc90
CL
2465 if (sig < _SIG_MIN) return 0;
2466 if (sig > _MY_SIG_MAX) return 0;
2467 return sig_code[sig];
2468}
2469
9c1171d1
JM
2470unsigned int
2471Perl_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
2e34cc90
CL
2481int
2482Perl_my_kill(int pid, int sig)
2483{
218fdd94 2484 dTHX;
2e34cc90
CL
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
7a7fd8e0
JM
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
9c1171d1 2516 code = Perl_sig_to_vmscondition_int(sig);
2e34cc90 2517
7a7fd8e0
JM
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);
f2610a60
CL
2533 return -1;
2534 }
2535
2e34cc90 2536 iss = sys$sigprc((unsigned int *)&pid,0,code);
f2610a60
CL
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:
ebd4d70b 2549 _ckvmssts_noperl(iss);
f2610a60
CL
2550 set_errno(EVMSERR);
2551 }
2552 set_vaxc_errno(iss);
2553
2554 return -1;
2555}
2556#endif
2557
2fbb330f
JM
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
7a7fd8e0 2579int Perl_vms_status_to_unix(int vms_status, int child_flag)
2fbb330f
JM
2580{
2581int facility;
2582int fac_sp;
2583int msg_no;
2584int msg_status;
2585int 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
0968cdad 2599 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2fbb330f
JM
2600 switch(msg_no) {
2601 case SS$_NORMAL:
2602 unix_status = 0;
2603 break;
2604 case SS$_ACCVIO:
2605 unix_status = EFAULT;
2606 break;
7a7fd8e0
JM
2607 case SS$_DEVOFFLINE:
2608 unix_status = EBUSY;
2609 break;
2610 case SS$_CLEARED:
2611 unix_status = ENOTCONN;
2612 break;
2613 case SS$_IVCHAN:
2fbb330f
JM
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;
7a7fd8e0
JM
2625 case SS$_UNSUPPORTED:
2626 unix_status = ENOTSUP;
2627 break;
2fbb330f
JM
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;
fb38d079
JM
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 */
2fbb330f
JM
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)) {
7a7fd8e0 2677 unix_status = (msg_no & 0x07F8) >> 3;
2fbb330f
JM
2678 }
2679 else {
7a7fd8e0
JM
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 /*--------------------------------------------*/
2fbb330f
JM
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;
7a7fd8e0
JM
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;
2fbb330f
JM
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;
ed1b9de0
JM
2745 case RMS$_MKD: /* Failed to mark for delete */
2746 unix_status = EPERM;
2747 break;
2fbb330f
JM
2748 /* case RMS$_NMF: */ /* No more files */
2749 }
2750 }
2751 }
2752
2753 return unix_status;
2754}
2755
7a7fd8e0
JM
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
2761int Perl_unix_status_to_vms(int unix_status)
2762{
2763int 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 */
0968cdad 2806 case EBUSY: return SS$_DEVOFFLINE;
7a7fd8e0
JM
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}
2fbb330f
JM
2885
2886
22d4bb9c
CB
2887/* default piping mailbox size */
2888#define PERL_BUFSIZ 512
2889
674d6c38 2890
a0d0e21e 2891static void
8a646e0b 2892create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
a0d0e21e 2893{
22d4bb9c
CB
2894 unsigned long int mbxbufsiz;
2895 static unsigned long int syssize = 0;
2896 unsigned long int dviitm = DVI$_DEVNAM;
22d4bb9c 2897 char csize[LNM$C_NAMLENGTH+1];
f7ddb74a
JM
2898 int sts;
2899
22d4bb9c
CB
2900 if (!syssize) {
2901 unsigned long syiitm = SYI$_MAXBUF;
a0d0e21e 2902 /*
22d4bb9c
CB
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 *
a0d0e21e 2909 */
ebd4d70b 2910 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
22d4bb9c
CB
2911 }
2912
2913 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2914 mbxbufsiz = atoi(csize);
2915 } else {
2916 mbxbufsiz = PERL_BUFSIZ;
a0d0e21e 2917 }
22d4bb9c
CB
2918 if (mbxbufsiz < 128) mbxbufsiz = 128;
2919 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2920
ebd4d70b 2921 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
a0d0e21e 2922
ebd4d70b
JM
2923 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2924 _ckvmssts_noperl(sts);
a0d0e21e
LW
2925 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2926
2927} /* end of create_mbx() */
2928
22d4bb9c 2929
a0d0e21e 2930/*{{{ my_popen and my_pclose*/
22d4bb9c
CB
2931
2932typedef struct _iosb IOSB;
2933typedef struct _iosb* pIOSB;
2934typedef struct _pipe Pipe;
2935typedef struct _pipe* pPipe;
2936typedef struct pipe_details Info;
2937typedef struct pipe_details* pInfo;
2938typedef struct _srqp RQE;
2939typedef struct _srqp* pRQE;
2940typedef struct _tochildbuf CBuf;
2941typedef struct _tochildbuf* pCBuf;
2942
2943struct _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
2951struct _srqp { /* VMS self-relative queue entry */
2952 unsigned long qptr[2];
2953};
2954#pragma member_alignment restore
2955static RQE RQE_ZERO = {0,0};
2956
2957struct _tochildbuf {
2958 RQE q;
2959 int eof;
2960 unsigned short size;
2961 char *buf;
2962};
2963
2964struct _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;
fd8cd3a3
DS
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
22d4bb9c
CB
2987};
2988
2989
a0d0e21e
LW
2990struct pipe_details
2991{
22d4bb9c 2992 pInfo next;
ff7adb52
CL
2993 PerlIO *fp; /* file pointer to pipe mailbox */
2994 int useFILE; /* using stdio, not perlio */
748a9306
LW
2995 int pid; /* PID of subprocess */
2996 int mode; /* == 'r' if pipe open for reading */
2997 int done; /* subprocess has completed */
ff7adb52 2998 int waiting; /* waiting for completion/closure */
22d4bb9c
CB
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;
cd1191f1
CB
3007 unsigned short xchan; /* channel to debug xterm */
3008 unsigned short xchan_valid; /* channel is assigned */
a0d0e21e
LW
3009};
3010
748a9306
LW
3011struct 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
d85f548a
JH
3020typedef struct _closed_pipes Xpipe;
3021typedef struct _closed_pipes* pXpipe;
3022
3023struct _closed_pipes {
3024 int pid; /* PID of subprocess */
3025 unsigned long completion; /* termination status of subprocess */
3026};
3027#define NKEEPCLOSED 50
3028static Xpipe closed_list[NKEEPCLOSED];
3029static int closed_index = 0;
3030static int closed_num = 0;
3031
22d4bb9c
CB
3032#define RETRY_DELAY "0 ::0.20"
3033#define MAX_RETRY 50
a0d0e21e 3034
22d4bb9c
CB
3035static int pipe_ef = 0; /* first call to safe_popen inits these*/
3036static unsigned long mypid;
3037static unsigned long delaytime[2];
3038
3039static pInfo open_pipes = NULL;
3040static $DESCRIPTOR(nl_desc, "NL:");
3eeba6fb 3041
ff7adb52
CL
3042#define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
3043
3044
3eeba6fb 3045
748a9306 3046static unsigned long int
ebd4d70b 3047pipe_exit_routine()
748a9306 3048{
22d4bb9c 3049 pInfo info;
1e422769 3050 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
ff7adb52
CL
3051 int sts, did_stuff, need_eof, j;
3052
5ce486e0
CB
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.
ff7adb52
CL
3057 */
3058 info = open_pipes;
3059 while (info) {
3060 if (info->fp) {
ebd4d70b
JM
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
5ce486e0
CB
3072 if (!info->useFILE
3073#if defined(USE_ITHREADS)
3074 && my_perl
3075#endif
3076 && PL_perlio_fd_refcnt)
3077 PerlIO_flush(info->fp);
ff7adb52
CL
3078 else
3079 fflush((FILE *)info->fp);
3080 }
3081 info = info->next;
3082 }
3eeba6fb
CB
3083
3084 /*
ff7adb52 3085 next we try sending an EOF...ignore if doesn't work, make sure we
3eeba6fb
CB
3086 don't hang
3087 */
3088 did_stuff = 0;
3089 info = open_pipes;
748a9306 3090
3eeba6fb 3091 while (info) {
b2b89246 3092 int need_eof;
d4c83939 3093 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 3094 if (info->in && !info->in->shut_on_empty) {
d4c83939 3095 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
ebd4d70b 3096 0, 0, 0, 0, 0, 0));
ff7adb52 3097 info->waiting = 1;
22d4bb9c 3098 did_stuff = 1;
748a9306 3099 }
d4c83939 3100 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3101 info = info->next;
3102 }
ff7adb52
CL
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) {
d4c83939 3111 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3112 if (info->waiting && info->done)
3113 info->waiting = 0;
3114 nwait += info->waiting;
d4c83939 3115 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3116 info = info->next;
3117 }
3118 if (!nwait) break;
3119 sleep(1);
3120 }
3eeba6fb
CB
3121
3122 did_stuff = 0;
3123 info = open_pipes;
3124 while (info) {
d4c83939 3125 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3126 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3127 sts = sys$forcex(&info->pid,0,&abort);
d4c83939 3128 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3eeba6fb
CB
3129 did_stuff = 1;
3130 }
d4c83939 3131 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3132 info = info->next;
3133 }
ff7adb52
CL
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) {
d4c83939 3142 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3143 if (info->waiting && info->done)
3144 info->waiting = 0;
3145 nwait += info->waiting;
d4c83939 3146 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3147 info = info->next;
3148 }
3149 if (!nwait) break;
3150 sleep(1);
3151 }
3eeba6fb
CB
3152
3153 info = open_pipes;
3154 while (info) {
d4c83939 3155 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3156 if (!info->done) { /* We tried to be nice . . . */
3157 sts = sys$delprc(&info->pid,0);
d4c83939 3158 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2f1dcba4 3159 info->done = 1; /* sys$delprc is as done as we're going to get. */
3eeba6fb 3160 }
d4c83939 3161 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3162 info = info->next;
3163 }
3164
3165 while(open_pipes) {
ebd4d70b
JM
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;
36b6faa8
CB
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;
ebd4d70b 3177#endif
1e422769 3178 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3179 else if (!(sts & 1)) retsts = sts;
748a9306
LW
3180 }
3181 return retsts;
3182}
3183
3184static struct exit_control_block pipe_exitblock =
3185 {(struct exit_control_block *) 0,
3186 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3187
22d4bb9c
CB
3188static void pipe_mbxtofd_ast(pPipe p);
3189static void pipe_tochild1_ast(pPipe p);
3190static void pipe_tochild2_ast(pPipe p);
748a9306 3191
a0d0e21e 3192static void
22d4bb9c 3193popen_completion_ast(pInfo info)
a0d0e21e 3194{
22d4bb9c
CB
3195 pInfo i = open_pipes;
3196 int iss;
f7ddb74a 3197 int sts;
d85f548a
JH
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++;
22d4bb9c
CB
3207
3208 while (i) {
3209 if (i == info) break;
3210 i = i->next;
3211 }
3212 if (!i) return; /* unlinked, probably freed too */
3213
22d4bb9c
CB
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;
fd8cd3a3 3235 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
22d4bb9c 3236 } else {
fd8cd3a3 3237 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
22d4bb9c
CB
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;
fd8cd3a3 3245 _ckvmssts_noperl(iss);
22d4bb9c
CB
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;
fd8cd3a3 3252 _ckvmssts_noperl(iss);
a0d0e21e 3253 }
fd8cd3a3 3254 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3255
a0d0e21e
LW
3256}
3257
2fbb330f 3258static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
218fdd94 3259static void vms_execfree(struct dsc$descriptor_s *vmscmd);
aa779de1 3260
22d4bb9c
CB
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
3267static unsigned short
fd8cd3a3 3268popen_translate(pTHX_ char *logical, char *result)
22d4bb9c
CB
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) {
18a3d61e 3307 memmove(&ifi,result+2,2);
22d4bb9c
CB
3308 strcpy(result,result+4);
3309 }
3310 return ifi; /* this is the RMS internal file id */
3311}
3312
22d4bb9c
CB
3313static 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
3326static pPipe
fd8cd3a3 3327pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3328{
22d4bb9c
CB
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
d4c83939 3339 n = sizeof(Pipe);
ebd4d70b 3340 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3341
8a646e0b
JM
3342 create_mbx(&p->chan_in , &d_mbx1);
3343 create_mbx(&p->chan_out, &d_mbx2);
ebd4d70b 3344 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
22d4bb9c
CB
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;
fd8cd3a3
DS
3358#ifdef PERL_IMPLICIT_CONTEXT
3359 p->thx = aTHX;
3360#endif
22d4bb9c
CB
3361
3362 n = sizeof(CBuf) + p->bufsize;
3363
3364 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
ebd4d70b 3365 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c 3366 b->buf = (char *) b + sizeof(CBuf);
ebd4d70b 3367 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
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
3379static void
3380pipe_tochild1_ast(pPipe p)
3381{
22d4bb9c
CB
3382 pCBuf b = p->curr;
3383 int iss = p->iosb.status;
3384 int eof = (iss == SS$_ENDOFFILE);
f7ddb74a 3385 int sts;
fd8cd3a3
DS
3386#ifdef PERL_IMPLICIT_CONTEXT
3387 pTHX = p->thx;
3388#endif
22d4bb9c
CB
3389
3390 if (p->retry) {
3391 if (eof) {
3392 p->shut_on_empty = TRUE;
3393 b->eof = TRUE;
ebd4d70b 3394 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c 3395 } else {
ebd4d70b 3396 _ckvmssts_noperl(iss);
22d4bb9c
CB
3397 }
3398
3399 b->eof = eof;
3400 b->size = p->iosb.count;
ebd4d70b 3401 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
22d4bb9c
CB
3402 if (p->need_wake) {
3403 p->need_wake = FALSE;
ebd4d70b 3404 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
22d4bb9c
CB
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;
ebd4d70b
JM
3415 _ckvmssts_noperl(iss);
3416 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c
CB
3417 }
3418 }
3419
3420 iss = lib$remqti(&p->free, &b);
3421 if (iss == LIB$_QUEWASEMP) {
3422 int n = sizeof(CBuf) + p->bufsize;
ebd4d70b 3423 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c
CB
3424 b->buf = (char *) b + sizeof(CBuf);
3425 } else {
ebd4d70b 3426 _ckvmssts_noperl(iss);
22d4bb9c
CB
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;
ebd4d70b 3435 _ckvmssts_noperl(iss);
22d4bb9c
CB
3436}
3437
3438
3439/* writes queued buffers to output, waits for each to complete before
3440 doing the next */
3441
3442static void
3443pipe_tochild2_ast(pPipe p)
3444{
22d4bb9c
CB
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;
fd8cd3a3
DS
3450#if defined(PERL_IMPLICIT_CONTEXT)
3451 pTHX = p->thx;
3452#endif
22d4bb9c
CB
3453
3454 do {
3455 if (p->type) { /* type=1 has old buffer, dispose */
3456 if (p->shut_on_empty) {
ebd4d70b 3457 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c 3458 } else {
ebd4d70b 3459 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
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) {
ebd4d70b 3468 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c 3469 *p->pipe_done = TRUE;
ebd4d70b 3470 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3471 } else {
ebd4d70b 3472 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
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 }
ebd4d70b 3480 _ckvmssts_noperl(iss);
22d4bb9c
CB
3481 p->type = 1;
3482 } while (done);
3483
3484
3485 p->curr2 = b;
3486 if (b->eof) {
ebd4d70b 3487 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3488 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3489 } else {
ebd4d70b 3490 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
22d4bb9c
CB
3491 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3492 }
3493
3494 return;
3495
3496}
3497
3498
3499static pPipe
fd8cd3a3 3500pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3501{
22d4bb9c
CB
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
d4c83939 3510 int n = sizeof(Pipe);
ebd4d70b 3511 _ckvmssts_noperl(lib$get_vm(&n, &p));
8a646e0b
JM
3512 create_mbx(&p->chan_in , &d_mbx1);
3513 create_mbx(&p->chan_out, &d_mbx2);
22d4bb9c 3514
ebd4d70b 3515 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3516 n = p->bufsize * sizeof(char);
ebd4d70b 3517 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3518 p->shut_on_empty = FALSE;
3519 p->info = 0;
3520 p->type = 0;
3521 p->iosb.status = SS$_NORMAL;
fd8cd3a3
DS
3522#if defined(PERL_IMPLICIT_CONTEXT)
3523 p->thx = aTHX;
3524#endif
22d4bb9c
CB
3525 pipe_infromchild_ast(p);
3526
3527 strcpy(wmbx, mbx1);
3528 strcpy(rmbx, mbx2);
3529 return p;
3530}
3531
3532static void
3533pipe_infromchild_ast(pPipe p)
3534{
22d4bb9c
CB
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));
fd8cd3a3
DS
3539#if defined(PERL_IMPLICIT_CONTEXT)
3540 pTHX = p->thx;
3541#endif
22d4bb9c
CB
3542
3543 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
ebd4d70b 3544 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c
CB
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 */
ebd4d70b 3558 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c
CB
3559 p->chan_in = 0;
3560 }
3561
3562 if (p->chan_out) {
3563 if (myeof || kideof) { /* pass EOF to parent */
ebd4d70b
JM
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));
22d4bb9c
CB
3567 return;
3568 } else if (eof) { /* eat EOF --- fall through to read*/
3569
3570 } else { /* transmit data */
ebd4d70b
JM
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));
22d4bb9c
CB
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;
ebd4d70b 3583 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
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;
ebd4d70b 3601 _ckvmssts_noperl(iss);
22d4bb9c
CB
3602 } else { /* send EOFs for extra reads */
3603 p->iosb.status = SS$_ENDOFFILE;
3604 p->iosb.dvispec = 0;
ebd4d70b
JM
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));
22d4bb9c
CB
3608 }
3609 }
3610}
3611
3612static pPipe
fd8cd3a3 3613pipe_mbxtofd_setup(pTHX_ int fd, char *out)
22d4bb9c 3614{
22d4bb9c
CB
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};
a480973c 3621 int n = sizeof(Pipe);
22d4bb9c
CB
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;
cfcfe586
JM
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);
ebd4d70b 3635 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
cfcfe586
JM
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);
ebd4d70b 3654 _ckvmssts_noperl(status);
cfcfe586
JM
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 }
22d4bb9c
CB
3663 }
3664
ebd4d70b 3665 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3666 p->fd_out = dup(fd);
8a646e0b 3667 create_mbx(&p->chan_in, &d_mbx);
ebd4d70b 3668 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3669 n = (p->bufsize+1) * sizeof(char);
ebd4d70b 3670 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3671 p->shut_on_empty = FALSE;
3672 p->retry = 0;
3673 p->info = 0;
3674 strcpy(out, mbx);
3675
ebd4d70b
JM
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));
22d4bb9c
CB
3679
3680 return p;
3681}
3682
3683static void
3684pipe_mbxtofd_ast(pPipe p)
3685{
22d4bb9c
CB
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;
fd8cd3a3
DS
3692#if defined(PERL_IMPLICIT_CONTEXT)
3693 pTHX = p->thx;
3694#endif
22d4bb9c
CB
3695
3696 if (done && myeof) { /* end piping */
3697 close(p->fd_out);
3698 sys$dassgn(p->chan_in);
3699 *p->pipe_done = TRUE;
ebd4d70b 3700 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
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) {
ebd4d70b 3710 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
22d4bb9c
CB
3711 return;
3712 }
3713 }
3714 p->retry = 0;
3715 } else if (err) {
ebd4d70b 3716 _ckvmssts_noperl(iss);
22d4bb9c
CB
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;
ebd4d70b 3724 _ckvmssts_noperl(iss);
22d4bb9c
CB
3725}
3726
3727
3728typedef struct _pipeloc PLOC;
3729typedef struct _pipeloc* pPLOC;
3730
3731struct _pipeloc {
3732 pPLOC next;
3733 char dir[NAM$C_MAXRSS+1];
3734};
3735static pPLOC head_PLOC = 0;
3736
5c0ae288 3737void
fd8cd3a3 3738free_pipelocs(pTHX_ void *head)
5c0ae288
CL
3739{
3740 pPLOC p, pnext;
ff7adb52 3741 pPLOC *pHead = (pPLOC *)head;
5c0ae288 3742
ff7adb52 3743 p = *pHead;
5c0ae288
CL
3744 while (p) {
3745 pnext = p->next;
e0ef6b43 3746 PerlMem_free(p);
5c0ae288
CL
3747 p = pnext;
3748 }
ff7adb52 3749 *pHead = 0;
5c0ae288 3750}
22d4bb9c
CB
3751
3752static void
fd8cd3a3 3753store_pipelocs(pTHX)
22d4bb9c
CB
3754{
3755 int i;
3756 pPLOC p;
ff7adb52 3757 AV *av = 0;
22d4bb9c
CB
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
ff7adb52 3765 if (head_PLOC)
218fdd94 3766 free_pipelocs(aTHX_ &head_PLOC);
ff7adb52 3767
22d4bb9c
CB
3768/* the . directory from @INC comes last */
3769
e0ef6b43 3770 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3771 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3772 p->next = head_PLOC;
3773 head_PLOC = p;
3774 strcpy(p->dir,"./");
3775
3776/* get the directory from $^X */
3777
c5375c28 3778 unixdir = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 3779 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 3780
218fdd94
CL
3781#ifdef PERL_IMPLICIT_CONTEXT
3782 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3783#else
22d4bb9c 3784 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
218fdd94 3785#endif
22d4bb9c
CB
3786 strcpy(temp, PL_origargv[0]);
3787 x = strrchr(temp,']');
2497a41f
JM
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 }
22d4bb9c 3802
4e205ed6 3803 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
e0ef6b43 3804 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3805 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
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';
c5375c28 3810 }
22d4bb9c
CB
3811 }
3812
3813/* reverse order of @INC entries, skip "." since entered above */
3814
218fdd94
CL
3815#ifdef PERL_IMPLICIT_CONTEXT
3816 if (aTHX)
3817#endif
ff7adb52
CL
3818 if (PL_incgv) av = GvAVn(PL_incgv);
3819
3820 for (i = 0; av && i <= AvFILL(av); i++) {
22d4bb9c
CB
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;
4e205ed6 3826 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
22d4bb9c
CB
3827 continue;
3828
e0ef6b43 3829 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
22d4bb9c
CB
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
4e205ed6 3839 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
e0ef6b43 3840 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3841 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
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
c5375c28 3848 PerlMem_free(unixdir);
22d4bb9c
CB
3849}
3850
a1887106
JM
3851static I32
3852Perl_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
22d4bb9c
CB
3859
3860static char *
fd8cd3a3 3861find_vmspipe(pTHX)
22d4bb9c
CB
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) {
a1887106
JM
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)) {
22d4bb9c
CB
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) {
2f4077ca 3884 char * exp_res;
4d743a9b 3885 int dirlen;
22d4bb9c 3886 strcpy(file, p->dir);
4d743a9b
JM
3887 dirlen = strlen(file);
3888 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
22d4bb9c
CB
3889 file[NAM$C_MAXRSS] = '\0';
3890 p = p->next;
3891
6fb6c614 3892 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
2f4077ca 3893 if (!exp_res) continue;
22d4bb9c 3894
a1887106
JM
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)) {
22d4bb9c
CB
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
3909static FILE *
fd8cd3a3 3910vmspipe_tempfile(pTHX)
22d4bb9c
CB
3911{
3912 char file[NAM$C_MAXRSS+1];
3913 FILE *fp;
3914 static int index = 0;
2497a41f
JM
3915 Stat_t s0, s1;
3916 int cmp_result;
22d4bb9c
CB
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++;
2497a41f
JM
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) {
22d4bb9c
CB
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");
2497a41f
JM
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 }
22d4bb9c
CB
3954 }
3955 if (!fp) return 0; /* we're hosed */
3956
f9ecfa39 3957 fprintf(fp,"$! 'f$verify(0)'\n");
22d4bb9c
CB
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");
2d5e9e5d
JH
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");
0e06870b 3968 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
48b5a746
CL
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");
22d4bb9c 3975 fprintf(fp,"$ perl_on\n");
f9ecfa39 3976 fprintf(fp,"$ 'c'\n");
22d4bb9c 3977 fprintf(fp,"$ perl_status = $STATUS\n");
0e06870b 3978 fprintf(fp,"$ perl_del 'perl_cfile'\n");
22d4bb9c
CB
3979 fprintf(fp,"$ perl_exit 'perl_status'\n");
3980 fsync(fileno(fp));
3981
3982 fgetname(fp, file, 1);
312ac60b 3983 fstat(fileno(fp), &s0.crtl_stat);
22d4bb9c
CB
3984 fclose(fp);
3985
2497a41f 3986 if (decc_filename_unix_only)
0e5ce2c7 3987 int_tounixspec(file, file, NULL);
22d4bb9c
CB
3988 fp = fopen(file,"r","shr=get");
3989 if (!fp) return 0;
312ac60b 3990 fstat(fileno(fp), &s1.crtl_stat);
2497a41f 3991
682e4b71 3992 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
2497a41f 3993 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
22d4bb9c
CB
3994 fclose(fp);
3995 return 0;
3996 }
3997
3998 return fp;
3999}
4000
4001
cd1191f1
CB
4002static 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 */
4068static 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
8cb5d3d5
JM
4092 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4093 /*---------------------------------------*/
d30c1055 4094 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
8cb5d3d5
JM
4095
4096
4097 /* Make sure that this is from the Perl debugger */
cd1191f1
CB
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
8cb5d3d5
JM
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
d30c1055 4114 status = lib$find_image_symbol
8cb5d3d5
JM
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
d30c1055 4124 status = lib$find_image_symbol
8cb5d3d5
JM
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
cd1191f1
CB
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 */
8cb5d3d5 4185 status = (*decw_term_port)
cd1191f1
CB
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;
4e205ed6 4216 info->fp = NULL;
cd1191f1
CB
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
8a646e0b 4238 create_mbx(&p_chan, &d_mbx1);
cd1191f1
CB
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);
ebd4d70b 4257 _ckvmssts_noperl(lib$free_vm(&n, &info));
cd1191f1
CB
4258 return NULL;
4259 }
4260
4261 /* All done */
4262 return info->fp;
4263}
22d4bb9c 4264
ebd4d70b
JM
4265static I32 my_pclose_pinfo(pTHX_ pInfo info);
4266
8fde5078 4267static PerlIO *
2fbb330f 4268safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
a0d0e21e 4269{
748a9306 4270 static int handler_set_up = FALSE;
ebd4d70b 4271 PerlIO * ret_fp;
55f2b99c 4272 unsigned long int sts, flags = CLI$M_NOWAIT;
f9ecfa39
PP
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;
d4c83939 4278 int j, wait = 0, n;
ff7adb52 4279 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
cfcfe586 4280 char *in, *out, *err, mbx[512];
22d4bb9c
CB
4281 FILE *tpipe = 0;
4282 char tfilebuf[NAM$C_MAXRSS+1];
d4c83939 4283 pInfo info = NULL;
48b5a746 4284 char cmd_sym_name[20];
22d4bb9c
CB
4285 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4286 DSC$K_CLASS_S, symbol};
22d4bb9c 4287 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
a0d0e21e 4288 DSC$K_CLASS_S, 0};
48b5a746
CL
4289 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4290 DSC$K_CLASS_S, cmd_sym_name};
218fdd94 4291 struct dsc$descriptor_s *vmscmd;
22d4bb9c 4292 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
0e06870b 4293 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
22d4bb9c 4294 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
cd1191f1 4295
cd1191f1
CB
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
4d9538c1
JM
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
cd1191f1 4309 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4e205ed6 4310 if (xterm_fd != NULL)
cd1191f1
CB
4311 return xterm_fd;
4312 }
cd1191f1 4313
afd8f436
JH
4314 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4315
22d4bb9c
CB
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) {
ebd4d70b 4327 _ckvmssts_noperl(sys$setast(0));
22d4bb9c
CB
4328 if (!pipe_ef) {
4329 unsigned long int pidcode = JPI$_PID;
4330 $DESCRIPTOR(d_delay, RETRY_DELAY);
ebd4d70b
JM
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));
22d4bb9c
CB
4334 }
4335 if (!handler_set_up) {
ebd4d70b 4336 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
22d4bb9c
CB
4337 handler_set_up = TRUE;
4338 }
ebd4d70b 4339 _ckvmssts_noperl(sys$setast(1));
22d4bb9c
CB
4340 }
4341
4342 /* see if we can find a VMSPIPE.COM */
4343
4344 tfilebuf[0] = '@';
fd8cd3a3 4345 vmspipe = find_vmspipe(aTHX);
22d4bb9c
CB
4346 if (vmspipe) {
4347 strcpy(tfilebuf+1,vmspipe);
4348 } else { /* uh, oh...we're in tempfile hell */
fd8cd3a3 4349 tpipe = vmspipe_tempfile(aTHX);
22d4bb9c
CB
4350 if (!tpipe) { /* a fish popular in Boston */
4351 if (ckWARN(WARN_PIPE)) {
f98bc0c6 4352 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
22d4bb9c 4353 }
4e205ed6 4354 return NULL;
22d4bb9c
CB
4355 }
4356 fgetname(tpipe,tfilebuf+1,1);
4357 }
4358 vmspipedsc.dsc$a_pointer = tfilebuf;
4359 vmspipedsc.dsc$w_length = strlen(tfilebuf);
a0d0e21e 4360
218fdd94 4361 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
a2669cfc
JH
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 */
ebd4d70b 4377 _ckvmssts_noperl(sts); /* fall through */
a2669cfc
JH
4378 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4379 set_errno(EVMSERR);
4380 }
4381 set_vaxc_errno(sts);
cd1191f1 4382 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
f98bc0c6 4383 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
a2669cfc 4384 }
ff7adb52 4385 *psts = sts;
4e205ed6 4386 return NULL;
a2669cfc 4387 }
d4c83939 4388 n = sizeof(Info);
ebd4d70b 4389 _ckvmssts_noperl(lib$get_vm(&n, &info));
22d4bb9c 4390
ff7adb52 4391 strcpy(mode,in_mode);
22d4bb9c
CB
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;
4e205ed6 4399 info->fp = NULL;
ff7adb52
CL
4400 info->useFILE = 0;
4401 info->waiting = 0;
22d4bb9c
CB
4402 info->in_done = TRUE;
4403 info->out_done = TRUE;
4404 info->err_done = TRUE;
cd1191f1
CB
4405 info->xchan = 0;
4406 info->xchan_valid = 0;
cfcfe586
JM
4407
4408 in = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4409 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4410 out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4411 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4412 err = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4413 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4414
0e06870b 4415 in[0] = out[0] = err[0] = '\0';
22d4bb9c 4416
ff7adb52
CL
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
22d4bb9c 4426 if (*mode == 'r') { /* piping from subroutine */
22d4bb9c 4427
fd8cd3a3 4428 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
22d4bb9c
CB
4429 if (info->out) {
4430 info->out->pipe_done = &info->out_done;
4431 info->out_done = FALSE;
4432 info->out->info = info;
4433 }
ff7adb52 4434 if (!info->useFILE) {
cd1191f1 4435 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4436 } else {
4437 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4438 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4439 }
4440
22d4bb9c
CB
4441 if (!info->fp && info->out) {
4442 sys$cancel(info->out->chan_out);
4443
4444 while (!info->out_done) {
4445 int done;
ebd4d70b 4446 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4447 done = info->out_done;
ebd4d70b
JM
4448 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4449 _ckvmssts_noperl(sys$setast(1));
4450 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
0e06870b 4451 }
22d4bb9c 4452
d4c83939
CB
4453 if (info->out->buf) {
4454 n = info->out->bufsize * sizeof(char);
ebd4d70b 4455 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
d4c83939
CB
4456 }
4457 n = sizeof(Pipe);
ebd4d70b 4458 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
d4c83939 4459 n = sizeof(Info);
ebd4d70b 4460 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4461 *psts = RMS$_FNF;
4e205ed6 4462 return NULL;
0e06870b 4463 }
22d4bb9c 4464
fd8cd3a3 4465 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
22d4bb9c
CB
4466 if (info->err) {
4467 info->err->pipe_done = &info->err_done;
4468 info->err_done = FALSE;
4469 info->err->info = info;
4470 }
a0d0e21e 4471
ff7adb52
CL
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 }
a0d0e21e 4487
fd8cd3a3 4488 info->in = pipe_tochild_setup(aTHX_ in,mbx);
ff7adb52 4489 if (!info->useFILE) {
a480973c 4490 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4491 } else {
4492 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4493 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4494 }
4495
22d4bb9c
CB
4496 if (info->in) {
4497 info->in->pipe_done = &info->in_done;
4498 info->in_done = FALSE;
4499 info->in->info = info;
4500 }
a0d0e21e 4501
22d4bb9c
CB
4502 /* error cleanup */
4503 if (!info->fp && info->in) {
4504 info->done = TRUE;
ebd4d70b
JM
4505 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4506 0, 0, 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
4507
4508 while (!info->in_done) {
4509 int done;
ebd4d70b 4510 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4511 done = info->in_done;
ebd4d70b
JM
4512 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4513 _ckvmssts_noperl(sys$setast(1));
4514 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
22d4bb9c 4515 }
a0d0e21e 4516
d4c83939
CB
4517 if (info->in->buf) {
4518 n = info->in->bufsize * sizeof(char);
ebd4d70b 4519 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
d4c83939
CB
4520 }
4521 n = sizeof(Pipe);
ebd4d70b 4522 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
d4c83939 4523 n = sizeof(Info);
ebd4d70b 4524 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4525 *psts = RMS$_FNF;
4e205ed6 4526 return NULL;
22d4bb9c 4527 }
a0d0e21e 4528
22d4bb9c 4529
ff7adb52 4530 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
fd8cd3a3 4531 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
22d4bb9c
CB
4532 if (info->out) {
4533 info->out->pipe_done = &info->out_done;
4534 info->out_done = FALSE;
4535 info->out->info = info;
4536 }
0e06870b 4537
fd8cd3a3 4538 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
0e06870b
CB
4539 if (info->err) {
4540 info->err->pipe_done = &info->err_done;
4541 info->err_done = FALSE;
4542 info->err->info = info;
4543 }
748a9306 4544 }
22d4bb9c
CB
4545
4546 symbol[MAX_DCL_SYMBOL] = '\0';
4547
4548 strncpy(symbol, in, MAX_DCL_SYMBOL);
4549 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4550 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
22d4bb9c
CB
4551
4552 strncpy(symbol, err, MAX_DCL_SYMBOL);
4553 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4554 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
22d4bb9c 4555
0e06870b
CB
4556 strncpy(symbol, out, MAX_DCL_SYMBOL);
4557 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4558 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
22d4bb9c 4559
cfcfe586
JM
4560 /* Done with the names for the pipes */
4561 PerlMem_free(err);
4562 PerlMem_free(out);
4563 PerlMem_free(in);
4564
218fdd94 4565 p = vmscmd->dsc$a_pointer;
22d4bb9c
CB
4566 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4567 if (*p == '$') p++; /* remove leading $ */
4568 while (*p == ' ' || *p == '\t') p++;
48b5a746
CL
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
22d4bb9c
CB
4574 strncpy(symbol, p, MAX_DCL_SYMBOL);
4575 d_symbol.dsc$w_length = strlen(symbol);
ebd4d70b 4576 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
22d4bb9c 4577
48b5a746
CL
4578 if (strlen(p) > MAX_DCL_SYMBOL) {
4579 p += MAX_DCL_SYMBOL;
4580 } else {
4581 p += strlen(p);
4582 }
4583 }
ebd4d70b 4584 _ckvmssts_noperl(sys$setast(0));
a0d0e21e
LW
4585 info->next=open_pipes; /* prepend to list */
4586 open_pipes=info;
ebd4d70b 4587 _ckvmssts_noperl(sys$setast(1));
55f2b99c
CB
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 */
ebd4d70b 4592 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
22d4bb9c
CB
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
ff7adb52 4600 /* once the subprocess is spawned, it has copied the symbols and
22d4bb9c
CB
4601 we can get rid of ours */
4602
48b5a746
CL
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);
ebd4d70b 4606 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
48b5a746 4607 }
ebd4d70b
JM
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));
218fdd94 4611 vms_execfree(vmscmd);
a0d0e21e 4612
218fdd94
CL
4613#ifdef PERL_IMPLICIT_CONTEXT
4614 if (aTHX)
4615#endif
6b88bc9c 4616 PL_forkprocess = info->pid;
218fdd94 4617
ebd4d70b 4618 ret_fp = info->fp;
ff7adb52 4619 if (wait) {
ebd4d70b 4620 dSAVEDERRNO;
ff7adb52
CL
4621 int done = 0;
4622 while (!done) {
ebd4d70b 4623 _ckvmssts_noperl(sys$setast(0));
ff7adb52 4624 done = info->done;
ebd4d70b
JM
4625 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4626 _ckvmssts_noperl(sys$setast(1));
4627 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
ff7adb52
CL
4628 }
4629 *psts = info->completion;
2fbb330f
JM
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); */
ebd4d70b
JM
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
ff7adb52 4642 } else {
eed5d6a1 4643 *psts = info->pid;
ff7adb52 4644 }
ebd4d70b 4645 return ret_fp;
1e422769 4646} /* end of safe_popen */
4647
4648
a15cef0c
CB
4649/*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4650PerlIO *
2fbb330f 4651Perl_my_popen(pTHX_ const char *cmd, const char *mode)
1e422769 4652{
ff7adb52 4653 int sts;
1e422769 4654 TAINT_ENV();
4655 TAINT_PROPER("popen");
45bc9206 4656 PERL_FLUSHALL_FOR_CHILD;
ff7adb52 4657 return safe_popen(aTHX_ cmd,mode,&sts);
a0d0e21e 4658}
1e422769 4659
a0d0e21e
LW
4660/*}}}*/
4661
ebd4d70b
JM
4662
4663/* Routine to close and cleanup a pipe info structure */
4664
4665static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4666
748a9306 4667 unsigned long int retsts;
d4c83939 4668 int done, iss, n;
cd1191f1 4669 int status;
ebd4d70b 4670 pInfo next, last;
748a9306 4671
bbce6d69 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
22d4bb9c
CB
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 */
ff7adb52 4679 if (info->fp) {
5ce486e0
CB
4680 if (!info->useFILE
4681#if defined(USE_ITHREADS)
4682 && my_perl
4683#endif
4684 && PL_perlio_fd_refcnt)
4685 PerlIO_flush(info->fp);
ff7adb52
CL
4686 else
4687 fflush((FILE *)info->fp);
4688 }
22d4bb9c 4689
b08af3f0 4690 _ckvmssts(sys$setast(0));
22d4bb9c
CB
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));
b08af3f0 4705 _ckvmssts(sys$setast(1));
ff7adb52 4706 if (info->fp) {
5ce486e0
CB
4707 if (!info->useFILE
4708#if defined(USE_ITHREADS)
4709 && my_perl
4710#endif
4711 && PL_perlio_fd_refcnt)
d4c83939 4712 PerlIO_close(info->fp);
ff7adb52
CL
4713 else
4714 fclose((FILE *)info->fp);
4715 }
22d4bb9c
CB
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;
a0d0e21e 4730
a0d0e21e 4731 /* remove from list of open pipes */
b08af3f0 4732 _ckvmssts(sys$setast(0));
ebd4d70b
JM
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;
b08af3f0 4743 _ckvmssts(sys$setast(1));
22d4bb9c
CB
4744
4745 /* free buffers and structures */
4746
4747 if (info->in) {
d4c83939
CB
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));
22d4bb9c
CB
4754 }
4755 if (info->out) {
d4c83939
CB
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));
22d4bb9c
CB
4762 }
4763 if (info->err) {
d4c83939
CB
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));
22d4bb9c 4770 }
d4c83939
CB
4771 n = sizeof(Info);
4772 _ckvmssts(lib$free_vm(&n, &info));
a0d0e21e
LW
4773
4774 return retsts;
ebd4d70b
JM
4775}
4776
4777
4778/*{{{ I32 my_pclose(PerlIO *fp)*/
4779I32 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;
748a9306 4797
a0d0e21e
LW
4798} /* end of my_pclose() */
4799
119586db 4800#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
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 */
4fdae800 4811/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4812Pid_t
fd8cd3a3 4813Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
a0d0e21e 4814{
22d4bb9c
CB
4815 pInfo info;
4816 int done;
aeb5cf3c 4817 int sts;
d85f548a 4818 int j;
aeb5cf3c
CB
4819
4820 if (statusp) *statusp = 0;
a0d0e21e
LW
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 */
748a9306 4826 while (!info->done) {
22d4bb9c
CB
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));
a0d0e21e
LW
4832 }
4833
aeb5cf3c 4834 if (statusp) *statusp = info->completion;
a0d0e21e 4835 return pid;
d85f548a
JH
4836 }
4837
4838 /* child that already terminated? */
aeb5cf3c 4839
d85f548a
JH
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 }
a0d0e21e 4845 }
d85f548a
JH
4846
4847 /* fall through if this child is not one of our own pipe children */
aeb5cf3c 4848
119586db 4849#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
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
119586db 4868#endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
aeb5cf3c 4869
21bc9d50 4870 {
a0d0e21e 4871 $DESCRIPTOR(intdsc,"0 00:00:01");
aeb5cf3c
CB
4872 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4873 unsigned long int pidcode = JPI$_PID, mypid;
4874 unsigned long int interval[2];
aeb5cf3c 4875 unsigned int jpi_iosb[2];
d85f548a 4876 struct itmlst_3 jpilist[2] = {
aeb5cf3c 4877 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
aeb5cf3c
CB
4878 { 0, 0, 0, 0}
4879 };
aeb5cf3c
CB
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
d85f548a
JH
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.
aeb5cf3c
CB
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 }
a0d0e21e 4910
3eeba6fb 4911 if (ckWARN(WARN_EXEC)) {
aeb5cf3c
CB
4912 /* remind folks they are asking for non-standard waitpid behavior */
4913 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
748a9306 4914 if (ownerpid != mypid)
f98bc0c6 4915 Perl_warner(aTHX_ packWARN(WARN_EXEC),
aeb5cf3c
CB
4916 "waitpid: process %x is not a child of process %x",
4917 pid,mypid);
748a9306 4918 }
a0d0e21e 4919
d85f548a
JH
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) {
aeb5cf3c
CB
4924 _ckvmssts(sys$schdwk(0,0,interval,0));
4925 _ckvmssts(sys$hiber());
d85f548a
JH
4926 }
4927 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
aeb5cf3c
CB
4928
4929 _ckvmssts(sts);
a0d0e21e 4930 return pid;
21bc9d50 4931 }
a0d0e21e 4932} /* end of waitpid() */
a0d0e21e
LW
4933/*}}}*/
4934/*}}}*/
4935/*}}}*/
4936
4937/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4938char *
4939my_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;
71be2cbc 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
a0d0e21e
LW
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
988c775c 4965#if defined(__VAX) || !defined(NAML$C_MAXRSS)
a480973c
JM
4966static int rms_free_search_context(struct FAB * fab)
4967{
4968struct 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
4d743a9b 4978#define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
a480973c
JM
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) \
a1887106 4994 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
a480973c
JM
4995#define rms_get_fna(fab, nam) fab.fab$l_fna
4996#define rms_set_dna(fab, nam, name, size) \
a1887106
JM
4997 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4998#define rms_nam_dns(fab, nam) fab.fab$b_dns
d584a1c6 4999#define rms_set_esa(nam, name, size) \
a1887106 5000 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
a480973c 5001#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 5002 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
a480973c 5003#define rms_set_rsa(nam, name, size) \
a1887106 5004 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
a480973c 5005#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106
JM
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)
a480973c
JM
5009#else
5010static int rms_free_search_context(struct FAB * fab)
5011{
5012struct 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;
988c775c 5018
a480973c
JM
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
4d743a9b 5024#define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
a480973c
JM
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) \
a1887106 5040 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
a480973c 5041 nam.naml$l_long_filename_size = size; \
a1887106 5042 nam.naml$l_long_filename = name;}
a480973c
JM
5043#define rms_get_fna(fab, nam) nam.naml$l_long_filename
5044#define rms_set_dna(fab, nam, name, size) \
a1887106 5045 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
a480973c 5046 nam.naml$l_long_defname_size = size; \
a1887106 5047 nam.naml$l_long_defname = name; }
a480973c 5048#define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
d584a1c6 5049#define rms_set_esa(nam, name, size) \
a1887106 5050 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
a480973c 5051 nam.naml$l_long_expand_alloc = size; \
a1887106 5052 nam.naml$l_long_expand = name; }
a480973c 5053#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 5054 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
a480973c 5055 nam.naml$l_long_expand = l_name; \
a1887106 5056 nam.naml$l_long_expand_alloc = l_size; }
a480973c 5057#define rms_set_rsa(nam, name, size) \
a1887106 5058 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
a480973c 5059 nam.naml$l_long_result = name; \
a1887106 5060 nam.naml$l_long_result_alloc = size; }
a480973c 5061#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106 5062 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
a480973c 5063 nam.naml$l_long_result = l_name; \
a1887106
JM
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)
a480973c
JM
5067#endif
5068
4fdf8f88 5069
e0e5e8d6
JM
5070/* rms_erase
5071 * The CRTL for 8.3 and later can create symbolic links in any mode,
4fdf8f88 5072 * however in 8.3 the unlink/remove/delete routines will only properly handle
e0e5e8d6 5073 * them if one of the PCP modes is active.
e0e5e8d6
JM
5074 */
5075static 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);
4fdf8f88 5083
e0e5e8d6
JM
5084#ifdef NAML$M_OPEN_SPECIAL
5085 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5086#endif
5087
d30c1055 5088 status = sys$erase(&myfab, 0, 0);
e0e5e8d6
JM
5089
5090 return status;
5091}
5092
bbce6d69 5093
4fdf8f88
JM
5094static int
5095vms_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
5108const unsigned int access_mode = 0;
5109$DESCRIPTOR(obj_file_dsc,"FILE");
5110char *vmsname;
5111char *rslt;
5112unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5113int aclsts, fndsts, rnsts = -1;
5114unsigned int ctx = 0;
5115struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5116struct dsc$descriptor_s * clean_dsc;
5117
5118struct 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
5129struct 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
6fb6c614 5144 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4fdf8f88 5145 vmsname,
6fb6c614 5146 PERL_RMSEXPAND_M_SYMLINK);
4fdf8f88
JM
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 */
ebd4d70b 5156 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4fdf8f88
JM
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 */
5263int
5264Perl_rename(pTHX_ const char *src, const char * dst)
5265{
5266int retval;
5267int pre_delete = 0;
5268int src_sts;
5269int dst_sts;
5270Stat_t src_st;
5271Stat_t dst_st;
5272
5273 /* Validate the source file */
46c05374 5274 src_sts = flex_lstat(src, &src_st);
4fdf8f88
JM
5275 if (src_sts != 0) {
5276
5277 /* No source file or other problem */
5278 return src_sts;
5279 }
b94a8c49
JM
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 }
4fdf8f88 5285
46c05374 5286 dst_sts = flex_lstat(dst, &dst_st);
4fdf8f88
JM
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;
46c05374 5330 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
b94a8c49
JM
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) {
46c05374 5338 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
b94a8c49
JM
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
4fdf8f88
JM
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
4fdf8f88
JM
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
4fdf8f88
JM
5383 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5384 if (vms_dst == NULL)
ebd4d70b 5385 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88
JM
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)
ebd4d70b 5393 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88 5394
4fdf8f88
JM
5395 /* If the dest is a directory, we must remove it
5396 if (dst_sts == 0) {
5397 int d_sts;
46c05374 5398 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
4fdf8f88 5399 if (d_sts != 0) {
4fdf8f88
JM
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 */
df278665 5409 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5410 if (ret_str == NULL) {
4fdf8f88
JM
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)
ebd4d70b 5419 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88
JM
5420
5421 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5422 if (ret_str == NULL) {
4fdf8f88
JM
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 */
4846f1d7 5436 ret_str = int_tovmspath(dst, vms_dst, NULL);
4fdf8f88 5437 if (ret_str == NULL) {
4fdf8f88
JM
5438 PerlMem_free(vms_dst);
5439 errno = EIO;
5440 return -1;
5441 }
5442 } else {
b94a8c49
JM
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;
4fdf8f88
JM
5446
5447 /* fileify a target VMS file specification */
df278665 5448 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5449 if (ret_str == NULL) {
4fdf8f88
JM
5450 PerlMem_free(vms_dst);
5451 errno = EIO;
5452 return -1;
5453 }
b94a8c49
JM
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 }
4fdf8f88
JM
5469 }
5470 }
5471
b94a8c49
JM
5472 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5473 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
4fdf8f88
JM
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)
449de3c2 5484 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
4fdf8f88
JM
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
4fdf8f88
JM
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 */
b94a8c49
JM
5513 int i = 0;
5514 dSAVEDERRNO;
5515 SAVE_ERRNO;
46c05374 5516 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5517 S_ISDIR(src_st.st_mode));
5518 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
46c05374 5519 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
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;
4fdf8f88
JM
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
bbce6d69 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.
e886094b
JM
5554 *
5555 * New functionality for previously unused opts value:
5556 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
a1887106
JM
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
e0e5e8d6 5559 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
bbce6d69 5560 */
360732b5 5561static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
96e4d5b1 5562
bbce6d69 5563static char *
6fb6c614
JM
5564int_rmsexpand
5565 (const char *filespec,
360732b5 5566 char *outbuf,
360732b5
JM
5567 const char *defspec,
5568 unsigned opts,
5569 int * fs_utf8,
5570 int * dfs_utf8)
bbce6d69 5571{
6fb6c614
JM
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;
7566800d 5578 char * esal = NULL;
18a3d61e
JM
5579 char * outbufl;
5580 struct FAB myfab = cc$rms_fab;
a480973c 5581 rms_setup_nam(mynam);
18a3d61e
JM
5582 STRLEN speclen;
5583 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5584 int sts;
5585
360732b5
JM
5586 /* temp hack until UTF8 is actually implemented */
5587 if (fs_utf8 != NULL)
5588 *fs_utf8 = 0;
5589
18a3d61e
JM
5590 if (!filespec || !*filespec) {
5591 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5592 return NULL;
5593 }
18a3d61e
JM
5594
5595 vmsfspec = NULL;
6fb6c614 5596 vmsdefspec = NULL;
18a3d61e 5597 outbufl = NULL;
a1887106 5598
6fb6c614 5599 in_spec = filespec;
a1887106
JM
5600 isunix = 0;
5601 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
6fb6c614
JM
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;
18a3d61e 5621
6fb6c614
JM
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)
b1a8dcd7 5626#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6fb6c614 5627 opts |= PERL_RMSEXPAND_M_LONG;
778e045f
CB
5628#else
5629 NOOP;
b1a8dcd7 5630#endif
6fb6c614
JM
5631 else
5632 isunix = 0;
a1887106 5633 }
18a3d61e 5634
6fb6c614
JM
5635 }
5636
5637 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
a480973c 5638 rms_bind_fab_nam(myfab, mynam);
18a3d61e 5639
6fb6c614
JM
5640 /* Process the default file specification if present */
5641 def_spec = defspec;
18a3d61e
JM
5642 if (defspec && *defspec) {
5643 int t_isunix;
5644 t_isunix = is_unix_filespec(defspec);
5645 if (t_isunix) {
6fb6c614
JM
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;
18a3d61e 5658 }
6fb6c614
JM
5659 rms_set_dna(myfab, mynam,
5660 (char *)def_spec, strlen(def_spec)); /* cast ok */
18a3d61e
JM
5661 }
5662
6fb6c614 5663 /* Now we need the expansion buffers */
c5375c28 5664 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 5665 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5666#if !defined(__VAX) && defined(NAML$C_MAXRSS)
a1887106 5667 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5668 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5669#endif
a1887106 5670 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
18a3d61e 5671
d584a1c6
JM
5672 /* If a NAML block is used RMS always writes to the long and short
5673 * addresses unless you suppress the short name.
5674 */
a480973c 5675#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 5676 outbufl = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5677 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5678#endif
d584a1c6 5679 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
18a3d61e 5680
f7ddb74a
JM
5681#ifdef NAM$M_NO_SHORT_UPCASE
5682 if (decc_efs_case_preserve)
a480973c 5683 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 5684#endif
18a3d61e 5685
e0e5e8d6
JM
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
18a3d61e
JM
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 */
a480973c 5697 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
6fb6c614
JM
5698 if (retsts == RMS$_DNF ||
5699 retsts == RMS$_DIR ||
5700 retsts == RMS$_DEV ||
5701 retsts == RMS$_PRV) {
18a3d61e 5702 retsts = sys$parse(&myfab,0,0);
6fb6c614 5703 if (retsts & STS$K_SUCCESS) goto int_expanded;
18a3d61e
JM
5704 }
5705
5706 /* Still could not parse the file specification */
5707 /*----------------------------------------------*/
a480973c 5708 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5709 if (vmsdefspec != NULL)
5710 PerlMem_free(vmsdefspec);
18a3d61e 5711 if (vmsfspec != NULL)
c5375c28
JM
5712 PerlMem_free(vmsfspec);
5713 if (outbufl != NULL)
5714 PerlMem_free(outbufl);
5715 PerlMem_free(esa);
7566800d
CB
5716 if (esal != NULL)
5717 PerlMem_free(esal);
18a3d61e
JM
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) {
a480973c 5727 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5728 if (vmsdefspec != NULL)
5729 PerlMem_free(vmsdefspec);
18a3d61e 5730 if (vmsfspec != NULL)
c5375c28
JM
5731 PerlMem_free(vmsfspec);
5732 if (outbufl != NULL)
5733 PerlMem_free(outbufl);
5734 PerlMem_free(esa);
7566800d
CB
5735 if (esal != NULL)
5736 PerlMem_free(esal);
18a3d61e
JM
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. */
6fb6c614 5745int_expanded:
18a3d61e 5746 if (!decc_efs_case_preserve) {
6fb6c614 5747 char * tbuf;
c5375c28
JM
5748 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5749 if (islower(*tbuf)) { haslower = 1; break; }
18a3d61e
JM
5750 }
5751
5752 /* Is a long or a short name expected */
5753 /*------------------------------------*/
6fb6c614 5754 spec_buf = NULL;
778e045f 5755#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5756 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5757 if (rms_nam_rsll(mynam)) {
6fb6c614 5758 spec_buf = outbufl;
a480973c 5759 speclen = rms_nam_rsll(mynam);
18a3d61e
JM
5760 }
5761 else {
6fb6c614 5762 spec_buf = esal; /* Not esa */
a480973c 5763 speclen = rms_nam_esll(mynam);
18a3d61e
JM
5764 }
5765 }
5766 else {
778e045f 5767#endif
a480973c 5768 if (rms_nam_rsl(mynam)) {
6fb6c614 5769 spec_buf = outbuf;
a480973c 5770 speclen = rms_nam_rsl(mynam);
18a3d61e
JM
5771 }
5772 else {
6fb6c614 5773 spec_buf = esa; /* Not esal */
a480973c 5774 speclen = rms_nam_esl(mynam);
18a3d61e 5775 }
778e045f 5776#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5777 }
778e045f 5778#endif
6fb6c614 5779 spec_buf[speclen] = '\0';
4d743a9b 5780
18a3d61e
JM
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 */
a480973c 5785 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
18a3d61e 5786 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5787 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5788 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
18a3d61e
JM
5789 }
5790 else {
a480973c
JM
5791 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5792 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
18a3d61e
JM
5793 }
5794 if (trimver || trimtype) {
5795 if (defspec && *defspec) {
5796 char *defesal = NULL;
d584a1c6
JM
5797 char *defesa = NULL;
5798 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5799 if (defesa != NULL) {
6fb6c614 5800 struct FAB deffab = cc$rms_fab;
d584a1c6
JM
5801#if !defined(__VAX) && defined(NAML$C_MAXRSS)
5802 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 5803 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 5804#endif
a480973c 5805 rms_setup_nam(defnam);
18a3d61e 5806
a480973c
JM
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
d584a1c6
JM
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);
a480973c 5815
4d743a9b 5816 rms_clear_nam_nop(defnam);
a480973c 5817 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
18a3d61e
JM
5818#ifdef NAM$M_NO_SHORT_UPCASE
5819 if (decc_efs_case_preserve)
a480973c 5820 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
18a3d61e 5821#endif
e0e5e8d6
JM
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
18a3d61e
JM
5826 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5827 if (trimver) {
a480973c 5828 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
18a3d61e
JM
5829 }
5830 if (trimtype) {
a480973c 5831 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
18a3d61e
JM
5832 }
5833 }
d584a1c6
JM
5834 if (defesal != NULL)
5835 PerlMem_free(defesal);
5836 PerlMem_free(defesa);
6fb6c614
JM
5837 } else {
5838 _ckvmssts_noperl(SS$_INSFMEM);
18a3d61e
JM
5839 }
5840 }
5841 if (trimver) {
5842 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5843 if (*(rms_nam_verl(mynam)) != '\"')
6fb6c614 5844 speclen = rms_nam_verl(mynam) - spec_buf;
18a3d61e
JM
5845 }
5846 else {
a480973c 5847 if (*(rms_nam_ver(mynam)) != '\"')
6fb6c614 5848 speclen = rms_nam_ver(mynam) - spec_buf;
18a3d61e
JM
5849 }
5850 }
5851 if (trimtype) {
5852 /* If we didn't already trim version, copy down */
5853 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
6fb6c614 5854 if (speclen > rms_nam_verl(mynam) - spec_buf)
18a3d61e 5855 memmove
a480973c
JM
5856 (rms_nam_typel(mynam),
5857 rms_nam_verl(mynam),
6fb6c614 5858 speclen - (rms_nam_verl(mynam) - spec_buf));
a480973c 5859 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
18a3d61e
JM
5860 }
5861 else {
6fb6c614 5862 if (speclen > rms_nam_ver(mynam) - spec_buf)
18a3d61e 5863 memmove
a480973c
JM
5864 (rms_nam_type(mynam),
5865 rms_nam_ver(mynam),
6fb6c614 5866 speclen - (rms_nam_ver(mynam) - spec_buf));
a480973c 5867 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
18a3d61e
JM
5868 }
5869 }
5870 }
5871
5872 /* Done with these copies of the input files */
5873 /*-------------------------------------------*/
5874 if (vmsfspec != NULL)
c5375c28 5875 PerlMem_free(vmsfspec);
6fb6c614
JM
5876 if (vmsdefspec != NULL)
5877 PerlMem_free(vmsdefspec);
18a3d61e
JM
5878
5879 /* If we just had a directory spec on input, $PARSE "helpfully"
5880 * adds an empty name and type for us */
d584a1c6 5881#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5882 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
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)))
6fb6c614 5886 speclen = rms_nam_namel(mynam) - spec_buf;
18a3d61e 5887 }
d584a1c6
JM
5888 else
5889#endif
5890 {
a480973c
JM
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)))
6fb6c614 5894 speclen = rms_nam_name(mynam) - spec_buf;
18a3d61e
JM
5895 }
5896
5897 /* Posix format specifications must have matching quotes */
4d743a9b 5898 if (speclen < (VMS_MAXRSS - 1)) {
6fb6c614
JM
5899 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5900 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5901 spec_buf[speclen] = '\"';
4d743a9b
JM
5902 speclen++;
5903 }
18a3d61e
JM
5904 }
5905 }
6fb6c614
JM
5906 spec_buf[speclen] = '\0';
5907 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
18a3d61e
JM
5908
5909 /* Have we been working with an expanded, but not resultant, spec? */
5910 /* Also, convert back to Unix syntax if necessary. */
d584a1c6
JM
5911 {
5912 int rsl;
18a3d61e 5913
d584a1c6
JM
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) {
6fb6c614
JM
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 */
d584a1c6 5926 if (isunix) {
6fb6c614
JM
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;
18a3d61e
JM
5934 }
5935 }
6fb6c614
JM
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;
d584a1c6 5963 }
18a3d61e 5964 }
18a3d61e 5965 }
6fb6c614
JM
5966
5967 /* Need to clean up the search context */
a480973c
JM
5968 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5969 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5970
5971 /* Clean up the extra buffers */
7566800d 5972 if (esal != NULL)
6fb6c614
JM
5973 PerlMem_free(esal);
5974 PerlMem_free(esa);
c5375c28
JM
5975 if (outbufl != NULL)
5976 PerlMem_free(outbufl);
6fb6c614
JM
5977
5978 /* Return the result */
5979 return ret_spec;
5980}
5981
5982/* Common simple case - Expand an already VMS spec */
5983static char *
5984int_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 */
5990static char *
5991int_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 */
5998static char *
5999mp_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;
bbce6d69 6035}
6036/*}}}*/
6037/* External entry points */
2fbb330f 6038char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5 6039{ return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
2fbb330f 6040char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5
JM
6041{ return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
6042char *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); }
6046char *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); }
bbce6d69 6050
6051
a0d0e21e
LW
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
748a9306 6057** return a pointer to a buffer containing the appropriately
a0d0e21e
LW
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.
360732b5 6078** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
e518068a 6079**
bd3fa61c 6080** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
01b8edb6 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.
a0d0e21e
LW
6085 */
6086
a979ce91
JM
6087/*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6088static char *
6089int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
a0d0e21e 6090{
b7ae7a0d 6091 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
a979ce91 6092 char *cp1, *cp2, *lastdir;
a480973c 6093 char *trndir, *vmsdir;
2d9f3838 6094 unsigned short int trnlnm_iter_count;
df278665
JM
6095 int is_vms = 0;
6096 int is_unix = 0;
f7ddb74a 6097 int sts;
360732b5
JM
6098 if (utf8_fl != NULL)
6099 *utf8_fl = 0;
a0d0e21e 6100
c07a80fd 6101 if (!dir || !*dir) {
6102 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6103 }
a0d0e21e 6104 dirlen = strlen(dir);
a2a90019 6105 while (dirlen && dir[dirlen-1] == '/') --dirlen;
61bb5906 6106 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
f7ddb74a
JM
6107 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6108 dir = "/sys$disk";
6109 dirlen = 9;
6110 }
6111 else
6112 dirlen = 1;
61bb5906 6113 }
a480973c
JM
6114 if (dirlen > (VMS_MAXRSS - 1)) {
6115 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6116 return NULL;
c07a80fd 6117 }
c5375c28 6118 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 6119 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
6120 if (!strpbrk(dir+1,"/]>:") &&
6121 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
e518068a 6122 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2d9f3838 6123 trnlnm_iter_count = 0;
b8486b9d 6124 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
2d9f3838
CB
6125 trnlnm_iter_count++;
6126 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6127 }
b8ffc8df 6128 dirlen = strlen(trndir);
e518068a 6129 }
01b8edb6 6130 else {
6131 strncpy(trndir,dir,dirlen);
6132 trndir[dirlen] = '\0';
01b8edb6 6133 }
b8ffc8df
RGS
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
c07a80fd 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 */
b8ffc8df
RGS
6145 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6146 trndir[--dirlen] = '\0';
6147 trndir[dirlen-1] = ']';
c07a80fd 6148 }
b8ffc8df
RGS
6149 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6150 trndir[--dirlen] = '\0';
6151 trndir[dirlen-1] = '>';
46112e17 6152 }
e518068a 6153
b8ffc8df 6154 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
b7ae7a0d 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 {
b8ffc8df 6160 for (cp2 = cp1; cp2 > trndir; cp2--) {
f7ddb74a
JM
6161 if (*cp2 == '.') {
6162 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
657054d4 6163/* fix-me, can not scan EFS file specs backward like this */
f7ddb74a
JM
6164 *cp2 = *cp1; *cp1 = '\0';
6165 hasfilename = 1;
6166 break;
6167 }
b7ae7a0d 6168 }
6169 if (*cp2 == '[' || *cp2 == '<') break;
6170 }
6171 }
6172 }
6173
c5375c28 6174 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 6175 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 6176 cp1 = strpbrk(trndir,"]:>");
a979ce91
JM
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 */
b8ffc8df 6203 if (trndir[0] == '.') {
a480973c 6204 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
c5375c28
JM
6205 PerlMem_free(trndir);
6206 PerlMem_free(vmsdir);
a979ce91 6207 return int_fileify_dirspec("[]", buf, NULL);
a480973c 6208 }
b8ffc8df 6209 else if (trndir[1] == '.' &&
a480973c 6210 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
c5375c28
JM
6211 PerlMem_free(trndir);
6212 PerlMem_free(vmsdir);
a979ce91 6213 return int_fileify_dirspec("[-]", buf, NULL);
a480973c 6214 }
748a9306 6215 }
b8ffc8df 6216 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
a0d0e21e 6217 dirlen -= 1; /* to last element */
b8ffc8df 6218 lastdir = strrchr(trndir,'/');
a0d0e21e 6219 }
b8ffc8df 6220 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
01b8edb6 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 */
4633a7c4
LW
6224 do {
6225 if (*(cp1+2) == '.') cp1++;
6226 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
a480973c 6227 char * ret_chr;
df278665 6228 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
c5375c28
JM
6229 PerlMem_free(trndir);
6230 PerlMem_free(vmsdir);
a480973c
JM
6231 return NULL;
6232 }
fc1ce8cc 6233 if (strchr(vmsdir,'/') != NULL) {
df278665 6234 /* If int_tovmsspec() returned it, it must have VMS syntax
fc1ce8cc
CB
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 */
c5375c28
JM
6239 PerlMem_free(trndir);
6240 PerlMem_free(vmsdir);
a480973c
JM
6241 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6242 return NULL;
fc1ce8cc 6243 }
a979ce91 6244 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6245 PerlMem_free(trndir);
6246 PerlMem_free(vmsdir);
a480973c
JM
6247 return NULL;
6248 }
0e5ce2c7 6249 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6250 PerlMem_free(trndir);
6251 PerlMem_free(vmsdir);
a480973c 6252 return ret_chr;
4633a7c4
LW
6253 }
6254 cp1++;
6255 } while ((cp1 = strstr(cp1,"/.")) != NULL);
b8ffc8df 6256 lastdir = strrchr(trndir,'/');
748a9306 6257 }
b8ffc8df 6258 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
a480973c 6259 char * ret_chr;
61bb5906
CB
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. */
f7ddb74a
JM
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
b8ffc8df 6269 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
df278665 6270 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
c5375c28
JM
6271 PerlMem_free(trndir);
6272 PerlMem_free(vmsdir);
a480973c
JM
6273 return NULL;
6274 }
a979ce91 6275 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6276 PerlMem_free(trndir);
6277 PerlMem_free(vmsdir);
a480973c
JM
6278 return NULL;
6279 }
0e5ce2c7 6280 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6281 PerlMem_free(trndir);
6282 PerlMem_free(vmsdir);
a480973c 6283 return ret_chr;
61bb5906 6284 }
a0d0e21e 6285 else {
f7ddb74a 6286
b8ffc8df
RGS
6287 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6288 !(lastdir = cp1 = strrchr(trndir,']')) &&
6289 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
f7ddb74a 6290
a979ce91
JM
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 }
a0d0e21e 6320 }
a979ce91 6321
748a9306 6322 }
f7ddb74a
JM
6323
6324 retlen = dirlen + 6;
a979ce91
JM
6325 memcpy(buf, trndir, dirlen);
6326 buf[dirlen] = '\0';
f7ddb74a 6327
a0d0e21e
LW
6328 /* We've picked up everything up to the directory file name.
6329 Now just add the type and version, and we're set. */
df278665
JM
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 */
a979ce91 6365 strcat(buf, ".dir");
df278665
JM
6366 } else {
6367 /* VMS expects the .DIR to be in upper case */
a979ce91 6368 strcat(buf, ".DIR");
df278665
JM
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))
a979ce91 6374 strcat(buf, ";1");
df278665 6375 }
c5375c28
JM
6376 PerlMem_free(trndir);
6377 PerlMem_free(vmsdir);
a979ce91 6378 return buf;
a0d0e21e
LW
6379 }
6380 else { /* VMS-style directory spec */
a480973c 6381
d584a1c6
JM
6382 char *esa, *esal, term, *cp;
6383 char *my_esa;
6384 int my_esa_len;
01b8edb6 6385 unsigned long int sts, cmplen, haslower = 0;
a480973c
JM
6386 unsigned int nam_fnb;
6387 char * nam_type;
a0d0e21e 6388 struct FAB dirfab = cc$rms_fab;
a480973c
JM
6389 rms_setup_nam(savnam);
6390 rms_setup_nam(dirnam);
6391
d584a1c6 6392 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 6393 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
6394 esal = NULL;
6395#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6396 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 6397 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 6398#endif
a480973c
JM
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);
d584a1c6 6402 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
f7ddb74a
JM
6403#ifdef NAM$M_NO_SHORT_UPCASE
6404 if (decc_efs_case_preserve)
a480973c 6405 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6406#endif
01b8edb6 6407
b8ffc8df 6408 for (cp = trndir; *cp; cp++)
01b8edb6 6409 if (islower(*cp)) { haslower = 1; break; }
a480973c 6410 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
a979ce91
JM
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);
e518068a 6416 }
6417 if (!sts) {
c5375c28 6418 PerlMem_free(esa);
d584a1c6
JM
6419 if (esal != NULL)
6420 PerlMem_free(esal);
c5375c28
JM
6421 PerlMem_free(trndir);
6422 PerlMem_free(vmsdir);
748a9306
LW
6423 set_errno(EVMSERR);
6424 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
6425 return NULL;
6426 }
e518068a 6427 }
6428 else {
6429 savnam = dirnam;
a480973c
JM
6430 /* Does the file really exist? */
6431 if (sys$search(&dirfab)& STS$K_SUCCESS) {
e518068a 6432 /* Yes; fake the fnb bits so we'll check type below */
a979ce91 6433 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
e518068a 6434 }
752635ea
CB
6435 else { /* No; just work with potential name */
6436 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6437 else {
2623a4a6
JM
6438 int fab_sts;
6439 fab_sts = dirfab.fab$l_sts;
6440 sts = rms_free_search_context(&dirfab);
c5375c28 6441 PerlMem_free(esa);
d584a1c6
JM
6442 if (esal != NULL)
6443 PerlMem_free(esal);
c5375c28
JM
6444 PerlMem_free(trndir);
6445 PerlMem_free(vmsdir);
2623a4a6 6446 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
e518068a 6447 return NULL;
6448 }
e518068a 6449 }
a0d0e21e 6450 }
d584a1c6
JM
6451
6452 /* Make sure we are using the right buffer */
778e045f 6453#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6
JM
6454 if (esal != NULL) {
6455 my_esa = esal;
6456 my_esa_len = rms_nam_esll(dirnam);
6457 } else {
778e045f 6458#endif
d584a1c6
JM
6459 my_esa = esa;
6460 my_esa_len = rms_nam_esl(dirnam);
778e045f 6461#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 6462 }
778e045f 6463#endif
d584a1c6 6464 my_esa[my_esa_len] = '\0';
a480973c 6465 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
d584a1c6
JM
6466 cp1 = strchr(my_esa,']');
6467 if (!cp1) cp1 = strchr(my_esa,'>');
748a9306 6468 if (cp1) { /* Should always be true */
d584a1c6
JM
6469 my_esa_len -= cp1 - my_esa - 1;
6470 memmove(my_esa, cp1 + 1, my_esa_len);
748a9306
LW
6471 }
6472 }
a480973c 6473 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
a0d0e21e 6474 /* Yep; check version while we're at it, if it's there. */
a480973c
JM
6475 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6476 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
a0d0e21e 6477 /* Something other than .DIR[;1]. Bzzt. */
a480973c 6478 sts = rms_free_search_context(&dirfab);
c5375c28 6479 PerlMem_free(esa);
d584a1c6
JM
6480 if (esal != NULL)
6481 PerlMem_free(esal);
c5375c28
JM
6482 PerlMem_free(trndir);
6483 PerlMem_free(vmsdir);
748a9306
LW
6484 set_errno(ENOTDIR);
6485 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
6486 return NULL;
6487 }
748a9306 6488 }
ae6d78fe 6489
a480973c 6490 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
748a9306 6491 /* They provided at least the name; we added the type, if necessary, */
a979ce91 6492 strcpy(buf, my_esa);
a480973c 6493 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6494 PerlMem_free(trndir);
6495 PerlMem_free(esa);
d584a1c6
JM
6496 if (esal != NULL)
6497 PerlMem_free(esal);
c5375c28 6498 PerlMem_free(vmsdir);
a979ce91 6499 return buf;
748a9306 6500 }
c07a80fd 6501 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6502 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6503 *cp1 = '\0';
d584a1c6 6504 my_esa_len -= 9;
c07a80fd 6505 }
d584a1c6 6506 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
752635ea 6507 if (cp1 == NULL) { /* should never happen */
a480973c 6508 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6509 PerlMem_free(trndir);
6510 PerlMem_free(esa);
d584a1c6
JM
6511 if (esal != NULL)
6512 PerlMem_free(esal);
c5375c28 6513 PerlMem_free(vmsdir);
752635ea
CB
6514 return NULL;
6515 }
748a9306
LW
6516 term = *cp1;
6517 *cp1 = '\0';
d584a1c6
JM
6518 retlen = strlen(my_esa);
6519 cp1 = strrchr(my_esa,'.');
f7ddb74a 6520 /* ODS-5 directory specifications can have extra "." in them. */
657054d4 6521 /* Fix-me, can not scan EFS file specifications backwards */
f7ddb74a 6522 while (cp1 != NULL) {
d584a1c6 6523 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
f7ddb74a
JM
6524 break;
6525 else {
6526 cp1--;
d584a1c6 6527 while ((cp1 > my_esa) && (*cp1 != '.'))
f7ddb74a
JM
6528 cp1--;
6529 }
d584a1c6 6530 if (cp1 == my_esa)
f7ddb74a
JM
6531 cp1 = NULL;
6532 }
6533
6534 if ((cp1) != NULL) {
748a9306
LW
6535 /* There's more than one directory in the path. Just roll back. */
6536 *cp1 = term;
a979ce91 6537 strcpy(buf, my_esa);
a0d0e21e
LW
6538 }
6539 else {
a480973c 6540 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
748a9306 6541 /* Go back and expand rooted logical name */
a480973c 6542 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
f7ddb74a
JM
6543#ifdef NAM$M_NO_SHORT_UPCASE
6544 if (decc_efs_case_preserve)
a480973c 6545 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6546#endif
a480973c
JM
6547 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6548 sts = rms_free_search_context(&dirfab);
c5375c28 6549 PerlMem_free(esa);
d584a1c6
JM
6550 if (esal != NULL)
6551 PerlMem_free(esal);
c5375c28
JM
6552 PerlMem_free(trndir);
6553 PerlMem_free(vmsdir);
748a9306
LW
6554 set_errno(EVMSERR);
6555 set_vaxc_errno(dirfab.fab$l_sts);
6556 return NULL;
6557 }
d584a1c6
JM
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' */
d584a1c6
JM
6567 cp1 = strstr(my_esa,"][");
6568 if (!cp1) cp1 = strstr(my_esa,"]<");
6569 dirlen = cp1 - my_esa;
a979ce91 6570 memcpy(buf, my_esa, dirlen);
748a9306 6571 if (!strncmp(cp1+2,"000000]",7)) {
a979ce91 6572 buf[dirlen-1] = '\0';
657054d4 6573 /* fix-me Not full ODS-5, just extra dots in directories for now */
a979ce91
JM
6574 cp1 = buf + dirlen - 1;
6575 while (cp1 > buf)
f7ddb74a
JM
6576 {
6577 if (*cp1 == '[')
6578 break;
6579 if (*cp1 == '.') {
6580 if (*(cp1-1) != '^')
6581 break;
6582 }
6583 cp1--;
6584 }
4633a7c4
LW
6585 if (*cp1 == '.') *cp1 = ']';
6586 else {
a979ce91 6587 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6588 memmove(cp1+1,"000000]",7);
4633a7c4 6589 }
748a9306
LW
6590 }
6591 else {
a979ce91
JM
6592 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6593 buf[retlen] = '\0';
748a9306 6594 /* Convert last '.' to ']' */
a979ce91 6595 cp1 = buf+retlen-1;
f7ddb74a
JM
6596 while (*cp != '[') {
6597 cp1--;
6598 if (*cp1 == '.') {
6599 /* Do not trip on extra dots in ODS-5 directories */
a979ce91 6600 if ((cp1 == buf) || (*(cp1-1) != '^'))
f7ddb74a
JM
6601 break;
6602 }
6603 }
4633a7c4
LW
6604 if (*cp1 == '.') *cp1 = ']';
6605 else {
a979ce91 6606 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6607 memmove(cp1+1,"000000]",7);
4633a7c4 6608 }
748a9306 6609 }
a0d0e21e 6610 }
748a9306 6611 else { /* This is a top-level dir. Add the MFD to the path. */
d584a1c6 6612 cp1 = my_esa;
a979ce91 6613 cp2 = buf;
bbdb6c9a 6614 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
a0d0e21e
LW
6615 strcpy(cp2,":[000000]");
6616 cp1 += 2;
6617 strcpy(cp2+9,cp1);
6618 }
748a9306 6619 }
a480973c 6620 sts = rms_free_search_context(&dirfab);
748a9306 6621 /* We've set up the string up through the filename. Add the
a0d0e21e 6622 type and version, and we're done. */
a979ce91 6623 strcat(buf,".DIR;1");
01b8edb6 6624
6625 /* $PARSE may have upcased filespec, so convert output to lower
6626 * case if input contained any lowercase characters. */
a979ce91 6627 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
c5375c28
JM
6628 PerlMem_free(trndir);
6629 PerlMem_free(esa);
d584a1c6
JM
6630 if (esal != NULL)
6631 PerlMem_free(esal);
c5375c28 6632 PerlMem_free(vmsdir);
a979ce91 6633 return buf;
a0d0e21e 6634 }
a979ce91
JM
6635} /* end of int_fileify_dirspec() */
6636
6637
6638/*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6639static 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;
a0d0e21e
LW
6666} /* end of do_fileify_dirspec() */
6667/*}}}*/
a979ce91 6668
a0d0e21e 6669/* External entry points */
b8ffc8df 6670char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 6671{ return do_fileify_dirspec(dir,buf,0,NULL); }
b8ffc8df 6672char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
6673{ return do_fileify_dirspec(dir,buf,1,NULL); }
6674char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6675{ return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6676char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6677{ return do_fileify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 6678
1fe570cc
JM
6679static 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 */
6759static char *int_pathify_dirspec(const char *dir, char *buf)
a0d0e21e 6760{
1fe570cc
JM
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;
2d9f3838 6765 unsigned short int trnlnm_iter_count;
baf3cf9c 6766 STRLEN trnlen;
1fe570cc
JM
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;
a0d0e21e 6779
c07a80fd 6780 if (!dir || !*dir) {
1fe570cc
JM
6781 set_errno(EINVAL);
6782 set_vaxc_errno(SS$_BADPARAM);
6783 return NULL;
c07a80fd 6784 }
6785
c5375c28 6786 trndir = PerlMem_malloc(VMS_MAXRSS);
1fe570cc
JM
6787 if (trndir == NULL)
6788 _ckvmssts_noperl(SS$_INSFMEM);
c07a80fd 6789
1fe570cc
JM
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 */
2d9f3838 6799 trnlnm_iter_count = 0;
93948341 6800 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
1fe570cc
JM
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 }
c07a80fd 6819 }
748a9306 6820
1fe570cc 6821 /* At this point we do not work with *dir, but the copy in *trndir */
b8ffc8df 6822
1fe570cc
JM
6823 if (need_to_lower && !decc_efs_case_preserve) {
6824 /* Legacy mode, lower case the returned value */
6825 __mystrtolower(trndir);
6826 }
f7ddb74a 6827
1fe570cc
JM
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 }
a0d0e21e 6877 }
a0d0e21e 6878 }
1fe570cc
JM
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);
a0d0e21e 6884
1fe570cc
JM
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;
b7ae7a0d 6892 }
1fe570cc
JM
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 }
b7ae7a0d 6922 }
1fe570cc
JM
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;
a480973c 6933
1fe570cc
JM
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 }
01b8edb6 6974
1fe570cc
JM
6975 if (!is_dir) {
6976 lastdot = strrchr(lastslash, '.');
6977 }
6978 if (lastdot != NULL) {
6979 STRLEN e_len;
01b8edb6 6980
1fe570cc
JM
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 }
e518068a 6991 }
1fe570cc
JM
6992
6993 strcpy(buf, trndir);
6994 if (buf[dir_len - 1] != '/') {
6995 buf[dir_len] = '/';
6996 buf[dir_len + 1] = '\0';
a0d0e21e 6997 }
1fe570cc
JM
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 }
e518068a 7046 }
1fe570cc
JM
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);
a0d0e21e 7055 }
1fe570cc
JM
7056 return ret_spec;
7057 }
7058}
d584a1c6 7059
1fe570cc
JM
7060/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
7061static 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 }
d584a1c6 7078
1fe570cc
JM
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);
a0d0e21e
LW
7085 }
7086
1fe570cc
JM
7087 return ret_spec;
7088
a0d0e21e 7089} /* end of do_pathify_dirspec() */
1fe570cc
JM
7090
7091
a0d0e21e 7092/* External entry points */
b8ffc8df 7093char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 7094{ return do_pathify_dirspec(dir,buf,0,NULL); }
b8ffc8df 7095char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
7096{ return do_pathify_dirspec(dir,buf,1,NULL); }
7097char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7098{ return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7099char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7100{ return do_pathify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 7101
0e5ce2c7
JM
7102/* Internal tounixspec routine that does not use a thread context */
7103/*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7104static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
a0d0e21e 7105{
0e5ce2c7 7106 char *dirend, *cp1, *cp3, *tmp;
b8ffc8df 7107 const char *cp2;
a480973c 7108 int devlen, dirlen, retlen = VMS_MAXRSS;
0f20d7df 7109 int expand = 1; /* guarantee room for leading and trailing slashes */
2d9f3838 7110 unsigned short int trnlnm_iter_count;
f7ddb74a 7111 int cmp_rslt;
360732b5
JM
7112 if (utf8_fl != NULL)
7113 *utf8_fl = 0;
a0d0e21e 7114
0e5ce2c7
JM
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;
e518068a 7132 }
f7ddb74a 7133
2497a41f
JM
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
c5375c28 7144 tunix = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7145 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
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);
367e4b85 7156 PerlMem_free(tunix);
2497a41f
JM
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
f7ddb74a
JM
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) {
a0d0e21e 7205 strcpy(rslt,spec);
0e5ce2c7
JM
7206 if (vms_debug_fileify) {
7207 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7208 }
a0d0e21e
LW
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);
0e5ce2c7
JM
7219 if (vms_debug_fileify) {
7220 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7221 }
a0d0e21e
LW
7222 return rslt;
7223 }
f7ddb74a
JM
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
c5375c28 7263 tmp = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7264 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
7265 if (cmp_rslt == 0) {
7266 int islnm;
7267
b8486b9d 7268 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
f7ddb74a
JM
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
a5f75d66 7281 if (*cp2 != '[' && *cp2 != '<') {
a0d0e21e
LW
7282 *(cp1++) = '/';
7283 }
7284 else { /* the VMS spec begins with directories */
7285 cp2++;
a5f75d66 7286 if (*cp2 == ']' || *cp2 == '>') {
f86702cc 7287 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
367e4b85 7288 PerlMem_free(tmp);
a5f75d66
AD
7289 return rslt;
7290 }
f7ddb74a 7291 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
2f4077ca 7292 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
367e4b85 7293 PerlMem_free(tmp);
0e5ce2c7
JM
7294 if (vms_debug_fileify) {
7295 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7296 }
a0d0e21e
LW
7297 return NULL;
7298 }
2d9f3838 7299 trnlnm_iter_count = 0;
a0d0e21e
LW
7300 do {
7301 cp3 = tmp;
7302 while (*cp3 != ':' && *cp3) cp3++;
7303 *(cp3++) = '\0';
7304 if (strchr(cp3,']') != NULL) break;
2d9f3838
CB
7305 trnlnm_iter_count++;
7306 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
f675dbe5 7307 } while (vmstrnenv(tmp,tmp,0,fildev,0));
0e5ce2c7 7308 cp1 = rslt;
f86702cc 7309 cp3 = tmp;
7310 *(cp1++) = '/';
7311 while (*cp3) {
7312 *(cp1++) = *(cp3++);
0e5ce2c7 7313 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
367e4b85 7314 PerlMem_free(tmp);
0e5ce2c7
JM
7315 set_errno(ENAMETOOLONG);
7316 set_vaxc_errno(SS$_BUFFEROVF);
7317 if (vms_debug_fileify) {
7318 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7319 }
2f4077ca
JM
7320 return NULL; /* No room */
7321 }
a0d0e21e 7322 }
f86702cc 7323 *(cp1++) = '/';
7324 }
f7ddb74a
JM
7325 if ((*cp2 == '^')) {
7326 /* EFS file escape, pass the next character as is */
38a44b82 7327 /* Fix me: HEX encoding for Unicode not implemented */
f7ddb74a
JM
7328 cp2++;
7329 }
f86702cc 7330 else if ( *cp2 == '.') {
7331 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7332 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7333 cp2 += 3;
7334 }
7335 else cp2++;
a0d0e21e 7336 }
a0d0e21e 7337 }
367e4b85 7338 PerlMem_free(tmp);
a0d0e21e 7339 for (; cp2 <= dirend; cp2++) {
f7ddb74a
JM
7340 if ((*cp2 == '^')) {
7341 /* EFS file escape, pass the next character as is */
38a44b82 7342 /* Fix me: HEX encoding for Unicode not implemented */
42cd432e
CB
7343 *(cp1++) = *(++cp2);
7344 /* An escaped dot stays as is -- don't convert to slash */
7345 if (*cp2 == '.') cp2++;
f7ddb74a 7346 }
a0d0e21e
LW
7347 if (*cp2 == ':') {
7348 *(cp1++) = '/';
5ad5b34c 7349 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
a0d0e21e 7350 }
f86702cc 7351 else if (*cp2 == ']' || *cp2 == '>') {
7352 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7353 }
f7ddb74a 7354 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
a0d0e21e 7355 *(cp1++) = '/';
e518068a 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 }
f86702cc 7362 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7363 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7364 cp2 += 2;
7365 }
a0d0e21e
LW
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 */
0e5ce2c7 7374 /* filespecs like */
01b8edb6 7375 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
0e5ce2c7
JM
7376 if (vms_debug_fileify) {
7377 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7378 }
a0d0e21e
LW
7379 return NULL;
7380 }
a0d0e21e
LW
7381 }
7382 else *(cp1++) = *cp2;
7383 }
7384 else *(cp1++) = *cp2;
7385 }
0e5ce2c7 7386 /* Translate the rest of the filename. */
42cd432e 7387 while (*cp2) {
0e5ce2c7
JM
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 }
42cd432e 7457 }
a0d0e21e
LW
7458 *cp1 = '\0';
7459
f7ddb74a
JM
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
0e5ce2c7
JM
7482 if (vms_debug_fileify) {
7483 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7484 }
a0d0e21e
LW
7485 return rslt;
7486
0e5ce2c7
JM
7487} /* end of int_tounixspec() */
7488
7489
7490/*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7491static 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
a0d0e21e
LW
7519} /* end of do_tounixspec() */
7520/*}}}*/
7521/* External entry points */
360732b5
JM
7522char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7523 { return do_tounixspec(spec,buf,0, NULL); }
7524char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7525 { return do_tounixspec(spec,buf,1, NULL); }
7526char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7527 { return do_tounixspec(spec,buf,0, utf8_fl); }
7528char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7529 { return do_tounixspec(spec,buf,1, utf8_fl); }
a0d0e21e 7530
360732b5 7531#if __CRTL_VER >= 70200000 && !defined(__VAX)
2497a41f 7532
360732b5
JM
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
7548static int posix_root_to_vms
7549 (char *vmspath, int vmspath_len,
7550 const char *unixpath,
d584a1c6
JM
7551 const int * utf8_fl)
7552{
2497a41f
JM
7553int sts;
7554struct FAB myfab = cc$rms_fab;
d584a1c6 7555rms_setup_nam(mynam);
2497a41f 7556struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
d584a1c6
JM
7557struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7558char * esa, * esal, * rsa, * rsal;
2497a41f
JM
7559char *vms_delim;
7560int dir_flag;
7561int unixlen;
7562
360732b5 7563 dir_flag = 0;
d584a1c6 7564 vmspath[0] = '\0';
360732b5
JM
7565 unixlen = strlen(unixpath);
7566 if (unixlen == 0) {
360732b5
JM
7567 return RMS$_FNF;
7568 }
7569
7570#if __CRTL_VER >= 80200000
2497a41f 7571 /* If not a posix spec already, convert it */
360732b5
JM
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 }
2497a41f 7581 }
360732b5
JM
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
2497a41f
JM
7630 }
7631 vmspath[vmspath_len] = 0;
7632 if (unixpath[unixlen - 1] == '/')
7633 dir_flag = 1;
d584a1c6
JM
7634 esal = PerlMem_malloc(VMS_MAXRSS);
7635 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7636 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
c5375c28 7637 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
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);
2497a41f
JM
7646 if (decc_efs_case_preserve)
7647 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
ea0c9945 7648#ifdef NAML$M_OPEN_SPECIAL
2497a41f 7649 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
ea0c9945 7650#endif
2497a41f
JM
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)) {
d584a1c6 7657 PerlMem_free(esal);
367e4b85 7658 PerlMem_free(esa);
d584a1c6
JM
7659 PerlMem_free(rsal);
7660 PerlMem_free(rsa);
2497a41f
JM
7661 return sts;
7662 }
7663
7664 /* get the Device ID and the FID */
7665 sts = sys$search(&myfab);
d584a1c6
JM
7666
7667 /* These are no longer needed */
7668 PerlMem_free(esa);
7669 PerlMem_free(rsal);
7670 PerlMem_free(rsa);
7671
2497a41f
JM
7672 /* on any failure, returned the POSIX ^UP^ filespec */
7673 if (!(sts & 1)) {
d584a1c6 7674 PerlMem_free(esal);
2497a41f
JM
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 }
d584a1c6 7748 PerlMem_free(esal);
2497a41f
JM
7749 return sts;
7750}
7751
360732b5
JM
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
7757static int
7758slash_dev_special_to_vms
7759 (const char * unixptr,
7760 char * vmspath,
7761 int vmspath_len)
7762{
7763char * nextslash;
7764int len;
7765int cmp;
7766int 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 */
2497a41f 7793static int posix_to_vmsspec_hardway
360732b5
JM
7794 (char *vmspath, int vmspath_len,
7795 const char *unixpath,
7796 int dir_flag,
7797 int * utf8_fl) {
2497a41f
JM
7798
7799char *esa;
7800const char *unixptr;
360732b5 7801const char *unixend;
2497a41f
JM
7802char *vmsptr;
7803const char *lastslash;
7804const char *lastdot;
7805int unixlen;
7806int vmslen;
7807int dir_start;
7808int dir_dot;
7809int quoted;
360732b5
JM
7810char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7811int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
2497a41f 7812
360732b5
JM
7813 if (utf8_fl != NULL)
7814 *utf8_fl = 0;
2497a41f
JM
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
360732b5
JM
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
2497a41f
JM
7839 lastslash = strrchr(unixptr,'/');
7840 lastdot = strrchr(unixptr,'.');
360732b5
JM
7841 unixend = strrchr(unixptr,'\"');
7842 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7843 unixend = unixptr + unixlen;
7844 }
2497a41f
JM
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;
2497a41f
JM
7873
7874 vmsptr = vmspath;
7875 vmslen = 0;
7876
2497a41f
JM
7877 /* Start with the UNIX path */
7878 if (*unixptr != '/') {
7879 /* relative paths */
360732b5
JM
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
2497a41f
JM
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] == '.') &&
360732b5 8055 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
2497a41f
JM
8056 *vmsptr++ = '.';
8057 vmslen++;
8058 dir_dot = 1;
360732b5 8059 }
2497a41f
JM
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] == '.') {
360732b5 8075 if (&unixptr[1] == unixend) {
2497a41f
JM
8076 *vmsptr++ = '[';
8077 *vmsptr++ = ']';
8078 vmslen += 2;
8079 *vmsptr++ = '\0';
8080 return SS$_NORMAL;
8081 }
360732b5 8082 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
2497a41f
JM
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 */
c5375c28
JM
8105 esa = PerlMem_malloc(vmspath_len);
8106 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
8107
8108 sts = SS$_NORMAL;
8109 nextslash = strchr(&unixptr[1],'/');
8110 seg_len = 0;
8111 if (nextslash != NULL) {
360732b5 8112 int cmp;
2497a41f
JM
8113 seg_len = nextslash - &unixptr[1];
8114 strncpy(vmspath, unixptr, seg_len + 1);
8115 vmspath[seg_len+1] = 0;
360732b5
JM
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);
2497a41f
JM
8126 }
8127
360732b5 8128 if ($VMS_STATUS_SUCCESS(sts)) {
2497a41f
JM
8129 /* This is verified to be a real path */
8130
360732b5
JM
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 }
2497a41f
JM
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;
360732b5 8193 int cmp;
2497a41f
JM
8194
8195 /* now we have foo:bar or foo:[000000]bar to decide from */
7ded3206 8196 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
360732b5
JM
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
7ded3206 8214 trnend = islnm ? islnm - 1 : 0;
2497a41f
JM
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.
360732b5
JM
8229 *
8230 * Not that easy to detect as "/dev" may be file structured with
8231 * special device files.
2497a41f
JM
8232 */
8233
30e68285 8234 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
360732b5 8235 (&nextslash[1] == unixend)) {
2497a41f
JM
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 */
367e4b85 8268 PerlMem_free(esa);
2497a41f
JM
8269 } /* End of relative/absolute path handling */
8270
360732b5 8271 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
2497a41f 8272 int dash_flag;
360732b5
JM
8273 int in_cnt;
8274 int out_cnt;
2497a41f
JM
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 == '.') &&
360732b5
JM
8283 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8284 (&unixptr[1]==unixend)))) {
2497a41f
JM
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 == '.') &&
360732b5 8301 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
2497a41f
JM
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] == '.') &&
360732b5 8314 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
2497a41f
JM
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? */
360732b5 8389 if (unixptr >= unixend)
2497a41f
JM
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;
360732b5 8420 if (unixptr != unixend)
2497a41f
JM
8421 unixptr++;
8422 vmslen++;
8423 break;
2497a41f 8424 case '.':
360732b5
JM
8425 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8426 (&unixptr[1] == unixend)) {
2497a41f
JM
8427 *vmsptr++ = '^';
8428 *vmsptr++ = '.';
8429 vmslen += 2;
8430 unixptr++;
8431
8432 /* trailing dot ==> '^..' on VMS */
360732b5 8433 if (unixptr == unixend) {
2497a41f
JM
8434 *vmsptr++ = '.';
8435 vmslen++;
360732b5 8436 unixptr++;
2497a41f 8437 }
2497a41f
JM
8438 break;
8439 }
360732b5 8440
2497a41f 8441 *vmsptr++ = *unixptr++;
360732b5
JM
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;
2497a41f
JM
8453 break;
8454 case '~':
8455 case ';':
8456 case '\\':
360732b5
JM
8457 case '?':
8458 case ' ':
2497a41f 8459 default:
360732b5
JM
8460 in_cnt = copy_expand_unix_filename_escape
8461 (vmsptr, unixptr, &out_cnt, utf8_fl);
8462 vmsptr += out_cnt;
8463 unixptr += in_cnt;
2497a41f
JM
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;
360732b5
JM
8492 if ((vmslen > 1) &&
8493 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
30e68285 8494 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
2497a41f
JM
8495 *vmsptr++ = '.';
8496 vmslen++;
8497 }
8498 }
8499
8500 *vmsptr = '\0';
8501 return SS$_NORMAL;
8502}
8503#endif
8504
360732b5
JM
8505 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8506static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8507{
8508char * result;
8509int 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
df278665 8531
360732b5 8532/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
df278665
JM
8533static char *int_tovmsspec
8534 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8535 char *dirend;
f7ddb74a
JM
8536 char *lastdot;
8537 char *vms_delim;
b8ffc8df
RGS
8538 register char *cp1;
8539 const char *cp2;
e518068a 8540 unsigned long int infront = 0, hasdir = 1;
f7ddb74a
JM
8541 int rslt_len;
8542 int no_type_seen;
360732b5
JM
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;
a0d0e21e 8545
df278665
JM
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 }
4d743a9b 8559 rslt_len = VMS_MAXRSS-1;
360732b5
JM
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 }
a0d0e21e 8577 }
f7ddb74a 8578
2497a41f
JM
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) {
360732b5 8584 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
2497a41f
JM
8585 return rslt;
8586 }
8587 }
8588#endif
8589
360732b5
JM
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 */
2497a41f 8615
360732b5
JM
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);
df278665
JM
8621 if (vms_debug_fileify) {
8622 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8623 }
360732b5
JM
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.
2497a41f 8631
360732b5
JM
8632 So in the case of ambiguity, leave the specification alone.
8633 */
2497a41f 8634
2497a41f 8635
360732b5
JM
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);
df278665
JM
8642 if (vms_debug_fileify) {
8643 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8644 }
2497a41f
JM
8645 return rslt;
8646 }
8647
360732b5
JM
8648 dirend = strrchr(path,'/');
8649
8650 if (dirend == NULL) {
df278665
JM
8651 char *macro_start;
8652 int has_macro;
8653
360732b5
JM
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
df278665
JM
8657 it a UNIX shell macro. MakeMaker wants shell macros passed
8658 through AS-IS,
360732b5
JM
8659
8660 utf8 flag setting needs to be preserved.
8661 */
df278665
JM
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 }
360732b5
JM
8678 }
8679
30e68285 8680/* If EFS charset mode active, handle the conversion */
2497a41f 8681#if __CRTL_VER >= 80200000 && !defined(__VAX)
360732b5
JM
8682 if (decc_efs_charset) {
8683 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
df278665
JM
8684 if (vms_debug_fileify) {
8685 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8686 }
2497a41f
JM
8687 return rslt;
8688 }
8689#endif
f7ddb74a 8690
f86702cc 8691 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
748a9306
LW
8692 if (!*(dirend+2)) dirend +=2;
8693 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
360732b5
JM
8694 if (decc_efs_charset == 0) {
8695 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8696 }
748a9306 8697 }
f7ddb74a 8698
a0d0e21e
LW
8699 cp1 = rslt;
8700 cp2 = path;
f7ddb74a 8701 lastdot = strrchr(cp2,'.');
a0d0e21e 8702 if (*cp2 == '/') {
a480973c 8703 char *trndev;
e518068a 8704 int islnm, rooted;
8705 STRLEN trnend;
8706
b7ae7a0d 8707 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
61bb5906 8708 if (!*(cp2+1)) {
f7ddb74a
JM
8709 if (decc_disable_posix_root) {
8710 strcpy(rslt,"sys$disk:[000000]");
8711 }
8712 else {
8713 strcpy(rslt,"sys$posix_root:[000000]");
8714 }
360732b5
JM
8715 if (utf8_flag != NULL)
8716 *utf8_flag = 0;
df278665
JM
8717 if (vms_debug_fileify) {
8718 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8719 }
61bb5906
CB
8720 return rslt;
8721 }
a0d0e21e 8722 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
e518068a 8723 *cp1 = '\0';
c5375c28 8724 trndev = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 8725 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
b8486b9d 8726 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
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;
b8486b9d 8734 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8735 }
8736 else if (strcmp(rslt,"tmp") == 0) {
8737 strcpy(rslt,"sys$scratch");
8738 cp1 = rslt + 11;
8739 *cp1 = 0;
b8486b9d 8740 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8741 }
8742 else if (!decc_disable_posix_root) {
8743 strcpy(rslt, "sys$posix_root");
b8486b9d 8744 cp1 = rslt + 14;
f7ddb74a
JM
8745 *cp1 = 0;
8746 cp2 = path;
8747 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
b8486b9d 8748 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
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;
b8486b9d 8757 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8758 }
8759 }
8760 }
8761 }
8762
e518068a 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) {
e518068a 8776 strcpy(rslt,trndev);
8777 cp1 = rslt + trnend;
755b3d5d
JM
8778 if (*cp2 != 0) {
8779 *(cp1++) = '.';
8780 cp2++;
8781 }
e518068a 8782 }
8783 else {
f7ddb74a
JM
8784 if (decc_disable_posix_root) {
8785 *(cp1++) = ':';
8786 hasdir = 0;
8787 }
e518068a 8788 }
8789 }
367e4b85 8790 PerlMem_free(trndev);
748a9306 8791 }
a0d0e21e
LW
8792 else {
8793 *(cp1++) = '[';
748a9306
LW
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 }
f86702cc 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 }
f7ddb74a
JM
8809 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8810 /* Escape the extra dots in EFS file specifications */
8811 *(cp1++) = '^';
8812 }
748a9306
LW
8813 if (cp2 > dirend) cp2 = dirend;
8814 }
8815 else *(cp1++) = '.';
8816 }
8817 for (; cp2 < dirend; cp2++) {
8818 if (*cp2 == '/') {
01b8edb6 8819 if (*(cp2-1) == '/') continue;
748a9306
LW
8820 if (*(cp1-1) != '.') *(cp1++) = '.';
8821 infront = 0;
8822 }
8823 else if (!infront && *cp2 == '.') {
01b8edb6 8824 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8825 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
fd7385b9
CB
8826 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8827 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
748a9306 8828 else if (*(cp1-2) == '[') *(cp1-1) = '-';
fd7385b9
CB
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 }
748a9306
LW
8836 }
8837 cp2 += 2;
01b8edb6 8838 if (cp2 == dirend) break;
748a9306 8839 }
f86702cc 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 }
f7ddb74a
JM
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 }
748a9306
LW
8858 }
8859 else {
e518068a 8860 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
f7ddb74a
JM
8861 if (*cp2 == '.') {
8862 if (decc_efs_charset == 0)
8863 *(cp1++) = '_';
8864 else {
8865 *(cp1++) = '^';
8866 *(cp1++) = '.';
8867 }
8868 }
748a9306
LW
8869 else *(cp1++) = *cp2;
8870 infront = 1;
8871 }
a0d0e21e 8872 }
748a9306 8873 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
e518068a 8874 if (hasdir) *(cp1++) = ']';
748a9306 8875 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
f7ddb74a
JM
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 '?':
360732b5
JM
8883 if (decc_efs_charset == 0)
8884 *(cp1++) = '%';
8885 else
8886 *(cp1++) = '?';
f7ddb74a
JM
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;
360732b5
JM
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;
f7ddb74a
JM
8943 case '\"':
8944 case '~':
8945 case '`':
8946 case '!':
8947 case '#':
8948 case '%':
8949 case '^':
adc11f0b
CB
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. */
f7ddb74a
JM
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
657054d4 8979 * which is wrong. UNIX notation should be ".dir." unless
f7ddb74a
JM
8980 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8981 * changing this behavior could break more things at this time.
2497a41f
JM
8982 * efs character set effectively does not allow "." to be a version
8983 * delimiter as a further complication about changing this.
f7ddb74a
JM
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 }
a0d0e21e
LW
9004 *cp1 = '\0';
9005
360732b5
JM
9006 if (utf8_flag != NULL)
9007 *utf8_flag = 0;
df278665
JM
9008 if (vms_debug_fileify) {
9009 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
9010 }
a0d0e21e
LW
9011 return rslt;
9012
df278665
JM
9013} /* end of int_tovmsspec() */
9014
9015
9016/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
9017static 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() */
a0d0e21e
LW
9046/*}}}*/
9047/* External entry points */
360732b5
JM
9048char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
9049 { return do_tovmsspec(path,buf,0,NULL); }
9050char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
9051 { return do_tovmsspec(path,buf,1,NULL); }
9052char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9053 { return do_tovmsspec(path,buf,0,utf8_fl); }
9054char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9055 { return do_tovmsspec(path,buf,1,utf8_fl); }
9056
4846f1d7
JM
9057/*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
9058/* Internal routine for use with out an explict context present */
9059static 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
360732b5
JM
9084/*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9085static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 9086 static char __tovmspath_retbuf[VMS_MAXRSS];
a0d0e21e 9087 int vmslen;
a480973c 9088 char *pathified, *vmsified, *cp;
a0d0e21e 9089
748a9306 9090 if (path == NULL) return NULL;
c5375c28
JM
9091 pathified = PerlMem_malloc(VMS_MAXRSS);
9092 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 9093 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 9094 PerlMem_free(pathified);
a480973c
JM
9095 return NULL;
9096 }
c5375c28
JM
9097
9098 vmsified = NULL;
9099 if (buf == NULL)
9100 Newx(vmsified, VMS_MAXRSS, char);
360732b5 9101 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
c5375c28
JM
9102 PerlMem_free(pathified);
9103 if (vmsified) Safefree(vmsified);
a480973c
JM
9104 return NULL;
9105 }
c5375c28 9106 PerlMem_free(pathified);
a480973c 9107 if (buf) {
a480973c
JM
9108 return buf;
9109 }
a0d0e21e
LW
9110 else if (ts) {
9111 vmslen = strlen(vmsified);
a02a5408 9112 Newx(cp,vmslen+1,char);
a0d0e21e
LW
9113 memcpy(cp,vmsified,vmslen);
9114 cp[vmslen] = '\0';
a480973c 9115 Safefree(vmsified);
a0d0e21e
LW
9116 return cp;
9117 }
9118 else {
9119 strcpy(__tovmspath_retbuf,vmsified);
a480973c 9120 Safefree(vmsified);
a0d0e21e
LW
9121 return __tovmspath_retbuf;
9122 }
9123
9124} /* end of do_tovmspath() */
9125/*}}}*/
9126/* External entry points */
360732b5
JM
9127char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9128 { return do_tovmspath(path,buf,0, NULL); }
9129char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9130 { return do_tovmspath(path,buf,1, NULL); }
9131char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9132 { return do_tovmspath(path,buf,0,utf8_fl); }
9133char *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)*/
9138static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 9139 static char __tounixpath_retbuf[VMS_MAXRSS];
a0d0e21e 9140 int unixlen;
a480973c 9141 char *pathified, *unixified, *cp;
a0d0e21e 9142
748a9306 9143 if (path == NULL) return NULL;
c5375c28
JM
9144 pathified = PerlMem_malloc(VMS_MAXRSS);
9145 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 9146 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 9147 PerlMem_free(pathified);
a480973c
JM
9148 return NULL;
9149 }
c5375c28
JM
9150
9151 unixified = NULL;
9152 if (buf == NULL) {
9153 Newx(unixified, VMS_MAXRSS, char);
9154 }
360732b5 9155 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
c5375c28
JM
9156 PerlMem_free(pathified);
9157 if (unixified) Safefree(unixified);
a480973c
JM
9158 return NULL;
9159 }
c5375c28 9160 PerlMem_free(pathified);
a480973c 9161 if (buf) {
a480973c
JM
9162 return buf;
9163 }
a0d0e21e
LW
9164 else if (ts) {
9165 unixlen = strlen(unixified);
a02a5408 9166 Newx(cp,unixlen+1,char);
a0d0e21e
LW
9167 memcpy(cp,unixified,unixlen);
9168 cp[unixlen] = '\0';
a480973c 9169 Safefree(unixified);
a0d0e21e
LW
9170 return cp;
9171 }
9172 else {
9173 strcpy(__tounixpath_retbuf,unixified);
a480973c 9174 Safefree(unixified);
a0d0e21e
LW
9175 return __tounixpath_retbuf;
9176 }
9177
9178} /* end of do_tounixpath() */
9179/*}}}*/
9180/* External entry points */
360732b5
JM
9181char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9182 { return do_tounixpath(path,buf,0,NULL); }
9183char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9184 { return do_tounixpath(path,buf,1,NULL); }
9185char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9186 { return do_tounixpath(path,buf,0,utf8_fl); }
9187char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9188 { return do_tounixpath(path,buf,1,utf8_fl); }
a0d0e21e
LW
9189
9190/*
cbb8049c 9191 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
9192 *
9193 *****************************************************************************
9194 * *
cbb8049c 9195 * Copyright (C) 1989-1994, 2007 by *
a0d0e21e
LW
9196 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9197 * *
cbb8049c
MP
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. *
a0d0e21e
LW
9202 * *
9203 * 27-Aug-1994 Modified for inclusion in perl5 *
cbb8049c 9204 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
a0d0e21e
LW
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 *
cbb8049c 9220 * Author: Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
9221 */
9222struct list_item
9223 {
9224 struct list_item *next;
9225 char *value;
9226 };
9227
9228static void add_item(struct list_item **head,
9229 struct list_item **tail,
9230 char *value,
9231 int *count);
9232
4b19af01
CB
9233static void mp_expand_wild_cards(pTHX_ char *item,
9234 struct list_item **head,
9235 struct list_item **tail,
9236 int *count);
a0d0e21e 9237
8df869cb 9238static int background_process(pTHX_ int argc, char **argv);
a0d0e21e 9239
fd8cd3a3 9240static void pipe_and_fork(pTHX_ char **cmargv);
a0d0e21e
LW
9241
9242/*{{{ void getredirection(int *ac, char ***av)*/
84902520 9243static void
4b19af01 9244mp_getredirection(pTHX_ int *ac, char ***av)
a0d0e21e
LW
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 */
a0d0e21e
LW
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))
8c3eed29 9288 exit(background_process(aTHX_ --argc, argv));
e518068a 9289 if (*ap && '&' == ap[strlen(ap)-1])
a0d0e21e
LW
9290 {
9291 ap[strlen(ap)-1] = '\0';
8c3eed29 9292 exit(background_process(aTHX_ argc, argv));
a0d0e21e
LW
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 {
fd71b04b 9304 fprintf(stderr,"No input file after < on command line");
748a9306 9305 exit(LIB$_WRONUMARG);
a0d0e21e
LW
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 {
fd71b04b 9319 fprintf(stderr,"No output file after > on command line");
748a9306 9320 exit(LIB$_WRONUMARG);
a0d0e21e
LW
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 {
fd71b04b 9339 fprintf(stderr,"No output file after > or >> on command line");
748a9306 9340 exit(LIB$_WRONUMARG);
a0d0e21e
LW
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
748a9306 9358 err = 2 + ap;
a0d0e21e
LW
9359 if (j >= argc)
9360 {
fd71b04b 9361 fprintf(stderr,"No output file after 2> or 2>> on command line");
748a9306 9362 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9363 }
9364 continue;
9365 }
9366 if (0 == strcmp("|", argv[j]))
9367 {
9368 if (j+1 >= argc)
9369 {
fd71b04b 9370 fprintf(stderr,"No command into which to pipe on command line");
748a9306 9371 exit(LIB$_WRONUMARG);
a0d0e21e
LW
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 */
e0ef6b43 9392 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
c5375c28 9393 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
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 {
fd71b04b 9402 fprintf(stderr,"'|' and '>' may not both be specified on command line");
748a9306 9403 exit(LIB$_INVARGORD);
a0d0e21e 9404 }
fd8cd3a3 9405 pipe_and_fork(aTHX_ cmargv);
a0d0e21e
LW
9406 }
9407
9408 /* Check for input from a pipe (mailbox) */
9409
a5f75d66 9410 if (in == NULL && 1 == isapipe(0))
a0d0e21e
LW
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
bf8d1304 9421 fgetname(stdin, mbxname, 1);
a0d0e21e
LW
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';
748a9306
LW
9430 set_errno(0);
9431 set_vaxc_errno(1);
a0d0e21e
LW
9432 freopen(mbxname, "rb", stdin);
9433 if (errno != 0)
9434 {
fd71b04b 9435 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
748a9306 9436 exit(vaxc$errno);
a0d0e21e
LW
9437 }
9438 }
9439 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9440 {
fd71b04b 9441 fprintf(stderr,"Can't open input file %s as stdin",in);
748a9306 9442 exit(vaxc$errno);
a0d0e21e
LW
9443 }
9444 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9445 {
fd71b04b 9446 fprintf(stderr,"Can't open output file %s as stdout",out);
748a9306 9447 exit(vaxc$errno);
a0d0e21e 9448 }
fd8cd3a3 9449 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
0e06870b 9450
748a9306 9451 if (err != NULL) {
71d7ec5d 9452 if (strcmp(err,"&1") == 0) {
a15cef0c 9453 dup2(fileno(stdout), fileno(stderr));
fd8cd3a3 9454 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
71d7ec5d 9455 } else {
748a9306
LW
9456 FILE *tmperr;
9457 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9458 {
fd71b04b 9459 fprintf(stderr,"Can't open error file %s as stderr",err);
748a9306
LW
9460 exit(vaxc$errno);
9461 }
9462 fclose(tmperr);
a15cef0c 9463 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
748a9306
LW
9464 {
9465 exit(vaxc$errno);
9466 }
fd8cd3a3 9467 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
a0d0e21e 9468 }
71d7ec5d 9469 }
a0d0e21e 9470#ifdef ARGPROC_DEBUG
740ce14c 9471 PerlIO_printf(Perl_debug_log, "Arglist:\n");
a0d0e21e 9472 for (j = 0; j < *ac; ++j)
740ce14c 9473 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
a0d0e21e 9474#endif
b7ae7a0d 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);
a0d0e21e
LW
9478} /* end of getredirection() */
9479/*}}}*/
9480
9481static void add_item(struct list_item **head,
9482 struct list_item **tail,
9483 char *value,
9484 int *count)
9485{
9486 if (*head == 0)
9487 {
e0ef6b43 9488 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9489 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9490 *tail = *head;
9491 }
9492 else {
e0ef6b43 9493 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9494 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9495 *tail = (*tail)->next;
9496 }
9497 (*tail)->value = value;
9498 ++(*count);
9499}
9500
4b19af01 9501static void mp_expand_wild_cards(pTHX_ char *item,
a0d0e21e
LW
9502 struct list_item **head,
9503 struct list_item **tail,
9504 int *count)
9505{
9506int expcount = 0;
748a9306 9507unsigned long int context = 0;
a0d0e21e 9508int isunix = 0;
773da73d 9509int item_len = 0;
a0d0e21e
LW
9510char *had_version;
9511char *had_device;
9512int had_directory;
f675dbe5 9513char *devdir,*cp;
a480973c 9514char *vmsspec;
a0d0e21e 9515$DESCRIPTOR(filespec, "");
748a9306 9516$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
a0d0e21e 9517$DESCRIPTOR(resultspec, "");
a480973c
JM
9518unsigned long int lff_flags = 0;
9519int sts;
dca5a913 9520int rms_sts;
a480973c
JM
9521
9522#ifdef VMS_LONGNAME_SUPPORT
9523 lff_flags = LIB$M_FIL_LONG_NAMES;
9524#endif
a0d0e21e 9525
f675dbe5
CB
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))
a0d0e21e
LW
9531 {
9532 add_item(head, tail, item, count);
9533 return;
9534 }
773da73d
JH
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 }
a0d0e21e
LW
9549 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9550 resultspec.dsc$b_class = DSC$K_CLASS_D;
9551 resultspec.dsc$a_pointer = NULL;
c5375c28
JM
9552 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9553 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
748a9306 9554 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
df278665 9555 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
a0d0e21e
LW
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
a480973c
JM
9569 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9570 (&filespec, &resultspec, &context,
dca5a913 9571 &defaultspec, 0, &rms_sts, &lff_flags)))
a0d0e21e
LW
9572 {
9573 char *string;
9574 char *c;
9575
c5375c28
JM
9576 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9577 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9578 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9579 string[resultspec.dsc$w_length] = '\0';
9580 if (NULL == had_version)
f7ddb74a 9581 *(strrchr(string, ';')) = '\0';
a0d0e21e
LW
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 */
f7ddb74a
JM
9592 if (!decc_efs_case_preserve) {
9593 for (c = string; *c; ++c)
a0d0e21e
LW
9594 if (isupper(*c))
9595 *c = tolower(*c);
f7ddb74a 9596 }
f86702cc 9597 if (isunix) trim_unixpath(string,item,1);
a0d0e21e
LW
9598 add_item(head, tail, string, count);
9599 ++expcount;
a480973c 9600 }
367e4b85 9601 PerlMem_free(vmsspec);
c07a80fd 9602 if (sts != RMS$_NMF)
9603 {
9604 set_vaxc_errno(sts);
9605 switch (sts)
9606 {
f282b18d 9607 case RMS$_FNF: case RMS$_DNF:
c07a80fd 9608 set_errno(ENOENT); break;
f282b18d
CB
9609 case RMS$_DIR:
9610 set_errno(ENOTDIR); break;
c07a80fd 9611 case RMS$_DEV:
9612 set_errno(ENODEV); break;
f282b18d 9613 case RMS$_FNM: case RMS$_SYN:
c07a80fd 9614 set_errno(EINVAL); break;
9615 case RMS$_PRV:
9616 set_errno(EACCES); break;
9617 default:
b7ae7a0d 9618 _ckvmssts_noperl(sts);
c07a80fd 9619 }
9620 }
a0d0e21e
LW
9621 if (expcount == 0)
9622 add_item(head, tail, item, count);
b7ae7a0d 9623 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9624 _ckvmssts_noperl(lib$find_file_end(&context));
a0d0e21e
LW
9625}
9626
9627static int child_st[2];/* Event Flag set when child process completes */
9628
748a9306 9629static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
a0d0e21e 9630
748a9306 9631static unsigned long int exit_handler(int *status)
a0d0e21e
LW
9632{
9633short iosb[4];
9634
9635 if (0 == child_st[0])
9636 {
9637#ifdef ARGPROC_DEBUG
740ce14c 9638 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
a0d0e21e
LW
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
9650static void sig_child(int chan)
9651{
9652#ifdef ARGPROC_DEBUG
740ce14c 9653 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
a0d0e21e
LW
9654#endif
9655 if (child_st[0] == 0)
9656 child_st[0] = 1;
9657}
9658
748a9306 9659static struct exit_control_block exit_block =
a0d0e21e
LW
9660 {
9661 0,
9662 exit_handler,
9663 1,
9664 &exit_block.exit_status,
9665 0
9666 };
9667
ff7adb52
CL
9668static void
9669pipe_and_fork(pTHX_ char **cmargv)
a0d0e21e 9670{
ff7adb52 9671 PerlIO *fp;
218fdd94 9672 struct dsc$descriptor_s *vmscmd;
ff7adb52
CL
9673 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9674 int sts, j, l, ismcr, quote, tquote = 0;
9675
218fdd94
CL
9676 sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9677 vms_execfree(vmscmd);
ff7adb52
CL
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 }
988c775c 9701 }
ff7adb52
CL
9702 } else {
9703 if ((quote||tquote) && *q == '"') {
9704 *p++ = '"';
9705 l++;
988c775c 9706 }
ff7adb52
CL
9707 *p++ = *q++;
9708 l++;
9709 }
9710 }
9711 *p = '\0';
a0d0e21e 9712
218fdd94 9713 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
4e205ed6 9714 if (fp == NULL) {
ff7adb52 9715 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
988c775c 9716 }
a0d0e21e
LW
9717}
9718
8df869cb 9719static int background_process(pTHX_ int argc, char **argv)
a0d0e21e 9720{
a480973c 9721char command[MAX_DCL_SYMBOL + 1] = "$";
a0d0e21e
LW
9722$DESCRIPTOR(value, "");
9723static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9724static $DESCRIPTOR(null, "NLA0:");
9725static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9726char pidstring[80];
9727$DESCRIPTOR(pidstr, "");
9728int pid;
748a9306 9729unsigned long int flags = 17, one = 1, retsts;
a480973c 9730int len;
a0d0e21e
LW
9731
9732 strcat(command, argv[0]);
a480973c
JM
9733 len = strlen(command);
9734 while (--argc && (len < MAX_DCL_SYMBOL))
a0d0e21e
LW
9735 {
9736 strcat(command, " \"");
9737 strcat(command, *(++argv));
9738 strcat(command, "\"");
a480973c 9739 len = strlen(command);
a0d0e21e
LW
9740 }
9741 value.dsc$a_pointer = command;
9742 value.dsc$w_length = strlen(value.dsc$a_pointer);
b7ae7a0d 9743 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
748a9306
LW
9744 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9745 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
b7ae7a0d 9746 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
748a9306
LW
9747 }
9748 else {
b7ae7a0d 9749 _ckvmssts_noperl(retsts);
748a9306 9750 }
a0d0e21e 9751#ifdef ARGPROC_DEBUG
740ce14c 9752 PerlIO_printf(Perl_debug_log, "%s\n", command);
a0d0e21e
LW
9753#endif
9754 sprintf(pidstring, "%08X", pid);
740ce14c 9755 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
a0d0e21e
LW
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
84902520
TB
9764
9765/* OS-specific initialization at image activation (not thread startup) */
61bb5906
CB
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
a480973c 9773
e0ef6b43
CB
9774/* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9775
84902520
TB
9776/*{{{void vms_image_init(int *, char ***)*/
9777void
9778vms_image_init(int *argcp, char ***argvp)
9779{
b53f3677 9780 int status;
f675dbe5
CB
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;
61bb5906
CB
9784 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9785 unsigned short int dummy, rlen;
f675dbe5 9786 struct dsc$descriptor_s **tabvec;
fd8cd3a3
DS
9787#if defined(PERL_IMPLICIT_CONTEXT)
9788 pTHX = NULL;
9789#endif
61bb5906
CB
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} };
84902520 9794
2e34cc90 9795#ifdef KILL_BY_SIGPRC
f7ddb74a 9796 Perl_csighandler_init();
2e34cc90
CL
9797#endif
9798
778e045f 9799#if __CRTL_VER >= 70300000 && !defined(__VAX)
b53f3677
JM
9800 /* This was moved from the pre-image init handler because on threaded */
9801 /* Perl it was always returning 0 for the default value. */
98c7875d 9802 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
b53f3677
JM
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);
98c7875d
CB
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 */
b53f3677
JM
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 {
98c7875d 9823 /* Nothing has asked for it explicitly, so use our own default. */
b53f3677
JM
9824 decc_disable_posix_root = 1;
9825 decc$feature_set_value(s, 1, 1);
9826 }
9827 }
9828 }
778e045f 9829#endif
b53f3677 9830
fd8cd3a3
DS
9831 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9832 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
9833 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9834 if (iprv[i]) { /* Running image installed with privs? */
fd8cd3a3 9835 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
f675dbe5 9836 will_taint = TRUE;
84902520
TB
9837 break;
9838 }
9839 }
61bb5906 9840 /* Rights identifiers might trigger tainting as well. */
f675dbe5 9841 if (!will_taint && (rlen || rsz)) {
61bb5906
CB
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.
22d4bb9c
CB
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?
61bb5906 9849 */
22d4bb9c
CB
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 }
e0ef6b43
CB
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));
c5375c28 9859 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
61bb5906 9860 jpilist[1].buflen = rsz * sizeof(unsigned long int);
fd8cd3a3
DS
9861 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9862 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
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) {
f675dbe5 9870 will_taint = TRUE;
61bb5906
CB
9871 break;
9872 }
9873 }
367e4b85 9874 if (mask != rlst) PerlMem_free(mask);
61bb5906 9875 }
f7ddb74a
JM
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
61bb5906 9903 /* We need to use this hack to tell Perl it should run with tainting,
6b88bc9c 9904 * since its tainting flag may be part of the PL_curinterp struct, which
61bb5906
CB
9905 * hasn't been allocated when vms_image_init() is called.
9906 */
f675dbe5 9907 if (will_taint) {
ec618cdf
CB
9908 char **newargv, **oldargv;
9909 oldargv = *argvp;
e0ef6b43 9910 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
c5375c28 9911 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf 9912 newargv[0] = oldargv[0];
c5375c28
JM
9913 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9914 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf
CB
9915 strcpy(newargv[1], "-T");
9916 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9917 (*argcp)++;
9918 newargv[*argcp] = NULL;
61bb5906
CB
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 */
ec618cdf 9922 *argvp = newargv;
61bb5906 9923 }
f675dbe5
CB
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++) {
c5375c28
JM
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 }
f675dbe5
CB
9946 else if (tabidx >= tabct) {
9947 tabct += 8;
e0ef6b43 9948 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
c5375c28 9949 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5 9950 }
e0ef6b43 9951 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
c5375c28 9952 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5
CB
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;
fd8cd3a3 9957 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
f675dbe5
CB
9958 }
9959 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9960
84902520 9961 getredirection(argcp,argvp);
3bc25146
CB
9962#if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9963 {
9964# include <reentrancy.h>
f7ddb74a 9965 decc$set_reentrancy(C$C_MULTITHREAD);
3bc25146
CB
9966 }
9967#endif
84902520
TB
9968 return;
9969}
9970/*}}}*/
9971
9972
a0d0e21e
LW
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 *
a3e9d8c9 9979 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
f86702cc 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.
a3e9d8c9 9984 *
9985 * Returns !=0 on success, with trimmed filespec replacing contents of
9986 * fspec, and 0 on failure, with contents of fpsec unchanged.
a0d0e21e 9987 */
f86702cc 9988/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
a0d0e21e 9989int
2fbb330f 9990Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
a0d0e21e 9991{
a480973c 9992 char *unixified, *unixwild,
f86702cc 9993 *template, *base, *end, *cp1, *cp2;
9994 register int tmplen, reslen = 0, dirs = 0;
a0d0e21e 9995
a3e9d8c9 9996 if (!wildspec || !fspec) return 0;
ebd4d70b
JM
9997
9998 unixwild = PerlMem_malloc(VMS_MAXRSS);
9999 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 10000 template = unixwild;
a3e9d8c9 10001 if (strpbrk(wildspec,"]>:") != NULL) {
0e5ce2c7 10002 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
367e4b85 10003 PerlMem_free(unixwild);
a480973c
JM
10004 return 0;
10005 }
a3e9d8c9 10006 }
2fbb330f 10007 else {
a480973c
JM
10008 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
10009 unixwild[VMS_MAXRSS-1] = 0;
2fbb330f 10010 }
c5375c28 10011 unixified = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 10012 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e 10013 if (strpbrk(fspec,"]>:") != NULL) {
0e5ce2c7 10014 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
367e4b85
JM
10015 PerlMem_free(unixwild);
10016 PerlMem_free(unixified);
a480973c
JM
10017 return 0;
10018 }
a0d0e21e 10019 else base = unixified;
a3e9d8c9 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);
a0d0e21e
LW
10023 }
10024 else base = fspec;
a3e9d8c9 10025
10026 /* No prefix or absolute path on wildcard, so nothing to remove */
10027 if (!*template || *template == '/') {
367e4b85 10028 PerlMem_free(unixwild);
a480973c 10029 if (base == fspec) {
367e4b85 10030 PerlMem_free(unixified);
a480973c
JM
10031 return 1;
10032 }
a3e9d8c9 10033 tmplen = strlen(unixified);
a480973c 10034 if (tmplen > reslen) {
367e4b85 10035 PerlMem_free(unixified);
a480973c
JM
10036 return 0; /* not enough space */
10037 }
a3e9d8c9 10038 /* Copy unixified resultant, including trailing NUL */
10039 memmove(fspec,unixified,tmplen+1);
367e4b85 10040 PerlMem_free(unixified);
a3e9d8c9 10041 return 1;
10042 }
a0d0e21e 10043
f86702cc 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);
367e4b85
JM
10051 PerlMem_free(unixified);
10052 PerlMem_free(unixwild);
a3e9d8c9 10053 return 1;
10054 }
f86702cc 10055 else {
a480973c 10056 char *tpl, *lcres;
f86702cc 10057 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
10058 int ells = 1, totells, segdirs, match;
a480973c 10059 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
f86702cc 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++;
367e4b85 10065 tpl = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 10066 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f86702cc 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 */
a480973c 10076 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
367e4b85
JM
10077 PerlMem_free(tpl);
10078 PerlMem_free(unixified);
10079 PerlMem_free(unixwild);
a480973c
JM
10080 return 0;
10081 }
f7ddb74a
JM
10082 if (!decc_efs_case_preserve) {
10083 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10084 if (_tolower(*cp1) != _tolower(*cp2)) break;
10085 }
f86702cc 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) {
18a3d61e 10089 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
10090 PerlMem_free(tpl);
10091 PerlMem_free(unixified);
10092 PerlMem_free(unixwild);
f86702cc 10093 return 1;
a3e9d8c9 10094 }
a3e9d8c9 10095 }
f86702cc 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; }
a3e9d8c9 10100 }
c5375c28 10101 lcres = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 10102 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
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') {
367e4b85
JM
10113 PerlMem_free(tpl);
10114 PerlMem_free(unixified);
10115 PerlMem_free(unixwild);
c5375c28 10116 PerlMem_free(lcres);
a480973c 10117 return 0; /* Path too long. */
f7ddb74a 10118 }
f86702cc 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 }
a480973c 10131 wilddsc.dsc$a_pointer = tpl;
f86702cc 10132 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10133 nextell = cp1;
10134 for (segdirs = 0, cp2 = tpl;
a480973c 10135 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
f86702cc 10136 cp1++, cp2++) {
10137 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
f7ddb74a
JM
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 }
f86702cc 10146 if (*cp2 == '/') segdirs++;
10147 }
a480973c 10148 if (cp1 != ellipsis - 1) {
367e4b85
JM
10149 PerlMem_free(tpl);
10150 PerlMem_free(unixified);
10151 PerlMem_free(unixwild);
10152 PerlMem_free(lcres);
a480973c
JM
10153 return 0; /* Path too long */
10154 }
f86702cc 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 }
a480973c 10166 if (!match) {
367e4b85
JM
10167 PerlMem_free(tpl);
10168 PerlMem_free(unixified);
10169 PerlMem_free(unixwild);
10170 PerlMem_free(lcres);
a480973c
JM
10171 return 0; /* Can't find prefix ??? */
10172 }
f86702cc 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
a480973c 10184 if (getcwd(def, sizeof def,0) == NULL) {
827f156d
JM
10185 PerlMem_free(unixified);
10186 PerlMem_free(unixwild);
10187 PerlMem_free(lcres);
10188 PerlMem_free(tpl);
a480973c
JM
10189 return 0;
10190 }
f7ddb74a
JM
10191 if (!decc_efs_case_preserve) {
10192 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10193 if (_tolower(*cp1) != _tolower(*cp2)) break;
10194 }
f86702cc 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 == '/') {
18a3d61e 10198 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
10199 PerlMem_free(tpl);
10200 PerlMem_free(unixified);
10201 PerlMem_free(unixwild);
10202 PerlMem_free(lcres);
f86702cc 10203 return 1;
10204 }
10205 /* Nope -- stick with lcfront from above and keep going. */
10206 }
10207 }
18a3d61e 10208 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
367e4b85
JM
10209 PerlMem_free(tpl);
10210 PerlMem_free(unixified);
10211 PerlMem_free(unixwild);
10212 PerlMem_free(lcres);
a3e9d8c9 10213 return 1;
f86702cc 10214 ellipsis = nextell;
a0d0e21e 10215 }
a0d0e21e
LW
10216
10217} /* end of trim_unixpath() */
10218/*}}}*/
10219
a0d0e21e
LW
10220
10221/*
10222 * VMS readdir() routines.
10223 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
a0d0e21e 10224 *
bd3fa61c 10225 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
a0d0e21e
LW
10226 * Minor modifications to original routines.
10227 */
10228
a9852f7c
CB
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
a0d0e21e
LW
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) */
ddcbaa1c 10248DIR *
b8ffc8df 10249Perl_opendir(pTHX_ const char *name)
a0d0e21e 10250{
ddcbaa1c 10251 DIR *dd;
657054d4 10252 char *dir;
61bb5906 10253 Stat_t sb;
657054d4
JM
10254
10255 Newx(dir, VMS_MAXRSS, char);
4846f1d7 10256 if (int_tovmspath(name, dir, NULL) == NULL) {
657054d4 10257 Safefree(dir);
61bb5906 10258 return NULL;
a0d0e21e 10259 }
ada67d10
CB
10260 /* Check access before stat; otherwise stat does not
10261 * accurately report whether it's a directory.
10262 */
a1887106 10263 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
fac786e7 10264 /* cando_by_name has already set errno */
657054d4 10265 Safefree(dir);
ada67d10
CB
10266 return NULL;
10267 }
61bb5906
CB
10268 if (flex_stat(dir,&sb) == -1) return NULL;
10269 if (!S_ISDIR(sb.st_mode)) {
657054d4 10270 Safefree(dir);
61bb5906
CB
10271 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10272 return NULL;
10273 }
61bb5906 10274 /* Get memory for the handle, and the pattern. */
ddcbaa1c 10275 Newx(dd,1,DIR);
a02a5408 10276 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
a0d0e21e
LW
10277
10278 /* Fill in the fields; mainly playing with the descriptor. */
f7ddb74a 10279 sprintf(dd->pattern, "%s*.*",dir);
657054d4 10280 Safefree(dir);
a0d0e21e
LW
10281 dd->context = 0;
10282 dd->count = 0;
657054d4 10283 dd->flags = 0;
a096370a
CB
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;
a0d0e21e
LW
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;
3bc25146 10294#if defined(USE_ITHREADS)
a02a5408 10295 Newx(dd->mutex,1,perl_mutex);
a9852f7c
CB
10296 MUTEX_INIT( (perl_mutex *) dd->mutex );
10297#else
10298 dd->mutex = NULL;
10299#endif
a0d0e21e
LW
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)*/
10309void
ddcbaa1c 10310vmsreaddirversions(DIR *dd, int flag)
a0d0e21e 10311{
657054d4
JM
10312 if (flag)
10313 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10314 else
10315 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
a0d0e21e
LW
10316}
10317/*}}}*/
10318
10319/*
10320 * Free up an opened directory.
10321 */
10322/*{{{ void closedir(DIR *dd)*/
10323void
ddcbaa1c 10324Perl_closedir(DIR *dd)
a0d0e21e 10325{
f7ddb74a
JM
10326 int sts;
10327
10328 sts = lib$find_file_end(&dd->context);
a0d0e21e 10329 Safefree(dd->pattern);
3bc25146 10330#if defined(USE_ITHREADS)
a9852f7c
CB
10331 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10332 Safefree(dd->mutex);
10333#endif
f7ddb74a 10334 Safefree(dd);
a0d0e21e
LW
10335}
10336/*}}}*/
10337
10338/*
10339 * Collect all the version numbers for the current file.
10340 */
10341static void
ddcbaa1c 10342collectversions(pTHX_ DIR *dd)
a0d0e21e
LW
10343{
10344 struct dsc$descriptor_s pat;
10345 struct dsc$descriptor_s res;
ddcbaa1c 10346 struct dirent *e;
657054d4 10347 char *p, *text, *buff;
a0d0e21e
LW
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);
a02a5408 10356 Newx(text,i + e->d_namlen + 3,char);
f7ddb74a
JM
10357 strcpy(text, dd->pattern);
10358 sprintf(&text[i - 3], "%s;*", e->d_name);
a0d0e21e
LW
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. */
657054d4 10367 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10368 res.dsc$a_pointer = buff;
657054d4 10369 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
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++) {
657054d4
JM
10377 unsigned long rsts;
10378 unsigned long flags = 0;
10379
10380#ifdef VMS_LONGNAME_SUPPORT
988c775c 10381 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10382#endif
10383 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
a0d0e21e 10384 if (tmpsts == RMS$_NMF || context == 0) break;
748a9306 10385 _ckvmssts(tmpsts);
657054d4 10386 buff[VMS_MAXRSS - 1] = '\0';
748a9306 10387 if ((p = strchr(buff, ';')))
a0d0e21e
LW
10388 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10389 else
10390 e->vms_versions[e->vms_verscount] = -1;
10391 }
10392
748a9306 10393 _ckvmssts(lib$find_file_end(&context));
a0d0e21e 10394 Safefree(text);
657054d4 10395 Safefree(buff);
a0d0e21e
LW
10396
10397} /* end of collectversions() */
10398
10399/*
10400 * Read the next entry from the directory.
10401 */
10402/*{{{ struct dirent *readdir(DIR *dd)*/
ddcbaa1c
CB
10403struct dirent *
10404Perl_readdir(pTHX_ DIR *dd)
a0d0e21e
LW
10405{
10406 struct dsc$descriptor_s res;
657054d4 10407 char *p, *buff;
a0d0e21e 10408 unsigned long int tmpsts;
657054d4
JM
10409 unsigned long rsts;
10410 unsigned long flags = 0;
dca5a913 10411 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
657054d4 10412 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e
LW
10413
10414 /* Set up result descriptor, and get next file. */
657054d4 10415 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10416 res.dsc$a_pointer = buff;
657054d4 10417 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10418 res.dsc$b_dtype = DSC$K_DTYPE_T;
10419 res.dsc$b_class = DSC$K_CLASS_S;
657054d4
JM
10420
10421#ifdef VMS_LONGNAME_SUPPORT
988c775c 10422 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10423#endif
10424
10425 tmpsts = lib$find_file
10426 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
4633a7c4
LW
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:
c07a80fd 10432 set_errno(EACCES); break;
4633a7c4 10433 case RMS$_DEV:
c07a80fd 10434 set_errno(ENODEV); break;
4633a7c4 10435 case RMS$_DIR:
f282b18d
CB
10436 set_errno(ENOTDIR); break;
10437 case RMS$_FNF: case RMS$_DNF:
c07a80fd 10438 set_errno(ENOENT); break;
4633a7c4
LW
10439 default:
10440 set_errno(EVMSERR);
10441 }
657054d4 10442 Safefree(buff);
4633a7c4
LW
10443 return NULL;
10444 }
10445 dd->count++;
a0d0e21e 10446 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
c43a0d1c
CB
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';
f7ddb74a 10451 if (!decc_efs_case_preserve) {
f7ddb74a 10452 for (p = buff; *p; p++) *p = _tolower(*p);
f7ddb74a 10453 }
a0d0e21e
LW
10454
10455 /* Skip any directory component and just copy the name. */
657054d4 10456 sts = vms_split_path
360732b5 10457 (buff,
657054d4
JM
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
0dddfaca
JM
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) {
f785e3a1
JM
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;
0dddfaca
JM
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 }
dca5a913
JM
10493 }
10494
657054d4
JM
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);
a0d0e21e 10498
657054d4
JM
10499 /* Convert the filename to UNIX format if needed */
10500 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10501
10502 /* Translate the encoded characters. */
38a44b82 10503 /* Fixme: Unicode handling could result in embedded 0 characters */
657054d4
JM
10504 if (strchr(dd->entry.d_name, '^') != NULL) {
10505 char new_name[256];
10506 char * q;
657054d4
JM
10507 p = dd->entry.d_name;
10508 q = new_name;
10509 while (*p != 0) {
f617045b
CB
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;
dca5a913 10514 /* fix-me */
f617045b 10515 /* if outchars_added > 1, then this is a wide file specification */
dca5a913 10516 /* Wide file specifications need to be passed in Perl */
38a44b82 10517 /* counted strings apparently with a Unicode flag */
657054d4
JM
10518 }
10519 *q = 0;
10520 strcpy(dd->entry.d_name, new_name);
f617045b 10521 dd->entry.d_namlen = strlen(dd->entry.d_name);
657054d4 10522 }
657054d4 10523 }
a0d0e21e 10524
a0d0e21e 10525 dd->entry.vms_verscount = 0;
657054d4
JM
10526 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10527 Safefree(buff);
a0d0e21e
LW
10528 return &dd->entry;
10529
10530} /* end of readdir() */
10531/*}}}*/
10532
10533/*
a9852f7c
CB
10534 * Read the next entry from the directory -- thread-safe version.
10535 */
10536/*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10537int
ddcbaa1c 10538Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
a9852f7c
CB
10539{
10540 int retval;
10541
10542 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10543
7ded3206 10544 entry = readdir(dd);
a9852f7c
CB
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/*
a0d0e21e
LW
10556 * Return something that can be used in a seekdir later.
10557 */
10558/*{{{ long telldir(DIR *dd)*/
10559long
ddcbaa1c 10560Perl_telldir(DIR *dd)
a0d0e21e
LW
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)*/
10570void
ddcbaa1c 10571Perl_seekdir(pTHX_ DIR *dd, long count)
a0d0e21e 10572{
657054d4 10573 int old_flags;
a0d0e21e
LW
10574
10575 /* If we haven't done anything yet... */
10576 if (dd->count == 0)
10577 return;
10578
10579 /* Remember some state, and clear it. */
657054d4
JM
10580 old_flags = dd->flags;
10581 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
748a9306 10582 _ckvmssts(lib$find_file_end(&dd->context));
a0d0e21e
LW
10583 dd->context = 0;
10584
10585 /* The increment is in readdir(). */
10586 for (dd->count = 0; dd->count < count; )
f7ddb74a 10587 readdir(dd);
a0d0e21e 10588
657054d4 10589 dd->flags = old_flags;
a0d0e21e
LW
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
a6d05634 10601 * call out the regular perl routines in doio.c which do an
a0d0e21e
LW
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
eed5d6a1
CB
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),
a6d05634 10614 * the command string is handed off to DCL directly. Otherwise,
a0d0e21e
LW
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
3eeba6fb 10617 * the process defaults for device, directory, etc., and if found, the resultant
a0d0e21e 10618 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
3eeba6fb 10619 * the command string as parameters. This is perhaps a bit complicated,
a0d0e21e
LW
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
10624static int vfork_called;
10625
10626/*{{{int my_vfork()*/
10627int
10628my_vfork()
10629{
748a9306 10630 vfork_called++;
a0d0e21e
LW
10631 return vfork();
10632}
10633/*}}}*/
10634
4633a7c4 10635
a0d0e21e 10636static void
218fdd94
CL
10637vms_execfree(struct dsc$descriptor_s *vmscmd)
10638{
10639 if (vmscmd) {
10640 if (vmscmd->dsc$a_pointer) {
c5375c28 10641 PerlMem_free(vmscmd->dsc$a_pointer);
218fdd94 10642 }
c5375c28 10643 PerlMem_free(vmscmd);
4633a7c4
LW
10644 }
10645}
10646
10647static char *
fd8cd3a3 10648setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
a0d0e21e 10649{
4e205ed6 10650 char *junk, *tmps = NULL;
a0d0e21e
LW
10651 register size_t cmdlen = 0;
10652 size_t rlen;
10653 register SV **idx;
2d8e6c8d 10654 STRLEN n_a;
a0d0e21e
LW
10655
10656 idx = mark;
4633a7c4
LW
10657 if (really) {
10658 tmps = SvPV(really,rlen);
10659 if (*tmps) {
10660 cmdlen += rlen + 1;
10661 idx++;
10662 }
a0d0e21e
LW
10663 }
10664
10665 for (idx++; idx <= sp; idx++) {
10666 if (*idx) {
10667 junk = SvPVx(*idx,rlen);
10668 cmdlen += rlen ? rlen + 1 : 0;
10669 }
10670 }
c5375c28 10671 Newx(PL_Cmd, cmdlen+1, char);
a0d0e21e 10672
4633a7c4 10673 if (tmps && *tmps) {
6b88bc9c 10674 strcpy(PL_Cmd,tmps);
a0d0e21e
LW
10675 mark++;
10676 }
6b88bc9c 10677 else *PL_Cmd = '\0';
a0d0e21e
LW
10678 while (++mark <= sp) {
10679 if (*mark) {
3eeba6fb
CB
10680 char *s = SvPVx(*mark,n_a);
10681 if (!*s) continue;
10682 if (*PL_Cmd) strcat(PL_Cmd," ");
10683 strcat(PL_Cmd,s);
a0d0e21e
LW
10684 }
10685 }
6b88bc9c 10686 return PL_Cmd;
a0d0e21e
LW
10687
10688} /* end of setup_argstr() */
10689
4633a7c4 10690
a0d0e21e 10691static unsigned long int
2fbb330f 10692setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
218fdd94 10693 struct dsc$descriptor_s **pvmscmd)
a0d0e21e 10694{
e919cd19
JM
10695 char * vmsspec;
10696 char * resspec;
e886094b
JM
10697 char image_name[NAM$C_MAXRSS+1];
10698 char image_argv[NAM$C_MAXRSS+1];
a0d0e21e 10699 $DESCRIPTOR(defdsc,".EXE");
8012a33e 10700 $DESCRIPTOR(defdsc2,".");
e919cd19 10701 struct dsc$descriptor_s resdsc;
218fdd94 10702 struct dsc$descriptor_s *vmscmd;
a0d0e21e 10703 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3eeba6fb 10704 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
aa779de1 10705 register char *s, *rest, *cp, *wordbreak;
2fbb330f
JM
10706 char * cmd;
10707 int cmdlen;
aa779de1 10708 register int isdcl;
a0d0e21e 10709
c5375c28 10710 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
ebd4d70b 10711 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 10712
e919cd19
JM
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
2fbb330f
JM
10722 /* Make a copy for modification */
10723 cmdlen = strlen(incmd);
c5375c28 10724 cmd = PerlMem_malloc(cmdlen+1);
ebd4d70b 10725 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f
JM
10726 strncpy(cmd, incmd, cmdlen);
10727 cmd[cmdlen] = 0;
e886094b
JM
10728 image_name[0] = 0;
10729 image_argv[0] = 0;
2fbb330f 10730
e919cd19
JM
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
218fdd94
CL
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
ff7adb52
CL
10742 if (suggest_quote) *suggest_quote = 0;
10743
2fbb330f 10744 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
c5375c28 10745 PerlMem_free(cmd);
e919cd19
JM
10746 PerlMem_free(vmsspec);
10747 PerlMem_free(resspec);
a2669cfc 10748 return CLI$_BUFOVF; /* continuation lines currently unsupported */
2fbb330f
JM
10749 }
10750
a0d0e21e 10751 s = cmd;
2fbb330f 10752
a0d0e21e 10753 while (*s && isspace(*s)) s++;
aa779de1
CB
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;
e919cd19 10763 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
aa779de1
CB
10764 rest++, cp2++) *cp2 = *rest;
10765 *cp2 = '\0';
df278665 10766 if (int_tovmsspec(resspec, cp, 0, NULL)) {
aa779de1 10767 s = vmsspec;
cfbf46cd
JM
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
aa779de1
CB
10781 if (*rest) {
10782 for (cp2 = vmsspec + strlen(vmsspec);
e919cd19 10783 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
aa779de1
CB
10784 rest++, cp2++) *cp2 = *rest;
10785 *cp2 = '\0';
a0d0e21e
LW
10786 }
10787 }
10788 }
aa779de1
CB
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 */
ff7adb52
CL
10796 if (*s == '@') {
10797 isdcl = 1;
10798 if (suggest_quote) *suggest_quote = 1;
10799 } else {
aa779de1
CB
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
3eeba6fb 10808 if (!isdcl) {
dca5a913 10809 int rsts;
aa779de1
CB
10810 imgdsc.dsc$a_pointer = s;
10811 imgdsc.dsc$w_length = wordbreak - s;
dca5a913 10812 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8012a33e 10813 if (!(retsts&1)) {
ebd4d70b 10814 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10815 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f 10816 if (!(retsts & 1) && *s == '$') {
ebd4d70b 10817 _ckvmssts_noperl(lib$find_file_end(&cxt));
2497a41f 10818 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
dca5a913 10819 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
2497a41f 10820 if (!(retsts&1)) {
ebd4d70b 10821 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10822 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f
JM
10823 }
10824 }
aa779de1 10825 }
ebd4d70b 10826 _ckvmssts_noperl(lib$find_file_end(&cxt));
8012a33e 10827
aa779de1 10828 if (retsts & 1) {
8012a33e 10829 FILE *fp;
a0d0e21e
LW
10830 s = resspec;
10831 while (*s && !isspace(*s)) s++;
10832 *s = '\0';
8012a33e
CB
10833
10834 /* check that it's really not DCL with no file extension */
e886094b 10835 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8012a33e 10836 if (fp) {
2497a41f
JM
10837 char b[256] = {0,0,0,0};
10838 read(fileno(fp), b, 256);
8012a33e 10839 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
2497a41f 10840 if (isdcl) {
e886094b
JM
10841 int shebang_len;
10842
2497a41f 10843 /* Check for script */
e886094b
JM
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
2497a41f 10892 /* It will need to be converted to VMS format and validated */
e886094b
JM
10893 if (tmpspec[0] != '\0') {
10894 char * iname;
10895
10896 /* Try to find the exact program requested to be run */
10897 /*---------------------------------------------------*/
6fb6c614
JM
10898 iname = int_rmsexpand
10899 (tmpspec, image_name, ".exe",
360732b5 10900 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10901 if (iname != NULL) {
a1887106
JM
10902 if (cando_by_name_int
10903 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10904 /* MCR prefix needed */
10905 isdcl = 0;
10906 }
10907 else {
10908 /* Try again with a null type */
10909 /*----------------------------*/
6fb6c614
JM
10910 iname = int_rmsexpand
10911 (tmpspec, image_name, ".",
360732b5 10912 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10913 if (iname != NULL) {
a1887106
JM
10914 if (cando_by_name_int
10915 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
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 }
2497a41f
JM
10940 }
10941 }
8012a33e
CB
10942 fclose(fp);
10943 }
e919cd19
JM
10944 if (check_img && isdcl) {
10945 PerlMem_free(cmd);
10946 PerlMem_free(resspec);
10947 PerlMem_free(vmsspec);
10948 return RMS$_FNF;
10949 }
8012a33e 10950
3eeba6fb 10951 if (cando_by_name(S_IXUSR,0,resspec)) {
c5375c28 10952 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
ebd4d70b 10953 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8012a33e 10954 if (!isdcl) {
218fdd94 10955 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
e886094b
JM
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, " ");
8012a33e 10963 } else {
218fdd94 10964 strcpy(vmscmd->dsc$a_pointer,"@");
8012a33e 10965 }
e886094b
JM
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 }
218fdd94 10991 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
c5375c28 10992 PerlMem_free(cmd);
e919cd19
JM
10993 PerlMem_free(vmsspec);
10994 PerlMem_free(resspec);
218fdd94 10995 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
3eeba6fb 10996 }
c5375c28
JM
10997 else
10998 retsts = RMS$_PRV;
a0d0e21e
LW
10999 }
11000 }
3eeba6fb 11001 /* It's either a DCL command or we couldn't find a suitable image */
218fdd94 11002 vmscmd->dsc$w_length = strlen(cmd);
ff7adb52 11003
b011c7bd 11004 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
c5375c28 11005 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
b011c7bd 11006 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
c5375c28
JM
11007
11008 PerlMem_free(cmd);
e919cd19
JM
11009 PerlMem_free(resspec);
11010 PerlMem_free(vmsspec);
2fbb330f 11011
ff7adb52
CL
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
218fdd94 11019 iss = lib$get_symbol(vmscmd,&eqvdsc);
ff7adb52
CL
11020 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
11021 }
3eeba6fb
CB
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;
ebd4d70b 11027 else { _ckvmssts_noperl(retsts); }
3eeba6fb 11028 }
a0d0e21e 11029
218fdd94 11030 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
a3e9d8c9 11031
a0d0e21e
LW
11032} /* end of setup_cmddsc() */
11033
a3e9d8c9 11034
a0d0e21e
LW
11035/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
11036bool
fd8cd3a3 11037Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
a0d0e21e 11038{
c5375c28
JM
11039bool exec_sts;
11040char * cmd;
11041
a0d0e21e
LW
11042 if (sp > mark) {
11043 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
11044 vfork_called--;
11045 if (vfork_called < 0) {
5c84aa53 11046 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
11047 vfork_called = 0;
11048 }
11049 else return do_aexec(really,mark,sp);
a0d0e21e 11050 }
4633a7c4 11051 /* no vfork - act VMSish */
c5375c28
JM
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;
a0d0e21e
LW
11056 }
11057
11058 return FALSE;
11059} /* end of vms_do_aexec() */
11060/*}}}*/
11061
11062/* {{{bool vms_do_exec(char *cmd) */
11063bool
2fbb330f 11064Perl_vms_do_exec(pTHX_ const char *cmd)
a0d0e21e 11065{
218fdd94 11066 struct dsc$descriptor_s *vmscmd;
a0d0e21e
LW
11067
11068 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
11069 vfork_called--;
11070 if (vfork_called < 0) {
5c84aa53 11071 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
11072 vfork_called = 0;
11073 }
11074 else return do_exec(cmd);
a0d0e21e 11075 }
748a9306
LW
11076
11077 { /* no vfork - act VMSish */
748a9306 11078 unsigned long int retsts;
a0d0e21e 11079
1e422769 11080 TAINT_ENV();
11081 TAINT_PROPER("exec");
218fdd94
CL
11082 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11083 retsts = lib$do_command(vmscmd);
a0d0e21e 11084
09b7f37c 11085 switch (retsts) {
f282b18d 11086 case RMS$_FNF: case RMS$_DNF:
09b7f37c 11087 set_errno(ENOENT); break;
f282b18d 11088 case RMS$_DIR:
09b7f37c 11089 set_errno(ENOTDIR); break;
f282b18d
CB
11090 case RMS$_DEV:
11091 set_errno(ENODEV); break;
09b7f37c
CB
11092 case RMS$_PRV:
11093 set_errno(EACCES); break;
11094 case RMS$_SYN:
11095 set_errno(EINVAL); break;
a2669cfc 11096 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
09b7f37c
CB
11097 set_errno(E2BIG); break;
11098 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 11099 _ckvmssts_noperl(retsts); /* fall through */
09b7f37c
CB
11100 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11101 set_errno(EVMSERR);
11102 }
748a9306 11103 set_vaxc_errno(retsts);
3eeba6fb 11104 if (ckWARN(WARN_EXEC)) {
f98bc0c6 11105 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
218fdd94 11106 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
3eeba6fb 11107 }
218fdd94 11108 vms_execfree(vmscmd);
a0d0e21e
LW
11109 }
11110
11111 return FALSE;
11112
11113} /* end of vms_do_exec() */
11114/*}}}*/
11115
9ec7171b 11116int do_spawn2(pTHX_ const char *, int);
a0d0e21e 11117
9ec7171b
CB
11118int
11119Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
a0d0e21e 11120{
c5375c28
JM
11121unsigned long int sts;
11122char * cmd;
eed5d6a1 11123int flags = 0;
a0d0e21e 11124
c5375c28 11125 if (sp > mark) {
eed5d6a1
CB
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 */
9ec7171b 11132 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
eed5d6a1 11133 ++mark;
9ec7171b 11134 flags = SvIVx(*mark);
eed5d6a1
CB
11135 }
11136
11137 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11138 flags = CLI$M_NOWAIT;
11139 else
11140 flags = 0;
11141
9ec7171b 11142 cmd = setup_argstr(aTHX_ really, mark, sp);
eed5d6a1 11143 sts = do_spawn2(aTHX_ cmd, flags);
c5375c28
JM
11144 /* pp_sys will clean up cmd */
11145 return sts;
11146 }
a0d0e21e
LW
11147 return SS$_ABORT;
11148} /* end of do_aspawn() */
11149/*}}}*/
11150
eed5d6a1 11151
9ec7171b
CB
11152/* {{{int do_spawn(char* cmd) */
11153int
11154Perl_do_spawn(pTHX_ char* cmd)
a0d0e21e 11155{
7918f24d
NC
11156 PERL_ARGS_ASSERT_DO_SPAWN;
11157
eed5d6a1
CB
11158 return do_spawn2(aTHX_ cmd, 0);
11159}
11160/*}}}*/
11161
9ec7171b
CB
11162/* {{{int do_spawn_nowait(char* cmd) */
11163int
11164Perl_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) */
11173int
eed5d6a1
CB
11174do_spawn2(pTHX_ const char *cmd, int flags)
11175{
209030df 11176 unsigned long int sts, substs;
a0d0e21e 11177
c5375c28
JM
11178 /* The caller of this routine expects to Safefree(PL_Cmd) */
11179 Newx(PL_Cmd,10,char);
11180
1e422769 11181 TAINT_ENV();
11182 TAINT_PROPER("spawn");
748a9306 11183 if (!cmd || !*cmd) {
eed5d6a1 11184 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
c8795d8b
JH
11185 if (!(sts & 1)) {
11186 switch (sts) {
209030df
JH
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 */
ebd4d70b 11200 _ckvmssts_noperl(sts); /* fall through */
209030df
JH
11201 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11202 set_errno(EVMSERR);
c8795d8b
JH
11203 }
11204 set_vaxc_errno(sts);
11205 if (ckWARN(WARN_EXEC)) {
f98bc0c6 11206 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
c8795d8b
JH
11207 Strerror(errno));
11208 }
09b7f37c 11209 }
c8795d8b 11210 sts = substs;
48023aa8
CL
11211 }
11212 else {
eed5d6a1 11213 char mode[3];
2fbb330f 11214 PerlIO * fp;
eed5d6a1
CB
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);
2fbb330f
JM
11221 if (fp != NULL)
11222 my_pclose(fp);
eed5d6a1 11223 /* sts will be the pid in the nowait case */
48023aa8 11224 }
48023aa8 11225 return sts;
eed5d6a1 11226} /* end of do_spawn2() */
a0d0e21e
LW
11227/*}}}*/
11228
bc10a425
CB
11229
11230static 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 */
275feba9
CB
11238/*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11239FILE *my_fdopen(int fd, const char *mode)
bc10a425 11240{
f7ddb74a 11241 FILE *fp = fdopen(fd, mode);
bc10a425
CB
11242
11243 if (fp) {
11244 unsigned int fdoff = fd / sizeof(unsigned int);
2497a41f 11245 Stat_t sbuf; /* native stat; we don't need flex_stat */
bc10a425
CB
11246 if (!sockflagsize || fdoff > sockflagsize) {
11247 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
a02a5408 11248 else Newx (sockflags,fdoff+2,unsigned int);
bc10a425
CB
11249 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11250 sockflagsize = fdoff + 2;
11251 }
312ac60b 11252 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
bc10a425
CB
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)*/
11267int my_fclose(FILE *fp) {
11268 if (fp) {
11269 unsigned int fd = fileno(fp);
11270 unsigned int fdoff = fd / sizeof(unsigned int);
11271
e0951028 11272 if (sockflagsize && fdoff < sockflagsize)
bc10a425
CB
11273 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11274 }
11275 return fclose(fp);
11276}
11277/*}}}*/
11278
11279
a0d0e21e
LW
11280/*
11281 * A simple fwrite replacement which outputs itmsz*nitm chars without
11282 * introducing record boundaries every itmsz chars.
22d4bb9c
CB
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.
a0d0e21e 11287 */
a15cef0c 11288/*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
a0d0e21e 11289int
a15cef0c 11290my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
a0d0e21e 11291{
22d4bb9c 11292 register char *cp, *end, *cpd, *data;
bc10a425
CB
11293 register unsigned int fd = fileno(dest);
11294 register unsigned int fdoff = fd / sizeof(unsigned int);
22d4bb9c 11295 int retval;
bc10a425
CB
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 }
22d4bb9c 11303
bc10a425 11304 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
22d4bb9c
CB
11305 memcpy( data, src, itmsz*nitm );
11306 data[itmsz*nitm] = '\0';
a0d0e21e 11307
22d4bb9c
CB
11308 end = data + itmsz * nitm;
11309 retval = (int) nitm; /* on success return # items written */
a0d0e21e 11310
22d4bb9c
CB
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; }
a0d0e21e 11315 if (cp < end)
22d4bb9c
CB
11316 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11317 cpd = cp + 1;
a0d0e21e
LW
11318 }
11319
bc10a425 11320 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
22d4bb9c 11321 return retval;
a0d0e21e
LW
11322
11323} /* end of my_fwrite() */
11324/*}}}*/
11325
d27fe803
JH
11326/*{{{ int my_flush(FILE *fp)*/
11327int
fd8cd3a3 11328Perl_my_flush(pTHX_ FILE *fp)
d27fe803
JH
11329{
11330 int res;
93948341 11331 if ((res = fflush(fp)) == 0 && fp) {
d27fe803 11332#ifdef VMS_DO_SOCKETS
61bb5906 11333 Stat_t s;
ed1b9de0 11334 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
d27fe803
JH
11335#endif
11336 res = fsync(fileno(fp));
11337 }
22d4bb9c
CB
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
d27fe803
JH
11345 return res;
11346}
11347/*}}}*/
11348
bf8d1304
JM
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)*/
11355char *
11356Perl_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
748a9306
LW
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
4633a7c4
LW
11426static char __empty[]= "";
11427static struct passwd __passwd_empty=
748a9306
LW
11428 {(char *) __empty, (char *) __empty, 0, 0,
11429 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11430static int contxt= 0;
11431static struct passwd __pwdcache;
11432static char __pw_namecache[UAI$S_IDENT+1];
11433
748a9306
LW
11434/*
11435 * This routine does most of the work extracting the user information.
11436 */
fd8cd3a3 11437static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
a0d0e21e 11438{
748a9306
LW
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;
c07a80fd 11460 unsigned long int sts;
748a9306 11461
4633a7c4 11462 static struct itmlst_3 itmlst[]= {
748a9306
LW
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;
f7ddb74a 11474 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
748a9306
LW
11475
11476/* Note that sys$getuai returns many fields as counted strings. */
c07a80fd 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 */
748a9306
LW
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
5c84aa53 11497 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
748a9306
LW
11498 pwd->pw_passwd= pw_passwd;
11499 pwd->pw_gecos= owner.pw_gecos;
11500 pwd->pw_dir= defdev.pw_dir;
360732b5 11501 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
748a9306
LW
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);
f7ddb74a
JM
11510 if (!decc_efs_case_preserve)
11511 __mystrtolower(pwd->pw_unixdir);
c07a80fd 11512 return 1;
a0d0e21e 11513}
748a9306
LW
11514
11515/*
11516 * Get information for a named user.
11517*/
11518/*{{{struct passwd *getpwnam(char *name)*/
2fbb330f 11519struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
748a9306
LW
11520{
11521 struct dsc$descriptor_s name_desc;
11522 union uicdef uic;
aa689395 11523 unsigned long int status, sts;
748a9306
LW
11524
11525 __pwdcache = __passwd_empty;
fd8cd3a3 11526 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
748a9306
LW
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;
aa689395 11532 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
748a9306
LW
11533 __pwdcache.pw_uid= uic.uic$l_uic;
11534 __pwdcache.pw_gid= uic.uic$v_group;
11535 }
c07a80fd 11536 else {
aa689395 11537 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11538 set_vaxc_errno(sts);
11539 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
c07a80fd 11540 return NULL;
11541 }
aa689395 11542 else { _ckvmssts(sts); }
c07a80fd 11543 }
748a9306 11544 }
748a9306
LW
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() */
a0d0e21e
LW
11550/*}}}*/
11551
748a9306
LW
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)*/
fd8cd3a3 11557struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
a0d0e21e 11558{
748a9306
LW
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) {
c07a80fd 11568 set_vaxc_errno(status);
11569 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
748a9306
LW
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;
c07a80fd 11578 if (!uic.uic$v_group)
76e3520e 11579 uic.uic$v_group= PerlProc_getgid();
748a9306
LW
11580 if (valid_uic(uic))
11581 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11582 else status = SS$_IVIDENT;
c07a80fd 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); }
748a9306
LW
11589 }
11590 __pw_namecache[lname]= '\0';
01b8edb6 11591 __mystrtolower(__pw_namecache);
748a9306
LW
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
fd8cd3a3 11602 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
748a9306 11603 return &__pwdcache;
a0d0e21e 11604
748a9306
LW
11605} /* end of my_getpwuid() */
11606/*}}}*/
11607
11608/*
11609 * Get information for next user.
11610*/
11611/*{{{struct passwd *my_getpwent()*/
fd8cd3a3 11612struct passwd *Perl_my_getpwent(pTHX)
748a9306
LW
11613{
11614 return (my_getpwuid((unsigned int) -1));
11615}
11616/*}}}*/
a0d0e21e 11617
748a9306
LW
11618/*
11619 * Finish searching rights database for users.
11620*/
11621/*{{{void my_endpwent()*/
fd8cd3a3 11622void Perl_my_endpwent(pTHX)
748a9306
LW
11623{
11624 if (contxt) {
11625 _ckvmssts(sys$finish_rdb(&contxt));
11626 contxt= 0;
11627 }
a0d0e21e
LW
11628}
11629/*}}}*/
748a9306 11630
61bb5906
CB
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 */
5b411029 11639
61bb5906
CB
11640 /* sigset_t is atomic under VMS, so these routines are easy */
11641/*{{{int my_sigemptyset(sigset_t *) */
5b411029 11642int my_sigemptyset(sigset_t *set) {
61bb5906
CB
11643 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11644 *set = 0; return 0;
5b411029 11645}
61bb5906
CB
11646/*}}}*/
11647
11648
11649/*{{{int my_sigfillset(sigset_t *)*/
5b411029 11650int my_sigfillset(sigset_t *set) {
61bb5906
CB
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;
5b411029 11655}
61bb5906
CB
11656/*}}}*/
11657
11658
11659/*{{{int my_sigaddset(sigset_t *set, int sig)*/
5b411029 11660int my_sigaddset(sigset_t *set, int sig) {
61bb5906
CB
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;
5b411029 11665}
61bb5906
CB
11666/*}}}*/
11667
11668
11669/*{{{int my_sigdelset(sigset_t *set, int sig)*/
5b411029 11670int my_sigdelset(sigset_t *set, int sig) {
61bb5906
CB
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;
5b411029 11675}
61bb5906
CB
11676/*}}}*/
11677
11678
11679/*{{{int my_sigismember(sigset_t *set, int sig)*/
5b411029 11680int my_sigismember(sigset_t *set, int sig) {
61bb5906
CB
11681 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11682 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
73e350d9 11683 return *set & (1 << (sig - 1));
5b411029 11684}
61bb5906 11685/*}}}*/
5b411029 11686
5b411029 11687
61bb5906
CB
11688/*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11689int 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);
5b411029
MB
11695 return -1;
11696 }
5b411029 11697
61bb5906
CB
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;
5b411029 11723
61bb5906 11724 return 0;
5b411029 11725}
61bb5906
CB
11726/*}}}*/
11727#endif /* HOMEGROWN_POSIX_SIGNALS */
11728
5b411029 11729
ff0cee69 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
e518068a 11736 */
ff0cee69 11737static int gmtime_emulation_type;
11738/* number of secs to add to UTC POSIX-style time to get local time */
11739static long int utc_offset_secs;
e518068a 11740
ff0cee69 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.
e518068a 11744 */
11745#undef gmtime
ff0cee69 11746#undef localtime
11747#undef time
11748
61bb5906 11749
a44ceb8e
CB
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
61bb5906
CB
11771static 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}
32da55ab 11779#define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11780 ((gmtime_emulation_type || my_time(NULL)), \
11781 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11782 ((secs) - utc_offset_secs))))
11783
11784static 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}
32da55ab 11792#define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11793 ((gmtime_emulation_type || my_time(NULL)), \
11794 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11795 ((secs) + utc_offset_secs))))
11796
22d4bb9c
CB
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
11816static char *
11817tz_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
11906before:
11907 *past = 0;
11908 return s;
11909after:
11910 *past = 1;
11911 return s;
11912}
11913
11914
11915
11916
11917/* parse the offset: (+|-)hh[:mm[:ss]] */
11918
11919static char *
11920tz_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
11965static int
fd8cd3a3 11966tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
22d4bb9c
CB
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);
f7ddb74a
JM
12056 tz_parse_startend(s_start,w2,&ds);
12057 tz_parse_startend(s_end,w2,&de);
22d4bb9c
CB
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
12068done:
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 */
61bb5906 12080
ff0cee69 12081/* my_time(), my_localtime(), my_gmtime()
61bb5906 12082 * By default traffic in UTC time values, using CRTL gmtime() or
ff0cee69 12083 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
61bb5906
CB
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 *
ff0cee69 12087 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
bd3fa61c 12088 * Modified by Charles Bailey <bailey@newman.upenn.edu>
ff0cee69 12089 */
12090
12091/*{{{time_t my_time(time_t *timep)*/
fd8cd3a3 12092time_t Perl_my_time(pTHX_ time_t *timep)
e518068a 12093{
e518068a 12094 time_t when;
61bb5906 12095 struct tm *tm_p;
e518068a 12096
12097 if (gmtime_emulation_type == 0) {
61bb5906
CB
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 */
ff0cee69 12102
e518068a 12103 gmtime_emulation_type++;
ff0cee69 12104 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
f675dbe5 12105 char off[LNM$C_NAMLENGTH+1];;
ff0cee69 12106
e518068a 12107 gmtime_emulation_type++;
f675dbe5 12108 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
e518068a 12109 gmtime_emulation_type++;
22d4bb9c 12110 utc_offset_secs = 0;
5c84aa53 12111 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
ff0cee69 12112 }
12113 else { utc_offset_secs = atol(off); }
e518068a 12114 }
ff0cee69 12115 else { /* We've got a working gmtime() */
12116 struct tm gmt, local;
e518068a 12117
ff0cee69 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 }
e518068a 12126 }
ff0cee69 12127
12128 when = time(NULL);
61bb5906
CB
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
ff0cee69 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)*/
12144struct tm *
fd8cd3a3 12145Perl_my_gmtime(pTHX_ const time_t *timep)
ff0cee69 12146{
12147 char *p;
12148 time_t when;
61bb5906 12149 struct tm *rsltmp;
ff0cee69 12150
68dc0745 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 */
ff0cee69 12156
12157 when = *timep;
12158# ifdef VMSISH_TIME
61bb5906
CB
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
ff0cee69 12164 /* CRTL localtime() wants local time as input, so does no tz correction */
61bb5906
CB
12165 rsltmp = localtime(&when);
12166 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12167 return rsltmp;
12168#endif
e518068a 12169} /* end of my_gmtime() */
e518068a 12170/*}}}*/
12171
12172
ff0cee69 12173/*{{{struct tm *my_localtime(const time_t *timep)*/
12174struct tm *
fd8cd3a3 12175Perl_my_localtime(pTHX_ const time_t *timep)
ff0cee69 12176{
22d4bb9c 12177 time_t when, whenutc;
61bb5906 12178 struct tm *rsltmp;
22d4bb9c 12179 int dst, offset;
ff0cee69 12180
68dc0745 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 */
f7ddb74a 12186 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
ff0cee69 12187
12188 when = *timep;
61bb5906 12189# ifdef RTL_USES_UTC
ff0cee69 12190# ifdef VMSISH_TIME
61bb5906 12191 if (VMSISH_TIME) when = _toutc(when);
ff0cee69 12192# endif
61bb5906 12193 /* CRTL localtime() wants UTC as input, does tz correction itself */
ff0cee69 12194 return localtime(&when);
22d4bb9c
CB
12195
12196# else /* !RTL_USES_UTC */
12197 whenutc = when;
61bb5906 12198# ifdef VMSISH_TIME
22d4bb9c
CB
12199 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12200 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
61bb5906 12201# endif
22d4bb9c
CB
12202 dst = -1;
12203#ifndef RTL_USES_UTC
32af7c23 12204 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
22d4bb9c
CB
12205 when = whenutc - offset; /* pseudolocal time*/
12206 }
61bb5906
CB
12207# endif
12208 /* CRTL localtime() wants local time as input, so does no tz correction */
12209 rsltmp = localtime(&when);
22d4bb9c 12210 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
61bb5906 12211 return rsltmp;
22d4bb9c 12212# endif
ff0cee69 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
941b3de1
CB
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
ff0cee69 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.
941b3de1 12239 *
ff0cee69 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 */
12246static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12247
94a11853
CB
12248/*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12249int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
ff0cee69 12250{
941b3de1
CB
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
ff0cee69 12274 register int i;
f7ddb74a 12275 int sts;
ff0cee69 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
f7ddb74a 12297 /* cast ok for read only parameter */
ff0cee69 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};
704c2eb3 12301
ff0cee69 12302 if (file == NULL || *file == '\0') {
941b3de1 12303 SETERRNO(ENOENT, LIB$_INVARG);
ff0cee69 12304 return -1;
12305 }
704c2eb3
JM
12306
12307 /* Convert to VMS format ensuring that it will fit in 255 characters */
6fb6c614 12308 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
941b3de1
CB
12309 SETERRNO(ENOENT, LIB$_INVARG);
12310 return -1;
12311 }
ff0cee69 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;
61bb5906
CB
12321# ifdef VMSISH_TIME
12322 /* If input was UTC; convert to local for sys svc */
12323 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
ff0cee69 12324# endif
1a6334fb 12325 unixtime >>= 1; secscale <<= 1;
ff0cee69 12326 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12327 if (!(retsts & 1)) {
941b3de1 12328 SETERRNO(EVMSERR, retsts);
ff0cee69 12329 return -1;
12330 }
12331 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12332 if (!(retsts & 1)) {
941b3de1 12333 SETERRNO(EVMSERR, retsts);
ff0cee69 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)) {
941b3de1 12341 SETERRNO(EVMSERR, retsts);
ff0cee69 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;
f7ddb74a
JM
12353 if (decc_efs_case_preserve)
12354 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
ff0cee69 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)) {
752635ea 12370 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12371 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 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;
f7ddb74a 12380 /* cast ok for read only parameter */
ff0cee69 12381 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12382
12383 retsts = sys$assign(&devdsc,&chan,0,0);
12384 if (!(retsts & 1)) {
752635ea 12385 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12386 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 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);
22d4bb9c 12399#if defined(__DECC) || defined(__DECCXX)
ff0cee69 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);
752635ea 12411 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 12412 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 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;
941b3de1
CB
12423
12424#endif /* #if __CRTL_VER >= 70300000 */
12425
ff0cee69 12426} /* end of my_utime() */
12427/*}}}*/
12428
748a9306 12429/*
2497a41f 12430 * flex_stat, flex_lstat, flex_fstat
748a9306
LW
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
2497a41f 12435#ifndef _USE_STD_STAT
748a9306
LW
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 *
e518068a 12452 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
748a9306
LW
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.
cb9e088c 12460 * The number returned would be more intelligible.
748a9306
LW
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 */
fd8cd3a3 12465static mydev_t encode_dev (pTHX_ const char *dev)
748a9306
LW
12466{
12467 int i;
12468 unsigned long int f;
aa689395 12469 mydev_t enc;
748a9306
LW
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;
cb9e088c 12478 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
748a9306
LW
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;
f7ddb74a 12485 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
cb9e088c 12486 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
360732b5 12487 if (!$VMS_STATUS_SUCCESS(status)) {
cb9e088c
CB
12488 switch (status) {
12489 case SS$_NOSUCHDEV:
12490 SETERRNO(ENODEV, status);
12491 return 0;
12492 default:
12493 _ckvmssts(status);
12494 }
12495 }
748a9306
LW
12496 if (lockid) return (lockid & ~LOCKID_MASK);
12497 }
a0d0e21e 12498#endif
748a9306
LW
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) {
988c775c
JM
12505 if (*q == ':')
12506 break;
748a9306
LW
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() */
cfcfe586
JM
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
2497a41f 12526#endif
748a9306 12527
748a9306
LW
12528static int
12529is_null_device(name)
12530 const char *name;
12531{
2497a41f 12532 if (decc_bug_devnull != 0) {
682e4b71 12533 if (strncmp("/dev/null", name, 9) == 0)
2497a41f
JM
12534 return 1;
12535 }
748a9306
LW
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
312ac60b
JM
12550static int
12551Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
c07a80fd 12552
46c05374
CB
12553#define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12554
a1887106
JM
12555static I32
12556Perl_cando_by_name_int
12557 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
748a9306 12558{
e538e23f
CB
12559 char usrname[L_cuserid];
12560 struct dsc$descriptor_s usrdsc =
748a9306 12561 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
e538e23f 12562 char *vmsname = NULL, *fileified = NULL;
597c27e2 12563 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
2d9f3838 12564 unsigned short int retlen, trnlnm_iter_count;
748a9306
LW
12565 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12566 union prvdef curprv;
597c27e2
CB
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}};
ada67d10
CB
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},
748a9306 12574 {0,0,0,0}};
ada67d10 12575 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
858aded6 12576 Stat_t st;
6151c65c 12577 static int profile_context = -1;
748a9306
LW
12578
12579 if (!fname || !*fname) return FALSE;
a1887106 12580
e538e23f
CB
12581 /* Make sure we expand logical names, since sys$check_access doesn't */
12582 fileified = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12583 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f 12584 if (!strpbrk(fname,"/]>:")) {
a1887106
JM
12585 strcpy(fileified,fname);
12586 trnlnm_iter_count = 0;
e538e23f 12587 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
2d9f3838
CB
12588 trnlnm_iter_count++;
12589 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
a1887106
JM
12590 }
12591 fname = fileified;
e538e23f
CB
12592 }
12593
12594 vmsname = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12595 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f
CB
12596 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12597 /* Don't know if already in VMS format, so make sure */
360732b5 12598 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
a1887106 12599 PerlMem_free(fileified);
e538e23f 12600 PerlMem_free(vmsname);
a1887106
JM
12601 return FALSE;
12602 }
a1887106
JM
12603 }
12604 else {
e538e23f 12605 strcpy(vmsname,fname);
a5f75d66
AD
12606 }
12607
858aded6 12608 /* sys$check_access needs a file spec, not a directory spec.
312ac60b 12609 * flex_stat now will handle a null thread context during startup.
858aded6 12610 */
e538e23f
CB
12611
12612 retlen = namdsc.dsc$w_length = strlen(vmsname);
12613 if (vmsname[retlen-1] == ']'
12614 || vmsname[retlen-1] == '>'
858aded6 12615 || vmsname[retlen-1] == ':'
46c05374 12616 || (!flex_stat_int(vmsname, &st, 1) &&
312ac60b 12617 S_ISDIR(st.st_mode))) {
e538e23f 12618
a979ce91 12619 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
e538e23f
CB
12620 PerlMem_free(fileified);
12621 PerlMem_free(vmsname);
12622 return FALSE;
12623 }
12624 fname = fileified;
12625 }
858aded6
CB
12626 else {
12627 fname = vmsname;
12628 }
e538e23f
CB
12629
12630 retlen = namdsc.dsc$w_length = strlen(fname);
12631 namdsc.dsc$a_pointer = (char *)fname;
12632
748a9306 12633 switch (bit) {
f282b18d 12634 case S_IXUSR: case S_IXGRP: case S_IXOTH:
360732b5 12635 access = ARM$M_EXECUTE;
597c27e2
CB
12636 flags = CHP$M_READ;
12637 break;
f282b18d 12638 case S_IRUSR: case S_IRGRP: case S_IROTH:
360732b5 12639 access = ARM$M_READ;
597c27e2
CB
12640 flags = CHP$M_READ | CHP$M_USEREADALL;
12641 break;
f282b18d 12642 case S_IWUSR: case S_IWGRP: case S_IWOTH:
360732b5 12643 access = ARM$M_WRITE;
597c27e2
CB
12644 flags = CHP$M_READ | CHP$M_WRITE;
12645 break;
f282b18d 12646 case S_IDUSR: case S_IDGRP: case S_IDOTH:
360732b5 12647 access = ARM$M_DELETE;
597c27e2
CB
12648 flags = CHP$M_READ | CHP$M_WRITE;
12649 break;
748a9306 12650 default:
a1887106
JM
12651 if (fileified != NULL)
12652 PerlMem_free(fileified);
e538e23f
CB
12653 if (vmsname != NULL)
12654 PerlMem_free(vmsname);
748a9306
LW
12655 return FALSE;
12656 }
12657
ada67d10
CB
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
baf3cf9c
CB
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.
ada67d10
CB
12663 */
12664
12665 /* get current process privs and username */
ebd4d70b
JM
12666 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12667 _ckvmssts_noperl(iosb[0]);
ada67d10 12668
baf3cf9c
CB
12669#if defined(__VMS_VER) && __VMS_VER >= 60000000
12670
ada67d10 12671 /* find out the space required for the profile */
ebd4d70b 12672 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
6151c65c 12673 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12674
12675 /* allocate space for the profile and get it filled in */
c5375c28 12676 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
ebd4d70b
JM
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,
6151c65c 12679 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12680
12681 /* use the profile to check access to the file; free profile & analyze results */
6151c65c 12682 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
c5375c28 12683 PerlMem_free(usrprodsc.dsc$a_pointer);
ada67d10 12684 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
baf3cf9c
CB
12685
12686#else
12687
12688 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12689
12690#endif
12691
bbce6d69 12692 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
61bb5906 12693 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
22d4bb9c 12694 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
bbce6d69 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);
a1887106
JM
12699 if (fileified != NULL)
12700 PerlMem_free(fileified);
e538e23f
CB
12701 if (vmsname != NULL)
12702 PerlMem_free(vmsname);
a3e9d8c9 12703 return FALSE;
12704 }
ada67d10 12705 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
a1887106
JM
12706 if (fileified != NULL)
12707 PerlMem_free(fileified);
e538e23f
CB
12708 if (vmsname != NULL)
12709 PerlMem_free(vmsname);
3a385817
GS
12710 return TRUE;
12711 }
ebd4d70b 12712 _ckvmssts_noperl(retsts);
748a9306 12713
a1887106
JM
12714 if (fileified != NULL)
12715 PerlMem_free(fileified);
e538e23f
CB
12716 if (vmsname != NULL)
12717 PerlMem_free(vmsname);
748a9306
LW
12718 return FALSE; /* Should never get here */
12719
a1887106
JM
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 */
12726bool
12727Perl_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)*/
12736I32
12737Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12738{
12739 return cando_by_name_int(bit, effective, fname, 0);
12740
748a9306
LW
12741} /* end of cando_by_name() */
12742/*}}}*/
12743
12744
61bb5906 12745/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
748a9306 12746int
fd8cd3a3 12747Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
748a9306 12748{
312ac60b 12749 if (!fstat(fd, &statbufp->crtl_stat)) {
75796008 12750 char *cptr;
988c775c
JM
12751 char *vms_filename;
12752 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12753 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
75796008 12754
988c775c
JM
12755 /* Save name for cando by name in VMS format */
12756 cptr = getname(fd, vms_filename, 1);
75796008 12757
988c775c
JM
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 */
6fb6c614 12764 cptr = int_rmsexpand_vms
988c775c
JM
12765 (vms_filename,
12766 statbufp->st_devnam,
6fb6c614 12767 0);
75796008 12768 if (cptr == NULL)
988c775c 12769 statbufp->st_devnam[0] = 0;
75796008 12770 }
988c775c 12771 PerlMem_free(vms_filename);
682e4b71
JM
12772
12773 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12774 VMS_DEVICE_ENCODE
12775 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
2497a41f 12776
61bb5906
CB
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
ff0cee69 12786# ifdef VMSISH_TIME
12787 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12788# else
12789 if (1) {
12790# endif
61bb5906
CB
12791 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12792 statbufp->st_atime = _toutc(statbufp->st_atime);
12793 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 12794 }
61bb5906 12795#endif
b7ae7a0d 12796 return 0;
12797 }
12798 return -1;
748a9306
LW
12799
12800} /* end of flex_fstat() */
12801/*}}}*/
12802
2497a41f
JM
12803static int
12804Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
748a9306 12805{
312ac60b
JM
12806 char *fileified;
12807 char *temp_fspec;
12808 const char *save_spec;
12809 char *ret_spec;
bbce6d69 12810 int retval = -1;
312ac60b 12811 int efs_hack = 0;
4ee39169 12812 dSAVEDERRNO;
748a9306 12813
312ac60b
JM
12814 if (!fspec) {
12815 errno = EINVAL;
12816 return retval;
12817 }
988c775c 12818
2497a41f 12819 if (decc_bug_devnull != 0) {
312ac60b 12820 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
2497a41f 12821 memset(statbufp,0,sizeof *statbufp);
cfcfe586 12822 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
2497a41f
JM
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 }
748a9306
LW
12830 }
12831
bbce6d69 12832 /* Try for a directory name first. If fspec contains a filename without
61bb5906 12833 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
bbce6d69 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.
2497a41f
JM
12839 *
12840 * If we are in Posix filespec mode, accept the filename as is.
bbce6d69 12841 */
f36b279d
CB
12842
12843
312ac60b
JM
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;
f36b279d 12855
2497a41f
JM
12856#if __CRTL_VER >= 80200000 && !defined(__VAX)
12857 if (decc_posix_compliant_pathnames == 0) {
12858#endif
312ac60b
JM
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) {
d94c5a78 12869 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
312ac60b
JM
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 }
748a9306 12877 }
312ac60b
JM
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 }
2497a41f 12893 }
312ac60b 12894
f1db9cda 12895 if (retval) {
312ac60b
JM
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
f1db9cda 12916 }
312ac60b 12917
2497a41f
JM
12918#if __CRTL_VER >= 80200000 && !defined(__VAX)
12919 } else {
12920 if (lstat_flag == 0)
312ac60b 12921 retval = stat(temp_fspec, &statbufp->crtl_stat);
2497a41f 12922 else
312ac60b 12923 retval = lstat(temp_fspec, &statbufp->crtl_stat);
988c775c 12924 save_spec = temp_fspec;
2497a41f
JM
12925 }
12926#endif
f36b279d
CB
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
ff0cee69 12934 if (!retval) {
988c775c 12935 char * cptr;
d584a1c6
JM
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
312ac60b
JM
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
6fb6c614 12949 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
312ac60b
JM
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 */
988c775c
JM
12958 if (cptr == NULL)
12959 statbufp->st_devnam[0] = 0;
12960
682e4b71 12961 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12962 VMS_DEVICE_ENCODE
12963 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
61bb5906
CB
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
ff0cee69 12973# ifdef VMSISH_TIME
12974 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12975# else
12976 if (1) {
12977# endif
61bb5906
CB
12978 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12979 statbufp->st_atime = _toutc(statbufp->st_atime);
12980 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 12981 }
61bb5906 12982# endif
ff0cee69 12983 }
9543c6b6 12984 /* If we were successful, leave errno where we found it */
4ee39169 12985 if (retval == 0) RESTORE_ERRNO;
748a9306
LW
12986 return retval;
12987
2497a41f
JM
12988} /* end of flex_stat_int() */
12989
12990
12991/*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12992int
12993Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12994{
7ded3206 12995 return flex_stat_int(fspec, statbufp, 0);
2497a41f
JM
12996}
12997/*}}}*/
12998
12999/*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
13000int
13001Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
13002{
7ded3206 13003 return flex_stat_int(fspec, statbufp, 1);
2497a41f 13004}
748a9306
LW
13005/*}}}*/
13006
b7ae7a0d 13007
c07a80fd 13008/*{{{char *my_getlogin()*/
13009/* VMS cuserid == Unix getlogin, except calling sequence */
13010char *
2fbb330f 13011my_getlogin(void)
c07a80fd 13012{
13013 static char user[L_cuserid];
13014 return cuserid(user);
13015}
13016/*}}}*/
13017
13018
a5f75d66
AD
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
a3e9d8c9 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.
a5f75d66 13035 *
bd3fa61c 13036 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
a5f75d66 13037 * Incorporates, with permission, some code from EZCOPY by Tim Adye
01b8edb6 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.
a480973c 13042 */ /* FIXME */
a3e9d8c9 13043/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
a480973c
JM
13044int
13045Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
13046{
d584a1c6
JM
13047 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
13048 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
a480973c 13049 unsigned long int i, sts, sts2;
a1887106 13050 int dna_len;
a480973c
JM
13051 struct FAB fab_in, fab_out;
13052 struct RAB rab_in, rab_out;
a1887106
JM
13053 rms_setup_nam(nam);
13054 rms_setup_nam(nam_out);
a480973c
JM
13055 struct XABDAT xabdat;
13056 struct XABFHC xabfhc;
13057 struct XABRDT xabrdt;
13058 struct XABSUM xabsum;
13059
c5375c28 13060 vmsin = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13061 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 13062 vmsout = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13063 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
df278665
JM
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)) {
c5375c28
JM
13066 PerlMem_free(vmsin);
13067 PerlMem_free(vmsout);
a480973c
JM
13068 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13069 return 0;
13070 }
13071
b1a8dcd7 13072 esa = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13073 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13074 esal = NULL;
13075#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13076 esal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13077 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13078#endif
a480973c 13079 fab_in = cc$rms_fab;
a1887106 13080 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
a480973c
JM
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;
a1887106 13084 rms_bind_fab_nam(fab_in, nam);
a480973c
JM
13085 fab_in.fab$l_xab = (void *) &xabdat;
13086
b1a8dcd7 13087 rsa = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13088 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13089 rsal = NULL;
13090#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13091 rsal = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13092 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
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));
a1887106
JM
13096 rms_nam_esl(nam) = 0;
13097 rms_nam_rsl(nam) = 0;
13098 rms_nam_esll(nam) = 0;
13099 rms_nam_rsll(nam) = 0;
a480973c
JM
13100#ifdef NAM$M_NO_SHORT_UPCASE
13101 if (decc_efs_case_preserve)
a1887106 13102 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
a480973c
JM
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)) {
c5375c28
JM
13114 PerlMem_free(vmsin);
13115 PerlMem_free(vmsout);
13116 PerlMem_free(esa);
d584a1c6
JM
13117 if (esal != NULL)
13118 PerlMem_free(esal);
c5375c28 13119 PerlMem_free(rsa);
d584a1c6
JM
13120 if (rsal != NULL)
13121 PerlMem_free(rsal);
a480973c
JM
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;
a1887106
JM
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);
d584a1c6 13150 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 13151 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13152 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 13153 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
13154 esal_out = NULL;
13155 rsal_out = NULL;
13156#if !defined(__VAX) && defined(NAML$C_MAXRSS)
13157 esal_out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13158 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 13159 rsal_out = PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 13160 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
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));
a480973c
JM
13164
13165 if (preserve_dates == 0) { /* Act like DCL COPY */
a1887106 13166 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
a480973c 13167 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
a1887106 13168 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
13169 PerlMem_free(vmsin);
13170 PerlMem_free(vmsout);
13171 PerlMem_free(esa);
d584a1c6
JM
13172 if (esal != NULL)
13173 PerlMem_free(esal);
c5375c28 13174 PerlMem_free(rsa);
d584a1c6
JM
13175 if (rsal != NULL)
13176 PerlMem_free(rsal);
c5375c28 13177 PerlMem_free(esa_out);
d584a1c6
JM
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);
a480973c
JM
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;
a1887106
JM
13188 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13189 preserve_dates = 1;
a480973c
JM
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;
a1887106 13195 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
13196 PerlMem_free(vmsin);
13197 PerlMem_free(vmsout);
13198 PerlMem_free(esa);
d584a1c6
JM
13199 if (esal != NULL)
13200 PerlMem_free(esal);
c5375c28 13201 PerlMem_free(rsa);
d584a1c6
JM
13202 if (rsal != NULL)
13203 PerlMem_free(rsal);
c5375c28 13204 PerlMem_free(esa_out);
d584a1c6
JM
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);
a480973c
JM
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
c5375c28 13241 ubf = PerlMem_malloc(32256);
ebd4d70b 13242 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
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);
c5375c28
JM
13250 PerlMem_free(vmsin);
13251 PerlMem_free(vmsout);
c5375c28 13252 PerlMem_free(ubf);
d584a1c6
JM
13253 PerlMem_free(esa);
13254 if (esal != NULL)
13255 PerlMem_free(esal);
c5375c28 13256 PerlMem_free(rsa);
d584a1c6
JM
13257 if (rsal != NULL)
13258 PerlMem_free(rsal);
c5375c28 13259 PerlMem_free(esa_out);
d584a1c6
JM
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);
a480973c
JM
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);
c5375c28
JM
13274 PerlMem_free(vmsin);
13275 PerlMem_free(vmsout);
c5375c28 13276 PerlMem_free(ubf);
d584a1c6
JM
13277 PerlMem_free(esa);
13278 if (esal != NULL)
13279 PerlMem_free(esal);
c5375c28 13280 PerlMem_free(rsa);
d584a1c6
JM
13281 if (rsal != NULL)
13282 PerlMem_free(rsal);
c5375c28 13283 PerlMem_free(esa_out);
d584a1c6
JM
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);
a480973c
JM
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);
c5375c28
JM
13298 PerlMem_free(vmsin);
13299 PerlMem_free(vmsout);
c5375c28 13300 PerlMem_free(ubf);
d584a1c6
JM
13301 PerlMem_free(esa);
13302 if (esal != NULL)
13303 PerlMem_free(esal);
c5375c28 13304 PerlMem_free(rsa);
d584a1c6
JM
13305 if (rsal != NULL)
13306 PerlMem_free(rsal);
c5375c28 13307 PerlMem_free(esa_out);
d584a1c6
JM
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);
a480973c
JM
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;
a480973c 13322
c5375c28
JM
13323 PerlMem_free(vmsin);
13324 PerlMem_free(vmsout);
c5375c28 13325 PerlMem_free(ubf);
d584a1c6
JM
13326 PerlMem_free(esa);
13327 if (esal != NULL)
13328 PerlMem_free(esal);
c5375c28 13329 PerlMem_free(rsa);
d584a1c6
JM
13330 if (rsal != NULL)
13331 PerlMem_free(rsal);
c5375c28 13332 PerlMem_free(esa_out);
d584a1c6
JM
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
a480973c
JM
13344 return 1;
13345
13346} /* end of rmscopy() */
a5f75d66
AD
13347/*}}}*/
13348
13349
748a9306
LW
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
13359void
5c84aa53 13360rmsexpand_fromperl(pTHX_ CV *cv)
01b8edb6 13361{
13362 dXSARGS;
bbce6d69 13363 char *fspec, *defspec = NULL, *rslt;
2d8e6c8d 13364 STRLEN n_a;
360732b5 13365 int fs_utf8, dfs_utf8;
01b8edb6 13366
360732b5
JM
13367 fs_utf8 = 0;
13368 dfs_utf8 = 0;
bbce6d69 13369 if (!items || items > 2)
5c84aa53 13370 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
2d8e6c8d 13371 fspec = SvPV(ST(0),n_a);
360732b5 13372 fs_utf8 = SvUTF8(ST(0));
bbce6d69 13373 if (!fspec || !*fspec) XSRETURN_UNDEF;
360732b5
JM
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);
bbce6d69 13379 ST(0) = sv_newmortal();
360732b5
JM
13380 if (rslt != NULL) {
13381 sv_usepvn(ST(0),rslt,strlen(rslt));
13382 if (fs_utf8) {
13383 SvUTF8_on(ST(0));
13384 }
13385 }
740ce14c 13386 XSRETURN(1);
01b8edb6 13387}
13388
13389void
5c84aa53 13390vmsify_fromperl(pTHX_ CV *cv)
748a9306
LW
13391{
13392 dXSARGS;
13393 char *vmsified;
2d8e6c8d 13394 STRLEN n_a;
360732b5 13395 int utf8_fl;
748a9306 13396
5c84aa53 13397 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
360732b5
JM
13398 utf8_fl = SvUTF8(ST(0));
13399 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13400 ST(0) = sv_newmortal();
360732b5
JM
13401 if (vmsified != NULL) {
13402 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13403 if (utf8_fl) {
13404 SvUTF8_on(ST(0));
13405 }
13406 }
748a9306
LW
13407 XSRETURN(1);
13408}
13409
13410void
5c84aa53 13411unixify_fromperl(pTHX_ CV *cv)
748a9306
LW
13412{
13413 dXSARGS;
13414 char *unixified;
2d8e6c8d 13415 STRLEN n_a;
360732b5 13416 int utf8_fl;
748a9306 13417
5c84aa53 13418 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
360732b5
JM
13419 utf8_fl = SvUTF8(ST(0));
13420 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13421 ST(0) = sv_newmortal();
360732b5
JM
13422 if (unixified != NULL) {
13423 sv_usepvn(ST(0),unixified,strlen(unixified));
13424 if (utf8_fl) {
13425 SvUTF8_on(ST(0));
13426 }
13427 }
748a9306
LW
13428 XSRETURN(1);
13429}
13430
13431void
5c84aa53 13432fileify_fromperl(pTHX_ CV *cv)
748a9306
LW
13433{
13434 dXSARGS;
13435 char *fileified;
2d8e6c8d 13436 STRLEN n_a;
360732b5 13437 int utf8_fl;
748a9306 13438
5c84aa53 13439 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
360732b5
JM
13440 utf8_fl = SvUTF8(ST(0));
13441 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13442 ST(0) = sv_newmortal();
360732b5
JM
13443 if (fileified != NULL) {
13444 sv_usepvn(ST(0),fileified,strlen(fileified));
13445 if (utf8_fl) {
13446 SvUTF8_on(ST(0));
13447 }
13448 }
748a9306
LW
13449 XSRETURN(1);
13450}
13451
13452void
5c84aa53 13453pathify_fromperl(pTHX_ CV *cv)
748a9306
LW
13454{
13455 dXSARGS;
13456 char *pathified;
2d8e6c8d 13457 STRLEN n_a;
360732b5 13458 int utf8_fl;
748a9306 13459
5c84aa53 13460 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
360732b5
JM
13461 utf8_fl = SvUTF8(ST(0));
13462 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13463 ST(0) = sv_newmortal();
360732b5
JM
13464 if (pathified != NULL) {
13465 sv_usepvn(ST(0),pathified,strlen(pathified));
13466 if (utf8_fl) {
13467 SvUTF8_on(ST(0));
13468 }
13469 }
748a9306
LW
13470 XSRETURN(1);
13471}
13472
13473void
5c84aa53 13474vmspath_fromperl(pTHX_ CV *cv)
748a9306
LW
13475{
13476 dXSARGS;
13477 char *vmspath;
2d8e6c8d 13478 STRLEN n_a;
360732b5 13479 int utf8_fl;
748a9306 13480
5c84aa53 13481 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
360732b5
JM
13482 utf8_fl = SvUTF8(ST(0));
13483 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13484 ST(0) = sv_newmortal();
360732b5
JM
13485 if (vmspath != NULL) {
13486 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13487 if (utf8_fl) {
13488 SvUTF8_on(ST(0));
13489 }
13490 }
748a9306
LW
13491 XSRETURN(1);
13492}
13493
13494void
5c84aa53 13495unixpath_fromperl(pTHX_ CV *cv)
748a9306
LW
13496{
13497 dXSARGS;
13498 char *unixpath;
2d8e6c8d 13499 STRLEN n_a;
360732b5 13500 int utf8_fl;
748a9306 13501
5c84aa53 13502 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
360732b5
JM
13503 utf8_fl = SvUTF8(ST(0));
13504 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 13505 ST(0) = sv_newmortal();
360732b5
JM
13506 if (unixpath != NULL) {
13507 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13508 if (utf8_fl) {
13509 SvUTF8_on(ST(0));
13510 }
13511 }
748a9306
LW
13512 XSRETURN(1);
13513}
13514
13515void
5c84aa53 13516candelete_fromperl(pTHX_ CV *cv)
748a9306
LW
13517{
13518 dXSARGS;
988c775c 13519 char *fspec, *fsp;
a5f75d66
AD
13520 SV *mysv;
13521 IO *io;
2d8e6c8d 13522 STRLEN n_a;
748a9306 13523
5c84aa53 13524 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
a5f75d66
AD
13525
13526 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
988c775c
JM
13527 Newx(fspec, VMS_MAXRSS, char);
13528 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
a5f75d66 13529 if (SvTYPE(mysv) == SVt_PVGV) {
a15cef0c 13530 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
a5f75d66 13531 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13532 ST(0) = &PL_sv_no;
988c775c 13533 Safefree(fspec);
a5f75d66
AD
13534 XSRETURN(1);
13535 }
13536 fsp = fspec;
13537 }
13538 else {
2d8e6c8d 13539 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
a5f75d66 13540 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13541 ST(0) = &PL_sv_no;
988c775c 13542 Safefree(fspec);
a5f75d66
AD
13543 XSRETURN(1);
13544 }
13545 }
13546
54310121 13547 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
988c775c 13548 Safefree(fspec);
a5f75d66
AD
13549 XSRETURN(1);
13550}
13551
13552void
5c84aa53 13553rmscopy_fromperl(pTHX_ CV *cv)
a5f75d66
AD
13554{
13555 dXSARGS;
a480973c 13556 char *inspec, *outspec, *inp, *outp;
a3e9d8c9 13557 int date_flag;
a5f75d66
AD
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;
2d8e6c8d 13563 STRLEN n_a;
a5f75d66 13564
a3e9d8c9 13565 if (items < 2 || items > 3)
5c84aa53 13566 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
a5f75d66
AD
13567
13568 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
a480973c 13569 Newx(inspec, VMS_MAXRSS, char);
a5f75d66 13570 if (SvTYPE(mysv) == SVt_PVGV) {
a15cef0c 13571 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
a5f75d66 13572 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13573 ST(0) = &PL_sv_no;
a480973c 13574 Safefree(inspec);
a5f75d66
AD
13575 XSRETURN(1);
13576 }
13577 inp = inspec;
13578 }
13579 else {
2d8e6c8d 13580 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
a5f75d66 13581 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13582 ST(0) = &PL_sv_no;
a480973c 13583 Safefree(inspec);
a5f75d66
AD
13584 XSRETURN(1);
13585 }
13586 }
13587 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
a480973c 13588 Newx(outspec, VMS_MAXRSS, char);
a5f75d66 13589 if (SvTYPE(mysv) == SVt_PVGV) {
a15cef0c 13590 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
a5f75d66 13591 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13592 ST(0) = &PL_sv_no;
a480973c
JM
13593 Safefree(inspec);
13594 Safefree(outspec);
a5f75d66
AD
13595 XSRETURN(1);
13596 }
13597 outp = outspec;
13598 }
13599 else {
2d8e6c8d 13600 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
a5f75d66 13601 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 13602 ST(0) = &PL_sv_no;
a480973c
JM
13603 Safefree(inspec);
13604 Safefree(outspec);
a5f75d66
AD
13605 XSRETURN(1);
13606 }
13607 }
a3e9d8c9 13608 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
a5f75d66 13609
54310121 13610 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
a480973c
JM
13611 Safefree(inspec);
13612 Safefree(outspec);
748a9306
LW
13613 XSRETURN(1);
13614}
13615
a480973c
JM
13616/* The mod2fname is limited to shorter filenames by design, so it should
13617 * not be modified to support longer EFS pathnames
13618 */
4b19af01 13619void
fd8cd3a3 13620mod2fname(pTHX_ CV *cv)
4b19af01
CB
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 }
bfd025d9 13644 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
4b19af01
CB
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
748a9306 13693void
96e176bf
CL
13694hushexit_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
dca5a913
JM
13705
13706PerlIO *
13707Perl_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;
85e7c9de 13725 int vms_old_glob = 1;
dca5a913 13726
83b907a4
CB
13727 if (!SvOK(tmpglob)) {
13728 SETERRNO(ENOENT,RMS$_FNF);
13729 return NULL;
13730 }
13731
85e7c9de
JM
13732 vms_old_glob = !decc_filename_unix_report;
13733
dca5a913
JM
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 }
85e7c9de
JM
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
dca5a913 13784 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
85e7c9de
JM
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;
990cad08 13789 int found = 0;
dca5a913
JM
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)) {
85e7c9de
JM
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 }
dca5a913
JM
13819 }
13820 else {
360732b5 13821 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
dca5a913
JM
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 */
85e7c9de
JM
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 }
dca5a913 13839 }
85e7c9de
JM
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
dca5a913
JM
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;
85e7c9de 13857 int valid_find;
dca5a913 13858
85e7c9de 13859 valid_find = 0;
dca5a913
JM
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
360732b5 13870 (rstr,
dca5a913
JM
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;
85e7c9de
JM
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 }
dca5a913
JM
13907
13908 /* No version & a null extension on UNIX handling */
85e7c9de 13909 if ((e_len == 1) && decc_readdir_dropdotnotype) {
dca5a913
JM
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
85e7c9de
JM
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 }
dca5a913
JM
13958 }
13959 if (cxt) (void)lib$find_file_end(&cxt);
990cad08
CB
13960
13961 if (!found) {
13962 /* Be POSIXish: return the input pattern when no matches */
2da7a6b5
CB
13963 strcpy(rstr,SvPVX(tmpglob));
13964 strcat(rstr,"\n");
13965 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
990cad08
CB
13966 }
13967
dca5a913
JM
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
cd1191f1 13989
2497a41f 13990static char *
5c4d031a 13991mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
d584a1c6 13992 int *utf8_fl);
2497a41f
JM
13993
13994void
4d8d3a9c 13995unixrealpath_fromperl(pTHX_ CV *cv)
2497a41f 13996{
d584a1c6
JM
13997 dXSARGS;
13998 char *fspec, *rslt_spec, *rslt;
13999 STRLEN n_a;
2497a41f 14000
d584a1c6 14001 if (!items || items != 1)
4d8d3a9c 14002 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
2497a41f 14003
d584a1c6
JM
14004 fspec = SvPV(ST(0),n_a);
14005 if (!fspec || !*fspec) XSRETURN_UNDEF;
2497a41f 14006
d584a1c6
JM
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);
2497a41f 14016}
2ee6e19d 14017
b1a8dcd7
JM
14018static char *
14019mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
14020 int *utf8_fl);
14021
14022void
4d8d3a9c 14023vmsrealpath_fromperl(pTHX_ CV *cv)
b1a8dcd7
JM
14024{
14025 dXSARGS;
14026 char *fspec, *rslt_spec, *rslt;
14027 STRLEN n_a;
14028
14029 if (!items || items != 1)
4d8d3a9c 14030 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
b1a8dcd7
JM
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
2ee6e19d
CB
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.
4148925f
JM
14050 *
14051 * Also in ODS-2 mode, existing tests assume that the link target
14052 * will be converted to UNIX format.
2ee6e19d 14053 */
4148925f
JM
14054/*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
14055int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
14056 if (!link_name || !*link_name) {
2ee6e19d
CB
14057 SETERRNO(ENOENT, SS$_NOSUCHFILE);
14058 return -1;
14059 }
4148925f
JM
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
4d9538c1 14073 utarget = PerlMem_malloc(VMS_MAXRSS + 1);
0e5ce2c7 14074 if (int_tounixspec(contents, utarget, NULL) == NULL) {
4148925f
JM
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);
4d9538c1 14081 PerlMem_free(utarget);
4148925f
JM
14082 return sts;
14083 }
14084
2ee6e19d
CB
14085}
14086/*}}}*/
14087
14088#endif /* HAS_SYMLINK */
2497a41f 14089
2497a41f
JM
14090int do_vms_case_tolerant(void);
14091
14092void
4d8d3a9c 14093case_tolerant_process_fromperl(pTHX_ CV *cv)
2497a41f
JM
14094{
14095 dXSARGS;
14096 ST(0) = boolSV(do_vms_case_tolerant());
14097 XSRETURN(1);
14098}
2497a41f 14099
9ec7171b
CB
14100#ifdef USE_ITHREADS
14101
96e176bf
CL
14102void
14103Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14104 struct interp_intern *dst)
14105{
7918f24d
NC
14106 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14107
96e176bf
CL
14108 memcpy(dst,src,sizeof(struct interp_intern));
14109}
14110
9ec7171b
CB
14111#endif
14112
96e176bf
CL
14113void
14114Perl_sys_intern_clear(pTHX)
14115{
14116}
14117
14118void
14119Perl_sys_intern_init(pTHX)
14120{
3ff49832
CL
14121 unsigned int ix = RAND_MAX;
14122 double x;
96e176bf
CL
14123
14124 VMSISH_HUSHED = 0;
14125
1a3aec58 14126 MY_POSIX_EXIT = vms_posix_exit;
7a7fd8e0 14127
96e176bf
CL
14128 x = (float)ix;
14129 MY_INV_RAND_MAX = 1./x;
ff7adb52 14130}
96e176bf
CL
14131
14132void
f7ddb74a 14133init_os_extras(void)
748a9306 14134{
a69a6dba 14135 dTHX;
748a9306 14136 char* file = __FILE__;
988c775c 14137 if (decc_disable_to_vms_logname_translation) {
93948341
CB
14138 no_translate_barewords = TRUE;
14139 } else {
14140 no_translate_barewords = FALSE;
14141 }
748a9306 14142
740ce14c 14143 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
a5f75d66
AD
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,"$");
4b19af01 14151 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
a5f75d66 14152 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
96e176bf 14153 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
4d8d3a9c
CB
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,"");
17f28c40 14158
afd8f436 14159 store_pipelocs(aTHX); /* will redo any earlier attempts */
22d4bb9c 14160
748a9306
LW
14161 return;
14162}
14163
f7ddb74a
JM
14164#if __CRTL_VER == 80200000
14165/* This missed getting in to the DECC SDK for 8.2 */
14166char *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 */
d584a1c6
JM
14174
14175/* Hack, use old stat() as fastest way of getting ino_t and device */
14176int decc$stat(const char *name, void * statbuf);
312ac60b
JM
14177#if !defined(__VAX) && __CRTL_VER >= 80200000
14178int decc$lstat(const char *name, void * statbuf);
14179#else
14180#define decc$lstat decc$stat
14181#endif
d584a1c6
JM
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
312ac60b
JM
14192int vms_fid_to_name(char * outname, int outlen,
14193 const char * name, int lstat_flag, mode_t * mode)
d584a1c6 14194{
312ac60b
JM
14195#pragma message save
14196#pragma message disable MISALGNDSTRCT
14197#pragma message disable MISALGNDMEM
14198#pragma member_alignment save
14199#pragma nomember_alignment
d584a1c6
JM
14200struct statbuf_t {
14201 char * st_dev;
b1a8dcd7 14202 unsigned short st_ino[3];
312ac60b 14203 unsigned short old_st_mode;
d584a1c6
JM
14204 unsigned long padl[30]; /* plenty of room */
14205} statbuf;
312ac60b
JM
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 }
d584a1c6 14286
d584a1c6 14287 if (sts == 0) {
312ac60b 14288 int vms_sts;
d584a1c6
JM
14289
14290 dvidsc.dsc$a_pointer=statbuf.st_dev;
d94c5a78 14291 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
d584a1c6
JM
14292
14293 specdsc.dsc$a_pointer = outname;
14294 specdsc.dsc$w_length = outlen-1;
14295
d94c5a78 14296 vms_sts = lib$fid_to_name
d584a1c6 14297 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
d94c5a78 14298 if ($VMS_STATUS_SUCCESS(vms_sts)) {
d584a1c6 14299 outname[specdsc.dsc$w_length] = 0;
312ac60b
JM
14300
14301 /* Return the mode */
14302 if (mode) {
14303 *mode = statbuf.old_st_mode;
14304 }
d584a1c6
JM
14305 return 0;
14306 }
14307 }
14308 return sts;
14309}
14310
14311
14312
f7ddb74a 14313static char *
5c4d031a 14314mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
d584a1c6 14315 int *utf8_fl)
f7ddb74a 14316{
d584a1c6
JM
14317 char * rslt = NULL;
14318
b1a8dcd7
JM
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 */
d584a1c6 14325 rslt = realpath(filespec, outbuf);
b1a8dcd7
JM
14326 }
14327#endif
d584a1c6
JM
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;
312ac60b 14334 mode_t my_mode;
d584a1c6
JM
14335
14336 /* Fall back to fid_to_name */
14337
14338 Newx(vms_spec, VMS_MAXRSS + 1, char);
14339
312ac60b 14340 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
4d8d3a9c 14341 if (sts == 0) {
d584a1c6
JM
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
4d8d3a9c
CB
14361 if (sts == 0) {
14362 int haslower = 0;
14363 const char *cp;
d584a1c6 14364
4d8d3a9c
CB
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;
d584a1c6 14368
f785e3a1
JM
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
4d8d3a9c 14383 /* The result is expected to be in UNIX format */
0e5ce2c7 14384 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
4d8d3a9c
CB
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 }
643f470b
CB
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 */
6fb6c614
JM
14403 rslt = int_rmsexpand(filespec, outbuf,
14404 NULL, 0, NULL, utf8_fl);
643f470b
CB
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
4148925f
JM
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,
312ac60b 14473 dir_name, 0, NULL);
4148925f
JM
14474
14475 if (sts == 0) {
14476 /* Now need to pathify it.
1fe570cc
JM
14477 char *tdir = int_pathify_dirspec(vms_dir_name,
14478 outbuf);
4148925f
JM
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 }
643f470b 14490 }
d584a1c6
JM
14491 Safefree(vms_spec);
14492 }
14493 return rslt;
f7ddb74a
JM
14494}
14495
b1a8dcd7
JM
14496static char *
14497mp_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
312ac60b 14506 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
cd43acd7
CB
14507 if (sts != 0) {
14508 return NULL;
14509 }
14510 else {
b1a8dcd7
JM
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) {
4d8d3a9c
CB
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;
b1a8dcd7 14537
4d8d3a9c
CB
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 }
b1a8dcd7
JM
14547 }
14548 }
14549 return outbuf;
14550}
14551
14552
f7ddb74a
JM
14553/*}}}*/
14554/* External entry points */
360732b5
JM
14555char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14556{ return do_vms_realpath(filespec, outbuf, utf8_fl); }
f7ddb74a 14557
b1a8dcd7
JM
14558char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14559{ return do_vms_realname(filespec, outbuf, utf8_fl); }
f7ddb74a 14560
f7ddb74a
JM
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 */
14567int do_vms_case_tolerant(void)
14568{
14569 return vms_process_case_tolerant;
14570}
14571/*}}}*/
14572/* External entry points */
b1a8dcd7 14573#if __CRTL_VER >= 70301000 && !defined(__VAX)
f7ddb74a
JM
14574int Perl_vms_case_tolerant(void)
14575{ return do_vms_case_tolerant(); }
14576#else
14577int Perl_vms_case_tolerant(void)
14578{ return vms_process_case_tolerant; }
14579#endif
14580
14581
14582 /* Start of DECC RTL Feature handling */
14583
14584static 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
14614static 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
f7ddb74a
JM
14652/* C RTL Feature settings */
14653
14654static 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;
f7ddb74a
JM
14661 char* str;
14662 char val_str[10];
3c841f20 14663#if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
f7ddb74a
JM
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;
3c841f20 14668#endif
f7ddb74a 14669
9c1171d1
JM
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)) {
b53f3677 14674 val_str[0] = _toupper(val_str[0]);
9c1171d1
JM
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
b53f3677
JM
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
38a44b82 14713 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
360732b5
JM
14714 vms_vtf7_filenames = 0;
14715 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14716 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14717 val_str[0] = _toupper(val_str[0]);
360732b5
JM
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
e0e5e8d6 14724 /* unlink all versions on unlink() or rename() */
d584a1c6 14725 vms_unlink_all_versions = 0;
e0e5e8d6
JM
14726 status = sys_trnlnm
14727 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14728 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14729 val_str[0] = _toupper(val_str[0]);
e0e5e8d6
JM
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
360732b5
JM
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)) {
360732b5
JM
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);
e0e5e8d6 14748 vms_unlink_all_versions = 1;
1a3aec58 14749 vms_posix_exit = 1;
360732b5
JM
14750 }
14751#endif
9c1171d1 14752
2497a41f
JM
14753 /* hacks to see if known bugs are still present for testing */
14754
2497a41f 14755 /* PCP mode requires creating /dev/null special device file */
2623a4a6 14756 decc_bug_devnull = 0;
2497a41f
JM
14757 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14758 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14759 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14760 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14761 decc_bug_devnull = 1;
682e4b71
JM
14762 else
14763 decc_bug_devnull = 0;
2497a41f
JM
14764 }
14765
2497a41f
JM
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)) {
b53f3677 14770 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
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
f7ddb74a
JM
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");
b53f3677 14793 decc_efs_charset_index = s;
f7ddb74a
JM
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);
1a3aec58 14803 if (decc_filename_unix_report > 0) {
f7ddb74a 14804 decc_filename_unix_report = 1;
1a3aec58
JM
14805 vms_posix_exit = 1;
14806 }
f7ddb74a
JM
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
f7ddb74a
JM
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
28ff9735 14898#if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
f7ddb74a
JM
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
1a3aec58
JM
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)) {
b53f3677 14919 val_str[0] = _toupper(val_str[0]);
1a3aec58
JM
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
f7ddb74a
JM
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
f7ddb74a
JM
14934#pragma nostandard
14935#pragma extern_model save
14936#pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
f7ddb74a 14937 const __align (LONGWORD) int spare[8] = {0};
dfffea70
CB
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
f7ddb74a 14944#endif
dfffea70
CB
14945#endif /* __DECC */
14946
f7ddb74a
JM
14947const 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*/
14953int 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
748a9306 14964/* End of vms.c */