This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Use PL_parser->lex_shared instead of Sv[IN]VX(PL_linestr)
[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>
3ce52d1b
CB
29#if __CRTL_VER < 70300000
30/* needed for home-rolled utime() */
748a9306 31#include <atrdef.h>
3ce52d1b
CB
32#include <fibdef.h>
33#endif
a0d0e21e 34#include <chpdef.h>
8fde5078 35#include <clidef.h>
a3e9d8c9 36#include <climsgdef.h>
cd1191f1 37#include <dcdef.h>
a0d0e21e 38#include <descrip.h>
22d4bb9c 39#include <devdef.h>
a0d0e21e
LW
40#include <dvidef.h>
41#include <float.h>
42#include <fscndef.h>
43#include <iodef.h>
44#include <jpidef.h>
61bb5906 45#include <kgbdef.h>
f675dbe5 46#include <libclidef.h>
a0d0e21e
LW
47#include <libdef.h>
48#include <lib$routines.h>
49#include <lnmdef.h>
4fdf8f88 50#include <ossdef.h>
f7ddb74a
JM
51#if __CRTL_VER >= 70301000 && !defined(__VAX)
52#include <ppropdef.h>
53#endif
748a9306 54#include <prvdef.h>
a0d0e21e
LW
55#include <psldef.h>
56#include <rms.h>
57#include <shrdef.h>
58#include <ssdef.h>
59#include <starlet.h>
f86702cc 60#include <strdef.h>
61#include <str$routines.h>
a0d0e21e 62#include <syidef.h>
748a9306
LW
63#include <uaidef.h>
64#include <uicdef.h>
2fbb330f 65#include <stsdef.h>
cfcfe586
JM
66#include <efndef.h>
67#define NO_EFN EFN$C_ENF
a0d0e21e 68
f7ddb74a
JM
69#if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
70int decc$feature_get_index(const char *name);
71char* decc$feature_get_name(int index);
72int decc$feature_get_value(int index, int mode);
73int decc$feature_set_value(int index, int mode, int value);
74#else
75#include <unixlib.h>
76#endif
77
cfcfe586
JM
78#pragma member_alignment save
79#pragma nomember_alignment longword
80struct item_list_3 {
81 unsigned short len;
82 unsigned short code;
83 void * bufadr;
84 unsigned short * retadr;
85};
86#pragma member_alignment restore
87
7a7fd8e0 88#if __CRTL_VER >= 70300000 && !defined(__VAX)
f7ddb74a
JM
89
90static int set_feature_default(const char *name, int value)
91{
92 int status;
93 int index;
94
95 index = decc$feature_get_index(name);
96
97 status = decc$feature_set_value(index, 1, value);
98 if (index == -1 || (status == -1)) {
99 return -1;
100 }
101
102 status = decc$feature_get_value(index, 1);
103 if (status != value) {
104 return -1;
105 }
106
107return 0;
108}
109#endif
f7ddb74a 110
740ce14c 111/* Older versions of ssdef.h don't have these */
112#ifndef SS$_INVFILFOROP
113# define SS$_INVFILFOROP 3930
114#endif
115#ifndef SS$_NOSUCHOBJECT
b7ae7a0d 116# define SS$_NOSUCHOBJECT 2696
117#endif
118
a15cef0c
CB
119/* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
120#define PERLIO_NOT_STDIO 0
121
2497a41f 122/* Don't replace system definitions of vfork, getenv, lstat, and stat,
aa689395 123 * code below needs to get to the underlying CRTL routines. */
124#define DONT_MASK_RTL_CALLS
a0d0e21e
LW
125#include "EXTERN.h"
126#include "perl.h"
748a9306 127#include "XSUB.h"
3eeba6fb
CB
128/* Anticipating future expansion in lexical warnings . . . */
129#ifndef WARN_INTERNAL
130# define WARN_INTERNAL WARN_MISC
131#endif
a0d0e21e 132
988c775c
JM
133#ifdef VMS_LONGNAME_SUPPORT
134#include <libfildef.h>
135#endif
136
58472d87
CB
137#if !defined(__VAX) && __CRTL_VER >= 80200000
138#ifdef lstat
139#undef lstat
140#endif
141#else
142#ifdef lstat
143#undef lstat
144#endif
145#define lstat(_x, _y) stat(_x, _y)
146#endif
147
5f1992ed
CB
148/* Routine to create a decterm for use with the Perl debugger */
149/* No headers, this information was found in the Programming Concepts Manual */
150
8cb5d3d5 151static int (*decw_term_port)
5f1992ed
CB
152 (const struct dsc$descriptor_s * display,
153 const struct dsc$descriptor_s * setup_file,
154 const struct dsc$descriptor_s * customization,
155 struct dsc$descriptor_s * result_device_name,
156 unsigned short * result_device_name_length,
157 void * controller,
158 void * char_buffer,
8cb5d3d5 159 void * char_change_buffer) = 0;
22d4bb9c 160
c07a80fd 161/* gcc's header files don't #define direct access macros
162 * corresponding to VAXC's variant structs */
163#ifdef __GNUC__
482b294c 164# define uic$v_format uic$r_uic_form.uic$v_format
165# define uic$v_group uic$r_uic_form.uic$v_group
166# define uic$v_member uic$r_uic_form.uic$v_member
c07a80fd 167# define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
168# define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
169# define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
170# define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
171#endif
172
c645ec3f
GS
173#if defined(NEED_AN_H_ERRNO)
174dEXT int h_errno;
175#endif
c07a80fd 176
81bca5f9 177#if defined(__DECC) || defined(__DECCXX)
f7ddb74a
JM
178#pragma member_alignment save
179#pragma nomember_alignment longword
180#pragma message save
181#pragma message disable misalgndmem
182#endif
a0d0e21e
LW
183struct itmlst_3 {
184 unsigned short int buflen;
185 unsigned short int itmcode;
186 void *bufadr;
748a9306 187 unsigned short int *retlen;
a0d0e21e 188};
657054d4
JM
189
190struct filescan_itmlst_2 {
191 unsigned short length;
192 unsigned short itmcode;
193 char * component;
194};
195
dca5a913
JM
196struct vs_str_st {
197 unsigned short length;
7202b047
CB
198 char str[VMS_MAXRSS];
199 unsigned short pad; /* for longword struct alignment */
dca5a913
JM
200};
201
81bca5f9 202#if defined(__DECC) || defined(__DECCXX)
f7ddb74a
JM
203#pragma message restore
204#pragma member_alignment restore
205#endif
a0d0e21e 206
360732b5
JM
207#define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
208#define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
209#define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
210#define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
211#define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
212#define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
b1a8dcd7 213#define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
360732b5
JM
214#define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
215#define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
f7ddb74a 216#define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
4b19af01
CB
217#define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
218#define getredirection(a,b) mp_getredirection(aTHX_ a,b)
219
360732b5
JM
220static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
221static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
222static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
223static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
f7ddb74a 224
6fb6c614
JM
225static char * int_rmsexpand_vms(
226 const char * filespec, char * outbuf, unsigned opts);
227static char * int_rmsexpand_tovms(
228 const char * filespec, char * outbuf, unsigned opts);
df278665
JM
229static char *int_tovmsspec
230 (const char *path, char *buf, int dir_flag, int * utf8_flag);
a979ce91 231static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
0e5ce2c7 232static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
4846f1d7 233static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
df278665 234
0e06870b
CB
235/* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
236#define PERL_LNM_MAX_ALLOWED_INDEX 127
237
2d9f3838
CB
238/* OpenVMS User's Guide says at least 9 iterative translations will be performed,
239 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
240 * the Perl facility.
241 */
242#define PERL_LNM_MAX_ITER 10
243
2497a41f
JM
244 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
245#if __CRTL_VER >= 70302000 && !defined(__VAX)
246#define MAX_DCL_SYMBOL (8192)
247#define MAX_DCL_LINE_LENGTH (4096 - 4)
248#else
249#define MAX_DCL_SYMBOL (1024)
250#define MAX_DCL_LINE_LENGTH (1024 - 4)
251#endif
ff7adb52 252
01b8edb6 253static char *__mystrtolower(char *str)
254{
255 if (str) for (; *str; ++str) *str= tolower(*str);
256 return str;
257}
258
f675dbe5
CB
259static struct dsc$descriptor_s fildevdsc =
260 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
261static struct dsc$descriptor_s crtlenvdsc =
262 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
263static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
264static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
265static struct dsc$descriptor_s **env_tables = defenv;
266static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
267
93948341
CB
268/* True if we shouldn't treat barewords as logicals during directory */
269/* munching */
270static int no_translate_barewords;
271
f7ddb74a
JM
272/* DECC Features that may need to affect how Perl interprets
273 * displays filename information
274 */
275static int decc_disable_to_vms_logname_translation = 1;
276static int decc_disable_posix_root = 1;
277int decc_efs_case_preserve = 0;
278static int decc_efs_charset = 0;
b53f3677 279static int decc_efs_charset_index = -1;
f7ddb74a
JM
280static int decc_filename_unix_no_version = 0;
281static int decc_filename_unix_only = 0;
282int decc_filename_unix_report = 0;
283int decc_posix_compliant_pathnames = 0;
284int decc_readdir_dropdotnotype = 0;
285static int vms_process_case_tolerant = 1;
360732b5
JM
286int vms_vtf7_filenames = 0;
287int gnv_unix_shell = 0;
e0e5e8d6 288static int vms_unlink_all_versions = 0;
1a3aec58 289static int vms_posix_exit = 0;
f7ddb74a 290
2497a41f 291/* bug workarounds if needed */
682e4b71 292int decc_bug_devnull = 1;
2497a41f 293int decc_dir_barename = 0;
b53f3677 294int vms_bug_stat_filename = 0;
2497a41f 295
9c1171d1 296static int vms_debug_on_exception = 0;
b53f3677
JM
297static int vms_debug_fileify = 0;
298
299/* Simple logical name translation */
300static int simple_trnlnm
301 (const char * logname,
302 char * value,
303 int value_len)
304{
305 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
306 const unsigned long attr = LNM$M_CASE_BLIND;
307 struct dsc$descriptor_s name_dsc;
308 int status;
309 unsigned short result;
310 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
311 {0, 0, 0, 0}};
312
313 name_dsc.dsc$w_length = strlen(logname);
314 name_dsc.dsc$a_pointer = (char *)logname;
315 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
316 name_dsc.dsc$b_class = DSC$K_CLASS_S;
317
318 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
319
320 if ($VMS_STATUS_SUCCESS(status)) {
321
322 /* Null terminate and return the string */
323 /*--------------------------------------*/
324 value[result] = 0;
325 return result;
326 }
327
328 return 0;
329}
330
9c1171d1 331
f7ddb74a
JM
332/* Is this a UNIX file specification?
333 * No longer a simple check with EFS file specs
334 * For now, not a full check, but need to
335 * handle POSIX ^UP^ specifications
336 * Fixing to handle ^/ cases would require
337 * changes to many other conversion routines.
338 */
339
657054d4 340static int is_unix_filespec(const char *path)
f7ddb74a
JM
341{
342int ret_val;
343const char * pch1;
344
345 ret_val = 0;
346 if (strncmp(path,"\"^UP^",5) != 0) {
347 pch1 = strchr(path, '/');
348 if (pch1 != NULL)
349 ret_val = 1;
350 else {
351
352 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
353 if (decc_filename_unix_report || decc_filename_unix_only) {
354 if (strcmp(path,".") == 0)
355 ret_val = 1;
356 }
357 }
358 }
359 return ret_val;
360}
361
360732b5
JM
362/* This routine converts a UCS-2 character to be VTF-7 encoded.
363 */
364
365static void ucs2_to_vtf7
366 (char *outspec,
367 unsigned long ucs2_char,
368 int * output_cnt)
369{
370unsigned char * ucs_ptr;
371int hex;
372
373 ucs_ptr = (unsigned char *)&ucs2_char;
374
375 outspec[0] = '^';
376 outspec[1] = 'U';
377 hex = (ucs_ptr[1] >> 4) & 0xf;
378 if (hex < 0xA)
379 outspec[2] = hex + '0';
380 else
381 outspec[2] = (hex - 9) + 'A';
382 hex = ucs_ptr[1] & 0xF;
383 if (hex < 0xA)
384 outspec[3] = hex + '0';
385 else {
386 outspec[3] = (hex - 9) + 'A';
387 }
388 hex = (ucs_ptr[0] >> 4) & 0xf;
389 if (hex < 0xA)
390 outspec[4] = hex + '0';
391 else
392 outspec[4] = (hex - 9) + 'A';
393 hex = ucs_ptr[1] & 0xF;
394 if (hex < 0xA)
395 outspec[5] = hex + '0';
396 else {
397 outspec[5] = (hex - 9) + 'A';
398 }
399 *output_cnt = 6;
400}
401
402
403/* This handles the conversion of a UNIX extended character set to a ^
404 * escaped VMS character.
405 * in a UNIX file specification.
406 *
407 * The output count variable contains the number of characters added
408 * to the output string.
409 *
410 * The return value is the number of characters read from the input string
411 */
412static int copy_expand_unix_filename_escape
413 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
414{
415int count;
360732b5
JM
416int utf8_flag;
417
418 utf8_flag = 0;
419 if (utf8_fl)
420 utf8_flag = *utf8_fl;
421
422 count = 0;
423 *output_cnt = 0;
424 if (*inspec >= 0x80) {
425 if (utf8_fl && vms_vtf7_filenames) {
426 unsigned long ucs_char;
427
428 ucs_char = 0;
429
430 if ((*inspec & 0xE0) == 0xC0) {
431 /* 2 byte Unicode */
432 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
433 if (ucs_char >= 0x80) {
434 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
435 return 2;
436 }
437 } else if ((*inspec & 0xF0) == 0xE0) {
438 /* 3 byte Unicode */
439 ucs_char = ((inspec[0] & 0xF) << 12) +
440 ((inspec[1] & 0x3f) << 6) +
441 (inspec[2] & 0x3f);
442 if (ucs_char >= 0x800) {
443 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
444 return 3;
445 }
446
447#if 0 /* I do not see longer sequences supported by OpenVMS */
448 /* Maybe some one can fix this later */
449 } else if ((*inspec & 0xF8) == 0xF0) {
450 /* 4 byte Unicode */
451 /* UCS-4 to UCS-2 */
452 } else if ((*inspec & 0xFC) == 0xF8) {
453 /* 5 byte Unicode */
454 /* UCS-4 to UCS-2 */
455 } else if ((*inspec & 0xFE) == 0xFC) {
456 /* 6 byte Unicode */
457 /* UCS-4 to UCS-2 */
458#endif
459 }
460 }
461
38a44b82 462 /* High bit set, but not a Unicode character! */
360732b5
JM
463
464 /* Non printing DECMCS or ISO Latin-1 character? */
b931d62c
CB
465 if ((unsigned char)*inspec <= 0x9F) {
466 int hex;
360732b5
JM
467 outspec[0] = '^';
468 outspec++;
469 hex = (*inspec >> 4) & 0xF;
470 if (hex < 0xA)
471 outspec[1] = hex + '0';
472 else {
473 outspec[1] = (hex - 9) + 'A';
474 }
475 hex = *inspec & 0xF;
476 if (hex < 0xA)
477 outspec[2] = hex + '0';
478 else {
479 outspec[2] = (hex - 9) + 'A';
480 }
481 *output_cnt = 3;
482 return 1;
b931d62c 483 } else if ((unsigned char)*inspec == 0xA0) {
360732b5
JM
484 outspec[0] = '^';
485 outspec[1] = 'A';
486 outspec[2] = '0';
487 *output_cnt = 3;
488 return 1;
b931d62c 489 } else if ((unsigned char)*inspec == 0xFF) {
360732b5
JM
490 outspec[0] = '^';
491 outspec[1] = 'F';
492 outspec[2] = 'F';
493 *output_cnt = 3;
494 return 1;
495 }
496 *outspec = *inspec;
497 *output_cnt = 1;
498 return 1;
499 }
500
501 /* Is this a macro that needs to be passed through?
502 * Macros start with $( and an alpha character, followed
503 * by a string of alpha numeric characters ending with a )
504 * If this does not match, then encode it as ODS-5.
505 */
506 if ((inspec[0] == '$') && (inspec[1] == '(')) {
507 int tcnt;
508
509 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
510 tcnt = 3;
511 outspec[0] = inspec[0];
512 outspec[1] = inspec[1];
513 outspec[2] = inspec[2];
514
515 while(isalnum(inspec[tcnt]) ||
516 (inspec[2] == '.') || (inspec[2] == '_')) {
517 outspec[tcnt] = inspec[tcnt];
518 tcnt++;
519 }
520 if (inspec[tcnt] == ')') {
521 outspec[tcnt] = inspec[tcnt];
522 tcnt++;
523 *output_cnt = tcnt;
524 return tcnt;
525 }
526 }
527 }
528
529 switch (*inspec) {
530 case 0x7f:
531 outspec[0] = '^';
532 outspec[1] = '7';
533 outspec[2] = 'F';
534 *output_cnt = 3;
535 return 1;
536 break;
537 case '?':
538 if (decc_efs_charset == 0)
539 outspec[0] = '%';
540 else
541 outspec[0] = '?';
542 *output_cnt = 1;
543 return 1;
544 break;
545 case '.':
546 case '~':
547 case '!':
548 case '#':
549 case '&':
550 case '\'':
551 case '`':
552 case '(':
553 case ')':
554 case '+':
555 case '@':
556 case '{':
557 case '}':
558 case ',':
559 case ';':
560 case '[':
561 case ']':
562 case '%':
563 case '^':
449de3c2 564 case '\\':
adc11f0b
CB
565 /* Don't escape again if following character is
566 * already something we escape.
567 */
449de3c2 568 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
adc11f0b
CB
569 *outspec = *inspec;
570 *output_cnt = 1;
571 return 1;
572 break;
573 }
574 /* But otherwise fall through and escape it. */
360732b5
JM
575 case '=':
576 /* Assume that this is to be escaped */
577 outspec[0] = '^';
578 outspec[1] = *inspec;
579 *output_cnt = 2;
580 return 1;
581 break;
582 case ' ': /* space */
583 /* Assume that this is to be escaped */
584 outspec[0] = '^';
585 outspec[1] = '_';
586 *output_cnt = 2;
587 return 1;
588 break;
589 default:
590 *outspec = *inspec;
591 *output_cnt = 1;
592 return 1;
593 break;
594 }
c11536f5 595 return 0;
360732b5
JM
596}
597
598
657054d4
JM
599/* This handles the expansion of a '^' prefix to the proper character
600 * in a UNIX file specification.
601 *
602 * The output count variable contains the number of characters added
603 * to the output string.
604 *
605 * The return value is the number of characters read from the input
606 * string
607 */
608static int copy_expand_vms_filename_escape
609 (char *outspec, const char *inspec, int *output_cnt)
610{
611int count;
612int scnt;
613
614 count = 0;
615 *output_cnt = 0;
616 if (*inspec == '^') {
617 inspec++;
618 switch (*inspec) {
adc11f0b
CB
619 /* Spaces and non-trailing dots should just be passed through,
620 * but eat the escape character.
621 */
657054d4 622 case '.':
657054d4 623 *outspec = *inspec;
adc11f0b
CB
624 count += 2;
625 (*output_cnt)++;
657054d4
JM
626 break;
627 case '_': /* space */
628 *outspec = ' ';
adc11f0b 629 count += 2;
657054d4
JM
630 (*output_cnt)++;
631 break;
adc11f0b
CB
632 case '^':
633 /* Hmm. Better leave the escape escaped. */
634 outspec[0] = '^';
635 outspec[1] = '^';
636 count += 2;
637 (*output_cnt) += 2;
638 break;
360732b5 639 case 'U': /* Unicode - FIX-ME this is wrong. */
657054d4
JM
640 inspec++;
641 count++;
642 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
643 if (scnt == 4) {
2f4077ca
JM
644 unsigned int c1, c2;
645 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
9960802c
NK
646 outspec[0] = c1 & 0xff;
647 outspec[1] = c2 & 0xff;
657054d4
JM
648 if (scnt > 1) {
649 (*output_cnt) += 2;
650 count += 4;
651 }
652 }
653 else {
654 /* Error - do best we can to continue */
655 *outspec = 'U';
656 outspec++;
657 (*output_cnt++);
658 *outspec = *inspec;
659 count++;
660 (*output_cnt++);
661 }
662 break;
663 default:
664 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
665 if (scnt == 2) {
666 /* Hex encoded */
2f4077ca
JM
667 unsigned int c1;
668 scnt = sscanf(inspec, "%2x", &c1);
669 outspec[0] = c1 & 0xff;
657054d4
JM
670 if (scnt > 0) {
671 (*output_cnt++);
672 count += 2;
673 }
674 }
675 else {
676 *outspec = *inspec;
677 count++;
678 (*output_cnt++);
679 }
680 }
681 }
682 else {
683 *outspec = *inspec;
684 count++;
685 (*output_cnt)++;
686 }
687 return count;
688}
689
657054d4
JM
690/* vms_split_path - Verify that the input file specification is a
691 * VMS format file specification, and provide pointers to the components of
692 * it. With EFS format filenames, this is virtually the only way to
693 * parse a VMS path specification into components.
694 *
695 * If the sum of the components do not add up to the length of the
696 * string, then the passed file specification is probably a UNIX style
697 * path.
698 */
699static int vms_split_path
360732b5 700 (const char * path,
dca5a913 701 char * * volume,
657054d4 702 int * vol_len,
dca5a913 703 char * * root,
657054d4 704 int * root_len,
dca5a913 705 char * * dir,
657054d4 706 int * dir_len,
dca5a913 707 char * * name,
657054d4 708 int * name_len,
dca5a913 709 char * * ext,
657054d4 710 int * ext_len,
dca5a913 711 char * * version,
657054d4
JM
712 int * ver_len)
713{
714struct dsc$descriptor path_desc;
715int status;
716unsigned long flags;
717int ret_stat;
718struct filescan_itmlst_2 item_list[9];
719const int filespec = 0;
720const int nodespec = 1;
721const int devspec = 2;
722const int rootspec = 3;
723const int dirspec = 4;
724const int namespec = 5;
725const int typespec = 6;
726const int verspec = 7;
727
728 /* Assume the worst for an easy exit */
729 ret_stat = -1;
730 *volume = NULL;
731 *vol_len = 0;
732 *root = NULL;
733 *root_len = 0;
734 *dir = NULL;
657054d4
JM
735 *name = NULL;
736 *name_len = 0;
737 *ext = NULL;
738 *ext_len = 0;
739 *version = NULL;
740 *ver_len = 0;
741
742 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
743 path_desc.dsc$w_length = strlen(path);
744 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
745 path_desc.dsc$b_class = DSC$K_CLASS_S;
746
747 /* Get the total length, if it is shorter than the string passed
748 * then this was probably not a VMS formatted file specification
749 */
750 item_list[filespec].itmcode = FSCN$_FILESPEC;
751 item_list[filespec].length = 0;
752 item_list[filespec].component = NULL;
753
754 /* If the node is present, then it gets considered as part of the
755 * volume name to hopefully make things simple.
756 */
757 item_list[nodespec].itmcode = FSCN$_NODE;
758 item_list[nodespec].length = 0;
759 item_list[nodespec].component = NULL;
760
761 item_list[devspec].itmcode = FSCN$_DEVICE;
762 item_list[devspec].length = 0;
763 item_list[devspec].component = NULL;
764
765 /* root is a special case, adding it to either the directory or
94ae10c0 766 * the device components will probably complicate things for the
657054d4
JM
767 * callers of this routine, so leave it separate.
768 */
769 item_list[rootspec].itmcode = FSCN$_ROOT;
770 item_list[rootspec].length = 0;
771 item_list[rootspec].component = NULL;
772
773 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
774 item_list[dirspec].length = 0;
775 item_list[dirspec].component = NULL;
776
777 item_list[namespec].itmcode = FSCN$_NAME;
778 item_list[namespec].length = 0;
779 item_list[namespec].component = NULL;
780
781 item_list[typespec].itmcode = FSCN$_TYPE;
782 item_list[typespec].length = 0;
783 item_list[typespec].component = NULL;
784
785 item_list[verspec].itmcode = FSCN$_VERSION;
786 item_list[verspec].length = 0;
787 item_list[verspec].component = NULL;
788
789 item_list[8].itmcode = 0;
790 item_list[8].length = 0;
791 item_list[8].component = NULL;
792
7566800d 793 status = sys$filescan
657054d4
JM
794 ((const struct dsc$descriptor_s *)&path_desc, item_list,
795 &flags, NULL, NULL);
360732b5 796 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
657054d4
JM
797
798 /* If we parsed it successfully these two lengths should be the same */
799 if (path_desc.dsc$w_length != item_list[filespec].length)
800 return ret_stat;
801
802 /* If we got here, then it is a VMS file specification */
803 ret_stat = 0;
804
805 /* set the volume name */
806 if (item_list[nodespec].length > 0) {
807 *volume = item_list[nodespec].component;
808 *vol_len = item_list[nodespec].length + item_list[devspec].length;
809 }
810 else {
811 *volume = item_list[devspec].component;
812 *vol_len = item_list[devspec].length;
813 }
814
815 *root = item_list[rootspec].component;
816 *root_len = item_list[rootspec].length;
817
818 *dir = item_list[dirspec].component;
819 *dir_len = item_list[dirspec].length;
820
821 /* Now fun with versions and EFS file specifications
822 * The parser can not tell the difference when a "." is a version
823 * delimiter or a part of the file specification.
824 */
825 if ((decc_efs_charset) &&
826 (item_list[verspec].length > 0) &&
827 (item_list[verspec].component[0] == '.')) {
828 *name = item_list[namespec].component;
829 *name_len = item_list[namespec].length + item_list[typespec].length;
830 *ext = item_list[verspec].component;
831 *ext_len = item_list[verspec].length;
832 *version = NULL;
833 *ver_len = 0;
834 }
835 else {
836 *name = item_list[namespec].component;
837 *name_len = item_list[namespec].length;
838 *ext = item_list[typespec].component;
839 *ext_len = item_list[typespec].length;
840 *version = item_list[verspec].component;
841 *ver_len = item_list[verspec].length;
842 }
843 return ret_stat;
844}
845
df278665
JM
846/* Routine to determine if the file specification ends with .dir */
847static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
848
849 /* e_len must be 4, and version must be <= 2 characters */
850 if (e_len != 4 || vs_len > 2)
851 return 0;
852
853 /* If a version number is present, it needs to be one */
854 if ((vs_len == 2) && (vs_spec[1] != '1'))
855 return 0;
856
857 /* Look for the DIR on the extension */
858 if (vms_process_case_tolerant) {
859 if ((toupper(e_spec[1]) == 'D') &&
860 (toupper(e_spec[2]) == 'I') &&
861 (toupper(e_spec[3]) == 'R')) {
862 return 1;
863 }
864 } else {
865 /* Directory extensions are supposed to be in upper case only */
866 /* I would not be surprised if this rule can not be enforced */
867 /* if and when someone fully debugs the case sensitive mode */
868 if ((e_spec[1] == 'D') &&
869 (e_spec[2] == 'I') &&
870 (e_spec[3] == 'R')) {
871 return 1;
872 }
873 }
874 return 0;
875}
876
f7ddb74a 877
fa537f88
CB
878/* my_maxidx
879 * Routine to retrieve the maximum equivalence index for an input
880 * logical name. Some calls to this routine have no knowledge if
881 * the variable is a logical or not. So on error we return a max
882 * index of zero.
883 */
f7ddb74a 884/*{{{int my_maxidx(const char *lnm) */
fa537f88 885static int
f7ddb74a 886my_maxidx(const char *lnm)
fa537f88
CB
887{
888 int status;
889 int midx;
890 int attr = LNM$M_CASE_BLIND;
891 struct dsc$descriptor lnmdsc;
892 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
893 {0, 0, 0, 0}};
894
895 lnmdsc.dsc$w_length = strlen(lnm);
896 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
897 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
f7ddb74a 898 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
fa537f88
CB
899
900 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
901 if ((status & 1) == 0)
902 midx = 0;
903
904 return (midx);
905}
906/*}}}*/
907
f675dbe5 908/*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
c07a80fd 909int
fd8cd3a3 910Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
f675dbe5 911 struct dsc$descriptor_s **tabvec, unsigned long int flags)
748a9306 912{
f7ddb74a
JM
913 const char *cp1;
914 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
f675dbe5 915 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
748a9306 916 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
fa537f88 917 int midx;
f675dbe5
CB
918 unsigned char acmode;
919 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
920 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
921 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
922 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
748a9306 923 {0, 0, 0, 0}};
f675dbe5 924 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
fd8cd3a3
DS
925#if defined(PERL_IMPLICIT_CONTEXT)
926 pTHX = NULL;
fd8cd3a3
DS
927 if (PL_curinterp) {
928 aTHX = PERL_GET_INTERP;
cc077a9f 929 } else {
fd8cd3a3 930 aTHX = NULL;
cc077a9f
HM
931 }
932#endif
748a9306 933
fa537f88 934 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
b7ae7a0d 935 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
936 }
f7ddb74a 937 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
938 *cp2 = _toupper(*cp1);
939 if (cp1 - lnm > LNM$C_NAMLENGTH) {
940 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
941 return 0;
942 }
943 }
944 lnmdsc.dsc$w_length = cp1 - lnm;
945 lnmdsc.dsc$a_pointer = uplnm;
fd7385b9 946 uplnm[lnmdsc.dsc$w_length] = '\0';
f675dbe5
CB
947 secure = flags & PERL__TRNENV_SECURE;
948 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
949 if (!tabvec || !*tabvec) tabvec = env_tables;
950
951 for (curtab = 0; tabvec[curtab]; curtab++) {
952 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
953 if (!ivenv && !secure) {
4e0c9737 954 char *eq;
f675dbe5
CB
955 int i;
956 if (!environ) {
957 ivenv = 1;
ebd4d70b
JM
958#if defined(PERL_IMPLICIT_CONTEXT)
959 if (aTHX == NULL) {
960 fprintf(stderr,
873f5ddf 961 "Can't read CRTL environ\n");
ebd4d70b
JM
962 } else
963#endif
964 Perl_warn(aTHX_ "Can't read CRTL environ\n");
f675dbe5
CB
965 continue;
966 }
967 retsts = SS$_NOLOGNAM;
968 for (i = 0; environ[i]; i++) {
969 if ((eq = strchr(environ[i],'=')) &&
299d126a 970 lnmdsc.dsc$w_length == (eq - environ[i]) &&
f675dbe5
CB
971 !strncmp(environ[i],uplnm,eq - environ[i])) {
972 eq++;
973 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
974 if (!eqvlen) continue;
975 retsts = SS$_NORMAL;
976 break;
977 }
978 }
979 if (retsts != SS$_NOLOGNAM) break;
980 }
981 }
982 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
983 !str$case_blind_compare(&tmpdsc,&clisym)) {
984 if (!ivsym && !secure) {
985 unsigned short int deflen = LNM$C_NAMLENGTH;
986 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
94ae10c0 987 /* dynamic dsc to accommodate possible long value */
ebd4d70b 988 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
f675dbe5
CB
989 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
990 if (retsts & 1) {
2497a41f 991 if (eqvlen > MAX_DCL_SYMBOL) {
f675dbe5 992 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
2497a41f 993 eqvlen = MAX_DCL_SYMBOL;
cc077a9f
HM
994 /* Special hack--we might be called before the interpreter's */
995 /* fully initialized, in which case either thr or PL_curcop */
996 /* might be bogus. We have to check, since ckWARN needs them */
997 /* both to be valid if running threaded */
8a646e0b
JM
998#if defined(PERL_IMPLICIT_CONTEXT)
999 if (aTHX == NULL) {
1000 fprintf(stderr,
873f5ddf 1001 "Value of CLI symbol \"%s\" too long",lnm);
8a646e0b
JM
1002 } else
1003#endif
cc077a9f 1004 if (ckWARN(WARN_MISC)) {
f98bc0c6 1005 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
cc077a9f 1006 }
f675dbe5
CB
1007 }
1008 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1009 }
ebd4d70b 1010 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
f675dbe5
CB
1011 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1012 if (retsts == LIB$_NOSUCHSYM) continue;
1013 break;
1014 }
1015 }
1016 else if (!ivlnm) {
843027b0 1017 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
f7ddb74a
JM
1018 midx = my_maxidx(lnm);
1019 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1020 lnmlst[1].bufadr = cp2;
fa537f88
CB
1021 eqvlen = 0;
1022 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1023 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1024 if (retsts == SS$_NOLOGNAM) break;
1025 /* PPFs have a prefix */
1026 if (
fd7385b9 1027#if INTSIZE == 4
fa537f88 1028 *((int *)uplnm) == *((int *)"SYS$") &&
fd7385b9 1029#endif
fa537f88
CB
1030 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1031 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1032 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1033 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1034 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
18a3d61e 1035 memmove(eqv,eqv+4,eqvlen-4);
fa537f88
CB
1036 eqvlen -= 4;
1037 }
f7ddb74a
JM
1038 cp2 += eqvlen;
1039 *cp2 = '\0';
fa537f88
CB
1040 }
1041 if ((retsts == SS$_IVLOGNAM) ||
1042 (retsts == SS$_NOLOGNAM)) { continue; }
fd7385b9 1043 }
fa537f88 1044 else {
fa537f88
CB
1045 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1046 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1047 if (retsts == SS$_NOLOGNAM) continue;
1048 eqv[eqvlen] = '\0';
1049 }
1050 eqvlen = strlen(eqv);
f675dbe5
CB
1051 break;
1052 }
c07a80fd 1053 }
f675dbe5
CB
1054 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1055 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1056 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1057 retsts == SS$_NOLOGNAM) {
1058 set_errno(EINVAL); set_vaxc_errno(retsts);
748a9306 1059 }
ebd4d70b 1060 else _ckvmssts_noperl(retsts);
f675dbe5
CB
1061 return 0;
1062} /* end of vmstrnenv */
1063/*}}}*/
c07a80fd 1064
f675dbe5
CB
1065/*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1066/* Define as a function so we can access statics. */
4b19af01 1067int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
f675dbe5 1068{
8a646e0b
JM
1069 int flags = 0;
1070
1071#if defined(PERL_IMPLICIT_CONTEXT)
1072 if (aTHX != NULL)
1073#endif
f675dbe5 1074#ifdef SECURE_INTERNAL_GETENV
8a646e0b
JM
1075 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1076 PERL__TRNENV_SECURE : 0;
f675dbe5 1077#endif
8a646e0b
JM
1078
1079 return vmstrnenv(lnm, eqv, idx, fildev, flags);
f675dbe5
CB
1080}
1081/*}}}*/
a0d0e21e
LW
1082
1083/* my_getenv
61bb5906
CB
1084 * Note: Uses Perl temp to store result so char * can be returned to
1085 * caller; this pointer will be invalidated at next Perl statement
1086 * transition.
a6c40364 1087 * We define this as a function rather than a macro in terms of my_getenv_len()
f675dbe5
CB
1088 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1089 * allocate SVs).
a0d0e21e 1090 */
f675dbe5 1091/*{{{ char *my_getenv(const char *lnm, bool sys)*/
a0d0e21e 1092char *
5c84aa53 1093Perl_my_getenv(pTHX_ const char *lnm, bool sys)
a0d0e21e 1094{
f7ddb74a 1095 const char *cp1;
fa537f88 1096 static char *__my_getenv_eqv = NULL;
f7ddb74a 1097 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
c07a80fd 1098 unsigned long int idx = 0;
4e0c9737 1099 int success, secure, saverr, savvmserr;
843027b0 1100 int midx, flags;
61bb5906 1101 SV *tmpsv;
a0d0e21e 1102
f7ddb74a 1103 midx = my_maxidx(lnm) + 1;
fa537f88 1104
6b88bc9c 1105 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
61bb5906
CB
1106 /* Set up a temporary buffer for the return value; Perl will
1107 * clean it up at the next statement transition */
fa537f88 1108 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
61bb5906
CB
1109 if (!tmpsv) return NULL;
1110 eqv = SvPVX(tmpsv);
1111 }
fa537f88
CB
1112 else {
1113 /* Assume no interpreter ==> single thread */
1114 if (__my_getenv_eqv != NULL) {
1115 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1116 }
1117 else {
a02a5408 1118 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1119 }
1120 eqv = __my_getenv_eqv;
1121 }
1122
f7ddb74a 1123 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1124 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
2497a41f 1125 int len;
61bb5906 1126 getcwd(eqv,LNM$C_NAMLENGTH);
2497a41f
JM
1127
1128 len = strlen(eqv);
1129
1130 /* Get rid of "000000/ in rooted filespecs */
1131 if (len > 7) {
1132 char * zeros;
1133 zeros = strstr(eqv, "/000000/");
1134 if (zeros != NULL) {
1135 int mlen;
1136 mlen = len - (zeros - eqv) - 7;
1137 memmove(zeros, &zeros[7], mlen);
1138 len = len - 7;
1139 eqv[len] = '\0';
1140 }
1141 }
61bb5906 1142 return eqv;
748a9306 1143 }
a0d0e21e 1144 else {
2512681b 1145 /* Impose security constraints only if tainting */
bc10a425
CB
1146 if (sys) {
1147 /* Impose security constraints only if tainting */
1148 secure = PL_curinterp ? PL_tainting : will_taint;
1149 saverr = errno; savvmserr = vaxc$errno;
1150 }
843027b0
CB
1151 else {
1152 secure = 0;
1153 }
1154
1155 flags =
f675dbe5 1156#ifdef SECURE_INTERNAL_GETENV
843027b0 1157 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1158#else
843027b0 1159 0
f675dbe5 1160#endif
843027b0
CB
1161 ;
1162
1163 /* For the getenv interface we combine all the equivalence names
1164 * of a search list logical into one value to acquire a maximum
1165 * value length of 255*128 (assuming %ENV is using logicals).
1166 */
1167 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1168
1169 /* If the name contains a semicolon-delimited index, parse it
1170 * off and make sure we only retrieve the equivalence name for
1171 * that index. */
1172 if ((cp2 = strchr(lnm,';')) != NULL) {
a35dcc95 1173 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
843027b0
CB
1174 idx = strtoul(cp2+1,NULL,0);
1175 lnm = uplnm;
1176 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1177 }
1178
1179 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1180
bc10a425
CB
1181 /* Discard NOLOGNAM on internal calls since we're often looking
1182 * for an optional name, and this "error" often shows up as the
1183 * (bogus) exit status for a die() call later on. */
1184 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
4e205ed6 1185 return success ? eqv : NULL;
a0d0e21e 1186 }
a0d0e21e
LW
1187
1188} /* end of my_getenv() */
1189/*}}}*/
1190
f675dbe5 1191
a6c40364
GS
1192/*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1193char *
fd8cd3a3 1194Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
f675dbe5 1195{
f7ddb74a
JM
1196 const char *cp1;
1197 char *buf, *cp2;
a6c40364 1198 unsigned long idx = 0;
843027b0 1199 int midx, flags;
fa537f88 1200 static char *__my_getenv_len_eqv = NULL;
bc10a425 1201 int secure, saverr, savvmserr;
cc077a9f
HM
1202 SV *tmpsv;
1203
f7ddb74a 1204 midx = my_maxidx(lnm) + 1;
fa537f88 1205
cc077a9f
HM
1206 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1207 /* Set up a temporary buffer for the return value; Perl will
1208 * clean it up at the next statement transition */
fa537f88 1209 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
cc077a9f
HM
1210 if (!tmpsv) return NULL;
1211 buf = SvPVX(tmpsv);
1212 }
fa537f88
CB
1213 else {
1214 /* Assume no interpreter ==> single thread */
1215 if (__my_getenv_len_eqv != NULL) {
1216 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1217 }
1218 else {
a02a5408 1219 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
fa537f88
CB
1220 }
1221 buf = __my_getenv_len_eqv;
1222 }
1223
f7ddb74a 1224 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
f675dbe5 1225 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
f7ddb74a
JM
1226 char * zeros;
1227
f675dbe5 1228 getcwd(buf,LNM$C_NAMLENGTH);
a6c40364 1229 *len = strlen(buf);
f7ddb74a
JM
1230
1231 /* Get rid of "000000/ in rooted filespecs */
1232 if (*len > 7) {
1233 zeros = strstr(buf, "/000000/");
1234 if (zeros != NULL) {
1235 int mlen;
1236 mlen = *len - (zeros - buf) - 7;
1237 memmove(zeros, &zeros[7], mlen);
1238 *len = *len - 7;
1239 buf[*len] = '\0';
1240 }
1241 }
a6c40364 1242 return buf;
f675dbe5
CB
1243 }
1244 else {
bc10a425
CB
1245 if (sys) {
1246 /* Impose security constraints only if tainting */
1247 secure = PL_curinterp ? PL_tainting : will_taint;
1248 saverr = errno; savvmserr = vaxc$errno;
1249 }
843027b0
CB
1250 else {
1251 secure = 0;
1252 }
1253
1254 flags =
f675dbe5 1255#ifdef SECURE_INTERNAL_GETENV
843027b0 1256 secure ? PERL__TRNENV_SECURE : 0
f675dbe5 1257#else
843027b0 1258 0
f675dbe5 1259#endif
843027b0
CB
1260 ;
1261
1262 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1263
1264 if ((cp2 = strchr(lnm,';')) != NULL) {
a35dcc95 1265 my_strlcpy(buf, lnm, cp2 - lnm + 1);
843027b0
CB
1266 idx = strtoul(cp2+1,NULL,0);
1267 lnm = buf;
1268 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1269 }
1270
1271 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1272
f7ddb74a
JM
1273 /* Get rid of "000000/ in rooted filespecs */
1274 if (*len > 7) {
1275 char * zeros;
1276 zeros = strstr(buf, "/000000/");
1277 if (zeros != NULL) {
1278 int mlen;
1279 mlen = *len - (zeros - buf) - 7;
1280 memmove(zeros, &zeros[7], mlen);
1281 *len = *len - 7;
1282 buf[*len] = '\0';
1283 }
1284 }
1285
bc10a425
CB
1286 /* Discard NOLOGNAM on internal calls since we're often looking
1287 * for an optional name, and this "error" often shows up as the
1288 * (bogus) exit status for a die() call later on. */
1289 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
4e205ed6 1290 return *len ? buf : NULL;
f675dbe5
CB
1291 }
1292
a6c40364 1293} /* end of my_getenv_len() */
f675dbe5
CB
1294/*}}}*/
1295
8a646e0b 1296static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
8fde5078
CB
1297
1298static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1e422769 1299
740ce14c 1300/*{{{ void prime_env_iter() */
1301void
1302prime_env_iter(void)
1303/* Fill the %ENV associative array with all logical names we can
1304 * find, in preparation for iterating over it.
1305 */
1306{
17f28c40 1307 static int primed = 0;
3eeba6fb 1308 HV *seenhv = NULL, *envhv;
22be8b3c 1309 SV *sv = NULL;
4e205ed6 1310 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
8fde5078
CB
1311 unsigned short int chan;
1312#ifndef CLI$M_TRUSTED
1313# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1314#endif
f675dbe5 1315 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
4e0c9737 1316 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
f675dbe5
CB
1317 long int i;
1318 bool have_sym = FALSE, have_lnm = FALSE;
1319 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1320 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1321 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1322 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1323 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
fd8cd3a3
DS
1324#if defined(PERL_IMPLICIT_CONTEXT)
1325 pTHX;
1326#endif
3db8f154 1327#if defined(USE_ITHREADS)
b2b3adea
HM
1328 static perl_mutex primenv_mutex;
1329 MUTEX_INIT(&primenv_mutex);
61bb5906 1330#endif
740ce14c 1331
fd8cd3a3
DS
1332#if defined(PERL_IMPLICIT_CONTEXT)
1333 /* We jump through these hoops because we can be called at */
1334 /* platform-specific initialization time, which is before anything is */
1335 /* set up--we can't even do a plain dTHX since that relies on the */
1336 /* interpreter structure to be initialized */
fd8cd3a3
DS
1337 if (PL_curinterp) {
1338 aTHX = PERL_GET_INTERP;
1339 } else {
ebd4d70b
JM
1340 /* we never get here because the NULL pointer will cause the */
1341 /* several of the routines called by this routine to access violate */
1342
1343 /* This routine is only called by hv.c/hv_iterinit which has a */
1344 /* context, so the real fix may be to pass it through instead of */
1345 /* the hoops above */
fd8cd3a3
DS
1346 aTHX = NULL;
1347 }
1348#endif
fd8cd3a3 1349
3eeba6fb 1350 if (primed || !PL_envgv) return;
61bb5906
CB
1351 MUTEX_LOCK(&primenv_mutex);
1352 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
3eeba6fb 1353 envhv = GvHVn(PL_envgv);
740ce14c 1354 /* Perform a dummy fetch as an lval to insure that the hash table is
8fde5078 1355 * set up. Otherwise, the hv_store() will turn into a nullop. */
740ce14c 1356 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
740ce14c 1357
f675dbe5
CB
1358 for (i = 0; env_tables[i]; i++) {
1359 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1360 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
f02a1854 1361 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
8fde5078 1362 }
f675dbe5
CB
1363 if (have_sym || have_lnm) {
1364 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1365 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1366 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1367 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
61bb5906 1368 }
f675dbe5
CB
1369
1370 for (i--; i >= 0; i--) {
1371 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1372 char *start;
1373 int j;
1374 for (j = 0; environ[j]; j++) {
1375 if (!(start = strchr(environ[j],'='))) {
3eeba6fb 1376 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1377 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
f675dbe5
CB
1378 }
1379 else {
1380 start++;
22be8b3c
CB
1381 sv = newSVpv(start,0);
1382 SvTAINTED_on(sv);
1383 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
f675dbe5
CB
1384 }
1385 }
1386 continue;
740ce14c 1387 }
f675dbe5
CB
1388 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1389 !str$case_blind_compare(&tmpdsc,&clisym)) {
a35dcc95 1390 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
f675dbe5
CB
1391 cmddsc.dsc$w_length = 20;
1392 if (env_tables[i]->dsc$w_length == 12 &&
1393 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
a35dcc95 1394 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
f675dbe5
CB
1395 flags = defflags | CLI$M_NOLOGNAM;
1396 }
1397 else {
a35dcc95 1398 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
f675dbe5 1399 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
a35dcc95
CB
1400 my_strlcat(cmd," /Table=", sizeof(cmd));
1401 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, env_tables[i]->dsc$w_length + 1);
f675dbe5
CB
1402 }
1403 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1404 flags = defflags | CLI$M_NOCLISYM;
1405 }
1406
1407 /* Create a new subprocess to execute each command, to exclude the
1408 * remote possibility that someone could subvert a mbx or file used
1409 * to write multiple commands to a single subprocess.
1410 */
1411 do {
1412 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1413 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1414 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1415 defflags &= ~CLI$M_TRUSTED;
1416 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1417 _ckvmssts(retsts);
a02a5408 1418 if (!buf) Newx(buf,mbxbufsiz + 1,char);
f675dbe5
CB
1419 if (seenhv) SvREFCNT_dec(seenhv);
1420 seenhv = newHV();
1421 while (1) {
1422 char *cp1, *cp2, *key;
1423 unsigned long int sts, iosb[2], retlen, keylen;
eb578fdb 1424 U32 hash;
f675dbe5
CB
1425
1426 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1427 if (sts & 1) sts = iosb[0] & 0xffff;
1428 if (sts == SS$_ENDOFFILE) {
1429 int wakect = 0;
1430 while (substs == 0) { sys$hiber(); wakect++;}
1431 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1432 _ckvmssts(substs);
1433 break;
1434 }
1435 _ckvmssts(sts);
1436 retlen = iosb[0] >> 16;
1437 if (!retlen) continue; /* blank line */
1438 buf[retlen] = '\0';
1439 if (iosb[1] != subpid) {
1440 if (iosb[1]) {
5c84aa53 1441 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
f675dbe5
CB
1442 }
1443 continue;
1444 }
3eeba6fb 1445 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
f98bc0c6 1446 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
f675dbe5
CB
1447
1448 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1449 if (*cp1 == '(' || /* Logical name table name */
1450 *cp1 == '=' /* Next eqv of searchlist */) continue;
1451 if (*cp1 == '"') cp1++;
1452 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1453 key = cp1; keylen = cp2 - cp1;
1454 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1455 while (*cp2 && *cp2 != '=') cp2++;
1f47e8e2
CB
1456 while (*cp2 && *cp2 == '=') cp2++;
1457 while (*cp2 && *cp2 == ' ') cp2++;
1458 if (*cp2 == '"') { /* String translation; may embed "" */
1459 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1460 cp2++; cp1--; /* Skip "" surrounding translation */
1461 }
1462 else { /* Numeric translation */
1463 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1464 cp1--; /* stop on last non-space char */
1465 }
1466 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
f98bc0c6 1467 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
edc7bc49
CB
1468 continue;
1469 }
5afd6d42 1470 PERL_HASH(hash,key,keylen);
ff79d39d
CB
1471
1472 if (cp1 == cp2 && *cp2 == '.') {
1473 /* A single dot usually means an unprintable character, such as a null
1474 * to indicate a zero-length value. Get the actual value to make sure.
1475 */
1476 char lnm[LNM$C_NAMLENGTH+1];
2497a41f 1477 char eqv[MAX_DCL_SYMBOL+1];
0faef845 1478 int trnlen;
ff79d39d 1479 strncpy(lnm, key, keylen);
0faef845 1480 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
ff79d39d
CB
1481 sv = newSVpvn(eqv, strlen(eqv));
1482 }
1483 else {
1484 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1485 }
1486
22be8b3c
CB
1487 SvTAINTED_on(sv);
1488 hv_store(envhv,key,keylen,sv,hash);
f675dbe5 1489 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
edc7bc49 1490 }
f675dbe5
CB
1491 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1492 /* get the PPFs for this process, not the subprocess */
f7ddb74a 1493 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
f675dbe5
CB
1494 char eqv[LNM$C_NAMLENGTH+1];
1495 int trnlen, i;
1496 for (i = 0; ppfs[i]; i++) {
1497 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
22be8b3c
CB
1498 sv = newSVpv(eqv,trnlen);
1499 SvTAINTED_on(sv);
1500 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
f675dbe5 1501 }
740ce14c 1502 }
1503 }
f675dbe5
CB
1504 primed = 1;
1505 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1506 if (buf) Safefree(buf);
1507 if (seenhv) SvREFCNT_dec(seenhv);
1508 MUTEX_UNLOCK(&primenv_mutex);
1509 return;
1510
740ce14c 1511} /* end of prime_env_iter */
1512/*}}}*/
740ce14c 1513
f675dbe5 1514
2c590a56 1515/*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1516/* Define or delete an element in the same "environment" as
1517 * vmstrnenv(). If an element is to be deleted, it's removed from
1518 * the first place it's found. If it's to be set, it's set in the
1519 * place designated by the first element of the table vector.
3eeba6fb 1520 * Like setenv() returns 0 for success, non-zero on error.
a0d0e21e 1521 */
f675dbe5 1522int
2c590a56 1523Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
a0d0e21e 1524{
f7ddb74a
JM
1525 const char *cp1;
1526 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
f675dbe5 1527 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
fa537f88 1528 int nseg = 0, j;
a0d0e21e 1529 unsigned long int retsts, usermode = PSL$C_USER;
fa537f88 1530 struct itmlst_3 *ile, *ilist;
a0d0e21e 1531 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
f675dbe5
CB
1532 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1533 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1534 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1535 $DESCRIPTOR(local,"_LOCAL");
1536
ed253963
CB
1537 if (!lnm) {
1538 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1539 return SS$_IVLOGNAM;
1540 }
1541
f7ddb74a 1542 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
f675dbe5
CB
1543 *cp2 = _toupper(*cp1);
1544 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1545 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1546 return SS$_IVLOGNAM;
1547 }
1548 }
a0d0e21e 1549 lnmdsc.dsc$w_length = cp1 - lnm;
f675dbe5
CB
1550 if (!tabvec || !*tabvec) tabvec = env_tables;
1551
3eeba6fb 1552 if (!eqv) { /* we're deleting n element */
f675dbe5
CB
1553 for (curtab = 0; tabvec[curtab]; curtab++) {
1554 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1555 int i;
299d126a 1556 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
f675dbe5 1557 if ((cp1 = strchr(environ[i],'=')) &&
299d126a 1558 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
f675dbe5 1559 !strncmp(environ[i],lnm,cp1 - environ[i])) {
3eeba6fb 1560#ifdef HAS_SETENV
0e06870b 1561 return setenv(lnm,"",1) ? vaxc$errno : 0;
f675dbe5
CB
1562 }
1563 }
1564 ivenv = 1; retsts = SS$_NOLOGNAM;
1565#else
3eeba6fb 1566 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1567 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
3eeba6fb
CB
1568 ivenv = 1; retsts = SS$_NOSUCHPGM;
1569 break;
1570 }
1571 }
f675dbe5
CB
1572#endif
1573 }
1574 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1575 !str$case_blind_compare(&tmpdsc,&clisym)) {
1576 unsigned int symtype;
1577 if (tabvec[curtab]->dsc$w_length == 12 &&
1578 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1579 !str$case_blind_compare(&tmpdsc,&local))
1580 symtype = LIB$K_CLI_LOCAL_SYM;
1581 else symtype = LIB$K_CLI_GLOBAL_SYM;
1582 retsts = lib$delete_symbol(&lnmdsc,&symtype);
3eeba6fb
CB
1583 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1584 if (retsts == LIB$_NOSUCHSYM) continue;
f675dbe5
CB
1585 break;
1586 }
1587 else if (!ivlnm) {
1588 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1589 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1590 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1591 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1592 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1593 }
a0d0e21e
LW
1594 }
1595 }
f675dbe5
CB
1596 else { /* we're defining a value */
1597 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1598#ifdef HAS_SETENV
3eeba6fb 1599 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
f675dbe5 1600#else
3eeba6fb 1601 if (ckWARN(WARN_INTERNAL))
f98bc0c6 1602 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
f675dbe5
CB
1603 retsts = SS$_NOSUCHPGM;
1604#endif
1605 }
1606 else {
f7ddb74a 1607 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
f675dbe5
CB
1608 eqvdsc.dsc$w_length = strlen(eqv);
1609 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1610 !str$case_blind_compare(&tmpdsc,&clisym)) {
1611 unsigned int symtype;
1612 if (tabvec[0]->dsc$w_length == 12 &&
1613 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1614 !str$case_blind_compare(&tmpdsc,&local))
1615 symtype = LIB$K_CLI_LOCAL_SYM;
1616 else symtype = LIB$K_CLI_GLOBAL_SYM;
1617 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1618 }
3eeba6fb
CB
1619 else {
1620 if (!*eqv) eqvdsc.dsc$w_length = 1;
a1dfe751 1621 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
fa537f88
CB
1622
1623 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1624 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1625 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1626 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1627 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1628 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1629 }
1630
a02a5408 1631 Newx(ilist,nseg+1,struct itmlst_3);
fa537f88
CB
1632 ile = ilist;
1633 if (!ile) {
1634 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1635 return SS$_INSFMEM;
a1dfe751 1636 }
fa537f88
CB
1637 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1638
1639 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1640 ile->itmcode = LNM$_STRING;
1641 ile->bufadr = c;
1642 if ((j+1) == nseg) {
1643 ile->buflen = strlen(c);
1644 /* in case we are truncating one that's too long */
1645 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1646 }
1647 else {
1648 ile->buflen = LNM$C_NAMLENGTH;
1649 }
1650 }
1651
1652 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1653 Safefree (ilist);
1654 }
1655 else {
1656 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
a1dfe751 1657 }
3eeba6fb 1658 }
f675dbe5
CB
1659 }
1660 }
1661 if (!(retsts & 1)) {
1662 switch (retsts) {
1663 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1664 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1665 set_errno(EVMSERR); break;
1666 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1667 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1668 set_errno(EINVAL); break;
1669 case SS$_NOPRIV:
7d2497bf 1670 set_errno(EACCES); break;
f675dbe5
CB
1671 default:
1672 _ckvmssts(retsts);
1673 set_errno(EVMSERR);
1674 }
1675 set_vaxc_errno(retsts);
1676 return (int) retsts || 44; /* retsts should never be 0, but just in case */
a0d0e21e 1677 }
3eeba6fb
CB
1678 else {
1679 /* We reset error values on success because Perl does an hv_fetch()
1680 * before each hv_store(), and if the thing we're setting didn't
1681 * previously exist, we've got a leftover error message. (Of course,
1682 * this fails in the face of
1683 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1684 * in that the error reported in $! isn't spurious,
1685 * but it's right more often than not.)
1686 */
f675dbe5
CB
1687 set_errno(0); set_vaxc_errno(retsts);
1688 return 0;
1689 }
1690
1691} /* end of vmssetenv() */
1692/*}}}*/
a0d0e21e 1693
2c590a56 1694/*{{{ void my_setenv(const char *lnm, const char *eqv)*/
f675dbe5
CB
1695/* This has to be a function since there's a prototype for it in proto.h */
1696void
2c590a56 1697Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
f675dbe5 1698{
bc10a425
CB
1699 if (lnm && *lnm) {
1700 int len = strlen(lnm);
1701 if (len == 7) {
1702 char uplnm[8];
22d4bb9c
CB
1703 int i;
1704 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
bc10a425 1705 if (!strcmp(uplnm,"DEFAULT")) {
7ded3206 1706 if (eqv && *eqv) my_chdir(eqv);
bc10a425
CB
1707 return;
1708 }
1709 }
22d4bb9c 1710 }
f675dbe5
CB
1711 (void) vmssetenv(lnm,eqv,NULL);
1712}
a0d0e21e
LW
1713/*}}}*/
1714
27c67b75 1715/*{{{static void vmssetuserlnm(char *name, char *eqv); */
0e06870b
CB
1716/* vmssetuserlnm
1717 * sets a user-mode logical in the process logical name table
1718 * used for redirection of sys$error
4d9538c1
JM
1719 *
1720 * Fix-me: The pTHX is not needed for this routine, however doio.c
1721 * is calling it with one instead of using a macro.
1722 * A macro needs to be added to vmsish.h and doio.c updated to use it.
1723 *
0e06870b
CB
1724 */
1725void
2fbb330f 1726Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
0e06870b
CB
1727{
1728 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1729 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
2d5e9e5d 1730 unsigned long int iss, attr = LNM$M_CONFINE;
0e06870b
CB
1731 unsigned char acmode = PSL$C_USER;
1732 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1733 {0, 0, 0, 0}};
2fbb330f 1734 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
0e06870b
CB
1735 d_name.dsc$w_length = strlen(name);
1736
1737 lnmlst[0].buflen = strlen(eqv);
2fbb330f 1738 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
0e06870b
CB
1739
1740 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1741 if (!(iss&1)) lib$signal(iss);
1742}
1743/*}}}*/
c07a80fd 1744
f675dbe5 1745
c07a80fd 1746/*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1747/* my_crypt - VMS password hashing
1748 * my_crypt() provides an interface compatible with the Unix crypt()
1749 * C library function, and uses sys$hash_password() to perform VMS
1750 * password hashing. The quadword hashed password value is returned
1751 * as a NUL-terminated 8 character string. my_crypt() does not change
1752 * the case of its string arguments; in order to match the behavior
1753 * of LOGINOUT et al., alphabetic characters in both arguments must
1754 * be upcased by the caller.
2497a41f
JM
1755 *
1756 * - fix me to call ACM services when available
c07a80fd 1757 */
1758char *
fd8cd3a3 1759Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
c07a80fd 1760{
1761# ifndef UAI$C_PREFERRED_ALGORITHM
1762# define UAI$C_PREFERRED_ALGORITHM 127
1763# endif
1764 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1765 unsigned short int salt = 0;
1766 unsigned long int sts;
1767 struct const_dsc {
1768 unsigned short int dsc$w_length;
1769 unsigned char dsc$b_type;
1770 unsigned char dsc$b_class;
1771 const char * dsc$a_pointer;
1772 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1773 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1774 struct itmlst_3 uailst[3] = {
1775 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1776 { sizeof salt, UAI$_SALT, &salt, 0},
1777 { 0, 0, NULL, NULL}};
1778 static char hash[9];
1779
1780 usrdsc.dsc$w_length = strlen(usrname);
1781 usrdsc.dsc$a_pointer = usrname;
1782 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1783 switch (sts) {
f282b18d 1784 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
c07a80fd 1785 set_errno(EACCES);
1786 break;
1787 case RMS$_RNF:
1788 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1789 break;
1790 default:
1791 set_errno(EVMSERR);
1792 }
1793 set_vaxc_errno(sts);
1794 if (sts != RMS$_RNF) return NULL;
1795 }
1796
1797 txtdsc.dsc$w_length = strlen(textpasswd);
1798 txtdsc.dsc$a_pointer = textpasswd;
1799 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1800 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1801 }
1802
1803 return (char *) hash;
1804
1805} /* end of my_crypt() */
1806/*}}}*/
1807
1808
360732b5
JM
1809static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1810static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1811static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
a0d0e21e 1812
2497a41f
JM
1813/* fixup barenames that are directories for internal use.
1814 * There have been problems with the consistent handling of UNIX
1815 * style directory names when routines are presented with a name that
94ae10c0 1816 * has no directory delimiters at all. So this routine will eventually
2497a41f
JM
1817 * fix the issue.
1818 */
1819static char * fixup_bare_dirnames(const char * name)
1820{
1821 if (decc_disable_to_vms_logname_translation) {
1822/* fix me */
1823 }
1824 return NULL;
1825}
1826
e0e5e8d6
JM
1827/* 8.3, remove() is now broken on symbolic links */
1828static int rms_erase(const char * vmsname);
1829
1830
2497a41f 1831/* mp_do_kill_file
94ae10c0 1832 * A little hack to get around a bug in some implementation of remove()
2497a41f
JM
1833 * that do not know how to delete a directory
1834 *
1835 * Delete any file to which user has control access, regardless of whether
1836 * delete access is explicitly allowed.
1837 * Limitations: User must have write access to parent directory.
1838 * Does not block signals or ASTs; if interrupted in midstream
1839 * may leave file with an altered ACL.
1840 * HANDLE WITH CARE!
1841 */
1842/*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1843static int
1844mp_do_kill_file(pTHX_ const char *name, int dirflag)
1845{
e0e5e8d6
JM
1846 char *vmsname;
1847 char *rslt;
2497a41f
JM
1848 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1849 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1850 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1851 struct myacedef {
1852 unsigned char myace$b_length;
1853 unsigned char myace$b_type;
1854 unsigned short int myace$w_flags;
1855 unsigned long int myace$l_access;
1856 unsigned long int myace$l_ident;
1857 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1858 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1859 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1860 struct itmlst_3
1861 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1862 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1863 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1864 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1865 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1866 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1867
1868 /* Expand the input spec using RMS, since the CRTL remove() and
1869 * system services won't do this by themselves, so we may miss
1870 * a file "hiding" behind a logical name or search list. */
c11536f5 1871 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
ebd4d70b 1872 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 1873
6fb6c614 1874 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
e0e5e8d6 1875 if (rslt == NULL) {
c5375c28 1876 PerlMem_free(vmsname);
2497a41f
JM
1877 return -1;
1878 }
c5375c28 1879
e0e5e8d6
JM
1880 /* Erase the file */
1881 rmsts = rms_erase(vmsname);
2497a41f 1882
e0e5e8d6
JM
1883 /* Did it succeed */
1884 if ($VMS_STATUS_SUCCESS(rmsts)) {
1885 PerlMem_free(vmsname);
1886 return 0;
2497a41f
JM
1887 }
1888
1889 /* If not, can changing protections help? */
e0e5e8d6
JM
1890 if (rmsts != RMS$_PRV) {
1891 set_vaxc_errno(rmsts);
1892 PerlMem_free(vmsname);
2497a41f
JM
1893 return -1;
1894 }
1895
1896 /* No, so we get our own UIC to use as a rights identifier,
1897 * and the insert an ACE at the head of the ACL which allows us
1898 * to delete the file.
1899 */
ebd4d70b 1900 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
e0e5e8d6
JM
1901 fildsc.dsc$w_length = strlen(vmsname);
1902 fildsc.dsc$a_pointer = vmsname;
2497a41f
JM
1903 cxt = 0;
1904 newace.myace$l_ident = oldace.myace$l_ident;
e0e5e8d6 1905 rmsts = -1;
2497a41f
JM
1906 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1907 switch (aclsts) {
1908 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1909 set_errno(ENOENT); break;
1910 case RMS$_DIR:
1911 set_errno(ENOTDIR); break;
1912 case RMS$_DEV:
1913 set_errno(ENODEV); break;
1914 case RMS$_SYN: case SS$_INVFILFOROP:
1915 set_errno(EINVAL); break;
1916 case RMS$_PRV:
1917 set_errno(EACCES); break;
1918 default:
ebd4d70b 1919 _ckvmssts_noperl(aclsts);
2497a41f
JM
1920 }
1921 set_vaxc_errno(aclsts);
e0e5e8d6 1922 PerlMem_free(vmsname);
2497a41f
JM
1923 return -1;
1924 }
1925 /* Grab any existing ACEs with this identifier in case we fail */
1926 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1927 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1928 || fndsts == SS$_NOMOREACE ) {
1929 /* Add the new ACE . . . */
1930 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1931 goto yourroom;
1932
e0e5e8d6
JM
1933 rmsts = rms_erase(vmsname);
1934 if ($VMS_STATUS_SUCCESS(rmsts)) {
1935 rmsts = 0;
2497a41f
JM
1936 }
1937 else {
e0e5e8d6 1938 rmsts = -1;
2497a41f
JM
1939 /* We blew it - dir with files in it, no write priv for
1940 * parent directory, etc. Put things back the way they were. */
1941 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1942 goto yourroom;
1943 if (fndsts & 1) {
1944 addlst[0].bufadr = &oldace;
1945 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1946 goto yourroom;
1947 }
1948 }
1949 }
1950
1951 yourroom:
1952 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1953 /* We just deleted it, so of course it's not there. Some versions of
1954 * VMS seem to return success on the unlock operation anyhow (after all
1955 * the unlock is successful), but others don't.
1956 */
1957 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1958 if (aclsts & 1) aclsts = fndsts;
1959 if (!(aclsts & 1)) {
1960 set_errno(EVMSERR);
1961 set_vaxc_errno(aclsts);
2497a41f
JM
1962 }
1963
e0e5e8d6 1964 PerlMem_free(vmsname);
2497a41f
JM
1965 return rmsts;
1966
1967} /* end of kill_file() */
1968/*}}}*/
1969
1970
a0d0e21e
LW
1971/*{{{int do_rmdir(char *name)*/
1972int
b8ffc8df 1973Perl_do_rmdir(pTHX_ const char *name)
a0d0e21e 1974{
e0e5e8d6 1975 char * dirfile;
a0d0e21e 1976 int retval;
61bb5906 1977 Stat_t st;
a0d0e21e 1978
d94c5a78
JM
1979 /* lstat returns a VMS fileified specification of the name */
1980 /* that is looked up, and also lets verifies that this is a directory */
e0e5e8d6 1981
46c05374 1982 retval = flex_lstat(name, &st);
d94c5a78
JM
1983 if (retval != 0) {
1984 char * ret_spec;
1985
1986 /* Due to a historical feature, flex_stat/lstat can not see some */
1987 /* Unix format file names that the rest of the CRTL can see */
1988 /* Fixing that feature will cause some perl tests to fail */
1989 /* So try this one more time. */
1990
1991 retval = lstat(name, &st.crtl_stat);
1992 if (retval != 0)
1993 return -1;
1994
1995 /* force it to a file spec for the kill file to work. */
1996 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1997 if (ret_spec == NULL) {
1998 errno = EIO;
1999 return -1;
2000 }
e0e5e8d6 2001 }
d94c5a78
JM
2002
2003 if (!S_ISDIR(st.st_mode)) {
e0e5e8d6
JM
2004 errno = ENOTDIR;
2005 retval = -1;
2006 }
d94c5a78
JM
2007 else {
2008 dirfile = st.st_devnam;
2009
2010 /* It may be possible for flex_stat to find a file and vmsify() to */
2011 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
2012 /* with that case, so fail it */
2013 if (dirfile[0] == 0) {
2014 errno = EIO;
2015 return -1;
2016 }
2017
e0e5e8d6 2018 retval = mp_do_kill_file(aTHX_ dirfile, 1);
d94c5a78 2019 }
e0e5e8d6 2020
a0d0e21e
LW
2021 return retval;
2022
2023} /* end of do_rmdir */
2024/*}}}*/
2025
2026/* kill_file
2027 * Delete any file to which user has control access, regardless of whether
2028 * delete access is explicitly allowed.
2029 * Limitations: User must have write access to parent directory.
2030 * Does not block signals or ASTs; if interrupted in midstream
2031 * may leave file with an altered ACL.
2032 * HANDLE WITH CARE!
2033 */
2034/*{{{int kill_file(char *name)*/
2035int
b8ffc8df 2036Perl_kill_file(pTHX_ const char *name)
a0d0e21e 2037{
d94c5a78 2038 char * vmsfile;
e0e5e8d6
JM
2039 Stat_t st;
2040 int rmsts;
a0d0e21e 2041
d94c5a78
JM
2042 /* Convert the filename to VMS format and see if it is a directory */
2043 /* flex_lstat returns a vmsified file specification */
46c05374 2044 rmsts = flex_lstat(name, &st);
d94c5a78
JM
2045 if (rmsts != 0) {
2046
2047 /* Due to a historical feature, flex_stat/lstat can not see some */
2048 /* Unix format file names that the rest of the CRTL can see when */
2049 /* ODS-2 file specifications are in use. */
2050 /* Fixing that feature will cause some perl tests to fail */
2051 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2052 st.st_mode = 0;
2053 vmsfile = (char *) name; /* cast ok */
2054
2055 } else {
2056 vmsfile = st.st_devnam;
2057 if (vmsfile[0] == 0) {
2058 /* It may be possible for flex_stat to find a file and vmsify() */
2059 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2060 /* deal with that case, so fail it */
2061 errno = EIO;
2062 return -1;
2063 }
2064 }
2065
2066 /* Remove() is allowed to delete directories, according to the X/Open
2067 * specifications.
2068 * This may need special handling to work with the ACL hacks.
a0d0e21e 2069 */
d94c5a78
JM
2070 if (S_ISDIR(st.st_mode)) {
2071 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2072 return rmsts;
a0d0e21e
LW
2073 }
2074
d94c5a78
JM
2075 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2076
2077 /* Need to delete all versions ? */
2078 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2079 int i = 0;
2080
2081 /* Just use lstat() here as do not need st_dev */
2082 /* and we know that the file is in VMS format or that */
2083 /* because of a historical bug, flex_stat can not see the file */
2084 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2085 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2086 if (rmsts != 0)
2087 break;
2088 i++;
2089
2090 /* Make sure that we do not loop forever */
2091 if (i > 32767) {
2092 errno = EIO;
2093 rmsts = -1;
2094 break;
2095 }
2096 }
2097 }
a0d0e21e
LW
2098
2099 return rmsts;
2100
2101} /* end of kill_file() */
2102/*}}}*/
2103
8cc95fdb 2104
84902520 2105/*{{{int my_mkdir(char *,Mode_t)*/
8cc95fdb 2106int
b8ffc8df 2107Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
8cc95fdb 2108{
2109 STRLEN dirlen = strlen(dir);
2110
a2a90019
CB
2111 /* zero length string sometimes gives ACCVIO */
2112 if (dirlen == 0) return -1;
2113
8cc95fdb 2114 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2115 * null file name/type. However, it's commonplace under Unix,
2116 * so we'll allow it for a gain in portability.
2117 */
2118 if (dir[dirlen-1] == '/') {
2119 char *newdir = savepvn(dir,dirlen-1);
2120 int ret = mkdir(newdir,mode);
2121 Safefree(newdir);
2122 return ret;
2123 }
2124 else return mkdir(dir,mode);
2125} /* end of my_mkdir */
2126/*}}}*/
2127
ee8c7f54
CB
2128/*{{{int my_chdir(char *)*/
2129int
b8ffc8df 2130Perl_my_chdir(pTHX_ const char *dir)
ee8c7f54
CB
2131{
2132 STRLEN dirlen = strlen(dir);
ee8c7f54
CB
2133
2134 /* zero length string sometimes gives ACCVIO */
2135 if (dirlen == 0) return -1;
f7ddb74a
JM
2136 const char *dir1;
2137
2138 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2139 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2140 * so that existing scripts do not need to be changed.
2141 */
2142 dir1 = dir;
2143 while ((dirlen > 0) && (*dir1 == ' ')) {
2144 dir1++;
2145 dirlen--;
2146 }
ee8c7f54
CB
2147
2148 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2149 * that implies
2150 * null file name/type. However, it's commonplace under Unix,
2151 * so we'll allow it for a gain in portability.
f7ddb74a 2152 *
4d9538c1 2153 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
ee8c7f54 2154 */
f7ddb74a 2155 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
4d9538c1
JM
2156 char *newdir;
2157 int ret;
c11536f5 2158 newdir = (char *)PerlMem_malloc(dirlen);
4d9538c1
JM
2159 if (newdir ==NULL)
2160 _ckvmssts_noperl(SS$_INSFMEM);
a35dcc95 2161 memcpy(newdir, dir1, dirlen-1);
4d9538c1
JM
2162 newdir[dirlen-1] = '\0';
2163 ret = chdir(newdir);
2164 PerlMem_free(newdir);
2165 return ret;
ee8c7f54 2166 }
dca5a913 2167 else return chdir(dir1);
ee8c7f54
CB
2168} /* end of my_chdir */
2169/*}}}*/
8cc95fdb 2170
674d6c38 2171
f1db9cda
JM
2172/*{{{int my_chmod(char *, mode_t)*/
2173int
2174Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2175{
4d9538c1
JM
2176 Stat_t st;
2177 int ret = -1;
2178 char * changefile;
f1db9cda
JM
2179 STRLEN speclen = strlen(file_spec);
2180
2181 /* zero length string sometimes gives ACCVIO */
2182 if (speclen == 0) return -1;
2183
2184 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2185 * that implies null file name/type. However, it's commonplace under Unix,
2186 * so we'll allow it for a gain in portability.
2187 *
2188 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2189 * in VMS file.dir notation.
2190 */
4d9538c1
JM
2191 changefile = (char *) file_spec; /* cast ok */
2192 ret = flex_lstat(file_spec, &st);
2193 if (ret != 0) {
f1db9cda 2194
4d9538c1
JM
2195 /* Due to a historical feature, flex_stat/lstat can not see some */
2196 /* Unix format file names that the rest of the CRTL can see when */
2197 /* ODS-2 file specifications are in use. */
2198 /* Fixing that feature will cause some perl tests to fail */
2199 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2200 st.st_mode = 0;
f1db9cda 2201
4d9538c1
JM
2202 } else {
2203 /* It may be possible to get here with nothing in st_devname */
2204 /* chmod still may work though */
2205 if (st.st_devnam[0] != 0) {
2206 changefile = st.st_devnam;
2207 }
f1db9cda 2208 }
4d9538c1
JM
2209 ret = chmod(changefile, mode);
2210 return ret;
f1db9cda
JM
2211} /* end of my_chmod */
2212/*}}}*/
2213
2214
674d6c38
CB
2215/*{{{FILE *my_tmpfile()*/
2216FILE *
2217my_tmpfile(void)
2218{
2219 FILE *fp;
2220 char *cp;
674d6c38
CB
2221
2222 if ((fp = tmpfile())) return fp;
2223
c11536f5 2224 cp = (char *)PerlMem_malloc(L_tmpnam+24);
c5375c28
JM
2225 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2226
2497a41f
JM
2227 if (decc_filename_unix_only == 0)
2228 strcpy(cp,"Sys$Scratch:");
2229 else
2230 strcpy(cp,"/tmp/");
674d6c38
CB
2231 tmpnam(cp+strlen(cp));
2232 strcat(cp,".Perltmp");
2233 fp = fopen(cp,"w+","fop=dlt");
c5375c28 2234 PerlMem_free(cp);
674d6c38
CB
2235 return fp;
2236}
2237/*}}}*/
2238
5c2d7af2 2239
5c2d7af2
CB
2240/*
2241 * The C RTL's sigaction fails to check for invalid signal numbers so we
2242 * help it out a bit. The docs are correct, but the actual routine doesn't
2243 * do what the docs say it will.
2244 */
2245/*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2246int
2247Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2248 struct sigaction* oact)
2249{
2250 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2251 SETERRNO(EINVAL, SS$_INVARG);
2252 return -1;
2253 }
2254 return sigaction(sig, act, oact);
2255}
2256/*}}}*/
5c2d7af2 2257
f2610a60
CL
2258#ifdef KILL_BY_SIGPRC
2259#include <errnodef.h>
2260
05c058bc
CB
2261/* We implement our own kill() using the undocumented system service
2262 sys$sigprc for one of two reasons:
2263
2264 1.) If the kill() in an older CRTL uses sys$forcex, causing the
f2610a60
CL
2265 target process to do a sys$exit, which usually can't be handled
2266 gracefully...certainly not by Perl and the %SIG{} mechanism.
2267
05c058bc
CB
2268 2.) If the kill() in the CRTL can't be called from a signal
2269 handler without disappearing into the ether, i.e., the signal
2270 it purportedly sends is never trapped. Still true as of VMS 7.3.
2271
2272 sys$sigprc has the same parameters as sys$forcex, but throws an exception
f2610a60
CL
2273 in the target process rather than calling sys$exit.
2274
2275 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2276 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2277 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2278 with condition codes C$_SIG0+nsig*8, catching the exception on the
2279 target process and resignaling with appropriate arguments.
2280
2281 But we don't have that VMS 7.0+ exception handler, so if you
2282 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2283
2284 Also note that SIGTERM is listed in the docs as being "unimplemented",
2285 yet always seems to be signaled with a VMS condition code of 4 (and
2286 correctly handled for that code). So we hardwire it in.
2287
2288 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2289 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2290 than signalling with an unrecognized (and unhandled by CRTL) code.
2291*/
2292
fe1de8ce 2293#define _MY_SIG_MAX 28
f2610a60 2294
9c1171d1
JM
2295static unsigned int
2296Perl_sig_to_vmscondition_int(int sig)
f2610a60 2297{
2e34cc90 2298 static unsigned int sig_code[_MY_SIG_MAX+1] =
f2610a60
CL
2299 {
2300 0, /* 0 ZERO */
2301 SS$_HANGUP, /* 1 SIGHUP */
2302 SS$_CONTROLC, /* 2 SIGINT */
2303 SS$_CONTROLY, /* 3 SIGQUIT */
2304 SS$_RADRMOD, /* 4 SIGILL */
2305 SS$_BREAK, /* 5 SIGTRAP */
2306 SS$_OPCCUS, /* 6 SIGABRT */
2307 SS$_COMPAT, /* 7 SIGEMT */
2308#ifdef __VAX
2309 SS$_FLTOVF, /* 8 SIGFPE VAX */
2310#else
2311 SS$_HPARITH, /* 8 SIGFPE AXP */
2312#endif
2313 SS$_ABORT, /* 9 SIGKILL */
2314 SS$_ACCVIO, /* 10 SIGBUS */
2315 SS$_ACCVIO, /* 11 SIGSEGV */
2316 SS$_BADPARAM, /* 12 SIGSYS */
2317 SS$_NOMBX, /* 13 SIGPIPE */
2318 SS$_ASTFLT, /* 14 SIGALRM */
2319 4, /* 15 SIGTERM */
2320 0, /* 16 SIGUSR1 */
fe1de8ce
CB
2321 0, /* 17 SIGUSR2 */
2322 0, /* 18 */
2323 0, /* 19 */
2324 0, /* 20 SIGCHLD */
2325 0, /* 21 SIGCONT */
2326 0, /* 22 SIGSTOP */
2327 0, /* 23 SIGTSTP */
2328 0, /* 24 SIGTTIN */
2329 0, /* 25 SIGTTOU */
2330 0, /* 26 */
2331 0, /* 27 */
2332 0 /* 28 SIGWINCH */
f2610a60
CL
2333 };
2334
f2610a60
CL
2335 static int initted = 0;
2336 if (!initted) {
2337 initted = 1;
2338 sig_code[16] = C$_SIGUSR1;
2339 sig_code[17] = C$_SIGUSR2;
fe1de8ce 2340 sig_code[20] = C$_SIGCHLD;
fe1de8ce
CB
2341#if __CRTL_VER >= 70300000
2342 sig_code[28] = C$_SIGWINCH;
2343#endif
f2610a60 2344 }
f2610a60 2345
2e34cc90
CL
2346 if (sig < _SIG_MIN) return 0;
2347 if (sig > _MY_SIG_MAX) return 0;
2348 return sig_code[sig];
2349}
2350
9c1171d1
JM
2351unsigned int
2352Perl_sig_to_vmscondition(int sig)
2353{
2354#ifdef SS$_DEBUG
2355 if (vms_debug_on_exception != 0)
2356 lib$signal(SS$_DEBUG);
2357#endif
2358 return Perl_sig_to_vmscondition_int(sig);
2359}
2360
2361
c11536f5
CB
2362#define sys$sigprc SYS$SIGPRC
2363#ifdef __cplusplus
2364extern "C" {
2365#endif
2366int sys$sigprc(unsigned int *pidadr,
2367 struct dsc$descriptor_s *prcname,
2368 unsigned int code);
2369#ifdef __cplusplus
2370}
2371#endif
2372
2e34cc90
CL
2373int
2374Perl_my_kill(int pid, int sig)
2375{
2376 int iss;
2377 unsigned int code;
2e34cc90 2378
7a7fd8e0
JM
2379 /* sig 0 means validate the PID */
2380 /*------------------------------*/
2381 if (sig == 0) {
2382 const unsigned long int jpicode = JPI$_PID;
2383 pid_t ret_pid;
2384 int status;
2385 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2386 if ($VMS_STATUS_SUCCESS(status))
2387 return 0;
2388 switch (status) {
2389 case SS$_NOSUCHNODE:
2390 case SS$_UNREACHABLE:
2391 case SS$_NONEXPR:
2392 errno = ESRCH;
2393 break;
2394 case SS$_NOPRIV:
2395 errno = EPERM;
2396 break;
2397 default:
2398 errno = EVMSERR;
2399 }
2400 vaxc$errno=status;
2401 return -1;
2402 }
2403
9c1171d1 2404 code = Perl_sig_to_vmscondition_int(sig);
2e34cc90 2405
7a7fd8e0
JM
2406 if (!code) {
2407 SETERRNO(EINVAL, SS$_BADPARAM);
2408 return -1;
2409 }
2410
2411 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2412 * signals are to be sent to multiple processes.
2413 * pid = 0 - all processes in group except ones that the system exempts
2414 * pid = -1 - all processes except ones that the system exempts
2415 * pid = -n - all processes in group (abs(n)) except ...
2416 * For now, just report as not supported.
2417 */
2418
2419 if (pid <= 0) {
2420 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
f2610a60
CL
2421 return -1;
2422 }
2423
2e34cc90 2424 iss = sys$sigprc((unsigned int *)&pid,0,code);
f2610a60
CL
2425 if (iss&1) return 0;
2426
2427 switch (iss) {
2428 case SS$_NOPRIV:
2429 set_errno(EPERM); break;
2430 case SS$_NONEXPR:
2431 case SS$_NOSUCHNODE:
2432 case SS$_UNREACHABLE:
2433 set_errno(ESRCH); break;
2434 case SS$_INSFMEM:
2435 set_errno(ENOMEM); break;
2436 default:
ebd4d70b 2437 _ckvmssts_noperl(iss);
f2610a60
CL
2438 set_errno(EVMSERR);
2439 }
2440 set_vaxc_errno(iss);
2441
2442 return -1;
2443}
2444#endif
2445
2fbb330f
JM
2446/* Routine to convert a VMS status code to a UNIX status code.
2447** More tricky than it appears because of conflicting conventions with
2448** existing code.
2449**
2450** VMS status codes are a bit mask, with the least significant bit set for
2451** success.
2452**
2453** Special UNIX status of EVMSERR indicates that no translation is currently
2454** available, and programs should check the VMS status code.
2455**
2456** Programs compiled with _POSIX_EXIT have a special encoding that requires
2457** decoding.
2458*/
2459
2460#ifndef C_FACILITY_NO
2461#define C_FACILITY_NO 0x350000
2462#endif
2463#ifndef DCL_IVVERB
2464#define DCL_IVVERB 0x38090
2465#endif
2466
7a7fd8e0 2467int Perl_vms_status_to_unix(int vms_status, int child_flag)
2fbb330f
JM
2468{
2469int facility;
2470int fac_sp;
2471int msg_no;
2472int msg_status;
2473int unix_status;
2474
2475 /* Assume the best or the worst */
2476 if (vms_status & STS$M_SUCCESS)
2477 unix_status = 0;
2478 else
2479 unix_status = EVMSERR;
2480
2481 msg_status = vms_status & ~STS$M_CONTROL;
2482
2483 facility = vms_status & STS$M_FAC_NO;
2484 fac_sp = vms_status & STS$M_FAC_SP;
2485 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2486
0968cdad 2487 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2fbb330f
JM
2488 switch(msg_no) {
2489 case SS$_NORMAL:
2490 unix_status = 0;
2491 break;
2492 case SS$_ACCVIO:
2493 unix_status = EFAULT;
2494 break;
7a7fd8e0
JM
2495 case SS$_DEVOFFLINE:
2496 unix_status = EBUSY;
2497 break;
2498 case SS$_CLEARED:
2499 unix_status = ENOTCONN;
2500 break;
2501 case SS$_IVCHAN:
2fbb330f
JM
2502 case SS$_IVLOGNAM:
2503 case SS$_BADPARAM:
2504 case SS$_IVLOGTAB:
2505 case SS$_NOLOGNAM:
2506 case SS$_NOLOGTAB:
2507 case SS$_INVFILFOROP:
2508 case SS$_INVARG:
2509 case SS$_NOSUCHID:
2510 case SS$_IVIDENT:
2511 unix_status = EINVAL;
2512 break;
7a7fd8e0
JM
2513 case SS$_UNSUPPORTED:
2514 unix_status = ENOTSUP;
2515 break;
2fbb330f
JM
2516 case SS$_FILACCERR:
2517 case SS$_NOGRPPRV:
2518 case SS$_NOSYSPRV:
2519 unix_status = EACCES;
2520 break;
2521 case SS$_DEVICEFULL:
2522 unix_status = ENOSPC;
2523 break;
2524 case SS$_NOSUCHDEV:
2525 unix_status = ENODEV;
2526 break;
2527 case SS$_NOSUCHFILE:
2528 case SS$_NOSUCHOBJECT:
2529 unix_status = ENOENT;
2530 break;
fb38d079
JM
2531 case SS$_ABORT: /* Fatal case */
2532 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2533 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2fbb330f
JM
2534 unix_status = EINTR;
2535 break;
2536 case SS$_BUFFEROVF:
2537 unix_status = E2BIG;
2538 break;
2539 case SS$_INSFMEM:
2540 unix_status = ENOMEM;
2541 break;
2542 case SS$_NOPRIV:
2543 unix_status = EPERM;
2544 break;
2545 case SS$_NOSUCHNODE:
2546 case SS$_UNREACHABLE:
2547 unix_status = ESRCH;
2548 break;
2549 case SS$_NONEXPR:
2550 unix_status = ECHILD;
2551 break;
2552 default:
2553 if ((facility == 0) && (msg_no < 8)) {
2554 /* These are not real VMS status codes so assume that they are
2555 ** already UNIX status codes
2556 */
2557 unix_status = msg_no;
2558 break;
2559 }
2560 }
2561 }
2562 else {
2563 /* Translate a POSIX exit code to a UNIX exit code */
2564 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
7a7fd8e0 2565 unix_status = (msg_no & 0x07F8) >> 3;
2fbb330f
JM
2566 }
2567 else {
7a7fd8e0
JM
2568
2569 /* Documented traditional behavior for handling VMS child exits */
2570 /*--------------------------------------------------------------*/
2571 if (child_flag != 0) {
2572
2573 /* Success / Informational return 0 */
2574 /*----------------------------------*/
2575 if (msg_no & STS$K_SUCCESS)
2576 return 0;
2577
2578 /* Warning returns 1 */
2579 /*-------------------*/
2580 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2581 return 1;
2582
2583 /* Everything else pass through the severity bits */
2584 /*------------------------------------------------*/
2585 return (msg_no & STS$M_SEVERITY);
2586 }
2587
2588 /* Normal VMS status to ERRNO mapping attempt */
2589 /*--------------------------------------------*/
2fbb330f
JM
2590 switch(msg_status) {
2591 /* case RMS$_EOF: */ /* End of File */
2592 case RMS$_FNF: /* File Not Found */
2593 case RMS$_DNF: /* Dir Not Found */
2594 unix_status = ENOENT;
2595 break;
2596 case RMS$_RNF: /* Record Not Found */
2597 unix_status = ESRCH;
2598 break;
2599 case RMS$_DIR:
2600 unix_status = ENOTDIR;
2601 break;
2602 case RMS$_DEV:
2603 unix_status = ENODEV;
2604 break;
7a7fd8e0
JM
2605 case RMS$_IFI:
2606 case RMS$_FAC:
2607 case RMS$_ISI:
2608 unix_status = EBADF;
2609 break;
2610 case RMS$_FEX:
2611 unix_status = EEXIST;
2612 break;
2fbb330f
JM
2613 case RMS$_SYN:
2614 case RMS$_FNM:
2615 case LIB$_INVSTRDES:
2616 case LIB$_INVARG:
2617 case LIB$_NOSUCHSYM:
2618 case LIB$_INVSYMNAM:
2619 case DCL_IVVERB:
2620 unix_status = EINVAL;
2621 break;
2622 case CLI$_BUFOVF:
2623 case RMS$_RTB:
2624 case CLI$_TKNOVF:
2625 case CLI$_RSLOVF:
2626 unix_status = E2BIG;
2627 break;
2628 case RMS$_PRV: /* No privilege */
2629 case RMS$_ACC: /* ACP file access failed */
2630 case RMS$_WLK: /* Device write locked */
2631 unix_status = EACCES;
2632 break;
ed1b9de0
JM
2633 case RMS$_MKD: /* Failed to mark for delete */
2634 unix_status = EPERM;
2635 break;
2fbb330f
JM
2636 /* case RMS$_NMF: */ /* No more files */
2637 }
2638 }
2639 }
2640
2641 return unix_status;
2642}
2643
7a7fd8e0
JM
2644/* Try to guess at what VMS error status should go with a UNIX errno
2645 * value. This is hard to do as there could be many possible VMS
2646 * error statuses that caused the errno value to be set.
2647 */
2648
2649int Perl_unix_status_to_vms(int unix_status)
2650{
2651int test_unix_status;
2652
2653 /* Trivial cases first */
2654 /*---------------------*/
2655 if (unix_status == EVMSERR)
2656 return vaxc$errno;
2657
2658 /* Is vaxc$errno sane? */
2659 /*---------------------*/
2660 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2661 if (test_unix_status == unix_status)
2662 return vaxc$errno;
2663
2664 /* If way out of range, must be VMS code already */
2665 /*-----------------------------------------------*/
2666 if (unix_status > EVMSERR)
2667 return unix_status;
2668
2669 /* If out of range, punt */
2670 /*-----------------------*/
2671 if (unix_status > __ERRNO_MAX)
2672 return SS$_ABORT;
2673
2674
2675 /* Ok, now we have to do it the hard way. */
2676 /*----------------------------------------*/
2677 switch(unix_status) {
2678 case 0: return SS$_NORMAL;
2679 case EPERM: return SS$_NOPRIV;
2680 case ENOENT: return SS$_NOSUCHOBJECT;
2681 case ESRCH: return SS$_UNREACHABLE;
2682 case EINTR: return SS$_ABORT;
2683 /* case EIO: */
2684 /* case ENXIO: */
2685 case E2BIG: return SS$_BUFFEROVF;
2686 /* case ENOEXEC */
2687 case EBADF: return RMS$_IFI;
2688 case ECHILD: return SS$_NONEXPR;
2689 /* case EAGAIN */
2690 case ENOMEM: return SS$_INSFMEM;
2691 case EACCES: return SS$_FILACCERR;
2692 case EFAULT: return SS$_ACCVIO;
2693 /* case ENOTBLK */
0968cdad 2694 case EBUSY: return SS$_DEVOFFLINE;
7a7fd8e0
JM
2695 case EEXIST: return RMS$_FEX;
2696 /* case EXDEV */
2697 case ENODEV: return SS$_NOSUCHDEV;
2698 case ENOTDIR: return RMS$_DIR;
2699 /* case EISDIR */
2700 case EINVAL: return SS$_INVARG;
2701 /* case ENFILE */
2702 /* case EMFILE */
2703 /* case ENOTTY */
2704 /* case ETXTBSY */
2705 /* case EFBIG */
2706 case ENOSPC: return SS$_DEVICEFULL;
2707 case ESPIPE: return LIB$_INVARG;
2708 /* case EROFS: */
2709 /* case EMLINK: */
2710 /* case EPIPE: */
2711 /* case EDOM */
2712 case ERANGE: return LIB$_INVARG;
2713 /* case EWOULDBLOCK */
2714 /* case EINPROGRESS */
2715 /* case EALREADY */
2716 /* case ENOTSOCK */
2717 /* case EDESTADDRREQ */
2718 /* case EMSGSIZE */
2719 /* case EPROTOTYPE */
2720 /* case ENOPROTOOPT */
2721 /* case EPROTONOSUPPORT */
2722 /* case ESOCKTNOSUPPORT */
2723 /* case EOPNOTSUPP */
2724 /* case EPFNOSUPPORT */
2725 /* case EAFNOSUPPORT */
2726 /* case EADDRINUSE */
2727 /* case EADDRNOTAVAIL */
2728 /* case ENETDOWN */
2729 /* case ENETUNREACH */
2730 /* case ENETRESET */
2731 /* case ECONNABORTED */
2732 /* case ECONNRESET */
2733 /* case ENOBUFS */
2734 /* case EISCONN */
2735 case ENOTCONN: return SS$_CLEARED;
2736 /* case ESHUTDOWN */
2737 /* case ETOOMANYREFS */
2738 /* case ETIMEDOUT */
2739 /* case ECONNREFUSED */
2740 /* case ELOOP */
2741 /* case ENAMETOOLONG */
2742 /* case EHOSTDOWN */
2743 /* case EHOSTUNREACH */
2744 /* case ENOTEMPTY */
2745 /* case EPROCLIM */
2746 /* case EUSERS */
2747 /* case EDQUOT */
2748 /* case ENOMSG */
2749 /* case EIDRM */
2750 /* case EALIGN */
2751 /* case ESTALE */
2752 /* case EREMOTE */
2753 /* case ENOLCK */
2754 /* case ENOSYS */
2755 /* case EFTYPE */
2756 /* case ECANCELED */
2757 /* case EFAIL */
2758 /* case EINPROG */
2759 case ENOTSUP:
2760 return SS$_UNSUPPORTED;
2761 /* case EDEADLK */
2762 /* case ENWAIT */
2763 /* case EILSEQ */
2764 /* case EBADCAT */
2765 /* case EBADMSG */
2766 /* case EABANDONED */
2767 default:
2768 return SS$_ABORT; /* punt */
2769 }
7a7fd8e0 2770}
2fbb330f
JM
2771
2772
22d4bb9c 2773/* default piping mailbox size */
df17c887
CB
2774#ifdef __VAX
2775# define PERL_BUFSIZ 512
2776#else
2777# define PERL_BUFSIZ 8192
2778#endif
22d4bb9c 2779
674d6c38 2780
a0d0e21e 2781static void
8a646e0b 2782create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
a0d0e21e 2783{
22d4bb9c
CB
2784 unsigned long int mbxbufsiz;
2785 static unsigned long int syssize = 0;
2786 unsigned long int dviitm = DVI$_DEVNAM;
22d4bb9c 2787 char csize[LNM$C_NAMLENGTH+1];
f7ddb74a
JM
2788 int sts;
2789
22d4bb9c
CB
2790 if (!syssize) {
2791 unsigned long syiitm = SYI$_MAXBUF;
a0d0e21e 2792 /*
22d4bb9c
CB
2793 * Get the SYSGEN parameter MAXBUF
2794 *
2795 * If the logical 'PERL_MBX_SIZE' is defined
2796 * use the value of the logical instead of PERL_BUFSIZ, but
2797 * keep the size between 128 and MAXBUF.
2798 *
a0d0e21e 2799 */
ebd4d70b 2800 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
22d4bb9c
CB
2801 }
2802
2803 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2804 mbxbufsiz = atoi(csize);
2805 } else {
2806 mbxbufsiz = PERL_BUFSIZ;
a0d0e21e 2807 }
22d4bb9c
CB
2808 if (mbxbufsiz < 128) mbxbufsiz = 128;
2809 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2810
ebd4d70b 2811 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
a0d0e21e 2812
ebd4d70b
JM
2813 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2814 _ckvmssts_noperl(sts);
a0d0e21e
LW
2815 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2816
2817} /* end of create_mbx() */
2818
22d4bb9c 2819
a0d0e21e 2820/*{{{ my_popen and my_pclose*/
22d4bb9c
CB
2821
2822typedef struct _iosb IOSB;
2823typedef struct _iosb* pIOSB;
2824typedef struct _pipe Pipe;
2825typedef struct _pipe* pPipe;
2826typedef struct pipe_details Info;
2827typedef struct pipe_details* pInfo;
2828typedef struct _srqp RQE;
2829typedef struct _srqp* pRQE;
2830typedef struct _tochildbuf CBuf;
2831typedef struct _tochildbuf* pCBuf;
2832
2833struct _iosb {
2834 unsigned short status;
2835 unsigned short count;
2836 unsigned long dvispec;
2837};
2838
2839#pragma member_alignment save
2840#pragma nomember_alignment quadword
2841struct _srqp { /* VMS self-relative queue entry */
2842 unsigned long qptr[2];
2843};
2844#pragma member_alignment restore
2845static RQE RQE_ZERO = {0,0};
2846
2847struct _tochildbuf {
2848 RQE q;
2849 int eof;
2850 unsigned short size;
2851 char *buf;
2852};
2853
2854struct _pipe {
2855 RQE free;
2856 RQE wait;
2857 int fd_out;
2858 unsigned short chan_in;
2859 unsigned short chan_out;
2860 char *buf;
2861 unsigned int bufsize;
2862 IOSB iosb;
2863 IOSB iosb2;
2864 int *pipe_done;
2865 int retry;
2866 int type;
2867 int shut_on_empty;
2868 int need_wake;
2869 pPipe *home;
2870 pInfo info;
2871 pCBuf curr;
2872 pCBuf curr2;
fd8cd3a3
DS
2873#if defined(PERL_IMPLICIT_CONTEXT)
2874 void *thx; /* Either a thread or an interpreter */
2875 /* pointer, depending on how we're built */
2876#endif
22d4bb9c
CB
2877};
2878
2879
a0d0e21e
LW
2880struct pipe_details
2881{
22d4bb9c 2882 pInfo next;
ff7adb52
CL
2883 PerlIO *fp; /* file pointer to pipe mailbox */
2884 int useFILE; /* using stdio, not perlio */
748a9306
LW
2885 int pid; /* PID of subprocess */
2886 int mode; /* == 'r' if pipe open for reading */
2887 int done; /* subprocess has completed */
ff7adb52 2888 int waiting; /* waiting for completion/closure */
22d4bb9c
CB
2889 int closing; /* my_pclose is closing this pipe */
2890 unsigned long completion; /* termination status of subprocess */
2891 pPipe in; /* pipe in to sub */
2892 pPipe out; /* pipe out of sub */
2893 pPipe err; /* pipe of sub's sys$error */
2894 int in_done; /* true when in pipe finished */
2895 int out_done;
2896 int err_done;
cd1191f1
CB
2897 unsigned short xchan; /* channel to debug xterm */
2898 unsigned short xchan_valid; /* channel is assigned */
a0d0e21e
LW
2899};
2900
748a9306
LW
2901struct exit_control_block
2902{
2903 struct exit_control_block *flink;
f7c699a0 2904 unsigned long int (*exit_routine)(void);
748a9306
LW
2905 unsigned long int arg_count;
2906 unsigned long int *status_address;
2907 unsigned long int exit_status;
2908};
2909
d85f548a
JH
2910typedef struct _closed_pipes Xpipe;
2911typedef struct _closed_pipes* pXpipe;
2912
2913struct _closed_pipes {
2914 int pid; /* PID of subprocess */
2915 unsigned long completion; /* termination status of subprocess */
2916};
2917#define NKEEPCLOSED 50
2918static Xpipe closed_list[NKEEPCLOSED];
2919static int closed_index = 0;
2920static int closed_num = 0;
2921
22d4bb9c
CB
2922#define RETRY_DELAY "0 ::0.20"
2923#define MAX_RETRY 50
a0d0e21e 2924
22d4bb9c
CB
2925static int pipe_ef = 0; /* first call to safe_popen inits these*/
2926static unsigned long mypid;
2927static unsigned long delaytime[2];
2928
2929static pInfo open_pipes = NULL;
2930static $DESCRIPTOR(nl_desc, "NL:");
3eeba6fb 2931
ff7adb52
CL
2932#define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2933
2934
3eeba6fb 2935
748a9306 2936static unsigned long int
f7c699a0 2937pipe_exit_routine(void)
748a9306 2938{
22d4bb9c 2939 pInfo info;
1e422769 2940 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
4e0c9737 2941 int sts, did_stuff, j;
ff7adb52 2942
5ce486e0
CB
2943 /*
2944 * Flush any pending i/o, but since we are in process run-down, be
2945 * careful about referencing PerlIO structures that may already have
2946 * been deallocated. We may not even have an interpreter anymore.
ff7adb52
CL
2947 */
2948 info = open_pipes;
2949 while (info) {
2950 if (info->fp) {
ebd4d70b
JM
2951#if defined(PERL_IMPLICIT_CONTEXT)
2952 /* We need to use the Perl context of the thread that created */
2953 /* the pipe. */
2954 pTHX;
2955 if (info->err)
2956 aTHX = info->err->thx;
2957 else if (info->out)
2958 aTHX = info->out->thx;
2959 else if (info->in)
2960 aTHX = info->in->thx;
2961#endif
5ce486e0
CB
2962 if (!info->useFILE
2963#if defined(USE_ITHREADS)
2964 && my_perl
2965#endif
a24c654f
CB
2966#ifdef USE_PERLIO
2967 && PL_perlio_fd_refcnt
2968#endif
2969 )
5ce486e0 2970 PerlIO_flush(info->fp);
ff7adb52
CL
2971 else
2972 fflush((FILE *)info->fp);
2973 }
2974 info = info->next;
2975 }
3eeba6fb
CB
2976
2977 /*
ff7adb52 2978 next we try sending an EOF...ignore if doesn't work, make sure we
3eeba6fb
CB
2979 don't hang
2980 */
2981 did_stuff = 0;
2982 info = open_pipes;
748a9306 2983
3eeba6fb 2984 while (info) {
d4c83939 2985 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 2986 if (info->in && !info->in->shut_on_empty) {
d4c83939 2987 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
ebd4d70b 2988 0, 0, 0, 0, 0, 0));
ff7adb52 2989 info->waiting = 1;
22d4bb9c 2990 did_stuff = 1;
748a9306 2991 }
d4c83939 2992 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
2993 info = info->next;
2994 }
ff7adb52
CL
2995
2996 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2997
2998 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2999 int nwait = 0;
3000
3001 info = open_pipes;
3002 while (info) {
d4c83939 3003 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3004 if (info->waiting && info->done)
3005 info->waiting = 0;
3006 nwait += info->waiting;
d4c83939 3007 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3008 info = info->next;
3009 }
3010 if (!nwait) break;
3011 sleep(1);
3012 }
3eeba6fb
CB
3013
3014 did_stuff = 0;
3015 info = open_pipes;
3016 while (info) {
d4c83939 3017 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3018 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3019 sts = sys$forcex(&info->pid,0,&abort);
d4c83939 3020 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3eeba6fb
CB
3021 did_stuff = 1;
3022 }
d4c83939 3023 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3024 info = info->next;
3025 }
ff7adb52
CL
3026
3027 /* again, wait for effect */
3028
3029 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3030 int nwait = 0;
3031
3032 info = open_pipes;
3033 while (info) {
d4c83939 3034 _ckvmssts_noperl(sys$setast(0));
ff7adb52
CL
3035 if (info->waiting && info->done)
3036 info->waiting = 0;
3037 nwait += info->waiting;
d4c83939 3038 _ckvmssts_noperl(sys$setast(1));
ff7adb52
CL
3039 info = info->next;
3040 }
3041 if (!nwait) break;
3042 sleep(1);
3043 }
3eeba6fb
CB
3044
3045 info = open_pipes;
3046 while (info) {
d4c83939 3047 _ckvmssts_noperl(sys$setast(0));
3eeba6fb
CB
3048 if (!info->done) { /* We tried to be nice . . . */
3049 sts = sys$delprc(&info->pid,0);
d4c83939 3050 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2f1dcba4 3051 info->done = 1; /* sys$delprc is as done as we're going to get. */
3eeba6fb 3052 }
d4c83939 3053 _ckvmssts_noperl(sys$setast(1));
3eeba6fb
CB
3054 info = info->next;
3055 }
3056
3057 while(open_pipes) {
ebd4d70b
JM
3058
3059#if defined(PERL_IMPLICIT_CONTEXT)
3060 /* We need to use the Perl context of the thread that created */
3061 /* the pipe. */
3062 pTHX;
36b6faa8
CB
3063 if (open_pipes->err)
3064 aTHX = open_pipes->err->thx;
3065 else if (open_pipes->out)
3066 aTHX = open_pipes->out->thx;
3067 else if (open_pipes->in)
3068 aTHX = open_pipes->in->thx;
ebd4d70b 3069#endif
1e422769 3070 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3071 else if (!(sts & 1)) retsts = sts;
748a9306
LW
3072 }
3073 return retsts;
3074}
3075
3076static struct exit_control_block pipe_exitblock =
3077 {(struct exit_control_block *) 0,
3078 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3079
22d4bb9c
CB
3080static void pipe_mbxtofd_ast(pPipe p);
3081static void pipe_tochild1_ast(pPipe p);
3082static void pipe_tochild2_ast(pPipe p);
748a9306 3083
a0d0e21e 3084static void
22d4bb9c 3085popen_completion_ast(pInfo info)
a0d0e21e 3086{
22d4bb9c
CB
3087 pInfo i = open_pipes;
3088 int iss;
d85f548a
JH
3089
3090 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3091 closed_list[closed_index].pid = info->pid;
3092 closed_list[closed_index].completion = info->completion;
3093 closed_index++;
3094 if (closed_index == NKEEPCLOSED)
3095 closed_index = 0;
3096 closed_num++;
22d4bb9c
CB
3097
3098 while (i) {
3099 if (i == info) break;
3100 i = i->next;
3101 }
3102 if (!i) return; /* unlinked, probably freed too */
3103
22d4bb9c
CB
3104 info->done = TRUE;
3105
3106/*
3107 Writing to subprocess ...
3108 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3109
3110 chan_out may be waiting for "done" flag, or hung waiting
3111 for i/o completion to child...cancel the i/o. This will
3112 put it into "snarf mode" (done but no EOF yet) that discards
3113 input.
3114
3115 Output from subprocess (stdout, stderr) needs to be flushed and
3116 shut down. We try sending an EOF, but if the mbx is full the pipe
3117 routine should still catch the "shut_on_empty" flag, telling it to
3118 use immediate-style reads so that "mbx empty" -> EOF.
3119
3120
3121*/
3122 if (info->in && !info->in_done) { /* only for mode=w */
3123 if (info->in->shut_on_empty && info->in->need_wake) {
3124 info->in->need_wake = FALSE;
fd8cd3a3 3125 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
22d4bb9c 3126 } else {
fd8cd3a3 3127 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
22d4bb9c
CB
3128 }
3129 }
3130
3131 if (info->out && !info->out_done) { /* were we also piping output? */
3132 info->out->shut_on_empty = TRUE;
3133 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3134 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
fd8cd3a3 3135 _ckvmssts_noperl(iss);
22d4bb9c
CB
3136 }
3137
3138 if (info->err && !info->err_done) { /* we were piping stderr */
3139 info->err->shut_on_empty = TRUE;
3140 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3141 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
fd8cd3a3 3142 _ckvmssts_noperl(iss);
a0d0e21e 3143 }
fd8cd3a3 3144 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3145
a0d0e21e
LW
3146}
3147
2fbb330f 3148static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
218fdd94 3149static void vms_execfree(struct dsc$descriptor_s *vmscmd);
22d4bb9c
CB
3150static void pipe_infromchild_ast(pPipe p);
3151
3152/*
3153 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3154 inside an AST routine without worrying about reentrancy and which Perl
3155 memory allocator is being used.
3156
3157 We read data and queue up the buffers, then spit them out one at a
3158 time to the output mailbox when the output mailbox is ready for one.
3159
3160*/
3161#define INITIAL_TOCHILDQUEUE 2
3162
3163static pPipe
fd8cd3a3 3164pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3165{
22d4bb9c
CB
3166 pPipe p;
3167 pCBuf b;
3168 char mbx1[64], mbx2[64];
3169 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3170 DSC$K_CLASS_S, mbx1},
3171 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3172 DSC$K_CLASS_S, mbx2};
3173 unsigned int dviitm = DVI$_DEVBUFSIZ;
3174 int j, n;
3175
d4c83939 3176 n = sizeof(Pipe);
ebd4d70b 3177 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3178
8a646e0b
JM
3179 create_mbx(&p->chan_in , &d_mbx1);
3180 create_mbx(&p->chan_out, &d_mbx2);
ebd4d70b 3181 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
22d4bb9c
CB
3182
3183 p->buf = 0;
3184 p->shut_on_empty = FALSE;
3185 p->need_wake = FALSE;
3186 p->type = 0;
3187 p->retry = 0;
3188 p->iosb.status = SS$_NORMAL;
3189 p->iosb2.status = SS$_NORMAL;
3190 p->free = RQE_ZERO;
3191 p->wait = RQE_ZERO;
3192 p->curr = 0;
3193 p->curr2 = 0;
3194 p->info = 0;
fd8cd3a3
DS
3195#ifdef PERL_IMPLICIT_CONTEXT
3196 p->thx = aTHX;
3197#endif
22d4bb9c
CB
3198
3199 n = sizeof(CBuf) + p->bufsize;
3200
3201 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
ebd4d70b 3202 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c 3203 b->buf = (char *) b + sizeof(CBuf);
ebd4d70b 3204 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
3205 }
3206
3207 pipe_tochild2_ast(p);
3208 pipe_tochild1_ast(p);
3209 strcpy(wmbx, mbx1);
3210 strcpy(rmbx, mbx2);
3211 return p;
3212}
3213
3214/* reads the MBX Perl is writing, and queues */
3215
3216static void
3217pipe_tochild1_ast(pPipe p)
3218{
22d4bb9c
CB
3219 pCBuf b = p->curr;
3220 int iss = p->iosb.status;
3221 int eof = (iss == SS$_ENDOFFILE);
f7ddb74a 3222 int sts;
fd8cd3a3
DS
3223#ifdef PERL_IMPLICIT_CONTEXT
3224 pTHX = p->thx;
3225#endif
22d4bb9c
CB
3226
3227 if (p->retry) {
3228 if (eof) {
3229 p->shut_on_empty = TRUE;
3230 b->eof = TRUE;
ebd4d70b 3231 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c 3232 } else {
ebd4d70b 3233 _ckvmssts_noperl(iss);
22d4bb9c
CB
3234 }
3235
3236 b->eof = eof;
3237 b->size = p->iosb.count;
ebd4d70b 3238 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
22d4bb9c
CB
3239 if (p->need_wake) {
3240 p->need_wake = FALSE;
ebd4d70b 3241 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
22d4bb9c
CB
3242 }
3243 } else {
3244 p->retry = 1; /* initial call */
3245 }
3246
3247 if (eof) { /* flush the free queue, return when done */
3248 int n = sizeof(CBuf) + p->bufsize;
3249 while (1) {
3250 iss = lib$remqti(&p->free, &b);
3251 if (iss == LIB$_QUEWASEMP) return;
ebd4d70b
JM
3252 _ckvmssts_noperl(iss);
3253 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c
CB
3254 }
3255 }
3256
3257 iss = lib$remqti(&p->free, &b);
3258 if (iss == LIB$_QUEWASEMP) {
3259 int n = sizeof(CBuf) + p->bufsize;
ebd4d70b 3260 _ckvmssts_noperl(lib$get_vm(&n, &b));
22d4bb9c
CB
3261 b->buf = (char *) b + sizeof(CBuf);
3262 } else {
ebd4d70b 3263 _ckvmssts_noperl(iss);
22d4bb9c
CB
3264 }
3265
3266 p->curr = b;
3267 iss = sys$qio(0,p->chan_in,
3268 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3269 &p->iosb,
3270 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3271 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
ebd4d70b 3272 _ckvmssts_noperl(iss);
22d4bb9c
CB
3273}
3274
3275
3276/* writes queued buffers to output, waits for each to complete before
3277 doing the next */
3278
3279static void
3280pipe_tochild2_ast(pPipe p)
3281{
22d4bb9c
CB
3282 pCBuf b = p->curr2;
3283 int iss = p->iosb2.status;
3284 int n = sizeof(CBuf) + p->bufsize;
3285 int done = (p->info && p->info->done) ||
3286 iss == SS$_CANCEL || iss == SS$_ABORT;
fd8cd3a3
DS
3287#if defined(PERL_IMPLICIT_CONTEXT)
3288 pTHX = p->thx;
3289#endif
22d4bb9c
CB
3290
3291 do {
3292 if (p->type) { /* type=1 has old buffer, dispose */
3293 if (p->shut_on_empty) {
ebd4d70b 3294 _ckvmssts_noperl(lib$free_vm(&n, &b));
22d4bb9c 3295 } else {
ebd4d70b 3296 _ckvmssts_noperl(lib$insqhi(b, &p->free));
22d4bb9c
CB
3297 }
3298 p->type = 0;
3299 }
3300
3301 iss = lib$remqti(&p->wait, &b);
3302 if (iss == LIB$_QUEWASEMP) {
3303 if (p->shut_on_empty) {
3304 if (done) {
ebd4d70b 3305 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c 3306 *p->pipe_done = TRUE;
ebd4d70b 3307 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c 3308 } else {
ebd4d70b 3309 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3310 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3311 }
3312 return;
3313 }
3314 p->need_wake = TRUE;
3315 return;
3316 }
ebd4d70b 3317 _ckvmssts_noperl(iss);
22d4bb9c
CB
3318 p->type = 1;
3319 } while (done);
3320
3321
3322 p->curr2 = b;
3323 if (b->eof) {
ebd4d70b 3324 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
22d4bb9c
CB
3325 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3326 } else {
ebd4d70b 3327 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
22d4bb9c
CB
3328 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3329 }
3330
3331 return;
3332
3333}
3334
3335
3336static pPipe
fd8cd3a3 3337pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
22d4bb9c 3338{
22d4bb9c
CB
3339 pPipe p;
3340 char mbx1[64], mbx2[64];
3341 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3342 DSC$K_CLASS_S, mbx1},
3343 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3344 DSC$K_CLASS_S, mbx2};
3345 unsigned int dviitm = DVI$_DEVBUFSIZ;
3346
d4c83939 3347 int n = sizeof(Pipe);
ebd4d70b 3348 _ckvmssts_noperl(lib$get_vm(&n, &p));
8a646e0b
JM
3349 create_mbx(&p->chan_in , &d_mbx1);
3350 create_mbx(&p->chan_out, &d_mbx2);
22d4bb9c 3351
ebd4d70b 3352 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3353 n = p->bufsize * sizeof(char);
ebd4d70b 3354 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3355 p->shut_on_empty = FALSE;
3356 p->info = 0;
3357 p->type = 0;
3358 p->iosb.status = SS$_NORMAL;
fd8cd3a3
DS
3359#if defined(PERL_IMPLICIT_CONTEXT)
3360 p->thx = aTHX;
3361#endif
22d4bb9c
CB
3362 pipe_infromchild_ast(p);
3363
3364 strcpy(wmbx, mbx1);
3365 strcpy(rmbx, mbx2);
3366 return p;
3367}
3368
3369static void
3370pipe_infromchild_ast(pPipe p)
3371{
22d4bb9c
CB
3372 int iss = p->iosb.status;
3373 int eof = (iss == SS$_ENDOFFILE);
3374 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3375 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
fd8cd3a3
DS
3376#if defined(PERL_IMPLICIT_CONTEXT)
3377 pTHX = p->thx;
3378#endif
22d4bb9c
CB
3379
3380 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
ebd4d70b 3381 _ckvmssts_noperl(sys$dassgn(p->chan_out));
22d4bb9c
CB
3382 p->chan_out = 0;
3383 }
3384
3385 /* read completed:
3386 input shutdown if EOF from self (done or shut_on_empty)
3387 output shutdown if closing flag set (my_pclose)
3388 send data/eof from child or eof from self
3389 otherwise, re-read (snarf of data from child)
3390 */
3391
3392 if (p->type == 1) {
3393 p->type = 0;
3394 if (myeof && p->chan_in) { /* input shutdown */
ebd4d70b 3395 _ckvmssts_noperl(sys$dassgn(p->chan_in));
22d4bb9c
CB
3396 p->chan_in = 0;
3397 }
3398
3399 if (p->chan_out) {
3400 if (myeof || kideof) { /* pass EOF to parent */
ebd4d70b
JM
3401 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3402 pipe_infromchild_ast, p,
3403 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
3404 return;
3405 } else if (eof) { /* eat EOF --- fall through to read*/
3406
3407 } else { /* transmit data */
ebd4d70b
JM
3408 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3409 pipe_infromchild_ast,p,
3410 p->buf, p->iosb.count, 0, 0, 0, 0));
22d4bb9c
CB
3411 return;
3412 }
3413 }
3414 }
3415
3416 /* everything shut? flag as done */
3417
3418 if (!p->chan_in && !p->chan_out) {
3419 *p->pipe_done = TRUE;
ebd4d70b 3420 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
3421 return;
3422 }
3423
3424 /* write completed (or read, if snarfing from child)
3425 if still have input active,
3426 queue read...immediate mode if shut_on_empty so we get EOF if empty
3427 otherwise,
3428 check if Perl reading, generate EOFs as needed
3429 */
3430
3431 if (p->type == 0) {
3432 p->type = 1;
3433 if (p->chan_in) {
3434 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3435 pipe_infromchild_ast,p,
3436 p->buf, p->bufsize, 0, 0, 0, 0);
3437 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
ebd4d70b 3438 _ckvmssts_noperl(iss);
22d4bb9c
CB
3439 } else { /* send EOFs for extra reads */
3440 p->iosb.status = SS$_ENDOFFILE;
3441 p->iosb.dvispec = 0;
ebd4d70b
JM
3442 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3443 0, 0, 0,
3444 pipe_infromchild_ast, p, 0, 0, 0, 0));
22d4bb9c
CB
3445 }
3446 }
3447}
3448
3449static pPipe
fd8cd3a3 3450pipe_mbxtofd_setup(pTHX_ int fd, char *out)
22d4bb9c 3451{
22d4bb9c
CB
3452 pPipe p;
3453 char mbx[64];
3454 unsigned long dviitm = DVI$_DEVBUFSIZ;
3455 struct stat s;
3456 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3457 DSC$K_CLASS_S, mbx};
a480973c 3458 int n = sizeof(Pipe);
22d4bb9c
CB
3459
3460 /* things like terminals and mbx's don't need this filter */
3461 if (fd && fstat(fd,&s) == 0) {
4e0c9737 3462 unsigned long devchar;
cfcfe586
JM
3463 char device[65];
3464 unsigned short dev_len;
3465 struct dsc$descriptor_s d_dev;
3466 char * cptr;
3467 struct item_list_3 items[3];
3468 int status;
3469 unsigned short dvi_iosb[4];
3470
3471 cptr = getname(fd, out, 1);
ebd4d70b 3472 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
cfcfe586
JM
3473 d_dev.dsc$a_pointer = out;
3474 d_dev.dsc$w_length = strlen(out);
3475 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3476 d_dev.dsc$b_class = DSC$K_CLASS_S;
3477
3478 items[0].len = 4;
3479 items[0].code = DVI$_DEVCHAR;
3480 items[0].bufadr = &devchar;
3481 items[0].retadr = NULL;
3482 items[1].len = 64;
3483 items[1].code = DVI$_FULLDEVNAM;
3484 items[1].bufadr = device;
3485 items[1].retadr = &dev_len;
3486 items[2].len = 0;
3487 items[2].code = 0;
3488
3489 status = sys$getdviw
3490 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
ebd4d70b 3491 _ckvmssts_noperl(status);
cfcfe586
JM
3492 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3493 device[dev_len] = 0;
3494
3495 if (!(devchar & DEV$M_DIR)) {
3496 strcpy(out, device);
3497 return 0;
3498 }
3499 }
22d4bb9c
CB
3500 }
3501
ebd4d70b 3502 _ckvmssts_noperl(lib$get_vm(&n, &p));
22d4bb9c 3503 p->fd_out = dup(fd);
8a646e0b 3504 create_mbx(&p->chan_in, &d_mbx);
ebd4d70b 3505 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
d4c83939 3506 n = (p->bufsize+1) * sizeof(char);
ebd4d70b 3507 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
22d4bb9c
CB
3508 p->shut_on_empty = FALSE;
3509 p->retry = 0;
3510 p->info = 0;
3511 strcpy(out, mbx);
3512
ebd4d70b
JM
3513 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3514 pipe_mbxtofd_ast, p,
3515 p->buf, p->bufsize, 0, 0, 0, 0));
22d4bb9c
CB
3516
3517 return p;
3518}
3519
3520static void
3521pipe_mbxtofd_ast(pPipe p)
3522{
22d4bb9c
CB
3523 int iss = p->iosb.status;
3524 int done = p->info->done;
3525 int iss2;
3526 int eof = (iss == SS$_ENDOFFILE);
3527 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3528 int err = !(iss&1) && !eof;
fd8cd3a3
DS
3529#if defined(PERL_IMPLICIT_CONTEXT)
3530 pTHX = p->thx;
3531#endif
22d4bb9c
CB
3532
3533 if (done && myeof) { /* end piping */
3534 close(p->fd_out);
3535 sys$dassgn(p->chan_in);
3536 *p->pipe_done = TRUE;
ebd4d70b 3537 _ckvmssts_noperl(sys$setef(pipe_ef));
22d4bb9c
CB
3538 return;
3539 }
3540
3541 if (!err && !eof) { /* good data to send to file */
3542 p->buf[p->iosb.count] = '\n';
3543 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3544 if (iss2 < 0) {
3545 p->retry++;
3546 if (p->retry < MAX_RETRY) {
ebd4d70b 3547 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
22d4bb9c
CB
3548 return;
3549 }
3550 }
3551 p->retry = 0;
3552 } else if (err) {
ebd4d70b 3553 _ckvmssts_noperl(iss);
22d4bb9c
CB
3554 }
3555
3556
3557 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3558 pipe_mbxtofd_ast, p,
3559 p->buf, p->bufsize, 0, 0, 0, 0);
3560 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
ebd4d70b 3561 _ckvmssts_noperl(iss);
22d4bb9c
CB
3562}
3563
3564
3565typedef struct _pipeloc PLOC;
3566typedef struct _pipeloc* pPLOC;
3567
3568struct _pipeloc {
3569 pPLOC next;
3570 char dir[NAM$C_MAXRSS+1];
3571};
3572static pPLOC head_PLOC = 0;
3573
5c0ae288 3574void
fd8cd3a3 3575free_pipelocs(pTHX_ void *head)
5c0ae288
CL
3576{
3577 pPLOC p, pnext;
ff7adb52 3578 pPLOC *pHead = (pPLOC *)head;
5c0ae288 3579
ff7adb52 3580 p = *pHead;
5c0ae288
CL
3581 while (p) {
3582 pnext = p->next;
e0ef6b43 3583 PerlMem_free(p);
5c0ae288
CL
3584 p = pnext;
3585 }
ff7adb52 3586 *pHead = 0;
5c0ae288 3587}
22d4bb9c
CB
3588
3589static void
fd8cd3a3 3590store_pipelocs(pTHX)
22d4bb9c
CB
3591{
3592 int i;
3593 pPLOC p;
ff7adb52 3594 AV *av = 0;
22d4bb9c 3595 SV *dirsv;
22d4bb9c
CB
3596 char *dir, *x;
3597 char *unixdir;
3598 char temp[NAM$C_MAXRSS+1];
3599 STRLEN n_a;
3600
ff7adb52 3601 if (head_PLOC)
218fdd94 3602 free_pipelocs(aTHX_ &head_PLOC);
ff7adb52 3603
22d4bb9c
CB
3604/* the . directory from @INC comes last */
3605
e0ef6b43 3606 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3607 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3608 p->next = head_PLOC;
3609 head_PLOC = p;
3610 strcpy(p->dir,"./");
3611
3612/* get the directory from $^X */
3613
c11536f5 3614 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 3615 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c5375c28 3616
218fdd94
CL
3617#ifdef PERL_IMPLICIT_CONTEXT
3618 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3619#else
22d4bb9c 3620 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
218fdd94 3621#endif
a35dcc95 3622 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
22d4bb9c 3623 x = strrchr(temp,']');
2497a41f
JM
3624 if (x == NULL) {
3625 x = strrchr(temp,'>');
3626 if (x == NULL) {
3627 /* It could be a UNIX path */
3628 x = strrchr(temp,'/');
3629 }
3630 }
3631 if (x)
3632 x[1] = '\0';
3633 else {
3634 /* Got a bare name, so use default directory */
3635 temp[0] = '.';
3636 temp[1] = '\0';
3637 }
22d4bb9c 3638
4e205ed6 3639 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
e0ef6b43 3640 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3641 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3642 p->next = head_PLOC;
3643 head_PLOC = p;
a35dcc95 3644 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
c5375c28 3645 }
22d4bb9c
CB
3646 }
3647
3648/* reverse order of @INC entries, skip "." since entered above */
3649
218fdd94
CL
3650#ifdef PERL_IMPLICIT_CONTEXT
3651 if (aTHX)
3652#endif
ff7adb52
CL
3653 if (PL_incgv) av = GvAVn(PL_incgv);
3654
3655 for (i = 0; av && i <= AvFILL(av); i++) {
22d4bb9c
CB
3656 dirsv = *av_fetch(av,i,TRUE);
3657
3658 if (SvROK(dirsv)) continue;
3659 dir = SvPVx(dirsv,n_a);
3660 if (strcmp(dir,".") == 0) continue;
4e205ed6 3661 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
22d4bb9c
CB
3662 continue;
3663
e0ef6b43 3664 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
22d4bb9c
CB
3665 p->next = head_PLOC;
3666 head_PLOC = p;
a35dcc95 3667 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
22d4bb9c
CB
3668 }
3669
3670/* most likely spot (ARCHLIB) put first in the list */
3671
3672#ifdef ARCHLIB_EXP
4e205ed6 3673 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
e0ef6b43 3674 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
ebd4d70b 3675 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
22d4bb9c
CB
3676 p->next = head_PLOC;
3677 head_PLOC = p;
a35dcc95 3678 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
22d4bb9c
CB
3679 }
3680#endif
c5375c28 3681 PerlMem_free(unixdir);
22d4bb9c
CB
3682}
3683
a1887106
JM
3684static I32
3685Perl_cando_by_name_int
3686 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3687#if !defined(PERL_IMPLICIT_CONTEXT)
3688#define cando_by_name_int Perl_cando_by_name_int
3689#else
3690#define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3691#endif
22d4bb9c
CB
3692
3693static char *
fd8cd3a3 3694find_vmspipe(pTHX)
22d4bb9c
CB
3695{
3696 static int vmspipe_file_status = 0;
3697 static char vmspipe_file[NAM$C_MAXRSS+1];
3698
3699 /* already found? Check and use ... need read+execute permission */
3700
3701 if (vmspipe_file_status == 1) {
a1887106
JM
3702 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3703 && cando_by_name_int
3704 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
22d4bb9c
CB
3705 return vmspipe_file;
3706 }
3707 vmspipe_file_status = 0;
3708 }
3709
3710 /* scan through stored @INC, $^X */
3711
3712 if (vmspipe_file_status == 0) {
3713 char file[NAM$C_MAXRSS+1];
3714 pPLOC p = head_PLOC;
3715
3716 while (p) {
2f4077ca 3717 char * exp_res;
4d743a9b 3718 int dirlen;
a35dcc95
CB
3719 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3720 my_strlcat(file, "vmspipe.com", sizeof(file));
22d4bb9c
CB
3721 p = p->next;
3722
6fb6c614 3723 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
2f4077ca 3724 if (!exp_res) continue;
22d4bb9c 3725
a1887106
JM
3726 if (cando_by_name_int
3727 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3728 && cando_by_name_int
3729 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
22d4bb9c
CB
3730 vmspipe_file_status = 1;
3731 return vmspipe_file;
3732 }
3733 }
3734 vmspipe_file_status = -1; /* failed, use tempfiles */
3735 }
3736
3737 return 0;
3738}
3739
3740static FILE *
fd8cd3a3 3741vmspipe_tempfile(pTHX)
22d4bb9c
CB
3742{
3743 char file[NAM$C_MAXRSS+1];
3744 FILE *fp;
3745 static int index = 0;
2497a41f
JM
3746 Stat_t s0, s1;
3747 int cmp_result;
22d4bb9c
CB
3748
3749 /* create a tempfile */
3750
3751 /* we can't go from W, shr=get to R, shr=get without
3752 an intermediate vulnerable state, so don't bother trying...
3753
3754 and lib$spawn doesn't shr=put, so have to close the write
3755
3756 So... match up the creation date/time and the FID to
3757 make sure we're dealing with the same file
3758
3759 */
3760
3761 index++;
2497a41f
JM
3762 if (!decc_filename_unix_only) {
3763 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3764 fp = fopen(file,"w");
3765 if (!fp) {
22d4bb9c
CB
3766 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3767 fp = fopen(file,"w");
3768 if (!fp) {
3769 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3770 fp = fopen(file,"w");
2497a41f
JM
3771 }
3772 }
3773 }
3774 else {
3775 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3776 fp = fopen(file,"w");
3777 if (!fp) {
3778 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3779 fp = fopen(file,"w");
3780 if (!fp) {
3781 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3782 fp = fopen(file,"w");
3783 }
3784 }
22d4bb9c
CB
3785 }
3786 if (!fp) return 0; /* we're hosed */
3787
f9ecfa39 3788 fprintf(fp,"$! 'f$verify(0)'\n");
22d4bb9c
CB
3789 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3790 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3791 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3792 fprintf(fp,"$ perl_on = \"set noon\"\n");
3793 fprintf(fp,"$ perl_exit = \"exit\"\n");
3794 fprintf(fp,"$ perl_del = \"delete\"\n");
3795 fprintf(fp,"$ pif = \"if\"\n");
3796 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
2d5e9e5d
JH
3797 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3798 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
0e06870b 3799 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
48b5a746
CL
3800 fprintf(fp,"$! --- build command line to get max possible length\n");
3801 fprintf(fp,"$c=perl_popen_cmd0\n");
3802 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3803 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3804 fprintf(fp,"$x=perl_popen_cmd3\n");
3805 fprintf(fp,"$c=c+x\n");
22d4bb9c 3806 fprintf(fp,"$ perl_on\n");
f9ecfa39 3807 fprintf(fp,"$ 'c'\n");
22d4bb9c 3808 fprintf(fp,"$ perl_status = $STATUS\n");
0e06870b 3809 fprintf(fp,"$ perl_del 'perl_cfile'\n");
22d4bb9c
CB
3810 fprintf(fp,"$ perl_exit 'perl_status'\n");
3811 fsync(fileno(fp));
3812
3813 fgetname(fp, file, 1);
312ac60b 3814 fstat(fileno(fp), &s0.crtl_stat);
22d4bb9c
CB
3815 fclose(fp);
3816
2497a41f 3817 if (decc_filename_unix_only)
0e5ce2c7 3818 int_tounixspec(file, file, NULL);
22d4bb9c
CB
3819 fp = fopen(file,"r","shr=get");
3820 if (!fp) return 0;
312ac60b 3821 fstat(fileno(fp), &s1.crtl_stat);
2497a41f 3822
682e4b71 3823 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
2497a41f 3824 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
22d4bb9c
CB
3825 fclose(fp);
3826 return 0;
3827 }
3828
3829 return fp;
3830}
3831
3832
cd1191f1
CB
3833static int vms_is_syscommand_xterm(void)
3834{
3835 const static struct dsc$descriptor_s syscommand_dsc =
3836 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3837
3838 const static struct dsc$descriptor_s decwdisplay_dsc =
3839 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3840
3841 struct item_list_3 items[2];
3842 unsigned short dvi_iosb[4];
3843 unsigned long devchar;
3844 unsigned long devclass;
3845 int status;
3846
3847 /* Very simple check to guess if sys$command is a decterm? */
3848 /* First see if the DECW$DISPLAY: device exists */
3849 items[0].len = 4;
3850 items[0].code = DVI$_DEVCHAR;
3851 items[0].bufadr = &devchar;
3852 items[0].retadr = NULL;
3853 items[1].len = 0;
3854 items[1].code = 0;
3855
3856 status = sys$getdviw
3857 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3858
3859 if ($VMS_STATUS_SUCCESS(status)) {
3860 status = dvi_iosb[0];
3861 }
3862
3863 if (!$VMS_STATUS_SUCCESS(status)) {
3864 SETERRNO(EVMSERR, status);
3865 return -1;
3866 }
3867
3868 /* If it does, then for now assume that we are on a workstation */
3869 /* Now verify that SYS$COMMAND is a terminal */
3870 /* for creating the debugger DECTerm */
3871
3872 items[0].len = 4;
3873 items[0].code = DVI$_DEVCLASS;
3874 items[0].bufadr = &devclass;
3875 items[0].retadr = NULL;
3876 items[1].len = 0;
3877 items[1].code = 0;
3878
3879 status = sys$getdviw
3880 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3881
3882 if ($VMS_STATUS_SUCCESS(status)) {
3883 status = dvi_iosb[0];
3884 }
3885
3886 if (!$VMS_STATUS_SUCCESS(status)) {
3887 SETERRNO(EVMSERR, status);
3888 return -1;
3889 }
3890 else {
3891 if (devclass == DC$_TERM) {
3892 return 0;
3893 }
3894 }
3895 return -1;
3896}
3897
3898/* If we are on a DECTerm, we can pretend to fork xterms when requested */
3899static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3900{
3901 int status;
3902 int ret_stat;
3903 char * ret_char;
3904 char device_name[65];
3905 unsigned short device_name_len;
3906 struct dsc$descriptor_s customization_dsc;
3907 struct dsc$descriptor_s device_name_dsc;
3908 const char * cptr;
cd1191f1
CB
3909 char customization[200];
3910 char title[40];
3911 pInfo info = NULL;
3912 char mbx1[64];
3913 unsigned short p_chan;
3914 int n;
3915 unsigned short iosb[4];
cd1191f1
CB
3916 const char * cust_str =
3917 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3918 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3919 DSC$K_CLASS_S, mbx1};
3920
8cb5d3d5
JM
3921 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3922 /*---------------------------------------*/
d30c1055 3923 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
8cb5d3d5
JM
3924
3925
3926 /* Make sure that this is from the Perl debugger */
cd1191f1
CB
3927 ret_char = strstr(cmd," xterm ");
3928 if (ret_char == NULL)
3929 return NULL;
3930 cptr = ret_char + 7;
3931 ret_char = strstr(cmd,"tty");
3932 if (ret_char == NULL)
3933 return NULL;
3934 ret_char = strstr(cmd,"sleep");
3935 if (ret_char == NULL)
3936 return NULL;
3937
8cb5d3d5
JM
3938 if (decw_term_port == 0) {
3939 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3940 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3941 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3942
d30c1055 3943 status = lib$find_image_symbol
8cb5d3d5
JM
3944 (&filename1_dsc,
3945 &decw_term_port_dsc,
3946 (void *)&decw_term_port,
3947 NULL,
3948 0);
3949
3950 /* Try again with the other image name */
3951 if (!$VMS_STATUS_SUCCESS(status)) {
3952
d30c1055 3953 status = lib$find_image_symbol
8cb5d3d5
JM
3954 (&filename2_dsc,
3955 &decw_term_port_dsc,
3956 (void *)&decw_term_port,
3957 NULL,
3958 0);
3959
3960 }
3961
3962 }
3963
3964
3965 /* No decw$term_port, give it up */
3966 if (!$VMS_STATUS_SUCCESS(status))
3967 return NULL;
3968
cd1191f1
CB
3969 /* Are we on a workstation? */
3970 /* to do: capture the rows / columns and pass their properties */
3971 ret_stat = vms_is_syscommand_xterm();
3972 if (ret_stat < 0)
3973 return NULL;
3974
3975 /* Make the title: */
3976 ret_char = strstr(cptr,"-title");
3977 if (ret_char != NULL) {
3978 while ((*cptr != 0) && (*cptr != '\"')) {
3979 cptr++;
3980 }
3981 if (*cptr == '\"')
3982 cptr++;
3983 n = 0;
3984 while ((*cptr != 0) && (*cptr != '\"')) {
3985 title[n] = *cptr;
3986 n++;
3987 if (n == 39) {
07bee079 3988 title[39] = 0;
cd1191f1
CB
3989 break;
3990 }
3991 cptr++;
3992 }
3993 title[n] = 0;
3994 }
3995 else {
3996 /* Default title */
3997 strcpy(title,"Perl Debug DECTerm");
3998 }
3999 sprintf(customization, cust_str, title);
4000
4001 customization_dsc.dsc$a_pointer = customization;
4002 customization_dsc.dsc$w_length = strlen(customization);
4003 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4004 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4005
4006 device_name_dsc.dsc$a_pointer = device_name;
4007 device_name_dsc.dsc$w_length = sizeof device_name -1;
4008 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4009 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4010
4011 device_name_len = 0;
4012
4013 /* Try to create the window */
8cb5d3d5 4014 status = (*decw_term_port)
cd1191f1
CB
4015 (NULL,
4016 NULL,
4017 &customization_dsc,
4018 &device_name_dsc,
4019 &device_name_len,
4020 NULL,
4021 NULL,
4022 NULL);
4023 if (!$VMS_STATUS_SUCCESS(status)) {
4024 SETERRNO(EVMSERR, status);
4025 return NULL;
4026 }
4027
4028 device_name[device_name_len] = '\0';
4029
4030 /* Need to set this up to look like a pipe for cleanup */
4031 n = sizeof(Info);
4032 status = lib$get_vm(&n, &info);
4033 if (!$VMS_STATUS_SUCCESS(status)) {
4034 SETERRNO(ENOMEM, status);
4035 return NULL;
4036 }
4037
4038 info->mode = *mode;
4039 info->done = FALSE;
4040 info->completion = 0;
4041 info->closing = FALSE;
4042 info->in = 0;
4043 info->out = 0;
4044 info->err = 0;
4e205ed6 4045 info->fp = NULL;
cd1191f1
CB
4046 info->useFILE = 0;
4047 info->waiting = 0;
4048 info->in_done = TRUE;
4049 info->out_done = TRUE;
4050 info->err_done = TRUE;
4051
4052 /* Assign a channel on this so that it will persist, and not login */
4053 /* We stash this channel in the info structure for reference. */
4054 /* The created xterm self destructs when the last channel is removed */
4055 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4056 /* So leave this assigned. */
4057 device_name_dsc.dsc$w_length = device_name_len;
4058 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4059 if (!$VMS_STATUS_SUCCESS(status)) {
4060 SETERRNO(EVMSERR, status);
4061 return NULL;
4062 }
4063 info->xchan_valid = 1;
4064
4065 /* Now create a mailbox to be read by the application */
4066
8a646e0b 4067 create_mbx(&p_chan, &d_mbx1);
cd1191f1
CB
4068
4069 /* write the name of the created terminal to the mailbox */
4070 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4071 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4072
4073 if (!$VMS_STATUS_SUCCESS(status)) {
4074 SETERRNO(EVMSERR, status);
4075 return NULL;
4076 }
4077
4078 info->fp = PerlIO_open(mbx1, mode);
4079
4080 /* Done with this channel */
4081 sys$dassgn(p_chan);
4082
4083 /* If any errors, then clean up */
4084 if (!info->fp) {
4085 n = sizeof(Info);
ebd4d70b 4086 _ckvmssts_noperl(lib$free_vm(&n, &info));
cd1191f1
CB
4087 return NULL;
4088 }
4089
4090 /* All done */
4091 return info->fp;
4092}
22d4bb9c 4093
ebd4d70b
JM
4094static I32 my_pclose_pinfo(pTHX_ pInfo info);
4095
8fde5078 4096static PerlIO *
2fbb330f 4097safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
a0d0e21e 4098{
748a9306 4099 static int handler_set_up = FALSE;
ebd4d70b 4100 PerlIO * ret_fp;
55f2b99c 4101 unsigned long int sts, flags = CLI$M_NOWAIT;
f9ecfa39
PP
4102 /* The use of a GLOBAL table (as was done previously) rendered
4103 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4104 * environment. Hence we've switched to LOCAL symbol table.
4105 */
4106 unsigned int table = LIB$K_CLI_LOCAL_SYM;
d4c83939 4107 int j, wait = 0, n;
ff7adb52 4108 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
cfcfe586 4109 char *in, *out, *err, mbx[512];
22d4bb9c
CB
4110 FILE *tpipe = 0;
4111 char tfilebuf[NAM$C_MAXRSS+1];
d4c83939 4112 pInfo info = NULL;
48b5a746 4113 char cmd_sym_name[20];
22d4bb9c
CB
4114 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4115 DSC$K_CLASS_S, symbol};
22d4bb9c 4116 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
a0d0e21e 4117 DSC$K_CLASS_S, 0};
48b5a746
CL
4118 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4119 DSC$K_CLASS_S, cmd_sym_name};
218fdd94 4120 struct dsc$descriptor_s *vmscmd;
22d4bb9c 4121 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
0e06870b 4122 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
22d4bb9c 4123 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
cd1191f1 4124
cd1191f1
CB
4125 /* Check here for Xterm create request. This means looking for
4126 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4127 * is possible to create an xterm.
4128 */
4129 if (*in_mode == 'r') {
4130 PerlIO * xterm_fd;
4131
4d9538c1
JM
4132#if defined(PERL_IMPLICIT_CONTEXT)
4133 /* Can not fork an xterm with a NULL context */
4134 /* This probably could never happen */
4135 xterm_fd = NULL;
4136 if (aTHX != NULL)
4137#endif
cd1191f1 4138 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4e205ed6 4139 if (xterm_fd != NULL)
cd1191f1
CB
4140 return xterm_fd;
4141 }
cd1191f1 4142
afd8f436
JH
4143 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4144
22d4bb9c
CB
4145 /* once-per-program initialization...
4146 note that the SETAST calls and the dual test of pipe_ef
4147 makes sure that only the FIRST thread through here does
4148 the initialization...all other threads wait until it's
4149 done.
4150
4151 Yeah, uglier than a pthread call, it's got all the stuff inline
4152 rather than in a separate routine.
4153 */
4154
4155 if (!pipe_ef) {
ebd4d70b 4156 _ckvmssts_noperl(sys$setast(0));
22d4bb9c
CB
4157 if (!pipe_ef) {
4158 unsigned long int pidcode = JPI$_PID;
4159 $DESCRIPTOR(d_delay, RETRY_DELAY);
ebd4d70b
JM
4160 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4161 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4162 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
22d4bb9c
CB
4163 }
4164 if (!handler_set_up) {
ebd4d70b 4165 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
22d4bb9c
CB
4166 handler_set_up = TRUE;
4167 }
ebd4d70b 4168 _ckvmssts_noperl(sys$setast(1));
22d4bb9c
CB
4169 }
4170
4171 /* see if we can find a VMSPIPE.COM */
4172
4173 tfilebuf[0] = '@';
fd8cd3a3 4174 vmspipe = find_vmspipe(aTHX);
22d4bb9c 4175 if (vmspipe) {
a35dcc95 4176 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
22d4bb9c 4177 } else { /* uh, oh...we're in tempfile hell */
fd8cd3a3 4178 tpipe = vmspipe_tempfile(aTHX);
22d4bb9c
CB
4179 if (!tpipe) { /* a fish popular in Boston */
4180 if (ckWARN(WARN_PIPE)) {
f98bc0c6 4181 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
22d4bb9c 4182 }
4e205ed6 4183 return NULL;
22d4bb9c
CB
4184 }
4185 fgetname(tpipe,tfilebuf+1,1);
a35dcc95 4186 vmspipedsc.dsc$w_length = strlen(tfilebuf);
22d4bb9c
CB
4187 }
4188 vmspipedsc.dsc$a_pointer = tfilebuf;
a0d0e21e 4189
218fdd94 4190 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
a2669cfc
JH
4191 if (!(sts & 1)) {
4192 switch (sts) {
4193 case RMS$_FNF: case RMS$_DNF:
4194 set_errno(ENOENT); break;
4195 case RMS$_DIR:
4196 set_errno(ENOTDIR); break;
4197 case RMS$_DEV:
4198 set_errno(ENODEV); break;
4199 case RMS$_PRV:
4200 set_errno(EACCES); break;
4201 case RMS$_SYN:
4202 set_errno(EINVAL); break;
4203 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4204 set_errno(E2BIG); break;
4205 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 4206 _ckvmssts_noperl(sts); /* fall through */
a2669cfc
JH
4207 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4208 set_errno(EVMSERR);
4209 }
4210 set_vaxc_errno(sts);
cd1191f1 4211 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
f98bc0c6 4212 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
a2669cfc 4213 }
ff7adb52 4214 *psts = sts;
4e205ed6 4215 return NULL;
a2669cfc 4216 }
d4c83939 4217 n = sizeof(Info);
ebd4d70b 4218 _ckvmssts_noperl(lib$get_vm(&n, &info));
22d4bb9c 4219
a35dcc95 4220 my_strlcpy(mode, in_mode, sizeof(mode));
22d4bb9c
CB
4221 info->mode = *mode;
4222 info->done = FALSE;
4223 info->completion = 0;
4224 info->closing = FALSE;
4225 info->in = 0;
4226 info->out = 0;
4227 info->err = 0;
4e205ed6 4228 info->fp = NULL;
ff7adb52
CL
4229 info->useFILE = 0;
4230 info->waiting = 0;
22d4bb9c
CB
4231 info->in_done = TRUE;
4232 info->out_done = TRUE;
4233 info->err_done = TRUE;
cd1191f1
CB
4234 info->xchan = 0;
4235 info->xchan_valid = 0;
cfcfe586 4236
c11536f5 4237 in = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4238 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 4239 out = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4240 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 4241 err = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 4242 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
cfcfe586 4243
0e06870b 4244 in[0] = out[0] = err[0] = '\0';
22d4bb9c 4245
ff7adb52
CL
4246 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4247 info->useFILE = 1;
4248 strcpy(p,p+1);
4249 }
4250 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4251 wait = 1;
4252 strcpy(p,p+1);
4253 }
4254
22d4bb9c 4255 if (*mode == 'r') { /* piping from subroutine */
22d4bb9c 4256
fd8cd3a3 4257 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
22d4bb9c
CB
4258 if (info->out) {
4259 info->out->pipe_done = &info->out_done;
4260 info->out_done = FALSE;
4261 info->out->info = info;
4262 }
ff7adb52 4263 if (!info->useFILE) {
cd1191f1 4264 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4265 } else {
4266 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4267 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4268 }
4269
22d4bb9c
CB
4270 if (!info->fp && info->out) {
4271 sys$cancel(info->out->chan_out);
4272
4273 while (!info->out_done) {
4274 int done;
ebd4d70b 4275 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4276 done = info->out_done;
ebd4d70b
JM
4277 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4278 _ckvmssts_noperl(sys$setast(1));
4279 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
0e06870b 4280 }
22d4bb9c 4281
d4c83939
CB
4282 if (info->out->buf) {
4283 n = info->out->bufsize * sizeof(char);
ebd4d70b 4284 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
d4c83939
CB
4285 }
4286 n = sizeof(Pipe);
ebd4d70b 4287 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
d4c83939 4288 n = sizeof(Info);
ebd4d70b 4289 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4290 *psts = RMS$_FNF;
4e205ed6 4291 return NULL;
0e06870b 4292 }
22d4bb9c 4293
fd8cd3a3 4294 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
22d4bb9c
CB
4295 if (info->err) {
4296 info->err->pipe_done = &info->err_done;
4297 info->err_done = FALSE;
4298 info->err->info = info;
4299 }
a0d0e21e 4300
ff7adb52
CL
4301 } else if (*mode == 'w') { /* piping to subroutine */
4302
4303 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4304 if (info->out) {
4305 info->out->pipe_done = &info->out_done;
4306 info->out_done = FALSE;
4307 info->out->info = info;
4308 }
4309
4310 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4311 if (info->err) {
4312 info->err->pipe_done = &info->err_done;
4313 info->err_done = FALSE;
4314 info->err->info = info;
4315 }
a0d0e21e 4316
fd8cd3a3 4317 info->in = pipe_tochild_setup(aTHX_ in,mbx);
ff7adb52 4318 if (!info->useFILE) {
a480973c 4319 info->fp = PerlIO_open(mbx, mode);
ff7adb52
CL
4320 } else {
4321 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4322 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4323 }
4324
22d4bb9c
CB
4325 if (info->in) {
4326 info->in->pipe_done = &info->in_done;
4327 info->in_done = FALSE;
4328 info->in->info = info;
4329 }
a0d0e21e 4330
22d4bb9c
CB
4331 /* error cleanup */
4332 if (!info->fp && info->in) {
4333 info->done = TRUE;
ebd4d70b
JM
4334 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4335 0, 0, 0, 0, 0, 0, 0, 0));
22d4bb9c
CB
4336
4337 while (!info->in_done) {
4338 int done;
ebd4d70b 4339 _ckvmssts_noperl(sys$setast(0));
22d4bb9c 4340 done = info->in_done;
ebd4d70b
JM
4341 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4342 _ckvmssts_noperl(sys$setast(1));
4343 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
22d4bb9c 4344 }
a0d0e21e 4345
d4c83939
CB
4346 if (info->in->buf) {
4347 n = info->in->bufsize * sizeof(char);
ebd4d70b 4348 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
d4c83939
CB
4349 }
4350 n = sizeof(Pipe);
ebd4d70b 4351 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
d4c83939 4352 n = sizeof(Info);
ebd4d70b 4353 _ckvmssts_noperl(lib$free_vm(&n, &info));
ff7adb52 4354 *psts = RMS$_FNF;
4e205ed6 4355 return NULL;
22d4bb9c 4356 }
a0d0e21e 4357
22d4bb9c 4358
ff7adb52 4359 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
fd8cd3a3 4360 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
22d4bb9c
CB
4361 if (info->out) {
4362 info->out->pipe_done = &info->out_done;
4363 info->out_done = FALSE;
4364 info->out->info = info;
4365 }
0e06870b 4366
fd8cd3a3 4367 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
0e06870b
CB
4368 if (info->err) {
4369 info->err->pipe_done = &info->err_done;
4370 info->err_done = FALSE;
4371 info->err->info = info;
4372 }
748a9306 4373 }
22d4bb9c 4374
a35dcc95 4375 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
ebd4d70b 4376 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
22d4bb9c 4377
a35dcc95 4378 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
ebd4d70b 4379 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
22d4bb9c 4380
a35dcc95 4381 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
ebd4d70b 4382 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
22d4bb9c 4383
cfcfe586
JM
4384 /* Done with the names for the pipes */
4385 PerlMem_free(err);
4386 PerlMem_free(out);
4387 PerlMem_free(in);
4388
218fdd94 4389 p = vmscmd->dsc$a_pointer;
22d4bb9c
CB
4390 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4391 if (*p == '$') p++; /* remove leading $ */
4392 while (*p == ' ' || *p == '\t') p++;
48b5a746
CL
4393
4394 for (j = 0; j < 4; j++) {
4395 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4396 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4397
a35dcc95 4398 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
ebd4d70b 4399 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
22d4bb9c 4400
48b5a746
CL
4401 if (strlen(p) > MAX_DCL_SYMBOL) {
4402 p += MAX_DCL_SYMBOL;
4403 } else {
4404 p += strlen(p);
4405 }
4406 }
ebd4d70b 4407 _ckvmssts_noperl(sys$setast(0));
a0d0e21e
LW
4408 info->next=open_pipes; /* prepend to list */
4409 open_pipes=info;
ebd4d70b 4410 _ckvmssts_noperl(sys$setast(1));
55f2b99c
CB
4411 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4412 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4413 * have SYS$COMMAND if we need it.
4414 */
ebd4d70b 4415 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
22d4bb9c
CB
4416 0, &info->pid, &info->completion,
4417 0, popen_completion_ast,info,0,0,0));
4418
4419 /* if we were using a tempfile, close it now */
4420
4421 if (tpipe) fclose(tpipe);
4422
ff7adb52 4423 /* once the subprocess is spawned, it has copied the symbols and
22d4bb9c
CB
4424 we can get rid of ours */
4425
48b5a746
CL
4426 for (j = 0; j < 4; j++) {
4427 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4428 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
ebd4d70b 4429 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
48b5a746 4430 }
ebd4d70b
JM
4431 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4432 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4433 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
218fdd94 4434 vms_execfree(vmscmd);
a0d0e21e 4435
218fdd94
CL
4436#ifdef PERL_IMPLICIT_CONTEXT
4437 if (aTHX)
4438#endif
6b88bc9c 4439 PL_forkprocess = info->pid;
218fdd94 4440
ebd4d70b 4441 ret_fp = info->fp;
ff7adb52 4442 if (wait) {
ebd4d70b 4443 dSAVEDERRNO;
ff7adb52
CL
4444 int done = 0;
4445 while (!done) {
ebd4d70b 4446 _ckvmssts_noperl(sys$setast(0));
ff7adb52 4447 done = info->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));
ff7adb52
CL
4451 }
4452 *psts = info->completion;
2fbb330f
JM
4453/* Caller thinks it is open and tries to close it. */
4454/* This causes some problems, as it changes the error status */
4455/* my_pclose(info->fp); */
ebd4d70b
JM
4456
4457 /* If we did not have a file pointer open, then we have to */
4458 /* clean up here or eventually we will run out of something */
4459 SAVE_ERRNO;
4460 if (info->fp == NULL) {
4461 my_pclose_pinfo(aTHX_ info);
4462 }
4463 RESTORE_ERRNO;
4464
ff7adb52 4465 } else {
eed5d6a1 4466 *psts = info->pid;
ff7adb52 4467 }
ebd4d70b 4468 return ret_fp;
1e422769 4469} /* end of safe_popen */
4470
4471
a15cef0c
CB
4472/*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4473PerlIO *
2fbb330f 4474Perl_my_popen(pTHX_ const char *cmd, const char *mode)
1e422769 4475{
ff7adb52 4476 int sts;
1e422769 4477 TAINT_ENV();
4478 TAINT_PROPER("popen");
45bc9206 4479 PERL_FLUSHALL_FOR_CHILD;
ff7adb52 4480 return safe_popen(aTHX_ cmd,mode,&sts);
a0d0e21e 4481}
1e422769 4482
a0d0e21e
LW
4483/*}}}*/
4484
ebd4d70b
JM
4485
4486/* Routine to close and cleanup a pipe info structure */
4487
4488static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4489
748a9306 4490 unsigned long int retsts;
4e0c9737 4491 int done, n;
ebd4d70b 4492 pInfo next, last;
748a9306 4493
bbce6d69 4494 /* If we were writing to a subprocess, insure that someone reading from
4495 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
22d4bb9c
CB
4496 * produce an EOF record in the mailbox.
4497 *
4498 * well, at least sometimes it *does*, so we have to watch out for
4499 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4500 */
ff7adb52 4501 if (info->fp) {
5ce486e0
CB
4502 if (!info->useFILE
4503#if defined(USE_ITHREADS)
4504 && my_perl
4505#endif
a24c654f
CB
4506#ifdef USE_PERLIO
4507 && PL_perlio_fd_refcnt
4508#endif
4509 )
5ce486e0 4510 PerlIO_flush(info->fp);
ff7adb52
CL
4511 else
4512 fflush((FILE *)info->fp);
4513 }
22d4bb9c 4514
b08af3f0 4515 _ckvmssts(sys$setast(0));
22d4bb9c
CB
4516 info->closing = TRUE;
4517 done = info->done && info->in_done && info->out_done && info->err_done;
4518 /* hanging on write to Perl's input? cancel it */
4519 if (info->mode == 'r' && info->out && !info->out_done) {
4520 if (info->out->chan_out) {
4521 _ckvmssts(sys$cancel(info->out->chan_out));
4522 if (!info->out->chan_in) { /* EOF generation, need AST */
4523 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4524 }
4525 }
4526 }
4527 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4528 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4529 0, 0, 0, 0, 0, 0));
b08af3f0 4530 _ckvmssts(sys$setast(1));
ff7adb52 4531 if (info->fp) {
5ce486e0
CB
4532 if (!info->useFILE
4533#if defined(USE_ITHREADS)
4534 && my_perl
4535#endif
a24c654f
CB
4536#ifdef USE_PERLIO
4537 && PL_perlio_fd_refcnt
4538#endif
4539 )
d4c83939 4540 PerlIO_close(info->fp);
ff7adb52
CL
4541 else
4542 fclose((FILE *)info->fp);
4543 }
22d4bb9c
CB
4544 /*
4545 we have to wait until subprocess completes, but ALSO wait until all
4546 the i/o completes...otherwise we'll be freeing the "info" structure
4547 that the i/o ASTs could still be using...
4548 */
4549
4550 while (!done) {
4551 _ckvmssts(sys$setast(0));
4552 done = info->done && info->in_done && info->out_done && info->err_done;
4553 if (!done) _ckvmssts(sys$clref(pipe_ef));
4554 _ckvmssts(sys$setast(1));
4555 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4556 }
4557 retsts = info->completion;
a0d0e21e 4558
a0d0e21e 4559 /* remove from list of open pipes */
b08af3f0 4560 _ckvmssts(sys$setast(0));
ebd4d70b
JM
4561 last = NULL;
4562 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4563 if (next == info)
4564 break;
4565 }
4566
4567 if (last)
4568 last->next = info->next;
4569 else
4570 open_pipes = info->next;
b08af3f0 4571 _ckvmssts(sys$setast(1));
22d4bb9c
CB
4572
4573 /* free buffers and structures */
4574
4575 if (info->in) {
d4c83939
CB
4576 if (info->in->buf) {
4577 n = info->in->bufsize * sizeof(char);
4578 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4579 }
4580 n = sizeof(Pipe);
4581 _ckvmssts(lib$free_vm(&n, &info->in));
22d4bb9c
CB
4582 }
4583 if (info->out) {
d4c83939
CB
4584 if (info->out->buf) {
4585 n = info->out->bufsize * sizeof(char);
4586 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4587 }
4588 n = sizeof(Pipe);
4589 _ckvmssts(lib$free_vm(&n, &info->out));
22d4bb9c
CB
4590 }
4591 if (info->err) {
d4c83939
CB
4592 if (info->err->buf) {
4593 n = info->err->bufsize * sizeof(char);
4594 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4595 }
4596 n = sizeof(Pipe);
4597 _ckvmssts(lib$free_vm(&n, &info->err));
22d4bb9c 4598 }
d4c83939
CB
4599 n = sizeof(Info);
4600 _ckvmssts(lib$free_vm(&n, &info));
a0d0e21e
LW
4601
4602 return retsts;
ebd4d70b
JM
4603}
4604
4605
4606/*{{{ I32 my_pclose(PerlIO *fp)*/
4607I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4608{
4609 pInfo info, last = NULL;
4610 I32 ret_status;
4611
4612 /* Fixme - need ast and mutex protection here */
4613 for (info = open_pipes; info != NULL; last = info, info = info->next)
4614 if (info->fp == fp) break;
4615
4616 if (info == NULL) { /* no such pipe open */
4617 set_errno(ECHILD); /* quoth POSIX */
4618 set_vaxc_errno(SS$_NONEXPR);
4619 return -1;
4620 }
4621
4622 ret_status = my_pclose_pinfo(aTHX_ info);
4623
4624 return ret_status;
748a9306 4625
a0d0e21e
LW
4626} /* end of my_pclose() */
4627
119586db 4628#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
4629 /* Roll our own prototype because we want this regardless of whether
4630 * _VMS_WAIT is defined.
4631 */
c11536f5
CB
4632
4633#ifdef __cplusplus
4634extern "C" {
4635#endif
aeb5cf3c 4636 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
c11536f5
CB
4637#ifdef __cplusplus
4638}
4639#endif
4640
aeb5cf3c
CB
4641#endif
4642/* sort-of waitpid; special handling of pipe clean-up for subprocesses
4643 created with popen(); otherwise partially emulate waitpid() unless
4644 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4645 Also check processes not considered by the CRTL waitpid().
4646 */
4fdae800 4647/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4648Pid_t
fd8cd3a3 4649Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
a0d0e21e 4650{
22d4bb9c
CB
4651 pInfo info;
4652 int done;
aeb5cf3c 4653 int sts;
d85f548a 4654 int j;
aeb5cf3c
CB
4655
4656 if (statusp) *statusp = 0;
a0d0e21e
LW
4657
4658 for (info = open_pipes; info != NULL; info = info->next)
4659 if (info->pid == pid) break;
4660
4661 if (info != NULL) { /* we know about this child */
748a9306 4662 while (!info->done) {
22d4bb9c
CB
4663 _ckvmssts(sys$setast(0));
4664 done = info->done;
4665 if (!done) _ckvmssts(sys$clref(pipe_ef));
4666 _ckvmssts(sys$setast(1));
4667 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
a0d0e21e
LW
4668 }
4669
aeb5cf3c 4670 if (statusp) *statusp = info->completion;
a0d0e21e 4671 return pid;
d85f548a
JH
4672 }
4673
4674 /* child that already terminated? */
aeb5cf3c 4675
d85f548a
JH
4676 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4677 if (closed_list[j].pid == pid) {
4678 if (statusp) *statusp = closed_list[j].completion;
4679 return pid;
4680 }
a0d0e21e 4681 }
d85f548a
JH
4682
4683 /* fall through if this child is not one of our own pipe children */
aeb5cf3c 4684
119586db 4685#if defined(__CRTL_VER) && __CRTL_VER >= 70200000
aeb5cf3c
CB
4686
4687 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4688 * in 7.2 did we get a version that fills in the VMS completion
4689 * status as Perl has always tried to do.
4690 */
4691
4692 sts = __vms_waitpid( pid, statusp, flags );
4693
4694 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4695 return sts;
4696
4697 /* If the real waitpid tells us the child does not exist, we
4698 * fall through here to implement waiting for a child that
4699 * was created by some means other than exec() (say, spawned
4700 * from DCL) or to wait for a process that is not a subprocess
4701 * of the current process.
4702 */
4703
119586db 4704#endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
aeb5cf3c 4705
21bc9d50 4706 {
a0d0e21e 4707 $DESCRIPTOR(intdsc,"0 00:00:01");
aeb5cf3c
CB
4708 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4709 unsigned long int pidcode = JPI$_PID, mypid;
4710 unsigned long int interval[2];
aeb5cf3c 4711 unsigned int jpi_iosb[2];
d85f548a 4712 struct itmlst_3 jpilist[2] = {
aeb5cf3c 4713 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
aeb5cf3c
CB
4714 { 0, 0, 0, 0}
4715 };
aeb5cf3c
CB
4716
4717 if (pid <= 0) {
4718 /* Sorry folks, we don't presently implement rooting around for
4719 the first child we can find, and we definitely don't want to
4720 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4721 */
4722 set_errno(ENOTSUP);
4723 return -1;
4724 }
4725
d85f548a
JH
4726 /* Get the owner of the child so I can warn if it's not mine. If the
4727 * process doesn't exist or I don't have the privs to look at it,
4728 * I can go home early.
aeb5cf3c
CB
4729 */
4730 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4731 if (sts & 1) sts = jpi_iosb[0];
4732 if (!(sts & 1)) {
4733 switch (sts) {
4734 case SS$_NONEXPR:
4735 set_errno(ECHILD);
4736 break;
4737 case SS$_NOPRIV:
4738 set_errno(EACCES);
4739 break;
4740 default:
4741 _ckvmssts(sts);
4742 }
4743 set_vaxc_errno(sts);
4744 return -1;
4745 }
a0d0e21e 4746
3eeba6fb 4747 if (ckWARN(WARN_EXEC)) {
aeb5cf3c
CB
4748 /* remind folks they are asking for non-standard waitpid behavior */
4749 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
748a9306 4750 if (ownerpid != mypid)
f98bc0c6 4751 Perl_warner(aTHX_ packWARN(WARN_EXEC),
aeb5cf3c
CB
4752 "waitpid: process %x is not a child of process %x",
4753 pid,mypid);
748a9306 4754 }
a0d0e21e 4755
d85f548a
JH
4756 /* simply check on it once a second until it's not there anymore. */
4757
4758 _ckvmssts(sys$bintim(&intdsc,interval));
4759 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
aeb5cf3c
CB
4760 _ckvmssts(sys$schdwk(0,0,interval,0));
4761 _ckvmssts(sys$hiber());
d85f548a
JH
4762 }
4763 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
aeb5cf3c
CB
4764
4765 _ckvmssts(sts);
a0d0e21e 4766 return pid;
21bc9d50 4767 }
a0d0e21e 4768} /* end of waitpid() */
a0d0e21e
LW
4769/*}}}*/
4770/*}}}*/
4771/*}}}*/
4772
4773/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4774char *
4775my_gconvert(double val, int ndig, int trail, char *buf)
4776{
4777 static char __gcvtbuf[DBL_DIG+1];
4778 char *loc;
4779
4780 loc = buf ? buf : __gcvtbuf;
71be2cbc 4781
a0d0e21e
LW
4782 if (val) {
4783 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4784 return gcvt(val,ndig,loc);
4785 }
4786 else {
4787 loc[0] = '0'; loc[1] = '\0';
4788 return loc;
4789 }
4790
4791}
4792/*}}}*/
4793
988c775c 4794#if defined(__VAX) || !defined(NAML$C_MAXRSS)
a480973c
JM
4795static int rms_free_search_context(struct FAB * fab)
4796{
4797struct NAM * nam;
4798
4799 nam = fab->fab$l_nam;
4800 nam->nam$b_nop |= NAM$M_SYNCHK;
4801 nam->nam$l_rlf = NULL;
4802 fab->fab$b_dns = 0;
4803 return sys$parse(fab, NULL, NULL);
4804}
4805
4806#define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4d743a9b 4807#define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
a480973c
JM
4808#define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4809#define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4810#define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4811#define rms_nam_esll(nam) nam.nam$b_esl
4812#define rms_nam_esl(nam) nam.nam$b_esl
4813#define rms_nam_name(nam) nam.nam$l_name
4814#define rms_nam_namel(nam) nam.nam$l_name
4815#define rms_nam_type(nam) nam.nam$l_type
4816#define rms_nam_typel(nam) nam.nam$l_type
4817#define rms_nam_ver(nam) nam.nam$l_ver
4818#define rms_nam_verl(nam) nam.nam$l_ver
4819#define rms_nam_rsll(nam) nam.nam$b_rsl
4820#define rms_nam_rsl(nam) nam.nam$b_rsl
4821#define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4822#define rms_set_fna(fab, nam, name, size) \
a1887106 4823 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
a480973c
JM
4824#define rms_get_fna(fab, nam) fab.fab$l_fna
4825#define rms_set_dna(fab, nam, name, size) \
a1887106
JM
4826 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4827#define rms_nam_dns(fab, nam) fab.fab$b_dns
d584a1c6 4828#define rms_set_esa(nam, name, size) \
a1887106 4829 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
a480973c 4830#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 4831 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
a480973c 4832#define rms_set_rsa(nam, name, size) \
a1887106 4833 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
a480973c 4834#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106
JM
4835 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4836#define rms_nam_name_type_l_size(nam) \
4837 (nam.nam$b_name + nam.nam$b_type)
a480973c
JM
4838#else
4839static int rms_free_search_context(struct FAB * fab)
4840{
4841struct NAML * nam;
4842
4843 nam = fab->fab$l_naml;
4844 nam->naml$b_nop |= NAM$M_SYNCHK;
4845 nam->naml$l_rlf = NULL;
4846 nam->naml$l_long_defname_size = 0;
988c775c 4847
a480973c
JM
4848 fab->fab$b_dns = 0;
4849 return sys$parse(fab, NULL, NULL);
4850}
4851
4852#define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4d743a9b 4853#define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
a480973c
JM
4854#define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4855#define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4856#define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4857#define rms_nam_esll(nam) nam.naml$l_long_expand_size
4858#define rms_nam_esl(nam) nam.naml$b_esl
4859#define rms_nam_name(nam) nam.naml$l_name
4860#define rms_nam_namel(nam) nam.naml$l_long_name
4861#define rms_nam_type(nam) nam.naml$l_type
4862#define rms_nam_typel(nam) nam.naml$l_long_type
4863#define rms_nam_ver(nam) nam.naml$l_ver
4864#define rms_nam_verl(nam) nam.naml$l_long_ver
4865#define rms_nam_rsll(nam) nam.naml$l_long_result_size
4866#define rms_nam_rsl(nam) nam.naml$b_rsl
4867#define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4868#define rms_set_fna(fab, nam, name, size) \
a1887106 4869 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
a480973c 4870 nam.naml$l_long_filename_size = size; \
a1887106 4871 nam.naml$l_long_filename = name;}
a480973c
JM
4872#define rms_get_fna(fab, nam) nam.naml$l_long_filename
4873#define rms_set_dna(fab, nam, name, size) \
a1887106 4874 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
a480973c 4875 nam.naml$l_long_defname_size = size; \
a1887106 4876 nam.naml$l_long_defname = name; }
a480973c 4877#define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
d584a1c6 4878#define rms_set_esa(nam, name, size) \
a1887106 4879 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
a480973c 4880 nam.naml$l_long_expand_alloc = size; \
a1887106 4881 nam.naml$l_long_expand = name; }
a480973c 4882#define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
a1887106 4883 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
a480973c 4884 nam.naml$l_long_expand = l_name; \
a1887106 4885 nam.naml$l_long_expand_alloc = l_size; }
a480973c 4886#define rms_set_rsa(nam, name, size) \
a1887106 4887 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
a480973c 4888 nam.naml$l_long_result = name; \
a1887106 4889 nam.naml$l_long_result_alloc = size; }
a480973c 4890#define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
a1887106 4891 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
a480973c 4892 nam.naml$l_long_result = l_name; \
a1887106
JM
4893 nam.naml$l_long_result_alloc = l_size; }
4894#define rms_nam_name_type_l_size(nam) \
4895 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
a480973c
JM
4896#endif
4897
4fdf8f88 4898
e0e5e8d6
JM
4899/* rms_erase
4900 * The CRTL for 8.3 and later can create symbolic links in any mode,
4fdf8f88 4901 * however in 8.3 the unlink/remove/delete routines will only properly handle
e0e5e8d6 4902 * them if one of the PCP modes is active.
e0e5e8d6
JM
4903 */
4904static int rms_erase(const char * vmsname)
4905{
4906 int status;
4907 struct FAB myfab = cc$rms_fab;
4908 rms_setup_nam(mynam);
4909
4910 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4911 rms_bind_fab_nam(myfab, mynam);
4fdf8f88 4912
e0e5e8d6
JM
4913#ifdef NAML$M_OPEN_SPECIAL
4914 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4915#endif
4916
d30c1055 4917 status = sys$erase(&myfab, 0, 0);
e0e5e8d6
JM
4918
4919 return status;
4920}
4921
bbce6d69 4922
4fdf8f88
JM
4923static int
4924vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4925 const struct dsc$descriptor_s * vms_dst_dsc,
4926 unsigned long flags)
4927{
4928 /* VMS and UNIX handle file permissions differently and the
4929 * the same ACL trick may be needed for renaming files,
4930 * especially if they are directories.
4931 */
4932
4933 /* todo: get kill_file and rename to share common code */
4934 /* I can not find online documentation for $change_acl
4935 * it appears to be replaced by $set_security some time ago */
4936
4937const unsigned int access_mode = 0;
4938$DESCRIPTOR(obj_file_dsc,"FILE");
4939char *vmsname;
4940char *rslt;
4e0c9737 4941unsigned long int jpicode = JPI$_UIC;
4fdf8f88
JM
4942int aclsts, fndsts, rnsts = -1;
4943unsigned int ctx = 0;
4944struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4945struct dsc$descriptor_s * clean_dsc;
4946
4947struct myacedef {
4948 unsigned char myace$b_length;
4949 unsigned char myace$b_type;
4950 unsigned short int myace$w_flags;
4951 unsigned long int myace$l_access;
4952 unsigned long int myace$l_ident;
4953} newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4954 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4955 0},
4956 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4957
4958struct item_list_3
4959 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4960 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4961 {0,0,0,0}},
4962 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4963 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4964 {0,0,0,0}};
4965
4966
4967 /* Expand the input spec using RMS, since we do not want to put
4968 * ACLs on the target of a symbolic link */
c11536f5 4969 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4fdf8f88
JM
4970 if (vmsname == NULL)
4971 return SS$_INSFMEM;
4972
6fb6c614 4973 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4fdf8f88 4974 vmsname,
6fb6c614 4975 PERL_RMSEXPAND_M_SYMLINK);
4fdf8f88
JM
4976 if (rslt == NULL) {
4977 PerlMem_free(vmsname);
4978 return SS$_INSFMEM;
4979 }
4980
4981 /* So we get our own UIC to use as a rights identifier,
4982 * and the insert an ACE at the head of the ACL which allows us
4983 * to delete the file.
4984 */
ebd4d70b 4985 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4fdf8f88
JM
4986
4987 fildsc.dsc$w_length = strlen(vmsname);
4988 fildsc.dsc$a_pointer = vmsname;
4989 ctx = 0;
4990 newace.myace$l_ident = oldace.myace$l_ident;
4991 rnsts = SS$_ABORT;
4992
4993 /* Grab any existing ACEs with this identifier in case we fail */
4994 clean_dsc = &fildsc;
4995 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4996 &fildsc,
4997 NULL,
4998 OSS$M_WLOCK,
4999 findlst,
5000 &ctx,
5001 &access_mode);
5002
5003 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5004 /* Add the new ACE . . . */
5005
5006 /* if the sys$get_security succeeded, then ctx is valid, and the
5007 * object/file descriptors will be ignored. But otherwise they
5008 * are needed
5009 */
5010 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5011 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5012 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5013 set_errno(EVMSERR);
5014 set_vaxc_errno(aclsts);
5015 PerlMem_free(vmsname);
5016 return aclsts;
5017 }
5018
5019 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5020 NULL, NULL,
5021 &flags,
5022 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5023
5024 if ($VMS_STATUS_SUCCESS(rnsts)) {
5025 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5026 }
5027
5028 /* Put things back the way they were. */
5029 ctx = 0;
5030 aclsts = sys$get_security(&obj_file_dsc,
5031 clean_dsc,
5032 NULL,
5033 OSS$M_WLOCK,
5034 findlst,
5035 &ctx,
5036 &access_mode);
5037
5038 if ($VMS_STATUS_SUCCESS(aclsts)) {
5039 int sec_flags;
5040
5041 sec_flags = 0;
5042 if (!$VMS_STATUS_SUCCESS(fndsts))
5043 sec_flags = OSS$M_RELCTX;
5044
5045 /* Get rid of the new ACE */
5046 aclsts = sys$set_security(NULL, NULL, NULL,
5047 sec_flags, dellst, &ctx, &access_mode);
5048
5049 /* If there was an old ACE, put it back */
5050 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5051 addlst[0].bufadr = &oldace;
5052 aclsts = sys$set_security(NULL, NULL, NULL,
5053 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5054 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5055 set_errno(EVMSERR);
5056 set_vaxc_errno(aclsts);
5057 rnsts = aclsts;
5058 }
5059 } else {
5060 int aclsts2;
5061
5062 /* Try to clear the lock on the ACL list */
5063 aclsts2 = sys$set_security(NULL, NULL, NULL,
5064 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5065
5066 /* Rename errors are most important */
5067 if (!$VMS_STATUS_SUCCESS(rnsts))
5068 aclsts = rnsts;
5069 set_errno(EVMSERR);
5070 set_vaxc_errno(aclsts);
5071 rnsts = aclsts;
5072 }
5073 }
5074 else {
5075 if (aclsts != SS$_ACLEMPTY)
5076 rnsts = aclsts;
5077 }
5078 }
5079 else
5080 rnsts = fndsts;
5081
5082 PerlMem_free(vmsname);
5083 return rnsts;
5084}
5085
5086
5087/*{{{int rename(const char *, const char * */
5088/* Not exactly what X/Open says to do, but doing it absolutely right
5089 * and efficiently would require a lot more work. This should be close
5090 * enough to pass all but the most strict X/Open compliance test.
5091 */
5092int
5093Perl_rename(pTHX_ const char *src, const char * dst)
5094{
5095int retval;
5096int pre_delete = 0;
5097int src_sts;
5098int dst_sts;
5099Stat_t src_st;
5100Stat_t dst_st;
5101
5102 /* Validate the source file */
46c05374 5103 src_sts = flex_lstat(src, &src_st);
4fdf8f88
JM
5104 if (src_sts != 0) {
5105
5106 /* No source file or other problem */
5107 return src_sts;
5108 }
b94a8c49
JM
5109 if (src_st.st_devnam[0] == 0) {
5110 /* This may be possible so fail if it is seen. */
5111 errno = EIO;
5112 return -1;
5113 }
4fdf8f88 5114
46c05374 5115 dst_sts = flex_lstat(dst, &dst_st);
4fdf8f88
JM
5116 if (dst_sts == 0) {
5117
5118 if (dst_st.st_dev != src_st.st_dev) {
5119 /* Must be on the same device */
5120 errno = EXDEV;
5121 return -1;
5122 }
5123
5124 /* VMS_INO_T_COMPARE is true if the inodes are different
5125 * to match the output of memcmp
5126 */
5127
5128 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5129 /* That was easy, the files are the same! */
5130 return 0;
5131 }
5132
5133 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5134 /* If source is a directory, so must be dest */
5135 errno = EISDIR;
5136 return -1;
5137 }
5138
5139 }
5140
5141
5142 if ((dst_sts == 0) &&
5143 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5144
5145 /* We have issues here if vms_unlink_all_versions is set
5146 * If the destination exists, and is not a directory, then
5147 * we must delete in advance.
5148 *
5149 * If the src is a directory, then we must always pre-delete
5150 * the destination.
5151 *
5152 * If we successfully delete the dst in advance, and the rename fails
5153 * X/Open requires that errno be EIO.
5154 *
5155 */
5156
5157 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5158 int d_sts;
46c05374 5159 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
b94a8c49
JM
5160 S_ISDIR(dst_st.st_mode));
5161
5162 /* Need to delete all versions ? */
5163 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5164 int i = 0;
5165
5166 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
46c05374 5167 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
b94a8c49
JM
5168 if (d_sts != 0)
5169 break;
5170 i++;
5171
5172 /* Make sure that we do not loop forever */
5173 if (i > 32767) {
5174 errno = EIO;
5175 d_sts = -1;
5176 break;
5177 }
5178 }
5179 }
5180
4fdf8f88
JM
5181 if (d_sts != 0)
5182 return d_sts;
5183
5184 /* We killed the destination, so only errno now is EIO */
5185 pre_delete = 1;
5186 }
5187 }
5188
5189 /* Originally the idea was to call the CRTL rename() and only
5190 * try the lib$rename_file if it failed.
5191 * It turns out that there are too many variants in what the
5192 * the CRTL rename might do, so only use lib$rename_file
5193 */
5194 retval = -1;
5195
5196 {
5197 /* Is the source and dest both in VMS format */
5198 /* if the source is a directory, then need to fileify */
94ae10c0 5199 /* and dest must be a directory or non-existent. */
4fdf8f88 5200
4fdf8f88
JM
5201 char * vms_dst;
5202 int sts;
5203 char * ret_str;
5204 unsigned long flags;
5205 struct dsc$descriptor_s old_file_dsc;
5206 struct dsc$descriptor_s new_file_dsc;
5207
5208 /* We need to modify the src and dst depending
5209 * on if one or more of them are directories.
5210 */
5211
c11536f5 5212 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
4fdf8f88 5213 if (vms_dst == NULL)
ebd4d70b 5214 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88
JM
5215
5216 if (S_ISDIR(src_st.st_mode)) {
5217 char * ret_str;
5218 char * vms_dir_file;
5219
c11536f5 5220 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
4fdf8f88 5221 if (vms_dir_file == NULL)
ebd4d70b 5222 _ckvmssts_noperl(SS$_INSFMEM);
4fdf8f88 5223
29475144 5224 /* If the dest is a directory, we must remove it */
4fdf8f88
JM
5225 if (dst_sts == 0) {
5226 int d_sts;
46c05374 5227 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
4fdf8f88 5228 if (d_sts != 0) {
4fdf8f88
JM
5229 PerlMem_free(vms_dst);
5230 errno = EIO;
29475144 5231 return d_sts;
4fdf8f88
JM
5232 }
5233
5234 pre_delete = 1;
5235 }
5236
5237 /* The dest must be a VMS file specification */
df278665 5238 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5239 if (ret_str == NULL) {
4fdf8f88
JM
5240 PerlMem_free(vms_dst);
5241 errno = EIO;
5242 return -1;
5243 }
5244
5245 /* The source must be a file specification */
4fdf8f88
JM
5246 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5247 if (ret_str == NULL) {
4fdf8f88
JM
5248 PerlMem_free(vms_dst);
5249 PerlMem_free(vms_dir_file);
5250 errno = EIO;
5251 return -1;
5252 }
5253 PerlMem_free(vms_dst);
5254 vms_dst = vms_dir_file;
5255
5256 } else {
5257 /* File to file or file to new dir */
5258
5259 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5260 /* VMS pathify a dir target */
4846f1d7 5261 ret_str = int_tovmspath(dst, vms_dst, NULL);
4fdf8f88 5262 if (ret_str == NULL) {
4fdf8f88
JM
5263 PerlMem_free(vms_dst);
5264 errno = EIO;
5265 return -1;
5266 }
5267 } else {
b94a8c49
JM
5268 char * v_spec, * r_spec, * d_spec, * n_spec;
5269 char * e_spec, * vs_spec;
5270 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
4fdf8f88
JM
5271
5272 /* fileify a target VMS file specification */
df278665 5273 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
4fdf8f88 5274 if (ret_str == NULL) {
4fdf8f88
JM
5275 PerlMem_free(vms_dst);
5276 errno = EIO;
5277 return -1;
5278 }
b94a8c49
JM
5279
5280 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5281 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5282 &e_len, &vs_spec, &vs_len);
5283 if (sts == 0) {
5284 if (e_len == 0) {
5285 /* Get rid of the version */
5286 if (vs_len != 0) {
5287 *vs_spec = '\0';
5288 }
5289 /* Need to specify a '.' so that the extension */
5290 /* is not inherited */
5291 strcat(vms_dst,".");
5292 }
5293 }
4fdf8f88
JM
5294 }
5295 }
5296
b94a8c49
JM
5297 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5298 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
4fdf8f88
JM
5299 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5300 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5301
5302 new_file_dsc.dsc$a_pointer = vms_dst;
5303 new_file_dsc.dsc$w_length = strlen(vms_dst);
5304 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5305 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5306
5307 flags = 0;
5308#if !defined(__VAX) && defined(NAML$C_MAXRSS)
449de3c2 5309 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
4fdf8f88
JM
5310#endif
5311
5312 sts = lib$rename_file(&old_file_dsc,
5313 &new_file_dsc,
5314 NULL, NULL,
5315 &flags,
5316 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5317 if (!$VMS_STATUS_SUCCESS(sts)) {
5318
5319 /* We could have failed because VMS style permissions do not
5320 * permit renames that UNIX will allow. Just like the hack
5321 * in for kill_file.
5322 */
5323 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5324 }
5325
4fdf8f88
JM
5326 PerlMem_free(vms_dst);
5327 if (!$VMS_STATUS_SUCCESS(sts)) {
5328 errno = EIO;
5329 return -1;
5330 }
5331 retval = 0;
5332 }
5333
5334 if (vms_unlink_all_versions) {
5335 /* Now get rid of any previous versions of the source file that
5336 * might still exist
5337 */
b94a8c49
JM
5338 int i = 0;
5339 dSAVEDERRNO;
5340 SAVE_ERRNO;
46c05374 5341 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5342 S_ISDIR(src_st.st_mode));
5343 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
46c05374 5344 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
b94a8c49
JM
5345 S_ISDIR(src_st.st_mode));
5346 if (src_sts != 0)
5347 break;
5348 i++;
5349
5350 /* Make sure that we do not loop forever */
5351 if (i > 32767) {
5352 src_sts = -1;
5353 break;
5354 }
5355 }
5356 RESTORE_ERRNO;
4fdf8f88
JM
5357 }
5358
5359 /* We deleted the destination, so must force the error to be EIO */
5360 if ((retval != 0) && (pre_delete != 0))
5361 errno = EIO;
5362
5363 return retval;
5364}
5365/*}}}*/
5366
5367
bbce6d69 5368/*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5369/* Shortcut for common case of simple calls to $PARSE and $SEARCH
5370 * to expand file specification. Allows for a single default file
5371 * specification and a simple mask of options. If outbuf is non-NULL,
5372 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5373 * the resultant file specification is placed. If outbuf is NULL, the
5374 * resultant file specification is placed into a static buffer.
5375 * The third argument, if non-NULL, is taken to be a default file
5376 * specification string. The fourth argument is unused at present.
5377 * rmesexpand() returns the address of the resultant string if
5378 * successful, and NULL on error.
e886094b
JM
5379 *
5380 * New functionality for previously unused opts value:
5381 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
a1887106
JM
5382 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5383 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
e0e5e8d6 5384 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
bbce6d69 5385 */
360732b5 5386static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
96e4d5b1 5387
bbce6d69 5388static char *
6fb6c614
JM
5389int_rmsexpand
5390 (const char *filespec,
360732b5 5391 char *outbuf,
360732b5
JM
5392 const char *defspec,
5393 unsigned opts,
5394 int * fs_utf8,
5395 int * dfs_utf8)
bbce6d69 5396{
6fb6c614
JM
5397 char * ret_spec;
5398 const char * in_spec;
5399 char * spec_buf;
5400 const char * def_spec;
5401 char * vmsfspec, *vmsdefspec;
5402 char * esa;
7566800d 5403 char * esal = NULL;
18a3d61e
JM
5404 char * outbufl;
5405 struct FAB myfab = cc$rms_fab;
a480973c 5406 rms_setup_nam(mynam);
18a3d61e
JM
5407 STRLEN speclen;
5408 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5409 int sts;
5410
360732b5
JM
5411 /* temp hack until UTF8 is actually implemented */
5412 if (fs_utf8 != NULL)
5413 *fs_utf8 = 0;
5414
18a3d61e
JM
5415 if (!filespec || !*filespec) {
5416 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5417 return NULL;
5418 }
18a3d61e
JM
5419
5420 vmsfspec = NULL;
6fb6c614 5421 vmsdefspec = NULL;
18a3d61e 5422 outbufl = NULL;
a1887106 5423
6fb6c614 5424 in_spec = filespec;
a1887106
JM
5425 isunix = 0;
5426 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
6fb6c614
JM
5427 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5428 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5429
5430 /* If this is a UNIX file spec, convert it to VMS */
5431 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5432 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5433 &e_len, &vs_spec, &vs_len);
5434 if (sts != 0) {
5435 isunix = 1;
5436 char * ret_spec;
5437
c11536f5 5438 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
6fb6c614
JM
5439 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5440 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5441 if (ret_spec == NULL) {
5442 PerlMem_free(vmsfspec);
5443 return NULL;
5444 }
5445 in_spec = (const char *)vmsfspec;
18a3d61e 5446
6fb6c614
JM
5447 /* Unless we are forcing to VMS format, a UNIX input means
5448 * UNIX output, and that requires long names to be used
5449 */
5450 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
b1a8dcd7 5451#if !defined(__VAX) && defined(NAML$C_MAXRSS)
6fb6c614 5452 opts |= PERL_RMSEXPAND_M_LONG;
778e045f
CB
5453#else
5454 NOOP;
b1a8dcd7 5455#endif
6fb6c614
JM
5456 else
5457 isunix = 0;
a1887106 5458 }
18a3d61e 5459
6fb6c614
JM
5460 }
5461
5462 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
a480973c 5463 rms_bind_fab_nam(myfab, mynam);
18a3d61e 5464
6fb6c614
JM
5465 /* Process the default file specification if present */
5466 def_spec = defspec;
18a3d61e
JM
5467 if (defspec && *defspec) {
5468 int t_isunix;
5469 t_isunix = is_unix_filespec(defspec);
5470 if (t_isunix) {
c11536f5 5471 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
6fb6c614
JM
5472 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5473 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5474
5475 if (ret_spec == NULL) {
5476 /* Clean up and bail */
5477 PerlMem_free(vmsdefspec);
5478 if (vmsfspec != NULL)
5479 PerlMem_free(vmsfspec);
5480 return NULL;
5481 }
5482 def_spec = (const char *)vmsdefspec;
18a3d61e 5483 }
6fb6c614
JM
5484 rms_set_dna(myfab, mynam,
5485 (char *)def_spec, strlen(def_spec)); /* cast ok */
18a3d61e
JM
5486 }
5487
6fb6c614 5488 /* Now we need the expansion buffers */
c11536f5 5489 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 5490 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5491#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 5492 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5493 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5494#endif
a1887106 5495 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
18a3d61e 5496
d584a1c6
JM
5497 /* If a NAML block is used RMS always writes to the long and short
5498 * addresses unless you suppress the short name.
5499 */
a480973c 5500#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 5501 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 5502 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5503#endif
d584a1c6 5504 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
18a3d61e 5505
f7ddb74a
JM
5506#ifdef NAM$M_NO_SHORT_UPCASE
5507 if (decc_efs_case_preserve)
a480973c 5508 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 5509#endif
18a3d61e 5510
e0e5e8d6
JM
5511 /* We may not want to follow symbolic links */
5512#ifdef NAML$M_OPEN_SPECIAL
5513 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5514 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5515#endif
5516
18a3d61e
JM
5517 /* First attempt to parse as an existing file */
5518 retsts = sys$parse(&myfab,0,0);
5519 if (!(retsts & STS$K_SUCCESS)) {
5520
5521 /* Could not find the file, try as syntax only if error is not fatal */
a480973c 5522 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
6fb6c614
JM
5523 if (retsts == RMS$_DNF ||
5524 retsts == RMS$_DIR ||
5525 retsts == RMS$_DEV ||
5526 retsts == RMS$_PRV) {
18a3d61e 5527 retsts = sys$parse(&myfab,0,0);
6fb6c614 5528 if (retsts & STS$K_SUCCESS) goto int_expanded;
18a3d61e
JM
5529 }
5530
5531 /* Still could not parse the file specification */
5532 /*----------------------------------------------*/
a480973c 5533 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5534 if (vmsdefspec != NULL)
5535 PerlMem_free(vmsdefspec);
18a3d61e 5536 if (vmsfspec != NULL)
c5375c28
JM
5537 PerlMem_free(vmsfspec);
5538 if (outbufl != NULL)
5539 PerlMem_free(outbufl);
5540 PerlMem_free(esa);
7566800d
CB
5541 if (esal != NULL)
5542 PerlMem_free(esal);
18a3d61e
JM
5543 set_vaxc_errno(retsts);
5544 if (retsts == RMS$_PRV) set_errno(EACCES);
5545 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5546 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5547 else set_errno(EVMSERR);
5548 return NULL;
5549 }
5550 retsts = sys$search(&myfab,0,0);
5551 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
a480973c 5552 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5553 if (vmsdefspec != NULL)
5554 PerlMem_free(vmsdefspec);
18a3d61e 5555 if (vmsfspec != NULL)
c5375c28
JM
5556 PerlMem_free(vmsfspec);
5557 if (outbufl != NULL)
5558 PerlMem_free(outbufl);
5559 PerlMem_free(esa);
7566800d
CB
5560 if (esal != NULL)
5561 PerlMem_free(esal);
18a3d61e
JM
5562 set_vaxc_errno(retsts);
5563 if (retsts == RMS$_PRV) set_errno(EACCES);
5564 else set_errno(EVMSERR);
5565 return NULL;
5566 }
5567
5568 /* If the input filespec contained any lowercase characters,
5569 * downcase the result for compatibility with Unix-minded code. */
6fb6c614 5570int_expanded:
18a3d61e 5571 if (!decc_efs_case_preserve) {
6fb6c614 5572 char * tbuf;
c5375c28
JM
5573 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5574 if (islower(*tbuf)) { haslower = 1; break; }
18a3d61e
JM
5575 }
5576
5577 /* Is a long or a short name expected */
5578 /*------------------------------------*/
6fb6c614 5579 spec_buf = NULL;
778e045f 5580#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5581 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5582 if (rms_nam_rsll(mynam)) {
6fb6c614 5583 spec_buf = outbufl;
a480973c 5584 speclen = rms_nam_rsll(mynam);
18a3d61e
JM
5585 }
5586 else {
6fb6c614 5587 spec_buf = esal; /* Not esa */
a480973c 5588 speclen = rms_nam_esll(mynam);
18a3d61e
JM
5589 }
5590 }
5591 else {
778e045f 5592#endif
a480973c 5593 if (rms_nam_rsl(mynam)) {
6fb6c614 5594 spec_buf = outbuf;
a480973c 5595 speclen = rms_nam_rsl(mynam);
18a3d61e
JM
5596 }
5597 else {
6fb6c614 5598 spec_buf = esa; /* Not esal */
a480973c 5599 speclen = rms_nam_esl(mynam);
18a3d61e 5600 }
778e045f 5601#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5602 }
778e045f 5603#endif
6fb6c614 5604 spec_buf[speclen] = '\0';
4d743a9b 5605
18a3d61e
JM
5606 /* Trim off null fields added by $PARSE
5607 * If type > 1 char, must have been specified in original or default spec
5608 * (not true for version; $SEARCH may have added version of existing file).
5609 */
a480973c 5610 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
18a3d61e 5611 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5612 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5613 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
18a3d61e
JM
5614 }
5615 else {
a480973c
JM
5616 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5617 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
18a3d61e
JM
5618 }
5619 if (trimver || trimtype) {
5620 if (defspec && *defspec) {
5621 char *defesal = NULL;
d584a1c6 5622 char *defesa = NULL;
c11536f5 5623 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
d584a1c6 5624 if (defesa != NULL) {
6fb6c614 5625 struct FAB deffab = cc$rms_fab;
d584a1c6 5626#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 5627 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 5628 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 5629#endif
a480973c 5630 rms_setup_nam(defnam);
18a3d61e 5631
a480973c
JM
5632 rms_bind_fab_nam(deffab, defnam);
5633
5634 /* Cast ok */
5635 rms_set_fna
5636 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5637
d584a1c6
JM
5638 /* RMS needs the esa/esal as a work area if wildcards are involved */
5639 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
a480973c 5640
4d743a9b 5641 rms_clear_nam_nop(defnam);
a480973c 5642 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
18a3d61e
JM
5643#ifdef NAM$M_NO_SHORT_UPCASE
5644 if (decc_efs_case_preserve)
a480973c 5645 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
18a3d61e 5646#endif
e0e5e8d6
JM
5647#ifdef NAML$M_OPEN_SPECIAL
5648 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5649 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5650#endif
18a3d61e
JM
5651 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5652 if (trimver) {
a480973c 5653 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
18a3d61e
JM
5654 }
5655 if (trimtype) {
a480973c 5656 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
18a3d61e
JM
5657 }
5658 }
d584a1c6
JM
5659 if (defesal != NULL)
5660 PerlMem_free(defesal);
5661 PerlMem_free(defesa);
6fb6c614
JM
5662 } else {
5663 _ckvmssts_noperl(SS$_INSFMEM);
18a3d61e
JM
5664 }
5665 }
5666 if (trimver) {
5667 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c 5668 if (*(rms_nam_verl(mynam)) != '\"')
6fb6c614 5669 speclen = rms_nam_verl(mynam) - spec_buf;
18a3d61e
JM
5670 }
5671 else {
a480973c 5672 if (*(rms_nam_ver(mynam)) != '\"')
6fb6c614 5673 speclen = rms_nam_ver(mynam) - spec_buf;
18a3d61e
JM
5674 }
5675 }
5676 if (trimtype) {
5677 /* If we didn't already trim version, copy down */
5678 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
6fb6c614 5679 if (speclen > rms_nam_verl(mynam) - spec_buf)
18a3d61e 5680 memmove
a480973c
JM
5681 (rms_nam_typel(mynam),
5682 rms_nam_verl(mynam),
6fb6c614 5683 speclen - (rms_nam_verl(mynam) - spec_buf));
a480973c 5684 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
18a3d61e
JM
5685 }
5686 else {
6fb6c614 5687 if (speclen > rms_nam_ver(mynam) - spec_buf)
18a3d61e 5688 memmove
a480973c
JM
5689 (rms_nam_type(mynam),
5690 rms_nam_ver(mynam),
6fb6c614 5691 speclen - (rms_nam_ver(mynam) - spec_buf));
a480973c 5692 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
18a3d61e
JM
5693 }
5694 }
5695 }
5696
5697 /* Done with these copies of the input files */
5698 /*-------------------------------------------*/
5699 if (vmsfspec != NULL)
c5375c28 5700 PerlMem_free(vmsfspec);
6fb6c614
JM
5701 if (vmsdefspec != NULL)
5702 PerlMem_free(vmsdefspec);
18a3d61e
JM
5703
5704 /* If we just had a directory spec on input, $PARSE "helpfully"
5705 * adds an empty name and type for us */
d584a1c6 5706#if !defined(__VAX) && defined(NAML$C_MAXRSS)
18a3d61e 5707 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
a480973c
JM
5708 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5709 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5710 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
6fb6c614 5711 speclen = rms_nam_namel(mynam) - spec_buf;
18a3d61e 5712 }
d584a1c6
JM
5713 else
5714#endif
5715 {
a480973c
JM
5716 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5717 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5718 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
6fb6c614 5719 speclen = rms_nam_name(mynam) - spec_buf;
18a3d61e
JM
5720 }
5721
5722 /* Posix format specifications must have matching quotes */
4d743a9b 5723 if (speclen < (VMS_MAXRSS - 1)) {
6fb6c614
JM
5724 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5725 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5726 spec_buf[speclen] = '\"';
4d743a9b
JM
5727 speclen++;
5728 }
18a3d61e
JM
5729 }
5730 }
6fb6c614
JM
5731 spec_buf[speclen] = '\0';
5732 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
18a3d61e
JM
5733
5734 /* Have we been working with an expanded, but not resultant, spec? */
5735 /* Also, convert back to Unix syntax if necessary. */
d584a1c6
JM
5736 {
5737 int rsl;
18a3d61e 5738
d584a1c6
JM
5739#if !defined(__VAX) && defined(NAML$C_MAXRSS)
5740 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5741 rsl = rms_nam_rsll(mynam);
5742 } else
5743#endif
5744 {
5745 rsl = rms_nam_rsl(mynam);
5746 }
5747 if (!rsl) {
6fb6c614
JM
5748 /* rsl is not present, it means that spec_buf is either */
5749 /* esa or esal, and needs to be copied to outbuf */
5750 /* convert to Unix if desired */
d584a1c6 5751 if (isunix) {
6fb6c614
JM
5752 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5753 } else {
5754 /* VMS file specs are not in UTF-8 */
5755 if (fs_utf8 != NULL)
5756 *fs_utf8 = 0;
a35dcc95 5757 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
6fb6c614 5758 ret_spec = outbuf;
18a3d61e
JM
5759 }
5760 }
6fb6c614
JM
5761 else {
5762 /* Now spec_buf is either outbuf or outbufl */
5763 /* We need the result into outbuf */
5764 if (isunix) {
5765 /* If we need this in UNIX, then we need another buffer */
5766 /* to keep things in order */
5767 char * src;
5768 char * new_src = NULL;
5769 if (spec_buf == outbuf) {
c11536f5 5770 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
a35dcc95 5771 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
6fb6c614
JM
5772 } else {
5773 src = spec_buf;
5774 }
5775 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5776 if (new_src) {
5777 PerlMem_free(new_src);
5778 }
5779 } else {
5780 /* VMS file specs are not in UTF-8 */
5781 if (fs_utf8 != NULL)
5782 *fs_utf8 = 0;
5783
5784 /* Copy the buffer if needed */
5785 if (outbuf != spec_buf)
a35dcc95 5786 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
6fb6c614 5787 ret_spec = outbuf;
d584a1c6 5788 }
18a3d61e 5789 }
18a3d61e 5790 }
6fb6c614
JM
5791
5792 /* Need to clean up the search context */
a480973c
JM
5793 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5794 sts = rms_free_search_context(&myfab); /* Free search context */
6fb6c614
JM
5795
5796 /* Clean up the extra buffers */
7566800d 5797 if (esal != NULL)
6fb6c614
JM
5798 PerlMem_free(esal);
5799 PerlMem_free(esa);
c5375c28
JM
5800 if (outbufl != NULL)
5801 PerlMem_free(outbufl);
6fb6c614
JM
5802
5803 /* Return the result */
5804 return ret_spec;
5805}
5806
5807/* Common simple case - Expand an already VMS spec */
5808static char *
5809int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5810 opts |= PERL_RMSEXPAND_M_VMS_IN;
5811 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5812}
5813
5814/* Common simple case - Expand to a VMS spec */
5815static char *
5816int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5817 opts |= PERL_RMSEXPAND_M_VMS;
5818 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5819}
5820
5821
5822/* Entry point used by perl routines */
5823static char *
5824mp_do_rmsexpand
5825 (pTHX_ const char *filespec,
5826 char *outbuf,
5827 int ts,
5828 const char *defspec,
5829 unsigned opts,
5830 int * fs_utf8,
5831 int * dfs_utf8)
5832{
5833 static char __rmsexpand_retbuf[VMS_MAXRSS];
5834 char * expanded, *ret_spec, *ret_buf;
5835
5836 expanded = NULL;
5837 ret_buf = outbuf;
5838 if (ret_buf == NULL) {
5839 if (ts) {
5840 Newx(expanded, VMS_MAXRSS, char);
5841 if (expanded == NULL)
5842 _ckvmssts(SS$_INSFMEM);
5843 ret_buf = expanded;
5844 } else {
5845 ret_buf = __rmsexpand_retbuf;
5846 }
5847 }
5848
5849
5850 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5851 opts, fs_utf8, dfs_utf8);
5852
5853 if (ret_spec == NULL) {
5854 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5855 if (expanded)
5856 Safefree(expanded);
5857 }
5858
5859 return ret_spec;
bbce6d69 5860}
5861/*}}}*/
5862/* External entry points */
2fbb330f 5863char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5 5864{ return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
2fbb330f 5865char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
360732b5
JM
5866{ return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5867char *Perl_rmsexpand_utf8
5868 (pTHX_ const char *spec, char *buf, const char *def,
5869 unsigned opt, int * fs_utf8, int * dfs_utf8)
5870{ return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5871char *Perl_rmsexpand_utf8_ts
5872 (pTHX_ const char *spec, char *buf, const char *def,
5873 unsigned opt, int * fs_utf8, int * dfs_utf8)
5874{ return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
bbce6d69 5875
5876
a0d0e21e
LW
5877/*
5878** The following routines are provided to make life easier when
5879** converting among VMS-style and Unix-style directory specifications.
5880** All will take input specifications in either VMS or Unix syntax. On
5881** failure, all return NULL. If successful, the routines listed below
748a9306 5882** return a pointer to a buffer containing the appropriately
a0d0e21e
LW
5883** reformatted spec (and, therefore, subsequent calls to that routine
5884** will clobber the result), while the routines of the same names with
5885** a _ts suffix appended will return a pointer to a mallocd string
5886** containing the appropriately reformatted spec.
5887** In all cases, only explicit syntax is altered; no check is made that
5888** the resulting string is valid or that the directory in question
5889** actually exists.
5890**
5891** fileify_dirspec() - convert a directory spec into the name of the
5892** directory file (i.e. what you can stat() to see if it's a dir).
5893** The style (VMS or Unix) of the result is the same as the style
5894** of the parameter passed in.
5895** pathify_dirspec() - convert a directory spec into a path (i.e.
5896** what you prepend to a filename to indicate what directory it's in).
5897** The style (VMS or Unix) of the result is the same as the style
5898** of the parameter passed in.
5899** tounixpath() - convert a directory spec into a Unix-style path.
5900** tovmspath() - convert a directory spec into a VMS-style path.
5901** tounixspec() - convert any file spec into a Unix-style file spec.
5902** tovmsspec() - convert any file spec into a VMS-style spec.
360732b5 5903** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
e518068a 5904**
bd3fa61c 5905** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
01b8edb6 5906** Permission is given to distribute this code as part of the Perl
5907** standard distribution under the terms of the GNU General Public
5908** License or the Perl Artistic License. Copies of each may be
5909** found in the Perl standard distribution.
a0d0e21e
LW
5910 */
5911
a979ce91
JM
5912/*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5913static char *
5914int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
a0d0e21e 5915{
4e0c9737 5916 unsigned long int dirlen, retlen, hasfilename = 0;
a979ce91 5917 char *cp1, *cp2, *lastdir;
a480973c 5918 char *trndir, *vmsdir;
2d9f3838 5919 unsigned short int trnlnm_iter_count;
f7ddb74a 5920 int sts;
360732b5
JM
5921 if (utf8_fl != NULL)
5922 *utf8_fl = 0;
a0d0e21e 5923
c07a80fd 5924 if (!dir || !*dir) {
5925 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5926 }
a0d0e21e 5927 dirlen = strlen(dir);
a2a90019 5928 while (dirlen && dir[dirlen-1] == '/') --dirlen;
61bb5906 5929 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
f7ddb74a
JM
5930 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5931 dir = "/sys$disk";
5932 dirlen = 9;
5933 }
5934 else
5935 dirlen = 1;
61bb5906 5936 }
a480973c
JM
5937 if (dirlen > (VMS_MAXRSS - 1)) {
5938 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5939 return NULL;
c07a80fd 5940 }
c11536f5 5941 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 5942 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
5943 if (!strpbrk(dir+1,"/]>:") &&
5944 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
e518068a 5945 strcpy(trndir,*dir == '/' ? dir + 1: dir);
2d9f3838 5946 trnlnm_iter_count = 0;
b8486b9d 5947 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
2d9f3838
CB
5948 trnlnm_iter_count++;
5949 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5950 }
b8ffc8df 5951 dirlen = strlen(trndir);
e518068a 5952 }
01b8edb6 5953 else {
a35dcc95 5954 memcpy(trndir, dir, dirlen);
01b8edb6 5955 trndir[dirlen] = '\0';
01b8edb6 5956 }
b8ffc8df
RGS
5957
5958 /* At this point we are done with *dir and use *trndir which is a
5959 * copy that can be modified. *dir must not be modified.
5960 */
5961
c07a80fd 5962 /* If we were handed a rooted logical name or spec, treat it like a
5963 * simple directory, so that
5964 * $ Define myroot dev:[dir.]
5965 * ... do_fileify_dirspec("myroot",buf,1) ...
5966 * does something useful.
5967 */
b8ffc8df
RGS
5968 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5969 trndir[--dirlen] = '\0';
5970 trndir[dirlen-1] = ']';
c07a80fd 5971 }
b8ffc8df
RGS
5972 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5973 trndir[--dirlen] = '\0';
5974 trndir[dirlen-1] = '>';
46112e17 5975 }
e518068a 5976
b8ffc8df 5977 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
b7ae7a0d 5978 /* If we've got an explicit filename, we can just shuffle the string. */
5979 if (*(cp1+1)) hasfilename = 1;
5980 /* Similarly, we can just back up a level if we've got multiple levels
5981 of explicit directories in a VMS spec which ends with directories. */
5982 else {
b8ffc8df 5983 for (cp2 = cp1; cp2 > trndir; cp2--) {
f7ddb74a
JM
5984 if (*cp2 == '.') {
5985 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
657054d4 5986/* fix-me, can not scan EFS file specs backward like this */
f7ddb74a
JM
5987 *cp2 = *cp1; *cp1 = '\0';
5988 hasfilename = 1;
5989 break;
5990 }
b7ae7a0d 5991 }
5992 if (*cp2 == '[' || *cp2 == '<') break;
5993 }
5994 }
5995 }
5996
c11536f5 5997 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
ebd4d70b 5998 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c 5999 cp1 = strpbrk(trndir,"]:>");
a979ce91
JM
6000 if (hasfilename || !cp1) { /* filename present or not VMS */
6001
b8ffc8df 6002 if (trndir[0] == '.') {
a480973c 6003 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
c5375c28
JM
6004 PerlMem_free(trndir);
6005 PerlMem_free(vmsdir);
a979ce91 6006 return int_fileify_dirspec("[]", buf, NULL);
a480973c 6007 }
b8ffc8df 6008 else if (trndir[1] == '.' &&
a480973c 6009 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
c5375c28
JM
6010 PerlMem_free(trndir);
6011 PerlMem_free(vmsdir);
a979ce91 6012 return int_fileify_dirspec("[-]", buf, NULL);
a480973c 6013 }
748a9306 6014 }
b8ffc8df 6015 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
a0d0e21e 6016 dirlen -= 1; /* to last element */
b8ffc8df 6017 lastdir = strrchr(trndir,'/');
a0d0e21e 6018 }
b8ffc8df 6019 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
01b8edb6 6020 /* If we have "/." or "/..", VMSify it and let the VMS code
6021 * below expand it, rather than repeating the code to handle
6022 * relative components of a filespec here */
4633a7c4
LW
6023 do {
6024 if (*(cp1+2) == '.') cp1++;
6025 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
a480973c 6026 char * ret_chr;
df278665 6027 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
c5375c28
JM
6028 PerlMem_free(trndir);
6029 PerlMem_free(vmsdir);
a480973c
JM
6030 return NULL;
6031 }
fc1ce8cc 6032 if (strchr(vmsdir,'/') != NULL) {
df278665 6033 /* If int_tovmsspec() returned it, it must have VMS syntax
fc1ce8cc
CB
6034 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6035 * the time to check this here only so we avoid a recursion
6036 * loop; otherwise, gigo.
6037 */
c5375c28
JM
6038 PerlMem_free(trndir);
6039 PerlMem_free(vmsdir);
a480973c
JM
6040 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6041 return NULL;
fc1ce8cc 6042 }
a979ce91 6043 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6044 PerlMem_free(trndir);
6045 PerlMem_free(vmsdir);
a480973c
JM
6046 return NULL;
6047 }
0e5ce2c7 6048 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6049 PerlMem_free(trndir);
6050 PerlMem_free(vmsdir);
a480973c 6051 return ret_chr;
4633a7c4
LW
6052 }
6053 cp1++;
6054 } while ((cp1 = strstr(cp1,"/.")) != NULL);
b8ffc8df 6055 lastdir = strrchr(trndir,'/');
748a9306 6056 }
b8ffc8df 6057 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
a480973c 6058 char * ret_chr;
61bb5906
CB
6059 /* Ditto for specs that end in an MFD -- let the VMS code
6060 * figure out whether it's a real device or a rooted logical. */
f7ddb74a
JM
6061
6062 /* This should not happen any more. Allowing the fake /000000
6063 * in a UNIX pathname causes all sorts of problems when trying
6064 * to run in UNIX emulation. So the VMS to UNIX conversions
6065 * now remove the fake /000000 directories.
6066 */
6067
b8ffc8df 6068 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
df278665 6069 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
c5375c28
JM
6070 PerlMem_free(trndir);
6071 PerlMem_free(vmsdir);
a480973c
JM
6072 return NULL;
6073 }
a979ce91 6074 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
c5375c28
JM
6075 PerlMem_free(trndir);
6076 PerlMem_free(vmsdir);
a480973c
JM
6077 return NULL;
6078 }
0e5ce2c7 6079 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
c5375c28
JM
6080 PerlMem_free(trndir);
6081 PerlMem_free(vmsdir);
a480973c 6082 return ret_chr;
61bb5906 6083 }
a0d0e21e 6084 else {
f7ddb74a 6085
b8ffc8df
RGS
6086 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6087 !(lastdir = cp1 = strrchr(trndir,']')) &&
6088 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
f7ddb74a 6089
a979ce91
JM
6090 cp2 = strrchr(cp1,'.');
6091 if (cp2) {
6092 int e_len, vs_len = 0;
6093 int is_dir = 0;
6094 char * cp3;
6095 cp3 = strchr(cp2,';');
6096 e_len = strlen(cp2);
6097 if (cp3) {
6098 vs_len = strlen(cp3);
6099 e_len = e_len - vs_len;
6100 }
6101 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6102 if (!is_dir) {
6103 if (!decc_efs_charset) {
6104 /* If this is not EFS, then not a directory */
6105 PerlMem_free(trndir);
6106 PerlMem_free(vmsdir);
6107 set_errno(ENOTDIR);
6108 set_vaxc_errno(RMS$_DIR);
6109 return NULL;
6110 }
6111 } else {
6112 /* Ok, here we have an issue, technically if a .dir shows */
6113 /* from inside a directory, then we should treat it as */
6114 /* xxx^.dir.dir. But we do not have that context at this */
6115 /* point unless this is totally restructured, so we remove */
6116 /* The .dir for now, and fix this better later */
6117 dirlen = cp2 - trndir;
6118 }
37769287
CB
6119 if (decc_efs_charset && !strchr(trndir,'/')) {
6120 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
a9fac63d
CB
6121 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6122
6123 for (; cp4 > cp1; cp4--) {
6124 if (*cp4 == '.') {
6125 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6126 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6127 *cp4 = '^';
6128 dirlen++;
6129 }
6130 }
6131 }
6132 }
a0d0e21e 6133 }
a979ce91 6134
748a9306 6135 }
f7ddb74a
JM
6136
6137 retlen = dirlen + 6;
a979ce91
JM
6138 memcpy(buf, trndir, dirlen);
6139 buf[dirlen] = '\0';
f7ddb74a 6140
a0d0e21e
LW
6141 /* We've picked up everything up to the directory file name.
6142 Now just add the type and version, and we're set. */
839e16da
CB
6143 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6144 strcat(buf,".dir;1");
6145 else
6146 strcat(buf,".DIR;1");
c5375c28
JM
6147 PerlMem_free(trndir);
6148 PerlMem_free(vmsdir);
a979ce91 6149 return buf;
a0d0e21e
LW
6150 }
6151 else { /* VMS-style directory spec */
a480973c 6152
d584a1c6
JM
6153 char *esa, *esal, term, *cp;
6154 char *my_esa;
6155 int my_esa_len;
4e0c9737 6156 unsigned long int cmplen, haslower = 0;
a0d0e21e 6157 struct FAB dirfab = cc$rms_fab;
a480973c
JM
6158 rms_setup_nam(savnam);
6159 rms_setup_nam(dirnam);
6160
c11536f5 6161 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 6162 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
6163 esal = NULL;
6164#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 6165 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 6166 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 6167#endif
a480973c
JM
6168 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6169 rms_bind_fab_nam(dirfab, dirnam);
6170 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
d584a1c6 6171 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
f7ddb74a
JM
6172#ifdef NAM$M_NO_SHORT_UPCASE
6173 if (decc_efs_case_preserve)
a480973c 6174 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6175#endif
01b8edb6 6176
b8ffc8df 6177 for (cp = trndir; *cp; cp++)
01b8edb6 6178 if (islower(*cp)) { haslower = 1; break; }
a480973c 6179 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
a979ce91
JM
6180 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6181 (dirfab.fab$l_sts == RMS$_DNF) ||
6182 (dirfab.fab$l_sts == RMS$_PRV)) {
6183 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6184 sts = sys$parse(&dirfab);
e518068a 6185 }
6186 if (!sts) {
c5375c28 6187 PerlMem_free(esa);
d584a1c6
JM
6188 if (esal != NULL)
6189 PerlMem_free(esal);
c5375c28
JM
6190 PerlMem_free(trndir);
6191 PerlMem_free(vmsdir);
748a9306
LW
6192 set_errno(EVMSERR);
6193 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
6194 return NULL;
6195 }
e518068a 6196 }
6197 else {
6198 savnam = dirnam;
a480973c
JM
6199 /* Does the file really exist? */
6200 if (sys$search(&dirfab)& STS$K_SUCCESS) {
e518068a 6201 /* Yes; fake the fnb bits so we'll check type below */
a979ce91 6202 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
e518068a 6203 }
752635ea
CB
6204 else { /* No; just work with potential name */
6205 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6206 else {
2623a4a6
JM
6207 int fab_sts;
6208 fab_sts = dirfab.fab$l_sts;
6209 sts = rms_free_search_context(&dirfab);
c5375c28 6210 PerlMem_free(esa);
d584a1c6
JM
6211 if (esal != NULL)
6212 PerlMem_free(esal);
c5375c28
JM
6213 PerlMem_free(trndir);
6214 PerlMem_free(vmsdir);
2623a4a6 6215 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
e518068a 6216 return NULL;
6217 }
e518068a 6218 }
a0d0e21e 6219 }
d584a1c6
JM
6220
6221 /* Make sure we are using the right buffer */
778e045f 6222#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6
JM
6223 if (esal != NULL) {
6224 my_esa = esal;
6225 my_esa_len = rms_nam_esll(dirnam);
6226 } else {
778e045f 6227#endif
d584a1c6
JM
6228 my_esa = esa;
6229 my_esa_len = rms_nam_esl(dirnam);
778e045f 6230#if !defined(__VAX) && defined(NAML$C_MAXRSS)
d584a1c6 6231 }
778e045f 6232#endif
d584a1c6 6233 my_esa[my_esa_len] = '\0';
a480973c 6234 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
d584a1c6
JM
6235 cp1 = strchr(my_esa,']');
6236 if (!cp1) cp1 = strchr(my_esa,'>');
748a9306 6237 if (cp1) { /* Should always be true */
d584a1c6
JM
6238 my_esa_len -= cp1 - my_esa - 1;
6239 memmove(my_esa, cp1 + 1, my_esa_len);
748a9306
LW
6240 }
6241 }
a480973c 6242 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
a0d0e21e 6243 /* Yep; check version while we're at it, if it's there. */
a480973c
JM
6244 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6245 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
a0d0e21e 6246 /* Something other than .DIR[;1]. Bzzt. */
a480973c 6247 sts = rms_free_search_context(&dirfab);
c5375c28 6248 PerlMem_free(esa);
d584a1c6
JM
6249 if (esal != NULL)
6250 PerlMem_free(esal);
c5375c28
JM
6251 PerlMem_free(trndir);
6252 PerlMem_free(vmsdir);
748a9306
LW
6253 set_errno(ENOTDIR);
6254 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
6255 return NULL;
6256 }
748a9306 6257 }
ae6d78fe 6258
a480973c 6259 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
748a9306 6260 /* They provided at least the name; we added the type, if necessary, */
a35dcc95 6261 my_strlcpy(buf, my_esa, VMS_MAXRSS);
a480973c 6262 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6263 PerlMem_free(trndir);
6264 PerlMem_free(esa);
d584a1c6
JM
6265 if (esal != NULL)
6266 PerlMem_free(esal);
c5375c28 6267 PerlMem_free(vmsdir);
a979ce91 6268 return buf;
748a9306 6269 }
c07a80fd 6270 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6271 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6272 *cp1 = '\0';
d584a1c6 6273 my_esa_len -= 9;
c07a80fd 6274 }
d584a1c6 6275 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
752635ea 6276 if (cp1 == NULL) { /* should never happen */
a480973c 6277 sts = rms_free_search_context(&dirfab);
c5375c28
JM
6278 PerlMem_free(trndir);
6279 PerlMem_free(esa);
d584a1c6
JM
6280 if (esal != NULL)
6281 PerlMem_free(esal);
c5375c28 6282 PerlMem_free(vmsdir);
752635ea
CB
6283 return NULL;
6284 }
748a9306
LW
6285 term = *cp1;
6286 *cp1 = '\0';
d584a1c6
JM
6287 retlen = strlen(my_esa);
6288 cp1 = strrchr(my_esa,'.');
f7ddb74a 6289 /* ODS-5 directory specifications can have extra "." in them. */
657054d4 6290 /* Fix-me, can not scan EFS file specifications backwards */
f7ddb74a 6291 while (cp1 != NULL) {
d584a1c6 6292 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
f7ddb74a
JM
6293 break;
6294 else {
6295 cp1--;
d584a1c6 6296 while ((cp1 > my_esa) && (*cp1 != '.'))
f7ddb74a
JM
6297 cp1--;
6298 }
d584a1c6 6299 if (cp1 == my_esa)
f7ddb74a
JM
6300 cp1 = NULL;
6301 }
6302
6303 if ((cp1) != NULL) {
748a9306
LW
6304 /* There's more than one directory in the path. Just roll back. */
6305 *cp1 = term;
a35dcc95 6306 my_strlcpy(buf, my_esa, VMS_MAXRSS);
a0d0e21e
LW
6307 }
6308 else {
a480973c 6309 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
748a9306 6310 /* Go back and expand rooted logical name */
a480973c 6311 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
f7ddb74a
JM
6312#ifdef NAM$M_NO_SHORT_UPCASE
6313 if (decc_efs_case_preserve)
a480973c 6314 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
f7ddb74a 6315#endif
a480973c
JM
6316 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6317 sts = rms_free_search_context(&dirfab);
c5375c28 6318 PerlMem_free(esa);
d584a1c6
JM
6319 if (esal != NULL)
6320 PerlMem_free(esal);
c5375c28
JM
6321 PerlMem_free(trndir);
6322 PerlMem_free(vmsdir);
748a9306
LW
6323 set_errno(EVMSERR);
6324 set_vaxc_errno(dirfab.fab$l_sts);
6325 return NULL;
6326 }
d584a1c6
JM
6327
6328 /* This changes the length of the string of course */
6329 if (esal != NULL) {
6330 my_esa_len = rms_nam_esll(dirnam);
6331 } else {
6332 my_esa_len = rms_nam_esl(dirnam);
6333 }
6334
6335 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
d584a1c6
JM
6336 cp1 = strstr(my_esa,"][");
6337 if (!cp1) cp1 = strstr(my_esa,"]<");
6338 dirlen = cp1 - my_esa;
a979ce91 6339 memcpy(buf, my_esa, dirlen);
748a9306 6340 if (!strncmp(cp1+2,"000000]",7)) {
a979ce91 6341 buf[dirlen-1] = '\0';
657054d4 6342 /* fix-me Not full ODS-5, just extra dots in directories for now */
a979ce91
JM
6343 cp1 = buf + dirlen - 1;
6344 while (cp1 > buf)
f7ddb74a
JM
6345 {
6346 if (*cp1 == '[')
6347 break;
6348 if (*cp1 == '.') {
6349 if (*(cp1-1) != '^')
6350 break;
6351 }
6352 cp1--;
6353 }
4633a7c4
LW
6354 if (*cp1 == '.') *cp1 = ']';
6355 else {
a979ce91 6356 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6357 memmove(cp1+1,"000000]",7);
4633a7c4 6358 }
748a9306
LW
6359 }
6360 else {
a979ce91
JM
6361 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6362 buf[retlen] = '\0';
748a9306 6363 /* Convert last '.' to ']' */
a979ce91 6364 cp1 = buf+retlen-1;
f7ddb74a
JM
6365 while (*cp != '[') {
6366 cp1--;
6367 if (*cp1 == '.') {
6368 /* Do not trip on extra dots in ODS-5 directories */
a979ce91 6369 if ((cp1 == buf) || (*(cp1-1) != '^'))
f7ddb74a
JM
6370 break;
6371 }
6372 }
4633a7c4
LW
6373 if (*cp1 == '.') *cp1 = ']';
6374 else {
a979ce91 6375 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
18a3d61e 6376 memmove(cp1+1,"000000]",7);
4633a7c4 6377 }
748a9306 6378 }
a0d0e21e 6379 }
748a9306 6380 else { /* This is a top-level dir. Add the MFD to the path. */
d584a1c6 6381 cp1 = my_esa;
a979ce91 6382 cp2 = buf;
bbdb6c9a 6383 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
a0d0e21e
LW
6384 strcpy(cp2,":[000000]");
6385 cp1 += 2;
6386 strcpy(cp2+9,cp1);
6387 }
748a9306 6388 }
a480973c 6389 sts = rms_free_search_context(&dirfab);
748a9306 6390 /* We've set up the string up through the filename. Add the
a0d0e21e 6391 type and version, and we're done. */
a979ce91 6392 strcat(buf,".DIR;1");
01b8edb6 6393
6394 /* $PARSE may have upcased filespec, so convert output to lower
6395 * case if input contained any lowercase characters. */
a979ce91 6396 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
c5375c28
JM
6397 PerlMem_free(trndir);
6398 PerlMem_free(esa);
d584a1c6
JM
6399 if (esal != NULL)
6400 PerlMem_free(esal);
c5375c28 6401 PerlMem_free(vmsdir);
a979ce91 6402 return buf;
a0d0e21e 6403 }
a979ce91
JM
6404} /* end of int_fileify_dirspec() */
6405
6406
6407/*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6408static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6409{
6410 static char __fileify_retbuf[VMS_MAXRSS];
6411 char * fileified, *ret_spec, *ret_buf;
6412
6413 fileified = NULL;
6414 ret_buf = buf;
6415 if (ret_buf == NULL) {
6416 if (ts) {
6417 Newx(fileified, VMS_MAXRSS, char);
6418 if (fileified == NULL)
6419 _ckvmssts(SS$_INSFMEM);
6420 ret_buf = fileified;
6421 } else {
6422 ret_buf = __fileify_retbuf;
6423 }
6424 }
6425
6426 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6427
6428 if (ret_spec == NULL) {
6429 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6430 if (fileified)
6431 Safefree(fileified);
6432 }
6433
6434 return ret_spec;
a0d0e21e
LW
6435} /* end of do_fileify_dirspec() */
6436/*}}}*/
a979ce91 6437
a0d0e21e 6438/* External entry points */
b8ffc8df 6439char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 6440{ return do_fileify_dirspec(dir,buf,0,NULL); }
b8ffc8df 6441char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
6442{ return do_fileify_dirspec(dir,buf,1,NULL); }
6443char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6444{ return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6445char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6446{ return do_fileify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 6447
1fe570cc
JM
6448static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6449 char * v_spec, int v_len, char * r_spec, int r_len,
6450 char * d_spec, int d_len, char * n_spec, int n_len,
6451 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6452
6453 /* VMS specification - Try to do this the simple way */
6454 if ((v_len + r_len > 0) || (d_len > 0)) {
6455 int is_dir;
6456
6457 /* No name or extension component, already a directory */
6458 if ((n_len + e_len + vs_len) == 0) {
6459 strcpy(buf, dir);
6460 return buf;
6461 }
6462
6463 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6464 /* This results from catfile() being used instead of catdir() */
6465 /* So even though it should not work, we need to allow it */
6466
6467 /* If this is .DIR;1 then do a simple conversion */
6468 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6469 if (is_dir || (e_len == 0) && (d_len > 0)) {
6470 int len;
6471 len = v_len + r_len + d_len - 1;
6472 char dclose = d_spec[d_len - 1];
a35dcc95 6473 memcpy(buf, dir, len);
1fe570cc
JM
6474 buf[len] = '.';
6475 len++;
a35dcc95 6476 memcpy(&buf[len], n_spec, n_len);
1fe570cc
JM
6477 len += n_len;
6478 buf[len] = dclose;
6479 buf[len + 1] = '\0';
6480 return buf;
6481 }
6482
6483#ifdef HAS_SYMLINK
6484 else if (d_len > 0) {
6485 /* In the olden days, a directory needed to have a .DIR */
6486 /* extension to be a valid directory, but now it could */
6487 /* be a symbolic link */
6488 int len;
6489 len = v_len + r_len + d_len - 1;
6490 char dclose = d_spec[d_len - 1];
a35dcc95 6491 memcpy(buf, dir, len);
1fe570cc
JM
6492 buf[len] = '.';
6493 len++;
a35dcc95 6494 memcpy(&buf[len], n_spec, n_len);
1fe570cc
JM
6495 len += n_len;
6496 if (e_len > 0) {
6497 if (decc_efs_charset) {
6498 buf[len] = '^';
6499 len++;
a35dcc95 6500 memcpy(&buf[len], e_spec, e_len);
1fe570cc
JM
6501 len += e_len;
6502 } else {
6503 set_vaxc_errno(RMS$_DIR);
6504 set_errno(ENOTDIR);
6505 return NULL;
6506 }
6507 }
6508 buf[len] = dclose;
6509 buf[len + 1] = '\0';
6510 return buf;
6511 }
6512#else
6513 else {
6514 set_vaxc_errno(RMS$_DIR);
6515 set_errno(ENOTDIR);
6516 return NULL;
6517 }
6518#endif
6519 }
6520 set_vaxc_errno(RMS$_DIR);
6521 set_errno(ENOTDIR);
6522 return NULL;
6523}
6524
6525
6526/* Internal routine to make sure or convert a directory to be in a */
6527/* path specification. No utf8 flag because it is not changed or used */
6528static char *int_pathify_dirspec(const char *dir, char *buf)
a0d0e21e 6529{
1fe570cc
JM
6530 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6531 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6532 char * exp_spec, *ret_spec;
6533 char * trndir;
2d9f3838 6534 unsigned short int trnlnm_iter_count;
baf3cf9c 6535 STRLEN trnlen;
1fe570cc
JM
6536 int need_to_lower;
6537
6538 if (vms_debug_fileify) {
6539 if (dir == NULL)
6540 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6541 else
6542 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6543 }
6544
6545 /* We may need to lower case the result if we translated */
6546 /* a logical name or got the current working directory */
6547 need_to_lower = 0;
a0d0e21e 6548
c07a80fd 6549 if (!dir || !*dir) {
1fe570cc
JM
6550 set_errno(EINVAL);
6551 set_vaxc_errno(SS$_BADPARAM);
6552 return NULL;
c07a80fd 6553 }
6554
c11536f5 6555 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
1fe570cc
JM
6556 if (trndir == NULL)
6557 _ckvmssts_noperl(SS$_INSFMEM);
c07a80fd 6558
1fe570cc
JM
6559 /* If no directory specified use the current default */
6560 if (*dir)
a35dcc95 6561 my_strlcpy(trndir, dir, VMS_MAXRSS);
1fe570cc
JM
6562 else {
6563 getcwd(trndir, VMS_MAXRSS - 1);
6564 need_to_lower = 1;
6565 }
6566
6567 /* now deal with bare names that could be logical names */
2d9f3838 6568 trnlnm_iter_count = 0;
93948341 6569 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
1fe570cc
JM
6570 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6571 trnlnm_iter_count++;
6572 need_to_lower = 1;
6573 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6574 break;
6575 trnlen = strlen(trndir);
6576
6577 /* Trap simple rooted lnms, and return lnm:[000000] */
6578 if (!strcmp(trndir+trnlen-2,".]")) {
a35dcc95 6579 my_strlcpy(buf, dir, VMS_MAXRSS);
1fe570cc
JM
6580 strcat(buf, ":[000000]");
6581 PerlMem_free(trndir);
6582
6583 if (vms_debug_fileify) {
6584 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6585 }
6586 return buf;
6587 }
c07a80fd 6588 }
748a9306 6589
1fe570cc 6590 /* At this point we do not work with *dir, but the copy in *trndir */
b8ffc8df 6591
1fe570cc
JM
6592 if (need_to_lower && !decc_efs_case_preserve) {
6593 /* Legacy mode, lower case the returned value */
6594 __mystrtolower(trndir);
6595 }
f7ddb74a 6596
1fe570cc
JM
6597
6598 /* Some special cases, '..', '.' */
6599 sts = 0;
6600 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6601 /* Force UNIX filespec */
6602 sts = 1;
6603
6604 } else {
6605 /* Is this Unix or VMS format? */
6606 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6607 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6608 &e_len, &vs_spec, &vs_len);
6609 if (sts == 0) {
6610
6611 /* Just a filename? */
6612 if ((v_len + r_len + d_len) == 0) {
6613
6614 /* Now we have a problem, this could be Unix or VMS */
6615 /* We have to guess. .DIR usually means VMS */
6616
6617 /* In UNIX report mode, the .DIR extension is removed */
6618 /* if one shows up, it is for a non-directory or a directory */
6619 /* in EFS charset mode */
6620
6621 /* So if we are in Unix report mode, assume that this */
6622 /* is a relative Unix directory specification */
6623
6624 sts = 1;
6625 if (!decc_filename_unix_report && decc_efs_charset) {
6626 int is_dir;
6627 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6628
6629 if (is_dir) {
6630 /* Traditional mode, assume .DIR is directory */
6631 buf[0] = '[';
6632 buf[1] = '.';
a35dcc95 6633 memcpy(&buf[2], n_spec, n_len);
1fe570cc
JM
6634 buf[n_len + 2] = ']';
6635 buf[n_len + 3] = '\0';
6636 PerlMem_free(trndir);
6637 if (vms_debug_fileify) {
6638 fprintf(stderr,
6639 "int_pathify_dirspec: buf = %s\n",
6640 buf);
6641 }
6642 return buf;
6643 }
6644 }
6645 }
a0d0e21e 6646 }
a0d0e21e 6647 }
1fe570cc
JM
6648 if (sts == 0) {
6649 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6650 v_spec, v_len, r_spec, r_len,
6651 d_spec, d_len, n_spec, n_len,
6652 e_spec, e_len, vs_spec, vs_len);
a0d0e21e 6653
1fe570cc
JM
6654 if (ret_spec != NULL) {
6655 PerlMem_free(trndir);
6656 if (vms_debug_fileify) {
6657 fprintf(stderr,
6658 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6659 }
6660 return ret_spec;
b7ae7a0d 6661 }
1fe570cc
JM
6662
6663 /* Simple way did not work, which means that a logical name */
6664 /* was present for the directory specification. */
6665 /* Need to use an rmsexpand variant to decode it completely */
c11536f5 6666 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
1fe570cc
JM
6667 if (exp_spec == NULL)
6668 _ckvmssts_noperl(SS$_INSFMEM);
6669
6670 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6671 if (ret_spec != NULL) {
6672 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6673 &r_spec, &r_len, &d_spec, &d_len,
6674 &n_spec, &n_len, &e_spec,
6675 &e_len, &vs_spec, &vs_len);
6676 if (sts == 0) {
6677 ret_spec = int_pathify_dirspec_simple(
6678 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6679 d_spec, d_len, n_spec, n_len,
6680 e_spec, e_len, vs_spec, vs_len);
6681
6682 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6683 /* Legacy mode, lower case the returned value */
6684 __mystrtolower(ret_spec);
6685 }
6686 } else {
6687 set_vaxc_errno(RMS$_DIR);
6688 set_errno(ENOTDIR);
6689 ret_spec = NULL;
6690 }
b7ae7a0d 6691 }
1fe570cc
JM
6692 PerlMem_free(exp_spec);
6693 PerlMem_free(trndir);
6694 if (vms_debug_fileify) {
6695 if (ret_spec == NULL)
6696 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6697 else
6698 fprintf(stderr,
6699 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6700 }
6701 return ret_spec;
a480973c 6702
1fe570cc 6703 } else {
bd1901c6
CB
6704 /* Unix specification, Could be trivial conversion, */
6705 /* but have to deal with trailing '.dir' or extra '.' */
1fe570cc 6706
bd1901c6
CB
6707 char * lastdot;
6708 char * lastslash;
6709 int is_dir;
6710 STRLEN dir_len = strlen(trndir);
1fe570cc 6711
bd1901c6
CB
6712 lastslash = strrchr(trndir, '/');
6713 if (lastslash == NULL)
6714 lastslash = trndir;
6715 else
6716 lastslash++;
6717
6718 lastdot = NULL;
6719
6720 /* '..' or '.' are valid directory components */
6721 is_dir = 0;
6722 if (lastslash[0] == '.') {
6723 if (lastslash[1] == '\0') {
6724 is_dir = 1;
6725 } else if (lastslash[1] == '.') {
6726 if (lastslash[2] == '\0') {
6727 is_dir = 1;
6728 } else {
6729 /* And finally allow '...' */
6730 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
1fe570cc 6731 is_dir = 1;
1fe570cc
JM
6732 }
6733 }
6734 }
bd1901c6 6735 }
01b8edb6 6736
bd1901c6
CB
6737 if (!is_dir) {
6738 lastdot = strrchr(lastslash, '.');
6739 }
6740 if (lastdot != NULL) {
6741 STRLEN e_len;
6742 /* '.dir' is discarded, and any other '.' is invalid */
6743 e_len = strlen(lastdot);
1fe570cc 6744
bd1901c6 6745 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
1fe570cc 6746
bd1901c6
CB
6747 if (is_dir) {
6748 dir_len = dir_len - 4;
1fe570cc 6749 }
e518068a 6750 }
1fe570cc 6751
a35dcc95 6752 my_strlcpy(buf, trndir, VMS_MAXRSS);
1fe570cc
JM
6753 if (buf[dir_len - 1] != '/') {
6754 buf[dir_len] = '/';
6755 buf[dir_len + 1] = '\0';
a0d0e21e 6756 }
1fe570cc
JM
6757
6758 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6759 if (!decc_efs_charset) {
6760 int dir_start = 0;
6761 char * str = buf;
6762 if (str[0] == '.') {
6763 char * dots = str;
6764 int cnt = 1;
6765 while ((dots[cnt] == '.') && (cnt < 3))
6766 cnt++;
6767 if (cnt <= 3) {
6768 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6769 dir_start = 1;
6770 str += cnt;
6771 }
6772 }
6773 }
6774 for (; *str; ++str) {
6775 while (*str == '/') {
6776 dir_start = 1;
6777 *str++;
6778 }
6779 if (dir_start) {
6780
6781 /* Have to skip up to three dots which could be */
6782 /* directories, 3 dots being a VMS extension for Perl */
6783 char * dots = str;
6784 int cnt = 0;
6785 while ((dots[cnt] == '.') && (cnt < 3)) {
6786 cnt++;
6787 }
6788 if (dots[cnt] == '\0')
6789 break;
6790 if ((cnt > 1) && (dots[cnt] != '/')) {
6791 dir_start = 0;
6792 } else {
6793 str += cnt;
6794 }
6795
6796 /* too many dots? */
6797 if ((cnt == 0) || (cnt > 3)) {
6798 dir_start = 0;
6799 }
6800 }
6801 if (!dir_start && (*str == '.')) {
6802 *str = '_';
6803 }
6804 }
e518068a 6805 }
1fe570cc
JM
6806 PerlMem_free(trndir);
6807 ret_spec = buf;
6808 if (vms_debug_fileify) {
6809 if (ret_spec == NULL)
6810 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6811 else
6812 fprintf(stderr,
6813 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
a0d0e21e 6814 }
1fe570cc
JM
6815 return ret_spec;
6816 }
6817}
d584a1c6 6818
1fe570cc
JM
6819/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6820static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6821{
6822 static char __pathify_retbuf[VMS_MAXRSS];
6823 char * pathified, *ret_spec, *ret_buf;
6824
6825 pathified = NULL;
6826 ret_buf = buf;
6827 if (ret_buf == NULL) {
6828 if (ts) {
6829 Newx(pathified, VMS_MAXRSS, char);
6830 if (pathified == NULL)
6831 _ckvmssts(SS$_INSFMEM);
6832 ret_buf = pathified;
6833 } else {
6834 ret_buf = __pathify_retbuf;
6835 }
6836 }
d584a1c6 6837
1fe570cc
JM
6838 ret_spec = int_pathify_dirspec(dir, ret_buf);
6839
6840 if (ret_spec == NULL) {
6841 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6842 if (pathified)
6843 Safefree(pathified);
a0d0e21e
LW
6844 }
6845
1fe570cc
JM
6846 return ret_spec;
6847
a0d0e21e 6848} /* end of do_pathify_dirspec() */
1fe570cc
JM
6849
6850
a0d0e21e 6851/* External entry points */
b8ffc8df 6852char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
360732b5 6853{ return do_pathify_dirspec(dir,buf,0,NULL); }
b8ffc8df 6854char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
360732b5
JM
6855{ return do_pathify_dirspec(dir,buf,1,NULL); }
6856char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6857{ return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6858char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6859{ return do_pathify_dirspec(dir,buf,1,utf8_fl); }
a0d0e21e 6860
0e5ce2c7
JM
6861/* Internal tounixspec routine that does not use a thread context */
6862/*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6863static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
a0d0e21e 6864{
0e5ce2c7 6865 char *dirend, *cp1, *cp3, *tmp;
b8ffc8df 6866 const char *cp2;
4e0c9737 6867 int dirlen;
2d9f3838 6868 unsigned short int trnlnm_iter_count;
f7ddb74a 6869 int cmp_rslt;
360732b5
JM
6870 if (utf8_fl != NULL)
6871 *utf8_fl = 0;
a0d0e21e 6872
0e5ce2c7
JM
6873 if (vms_debug_fileify) {
6874 if (spec == NULL)
6875 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6876 else
6877 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6878 }
6879
6880
6881 if (spec == NULL) {
6882 set_errno(EINVAL);
6883 set_vaxc_errno(SS$_BADPARAM);
6884 return NULL;
6885 }
6886 if (strlen(spec) > (VMS_MAXRSS-1)) {
6887 set_errno(E2BIG);
6888 set_vaxc_errno(SS$_BUFFEROVF);
6889 return NULL;
e518068a 6890 }
f7ddb74a 6891
2497a41f
JM
6892 /* New VMS specific format needs translation
6893 * glob passes filenames with trailing '\n' and expects this preserved.
6894 */
6895 if (decc_posix_compliant_pathnames) {
6896 if (strncmp(spec, "\"^UP^", 5) == 0) {
6897 char * uspec;
6898 char *tunix;
6899 int tunix_len;
6900 int nl_flag;
6901
c11536f5 6902 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 6903 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a35dcc95 6904 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
2497a41f
JM
6905 nl_flag = 0;
6906 if (tunix[tunix_len - 1] == '\n') {
6907 tunix[tunix_len - 1] = '\"';
6908 tunix[tunix_len] = '\0';
6909 tunix_len--;
6910 nl_flag = 1;
6911 }
6912 uspec = decc$translate_vms(tunix);
367e4b85 6913 PerlMem_free(tunix);
2497a41f 6914 if ((int)uspec > 0) {
a35dcc95 6915 my_strlcpy(rslt, uspec, VMS_MAXRSS);
2497a41f
JM
6916 if (nl_flag) {
6917 strcat(rslt,"\n");
6918 }
6919 else {
6920 /* If we can not translate it, makemaker wants as-is */
a35dcc95 6921 my_strlcpy(rslt, spec, VMS_MAXRSS);
2497a41f
JM
6922 }
6923 return rslt;
6924 }
6925 }
6926 }
6927
f7ddb74a
JM
6928 cmp_rslt = 0; /* Presume VMS */
6929 cp1 = strchr(spec, '/');
6930 if (cp1 == NULL)
6931 cmp_rslt = 0;
6932
6933 /* Look for EFS ^/ */
6934 if (decc_efs_charset) {
6935 while (cp1 != NULL) {
6936 cp2 = cp1 - 1;
6937 if (*cp2 != '^') {
6938 /* Found illegal VMS, assume UNIX */
6939 cmp_rslt = 1;
6940 break;
6941 }
6942 cp1++;
6943 cp1 = strchr(cp1, '/');
6944 }
6945 }
6946
6947 /* Look for "." and ".." */
6948 if (decc_filename_unix_report) {
6949 if (spec[0] == '.') {
6950 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6951 cmp_rslt = 1;
6952 }
6953 else {
6954 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6955 cmp_rslt = 1;
6956 }
6957 }
6958 }
6959 }
6960 /* This is already UNIX or at least nothing VMS understands */
6961 if (cmp_rslt) {
a35dcc95 6962 my_strlcpy(rslt, spec, VMS_MAXRSS);
0e5ce2c7
JM
6963 if (vms_debug_fileify) {
6964 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6965 }
a0d0e21e
LW
6966 return rslt;
6967 }
6968
6969 cp1 = rslt;
6970 cp2 = spec;
6971 dirend = strrchr(spec,']');
6972 if (dirend == NULL) dirend = strrchr(spec,'>');
6973 if (dirend == NULL) dirend = strchr(spec,':');
6974 if (dirend == NULL) {
6975 strcpy(rslt,spec);
0e5ce2c7
JM
6976 if (vms_debug_fileify) {
6977 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6978 }
a0d0e21e
LW
6979 return rslt;
6980 }
f7ddb74a
JM
6981
6982 /* Special case 1 - sys$posix_root = / */
f7ddb74a
JM
6983 if (!decc_disable_posix_root) {
6984 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6985 *cp1 = '/';
6986 cp1++;
6987 cp2 = cp2 + 15;
6988 }
6989 }
f7ddb74a
JM
6990
6991 /* Special case 2 - Convert NLA0: to /dev/null */
f7ddb74a 6992 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
f7ddb74a
JM
6993 if (cmp_rslt == 0) {
6994 strcpy(rslt, "/dev/null");
6995 cp1 = cp1 + 9;
6996 cp2 = cp2 + 5;
6997 if (spec[6] != '\0') {
07bee079 6998 cp1[9] = '/';
f7ddb74a
JM
6999 cp1++;
7000 cp2++;
7001 }
7002 }
7003
7004 /* Also handle special case "SYS$SCRATCH:" */
f7ddb74a 7005 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
c11536f5 7006 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 7007 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f7ddb74a
JM
7008 if (cmp_rslt == 0) {
7009 int islnm;
7010
b8486b9d 7011 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
f7ddb74a
JM
7012 if (!islnm) {
7013 strcpy(rslt, "/tmp");
7014 cp1 = cp1 + 4;
7015 cp2 = cp2 + 12;
7016 if (spec[12] != '\0') {
07bee079 7017 cp1[4] = '/';
f7ddb74a
JM
7018 cp1++;
7019 cp2++;
7020 }
7021 }
7022 }
7023
a5f75d66 7024 if (*cp2 != '[' && *cp2 != '<') {
a0d0e21e
LW
7025 *(cp1++) = '/';
7026 }
7027 else { /* the VMS spec begins with directories */
7028 cp2++;
a5f75d66 7029 if (*cp2 == ']' || *cp2 == '>') {
f86702cc 7030 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
367e4b85 7031 PerlMem_free(tmp);
a5f75d66
AD
7032 return rslt;
7033 }
f7ddb74a 7034 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
2f4077ca 7035 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
367e4b85 7036 PerlMem_free(tmp);
0e5ce2c7
JM
7037 if (vms_debug_fileify) {
7038 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7039 }
a0d0e21e
LW
7040 return NULL;
7041 }
2d9f3838 7042 trnlnm_iter_count = 0;
a0d0e21e
LW
7043 do {
7044 cp3 = tmp;
7045 while (*cp3 != ':' && *cp3) cp3++;
7046 *(cp3++) = '\0';
7047 if (strchr(cp3,']') != NULL) break;
2d9f3838
CB
7048 trnlnm_iter_count++;
7049 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
f675dbe5 7050 } while (vmstrnenv(tmp,tmp,0,fildev,0));
0e5ce2c7 7051 cp1 = rslt;
f86702cc 7052 cp3 = tmp;
7053 *(cp1++) = '/';
7054 while (*cp3) {
7055 *(cp1++) = *(cp3++);
0e5ce2c7 7056 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
367e4b85 7057 PerlMem_free(tmp);
0e5ce2c7
JM
7058 set_errno(ENAMETOOLONG);
7059 set_vaxc_errno(SS$_BUFFEROVF);
7060 if (vms_debug_fileify) {
7061 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7062 }
2f4077ca
JM
7063 return NULL; /* No room */
7064 }
a0d0e21e 7065 }
f86702cc 7066 *(cp1++) = '/';
7067 }
f7ddb74a
JM
7068 if ((*cp2 == '^')) {
7069 /* EFS file escape, pass the next character as is */
38a44b82 7070 /* Fix me: HEX encoding for Unicode not implemented */
f7ddb74a
JM
7071 cp2++;
7072 }
f86702cc 7073 else if ( *cp2 == '.') {
7074 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7075 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7076 cp2 += 3;
7077 }
7078 else cp2++;
a0d0e21e 7079 }
a0d0e21e 7080 }
367e4b85 7081 PerlMem_free(tmp);
a0d0e21e 7082 for (; cp2 <= dirend; cp2++) {
f7ddb74a
JM
7083 if ((*cp2 == '^')) {
7084 /* EFS file escape, pass the next character as is */
38a44b82 7085 /* Fix me: HEX encoding for Unicode not implemented */
42cd432e
CB
7086 *(cp1++) = *(++cp2);
7087 /* An escaped dot stays as is -- don't convert to slash */
7088 if (*cp2 == '.') cp2++;
f7ddb74a 7089 }
a0d0e21e
LW
7090 if (*cp2 == ':') {
7091 *(cp1++) = '/';
5ad5b34c 7092 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
a0d0e21e 7093 }
f86702cc 7094 else if (*cp2 == ']' || *cp2 == '>') {
7095 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7096 }
f7ddb74a 7097 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
a0d0e21e 7098 *(cp1++) = '/';
e518068a 7099 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7100 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7101 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7102 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7103 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7104 }
f86702cc 7105 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7106 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7107 cp2 += 2;
7108 }
a0d0e21e
LW
7109 }
7110 else if (*cp2 == '-') {
7111 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7112 while (*cp2 == '-') {
7113 cp2++;
7114 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7115 }
7116 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
0e5ce2c7 7117 /* filespecs like */
01b8edb6 7118 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
0e5ce2c7
JM
7119 if (vms_debug_fileify) {
7120 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7121 }
a0d0e21e
LW
7122 return NULL;
7123 }
a0d0e21e
LW
7124 }
7125 else *(cp1++) = *cp2;
7126 }
7127 else *(cp1++) = *cp2;
7128 }
0e5ce2c7 7129 /* Translate the rest of the filename. */
42cd432e 7130 while (*cp2) {
0e5ce2c7
JM
7131 int dot_seen;
7132 dot_seen = 0;
7133 switch(*cp2) {
7134 /* Fixme - for compatibility with the CRTL we should be removing */
7135 /* spaces from the file specifications, but this may show that */
7136 /* some tests that were appearing to pass are not really passing */
7137 case '%':
7138 cp2++;
7139 *(cp1++) = '?';
7140 break;
7141 case '^':
7142 /* Fix me hex expansions not implemented */
7143 cp2++; /* '^.' --> '.' and other. */
7144 if (*cp2) {
7145 if (*cp2 == '_') {
7146 cp2++;
7147 *(cp1++) = ' ';
7148 } else {
7149 *(cp1++) = *(cp2++);
7150 }
7151 }
7152 break;
7153 case ';':
7154 if (decc_filename_unix_no_version) {
7155 /* Easy, drop the version */
7156 while (*cp2)
7157 cp2++;
7158 break;
7159 } else {
7160 /* Punt - passing the version as a dot will probably */
7161 /* break perl in weird ways, but so did passing */
7162 /* through the ; as a version. Follow the CRTL and */
7163 /* hope for the best. */
7164 cp2++;
7165 *(cp1++) = '.';
7166 }
7167 break;
7168 case '.':
7169 if (dot_seen) {
7170 /* We will need to fix this properly later */
7171 /* As Perl may be installed on an ODS-5 volume, but not */
7172 /* have the EFS_CHARSET enabled, it still may encounter */
7173 /* filenames with extra dots in them, and a precedent got */
7174 /* set which allowed them to work, that we will uphold here */
7175 /* If extra dots are present in a name and no ^ is on them */
7176 /* VMS assumes that the first one is the extension delimiter */
7177 /* the rest have an implied ^. */
7178
7179 /* this is also a conflict as the . is also a version */
7180 /* delimiter in VMS, */
7181
7182 *(cp1++) = *(cp2++);
7183 break;
7184 }
7185 dot_seen = 1;
7186 /* This is an extension */
7187 if (decc_readdir_dropdotnotype) {
7188 cp2++;
7189 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7190 /* Drop the dot for the extension */
7191 break;
7192 } else {
7193 *(cp1++) = '.';
7194 }
7195 break;
7196 }
7197 default:
7198 *(cp1++) = *(cp2++);
7199 }
42cd432e 7200 }
a0d0e21e
LW
7201 *cp1 = '\0';
7202
f7ddb74a
JM
7203 /* This still leaves /000000/ when working with a
7204 * VMS device root or concealed root.
7205 */
7206 {
7207 int ulen;
7208 char * zeros;
7209
7210 ulen = strlen(rslt);
7211
7212 /* Get rid of "000000/ in rooted filespecs */
7213 if (ulen > 7) {
7214 zeros = strstr(rslt, "/000000/");
7215 if (zeros != NULL) {
7216 int mlen;
7217 mlen = ulen - (zeros - rslt) - 7;
7218 memmove(zeros, &zeros[7], mlen);
7219 ulen = ulen - 7;
7220 rslt[ulen] = '\0';
7221 }
7222 }
7223 }
7224
0e5ce2c7
JM
7225 if (vms_debug_fileify) {
7226 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7227 }
a0d0e21e
LW
7228 return rslt;
7229
0e5ce2c7
JM
7230} /* end of int_tounixspec() */
7231
7232
7233/*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7234static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7235{
7236 static char __tounixspec_retbuf[VMS_MAXRSS];
7237 char * unixspec, *ret_spec, *ret_buf;
7238
7239 unixspec = NULL;
7240 ret_buf = buf;
7241 if (ret_buf == NULL) {
7242 if (ts) {
7243 Newx(unixspec, VMS_MAXRSS, char);
7244 if (unixspec == NULL)
7245 _ckvmssts(SS$_INSFMEM);
7246 ret_buf = unixspec;
7247 } else {
7248 ret_buf = __tounixspec_retbuf;
7249 }
7250 }
7251
7252 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7253
7254 if (ret_spec == NULL) {
7255 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7256 if (unixspec)
7257 Safefree(unixspec);
7258 }
7259
7260 return ret_spec;
7261
a0d0e21e
LW
7262} /* end of do_tounixspec() */
7263/*}}}*/
7264/* External entry points */
360732b5
JM
7265char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7266 { return do_tounixspec(spec,buf,0, NULL); }
7267char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7268 { return do_tounixspec(spec,buf,1, NULL); }
7269char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7270 { return do_tounixspec(spec,buf,0, utf8_fl); }
7271char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7272 { return do_tounixspec(spec,buf,1, utf8_fl); }
a0d0e21e 7273
360732b5 7274#if __CRTL_VER >= 70200000 && !defined(__VAX)
2497a41f 7275
360732b5
JM
7276/*
7277 This procedure is used to identify if a path is based in either
7278 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7279 it returns the OpenVMS format directory for it.
7280
7281 It is expecting specifications of only '/' or '/xxxx/'
7282
7283 If a posix root does not exist, or 'xxxx' is not a directory
7284 in the posix root, it returns a failure.
7285
7286 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7287
7288 It is used only internally by posix_to_vmsspec_hardway().
7289 */
7290
7291static int posix_root_to_vms
7292 (char *vmspath, int vmspath_len,
7293 const char *unixpath,
d584a1c6
JM
7294 const int * utf8_fl)
7295{
2497a41f
JM
7296int sts;
7297struct FAB myfab = cc$rms_fab;
d584a1c6 7298rms_setup_nam(mynam);
2497a41f 7299struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
d584a1c6
JM
7300struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7301char * esa, * esal, * rsa, * rsal;
2497a41f
JM
7302int dir_flag;
7303int unixlen;
7304
360732b5 7305 dir_flag = 0;
d584a1c6 7306 vmspath[0] = '\0';
360732b5
JM
7307 unixlen = strlen(unixpath);
7308 if (unixlen == 0) {
360732b5
JM
7309 return RMS$_FNF;
7310 }
7311
7312#if __CRTL_VER >= 80200000
2497a41f 7313 /* If not a posix spec already, convert it */
360732b5
JM
7314 if (decc_posix_compliant_pathnames) {
7315 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7316 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7317 }
7318 else {
7319 /* This is already a VMS specification, no conversion */
7320 unixlen--;
a35dcc95 7321 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
360732b5 7322 }
2497a41f 7323 }
360732b5
JM
7324 else
7325#endif
7326 {
7327 int path_len;
7328 int i,j;
7329
7330 /* Check to see if this is under the POSIX root */
7331 if (decc_disable_posix_root) {
7332 return RMS$_FNF;
7333 }
7334
7335 /* Skip leading / */
7336 if (unixpath[0] == '/') {
7337 unixpath++;
7338 unixlen--;
7339 }
7340
7341
7342 strcpy(vmspath,"SYS$POSIX_ROOT:");
7343
7344 /* If this is only the / , or blank, then... */
7345 if (unixpath[0] == '\0') {
7346 /* by definition, this is the answer */
7347 return SS$_NORMAL;
7348 }
7349
7350 /* Need to look up a directory */
7351 vmspath[15] = '[';
7352 vmspath[16] = '\0';
7353
7354 /* Copy and add '^' escape characters as needed */
7355 j = 16;
7356 i = 0;
7357 while (unixpath[i] != 0) {
7358 int k;
7359
7360 j += copy_expand_unix_filename_escape
7361 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7362 i += k;
7363 }
7364
7365 path_len = strlen(vmspath);
7366 if (vmspath[path_len - 1] == '/')
7367 path_len--;
7368 vmspath[path_len] = ']';
7369 path_len++;
7370 vmspath[path_len] = '\0';
7371
2497a41f
JM
7372 }
7373 vmspath[vmspath_len] = 0;
7374 if (unixpath[unixlen - 1] == '/')
7375 dir_flag = 1;
c11536f5 7376 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
d584a1c6 7377 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 7378 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
c5375c28 7379 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 7380 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
d584a1c6 7381 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 7382 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
d584a1c6
JM
7383 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7384 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7385 rms_bind_fab_nam(myfab, mynam);
7386 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7387 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
2497a41f
JM
7388 if (decc_efs_case_preserve)
7389 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
ea0c9945 7390#ifdef NAML$M_OPEN_SPECIAL
2497a41f 7391 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
ea0c9945 7392#endif
2497a41f
JM
7393
7394 /* Set up the remaining naml fields */
7395 sts = sys$parse(&myfab);
7396
7397 /* It failed! Try again as a UNIX filespec */
7398 if (!(sts & 1)) {
d584a1c6 7399 PerlMem_free(esal);
367e4b85 7400 PerlMem_free(esa);
d584a1c6
JM
7401 PerlMem_free(rsal);
7402 PerlMem_free(rsa);
2497a41f
JM
7403 return sts;
7404 }
7405
7406 /* get the Device ID and the FID */
7407 sts = sys$search(&myfab);
d584a1c6
JM
7408
7409 /* These are no longer needed */
7410 PerlMem_free(esa);
7411 PerlMem_free(rsal);
7412 PerlMem_free(rsa);
7413
2497a41f
JM
7414 /* on any failure, returned the POSIX ^UP^ filespec */
7415 if (!(sts & 1)) {
d584a1c6 7416 PerlMem_free(esal);
2497a41f
JM
7417 return sts;
7418 }
7419 specdsc.dsc$a_pointer = vmspath;
7420 specdsc.dsc$w_length = vmspath_len;
7421
7422 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7423 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7424 sts = lib$fid_to_name
7425 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7426
7427 /* on any failure, returned the POSIX ^UP^ filespec */
7428 if (!(sts & 1)) {
7429 /* This can happen if user does not have permission to read directories */
7430 if (strncmp(unixpath,"\"^UP^",5) != 0)
7431 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7432 else
a35dcc95 7433 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
2497a41f
JM
7434 }
7435 else {
7436 vmspath[specdsc.dsc$w_length] = 0;
7437
7438 /* Are we expecting a directory? */
7439 if (dir_flag != 0) {
7440 int i;
7441 char *eptr;
7442
7443 eptr = NULL;
7444
7445 i = specdsc.dsc$w_length - 1;
7446 while (i > 0) {
7447 int zercnt;
7448 zercnt = 0;
7449 /* Version must be '1' */
7450 if (vmspath[i--] != '1')
7451 break;
7452 /* Version delimiter is one of ".;" */
7453 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7454 break;
7455 i--;
7456 if (vmspath[i--] != 'R')
7457 break;
7458 if (vmspath[i--] != 'I')
7459 break;
7460 if (vmspath[i--] != 'D')
7461 break;
7462 if (vmspath[i--] != '.')
7463 break;
7464 eptr = &vmspath[i+1];
7465 while (i > 0) {
7466 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7467 if (vmspath[i-1] != '^') {
7468 if (zercnt != 6) {
7469 *eptr = vmspath[i];
7470 eptr[1] = '\0';
7471 vmspath[i] = '.';
7472 break;
7473 }
7474 else {
7475 /* Get rid of 6 imaginary zero directory filename */
7476 vmspath[i+1] = '\0';
7477 }
7478 }
7479 }
7480 if (vmspath[i] == '0')
7481 zercnt++;
7482 else
7483 zercnt = 10;
7484 i--;
7485 }
7486 break;
7487 }
7488 }
7489 }
d584a1c6 7490 PerlMem_free(esal);
2497a41f
JM
7491 return sts;
7492}
7493
360732b5
JM
7494/* /dev/mumble needs to be handled special.
7495 /dev/null becomes NLA0:, And there is the potential for other stuff
7496 like /dev/tty which may need to be mapped to something.
7497*/
7498
7499static int
7500slash_dev_special_to_vms
7501 (const char * unixptr,
7502 char * vmspath,
7503 int vmspath_len)
7504{
7505char * nextslash;
7506int len;
7507int cmp;
360732b5
JM
7508
7509 unixptr += 4;
7510 nextslash = strchr(unixptr, '/');
7511 len = strlen(unixptr);
7512 if (nextslash != NULL)
7513 len = nextslash - unixptr;
7514 cmp = strncmp("null", unixptr, 5);
7515 if (cmp == 0) {
7516 if (vmspath_len >= 6) {
7517 strcpy(vmspath, "_NLA0:");
7518 return SS$_NORMAL;
7519 }
7520 }
c5193628 7521 return 0;
360732b5
JM
7522}
7523
7524
7525/* The built in routines do not understand perl's special needs, so
7526 doing a manual conversion from UNIX to VMS
7527
7528 If the utf8_fl is not null and points to a non-zero value, then
7529 treat 8 bit characters as UTF-8.
7530
7531 The sequence starting with '$(' and ending with ')' will be passed
7532 through with out interpretation instead of being escaped.
7533
7534 */
2497a41f 7535static int posix_to_vmsspec_hardway
360732b5
JM
7536 (char *vmspath, int vmspath_len,
7537 const char *unixpath,
7538 int dir_flag,
7539 int * utf8_fl) {
2497a41f
JM
7540
7541char *esa;
7542const char *unixptr;
360732b5 7543const char *unixend;
2497a41f
JM
7544char *vmsptr;
7545const char *lastslash;
7546const char *lastdot;
7547int unixlen;
7548int vmslen;
7549int dir_start;
7550int dir_dot;
7551int quoted;
360732b5
JM
7552char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7553int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
2497a41f 7554
360732b5
JM
7555 if (utf8_fl != NULL)
7556 *utf8_fl = 0;
2497a41f
JM
7557
7558 unixptr = unixpath;
7559 dir_dot = 0;
7560
7561 /* Ignore leading "/" characters */
7562 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7563 unixptr++;
7564 }
7565 unixlen = strlen(unixptr);
7566
7567 /* Do nothing with blank paths */
7568 if (unixlen == 0) {
7569 vmspath[0] = '\0';
7570 return SS$_NORMAL;
7571 }
7572
360732b5
JM
7573 quoted = 0;
7574 /* This could have a "^UP^ on the front */
7575 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7576 quoted = 1;
7577 unixptr+= 5;
7578 unixlen-= 5;
7579 }
7580
2497a41f
JM
7581 lastslash = strrchr(unixptr,'/');
7582 lastdot = strrchr(unixptr,'.');
360732b5
JM
7583 unixend = strrchr(unixptr,'\"');
7584 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7585 unixend = unixptr + unixlen;
7586 }
2497a41f
JM
7587
7588 /* last dot is last dot or past end of string */
7589 if (lastdot == NULL)
7590 lastdot = unixptr + unixlen;
7591
7592 /* if no directories, set last slash to beginning of string */
7593 if (lastslash == NULL) {
7594 lastslash = unixptr;
7595 }
7596 else {
7597 /* Watch out for trailing "." after last slash, still a directory */
7598 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7599 lastslash = unixptr + unixlen;
7600 }
7601
94ae10c0 7602 /* Watch out for trailing ".." after last slash, still a directory */
2497a41f
JM
7603 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7604 lastslash = unixptr + unixlen;
7605 }
7606
7607 /* dots in directories are aways escaped */
7608 if (lastdot < lastslash)
7609 lastdot = unixptr + unixlen;
7610 }
7611
7612 /* if (unixptr < lastslash) then we are in a directory */
7613
7614 dir_start = 0;
2497a41f
JM
7615
7616 vmsptr = vmspath;
7617 vmslen = 0;
7618
2497a41f
JM
7619 /* Start with the UNIX path */
7620 if (*unixptr != '/') {
7621 /* relative paths */
360732b5
JM
7622
7623 /* If allowing logical names on relative pathnames, then handle here */
7624 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7625 !decc_posix_compliant_pathnames) {
7626 char * nextslash;
7627 int seg_len;
7628 char * trn;
7629 int islnm;
7630
7631 /* Find the next slash */
7632 nextslash = strchr(unixptr,'/');
7633
c11536f5 7634 esa = (char *)PerlMem_malloc(vmspath_len);
360732b5
JM
7635 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7636
c11536f5 7637 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
360732b5
JM
7638 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7639
7640 if (nextslash != NULL) {
7641
7642 seg_len = nextslash - unixptr;
a35dcc95 7643 memcpy(esa, unixptr, seg_len);
360732b5
JM
7644 esa[seg_len] = 0;
7645 }
7646 else {
a35dcc95 7647 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
360732b5
JM
7648 }
7649 /* trnlnm(section) */
7650 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7651
7652 if (islnm) {
7653 /* Now fix up the directory */
7654
7655 /* Split up the path to find the components */
7656 sts = vms_split_path
7657 (trn,
7658 &v_spec,
7659 &v_len,
7660 &r_spec,
7661 &r_len,
7662 &d_spec,
7663 &d_len,
7664 &n_spec,
7665 &n_len,
7666 &e_spec,
7667 &e_len,
7668 &vs_spec,
7669 &vs_len);
7670
7671 while (sts == 0) {
360732b5
JM
7672 int cmp;
7673
7674 /* A logical name must be a directory or the full
7675 specification. It is only a full specification if
7676 it is the only component */
7677 if ((unixptr[seg_len] == '\0') ||
7678 (unixptr[seg_len+1] == '\0')) {
7679
7680 /* Is a directory being required? */
7681 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7682 /* Not a logical name */
7683 break;
7684 }
7685
7686
7687 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7688 /* This must be a directory */
7689 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
a35dcc95 7690 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
360732b5
JM
7691 vmsptr[vmslen] = ':';
7692 vmslen++;
7693 vmsptr[vmslen] = '\0';
7694 return SS$_NORMAL;
7695 }
7696 }
7697
7698 }
7699
7700
7701 /* must be dev/directory - ignore version */
7702 if ((n_len + e_len) != 0)
7703 break;
7704
7705 /* transfer the volume */
7706 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
a35dcc95 7707 memcpy(vmsptr, v_spec, v_len);
360732b5
JM
7708 vmsptr += v_len;
7709 vmsptr[0] = '\0';
7710 vmslen += v_len;
7711 }
7712
7713 /* unroot the rooted directory */
7714 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7715 r_spec[0] = '[';
7716 r_spec[r_len - 1] = ']';
7717
7718 /* This should not be there, but nothing is perfect */
7719 if (r_len > 9) {
7720 cmp = strcmp(&r_spec[1], "000000.");
7721 if (cmp == 0) {
7722 r_spec += 7;
7723 r_spec[7] = '[';
7724 r_len -= 7;
7725 if (r_len == 2)
7726 r_len = 0;
7727 }
7728 }
7729 if (r_len > 0) {
a35dcc95 7730 memcpy(vmsptr, r_spec, r_len);
360732b5
JM
7731 vmsptr += r_len;
7732 vmslen += r_len;
7733 vmsptr[0] = '\0';
7734 }
7735 }
7736 /* Bring over the directory. */
7737 if ((d_len > 0) &&
7738 ((d_len + vmslen) < vmspath_len)) {
7739 d_spec[0] = '[';
7740 d_spec[d_len - 1] = ']';
7741 if (d_len > 9) {
7742 cmp = strcmp(&d_spec[1], "000000.");
7743 if (cmp == 0) {
7744 d_spec += 7;
7745 d_spec[7] = '[';
7746 d_len -= 7;
7747 if (d_len == 2)
7748 d_len = 0;
7749 }
7750 }
7751
7752 if (r_len > 0) {
7753 /* Remove the redundant root */
7754 if (r_len > 0) {
7755 /* remove the ][ */
7756 vmsptr--;
7757 vmslen--;
7758 d_spec++;
7759 d_len--;
7760 }
a35dcc95 7761 memcpy(vmsptr, d_spec, d_len);
360732b5
JM
7762 vmsptr += d_len;
7763 vmslen += d_len;
7764 vmsptr[0] = '\0';
7765 }
7766 }
7767 break;
7768 }
7769 }
7770
7771 PerlMem_free(esa);
7772 PerlMem_free(trn);
7773 }
7774
2497a41f
JM
7775 if (lastslash > unixptr) {
7776 int dotdir_seen;
7777
7778 /* skip leading ./ */
7779 dotdir_seen = 0;
7780 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7781 dotdir_seen = 1;
7782 unixptr++;
7783 unixptr++;
7784 }
7785
7786 /* Are we still in a directory? */
7787 if (unixptr <= lastslash) {
7788 *vmsptr++ = '[';
7789 vmslen = 1;
7790 dir_start = 1;
7791
7792 /* if not backing up, then it is relative forward. */
7793 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
360732b5 7794 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
2497a41f
JM
7795 *vmsptr++ = '.';
7796 vmslen++;
7797 dir_dot = 1;
360732b5 7798 }
2497a41f
JM
7799 }
7800 else {
7801 if (dotdir_seen) {
7802 /* Perl wants an empty directory here to tell the difference
94ae10c0 7803 * between a DCL command and a filename
2497a41f
JM
7804 */
7805 *vmsptr++ = '[';
7806 *vmsptr++ = ']';
7807 vmslen = 2;
7808 }
7809 }
7810 }
7811 else {
7812 /* Handle two special files . and .. */
7813 if (unixptr[0] == '.') {
360732b5 7814 if (&unixptr[1] == unixend) {
2497a41f
JM
7815 *vmsptr++ = '[';
7816 *vmsptr++ = ']';
7817 vmslen += 2;
7818 *vmsptr++ = '\0';
7819 return SS$_NORMAL;
7820 }
360732b5 7821 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
2497a41f
JM
7822 *vmsptr++ = '[';
7823 *vmsptr++ = '-';
7824 *vmsptr++ = ']';
7825 vmslen += 3;
7826 *vmsptr++ = '\0';
7827 return SS$_NORMAL;
7828 }
7829 }
7830 }
7831 }
7832 else { /* Absolute PATH handling */
7833 int sts;
7834 char * nextslash;
7835 int seg_len;
7836 /* Need to find out where root is */
7837
7838 /* In theory, this procedure should never get an absolute POSIX pathname
7839 * that can not be found on the POSIX root.
7840 * In practice, that can not be relied on, and things will show up
7841 * here that are a VMS device name or concealed logical name instead.
7842 * So to make things work, this procedure must be tolerant.
7843 */
c11536f5 7844 esa = (char *)PerlMem_malloc(vmspath_len);
c5375c28 7845 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2497a41f
JM
7846
7847 sts = SS$_NORMAL;
7848 nextslash = strchr(&unixptr[1],'/');
7849 seg_len = 0;
7850 if (nextslash != NULL) {
db4c2905 7851 int cmp;
2497a41f 7852 seg_len = nextslash - &unixptr[1];
db4c2905 7853 my_strlcpy(vmspath, unixptr, seg_len + 2);
360732b5
JM
7854 cmp = 1;
7855 if (seg_len == 3) {
7856 cmp = strncmp(vmspath, "dev", 4);
7857 if (cmp == 0) {
7858 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
07bee079 7859 if (sts == SS$_NORMAL)
360732b5
JM
7860 return SS$_NORMAL;
7861 }
7862 }
7863 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
2497a41f
JM
7864 }
7865
360732b5 7866 if ($VMS_STATUS_SUCCESS(sts)) {
2497a41f
JM
7867 /* This is verified to be a real path */
7868
360732b5
JM
7869 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7870 if ($VMS_STATUS_SUCCESS(sts)) {
a35dcc95 7871 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
360732b5
JM
7872 vmsptr = vmspath + vmslen;
7873 unixptr++;
7874 if (unixptr < lastslash) {
7875 char * rptr;
7876 vmsptr--;
7877 *vmsptr++ = '.';
7878 dir_start = 1;
7879 dir_dot = 1;
7880 if (vmslen > 7) {
7881 int cmp;
7882 rptr = vmsptr - 7;
7883 cmp = strcmp(rptr,"000000.");
7884 if (cmp == 0) {
7885 vmslen -= 7;
7886 vmsptr -= 7;
7887 vmsptr[1] = '\0';
7888 } /* removing 6 zeros */
7889 } /* vmslen < 7, no 6 zeros possible */
7890 } /* Not in a directory */
7891 } /* Posix root found */
7892 else {
7893 /* No posix root, fall back to default directory */
7894 strcpy(vmspath, "SYS$DISK:[");
7895 vmsptr = &vmspath[10];
7896 vmslen = 10;
7897 if (unixptr > lastslash) {
7898 *vmsptr = ']';
7899 vmsptr++;
7900 vmslen++;
7901 }
7902 else {
7903 dir_start = 1;
7904 }
7905 }
2497a41f
JM
7906 } /* end of verified real path handling */
7907 else {
7908 int add_6zero;
7909 int islnm;
7910
7911 /* Ok, we have a device or a concealed root that is not in POSIX
7912 * or we have garbage. Make the best of it.
7913 */
7914
7915 /* Posix to VMS destroyed this, so copy it again */
db4c2905
CB
7916 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7917 vmslen = strlen(vmspath); /* We know we're truncating. */
2497a41f
JM
7918 vmsptr = &vmsptr[vmslen];
7919 islnm = 0;
7920
7921 /* Now do we need to add the fake 6 zero directory to it? */
7922 add_6zero = 1;
7923 if ((*lastslash == '/') && (nextslash < lastslash)) {
7924 /* No there is another directory */
7925 add_6zero = 0;
7926 }
7927 else {
7928 int trnend;
360732b5 7929 int cmp;
2497a41f
JM
7930
7931 /* now we have foo:bar or foo:[000000]bar to decide from */
7ded3206 7932 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
360732b5
JM
7933
7934 if (!islnm && !decc_posix_compliant_pathnames) {
7935
7936 cmp = strncmp("bin", vmspath, 4);
7937 if (cmp == 0) {
7938 /* bin => SYS$SYSTEM: */
7939 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7940 }
7941 else {
7942 /* tmp => SYS$SCRATCH: */
7943 cmp = strncmp("tmp", vmspath, 4);
7944 if (cmp == 0) {
7945 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7946 }
7947 }
7948 }
7949
7ded3206 7950 trnend = islnm ? islnm - 1 : 0;
2497a41f
JM
7951
7952 /* if this was a logical name, ']' or '>' must be present */
7953 /* if not a logical name, then assume a device and hope. */
7954 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7955
7956 /* if log name and trailing '.' then rooted - treat as device */
7957 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7958
7959 /* Fix me, if not a logical name, a device lookup should be
7960 * done to see if the device is file structured. If the device
7961 * is not file structured, the 6 zeros should not be put on.
7962 *
7963 * As it is, perl is occasionally looking for dev:[000000]tty.
7964 * which looks a little strange.
360732b5
JM
7965 *
7966 * Not that easy to detect as "/dev" may be file structured with
7967 * special device files.
2497a41f
JM
7968 */
7969
30e68285 7970 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
360732b5 7971 (&nextslash[1] == unixend)) {
2497a41f
JM
7972 /* No real directory present */
7973 add_6zero = 1;
7974 }
7975 }
7976
7977 /* Put the device delimiter on */
7978 *vmsptr++ = ':';
7979 vmslen++;
7980 unixptr = nextslash;
7981 unixptr++;
7982
7983 /* Start directory if needed */
7984 if (!islnm || add_6zero) {
7985 *vmsptr++ = '[';
7986 vmslen++;
7987 dir_start = 1;
7988 }
7989
7990 /* add fake 000000] if needed */
7991 if (add_6zero) {
7992 *vmsptr++ = '0';
7993 *vmsptr++ = '0';
7994 *vmsptr++ = '0';
7995 *vmsptr++ = '0';
7996 *vmsptr++ = '0';
7997 *vmsptr++ = '0';
7998 *vmsptr++ = ']';
7999 vmslen += 7;
8000 dir_start = 0;
8001 }
8002
8003 } /* non-POSIX translation */
367e4b85 8004 PerlMem_free(esa);
2497a41f
JM
8005 } /* End of relative/absolute path handling */
8006
360732b5 8007 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
2497a41f 8008 int dash_flag;
360732b5
JM
8009 int in_cnt;
8010 int out_cnt;
2497a41f
JM
8011
8012 dash_flag = 0;
8013
8014 if (dir_start != 0) {
8015
8016 /* First characters in a directory are handled special */
8017 while ((*unixptr == '/') ||
8018 ((*unixptr == '.') &&
360732b5
JM
8019 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8020 (&unixptr[1]==unixend)))) {
2497a41f
JM
8021 int loop_flag;
8022
8023 loop_flag = 0;
8024
8025 /* Skip redundant / in specification */
8026 while ((*unixptr == '/') && (dir_start != 0)) {
8027 loop_flag = 1;
8028 unixptr++;
8029 if (unixptr == lastslash)
8030 break;
8031 }
8032 if (unixptr == lastslash)
8033 break;
8034
8035 /* Skip redundant ./ characters */
8036 while ((*unixptr == '.') &&
360732b5 8037 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
2497a41f
JM
8038 loop_flag = 1;
8039 unixptr++;
8040 if (unixptr == lastslash)
8041 break;
8042 if (*unixptr == '/')
8043 unixptr++;
8044 }
8045 if (unixptr == lastslash)
8046 break;
8047
8048 /* Skip redundant ../ characters */
8049 while ((*unixptr == '.') && (unixptr[1] == '.') &&
360732b5 8050 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
2497a41f
JM
8051 /* Set the backing up flag */
8052 loop_flag = 1;
8053 dir_dot = 0;
8054 dash_flag = 1;
8055 *vmsptr++ = '-';
8056 vmslen++;
8057 unixptr++; /* first . */
8058 unixptr++; /* second . */
8059 if (unixptr == lastslash)
8060 break;
8061 if (*unixptr == '/') /* The slash */
8062 unixptr++;
8063 }
8064 if (unixptr == lastslash)
8065 break;
8066
8067 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8068 /* Not needed when VMS is pretending to be UNIX. */
8069
8070 /* Is this loop stuck because of too many dots? */
8071 if (loop_flag == 0) {
8072 /* Exit the loop and pass the rest through */
8073 break;
8074 }
8075 }
8076
8077 /* Are we done with directories yet? */
8078 if (unixptr >= lastslash) {
8079
8080 /* Watch out for trailing dots */
8081 if (dir_dot != 0) {
8082 vmslen --;
8083 vmsptr--;
8084 }
8085 *vmsptr++ = ']';
8086 vmslen++;
8087 dash_flag = 0;
8088 dir_start = 0;
8089 if (*unixptr == '/')
8090 unixptr++;
8091 }
8092 else {
8093 /* Have we stopped backing up? */
8094 if (dash_flag) {
8095 *vmsptr++ = '.';
8096 vmslen++;
8097 dash_flag = 0;
8098 /* dir_start continues to be = 1 */
8099 }
8100 if (*unixptr == '-') {
8101 *vmsptr++ = '^';
8102 *vmsptr++ = *unixptr++;
8103 vmslen += 2;
8104 dir_start = 0;
8105
8106 /* Now are we done with directories yet? */
8107 if (unixptr >= lastslash) {
8108
8109 /* Watch out for trailing dots */
8110 if (dir_dot != 0) {
8111 vmslen --;
8112 vmsptr--;
8113 }
8114
8115 *vmsptr++ = ']';
8116 vmslen++;
8117 dash_flag = 0;
8118 dir_start = 0;
8119 }
8120 }
8121 }
8122 }
8123
8124 /* All done? */
360732b5 8125 if (unixptr >= unixend)
2497a41f
JM
8126 break;
8127
8128 /* Normal characters - More EFS work probably needed */
8129 dir_start = 0;
8130 dir_dot = 0;
8131
8132 switch(*unixptr) {
8133 case '/':
8134 /* remove multiple / */
8135 while (unixptr[1] == '/') {
8136 unixptr++;
8137 }
8138 if (unixptr == lastslash) {
8139 /* Watch out for trailing dots */
8140 if (dir_dot != 0) {
8141 vmslen --;
8142 vmsptr--;
8143 }
8144 *vmsptr++ = ']';
8145 }
8146 else {
8147 dir_start = 1;
8148 *vmsptr++ = '.';
8149 dir_dot = 1;
8150
8151 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8152 /* Not needed when VMS is pretending to be UNIX. */
8153
8154 }
8155 dash_flag = 0;
360732b5 8156 if (unixptr != unixend)
2497a41f
JM
8157 unixptr++;
8158 vmslen++;
8159 break;
2497a41f 8160 case '.':
360732b5
JM
8161 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8162 (&unixptr[1] == unixend)) {
2497a41f
JM
8163 *vmsptr++ = '^';
8164 *vmsptr++ = '.';
8165 vmslen += 2;
8166 unixptr++;
8167
8168 /* trailing dot ==> '^..' on VMS */
360732b5 8169 if (unixptr == unixend) {
2497a41f
JM
8170 *vmsptr++ = '.';
8171 vmslen++;
360732b5 8172 unixptr++;
2497a41f 8173 }
2497a41f
JM
8174 break;
8175 }
360732b5 8176
2497a41f 8177 *vmsptr++ = *unixptr++;
360732b5
JM
8178 vmslen ++;
8179 break;
8180 case '"':
8181 if (quoted && (&unixptr[1] == unixend)) {
8182 unixptr++;
8183 break;
8184 }
8185 in_cnt = copy_expand_unix_filename_escape
8186 (vmsptr, unixptr, &out_cnt, utf8_fl);
8187 vmsptr += out_cnt;
8188 unixptr += in_cnt;
2497a41f
JM
8189 break;
8190 case '~':
8191 case ';':
8192 case '\\':
360732b5
JM
8193 case '?':
8194 case ' ':
2497a41f 8195 default:
360732b5
JM
8196 in_cnt = copy_expand_unix_filename_escape
8197 (vmsptr, unixptr, &out_cnt, utf8_fl);
8198 vmsptr += out_cnt;
8199 unixptr += in_cnt;
2497a41f
JM
8200 break;
8201 }
8202 }
8203
8204 /* Make sure directory is closed */
8205 if (unixptr == lastslash) {
8206 char *vmsptr2;
8207 vmsptr2 = vmsptr - 1;
8208
8209 if (*vmsptr2 != ']') {
8210 *vmsptr2--;
8211
8212 /* directories do not end in a dot bracket */
8213 if (*vmsptr2 == '.') {
8214 vmsptr2--;
8215
8216 /* ^. is allowed */
8217 if (*vmsptr2 != '^') {
8218 vmsptr--; /* back up over the dot */
8219 }
8220 }
8221 *vmsptr++ = ']';
8222 }
8223 }
8224 else {
8225 char *vmsptr2;
8226 /* Add a trailing dot if a file with no extension */
8227 vmsptr2 = vmsptr - 1;
360732b5
JM
8228 if ((vmslen > 1) &&
8229 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
30e68285 8230 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
2497a41f
JM
8231 *vmsptr++ = '.';
8232 vmslen++;
8233 }
8234 }
8235
8236 *vmsptr = '\0';
8237 return SS$_NORMAL;
8238}
8239#endif
8240
360732b5
JM
8241 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8242static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8243{
8244char * result;
8245int utf8_flag;
8246
8247 /* If a UTF8 flag is being passed, honor it */
8248 utf8_flag = 0;
8249 if (utf8_fl != NULL) {
8250 utf8_flag = *utf8_fl;
8251 *utf8_fl = 0;
8252 }
8253
8254 if (utf8_flag) {
8255 /* If there is a possibility of UTF8, then if any UTF8 characters
8256 are present, then they must be converted to VTF-7
8257 */
8258 result = strcpy(rslt, path); /* FIX-ME */
8259 }
8260 else
8261 result = strcpy(rslt, path);
8262
8263 return result;
8264}
8265
8266
df278665 8267
360732b5 8268/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
df278665
JM
8269static char *int_tovmsspec
8270 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8271 char *dirend;
f7ddb74a 8272 char *lastdot;
eb578fdb 8273 char *cp1;
b8ffc8df 8274 const char *cp2;
e518068a 8275 unsigned long int infront = 0, hasdir = 1;
f7ddb74a
JM
8276 int rslt_len;
8277 int no_type_seen;
360732b5
JM
8278 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8279 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e 8280
df278665
JM
8281 if (vms_debug_fileify) {
8282 if (path == NULL)
8283 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8284 else
8285 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8286 }
8287
8288 if (path == NULL) {
8289 /* If we fail, we should be setting errno */
8290 set_errno(EINVAL);
8291 set_vaxc_errno(SS$_BADPARAM);
8292 return NULL;
8293 }
4d743a9b 8294 rslt_len = VMS_MAXRSS-1;
360732b5
JM
8295
8296 /* '.' and '..' are "[]" and "[-]" for a quick check */
8297 if (path[0] == '.') {
8298 if (path[1] == '\0') {
8299 strcpy(rslt,"[]");
8300 if (utf8_flag != NULL)
8301 *utf8_flag = 0;
8302 return rslt;
8303 }
8304 else {
8305 if (path[1] == '.' && path[2] == '\0') {
8306 strcpy(rslt,"[-]");
8307 if (utf8_flag != NULL)
8308 *utf8_flag = 0;
8309 return rslt;
8310 }
8311 }
a0d0e21e 8312 }
f7ddb74a 8313
2497a41f
JM
8314 /* Posix specifications are now a native VMS format */
8315 /*--------------------------------------------------*/
8316#if __CRTL_VER >= 80200000 && !defined(__VAX)
8317 if (decc_posix_compliant_pathnames) {
8318 if (strncmp(path,"\"^UP^",5) == 0) {
360732b5 8319 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
2497a41f
JM
8320 return rslt;
8321 }
8322 }
8323#endif
8324
360732b5
JM
8325 /* This is really the only way to see if this is already in VMS format */
8326 sts = vms_split_path
8327 (path,
8328 &v_spec,
8329 &v_len,
8330 &r_spec,
8331 &r_len,
8332 &d_spec,
8333 &d_len,
8334 &n_spec,
8335 &n_len,
8336 &e_spec,
8337 &e_len,
8338 &vs_spec,
8339 &vs_len);
8340 if (sts == 0) {
8341 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8342 replacement, because the above parse just took care of most of
8343 what is needed to do vmspath when the specification is already
8344 in VMS format.
8345
8346 And if it is not already, it is easier to do the conversion as
8347 part of this routine than to call this routine and then work on
8348 the result.
8349 */
2497a41f 8350
360732b5
JM
8351 /* If VMS punctuation was found, it is already VMS format */
8352 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8353 if (utf8_flag != NULL)
8354 *utf8_flag = 0;
a35dcc95 8355 my_strlcpy(rslt, path, VMS_MAXRSS);
df278665
JM
8356 if (vms_debug_fileify) {
8357 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8358 }
360732b5
JM
8359 return rslt;
8360 }
8361 /* Now, what to do with trailing "." cases where there is no
8362 extension? If this is a UNIX specification, and EFS characters
8363 are enabled, then the trailing "." should be converted to a "^.".
8364 But if this was already a VMS specification, then it should be
8365 left alone.
2497a41f 8366
360732b5
JM
8367 So in the case of ambiguity, leave the specification alone.
8368 */
2497a41f 8369
2497a41f 8370
360732b5
JM
8371 /* If there is a possibility of UTF8, then if any UTF8 characters
8372 are present, then they must be converted to VTF-7
8373 */
8374 if (utf8_flag != NULL)
8375 *utf8_flag = 0;
a35dcc95 8376 my_strlcpy(rslt, path, VMS_MAXRSS);
df278665
JM
8377 if (vms_debug_fileify) {
8378 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8379 }
2497a41f
JM
8380 return rslt;
8381 }
8382
360732b5
JM
8383 dirend = strrchr(path,'/');
8384
8385 if (dirend == NULL) {
df278665
JM
8386 char *macro_start;
8387 int has_macro;
8388
360732b5
JM
8389 /* If we get here with no UNIX directory delimiters, then this is
8390 not a complete file specification, either garbage a UNIX glob
8391 specification that can not be converted to a VMS wildcard, or
df278665
JM
8392 it a UNIX shell macro. MakeMaker wants shell macros passed
8393 through AS-IS,
360732b5
JM
8394
8395 utf8 flag setting needs to be preserved.
8396 */
df278665
JM
8397 hasdir = 0;
8398
8399 has_macro = 0;
8400 macro_start = strchr(path,'$');
8401 if (macro_start != NULL) {
8402 if (macro_start[1] == '(') {
8403 has_macro = 1;
8404 }
8405 }
8406 if ((decc_efs_charset == 0) || (has_macro)) {
a35dcc95 8407 my_strlcpy(rslt, path, VMS_MAXRSS);
df278665
JM
8408 if (vms_debug_fileify) {
8409 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8410 }
8411 return rslt;
8412 }
360732b5 8413 }
e645f6f8 8414 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
748a9306
LW
8415 if (!*(dirend+2)) dirend +=2;
8416 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
360732b5
JM
8417 if (decc_efs_charset == 0) {
8418 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8419 }
748a9306 8420 }
f7ddb74a 8421
a0d0e21e
LW
8422 cp1 = rslt;
8423 cp2 = path;
f7ddb74a 8424 lastdot = strrchr(cp2,'.');
a0d0e21e 8425 if (*cp2 == '/') {
a480973c 8426 char *trndev;
e518068a 8427 int islnm, rooted;
8428 STRLEN trnend;
8429
b7ae7a0d 8430 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
61bb5906 8431 if (!*(cp2+1)) {
f7ddb74a
JM
8432 if (decc_disable_posix_root) {
8433 strcpy(rslt,"sys$disk:[000000]");
8434 }
8435 else {
8436 strcpy(rslt,"sys$posix_root:[000000]");
8437 }
360732b5
JM
8438 if (utf8_flag != NULL)
8439 *utf8_flag = 0;
df278665
JM
8440 if (vms_debug_fileify) {
8441 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8442 }
61bb5906
CB
8443 return rslt;
8444 }
a0d0e21e 8445 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
e518068a 8446 *cp1 = '\0';
c11536f5 8447 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 8448 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
b8486b9d 8449 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8450
8451 /* DECC special handling */
8452 if (!islnm) {
8453 if (strcmp(rslt,"bin") == 0) {
8454 strcpy(rslt,"sys$system");
8455 cp1 = rslt + 10;
8456 *cp1 = 0;
b8486b9d 8457 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8458 }
8459 else if (strcmp(rslt,"tmp") == 0) {
8460 strcpy(rslt,"sys$scratch");
8461 cp1 = rslt + 11;
8462 *cp1 = 0;
b8486b9d 8463 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8464 }
8465 else if (!decc_disable_posix_root) {
8466 strcpy(rslt, "sys$posix_root");
b8486b9d 8467 cp1 = rslt + 14;
f7ddb74a
JM
8468 *cp1 = 0;
8469 cp2 = path;
8470 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
b8486b9d 8471 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8472 }
8473 else if (strcmp(rslt,"dev") == 0) {
8474 if (strncmp(cp2,"/null", 5) == 0) {
8475 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8476 strcpy(rslt,"NLA0");
8477 cp1 = rslt + 4;
8478 *cp1 = 0;
8479 cp2 = cp2 + 5;
b8486b9d 8480 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
f7ddb74a
JM
8481 }
8482 }
8483 }
8484 }
8485
e518068a 8486 trnend = islnm ? strlen(trndev) - 1 : 0;
8487 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8488 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8489 /* If the first element of the path is a logical name, determine
8490 * whether it has to be translated so we can add more directories. */
8491 if (!islnm || rooted) {
8492 *(cp1++) = ':';
8493 *(cp1++) = '[';
8494 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8495 else cp2++;
8496 }
8497 else {
8498 if (cp2 != dirend) {
a35dcc95 8499 my_strlcpy(rslt, trndev, VMS_MAXRSS);
e518068a 8500 cp1 = rslt + trnend;
755b3d5d
JM
8501 if (*cp2 != 0) {
8502 *(cp1++) = '.';
8503 cp2++;
8504 }
e518068a 8505 }
8506 else {
f7ddb74a
JM
8507 if (decc_disable_posix_root) {
8508 *(cp1++) = ':';
8509 hasdir = 0;
8510 }
e518068a 8511 }
8512 }
367e4b85 8513 PerlMem_free(trndev);
748a9306 8514 }
a0d0e21e
LW
8515 else {
8516 *(cp1++) = '[';
748a9306
LW
8517 if (*cp2 == '.') {
8518 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8519 cp2 += 2; /* skip over "./" - it's redundant */
8520 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8521 }
8522 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8523 *(cp1++) = '-'; /* "../" --> "-" */
8524 cp2 += 3;
8525 }
f86702cc 8526 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8527 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8528 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8529 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8530 cp2 += 4;
8531 }
f7ddb74a
JM
8532 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8533 /* Escape the extra dots in EFS file specifications */
8534 *(cp1++) = '^';
8535 }
748a9306
LW
8536 if (cp2 > dirend) cp2 = dirend;
8537 }
8538 else *(cp1++) = '.';
8539 }
8540 for (; cp2 < dirend; cp2++) {
8541 if (*cp2 == '/') {
01b8edb6 8542 if (*(cp2-1) == '/') continue;
748a9306
LW
8543 if (*(cp1-1) != '.') *(cp1++) = '.';
8544 infront = 0;
8545 }
8546 else if (!infront && *cp2 == '.') {
01b8edb6 8547 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8548 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
fd7385b9
CB
8549 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8550 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
748a9306 8551 else if (*(cp1-2) == '[') *(cp1-1) = '-';
4ab1eb56
CB
8552 else {
8553 *(cp1++) = '-';
748a9306
LW
8554 }
8555 cp2 += 2;
01b8edb6 8556 if (cp2 == dirend) break;
748a9306 8557 }
f86702cc 8558 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8559 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8560 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8561 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8562 if (!*(cp2+3)) {
8563 *(cp1++) = '.'; /* Simulate trailing '/' */
8564 cp2 += 2; /* for loop will incr this to == dirend */
8565 }
8566 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8567 }
f7ddb74a
JM
8568 else {
8569 if (decc_efs_charset == 0)
8570 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8571 else {
8572 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8573 *(cp1++) = '.';
8574 }
8575 }
748a9306
LW
8576 }
8577 else {
e518068a 8578 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
f7ddb74a
JM
8579 if (*cp2 == '.') {
8580 if (decc_efs_charset == 0)
8581 *(cp1++) = '_';
8582 else {
8583 *(cp1++) = '^';
8584 *(cp1++) = '.';
8585 }
8586 }
748a9306
LW
8587 else *(cp1++) = *cp2;
8588 infront = 1;
8589 }
a0d0e21e 8590 }
748a9306 8591 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
e518068a 8592 if (hasdir) *(cp1++) = ']';
748a9306 8593 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
f7ddb74a
JM
8594 /* fixme for ODS5 */
8595 no_type_seen = 0;
8596 if (cp2 > lastdot)
8597 no_type_seen = 1;
8598 while (*cp2) {
8599 switch(*cp2) {
8600 case '?':
360732b5
JM
8601 if (decc_efs_charset == 0)
8602 *(cp1++) = '%';
8603 else
8604 *(cp1++) = '?';
f7ddb74a
JM
8605 cp2++;
8606 case ' ':
8607 *(cp1)++ = '^';
8608 *(cp1)++ = '_';
8609 cp2++;
8610 break;
8611 case '.':
8612 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8613 decc_readdir_dropdotnotype) {
8614 *(cp1)++ = '^';
8615 *(cp1)++ = '.';
8616 cp2++;
8617
8618 /* trailing dot ==> '^..' on VMS */
8619 if (*cp2 == '\0') {
8620 *(cp1++) = '.';
8621 no_type_seen = 0;
8622 }
8623 }
8624 else {
8625 *(cp1++) = *(cp2++);
8626 no_type_seen = 0;
8627 }
8628 break;
360732b5
JM
8629 case '$':
8630 /* This could be a macro to be passed through */
8631 *(cp1++) = *(cp2++);
8632 if (*cp2 == '(') {
8633 const char * save_cp2;
8634 char * save_cp1;
8635 int is_macro;
8636
8637 /* paranoid check */
8638 save_cp2 = cp2;
8639 save_cp1 = cp1;
8640 is_macro = 0;
8641
8642 /* Test through */
8643 *(cp1++) = *(cp2++);
8644 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8645 *(cp1++) = *(cp2++);
8646 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8647 *(cp1++) = *(cp2++);
8648 }
8649 if (*cp2 == ')') {
8650 *(cp1++) = *(cp2++);
8651 is_macro = 1;
8652 }
8653 }
8654 if (is_macro == 0) {
8655 /* Not really a macro - never mind */
8656 cp2 = save_cp2;
8657 cp1 = save_cp1;
8658 }
8659 }
8660 break;
f7ddb74a
JM
8661 case '\"':
8662 case '~':
8663 case '`':
8664 case '!':
8665 case '#':
8666 case '%':
8667 case '^':
adc11f0b
CB
8668 /* Don't escape again if following character is
8669 * already something we escape.
8670 */
8671 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8672 *(cp1++) = *(cp2++);
8673 break;
8674 }
8675 /* But otherwise fall through and escape it. */
f7ddb74a
JM
8676 case '&':
8677 case '(':
8678 case ')':
8679 case '=':
8680 case '+':
8681 case '\'':
8682 case '@':
8683 case '[':
8684 case ']':
8685 case '{':
8686 case '}':
8687 case ':':
8688 case '\\':
8689 case '|':
8690 case '<':
8691 case '>':
8692 *(cp1++) = '^';
8693 *(cp1++) = *(cp2++);
8694 break;
8695 case ';':
8696 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
657054d4 8697 * which is wrong. UNIX notation should be ".dir." unless
f7ddb74a
JM
8698 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8699 * changing this behavior could break more things at this time.
2497a41f
JM
8700 * efs character set effectively does not allow "." to be a version
8701 * delimiter as a further complication about changing this.
f7ddb74a
JM
8702 */
8703 if (decc_filename_unix_report != 0) {
8704 *(cp1++) = '^';
8705 }
8706 *(cp1++) = *(cp2++);
8707 break;
8708 default:
8709 *(cp1++) = *(cp2++);
8710 }
8711 }
8712 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8713 char *lcp1;
8714 lcp1 = cp1;
8715 lcp1--;
8716 /* Fix me for "^]", but that requires making sure that you do
8717 * not back up past the start of the filename
8718 */
8719 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8720 *cp1++ = '.';
8721 }
a0d0e21e
LW
8722 *cp1 = '\0';
8723
360732b5
JM
8724 if (utf8_flag != NULL)
8725 *utf8_flag = 0;
df278665
JM
8726 if (vms_debug_fileify) {
8727 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8728 }
a0d0e21e
LW
8729 return rslt;
8730
df278665
JM
8731} /* end of int_tovmsspec() */
8732
8733
8734/*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8735static char *mp_do_tovmsspec
8736 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8737 static char __tovmsspec_retbuf[VMS_MAXRSS];
8738 char * vmsspec, *ret_spec, *ret_buf;
8739
8740 vmsspec = NULL;
8741 ret_buf = buf;
8742 if (ret_buf == NULL) {
8743 if (ts) {
8744 Newx(vmsspec, VMS_MAXRSS, char);
8745 if (vmsspec == NULL)
8746 _ckvmssts(SS$_INSFMEM);
8747 ret_buf = vmsspec;
8748 } else {
8749 ret_buf = __tovmsspec_retbuf;
8750 }
8751 }
8752
8753 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8754
8755 if (ret_spec == NULL) {
8756 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8757 if (vmsspec)
8758 Safefree(vmsspec);
8759 }
8760
8761 return ret_spec;
8762
8763} /* end of mp_do_tovmsspec() */
a0d0e21e
LW
8764/*}}}*/
8765/* External entry points */
360732b5
JM
8766char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8767 { return do_tovmsspec(path,buf,0,NULL); }
8768char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8769 { return do_tovmsspec(path,buf,1,NULL); }
8770char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8771 { return do_tovmsspec(path,buf,0,utf8_fl); }
8772char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8773 { return do_tovmsspec(path,buf,1,utf8_fl); }
8774
4846f1d7 8775/*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
94ae10c0 8776/* Internal routine for use with out an explicit context present */
4846f1d7
JM
8777static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8778
8779 char * ret_spec, *pathified;
8780
8781 if (path == NULL)
8782 return NULL;
8783
c11536f5 8784 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
4846f1d7
JM
8785 if (pathified == NULL)
8786 _ckvmssts_noperl(SS$_INSFMEM);
8787
8788 ret_spec = int_pathify_dirspec(path, pathified);
8789
8790 if (ret_spec == NULL) {
8791 PerlMem_free(pathified);
8792 return NULL;
8793 }
8794
8795 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8796
8797 PerlMem_free(pathified);
8798 return ret_spec;
8799
8800}
8801
360732b5
JM
8802/*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8803static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 8804 static char __tovmspath_retbuf[VMS_MAXRSS];
a0d0e21e 8805 int vmslen;
a480973c 8806 char *pathified, *vmsified, *cp;
a0d0e21e 8807
748a9306 8808 if (path == NULL) return NULL;
c11536f5 8809 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
c5375c28 8810 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 8811 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 8812 PerlMem_free(pathified);
a480973c
JM
8813 return NULL;
8814 }
c5375c28
JM
8815
8816 vmsified = NULL;
8817 if (buf == NULL)
8818 Newx(vmsified, VMS_MAXRSS, char);
360732b5 8819 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
c5375c28
JM
8820 PerlMem_free(pathified);
8821 if (vmsified) Safefree(vmsified);
a480973c
JM
8822 return NULL;
8823 }
c5375c28 8824 PerlMem_free(pathified);
a480973c 8825 if (buf) {
a480973c
JM
8826 return buf;
8827 }
a0d0e21e
LW
8828 else if (ts) {
8829 vmslen = strlen(vmsified);
a02a5408 8830 Newx(cp,vmslen+1,char);
a0d0e21e
LW
8831 memcpy(cp,vmsified,vmslen);
8832 cp[vmslen] = '\0';
a480973c 8833 Safefree(vmsified);
a0d0e21e
LW
8834 return cp;
8835 }
8836 else {
a35dcc95 8837 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
a480973c 8838 Safefree(vmsified);
a0d0e21e
LW
8839 return __tovmspath_retbuf;
8840 }
8841
8842} /* end of do_tovmspath() */
8843/*}}}*/
8844/* External entry points */
360732b5
JM
8845char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8846 { return do_tovmspath(path,buf,0, NULL); }
8847char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8848 { return do_tovmspath(path,buf,1, NULL); }
8849char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8850 { return do_tovmspath(path,buf,0,utf8_fl); }
8851char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8852 { return do_tovmspath(path,buf,1,utf8_fl); }
8853
8854
8855/*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8856static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
a480973c 8857 static char __tounixpath_retbuf[VMS_MAXRSS];
a0d0e21e 8858 int unixlen;
a480973c 8859 char *pathified, *unixified, *cp;
a0d0e21e 8860
748a9306 8861 if (path == NULL) return NULL;
c11536f5 8862 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
c5375c28 8863 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
1fe570cc 8864 if (int_pathify_dirspec(path, pathified) == NULL) {
c5375c28 8865 PerlMem_free(pathified);
a480973c
JM
8866 return NULL;
8867 }
c5375c28
JM
8868
8869 unixified = NULL;
8870 if (buf == NULL) {
8871 Newx(unixified, VMS_MAXRSS, char);
8872 }
360732b5 8873 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
c5375c28
JM
8874 PerlMem_free(pathified);
8875 if (unixified) Safefree(unixified);
a480973c
JM
8876 return NULL;
8877 }
c5375c28 8878 PerlMem_free(pathified);
a480973c 8879 if (buf) {
a480973c
JM
8880 return buf;
8881 }
a0d0e21e
LW
8882 else if (ts) {
8883 unixlen = strlen(unixified);
a02a5408 8884 Newx(cp,unixlen+1,char);
a0d0e21e
LW
8885 memcpy(cp,unixified,unixlen);
8886 cp[unixlen] = '\0';
a480973c 8887 Safefree(unixified);
a0d0e21e
LW
8888 return cp;
8889 }
8890 else {
a35dcc95 8891 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
a480973c 8892 Safefree(unixified);
a0d0e21e
LW
8893 return __tounixpath_retbuf;
8894 }
8895
8896} /* end of do_tounixpath() */
8897/*}}}*/
8898/* External entry points */
360732b5
JM
8899char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8900 { return do_tounixpath(path,buf,0,NULL); }
8901char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8902 { return do_tounixpath(path,buf,1,NULL); }
8903char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8904 { return do_tounixpath(path,buf,0,utf8_fl); }
8905char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8906 { return do_tounixpath(path,buf,1,utf8_fl); }
a0d0e21e
LW
8907
8908/*
cbb8049c 8909 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
8910 *
8911 *****************************************************************************
8912 * *
cbb8049c 8913 * Copyright (C) 1989-1994, 2007 by *
a0d0e21e
LW
8914 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8915 * *
cbb8049c
MP
8916 * Permission is hereby granted for the reproduction of this software *
8917 * on condition that this copyright notice is included in source *
8918 * distributions of the software. The code may be modified and *
8919 * distributed under the same terms as Perl itself. *
a0d0e21e
LW
8920 * *
8921 * 27-Aug-1994 Modified for inclusion in perl5 *
cbb8049c 8922 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
a0d0e21e
LW
8923 *****************************************************************************
8924 */
8925
8926/*
8927 * getredirection() is intended to aid in porting C programs
8928 * to VMS (Vax-11 C). The native VMS environment does not support
8929 * '>' and '<' I/O redirection, or command line wild card expansion,
8930 * or a command line pipe mechanism using the '|' AND background
8931 * command execution '&'. All of these capabilities are provided to any
8932 * C program which calls this procedure as the first thing in the
8933 * main program.
8934 * The piping mechanism will probably work with almost any 'filter' type
8935 * of program. With suitable modification, it may useful for other
8936 * portability problems as well.
8937 *
cbb8049c 8938 * Author: Mark Pizzolato (mark AT infocomm DOT com)
a0d0e21e
LW
8939 */
8940struct list_item
8941 {
8942 struct list_item *next;
8943 char *value;
8944 };
8945
8946static void add_item(struct list_item **head,
8947 struct list_item **tail,
8948 char *value,
8949 int *count);
8950
4b19af01
CB
8951static void mp_expand_wild_cards(pTHX_ char *item,
8952 struct list_item **head,
8953 struct list_item **tail,
8954 int *count);
a0d0e21e 8955
8df869cb 8956static int background_process(pTHX_ int argc, char **argv);
a0d0e21e 8957
fd8cd3a3 8958static void pipe_and_fork(pTHX_ char **cmargv);
a0d0e21e
LW
8959
8960/*{{{ void getredirection(int *ac, char ***av)*/
84902520 8961static void
4b19af01 8962mp_getredirection(pTHX_ int *ac, char ***av)
a0d0e21e
LW
8963/*
8964 * Process vms redirection arg's. Exit if any error is seen.
8965 * If getredirection() processes an argument, it is erased
8966 * from the vector. getredirection() returns a new argc and argv value.
8967 * In the event that a background command is requested (by a trailing "&"),
8968 * this routine creates a background subprocess, and simply exits the program.
8969 *
8970 * Warning: do not try to simplify the code for vms. The code
8971 * presupposes that getredirection() is called before any data is
8972 * read from stdin or written to stdout.
8973 *
8974 * Normal usage is as follows:
8975 *
8976 * main(argc, argv)
8977 * int argc;
8978 * char *argv[];
8979 * {
8980 * getredirection(&argc, &argv);
8981 * }
8982 */
8983{
8984 int argc = *ac; /* Argument Count */
8985 char **argv = *av; /* Argument Vector */
8986 char *ap; /* Argument pointer */
8987 int j; /* argv[] index */
8988 int item_count = 0; /* Count of Items in List */
8989 struct list_item *list_head = 0; /* First Item in List */
8990 struct list_item *list_tail; /* Last Item in List */
8991 char *in = NULL; /* Input File Name */
8992 char *out = NULL; /* Output File Name */
8993 char *outmode = "w"; /* Mode to Open Output File */
8994 char *err = NULL; /* Error File Name */
8995 char *errmode = "w"; /* Mode to Open Error File */
8996 int cmargc = 0; /* Piped Command Arg Count */
8997 char **cmargv = NULL;/* Piped Command Arg Vector */
a0d0e21e
LW
8998
8999 /*
9000 * First handle the case where the last thing on the line ends with
9001 * a '&'. This indicates the desire for the command to be run in a
9002 * subprocess, so we satisfy that desire.
9003 */
9004 ap = argv[argc-1];
9005 if (0 == strcmp("&", ap))
8c3eed29 9006 exit(background_process(aTHX_ --argc, argv));
e518068a 9007 if (*ap && '&' == ap[strlen(ap)-1])
a0d0e21e
LW
9008 {
9009 ap[strlen(ap)-1] = '\0';
8c3eed29 9010 exit(background_process(aTHX_ argc, argv));
a0d0e21e
LW
9011 }
9012 /*
9013 * Now we handle the general redirection cases that involve '>', '>>',
9014 * '<', and pipes '|'.
9015 */
9016 for (j = 0; j < argc; ++j)
9017 {
9018 if (0 == strcmp("<", argv[j]))
9019 {
9020 if (j+1 >= argc)
9021 {
fd71b04b 9022 fprintf(stderr,"No input file after < on command line");
748a9306 9023 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9024 }
9025 in = argv[++j];
9026 continue;
9027 }
9028 if ('<' == *(ap = argv[j]))
9029 {
9030 in = 1 + ap;
9031 continue;
9032 }
9033 if (0 == strcmp(">", ap))
9034 {
9035 if (j+1 >= argc)
9036 {
fd71b04b 9037 fprintf(stderr,"No output file after > on command line");
748a9306 9038 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9039 }
9040 out = argv[++j];
9041 continue;
9042 }
9043 if ('>' == *ap)
9044 {
9045 if ('>' == ap[1])
9046 {
9047 outmode = "a";
9048 if ('\0' == ap[2])
9049 out = argv[++j];
9050 else
9051 out = 2 + ap;
9052 }
9053 else
9054 out = 1 + ap;
9055 if (j >= argc)
9056 {
fd71b04b 9057 fprintf(stderr,"No output file after > or >> on command line");
748a9306 9058 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9059 }
9060 continue;
9061 }
9062 if (('2' == *ap) && ('>' == ap[1]))
9063 {
9064 if ('>' == ap[2])
9065 {
9066 errmode = "a";
9067 if ('\0' == ap[3])
9068 err = argv[++j];
9069 else
9070 err = 3 + ap;
9071 }
9072 else
9073 if ('\0' == ap[2])
9074 err = argv[++j];
9075 else
748a9306 9076 err = 2 + ap;
a0d0e21e
LW
9077 if (j >= argc)
9078 {
fd71b04b 9079 fprintf(stderr,"No output file after 2> or 2>> on command line");
748a9306 9080 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9081 }
9082 continue;
9083 }
9084 if (0 == strcmp("|", argv[j]))
9085 {
9086 if (j+1 >= argc)
9087 {
fd71b04b 9088 fprintf(stderr,"No command into which to pipe on command line");
748a9306 9089 exit(LIB$_WRONUMARG);
a0d0e21e
LW
9090 }
9091 cmargc = argc-(j+1);
9092 cmargv = &argv[j+1];
9093 argc = j;
9094 continue;
9095 }
9096 if ('|' == *(ap = argv[j]))
9097 {
9098 ++argv[j];
9099 cmargc = argc-j;
9100 cmargv = &argv[j];
9101 argc = j;
9102 continue;
9103 }
9104 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9105 }
9106 /*
9107 * Allocate and fill in the new argument vector, Some Unix's terminate
9108 * the list with an extra null pointer.
9109 */
e0ef6b43 9110 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
c5375c28 9111 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9112 *av = argv;
9113 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9114 argv[j] = list_head->value;
9115 *ac = item_count;
9116 if (cmargv != NULL)
9117 {
9118 if (out != NULL)
9119 {
fd71b04b 9120 fprintf(stderr,"'|' and '>' may not both be specified on command line");
748a9306 9121 exit(LIB$_INVARGORD);
a0d0e21e 9122 }
fd8cd3a3 9123 pipe_and_fork(aTHX_ cmargv);
a0d0e21e
LW
9124 }
9125
9126 /* Check for input from a pipe (mailbox) */
9127
a5f75d66 9128 if (in == NULL && 1 == isapipe(0))
a0d0e21e
LW
9129 {
9130 char mbxname[L_tmpnam];
9131 long int bufsize;
9132 long int dvi_item = DVI$_DEVBUFSIZ;
9133 $DESCRIPTOR(mbxnam, "");
9134 $DESCRIPTOR(mbxdevnam, "");
9135
9136 /* Input from a pipe, reopen it in binary mode to disable */
9137 /* carriage control processing. */
9138
bf8d1304 9139 fgetname(stdin, mbxname, 1);
a0d0e21e
LW
9140 mbxnam.dsc$a_pointer = mbxname;
9141 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9142 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9143 mbxdevnam.dsc$a_pointer = mbxname;
9144 mbxdevnam.dsc$w_length = sizeof(mbxname);
9145 dvi_item = DVI$_DEVNAM;
9146 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9147 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
748a9306
LW
9148 set_errno(0);
9149 set_vaxc_errno(1);
a0d0e21e
LW
9150 freopen(mbxname, "rb", stdin);
9151 if (errno != 0)
9152 {
fd71b04b 9153 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
748a9306 9154 exit(vaxc$errno);
a0d0e21e
LW
9155 }
9156 }
9157 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9158 {
fd71b04b 9159 fprintf(stderr,"Can't open input file %s as stdin",in);
748a9306 9160 exit(vaxc$errno);
a0d0e21e
LW
9161 }
9162 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9163 {
fd71b04b 9164 fprintf(stderr,"Can't open output file %s as stdout",out);
748a9306 9165 exit(vaxc$errno);
a0d0e21e 9166 }
fd8cd3a3 9167 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
0e06870b 9168
748a9306 9169 if (err != NULL) {
71d7ec5d 9170 if (strcmp(err,"&1") == 0) {
a15cef0c 9171 dup2(fileno(stdout), fileno(stderr));
fd8cd3a3 9172 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
71d7ec5d 9173 } else {
748a9306
LW
9174 FILE *tmperr;
9175 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9176 {
fd71b04b 9177 fprintf(stderr,"Can't open error file %s as stderr",err);
748a9306
LW
9178 exit(vaxc$errno);
9179 }
9180 fclose(tmperr);
a15cef0c 9181 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
748a9306
LW
9182 {
9183 exit(vaxc$errno);
9184 }
fd8cd3a3 9185 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
a0d0e21e 9186 }
71d7ec5d 9187 }
a0d0e21e 9188#ifdef ARGPROC_DEBUG
740ce14c 9189 PerlIO_printf(Perl_debug_log, "Arglist:\n");
a0d0e21e 9190 for (j = 0; j < *ac; ++j)
740ce14c 9191 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
a0d0e21e 9192#endif
b7ae7a0d 9193 /* Clear errors we may have hit expanding wildcards, so they don't
9194 show up in Perl's $! later */
9195 set_errno(0); set_vaxc_errno(1);
a0d0e21e
LW
9196} /* end of getredirection() */
9197/*}}}*/
9198
9199static void add_item(struct list_item **head,
9200 struct list_item **tail,
9201 char *value,
9202 int *count)
9203{
9204 if (*head == 0)
9205 {
e0ef6b43 9206 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9207 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9208 *tail = *head;
9209 }
9210 else {
e0ef6b43 9211 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
c5375c28 9212 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e
LW
9213 *tail = (*tail)->next;
9214 }
9215 (*tail)->value = value;
9216 ++(*count);
9217}
9218
4b19af01 9219static void mp_expand_wild_cards(pTHX_ char *item,
a0d0e21e
LW
9220 struct list_item **head,
9221 struct list_item **tail,
9222 int *count)
9223{
9224int expcount = 0;
748a9306 9225unsigned long int context = 0;
a0d0e21e 9226int isunix = 0;
773da73d 9227int item_len = 0;
a0d0e21e
LW
9228char *had_version;
9229char *had_device;
9230int had_directory;
f675dbe5 9231char *devdir,*cp;
a480973c 9232char *vmsspec;
a0d0e21e 9233$DESCRIPTOR(filespec, "");
748a9306 9234$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
a0d0e21e 9235$DESCRIPTOR(resultspec, "");
a480973c
JM
9236unsigned long int lff_flags = 0;
9237int sts;
dca5a913 9238int rms_sts;
a480973c
JM
9239
9240#ifdef VMS_LONGNAME_SUPPORT
9241 lff_flags = LIB$M_FIL_LONG_NAMES;
9242#endif
a0d0e21e 9243
f675dbe5
CB
9244 for (cp = item; *cp; cp++) {
9245 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9246 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9247 }
9248 if (!*cp || isspace(*cp))
a0d0e21e
LW
9249 {
9250 add_item(head, tail, item, count);
9251 return;
9252 }
773da73d
JH
9253 else
9254 {
9255 /* "double quoted" wild card expressions pass as is */
9256 /* From DCL that means using e.g.: */
9257 /* perl program """perl.*""" */
9258 item_len = strlen(item);
9259 if ( '"' == *item && '"' == item[item_len-1] )
9260 {
9261 item++;
9262 item[item_len-2] = '\0';
9263 add_item(head, tail, item, count);
9264 return;
9265 }
9266 }
a0d0e21e
LW
9267 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9268 resultspec.dsc$b_class = DSC$K_CLASS_D;
9269 resultspec.dsc$a_pointer = NULL;
c11536f5 9270 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
c5375c28 9271 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
748a9306 9272 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
df278665 9273 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
a0d0e21e
LW
9274 if (!isunix || !filespec.dsc$a_pointer)
9275 filespec.dsc$a_pointer = item;
9276 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9277 /*
9278 * Only return version specs, if the caller specified a version
9279 */
9280 had_version = strchr(item, ';');
9281 /*
94ae10c0 9282 * Only return device and directory specs, if the caller specified either.
a0d0e21e
LW
9283 */
9284 had_device = strchr(item, ':');
9285 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9286
a480973c
JM
9287 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9288 (&filespec, &resultspec, &context,
dca5a913 9289 &defaultspec, 0, &rms_sts, &lff_flags)))
a0d0e21e
LW
9290 {
9291 char *string;
9292 char *c;
9293
c11536f5 9294 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
c5375c28 9295 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
db4c2905 9296 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
a0d0e21e 9297 if (NULL == had_version)
f7ddb74a 9298 *(strrchr(string, ';')) = '\0';
a0d0e21e
LW
9299 if ((!had_directory) && (had_device == NULL))
9300 {
9301 if (NULL == (devdir = strrchr(string, ']')))
9302 devdir = strrchr(string, '>');
db4c2905 9303 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
a0d0e21e
LW
9304 }
9305 /*
9306 * Be consistent with what the C RTL has already done to the rest of
9307 * the argv items and lowercase all of these names.
9308 */
f7ddb74a
JM
9309 if (!decc_efs_case_preserve) {
9310 for (c = string; *c; ++c)
a0d0e21e
LW
9311 if (isupper(*c))
9312 *c = tolower(*c);
f7ddb74a 9313 }
f86702cc 9314 if (isunix) trim_unixpath(string,item,1);
a0d0e21e
LW
9315 add_item(head, tail, string, count);
9316 ++expcount;
a480973c 9317 }
367e4b85 9318 PerlMem_free(vmsspec);
c07a80fd 9319 if (sts != RMS$_NMF)
9320 {
9321 set_vaxc_errno(sts);
9322 switch (sts)
9323 {
f282b18d 9324 case RMS$_FNF: case RMS$_DNF:
c07a80fd 9325 set_errno(ENOENT); break;
f282b18d
CB
9326 case RMS$_DIR:
9327 set_errno(ENOTDIR); break;
c07a80fd 9328 case RMS$_DEV:
9329 set_errno(ENODEV); break;
f282b18d 9330 case RMS$_FNM: case RMS$_SYN:
c07a80fd 9331 set_errno(EINVAL); break;
9332 case RMS$_PRV:
9333 set_errno(EACCES); break;
9334 default:
b7ae7a0d 9335 _ckvmssts_noperl(sts);
c07a80fd 9336 }
9337 }
a0d0e21e
LW
9338 if (expcount == 0)
9339 add_item(head, tail, item, count);
b7ae7a0d 9340 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9341 _ckvmssts_noperl(lib$find_file_end(&context));
a0d0e21e
LW
9342}
9343
9344static int child_st[2];/* Event Flag set when child process completes */
9345
748a9306 9346static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
a0d0e21e 9347
f7c699a0 9348static unsigned long int exit_handler(void)
a0d0e21e
LW
9349{
9350short iosb[4];
9351
9352 if (0 == child_st[0])
9353 {
9354#ifdef ARGPROC_DEBUG
740ce14c 9355 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
a0d0e21e
LW
9356#endif
9357 fflush(stdout); /* Have to flush pipe for binary data to */
9358 /* terminate properly -- <tp@mccall.com> */
9359 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9360 sys$dassgn(child_chan);
9361 fclose(stdout);
9362 sys$synch(0, child_st);
9363 }
9364 return(1);
9365}
9366
9367static void sig_child(int chan)
9368{
9369#ifdef ARGPROC_DEBUG
740ce14c 9370 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
a0d0e21e
LW
9371#endif
9372 if (child_st[0] == 0)
9373 child_st[0] = 1;
9374}
9375
748a9306 9376static struct exit_control_block exit_block =
a0d0e21e
LW
9377 {
9378 0,
9379 exit_handler,
9380 1,
9381 &exit_block.exit_status,
9382 0
9383 };
9384
ff7adb52
CL
9385static void
9386pipe_and_fork(pTHX_ char **cmargv)
a0d0e21e 9387{
ff7adb52 9388 PerlIO *fp;
218fdd94 9389 struct dsc$descriptor_s *vmscmd;
ff7adb52
CL
9390 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9391 int sts, j, l, ismcr, quote, tquote = 0;
9392
218fdd94
CL
9393 sts = setup_cmddsc(aTHX_ cmargv[0],0,&quote,&vmscmd);
9394 vms_execfree(vmscmd);
ff7adb52
CL
9395
9396 j = l = 0;
9397 p = subcmd;
9398 q = cmargv[0];
9399 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9400 && toupper(*(q+2)) == 'R' && !*(q+3);
9401
9402 while (q && l < MAX_DCL_LINE_LENGTH) {
9403 if (!*q) {
9404 if (j > 0 && quote) {
9405 *p++ = '"';
9406 l++;
9407 }
9408 q = cmargv[++j];
9409 if (q) {
9410 if (ismcr && j > 1) quote = 1;
9411 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9412 *p++ = ' ';
9413 l++;
9414 if (quote || tquote) {
9415 *p++ = '"';
9416 l++;
9417 }
988c775c 9418 }
ff7adb52
CL
9419 } else {
9420 if ((quote||tquote) && *q == '"') {
9421 *p++ = '"';
9422 l++;
988c775c 9423 }
ff7adb52
CL
9424 *p++ = *q++;
9425 l++;
9426 }
9427 }
9428 *p = '\0';
a0d0e21e 9429
218fdd94 9430 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
4e205ed6 9431 if (fp == NULL) {
ff7adb52 9432 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
988c775c 9433 }
a0d0e21e
LW
9434}
9435
8df869cb 9436static int background_process(pTHX_ int argc, char **argv)
a0d0e21e 9437{
a480973c 9438char command[MAX_DCL_SYMBOL + 1] = "$";
a0d0e21e
LW
9439$DESCRIPTOR(value, "");
9440static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9441static $DESCRIPTOR(null, "NLA0:");
9442static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9443char pidstring[80];
9444$DESCRIPTOR(pidstr, "");
9445int pid;
748a9306 9446unsigned long int flags = 17, one = 1, retsts;
a480973c 9447int len;
a0d0e21e 9448
a35dcc95 9449 len = my_strlcat(command, argv[0], sizeof(command));
a480973c 9450 while (--argc && (len < MAX_DCL_SYMBOL))
a0d0e21e 9451 {
a35dcc95
CB
9452 my_strlcat(command, " \"", sizeof(command));
9453 my_strlcat(command, *(++argv), sizeof(command));
9454 len = my_strlcat(command, "\"", sizeof(command));
a0d0e21e
LW
9455 }
9456 value.dsc$a_pointer = command;
9457 value.dsc$w_length = strlen(value.dsc$a_pointer);
b7ae7a0d 9458 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
748a9306
LW
9459 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9460 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
b7ae7a0d 9461 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
748a9306
LW
9462 }
9463 else {
b7ae7a0d 9464 _ckvmssts_noperl(retsts);
748a9306 9465 }
a0d0e21e 9466#ifdef ARGPROC_DEBUG
740ce14c 9467 PerlIO_printf(Perl_debug_log, "%s\n", command);
a0d0e21e
LW
9468#endif
9469 sprintf(pidstring, "%08X", pid);
740ce14c 9470 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
a0d0e21e
LW
9471 pidstr.dsc$a_pointer = pidstring;
9472 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9473 lib$set_symbol(&pidsymbol, &pidstr);
9474 return(SS$_NORMAL);
9475}
9476/*}}}*/
9477/***** End of code taken from Mark Pizzolato's argproc.c package *****/
9478
84902520
TB
9479
9480/* OS-specific initialization at image activation (not thread startup) */
61bb5906
CB
9481/* Older VAXC header files lack these constants */
9482#ifndef JPI$_RIGHTS_SIZE
9483# define JPI$_RIGHTS_SIZE 817
9484#endif
9485#ifndef KGB$M_SUBSYSTEM
9486# define KGB$M_SUBSYSTEM 0x8
9487#endif
a480973c 9488
e0ef6b43
CB
9489/* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9490
84902520
TB
9491/*{{{void vms_image_init(int *, char ***)*/
9492void
9493vms_image_init(int *argcp, char ***argvp)
9494{
b53f3677 9495 int status;
f675dbe5
CB
9496 char eqv[LNM$C_NAMLENGTH+1] = "";
9497 unsigned int len, tabct = 8, tabidx = 0;
9498 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
61bb5906
CB
9499 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9500 unsigned short int dummy, rlen;
f675dbe5 9501 struct dsc$descriptor_s **tabvec;
fd8cd3a3
DS
9502#if defined(PERL_IMPLICIT_CONTEXT)
9503 pTHX = NULL;
9504#endif
61bb5906
CB
9505 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9506 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9507 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9508 { 0, 0, 0, 0} };
84902520 9509
2e34cc90 9510#ifdef KILL_BY_SIGPRC
f7ddb74a 9511 Perl_csighandler_init();
2e34cc90
CL
9512#endif
9513
778e045f 9514#if __CRTL_VER >= 70300000 && !defined(__VAX)
b53f3677
JM
9515 /* This was moved from the pre-image init handler because on threaded */
9516 /* Perl it was always returning 0 for the default value. */
98c7875d 9517 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
b53f3677
JM
9518 if (status > 0) {
9519 int s;
9520 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9521 if (s > 0) {
9522 int initial;
9523 initial = decc$feature_get_value(s, 4);
98c7875d
CB
9524 if (initial > 0) {
9525 /* initial is: 0 if nothing has set the feature */
9526 /* -1 if initialized to default */
9527 /* 1 if set by logical name */
9528 /* 2 if set by decc$feature_set_value */
b53f3677
JM
9529 decc_disable_posix_root = decc$feature_get_value(s, 1);
9530
9531 /* If the value is not valid, force the feature off */
9532 if (decc_disable_posix_root < 0) {
9533 decc$feature_set_value(s, 1, 1);
9534 decc_disable_posix_root = 1;
9535 }
9536 }
9537 else {
98c7875d 9538 /* Nothing has asked for it explicitly, so use our own default. */
b53f3677
JM
9539 decc_disable_posix_root = 1;
9540 decc$feature_set_value(s, 1, 1);
9541 }
9542 }
9543 }
778e045f 9544#endif
b53f3677 9545
fd8cd3a3
DS
9546 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9547 _ckvmssts_noperl(iosb[0]);
61bb5906
CB
9548 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9549 if (iprv[i]) { /* Running image installed with privs? */
fd8cd3a3 9550 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
f675dbe5 9551 will_taint = TRUE;
84902520
TB
9552 break;
9553 }
9554 }
61bb5906 9555 /* Rights identifiers might trigger tainting as well. */
f675dbe5 9556 if (!will_taint && (rlen || rsz)) {
61bb5906
CB
9557 while (rlen < rsz) {
9558 /* We didn't get all the identifiers on the first pass. Allocate a
9559 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9560 * were needed to hold all identifiers at time of last call; we'll
9561 * allocate that many unsigned long ints), and go back and get 'em.
22d4bb9c
CB
9562 * If it gave us less than it wanted to despite ample buffer space,
9563 * something's broken. Is your system missing a system identifier?
61bb5906 9564 */
22d4bb9c
CB
9565 if (rsz <= jpilist[1].buflen) {
9566 /* Perl_croak accvios when used this early in startup. */
9567 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9568 rsz, (unsigned long) jpilist[1].buflen,
9569 "Check your rights database for corruption.\n");
9570 exit(SS$_ABORT);
9571 }
e0ef6b43
CB
9572 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9573 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
c5375c28 9574 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
61bb5906 9575 jpilist[1].buflen = rsz * sizeof(unsigned long int);
fd8cd3a3
DS
9576 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9577 _ckvmssts_noperl(iosb[0]);
61bb5906 9578 }
c11536f5 9579 mask = (unsigned long int *)jpilist[1].bufadr;
61bb5906
CB
9580 /* Check attribute flags for each identifier (2nd longword); protected
9581 * subsystem identifiers trigger tainting.
9582 */
9583 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9584 if (mask[i] & KGB$M_SUBSYSTEM) {
f675dbe5 9585 will_taint = TRUE;
61bb5906
CB
9586 break;
9587 }
9588 }
367e4b85 9589 if (mask != rlst) PerlMem_free(mask);
61bb5906 9590 }
f7ddb74a
JM
9591
9592 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9593 * logical, some versions of the CRTL will add a phanthom /000000/
9594 * directory. This needs to be removed.
9595 */
9596 if (decc_filename_unix_report) {
9597 char * zeros;
9598 int ulen;
9599 ulen = strlen(argvp[0][0]);
9600 if (ulen > 7) {
9601 zeros = strstr(argvp[0][0], "/000000/");
9602 if (zeros != NULL) {
9603 int mlen;
9604 mlen = ulen - (zeros - argvp[0][0]) - 7;
9605 memmove(zeros, &zeros[7], mlen);
9606 ulen = ulen - 7;
9607 argvp[0][0][ulen] = '\0';
9608 }
9609 }
9610 /* It also may have a trailing dot that needs to be removed otherwise
9611 * it will be converted to VMS mode incorrectly.
9612 */
9613 ulen--;
9614 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9615 argvp[0][0][ulen] = '\0';
9616 }
9617
61bb5906 9618 /* We need to use this hack to tell Perl it should run with tainting,
6b88bc9c 9619 * since its tainting flag may be part of the PL_curinterp struct, which
61bb5906
CB
9620 * hasn't been allocated when vms_image_init() is called.
9621 */
f675dbe5 9622 if (will_taint) {
ec618cdf
CB
9623 char **newargv, **oldargv;
9624 oldargv = *argvp;
e0ef6b43 9625 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
c5375c28 9626 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf 9627 newargv[0] = oldargv[0];
c11536f5 9628 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
c5375c28 9629 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
ec618cdf
CB
9630 strcpy(newargv[1], "-T");
9631 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9632 (*argcp)++;
9633 newargv[*argcp] = NULL;
61bb5906
CB
9634 /* We orphan the old argv, since we don't know where it's come from,
9635 * so we don't know how to free it.
9636 */
ec618cdf 9637 *argvp = newargv;
61bb5906 9638 }
f675dbe5
CB
9639 else { /* Did user explicitly request tainting? */
9640 int i;
9641 char *cp, **av = *argvp;
9642 for (i = 1; i < *argcp; i++) {
9643 if (*av[i] != '-') break;
9644 for (cp = av[i]+1; *cp; cp++) {
9645 if (*cp == 'T') { will_taint = 1; break; }
9646 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9647 strchr("DFIiMmx",*cp)) break;
9648 }
9649 if (will_taint) break;
9650 }
9651 }
9652
9653 for (tabidx = 0;
9654 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9655 tabidx++) {
c5375c28
JM
9656 if (!tabidx) {
9657 tabvec = (struct dsc$descriptor_s **)
9658 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9659 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9660 }
f675dbe5
CB
9661 else if (tabidx >= tabct) {
9662 tabct += 8;
e0ef6b43 9663 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
c5375c28 9664 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5 9665 }
e0ef6b43 9666 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
c5375c28 9667 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
f675dbe5
CB
9668 tabvec[tabidx]->dsc$w_length = 0;
9669 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9670 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9671 tabvec[tabidx]->dsc$a_pointer = NULL;
fd8cd3a3 9672 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
f675dbe5
CB
9673 }
9674 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9675
84902520 9676 getredirection(argcp,argvp);
3bc25146
CB
9677#if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9678 {
9679# include <reentrancy.h>
f7ddb74a 9680 decc$set_reentrancy(C$C_MULTITHREAD);
3bc25146
CB
9681 }
9682#endif
84902520
TB
9683 return;
9684}
9685/*}}}*/
9686
9687
a0d0e21e
LW
9688/* trim_unixpath()
9689 * Trim Unix-style prefix off filespec, so it looks like what a shell
9690 * glob expansion would return (i.e. from specified prefix on, not
9691 * full path). Note that returned filespec is Unix-style, regardless
9692 * of whether input filespec was VMS-style or Unix-style.
9693 *
a3e9d8c9 9694 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
f86702cc 9695 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9696 * vector of options; at present, only bit 0 is used, and if set tells
9697 * trim unixpath to try the current default directory as a prefix when
9698 * presented with a possibly ambiguous ... wildcard.
a3e9d8c9 9699 *
9700 * Returns !=0 on success, with trimmed filespec replacing contents of
9701 * fspec, and 0 on failure, with contents of fpsec unchanged.
a0d0e21e 9702 */
f86702cc 9703/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
a0d0e21e 9704int
2fbb330f 9705Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
a0d0e21e 9706{
c11536f5 9707 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
eb578fdb 9708 int tmplen, reslen = 0, dirs = 0;
a0d0e21e 9709
a3e9d8c9 9710 if (!wildspec || !fspec) return 0;
ebd4d70b 9711
c11536f5 9712 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9713 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 9714 tplate = unixwild;
a3e9d8c9 9715 if (strpbrk(wildspec,"]>:") != NULL) {
0e5ce2c7 9716 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
367e4b85 9717 PerlMem_free(unixwild);
a480973c
JM
9718 return 0;
9719 }
a3e9d8c9 9720 }
2fbb330f 9721 else {
a35dcc95 9722 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
2fbb330f 9723 }
c11536f5 9724 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9725 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a0d0e21e 9726 if (strpbrk(fspec,"]>:") != NULL) {
0e5ce2c7 9727 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
367e4b85
JM
9728 PerlMem_free(unixwild);
9729 PerlMem_free(unixified);
a480973c
JM
9730 return 0;
9731 }
a0d0e21e 9732 else base = unixified;
a3e9d8c9 9733 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9734 * check to see that final result fits into (isn't longer than) fspec */
9735 reslen = strlen(fspec);
a0d0e21e
LW
9736 }
9737 else base = fspec;
a3e9d8c9 9738
9739 /* No prefix or absolute path on wildcard, so nothing to remove */
c11536f5 9740 if (!*tplate || *tplate == '/') {
367e4b85 9741 PerlMem_free(unixwild);
a480973c 9742 if (base == fspec) {
367e4b85 9743 PerlMem_free(unixified);
a480973c
JM
9744 return 1;
9745 }
a3e9d8c9 9746 tmplen = strlen(unixified);
a480973c 9747 if (tmplen > reslen) {
367e4b85 9748 PerlMem_free(unixified);
a480973c
JM
9749 return 0; /* not enough space */
9750 }
a3e9d8c9 9751 /* Copy unixified resultant, including trailing NUL */
9752 memmove(fspec,unixified,tmplen+1);
367e4b85 9753 PerlMem_free(unixified);
a3e9d8c9 9754 return 1;
9755 }
a0d0e21e 9756
f86702cc 9757 for (end = base; *end; end++) ; /* Find end of resultant filespec */
c11536f5
CB
9758 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9759 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
f86702cc 9760 for (cp1 = end ;cp1 >= base; cp1--)
9761 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9762 { cp1++; break; }
9763 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
367e4b85
JM
9764 PerlMem_free(unixified);
9765 PerlMem_free(unixwild);
a3e9d8c9 9766 return 1;
9767 }
f86702cc 9768 else {
a480973c 9769 char *tpl, *lcres;
f86702cc 9770 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9771 int ells = 1, totells, segdirs, match;
a480973c 9772 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
f86702cc 9773 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9774
9775 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9776 totells = ells;
9777 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
c11536f5 9778 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9779 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 9780 if (ellipsis == tplate && opts & 1) {
f86702cc 9781 /* Template begins with an ellipsis. Since we can't tell how many
9782 * directory names at the front of the resultant to keep for an
9783 * arbitrary starting point, we arbitrarily choose the current
9784 * default directory as a starting point. If it's there as a prefix,
9785 * clip it off. If not, fall through and act as if the leading
9786 * ellipsis weren't there (i.e. return shortest possible path that
9787 * could match template).
9788 */
a480973c 9789 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
367e4b85
JM
9790 PerlMem_free(tpl);
9791 PerlMem_free(unixified);
9792 PerlMem_free(unixwild);
a480973c
JM
9793 return 0;
9794 }
f7ddb74a
JM
9795 if (!decc_efs_case_preserve) {
9796 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9797 if (_tolower(*cp1) != _tolower(*cp2)) break;
9798 }
f86702cc 9799 segdirs = dirs - totells; /* Min # of dirs we must have left */
9800 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9801 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
18a3d61e 9802 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
9803 PerlMem_free(tpl);
9804 PerlMem_free(unixified);
9805 PerlMem_free(unixwild);
f86702cc 9806 return 1;
a3e9d8c9 9807 }
a3e9d8c9 9808 }
f86702cc 9809 /* First off, back up over constant elements at end of path */
9810 if (dirs) {
9811 for (front = end ; front >= base; front--)
9812 if (*front == '/' && !dirs--) { front++; break; }
a3e9d8c9 9813 }
c11536f5 9814 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 9815 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 9816 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
a480973c
JM
9817 cp1++,cp2++) {
9818 if (!decc_efs_case_preserve) {
9819 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9820 }
9821 else {
9822 *cp2 = *cp1;
9823 }
9824 }
9825 if (cp1 != '\0') {
367e4b85
JM
9826 PerlMem_free(tpl);
9827 PerlMem_free(unixified);
9828 PerlMem_free(unixwild);
c5375c28 9829 PerlMem_free(lcres);
a480973c 9830 return 0; /* Path too long. */
f7ddb74a 9831 }
f86702cc 9832 lcend = cp2;
9833 *cp2 = '\0'; /* Pick up with memcpy later */
9834 lcfront = lcres + (front - base);
9835 /* Now skip over each ellipsis and try to match the path in front of it. */
9836 while (ells--) {
c11536f5 9837 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
f86702cc 9838 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9839 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
c11536f5 9840 if (cp1 < tplate) break; /* template started with an ellipsis */
f86702cc 9841 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9842 ellipsis = cp1; continue;
9843 }
a480973c 9844 wilddsc.dsc$a_pointer = tpl;
f86702cc 9845 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9846 nextell = cp1;
9847 for (segdirs = 0, cp2 = tpl;
a480973c 9848 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
f86702cc 9849 cp1++, cp2++) {
9850 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
f7ddb74a
JM
9851 else {
9852 if (!decc_efs_case_preserve) {
9853 *cp2 = _tolower(*cp1); /* else lowercase for match */
9854 }
9855 else {
9856 *cp2 = *cp1; /* else preserve case for match */
9857 }
9858 }
f86702cc 9859 if (*cp2 == '/') segdirs++;
9860 }
a480973c 9861 if (cp1 != ellipsis - 1) {
367e4b85
JM
9862 PerlMem_free(tpl);
9863 PerlMem_free(unixified);
9864 PerlMem_free(unixwild);
9865 PerlMem_free(lcres);
a480973c
JM
9866 return 0; /* Path too long */
9867 }
f86702cc 9868 /* Back up at least as many dirs as in template before matching */
9869 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9870 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9871 for (match = 0; cp1 > lcres;) {
9872 resdsc.dsc$a_pointer = cp1;
9873 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9874 match++;
9875 if (match == 1) lcfront = cp1;
9876 }
9877 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9878 }
a480973c 9879 if (!match) {
367e4b85
JM
9880 PerlMem_free(tpl);
9881 PerlMem_free(unixified);
9882 PerlMem_free(unixwild);
9883 PerlMem_free(lcres);
a480973c
JM
9884 return 0; /* Can't find prefix ??? */
9885 }
f86702cc 9886 if (match > 1 && opts & 1) {
9887 /* This ... wildcard could cover more than one set of dirs (i.e.
9888 * a set of similar dir names is repeated). If the template
9889 * contains more than 1 ..., upstream elements could resolve the
9890 * ambiguity, but it's not worth a full backtracking setup here.
9891 * As a quick heuristic, clip off the current default directory
9892 * if it's present to find the trimmed spec, else use the
9893 * shortest string that this ... could cover.
9894 */
9895 char def[NAM$C_MAXRSS+1], *st;
9896
a480973c 9897 if (getcwd(def, sizeof def,0) == NULL) {
827f156d
JM
9898 PerlMem_free(unixified);
9899 PerlMem_free(unixwild);
9900 PerlMem_free(lcres);
9901 PerlMem_free(tpl);
a480973c
JM
9902 return 0;
9903 }
f7ddb74a
JM
9904 if (!decc_efs_case_preserve) {
9905 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9906 if (_tolower(*cp1) != _tolower(*cp2)) break;
9907 }
f86702cc 9908 segdirs = dirs - totells; /* Min # of dirs we must have left */
9909 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9910 if (*cp1 == '\0' && *cp2 == '/') {
18a3d61e 9911 memmove(fspec,cp2+1,end - cp2);
367e4b85
JM
9912 PerlMem_free(tpl);
9913 PerlMem_free(unixified);
9914 PerlMem_free(unixwild);
9915 PerlMem_free(lcres);
f86702cc 9916 return 1;
9917 }
9918 /* Nope -- stick with lcfront from above and keep going. */
9919 }
9920 }
18a3d61e 9921 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
367e4b85
JM
9922 PerlMem_free(tpl);
9923 PerlMem_free(unixified);
9924 PerlMem_free(unixwild);
9925 PerlMem_free(lcres);
a3e9d8c9 9926 return 1;
a0d0e21e 9927 }
a0d0e21e
LW
9928
9929} /* end of trim_unixpath() */
9930/*}}}*/
9931
a0d0e21e
LW
9932
9933/*
9934 * VMS readdir() routines.
9935 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
a0d0e21e 9936 *
bd3fa61c 9937 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
a0d0e21e
LW
9938 * Minor modifications to original routines.
9939 */
9940
a9852f7c
CB
9941/* readdir may have been redefined by reentr.h, so make sure we get
9942 * the local version for what we do here.
9943 */
9944#ifdef readdir
9945# undef readdir
9946#endif
9947#if !defined(PERL_IMPLICIT_CONTEXT)
9948# define readdir Perl_readdir
9949#else
9950# define readdir(a) Perl_readdir(aTHX_ a)
9951#endif
9952
a0d0e21e
LW
9953 /* Number of elements in vms_versions array */
9954#define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9955
9956/*
9957 * Open a directory, return a handle for later use.
9958 */
9959/*{{{ DIR *opendir(char*name) */
ddcbaa1c 9960DIR *
b8ffc8df 9961Perl_opendir(pTHX_ const char *name)
a0d0e21e 9962{
ddcbaa1c 9963 DIR *dd;
657054d4 9964 char *dir;
61bb5906 9965 Stat_t sb;
657054d4
JM
9966
9967 Newx(dir, VMS_MAXRSS, char);
4846f1d7 9968 if (int_tovmspath(name, dir, NULL) == NULL) {
657054d4 9969 Safefree(dir);
61bb5906 9970 return NULL;
a0d0e21e 9971 }
ada67d10
CB
9972 /* Check access before stat; otherwise stat does not
9973 * accurately report whether it's a directory.
9974 */
a1887106 9975 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
fac786e7 9976 /* cando_by_name has already set errno */
657054d4 9977 Safefree(dir);
ada67d10
CB
9978 return NULL;
9979 }
61bb5906
CB
9980 if (flex_stat(dir,&sb) == -1) return NULL;
9981 if (!S_ISDIR(sb.st_mode)) {
657054d4 9982 Safefree(dir);
61bb5906
CB
9983 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9984 return NULL;
9985 }
61bb5906 9986 /* Get memory for the handle, and the pattern. */
ddcbaa1c 9987 Newx(dd,1,DIR);
a02a5408 9988 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
a0d0e21e
LW
9989
9990 /* Fill in the fields; mainly playing with the descriptor. */
f7ddb74a 9991 sprintf(dd->pattern, "%s*.*",dir);
657054d4 9992 Safefree(dir);
a0d0e21e
LW
9993 dd->context = 0;
9994 dd->count = 0;
657054d4 9995 dd->flags = 0;
a096370a
CB
9996 /* By saying we always want the result of readdir() in unix format, we
9997 * are really saying we want all the escapes removed. Otherwise the caller,
9998 * having no way to know whether it's already in VMS format, might send it
9999 * through tovmsspec again, thus double escaping.
10000 */
10001 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
a0d0e21e
LW
10002 dd->pat.dsc$a_pointer = dd->pattern;
10003 dd->pat.dsc$w_length = strlen(dd->pattern);
10004 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10005 dd->pat.dsc$b_class = DSC$K_CLASS_S;
3bc25146 10006#if defined(USE_ITHREADS)
a02a5408 10007 Newx(dd->mutex,1,perl_mutex);
a9852f7c
CB
10008 MUTEX_INIT( (perl_mutex *) dd->mutex );
10009#else
10010 dd->mutex = NULL;
10011#endif
a0d0e21e
LW
10012
10013 return dd;
10014} /* end of opendir() */
10015/*}}}*/
10016
10017/*
10018 * Set the flag to indicate we want versions or not.
10019 */
10020/*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10021void
ddcbaa1c 10022vmsreaddirversions(DIR *dd, int flag)
a0d0e21e 10023{
657054d4
JM
10024 if (flag)
10025 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10026 else
10027 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
a0d0e21e
LW
10028}
10029/*}}}*/
10030
10031/*
10032 * Free up an opened directory.
10033 */
10034/*{{{ void closedir(DIR *dd)*/
10035void
ddcbaa1c 10036Perl_closedir(DIR *dd)
a0d0e21e 10037{
f7ddb74a
JM
10038 int sts;
10039
10040 sts = lib$find_file_end(&dd->context);
a0d0e21e 10041 Safefree(dd->pattern);
3bc25146 10042#if defined(USE_ITHREADS)
a9852f7c
CB
10043 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10044 Safefree(dd->mutex);
10045#endif
f7ddb74a 10046 Safefree(dd);
a0d0e21e
LW
10047}
10048/*}}}*/
10049
10050/*
10051 * Collect all the version numbers for the current file.
10052 */
10053static void
ddcbaa1c 10054collectversions(pTHX_ DIR *dd)
a0d0e21e
LW
10055{
10056 struct dsc$descriptor_s pat;
10057 struct dsc$descriptor_s res;
ddcbaa1c 10058 struct dirent *e;
657054d4 10059 char *p, *text, *buff;
a0d0e21e
LW
10060 int i;
10061 unsigned long context, tmpsts;
10062
10063 /* Convenient shorthand. */
10064 e = &dd->entry;
10065
10066 /* Add the version wildcard, ignoring the "*.*" put on before */
10067 i = strlen(dd->pattern);
a02a5408 10068 Newx(text,i + e->d_namlen + 3,char);
a35dcc95 10069 my_strlcpy(text, dd->pattern, i + 1);
f7ddb74a 10070 sprintf(&text[i - 3], "%s;*", e->d_name);
a0d0e21e
LW
10071
10072 /* Set up the pattern descriptor. */
10073 pat.dsc$a_pointer = text;
10074 pat.dsc$w_length = i + e->d_namlen - 1;
10075 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10076 pat.dsc$b_class = DSC$K_CLASS_S;
10077
10078 /* Set up result descriptor. */
657054d4 10079 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10080 res.dsc$a_pointer = buff;
657054d4 10081 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10082 res.dsc$b_dtype = DSC$K_DTYPE_T;
10083 res.dsc$b_class = DSC$K_CLASS_S;
10084
10085 /* Read files, collecting versions. */
10086 for (context = 0, e->vms_verscount = 0;
10087 e->vms_verscount < VERSIZE(e);
10088 e->vms_verscount++) {
657054d4
JM
10089 unsigned long rsts;
10090 unsigned long flags = 0;
10091
10092#ifdef VMS_LONGNAME_SUPPORT
988c775c 10093 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10094#endif
10095 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
a0d0e21e 10096 if (tmpsts == RMS$_NMF || context == 0) break;
748a9306 10097 _ckvmssts(tmpsts);
657054d4 10098 buff[VMS_MAXRSS - 1] = '\0';
748a9306 10099 if ((p = strchr(buff, ';')))
a0d0e21e
LW
10100 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10101 else
10102 e->vms_versions[e->vms_verscount] = -1;
10103 }
10104
748a9306 10105 _ckvmssts(lib$find_file_end(&context));
a0d0e21e 10106 Safefree(text);
657054d4 10107 Safefree(buff);
a0d0e21e
LW
10108
10109} /* end of collectversions() */
10110
10111/*
10112 * Read the next entry from the directory.
10113 */
10114/*{{{ struct dirent *readdir(DIR *dd)*/
ddcbaa1c
CB
10115struct dirent *
10116Perl_readdir(pTHX_ DIR *dd)
a0d0e21e
LW
10117{
10118 struct dsc$descriptor_s res;
657054d4 10119 char *p, *buff;
a0d0e21e 10120 unsigned long int tmpsts;
657054d4
JM
10121 unsigned long rsts;
10122 unsigned long flags = 0;
dca5a913 10123 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
657054d4 10124 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
a0d0e21e
LW
10125
10126 /* Set up result descriptor, and get next file. */
657054d4 10127 Newx(buff, VMS_MAXRSS, char);
a0d0e21e 10128 res.dsc$a_pointer = buff;
657054d4 10129 res.dsc$w_length = VMS_MAXRSS - 1;
a0d0e21e
LW
10130 res.dsc$b_dtype = DSC$K_DTYPE_T;
10131 res.dsc$b_class = DSC$K_CLASS_S;
657054d4
JM
10132
10133#ifdef VMS_LONGNAME_SUPPORT
988c775c 10134 flags = LIB$M_FIL_LONG_NAMES;
657054d4
JM
10135#endif
10136
10137 tmpsts = lib$find_file
10138 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
4633a7c4
LW
10139 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10140 if (!(tmpsts & 1)) {
10141 set_vaxc_errno(tmpsts);
10142 switch (tmpsts) {
10143 case RMS$_PRV:
c07a80fd 10144 set_errno(EACCES); break;
4633a7c4 10145 case RMS$_DEV:
c07a80fd 10146 set_errno(ENODEV); break;
4633a7c4 10147 case RMS$_DIR:
f282b18d
CB
10148 set_errno(ENOTDIR); break;
10149 case RMS$_FNF: case RMS$_DNF:
c07a80fd 10150 set_errno(ENOENT); break;
4633a7c4
LW
10151 default:
10152 set_errno(EVMSERR);
10153 }
657054d4 10154 Safefree(buff);
4633a7c4
LW
10155 return NULL;
10156 }
10157 dd->count++;
a0d0e21e 10158 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
c43a0d1c
CB
10159 buff[res.dsc$w_length] = '\0';
10160 p = buff + res.dsc$w_length;
10161 while (--p >= buff) if (!isspace(*p)) break;
10162 *p = '\0';
f7ddb74a 10163 if (!decc_efs_case_preserve) {
f7ddb74a 10164 for (p = buff; *p; p++) *p = _tolower(*p);
f7ddb74a 10165 }
a0d0e21e
LW
10166
10167 /* Skip any directory component and just copy the name. */
657054d4 10168 sts = vms_split_path
360732b5 10169 (buff,
657054d4
JM
10170 &v_spec,
10171 &v_len,
10172 &r_spec,
10173 &r_len,
10174 &d_spec,
10175 &d_len,
10176 &n_spec,
10177 &n_len,
10178 &e_spec,
10179 &e_len,
10180 &vs_spec,
10181 &vs_len);
10182
0dddfaca
JM
10183 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10184
10185 /* In Unix report mode, remove the ".dir;1" from the name */
10186 /* if it is a real directory. */
10187 if (decc_filename_unix_report || decc_efs_charset) {
f785e3a1
JM
10188 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10189 Stat_t statbuf;
10190 int ret_sts;
10191
10192 ret_sts = flex_lstat(buff, &statbuf);
10193 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10194 e_len = 0;
10195 e_spec[0] = 0;
0dddfaca
JM
10196 }
10197 }
10198 }
10199
10200 /* Drop NULL extensions on UNIX file specification */
10201 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10202 e_len = 0;
10203 e_spec[0] = '\0';
10204 }
dca5a913
JM
10205 }
10206
a35dcc95 10207 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
657054d4
JM
10208 dd->entry.d_name[n_len + e_len] = '\0';
10209 dd->entry.d_namlen = strlen(dd->entry.d_name);
a0d0e21e 10210
657054d4
JM
10211 /* Convert the filename to UNIX format if needed */
10212 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10213
10214 /* Translate the encoded characters. */
38a44b82 10215 /* Fixme: Unicode handling could result in embedded 0 characters */
657054d4
JM
10216 if (strchr(dd->entry.d_name, '^') != NULL) {
10217 char new_name[256];
10218 char * q;
657054d4
JM
10219 p = dd->entry.d_name;
10220 q = new_name;
10221 while (*p != 0) {
f617045b
CB
10222 int inchars_read, outchars_added;
10223 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10224 p += inchars_read;
10225 q += outchars_added;
dca5a913 10226 /* fix-me */
f617045b 10227 /* if outchars_added > 1, then this is a wide file specification */
dca5a913 10228 /* Wide file specifications need to be passed in Perl */
38a44b82 10229 /* counted strings apparently with a Unicode flag */
657054d4
JM
10230 }
10231 *q = 0;
a35dcc95 10232 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
657054d4 10233 }
657054d4 10234 }
a0d0e21e 10235
a0d0e21e 10236 dd->entry.vms_verscount = 0;
657054d4
JM
10237 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10238 Safefree(buff);
a0d0e21e
LW
10239 return &dd->entry;
10240
10241} /* end of readdir() */
10242/*}}}*/
10243
10244/*
a9852f7c
CB
10245 * Read the next entry from the directory -- thread-safe version.
10246 */
10247/*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10248int
ddcbaa1c 10249Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
a9852f7c
CB
10250{
10251 int retval;
10252
10253 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10254
7ded3206 10255 entry = readdir(dd);
a9852f7c
CB
10256 *result = entry;
10257 retval = ( *result == NULL ? errno : 0 );
10258
10259 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10260
10261 return retval;
10262
10263} /* end of readdir_r() */
10264/*}}}*/
10265
10266/*
a0d0e21e
LW
10267 * Return something that can be used in a seekdir later.
10268 */
10269/*{{{ long telldir(DIR *dd)*/
10270long
ddcbaa1c 10271Perl_telldir(DIR *dd)
a0d0e21e
LW
10272{
10273 return dd->count;
10274}
10275/*}}}*/
10276
10277/*
10278 * Return to a spot where we used to be. Brute force.
10279 */
10280/*{{{ void seekdir(DIR *dd,long count)*/
10281void
ddcbaa1c 10282Perl_seekdir(pTHX_ DIR *dd, long count)
a0d0e21e 10283{
657054d4 10284 int old_flags;
a0d0e21e
LW
10285
10286 /* If we haven't done anything yet... */
10287 if (dd->count == 0)
10288 return;
10289
10290 /* Remember some state, and clear it. */
657054d4
JM
10291 old_flags = dd->flags;
10292 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
748a9306 10293 _ckvmssts(lib$find_file_end(&dd->context));
a0d0e21e
LW
10294 dd->context = 0;
10295
10296 /* The increment is in readdir(). */
10297 for (dd->count = 0; dd->count < count; )
f7ddb74a 10298 readdir(dd);
a0d0e21e 10299
657054d4 10300 dd->flags = old_flags;
a0d0e21e
LW
10301
10302} /* end of seekdir() */
10303/*}}}*/
10304
10305/* VMS subprocess management
10306 *
10307 * my_vfork() - just a vfork(), after setting a flag to record that
10308 * the current script is trying a Unix-style fork/exec.
10309 *
10310 * vms_do_aexec() and vms_do_exec() are called in response to the
10311 * perl 'exec' function. If this follows a vfork call, then they
a6d05634 10312 * call out the regular perl routines in doio.c which do an
a0d0e21e
LW
10313 * execvp (for those who really want to try this under VMS).
10314 * Otherwise, they do exactly what the perl docs say exec should
10315 * do - terminate the current script and invoke a new command
10316 * (See below for notes on command syntax.)
10317 *
10318 * do_aspawn() and do_spawn() implement the VMS side of the perl
10319 * 'system' function.
10320 *
10321 * Note on command arguments to perl 'exec' and 'system': When handled
10322 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
eed5d6a1
CB
10323 * are concatenated to form a DCL command string. If the first non-numeric
10324 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
a6d05634 10325 * the command string is handed off to DCL directly. Otherwise,
a0d0e21e
LW
10326 * the first token of the command is taken as the filespec of an image
10327 * to run. The filespec is expanded using a default type of '.EXE' and
3eeba6fb 10328 * the process defaults for device, directory, etc., and if found, the resultant
a0d0e21e 10329 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
3eeba6fb 10330 * the command string as parameters. This is perhaps a bit complicated,
a0d0e21e
LW
10331 * but I hope it will form a happy medium between what VMS folks expect
10332 * from lib$spawn and what Unix folks expect from exec.
10333 */
10334
10335static int vfork_called;
10336
f7c699a0 10337/*{{{int my_vfork(void)*/
a0d0e21e 10338int
f7c699a0 10339my_vfork(void)
a0d0e21e 10340{
748a9306 10341 vfork_called++;
a0d0e21e
LW
10342 return vfork();
10343}
10344/*}}}*/
10345
4633a7c4 10346
a0d0e21e 10347static void
218fdd94
CL
10348vms_execfree(struct dsc$descriptor_s *vmscmd)
10349{
10350 if (vmscmd) {
10351 if (vmscmd->dsc$a_pointer) {
c5375c28 10352 PerlMem_free(vmscmd->dsc$a_pointer);
218fdd94 10353 }
c5375c28 10354 PerlMem_free(vmscmd);
4633a7c4
LW
10355 }
10356}
10357
10358static char *
fd8cd3a3 10359setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
a0d0e21e 10360{
4e205ed6 10361 char *junk, *tmps = NULL;
eb578fdb 10362 size_t cmdlen = 0;
a0d0e21e 10363 size_t rlen;
eb578fdb 10364 SV **idx;
2d8e6c8d 10365 STRLEN n_a;
a0d0e21e
LW
10366
10367 idx = mark;
4633a7c4
LW
10368 if (really) {
10369 tmps = SvPV(really,rlen);
10370 if (*tmps) {
10371 cmdlen += rlen + 1;
10372 idx++;
10373 }
a0d0e21e
LW
10374 }
10375
10376 for (idx++; idx <= sp; idx++) {
10377 if (*idx) {
10378 junk = SvPVx(*idx,rlen);
10379 cmdlen += rlen ? rlen + 1 : 0;
10380 }
10381 }
c5375c28 10382 Newx(PL_Cmd, cmdlen+1, char);
a0d0e21e 10383
4633a7c4 10384 if (tmps && *tmps) {
a35dcc95 10385 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
a0d0e21e
LW
10386 mark++;
10387 }
6b88bc9c 10388 else *PL_Cmd = '\0';
a0d0e21e
LW
10389 while (++mark <= sp) {
10390 if (*mark) {
3eeba6fb
CB
10391 char *s = SvPVx(*mark,n_a);
10392 if (!*s) continue;
a35dcc95
CB
10393 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10394 my_strlcat(PL_Cmd, s, cmdlen+1);
a0d0e21e
LW
10395 }
10396 }
6b88bc9c 10397 return PL_Cmd;
a0d0e21e
LW
10398
10399} /* end of setup_argstr() */
10400
4633a7c4 10401
a0d0e21e 10402static unsigned long int
2fbb330f 10403setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
218fdd94 10404 struct dsc$descriptor_s **pvmscmd)
a0d0e21e 10405{
e919cd19
JM
10406 char * vmsspec;
10407 char * resspec;
e886094b
JM
10408 char image_name[NAM$C_MAXRSS+1];
10409 char image_argv[NAM$C_MAXRSS+1];
a0d0e21e 10410 $DESCRIPTOR(defdsc,".EXE");
8012a33e 10411 $DESCRIPTOR(defdsc2,".");
e919cd19 10412 struct dsc$descriptor_s resdsc;
218fdd94 10413 struct dsc$descriptor_s *vmscmd;
a0d0e21e 10414 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3eeba6fb 10415 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
eb578fdb 10416 char *s, *rest, *cp, *wordbreak;
2fbb330f
JM
10417 char * cmd;
10418 int cmdlen;
eb578fdb 10419 int isdcl;
a0d0e21e 10420
426fe37a 10421 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
ebd4d70b 10422 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2fbb330f 10423
e919cd19 10424 /* vmsspec is a DCL command buffer, not just a filename */
c11536f5 10425 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
e919cd19
JM
10426 if (vmsspec == NULL)
10427 _ckvmssts_noperl(SS$_INSFMEM);
10428
c11536f5 10429 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
e919cd19
JM
10430 if (resspec == NULL)
10431 _ckvmssts_noperl(SS$_INSFMEM);
10432
2fbb330f
JM
10433 /* Make a copy for modification */
10434 cmdlen = strlen(incmd);
c11536f5 10435 cmd = (char *)PerlMem_malloc(cmdlen+1);
ebd4d70b 10436 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a35dcc95 10437 my_strlcpy(cmd, incmd, cmdlen + 1);
e886094b
JM
10438 image_name[0] = 0;
10439 image_argv[0] = 0;
2fbb330f 10440
e919cd19
JM
10441 resdsc.dsc$a_pointer = resspec;
10442 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10443 resdsc.dsc$b_class = DSC$K_CLASS_S;
10444 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10445
218fdd94
CL
10446 vmscmd->dsc$a_pointer = NULL;
10447 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10448 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10449 vmscmd->dsc$w_length = 0;
10450 if (pvmscmd) *pvmscmd = vmscmd;
10451
ff7adb52
CL
10452 if (suggest_quote) *suggest_quote = 0;
10453
2fbb330f 10454 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
c5375c28 10455 PerlMem_free(cmd);
e919cd19
JM
10456 PerlMem_free(vmsspec);
10457 PerlMem_free(resspec);
a2669cfc 10458 return CLI$_BUFOVF; /* continuation lines currently unsupported */
2fbb330f
JM
10459 }
10460
a0d0e21e 10461 s = cmd;
2fbb330f 10462
a0d0e21e 10463 while (*s && isspace(*s)) s++;
aa779de1
CB
10464
10465 if (*s == '@' || *s == '$') {
10466 vmsspec[0] = *s; rest = s + 1;
10467 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10468 }
10469 else { cp = vmsspec; rest = s; }
22831cc5
CB
10470
10471 /* If the first word is quoted, then we need to unquote it and
10472 * escape spaces within it. We'll expand into the resspec buffer,
10473 * then copy back into the cmd buffer, expanding the latter if
10474 * necessary.
10475 */
10476 if (*rest == '"') {
10477 char *cp2;
10478 char *r = rest;
10479 bool in_quote = 0;
10480 int clen = cmdlen;
10481 int soff = s - cmd;
10482
10483 for (cp2 = resspec;
10484 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10485 rest++) {
10486
10487 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10488 *cp2 = '^';
10489 *(++cp2) = '_';
10490 cp2++;
10491 clen++;
10492 }
10493 else if (*rest == '"') {
10494 clen--;
10495 if (in_quote) { /* Must be closing quote. */
10496 rest++;
10497 break;
10498 }
10499 in_quote = 1;
10500 }
10501 else {
10502 *cp2 = *rest;
10503 cp2++;
10504 }
10505 }
10506 *cp2 = '\0';
10507
10508 /* Expand the command buffer if necessary. */
10509 if (clen > cmdlen) {
223c162b 10510 cmd = (char *)PerlMem_realloc(cmd, clen);
22831cc5
CB
10511 if (cmd == NULL)
10512 _ckvmssts_noperl(SS$_INSFMEM);
10513 /* Where we are may have changed, so recompute offsets */
10514 r = cmd + (r - s - soff);
10515 rest = cmd + (rest - s - soff);
10516 s = cmd + soff;
10517 }
10518
10519 /* Shift the non-verb portion of the command (if any) up or
10520 * down as necessary.
10521 */
10522 if (*rest)
10523 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10524
10525 /* Copy the unquoted and escaped command verb into place. */
10526 memcpy(r, resspec, cp2 - resspec);
10527 cmd[clen] = '\0';
10528 cmdlen = clen;
10529 rest = r; /* Rewind for subsequent operations. */
10530 }
10531
aa779de1
CB
10532 if (*rest == '.' || *rest == '/') {
10533 char *cp2;
10534 for (cp2 = resspec;
e919cd19 10535 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
aa779de1
CB
10536 rest++, cp2++) *cp2 = *rest;
10537 *cp2 = '\0';
df278665 10538 if (int_tovmsspec(resspec, cp, 0, NULL)) {
aa779de1 10539 s = vmsspec;
cfbf46cd
JM
10540
10541 /* When a UNIX spec with no file type is translated to VMS, */
10542 /* A trailing '.' is appended under ODS-5 rules. */
10543 /* Here we do not want that trailing "." as it prevents */
10544 /* Looking for a implied ".exe" type. */
10545 if (decc_efs_charset) {
10546 int i;
10547 i = strlen(vmsspec);
10548 if (vmsspec[i-1] == '.') {
10549 vmsspec[i-1] = '\0';
10550 }
10551 }
10552
aa779de1
CB
10553 if (*rest) {
10554 for (cp2 = vmsspec + strlen(vmsspec);
e919cd19 10555 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
aa779de1
CB
10556 rest++, cp2++) *cp2 = *rest;
10557 *cp2 = '\0';
a0d0e21e
LW
10558 }
10559 }
10560 }
aa779de1
CB
10561 /* Intuit whether verb (first word of cmd) is a DCL command:
10562 * - if first nonspace char is '@', it's a DCL indirection
10563 * otherwise
10564 * - if verb contains a filespec separator, it's not a DCL command
10565 * - if it doesn't, caller tells us whether to default to a DCL
10566 * command, or to a local image unless told it's DCL (by leading '$')
10567 */
ff7adb52
CL
10568 if (*s == '@') {
10569 isdcl = 1;
10570 if (suggest_quote) *suggest_quote = 1;
10571 } else {
eb578fdb 10572 char *filespec = strpbrk(s,":<[.;");
aa779de1
CB
10573 rest = wordbreak = strpbrk(s," \"\t/");
10574 if (!wordbreak) wordbreak = s + strlen(s);
10575 if (*s == '$') check_img = 0;
10576 if (filespec && (filespec < wordbreak)) isdcl = 0;
10577 else isdcl = !check_img;
10578 }
10579
3eeba6fb 10580 if (!isdcl) {
dca5a913 10581 int rsts;
aa779de1
CB
10582 imgdsc.dsc$a_pointer = s;
10583 imgdsc.dsc$w_length = wordbreak - s;
dca5a913 10584 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8012a33e 10585 if (!(retsts&1)) {
ebd4d70b 10586 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10587 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f 10588 if (!(retsts & 1) && *s == '$') {
ebd4d70b 10589 _ckvmssts_noperl(lib$find_file_end(&cxt));
2497a41f 10590 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
dca5a913 10591 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
2497a41f 10592 if (!(retsts&1)) {
ebd4d70b 10593 _ckvmssts_noperl(lib$find_file_end(&cxt));
dca5a913 10594 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
2497a41f
JM
10595 }
10596 }
aa779de1 10597 }
ebd4d70b 10598 _ckvmssts_noperl(lib$find_file_end(&cxt));
8012a33e 10599
aa779de1 10600 if (retsts & 1) {
8012a33e 10601 FILE *fp;
a0d0e21e
LW
10602 s = resspec;
10603 while (*s && !isspace(*s)) s++;
10604 *s = '\0';
8012a33e
CB
10605
10606 /* check that it's really not DCL with no file extension */
e886094b 10607 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8012a33e 10608 if (fp) {
2497a41f
JM
10609 char b[256] = {0,0,0,0};
10610 read(fileno(fp), b, 256);
8012a33e 10611 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
2497a41f 10612 if (isdcl) {
e886094b
JM
10613 int shebang_len;
10614
2497a41f 10615 /* Check for script */
e886094b
JM
10616 shebang_len = 0;
10617 if ((b[0] == '#') && (b[1] == '!'))
10618 shebang_len = 2;
10619#ifdef ALTERNATE_SHEBANG
10620 else {
10621 shebang_len = strlen(ALTERNATE_SHEBANG);
10622 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10623 char * perlstr;
10624 perlstr = strstr("perl",b);
10625 if (perlstr == NULL)
10626 shebang_len = 0;
10627 }
10628 else
10629 shebang_len = 0;
10630 }
10631#endif
10632
10633 if (shebang_len > 0) {
10634 int i;
10635 int j;
10636 char tmpspec[NAM$C_MAXRSS + 1];
10637
10638 i = shebang_len;
10639 /* Image is following after white space */
10640 /*--------------------------------------*/
10641 while (isprint(b[i]) && isspace(b[i]))
10642 i++;
10643
10644 j = 0;
10645 while (isprint(b[i]) && !isspace(b[i])) {
10646 tmpspec[j++] = b[i++];
10647 if (j >= NAM$C_MAXRSS)
10648 break;
10649 }
10650 tmpspec[j] = '\0';
10651
10652 /* There may be some default parameters to the image */
10653 /*---------------------------------------------------*/
10654 j = 0;
10655 while (isprint(b[i])) {
10656 image_argv[j++] = b[i++];
10657 if (j >= NAM$C_MAXRSS)
10658 break;
10659 }
10660 while ((j > 0) && !isprint(image_argv[j-1]))
10661 j--;
10662 image_argv[j] = 0;
10663
2497a41f 10664 /* It will need to be converted to VMS format and validated */
e886094b
JM
10665 if (tmpspec[0] != '\0') {
10666 char * iname;
10667
10668 /* Try to find the exact program requested to be run */
10669 /*---------------------------------------------------*/
6fb6c614
JM
10670 iname = int_rmsexpand
10671 (tmpspec, image_name, ".exe",
360732b5 10672 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10673 if (iname != NULL) {
a1887106
JM
10674 if (cando_by_name_int
10675 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10676 /* MCR prefix needed */
10677 isdcl = 0;
10678 }
10679 else {
10680 /* Try again with a null type */
10681 /*----------------------------*/
6fb6c614
JM
10682 iname = int_rmsexpand
10683 (tmpspec, image_name, ".",
360732b5 10684 PERL_RMSEXPAND_M_VMS, NULL, NULL);
e886094b 10685 if (iname != NULL) {
a1887106
JM
10686 if (cando_by_name_int
10687 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
e886094b
JM
10688 /* MCR prefix needed */
10689 isdcl = 0;
10690 }
10691 }
10692 }
10693
10694 /* Did we find the image to run the script? */
10695 /*------------------------------------------*/
10696 if (isdcl) {
10697 char *tchr;
10698
10699 /* Assume DCL or foreign command exists */
10700 /*--------------------------------------*/
10701 tchr = strrchr(tmpspec, '/');
10702 if (tchr != NULL) {
10703 tchr++;
10704 }
10705 else {
10706 tchr = tmpspec;
10707 }
a35dcc95 10708 my_strlcpy(image_name, tchr, sizeof(image_name));
e886094b
JM
10709 }
10710 }
10711 }
2497a41f
JM
10712 }
10713 }
8012a33e
CB
10714 fclose(fp);
10715 }
e919cd19
JM
10716 if (check_img && isdcl) {
10717 PerlMem_free(cmd);
10718 PerlMem_free(resspec);
10719 PerlMem_free(vmsspec);
10720 return RMS$_FNF;
10721 }
8012a33e 10722
3eeba6fb 10723 if (cando_by_name(S_IXUSR,0,resspec)) {
c11536f5 10724 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
ebd4d70b 10725 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8012a33e 10726 if (!isdcl) {
a35dcc95 10727 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
e886094b 10728 if (image_name[0] != 0) {
a35dcc95
CB
10729 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10730 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
e886094b
JM
10731 }
10732 } else if (image_name[0] != 0) {
a35dcc95
CB
10733 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10734 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
8012a33e 10735 } else {
a35dcc95 10736 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
8012a33e 10737 }
e886094b
JM
10738 if (suggest_quote) *suggest_quote = 1;
10739
10740 /* If there is an image name, use original command */
10741 if (image_name[0] == 0)
a35dcc95 10742 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
e886094b
JM
10743 else {
10744 rest = cmd;
10745 while (*rest && isspace(*rest)) rest++;
10746 }
10747
10748 if (image_argv[0] != 0) {
a35dcc95
CB
10749 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10750 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
e886094b
JM
10751 }
10752 if (rest) {
10753 int rest_len;
10754 int vmscmd_len;
10755
10756 rest_len = strlen(rest);
10757 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10758 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
a35dcc95 10759 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
e886094b
JM
10760 else
10761 retsts = CLI$_BUFOVF;
10762 }
218fdd94 10763 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
c5375c28 10764 PerlMem_free(cmd);
e919cd19
JM
10765 PerlMem_free(vmsspec);
10766 PerlMem_free(resspec);
218fdd94 10767 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
3eeba6fb 10768 }
c5375c28
JM
10769 else
10770 retsts = RMS$_PRV;
a0d0e21e
LW
10771 }
10772 }
3eeba6fb 10773 /* It's either a DCL command or we couldn't find a suitable image */
218fdd94 10774 vmscmd->dsc$w_length = strlen(cmd);
ff7adb52 10775
c11536f5 10776 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
a35dcc95 10777 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
c5375c28
JM
10778
10779 PerlMem_free(cmd);
e919cd19
JM
10780 PerlMem_free(resspec);
10781 PerlMem_free(vmsspec);
2fbb330f 10782
ff7adb52
CL
10783 /* check if it's a symbol (for quoting purposes) */
10784 if (suggest_quote && !*suggest_quote) {
10785 int iss;
10786 char equiv[LNM$C_NAMLENGTH];
10787 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10788 eqvdsc.dsc$a_pointer = equiv;
10789
218fdd94 10790 iss = lib$get_symbol(vmscmd,&eqvdsc);
ff7adb52
CL
10791 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10792 }
3eeba6fb
CB
10793 if (!(retsts & 1)) {
10794 /* just hand off status values likely to be due to user error */
10795 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10796 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10797 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
ebd4d70b 10798 else { _ckvmssts_noperl(retsts); }
3eeba6fb 10799 }
a0d0e21e 10800
218fdd94 10801 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
a3e9d8c9 10802
a0d0e21e
LW
10803} /* end of setup_cmddsc() */
10804
a3e9d8c9 10805
a0d0e21e
LW
10806/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10807bool
fd8cd3a3 10808Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
a0d0e21e 10809{
c5375c28
JM
10810bool exec_sts;
10811char * cmd;
10812
a0d0e21e
LW
10813 if (sp > mark) {
10814 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
10815 vfork_called--;
10816 if (vfork_called < 0) {
5c84aa53 10817 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
10818 vfork_called = 0;
10819 }
10820 else return do_aexec(really,mark,sp);
a0d0e21e 10821 }
4633a7c4 10822 /* no vfork - act VMSish */
c5375c28
JM
10823 cmd = setup_argstr(aTHX_ really,mark,sp);
10824 exec_sts = vms_do_exec(cmd);
10825 Safefree(cmd); /* Clean up from setup_argstr() */
10826 return exec_sts;
a0d0e21e
LW
10827 }
10828
10829 return FALSE;
10830} /* end of vms_do_aexec() */
10831/*}}}*/
10832
10833/* {{{bool vms_do_exec(char *cmd) */
10834bool
2fbb330f 10835Perl_vms_do_exec(pTHX_ const char *cmd)
a0d0e21e 10836{
218fdd94 10837 struct dsc$descriptor_s *vmscmd;
a0d0e21e
LW
10838
10839 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
10840 vfork_called--;
10841 if (vfork_called < 0) {
5c84aa53 10842 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
748a9306
LW
10843 vfork_called = 0;
10844 }
10845 else return do_exec(cmd);
a0d0e21e 10846 }
748a9306
LW
10847
10848 { /* no vfork - act VMSish */
748a9306 10849 unsigned long int retsts;
a0d0e21e 10850
1e422769 10851 TAINT_ENV();
10852 TAINT_PROPER("exec");
218fdd94
CL
10853 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10854 retsts = lib$do_command(vmscmd);
a0d0e21e 10855
09b7f37c 10856 switch (retsts) {
f282b18d 10857 case RMS$_FNF: case RMS$_DNF:
09b7f37c 10858 set_errno(ENOENT); break;
f282b18d 10859 case RMS$_DIR:
09b7f37c 10860 set_errno(ENOTDIR); break;
f282b18d
CB
10861 case RMS$_DEV:
10862 set_errno(ENODEV); break;
09b7f37c
CB
10863 case RMS$_PRV:
10864 set_errno(EACCES); break;
10865 case RMS$_SYN:
10866 set_errno(EINVAL); break;
a2669cfc 10867 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
09b7f37c
CB
10868 set_errno(E2BIG); break;
10869 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 10870 _ckvmssts_noperl(retsts); /* fall through */
09b7f37c
CB
10871 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10872 set_errno(EVMSERR);
10873 }
748a9306 10874 set_vaxc_errno(retsts);
3eeba6fb 10875 if (ckWARN(WARN_EXEC)) {
f98bc0c6 10876 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
218fdd94 10877 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
3eeba6fb 10878 }
218fdd94 10879 vms_execfree(vmscmd);
a0d0e21e
LW
10880 }
10881
10882 return FALSE;
10883
10884} /* end of vms_do_exec() */
10885/*}}}*/
10886
9ec7171b 10887int do_spawn2(pTHX_ const char *, int);
a0d0e21e 10888
9ec7171b
CB
10889int
10890Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
a0d0e21e 10891{
c5375c28
JM
10892unsigned long int sts;
10893char * cmd;
eed5d6a1 10894int flags = 0;
a0d0e21e 10895
c5375c28 10896 if (sp > mark) {
eed5d6a1
CB
10897
10898 /* We'll copy the (undocumented?) Win32 behavior and allow a
10899 * numeric first argument. But the only value we'll support
10900 * through do_aspawn is a value of 1, which means spawn without
10901 * waiting for completion -- other values are ignored.
10902 */
9ec7171b 10903 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
eed5d6a1 10904 ++mark;
9ec7171b 10905 flags = SvIVx(*mark);
eed5d6a1
CB
10906 }
10907
10908 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10909 flags = CLI$M_NOWAIT;
10910 else
10911 flags = 0;
10912
9ec7171b 10913 cmd = setup_argstr(aTHX_ really, mark, sp);
eed5d6a1 10914 sts = do_spawn2(aTHX_ cmd, flags);
c5375c28
JM
10915 /* pp_sys will clean up cmd */
10916 return sts;
10917 }
a0d0e21e
LW
10918 return SS$_ABORT;
10919} /* end of do_aspawn() */
10920/*}}}*/
10921
eed5d6a1 10922
9ec7171b
CB
10923/* {{{int do_spawn(char* cmd) */
10924int
10925Perl_do_spawn(pTHX_ char* cmd)
a0d0e21e 10926{
7918f24d
NC
10927 PERL_ARGS_ASSERT_DO_SPAWN;
10928
eed5d6a1
CB
10929 return do_spawn2(aTHX_ cmd, 0);
10930}
10931/*}}}*/
10932
9ec7171b
CB
10933/* {{{int do_spawn_nowait(char* cmd) */
10934int
10935Perl_do_spawn_nowait(pTHX_ char* cmd)
10936{
10937 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10938
10939 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10940}
10941/*}}}*/
10942
10943/* {{{int do_spawn2(char *cmd) */
10944int
eed5d6a1
CB
10945do_spawn2(pTHX_ const char *cmd, int flags)
10946{
209030df 10947 unsigned long int sts, substs;
a0d0e21e 10948
c5375c28
JM
10949 /* The caller of this routine expects to Safefree(PL_Cmd) */
10950 Newx(PL_Cmd,10,char);
10951
1e422769 10952 TAINT_ENV();
10953 TAINT_PROPER("spawn");
748a9306 10954 if (!cmd || !*cmd) {
eed5d6a1 10955 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
c8795d8b
JH
10956 if (!(sts & 1)) {
10957 switch (sts) {
209030df
JH
10958 case RMS$_FNF: case RMS$_DNF:
10959 set_errno(ENOENT); break;
10960 case RMS$_DIR:
10961 set_errno(ENOTDIR); break;
10962 case RMS$_DEV:
10963 set_errno(ENODEV); break;
10964 case RMS$_PRV:
10965 set_errno(EACCES); break;
10966 case RMS$_SYN:
10967 set_errno(EINVAL); break;
10968 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10969 set_errno(E2BIG); break;
10970 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
ebd4d70b 10971 _ckvmssts_noperl(sts); /* fall through */
209030df
JH
10972 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10973 set_errno(EVMSERR);
c8795d8b
JH
10974 }
10975 set_vaxc_errno(sts);
10976 if (ckWARN(WARN_EXEC)) {
f98bc0c6 10977 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
c8795d8b
JH
10978 Strerror(errno));
10979 }
09b7f37c 10980 }
c8795d8b 10981 sts = substs;
48023aa8
CL
10982 }
10983 else {
eed5d6a1 10984 char mode[3];
2fbb330f 10985 PerlIO * fp;
eed5d6a1
CB
10986 if (flags & CLI$M_NOWAIT)
10987 strcpy(mode, "n");
10988 else
10989 strcpy(mode, "nW");
10990
10991 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
2fbb330f
JM
10992 if (fp != NULL)
10993 my_pclose(fp);
eed5d6a1 10994 /* sts will be the pid in the nowait case */
48023aa8 10995 }
48023aa8 10996 return sts;
eed5d6a1 10997} /* end of do_spawn2() */
a0d0e21e
LW
10998/*}}}*/
10999
bc10a425
CB
11000
11001static unsigned int *sockflags, sockflagsize;
11002
11003/*
11004 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11005 * routines found in some versions of the CRTL can't deal with sockets.
11006 * We don't shim the other file open routines since a socket isn't
11007 * likely to be opened by a name.
11008 */
275feba9
CB
11009/*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11010FILE *my_fdopen(int fd, const char *mode)
bc10a425 11011{
f7ddb74a 11012 FILE *fp = fdopen(fd, mode);
bc10a425
CB
11013
11014 if (fp) {
11015 unsigned int fdoff = fd / sizeof(unsigned int);
2497a41f 11016 Stat_t sbuf; /* native stat; we don't need flex_stat */
bc10a425
CB
11017 if (!sockflagsize || fdoff > sockflagsize) {
11018 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
a02a5408 11019 else Newx (sockflags,fdoff+2,unsigned int);
bc10a425
CB
11020 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11021 sockflagsize = fdoff + 2;
11022 }
312ac60b 11023 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
bc10a425
CB
11024 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11025 }
11026 return fp;
11027
11028}
11029/*}}}*/
11030
11031
11032/*
11033 * Clear the corresponding bit when the (possibly) socket stream is closed.
11034 * There still a small hole: we miss an implicit close which might occur
11035 * via freopen(). >> Todo
11036 */
11037/*{{{ int my_fclose(FILE *fp)*/
11038int my_fclose(FILE *fp) {
11039 if (fp) {
11040 unsigned int fd = fileno(fp);
11041 unsigned int fdoff = fd / sizeof(unsigned int);
11042
e0951028 11043 if (sockflagsize && fdoff < sockflagsize)
bc10a425
CB
11044 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11045 }
11046 return fclose(fp);
11047}
11048/*}}}*/
11049
11050
a0d0e21e
LW
11051/*
11052 * A simple fwrite replacement which outputs itmsz*nitm chars without
11053 * introducing record boundaries every itmsz chars.
22d4bb9c
CB
11054 * We are using fputs, which depends on a terminating null. We may
11055 * well be writing binary data, so we need to accommodate not only
11056 * data with nulls sprinkled in the middle but also data with no null
11057 * byte at the end.
a0d0e21e 11058 */
a15cef0c 11059/*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
a0d0e21e 11060int
a15cef0c 11061my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
a0d0e21e 11062{
eb578fdb 11063 char *cp, *end, *cpd;
2e05a54c 11064 char *data;
eb578fdb
KW
11065 unsigned int fd = fileno(dest);
11066 unsigned int fdoff = fd / sizeof(unsigned int);
22d4bb9c 11067 int retval;
bc10a425
CB
11068 int bufsize = itmsz * nitm + 1;
11069
11070 if (fdoff < sockflagsize &&
11071 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11072 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11073 return nitm;
11074 }
22d4bb9c 11075
bc10a425 11076 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
22d4bb9c
CB
11077 memcpy( data, src, itmsz*nitm );
11078 data[itmsz*nitm] = '\0';
a0d0e21e 11079
22d4bb9c
CB
11080 end = data + itmsz * nitm;
11081 retval = (int) nitm; /* on success return # items written */
a0d0e21e 11082
22d4bb9c
CB
11083 cpd = data;
11084 while (cpd <= end) {
11085 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11086 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
a0d0e21e 11087 if (cp < end)
22d4bb9c
CB
11088 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11089 cpd = cp + 1;
a0d0e21e
LW
11090 }
11091
bc10a425 11092 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
22d4bb9c 11093 return retval;
a0d0e21e
LW
11094
11095} /* end of my_fwrite() */
11096/*}}}*/
11097
d27fe803
JH
11098/*{{{ int my_flush(FILE *fp)*/
11099int
fd8cd3a3 11100Perl_my_flush(pTHX_ FILE *fp)
d27fe803
JH
11101{
11102 int res;
93948341 11103 if ((res = fflush(fp)) == 0 && fp) {
d27fe803 11104#ifdef VMS_DO_SOCKETS
61bb5906 11105 Stat_t s;
ed1b9de0 11106 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
d27fe803
JH
11107#endif
11108 res = fsync(fileno(fp));
11109 }
22d4bb9c
CB
11110/*
11111 * If the flush succeeded but set end-of-file, we need to clear
11112 * the error because our caller may check ferror(). BTW, this
11113 * probably means we just flushed an empty file.
11114 */
11115 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11116
d27fe803
JH
11117 return res;
11118}
11119/*}}}*/
11120
bf8d1304
JM
11121/* fgetname() is not returning the correct file specifications when
11122 * decc_filename_unix_report mode is active. So we have to have it
11123 * aways return filenames in VMS mode and convert it ourselves.
11124 */
11125
11126/*{{{ char * my_fgetname(FILE *fp, buf)*/
11127char *
11128Perl_my_fgetname(FILE *fp, char * buf) {
11129 char * retname;
11130 char * vms_name;
11131
11132 retname = fgetname(fp, buf, 1);
11133
11134 /* If we are in VMS mode, then we are done */
11135 if (!decc_filename_unix_report || (retname == NULL)) {
11136 return retname;
11137 }
11138
11139 /* Convert this to Unix format */
c11536f5 11140 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
a35dcc95 11141 my_strlcpy(vms_name, retname, VMS_MAXRSS);
bf8d1304
JM
11142 retname = int_tounixspec(vms_name, buf, NULL);
11143 PerlMem_free(vms_name);
11144
11145 return retname;
11146}
11147/*}}}*/
11148
748a9306
LW
11149/*
11150 * Here are replacements for the following Unix routines in the VMS environment:
11151 * getpwuid Get information for a particular UIC or UID
11152 * getpwnam Get information for a named user
11153 * getpwent Get information for each user in the rights database
11154 * setpwent Reset search to the start of the rights database
11155 * endpwent Finish searching for users in the rights database
11156 *
11157 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11158 * (defined in pwd.h), which contains the following fields:-
11159 * struct passwd {
11160 * char *pw_name; Username (in lower case)
11161 * char *pw_passwd; Hashed password
11162 * unsigned int pw_uid; UIC
11163 * unsigned int pw_gid; UIC group number
11164 * char *pw_unixdir; Default device/directory (VMS-style)
11165 * char *pw_gecos; Owner name
11166 * char *pw_dir; Default device/directory (Unix-style)
11167 * char *pw_shell; Default CLI name (eg. DCL)
11168 * };
11169 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11170 *
11171 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11172 * not the UIC member number (eg. what's returned by getuid()),
11173 * getpwuid() can accept either as input (if uid is specified, the caller's
11174 * UIC group is used), though it won't recognise gid=0.
11175 *
11176 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11177 * information about other users in your group or in other groups, respectively.
11178 * If the required privilege is not available, then these routines fill only
11179 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11180 * string).
11181 *
11182 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11183 */
11184
11185/* sizes of various UAF record fields */
11186#define UAI$S_USERNAME 12
11187#define UAI$S_IDENT 31
11188#define UAI$S_OWNER 31
11189#define UAI$S_DEFDEV 31
11190#define UAI$S_DEFDIR 63
11191#define UAI$S_DEFCLI 31
11192#define UAI$S_PWD 8
11193
11194#define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11195 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11196 (uic).uic$v_group != UIC$K_WILD_GROUP)
11197
4633a7c4
LW
11198static char __empty[]= "";
11199static struct passwd __passwd_empty=
748a9306
LW
11200 {(char *) __empty, (char *) __empty, 0, 0,
11201 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11202static int contxt= 0;
11203static struct passwd __pwdcache;
11204static char __pw_namecache[UAI$S_IDENT+1];
11205
748a9306
LW
11206/*
11207 * This routine does most of the work extracting the user information.
11208 */
fd8cd3a3 11209static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
a0d0e21e 11210{
748a9306
LW
11211 static struct {
11212 unsigned char length;
11213 char pw_gecos[UAI$S_OWNER+1];
11214 } owner;
11215 static union uicdef uic;
11216 static struct {
11217 unsigned char length;
11218 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11219 } defdev;
11220 static struct {
11221 unsigned char length;
11222 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11223 } defdir;
11224 static struct {
11225 unsigned char length;
11226 char pw_shell[UAI$S_DEFCLI+1];
11227 } defcli;
11228 static char pw_passwd[UAI$S_PWD+1];
11229
11230 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11231 struct dsc$descriptor_s name_desc;
c07a80fd 11232 unsigned long int sts;
748a9306 11233
4633a7c4 11234 static struct itmlst_3 itmlst[]= {
748a9306
LW
11235 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11236 {sizeof(uic), UAI$_UIC, &uic, &luic},
11237 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11238 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11239 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11240 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11241 {0, 0, NULL, NULL}};
11242
11243 name_desc.dsc$w_length= strlen(name);
11244 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11245 name_desc.dsc$b_class= DSC$K_CLASS_S;
f7ddb74a 11246 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
748a9306
LW
11247
11248/* Note that sys$getuai returns many fields as counted strings. */
c07a80fd 11249 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11250 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11251 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11252 }
11253 else { _ckvmssts(sts); }
11254 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
748a9306
LW
11255
11256 if ((int) owner.length < lowner) lowner= (int) owner.length;
11257 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11258 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11259 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11260 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11261 owner.pw_gecos[lowner]= '\0';
11262 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11263 defcli.pw_shell[ldefcli]= '\0';
11264 if (valid_uic(uic)) {
11265 pwd->pw_uid= uic.uic$l_uic;
11266 pwd->pw_gid= uic.uic$v_group;
11267 }
11268 else
5c84aa53 11269 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
748a9306
LW
11270 pwd->pw_passwd= pw_passwd;
11271 pwd->pw_gecos= owner.pw_gecos;
11272 pwd->pw_dir= defdev.pw_dir;
360732b5 11273 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
748a9306
LW
11274 pwd->pw_shell= defcli.pw_shell;
11275 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11276 int ldir;
11277 ldir= strlen(pwd->pw_unixdir) - 1;
11278 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11279 }
11280 else
a35dcc95 11281 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
f7ddb74a
JM
11282 if (!decc_efs_case_preserve)
11283 __mystrtolower(pwd->pw_unixdir);
c07a80fd 11284 return 1;
a0d0e21e 11285}
748a9306
LW
11286
11287/*
11288 * Get information for a named user.
11289*/
11290/*{{{struct passwd *getpwnam(char *name)*/
2fbb330f 11291struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
748a9306
LW
11292{
11293 struct dsc$descriptor_s name_desc;
11294 union uicdef uic;
4e0c9737 11295 unsigned long int sts;
748a9306
LW
11296
11297 __pwdcache = __passwd_empty;
fd8cd3a3 11298 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
748a9306
LW
11299 /* We still may be able to determine pw_uid and pw_gid */
11300 name_desc.dsc$w_length= strlen(name);
11301 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11302 name_desc.dsc$b_class= DSC$K_CLASS_S;
11303 name_desc.dsc$a_pointer= (char *) name;
aa689395 11304 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
748a9306
LW
11305 __pwdcache.pw_uid= uic.uic$l_uic;
11306 __pwdcache.pw_gid= uic.uic$v_group;
11307 }
c07a80fd 11308 else {
aa689395 11309 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11310 set_vaxc_errno(sts);
11311 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
c07a80fd 11312 return NULL;
11313 }
aa689395 11314 else { _ckvmssts(sts); }
c07a80fd 11315 }
748a9306 11316 }
a35dcc95 11317 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
748a9306
LW
11318 __pwdcache.pw_name= __pw_namecache;
11319 return &__pwdcache;
11320} /* end of my_getpwnam() */
a0d0e21e
LW
11321/*}}}*/
11322
748a9306
LW
11323/*
11324 * Get information for a particular UIC or UID.
11325 * Called by my_getpwent with uid=-1 to list all users.
11326*/
11327/*{{{struct passwd *my_getpwuid(Uid_t uid)*/
fd8cd3a3 11328struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
a0d0e21e 11329{
748a9306
LW
11330 const $DESCRIPTOR(name_desc,__pw_namecache);
11331 unsigned short lname;
11332 union uicdef uic;
11333 unsigned long int status;
11334
11335 if (uid == (unsigned int) -1) {
11336 do {
11337 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11338 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
c07a80fd 11339 set_vaxc_errno(status);
11340 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
748a9306
LW
11341 my_endpwent();
11342 return NULL;
11343 }
11344 else { _ckvmssts(status); }
11345 } while (!valid_uic (uic));
11346 }
11347 else {
11348 uic.uic$l_uic= uid;
c07a80fd 11349 if (!uic.uic$v_group)
76e3520e 11350 uic.uic$v_group= PerlProc_getgid();
748a9306
LW
11351 if (valid_uic(uic))
11352 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11353 else status = SS$_IVIDENT;
c07a80fd 11354 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11355 status == RMS$_PRV) {
11356 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11357 return NULL;
11358 }
11359 else { _ckvmssts(status); }
748a9306
LW
11360 }
11361 __pw_namecache[lname]= '\0';
01b8edb6 11362 __mystrtolower(__pw_namecache);
748a9306
LW
11363
11364 __pwdcache = __passwd_empty;
11365 __pwdcache.pw_name = __pw_namecache;
11366
11367/* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11368 The identifier's value is usually the UIC, but it doesn't have to be,
11369 so if we can, we let fillpasswd update this. */
11370 __pwdcache.pw_uid = uic.uic$l_uic;
11371 __pwdcache.pw_gid = uic.uic$v_group;
11372
fd8cd3a3 11373 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
748a9306 11374 return &__pwdcache;
a0d0e21e 11375
748a9306
LW
11376} /* end of my_getpwuid() */
11377/*}}}*/
11378
11379/*
11380 * Get information for next user.
11381*/
11382/*{{{struct passwd *my_getpwent()*/
fd8cd3a3 11383struct passwd *Perl_my_getpwent(pTHX)
748a9306
LW
11384{
11385 return (my_getpwuid((unsigned int) -1));
11386}
11387/*}}}*/
a0d0e21e 11388
748a9306
LW
11389/*
11390 * Finish searching rights database for users.
11391*/
11392/*{{{void my_endpwent()*/
fd8cd3a3 11393void Perl_my_endpwent(pTHX)
748a9306
LW
11394{
11395 if (contxt) {
11396 _ckvmssts(sys$finish_rdb(&contxt));
11397 contxt= 0;
11398 }
a0d0e21e
LW
11399}
11400/*}}}*/
748a9306 11401
ff0cee69 11402/* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11403 * my_utime(), and flex_stat(), all of which operate on UTC unless
11404 * VMSISH_TIMES is true.
11405 */
11406/* method used to handle UTC conversions:
11407 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
e518068a 11408 */
ff0cee69 11409static int gmtime_emulation_type;
11410/* number of secs to add to UTC POSIX-style time to get local time */
11411static long int utc_offset_secs;
e518068a 11412
ff0cee69 11413/* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11414 * in vmsish.h. #undef them here so we can call the CRTL routines
11415 * directly.
e518068a 11416 */
11417#undef gmtime
ff0cee69 11418#undef localtime
11419#undef time
11420
61bb5906
CB
11421
11422static time_t toutc_dst(time_t loc) {
11423 struct tm *rsltmp;
11424
f7c699a0 11425 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
61bb5906
CB
11426 loc -= utc_offset_secs;
11427 if (rsltmp->tm_isdst) loc -= 3600;
11428 return loc;
11429}
32da55ab 11430#define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11431 ((gmtime_emulation_type || my_time(NULL)), \
11432 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11433 ((secs) - utc_offset_secs))))
11434
11435static time_t toloc_dst(time_t utc) {
11436 struct tm *rsltmp;
11437
11438 utc += utc_offset_secs;
f7c699a0 11439 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
61bb5906
CB
11440 if (rsltmp->tm_isdst) utc += 3600;
11441 return utc;
11442}
32da55ab 11443#define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
61bb5906
CB
11444 ((gmtime_emulation_type || my_time(NULL)), \
11445 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11446 ((secs) + utc_offset_secs))))
11447
ff0cee69 11448/* my_time(), my_localtime(), my_gmtime()
61bb5906 11449 * By default traffic in UTC time values, using CRTL gmtime() or
ff0cee69 11450 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
61bb5906
CB
11451 * Note: We need to use these functions even when the CRTL has working
11452 * UTC support, since they also handle C<use vmsish qw(times);>
11453 *
ff0cee69 11454 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
bd3fa61c 11455 * Modified by Charles Bailey <bailey@newman.upenn.edu>
ff0cee69 11456 */
11457
11458/*{{{time_t my_time(time_t *timep)*/
fd8cd3a3 11459time_t Perl_my_time(pTHX_ time_t *timep)
e518068a 11460{
e518068a 11461 time_t when;
61bb5906 11462 struct tm *tm_p;
e518068a 11463
11464 if (gmtime_emulation_type == 0) {
61bb5906
CB
11465 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11466 /* results of calls to gmtime() and localtime() */
11467 /* for same &base */
ff0cee69 11468
e518068a 11469 gmtime_emulation_type++;
ff0cee69 11470 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
f675dbe5 11471 char off[LNM$C_NAMLENGTH+1];;
ff0cee69 11472
e518068a 11473 gmtime_emulation_type++;
f675dbe5 11474 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
e518068a 11475 gmtime_emulation_type++;
22d4bb9c 11476 utc_offset_secs = 0;
5c84aa53 11477 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
ff0cee69 11478 }
11479 else { utc_offset_secs = atol(off); }
e518068a 11480 }
ff0cee69 11481 else { /* We've got a working gmtime() */
11482 struct tm gmt, local;
e518068a 11483
ff0cee69 11484 gmt = *tm_p;
11485 tm_p = localtime(&base);
11486 local = *tm_p;
11487 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11488 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11489 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11490 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11491 }
e518068a 11492 }
ff0cee69 11493
11494 when = time(NULL);
61bb5906 11495# ifdef VMSISH_TIME
61bb5906 11496 if (VMSISH_TIME) when = _toloc(when);
61bb5906 11497# endif
ff0cee69 11498 if (timep != NULL) *timep = when;
11499 return when;
11500
11501} /* end of my_time() */
11502/*}}}*/
11503
11504
11505/*{{{struct tm *my_gmtime(const time_t *timep)*/
11506struct tm *
fd8cd3a3 11507Perl_my_gmtime(pTHX_ const time_t *timep)
ff0cee69 11508{
ff0cee69 11509 time_t when;
61bb5906 11510 struct tm *rsltmp;
ff0cee69 11511
68dc0745 11512 if (timep == NULL) {
11513 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11514 return NULL;
11515 }
11516 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 11517
11518 when = *timep;
11519# ifdef VMSISH_TIME
61bb5906
CB
11520 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11521# endif
61bb5906 11522 return gmtime(&when);
e518068a 11523} /* end of my_gmtime() */
e518068a 11524/*}}}*/
11525
11526
ff0cee69 11527/*{{{struct tm *my_localtime(const time_t *timep)*/
11528struct tm *
fd8cd3a3 11529Perl_my_localtime(pTHX_ const time_t *timep)
ff0cee69 11530{
c11536f5 11531 time_t when;
ff0cee69 11532
68dc0745 11533 if (timep == NULL) {
11534 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11535 return NULL;
11536 }
11537 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
f7ddb74a 11538 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
ff0cee69 11539
11540 when = *timep;
11541# ifdef VMSISH_TIME
61bb5906 11542 if (VMSISH_TIME) when = _toutc(when);
ff0cee69 11543# endif
61bb5906 11544 /* CRTL localtime() wants UTC as input, does tz correction itself */
ff0cee69 11545 return localtime(&when);
ff0cee69 11546} /* end of my_localtime() */
11547/*}}}*/
11548
11549/* Reset definitions for later calls */
11550#define gmtime(t) my_gmtime(t)
11551#define localtime(t) my_localtime(t)
11552#define time(t) my_time(t)
11553
11554
941b3de1
CB
11555/* my_utime - update modification/access time of a file
11556 *
11557 * VMS 7.3 and later implementation
11558 * Only the UTC translation is home-grown. The rest is handled by the
11559 * CRTL utime(), which will take into account the relevant feature
11560 * logicals and ODS-5 volume characteristics for true access times.
11561 *
11562 * pre VMS 7.3 implementation:
11563 * The calling sequence is identical to POSIX utime(), but under
11564 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11565 * not maintain access times. Restrictions differ from the POSIX
ff0cee69 11566 * definition in that the time can be changed as long as the
11567 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11568 * no separate checks are made to insure that the caller is the
11569 * owner of the file or has special privs enabled.
11570 * Code here is based on Joe Meadows' FILE utility.
941b3de1 11571 *
ff0cee69 11572 */
11573
11574/* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11575 * to VMS epoch (01-JAN-1858 00:00:00.00)
11576 * in 100 ns intervals.
11577 */
11578static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11579
94a11853
CB
11580/*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11581int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
ff0cee69 11582{
941b3de1
CB
11583#if __CRTL_VER >= 70300000
11584 struct utimbuf utc_utimes, *utc_utimesp;
11585
11586 if (utimes != NULL) {
11587 utc_utimes.actime = utimes->actime;
11588 utc_utimes.modtime = utimes->modtime;
11589# ifdef VMSISH_TIME
11590 /* If input was local; convert to UTC for sys svc */
11591 if (VMSISH_TIME) {
11592 utc_utimes.actime = _toutc(utimes->actime);
11593 utc_utimes.modtime = _toutc(utimes->modtime);
11594 }
11595# endif
11596 utc_utimesp = &utc_utimes;
11597 }
11598 else {
11599 utc_utimesp = NULL;
11600 }
11601
11602 return utime(file, utc_utimesp);
11603
11604#else /* __CRTL_VER < 70300000 */
11605
eb578fdb 11606 int i;
f7ddb74a 11607 int sts;
ff0cee69 11608 long int bintime[2], len = 2, lowbit, unixtime,
11609 secscale = 10000000; /* seconds --> 100 ns intervals */
11610 unsigned long int chan, iosb[2], retsts;
11611 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11612 struct FAB myfab = cc$rms_fab;
11613 struct NAM mynam = cc$rms_nam;
11614#if defined (__DECC) && defined (__VAX)
11615 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11616 * at least through VMS V6.1, which causes a type-conversion warning.
11617 */
11618# pragma message save
11619# pragma message disable cvtdiftypes
11620#endif
11621 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11622 struct fibdef myfib;
11623#if defined (__DECC) && defined (__VAX)
11624 /* This should be right after the declaration of myatr, but due
11625 * to a bug in VAX DEC C, this takes effect a statement early.
11626 */
11627# pragma message restore
11628#endif
f7ddb74a 11629 /* cast ok for read only parameter */
ff0cee69 11630 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11631 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11632 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
704c2eb3 11633
ff0cee69 11634 if (file == NULL || *file == '\0') {
941b3de1 11635 SETERRNO(ENOENT, LIB$_INVARG);
ff0cee69 11636 return -1;
11637 }
704c2eb3
JM
11638
11639 /* Convert to VMS format ensuring that it will fit in 255 characters */
6fb6c614 11640 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
941b3de1
CB
11641 SETERRNO(ENOENT, LIB$_INVARG);
11642 return -1;
11643 }
ff0cee69 11644 if (utimes != NULL) {
11645 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11646 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11647 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11648 * as input, we force the sign bit to be clear by shifting unixtime right
11649 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11650 */
11651 lowbit = (utimes->modtime & 1) ? secscale : 0;
11652 unixtime = (long int) utimes->modtime;
61bb5906
CB
11653# ifdef VMSISH_TIME
11654 /* If input was UTC; convert to local for sys svc */
11655 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
ff0cee69 11656# endif
1a6334fb 11657 unixtime >>= 1; secscale <<= 1;
ff0cee69 11658 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11659 if (!(retsts & 1)) {
941b3de1 11660 SETERRNO(EVMSERR, retsts);
ff0cee69 11661 return -1;
11662 }
11663 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11664 if (!(retsts & 1)) {
941b3de1 11665 SETERRNO(EVMSERR, retsts);
ff0cee69 11666 return -1;
11667 }
11668 }
11669 else {
11670 /* Just get the current time in VMS format directly */
11671 retsts = sys$gettim(bintime);
11672 if (!(retsts & 1)) {
941b3de1 11673 SETERRNO(EVMSERR, retsts);
ff0cee69 11674 return -1;
11675 }
11676 }
11677
11678 myfab.fab$l_fna = vmsspec;
11679 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11680 myfab.fab$l_nam = &mynam;
11681 mynam.nam$l_esa = esa;
11682 mynam.nam$b_ess = (unsigned char) sizeof esa;
11683 mynam.nam$l_rsa = rsa;
11684 mynam.nam$b_rss = (unsigned char) sizeof rsa;
f7ddb74a
JM
11685 if (decc_efs_case_preserve)
11686 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
ff0cee69 11687
11688 /* Look for the file to be affected, letting RMS parse the file
11689 * specification for us as well. I have set errno using only
11690 * values documented in the utime() man page for VMS POSIX.
11691 */
11692 retsts = sys$parse(&myfab,0,0);
11693 if (!(retsts & 1)) {
11694 set_vaxc_errno(retsts);
11695 if (retsts == RMS$_PRV) set_errno(EACCES);
11696 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11697 else set_errno(EVMSERR);
11698 return -1;
11699 }
11700 retsts = sys$search(&myfab,0,0);
11701 if (!(retsts & 1)) {
752635ea 11702 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 11703 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 11704 set_vaxc_errno(retsts);
11705 if (retsts == RMS$_PRV) set_errno(EACCES);
11706 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11707 else set_errno(EVMSERR);
11708 return -1;
11709 }
11710
11711 devdsc.dsc$w_length = mynam.nam$b_dev;
f7ddb74a 11712 /* cast ok for read only parameter */
ff0cee69 11713 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11714
11715 retsts = sys$assign(&devdsc,&chan,0,0);
11716 if (!(retsts & 1)) {
752635ea 11717 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 11718 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 11719 set_vaxc_errno(retsts);
11720 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11721 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11722 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11723 else set_errno(EVMSERR);
11724 return -1;
11725 }
11726
11727 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11728 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11729
11730 memset((void *) &myfib, 0, sizeof myfib);
22d4bb9c 11731#if defined(__DECC) || defined(__DECCXX)
ff0cee69 11732 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11733 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11734 /* This prevents the revision time of the file being reset to the current
11735 * time as a result of our IO$_MODIFY $QIO. */
11736 myfib.fib$l_acctl = FIB$M_NORECORD;
11737#else
11738 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11739 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11740 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11741#endif
11742 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
752635ea 11743 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
f7ddb74a 11744 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
ff0cee69 11745 _ckvmssts(sys$dassgn(chan));
11746 if (retsts & 1) retsts = iosb[0];
11747 if (!(retsts & 1)) {
11748 set_vaxc_errno(retsts);
11749 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11750 else set_errno(EVMSERR);
11751 return -1;
11752 }
11753
11754 return 0;
941b3de1
CB
11755
11756#endif /* #if __CRTL_VER >= 70300000 */
11757
ff0cee69 11758} /* end of my_utime() */
11759/*}}}*/
11760
748a9306 11761/*
2497a41f 11762 * flex_stat, flex_lstat, flex_fstat
748a9306
LW
11763 * basic stat, but gets it right when asked to stat
11764 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11765 */
11766
2497a41f 11767#ifndef _USE_STD_STAT
748a9306
LW
11768/* encode_dev packs a VMS device name string into an integer to allow
11769 * simple comparisons. This can be used, for example, to check whether two
11770 * files are located on the same device, by comparing their encoded device
11771 * names. Even a string comparison would not do, because stat() reuses the
11772 * device name buffer for each call; so without encode_dev, it would be
11773 * necessary to save the buffer and use strcmp (this would mean a number of
11774 * changes to the standard Perl code, to say nothing of what a Perl script
11775 * would have to do.
11776 *
11777 * The device lock id, if it exists, should be unique (unless perhaps compared
11778 * with lock ids transferred from other nodes). We have a lock id if the disk is
11779 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11780 * device names. Thus we use the lock id in preference, and only if that isn't
11781 * available, do we try to pack the device name into an integer (flagged by
11782 * the sign bit (LOCKID_MASK) being set).
11783 *
e518068a 11784 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
748a9306
LW
11785 * name and its encoded form, but it seems very unlikely that we will find
11786 * two files on different disks that share the same encoded device names,
11787 * and even more remote that they will share the same file id (if the test
11788 * is to check for the same file).
11789 *
11790 * A better method might be to use sys$device_scan on the first call, and to
11791 * search for the device, returning an index into the cached array.
cb9e088c 11792 * The number returned would be more intelligible.
748a9306
LW
11793 * This is probably not worth it, and anyway would take quite a bit longer
11794 * on the first call.
11795 */
11796#define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
fd8cd3a3 11797static mydev_t encode_dev (pTHX_ const char *dev)
748a9306
LW
11798{
11799 int i;
11800 unsigned long int f;
aa689395 11801 mydev_t enc;
748a9306
LW
11802 char c;
11803 const char *q;
11804
11805 if (!dev || !dev[0]) return 0;
11806
11807#if LOCKID_MASK
11808 {
11809 struct dsc$descriptor_s dev_desc;
cb9e088c 11810 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
748a9306
LW
11811
11812 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11813 can try that first. */
11814 dev_desc.dsc$w_length = strlen (dev);
11815 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11816 dev_desc.dsc$b_class = DSC$K_CLASS_S;
f7ddb74a 11817 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
cb9e088c 11818 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
360732b5 11819 if (!$VMS_STATUS_SUCCESS(status)) {
cb9e088c
CB
11820 switch (status) {
11821 case SS$_NOSUCHDEV:
11822 SETERRNO(ENODEV, status);
11823 return 0;
11824 default:
11825 _ckvmssts(status);
11826 }
11827 }
748a9306
LW
11828 if (lockid) return (lockid & ~LOCKID_MASK);
11829 }
a0d0e21e 11830#endif
748a9306
LW
11831
11832 /* Otherwise we try to encode the device name */
11833 enc = 0;
11834 f = 1;
11835 i = 0;
11836 for (q = dev + strlen(dev); q--; q >= dev) {
988c775c
JM
11837 if (*q == ':')
11838 break;
748a9306
LW
11839 if (isdigit (*q))
11840 c= (*q) - '0';
11841 else if (isalpha (toupper (*q)))
11842 c= toupper (*q) - 'A' + (char)10;
11843 else
11844 continue; /* Skip '$'s */
11845 i++;
11846 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11847 if (i>1) f *= 36;
11848 enc += f * (unsigned long int) c;
11849 }
11850 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11851
11852} /* end of encode_dev() */
cfcfe586
JM
11853#define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11854 device_no = encode_dev(aTHX_ devname)
11855#else
11856#define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11857 device_no = new_dev_no
2497a41f 11858#endif
748a9306 11859
748a9306 11860static int
135577da 11861is_null_device(const char *name)
748a9306 11862{
2497a41f 11863 if (decc_bug_devnull != 0) {
682e4b71 11864 if (strncmp("/dev/null", name, 9) == 0)
2497a41f
JM
11865 return 1;
11866 }
748a9306
LW
11867 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11868 The underscore prefix, controller letter, and unit number are
11869 independently optional; for our purposes, the colon punctuation
11870 is not. The colon can be trailed by optional directory and/or
11871 filename, but two consecutive colons indicates a nodename rather
11872 than a device. [pr] */
11873 if (*name == '_') ++name;
11874 if (tolower(*name++) != 'n') return 0;
11875 if (tolower(*name++) != 'l') return 0;
11876 if (tolower(*name) == 'a') ++name;
11877 if (*name == '0') ++name;
11878 return (*name++ == ':') && (*name != ':');
11879}
11880
312ac60b
JM
11881static int
11882Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
c07a80fd 11883
46c05374
CB
11884#define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11885
a1887106
JM
11886static I32
11887Perl_cando_by_name_int
11888 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
748a9306 11889{
e538e23f
CB
11890 char usrname[L_cuserid];
11891 struct dsc$descriptor_s usrdsc =
748a9306 11892 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
e538e23f 11893 char *vmsname = NULL, *fileified = NULL;
597c27e2 11894 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
2d9f3838 11895 unsigned short int retlen, trnlnm_iter_count;
748a9306
LW
11896 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11897 union prvdef curprv;
597c27e2
CB
11898 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11899 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11900 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
ada67d10
CB
11901 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11902 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11903 {0,0,0,0}};
11904 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
748a9306 11905 {0,0,0,0}};
ada67d10 11906 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
858aded6 11907 Stat_t st;
6151c65c 11908 static int profile_context = -1;
748a9306
LW
11909
11910 if (!fname || !*fname) return FALSE;
a1887106 11911
e538e23f 11912 /* Make sure we expand logical names, since sys$check_access doesn't */
c11536f5 11913 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 11914 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f 11915 if (!strpbrk(fname,"/]>:")) {
a35dcc95 11916 my_strlcpy(fileified, fname, VMS_MAXRSS);
a1887106 11917 trnlnm_iter_count = 0;
e538e23f 11918 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
2d9f3838
CB
11919 trnlnm_iter_count++;
11920 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
a1887106
JM
11921 }
11922 fname = fileified;
e538e23f
CB
11923 }
11924
c11536f5 11925 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 11926 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
e538e23f
CB
11927 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11928 /* Don't know if already in VMS format, so make sure */
360732b5 11929 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
a1887106 11930 PerlMem_free(fileified);
e538e23f 11931 PerlMem_free(vmsname);
a1887106
JM
11932 return FALSE;
11933 }
a1887106
JM
11934 }
11935 else {
a35dcc95 11936 my_strlcpy(vmsname, fname, VMS_MAXRSS);
a5f75d66
AD
11937 }
11938
858aded6 11939 /* sys$check_access needs a file spec, not a directory spec.
312ac60b 11940 * flex_stat now will handle a null thread context during startup.
858aded6 11941 */
e538e23f
CB
11942
11943 retlen = namdsc.dsc$w_length = strlen(vmsname);
11944 if (vmsname[retlen-1] == ']'
11945 || vmsname[retlen-1] == '>'
858aded6 11946 || vmsname[retlen-1] == ':'
46c05374 11947 || (!flex_stat_int(vmsname, &st, 1) &&
312ac60b 11948 S_ISDIR(st.st_mode))) {
e538e23f 11949
a979ce91 11950 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
e538e23f
CB
11951 PerlMem_free(fileified);
11952 PerlMem_free(vmsname);
11953 return FALSE;
11954 }
11955 fname = fileified;
11956 }
858aded6
CB
11957 else {
11958 fname = vmsname;
11959 }
e538e23f
CB
11960
11961 retlen = namdsc.dsc$w_length = strlen(fname);
11962 namdsc.dsc$a_pointer = (char *)fname;
11963
748a9306 11964 switch (bit) {
f282b18d 11965 case S_IXUSR: case S_IXGRP: case S_IXOTH:
360732b5 11966 access = ARM$M_EXECUTE;
597c27e2
CB
11967 flags = CHP$M_READ;
11968 break;
f282b18d 11969 case S_IRUSR: case S_IRGRP: case S_IROTH:
360732b5 11970 access = ARM$M_READ;
597c27e2
CB
11971 flags = CHP$M_READ | CHP$M_USEREADALL;
11972 break;
f282b18d 11973 case S_IWUSR: case S_IWGRP: case S_IWOTH:
360732b5 11974 access = ARM$M_WRITE;
597c27e2
CB
11975 flags = CHP$M_READ | CHP$M_WRITE;
11976 break;
f282b18d 11977 case S_IDUSR: case S_IDGRP: case S_IDOTH:
360732b5 11978 access = ARM$M_DELETE;
597c27e2
CB
11979 flags = CHP$M_READ | CHP$M_WRITE;
11980 break;
748a9306 11981 default:
a1887106
JM
11982 if (fileified != NULL)
11983 PerlMem_free(fileified);
e538e23f
CB
11984 if (vmsname != NULL)
11985 PerlMem_free(vmsname);
748a9306
LW
11986 return FALSE;
11987 }
11988
ada67d10
CB
11989 /* Before we call $check_access, create a user profile with the current
11990 * process privs since otherwise it just uses the default privs from the
baf3cf9c
CB
11991 * UAF and might give false positives or negatives. This only works on
11992 * VMS versions v6.0 and later since that's when sys$create_user_profile
11993 * became available.
ada67d10
CB
11994 */
11995
11996 /* get current process privs and username */
ebd4d70b
JM
11997 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11998 _ckvmssts_noperl(iosb[0]);
ada67d10
CB
11999
12000 /* find out the space required for the profile */
ebd4d70b 12001 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
6151c65c 12002 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12003
12004 /* allocate space for the profile and get it filled in */
c11536f5 12005 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
ebd4d70b
JM
12006 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12007 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
6151c65c 12008 &usrprodsc.dsc$w_length,&profile_context));
ada67d10
CB
12009
12010 /* use the profile to check access to the file; free profile & analyze results */
6151c65c 12011 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
c5375c28 12012 PerlMem_free(usrprodsc.dsc$a_pointer);
ada67d10 12013 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
baf3cf9c 12014
bbce6d69 12015 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
61bb5906 12016 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
22d4bb9c 12017 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
bbce6d69 12018 set_vaxc_errno(retsts);
12019 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12020 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12021 else set_errno(ENOENT);
a1887106
JM
12022 if (fileified != NULL)
12023 PerlMem_free(fileified);
e538e23f
CB
12024 if (vmsname != NULL)
12025 PerlMem_free(vmsname);
a3e9d8c9 12026 return FALSE;
12027 }
ada67d10 12028 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
a1887106
JM
12029 if (fileified != NULL)
12030 PerlMem_free(fileified);
e538e23f
CB
12031 if (vmsname != NULL)
12032 PerlMem_free(vmsname);
3a385817
GS
12033 return TRUE;
12034 }
ebd4d70b 12035 _ckvmssts_noperl(retsts);
748a9306 12036
a1887106
JM
12037 if (fileified != NULL)
12038 PerlMem_free(fileified);
e538e23f
CB
12039 if (vmsname != NULL)
12040 PerlMem_free(vmsname);
748a9306
LW
12041 return FALSE; /* Should never get here */
12042
a1887106
JM
12043}
12044
12045/* Do the permissions allow some operation? Assumes PL_statcache already set. */
12046/* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12047 * subset of the applicable information.
12048 */
12049bool
12050Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12051{
12052 return cando_by_name_int
12053 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12054} /* end of cando() */
12055/*}}}*/
12056
12057
12058/*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12059I32
12060Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12061{
12062 return cando_by_name_int(bit, effective, fname, 0);
12063
748a9306
LW
12064} /* end of cando_by_name() */
12065/*}}}*/
12066
12067
61bb5906 12068/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
748a9306 12069int
fd8cd3a3 12070Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
748a9306 12071{
312ac60b 12072 if (!fstat(fd, &statbufp->crtl_stat)) {
75796008 12073 char *cptr;
988c775c 12074 char *vms_filename;
c11536f5 12075 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
988c775c 12076 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
75796008 12077
988c775c
JM
12078 /* Save name for cando by name in VMS format */
12079 cptr = getname(fd, vms_filename, 1);
75796008 12080
988c775c
JM
12081 /* This should not happen, but just in case */
12082 if (cptr == NULL) {
12083 statbufp->st_devnam[0] = 0;
12084 }
12085 else {
12086 /* Make sure that the saved name fits in 255 characters */
6fb6c614 12087 cptr = int_rmsexpand_vms
988c775c
JM
12088 (vms_filename,
12089 statbufp->st_devnam,
6fb6c614 12090 0);
75796008 12091 if (cptr == NULL)
988c775c 12092 statbufp->st_devnam[0] = 0;
75796008 12093 }
988c775c 12094 PerlMem_free(vms_filename);
682e4b71
JM
12095
12096 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12097 VMS_DEVICE_ENCODE
12098 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
2497a41f 12099
61bb5906
CB
12100# ifdef VMSISH_TIME
12101 if (VMSISH_TIME) {
12102 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12103 statbufp->st_atime = _toloc(statbufp->st_atime);
12104 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12105 }
12106# endif
b7ae7a0d 12107 return 0;
12108 }
12109 return -1;
748a9306
LW
12110
12111} /* end of flex_fstat() */
12112/*}}}*/
12113
2497a41f
JM
12114static int
12115Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
748a9306 12116{
9b9f19da
CB
12117 char *temp_fspec = NULL;
12118 char *fileified = NULL;
312ac60b
JM
12119 const char *save_spec;
12120 char *ret_spec;
bbce6d69 12121 int retval = -1;
cc5de3bd
CB
12122 char efs_hack = 0;
12123 char already_fileified = 0;
4ee39169 12124 dSAVEDERRNO;
748a9306 12125
312ac60b
JM
12126 if (!fspec) {
12127 errno = EINVAL;
12128 return retval;
12129 }
988c775c 12130
2497a41f 12131 if (decc_bug_devnull != 0) {
312ac60b 12132 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
2497a41f 12133 memset(statbufp,0,sizeof *statbufp);
cfcfe586 12134 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
2497a41f
JM
12135 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12136 statbufp->st_uid = 0x00010001;
12137 statbufp->st_gid = 0x0001;
12138 time((time_t *)&statbufp->st_mtime);
12139 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12140 return 0;
12141 }
748a9306
LW
12142 }
12143
9b9f19da
CB
12144 SAVE_ERRNO;
12145
12146#if __CRTL_VER >= 80200000 && !defined(__VAX)
12147 /*
12148 * If we are in POSIX filespec mode, accept the filename as is.
12149 */
12150 if (decc_posix_compliant_pathnames == 0) {
12151#endif
12152
12153 /* Try for a simple stat first. If fspec contains a filename without
61bb5906 12154 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
9b9f19da 12155 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
bbce6d69 12156 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12157 * not sea:[wine.dark]., if the latter exists. If the intended target is
12158 * the file with null type, specify this by calling flex_stat() with
12159 * a '.' at the end of fspec.
12160 */
f36b279d 12161
9b9f19da
CB
12162 if (lstat_flag == 0)
12163 retval = stat(fspec, &statbufp->crtl_stat);
12164 else
12165 retval = lstat(fspec, &statbufp->crtl_stat);
f36b279d 12166
cc5de3bd
CB
12167 if (!retval) {
12168 save_spec = fspec;
12169 }
12170 else {
12171 /* In the odd case where we have write but not read access
12172 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12173 */
c11536f5 12174 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
cc5de3bd
CB
12175 if (fileified == NULL)
12176 _ckvmssts_noperl(SS$_INSFMEM);
12177
12178 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12179 if (ret_spec != NULL) {
12180 if (lstat_flag == 0)
12181 retval = stat(fileified, &statbufp->crtl_stat);
12182 else
12183 retval = lstat(fileified, &statbufp->crtl_stat);
12184 save_spec = fileified;
12185 already_fileified = 1;
12186 }
12187 }
12188
312ac60b
JM
12189 if (retval && vms_bug_stat_filename) {
12190
c11536f5 12191 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9b9f19da
CB
12192 if (temp_fspec == NULL)
12193 _ckvmssts_noperl(SS$_INSFMEM);
12194
12195 /* We should try again as a vmsified file specification. */
312ac60b
JM
12196
12197 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12198 if (ret_spec != NULL) {
12199 if (lstat_flag == 0)
12200 retval = stat(temp_fspec, &statbufp->crtl_stat);
12201 else
12202 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12203 save_spec = temp_fspec;
12204 }
2497a41f 12205 }
312ac60b 12206
f1db9cda 12207 if (retval) {
9b9f19da 12208 /* Last chance - allow multiple dots without EFS CHARSET */
312ac60b
JM
12209 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12210 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12211 * enable it if it isn't already.
12212 */
12213#if __CRTL_VER >= 70300000 && !defined(__VAX)
12214 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12215 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12216#endif
12217 if (lstat_flag == 0)
12218 retval = stat(fspec, &statbufp->crtl_stat);
12219 else
12220 retval = lstat(fspec, &statbufp->crtl_stat);
12221 save_spec = fspec;
12222#if __CRTL_VER >= 70300000 && !defined(__VAX)
12223 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12224 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12225 efs_hack = 1;
12226 }
12227#endif
f1db9cda 12228 }
312ac60b 12229
2497a41f
JM
12230#if __CRTL_VER >= 80200000 && !defined(__VAX)
12231 } else {
12232 if (lstat_flag == 0)
312ac60b 12233 retval = stat(temp_fspec, &statbufp->crtl_stat);
2497a41f 12234 else
312ac60b 12235 retval = lstat(temp_fspec, &statbufp->crtl_stat);
988c775c 12236 save_spec = temp_fspec;
2497a41f
JM
12237 }
12238#endif
f36b279d
CB
12239
12240#if __CRTL_VER >= 70300000 && !defined(__VAX)
12241 /* As you were... */
12242 if (!decc_efs_charset)
12243 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12244#endif
12245
ff0cee69 12246 if (!retval) {
9b9f19da
CB
12247 char *cptr;
12248 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
d584a1c6
JM
12249
12250 /* If this is an lstat, do not follow the link */
12251 if (lstat_flag)
12252 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12253
312ac60b
JM
12254#if __CRTL_VER >= 70300000 && !defined(__VAX)
12255 /* If we used the efs_hack above, we must also use it here for */
12256 /* perl_cando to work */
12257 if (efs_hack && (decc_efs_charset_index > 0)) {
12258 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12259 }
12260#endif
9b9f19da
CB
12261
12262 /* If we've got a directory, save a fileified, expanded version of it
12263 * in st_devnam. If not a directory, just an expanded version.
12264 */
cc5de3bd 12265 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
c11536f5 12266 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
9b9f19da
CB
12267 if (fileified == NULL)
12268 _ckvmssts_noperl(SS$_INSFMEM);
12269
12270 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12271 if (cptr != NULL)
12272 save_spec = fileified;
12273 }
12274
12275 cptr = int_rmsexpand(save_spec,
12276 statbufp->st_devnam,
12277 NULL,
12278 rmsex_flags,
12279 0,
12280 0);
12281
312ac60b
JM
12282#if __CRTL_VER >= 70300000 && !defined(__VAX)
12283 if (efs_hack && (decc_efs_charset_index > 0)) {
12284 decc$feature_set_value(decc_efs_charset, 1, 0);
12285 }
12286#endif
12287
12288 /* Fix me: If this is NULL then stat found a file, and we could */
12289 /* not convert the specification to VMS - Should never happen */
988c775c
JM
12290 if (cptr == NULL)
12291 statbufp->st_devnam[0] = 0;
12292
682e4b71 12293 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
cfcfe586
JM
12294 VMS_DEVICE_ENCODE
12295 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
61bb5906
CB
12296# ifdef VMSISH_TIME
12297 if (VMSISH_TIME) {
12298 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12299 statbufp->st_atime = _toloc(statbufp->st_atime);
12300 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12301 }
12302# endif
ff0cee69 12303 }
9543c6b6 12304 /* If we were successful, leave errno where we found it */
4ee39169 12305 if (retval == 0) RESTORE_ERRNO;
9b9f19da
CB
12306 if (temp_fspec)
12307 PerlMem_free(temp_fspec);
12308 if (fileified)
12309 PerlMem_free(fileified);
748a9306
LW
12310 return retval;
12311
2497a41f
JM
12312} /* end of flex_stat_int() */
12313
12314
12315/*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12316int
12317Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12318{
7ded3206 12319 return flex_stat_int(fspec, statbufp, 0);
2497a41f
JM
12320}
12321/*}}}*/
12322
12323/*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12324int
12325Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12326{
7ded3206 12327 return flex_stat_int(fspec, statbufp, 1);
2497a41f 12328}
748a9306
LW
12329/*}}}*/
12330
b7ae7a0d 12331
c07a80fd 12332/*{{{char *my_getlogin()*/
12333/* VMS cuserid == Unix getlogin, except calling sequence */
12334char *
2fbb330f 12335my_getlogin(void)
c07a80fd 12336{
12337 static char user[L_cuserid];
12338 return cuserid(user);
12339}
12340/*}}}*/
12341
12342
a5f75d66
AD
12343/* rmscopy - copy a file using VMS RMS routines
12344 *
12345 * Copies contents and attributes of spec_in to spec_out, except owner
12346 * and protection information. Name and type of spec_in are used as
a3e9d8c9 12347 * defaults for spec_out. The third parameter specifies whether rmscopy()
12348 * should try to propagate timestamps from the input file to the output file.
12349 * If it is less than 0, no timestamps are preserved. If it is 0, then
12350 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12351 * propagated to the output file at creation iff the output file specification
12352 * did not contain an explicit name or type, and the revision date is always
12353 * updated at the end of the copy operation. If it is greater than 0, then
12354 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12355 * other than the revision date should be propagated, and bit 1 indicates
12356 * that the revision date should be propagated.
12357 *
12358 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
a5f75d66 12359 *
bd3fa61c 12360 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
a5f75d66 12361 * Incorporates, with permission, some code from EZCOPY by Tim Adye
01b8edb6 12362 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12363 * as part of the Perl standard distribution under the terms of the
12364 * GNU General Public License or the Perl Artistic License. Copies
12365 * of each may be found in the Perl standard distribution.
a480973c 12366 */ /* FIXME */
a3e9d8c9 12367/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
a480973c
JM
12368int
12369Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12370{
d584a1c6
JM
12371 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12372 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
4e0c9737 12373 unsigned long int sts;
a1887106 12374 int dna_len;
a480973c
JM
12375 struct FAB fab_in, fab_out;
12376 struct RAB rab_in, rab_out;
a1887106
JM
12377 rms_setup_nam(nam);
12378 rms_setup_nam(nam_out);
a480973c
JM
12379 struct XABDAT xabdat;
12380 struct XABFHC xabfhc;
12381 struct XABRDT xabrdt;
12382 struct XABSUM xabsum;
12383
c11536f5 12384 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12385 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 12386 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12387 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
df278665
JM
12388 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12389 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
c5375c28
JM
12390 PerlMem_free(vmsin);
12391 PerlMem_free(vmsout);
a480973c
JM
12392 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12393 return 0;
12394 }
12395
c11536f5 12396 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12397 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12398 esal = NULL;
12399#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 12400 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12401 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6 12402#endif
a480973c 12403 fab_in = cc$rms_fab;
a1887106 12404 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
a480973c
JM
12405 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12406 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12407 fab_in.fab$l_fop = FAB$M_SQO;
a1887106 12408 rms_bind_fab_nam(fab_in, nam);
a480973c
JM
12409 fab_in.fab$l_xab = (void *) &xabdat;
12410
c11536f5 12411 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12412 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12413 rsal = NULL;
12414#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 12415 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12416 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12417#endif
12418 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12419 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
a1887106
JM
12420 rms_nam_esl(nam) = 0;
12421 rms_nam_rsl(nam) = 0;
12422 rms_nam_esll(nam) = 0;
12423 rms_nam_rsll(nam) = 0;
a480973c
JM
12424#ifdef NAM$M_NO_SHORT_UPCASE
12425 if (decc_efs_case_preserve)
a1887106 12426 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
a480973c
JM
12427#endif
12428
12429 xabdat = cc$rms_xabdat; /* To get creation date */
12430 xabdat.xab$l_nxt = (void *) &xabfhc;
12431
12432 xabfhc = cc$rms_xabfhc; /* To get record length */
12433 xabfhc.xab$l_nxt = (void *) &xabsum;
12434
12435 xabsum = cc$rms_xabsum; /* To get key and area information */
12436
12437 if (!((sts = sys$open(&fab_in)) & 1)) {
c5375c28
JM
12438 PerlMem_free(vmsin);
12439 PerlMem_free(vmsout);
12440 PerlMem_free(esa);
d584a1c6
JM
12441 if (esal != NULL)
12442 PerlMem_free(esal);
c5375c28 12443 PerlMem_free(rsa);
d584a1c6
JM
12444 if (rsal != NULL)
12445 PerlMem_free(rsal);
a480973c
JM
12446 set_vaxc_errno(sts);
12447 switch (sts) {
12448 case RMS$_FNF: case RMS$_DNF:
12449 set_errno(ENOENT); break;
12450 case RMS$_DIR:
12451 set_errno(ENOTDIR); break;
12452 case RMS$_DEV:
12453 set_errno(ENODEV); break;
12454 case RMS$_SYN:
12455 set_errno(EINVAL); break;
12456 case RMS$_PRV:
12457 set_errno(EACCES); break;
12458 default:
12459 set_errno(EVMSERR);
12460 }
12461 return 0;
12462 }
12463
12464 nam_out = nam;
12465 fab_out = fab_in;
12466 fab_out.fab$w_ifi = 0;
12467 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12468 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12469 fab_out.fab$l_fop = FAB$M_SQO;
a1887106
JM
12470 rms_bind_fab_nam(fab_out, nam_out);
12471 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12472 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12473 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
c11536f5 12474 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 12475 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 12476 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
ebd4d70b 12477 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12478 esal_out = NULL;
12479 rsal_out = NULL;
12480#if !defined(__VAX) && defined(NAML$C_MAXRSS)
c11536f5 12481 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12482 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
c11536f5 12483 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
ebd4d70b 12484 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
d584a1c6
JM
12485#endif
12486 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12487 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
a480973c
JM
12488
12489 if (preserve_dates == 0) { /* Act like DCL COPY */
a1887106 12490 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
a480973c 12491 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
a1887106 12492 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
12493 PerlMem_free(vmsin);
12494 PerlMem_free(vmsout);
12495 PerlMem_free(esa);
d584a1c6
JM
12496 if (esal != NULL)
12497 PerlMem_free(esal);
c5375c28 12498 PerlMem_free(rsa);
d584a1c6
JM
12499 if (rsal != NULL)
12500 PerlMem_free(rsal);
c5375c28 12501 PerlMem_free(esa_out);
d584a1c6
JM
12502 if (esal_out != NULL)
12503 PerlMem_free(esal_out);
12504 PerlMem_free(rsa_out);
12505 if (rsal_out != NULL)
12506 PerlMem_free(rsal_out);
a480973c
JM
12507 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12508 set_vaxc_errno(sts);
12509 return 0;
12510 }
12511 fab_out.fab$l_xab = (void *) &xabdat;
a1887106
JM
12512 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12513 preserve_dates = 1;
a480973c
JM
12514 }
12515 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12516 preserve_dates =0; /* bitmask from this point forward */
12517
12518 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
a1887106 12519 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
c5375c28
JM
12520 PerlMem_free(vmsin);
12521 PerlMem_free(vmsout);
12522 PerlMem_free(esa);
d584a1c6
JM
12523 if (esal != NULL)
12524 PerlMem_free(esal);
c5375c28 12525 PerlMem_free(rsa);
d584a1c6
JM
12526 if (rsal != NULL)
12527 PerlMem_free(rsal);
c5375c28 12528 PerlMem_free(esa_out);
d584a1c6
JM
12529 if (esal_out != NULL)
12530 PerlMem_free(esal_out);
12531 PerlMem_free(rsa_out);
12532 if (rsal_out != NULL)
12533 PerlMem_free(rsal_out);
a480973c
JM
12534 set_vaxc_errno(sts);
12535 switch (sts) {
12536 case RMS$_DNF:
12537 set_errno(ENOENT); break;
12538 case RMS$_DIR:
12539 set_errno(ENOTDIR); break;
12540 case RMS$_DEV:
12541 set_errno(ENODEV); break;
12542 case RMS$_SYN:
12543 set_errno(EINVAL); break;
12544 case RMS$_PRV:
12545 set_errno(EACCES); break;
12546 default:
12547 set_errno(EVMSERR);
12548 }
12549 return 0;
12550 }
12551 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12552 if (preserve_dates & 2) {
12553 /* sys$close() will process xabrdt, not xabdat */
12554 xabrdt = cc$rms_xabrdt;
12555#ifndef __GNUC__
12556 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12557#else
12558 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12559 * is unsigned long[2], while DECC & VAXC use a struct */
12560 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12561#endif
12562 fab_out.fab$l_xab = (void *) &xabrdt;
12563 }
12564
c11536f5 12565 ubf = (char *)PerlMem_malloc(32256);
ebd4d70b 12566 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
a480973c
JM
12567 rab_in = cc$rms_rab;
12568 rab_in.rab$l_fab = &fab_in;
12569 rab_in.rab$l_rop = RAB$M_BIO;
12570 rab_in.rab$l_ubf = ubf;
12571 rab_in.rab$w_usz = 32256;
12572 if (!((sts = sys$connect(&rab_in)) & 1)) {
12573 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
12574 PerlMem_free(vmsin);
12575 PerlMem_free(vmsout);
c5375c28 12576 PerlMem_free(ubf);
d584a1c6
JM
12577 PerlMem_free(esa);
12578 if (esal != NULL)
12579 PerlMem_free(esal);
c5375c28 12580 PerlMem_free(rsa);
d584a1c6
JM
12581 if (rsal != NULL)
12582 PerlMem_free(rsal);
c5375c28 12583 PerlMem_free(esa_out);
d584a1c6
JM
12584 if (esal_out != NULL)
12585 PerlMem_free(esal_out);
12586 PerlMem_free(rsa_out);
12587 if (rsal_out != NULL)
12588 PerlMem_free(rsal_out);
a480973c
JM
12589 set_errno(EVMSERR); set_vaxc_errno(sts);
12590 return 0;
12591 }
12592
12593 rab_out = cc$rms_rab;
12594 rab_out.rab$l_fab = &fab_out;
12595 rab_out.rab$l_rbf = ubf;
12596 if (!((sts = sys$connect(&rab_out)) & 1)) {
12597 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
12598 PerlMem_free(vmsin);
12599 PerlMem_free(vmsout);
c5375c28 12600 PerlMem_free(ubf);
d584a1c6
JM
12601 PerlMem_free(esa);
12602 if (esal != NULL)
12603 PerlMem_free(esal);
c5375c28 12604 PerlMem_free(rsa);
d584a1c6
JM
12605 if (rsal != NULL)
12606 PerlMem_free(rsal);
c5375c28 12607 PerlMem_free(esa_out);
d584a1c6
JM
12608 if (esal_out != NULL)
12609 PerlMem_free(esal_out);
12610 PerlMem_free(rsa_out);
12611 if (rsal_out != NULL)
12612 PerlMem_free(rsal_out);
a480973c
JM
12613 set_errno(EVMSERR); set_vaxc_errno(sts);
12614 return 0;
12615 }
12616
12617 while ((sts = sys$read(&rab_in))) { /* always true */
12618 if (sts == RMS$_EOF) break;
12619 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12620 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12621 sys$close(&fab_in); sys$close(&fab_out);
c5375c28
JM
12622 PerlMem_free(vmsin);
12623 PerlMem_free(vmsout);
c5375c28 12624 PerlMem_free(ubf);
d584a1c6
JM
12625 PerlMem_free(esa);
12626 if (esal != NULL)
12627 PerlMem_free(esal);
c5375c28 12628 PerlMem_free(rsa);
d584a1c6
JM
12629 if (rsal != NULL)
12630 PerlMem_free(rsal);
c5375c28 12631 PerlMem_free(esa_out);
d584a1c6
JM
12632 if (esal_out != NULL)
12633 PerlMem_free(esal_out);
12634 PerlMem_free(rsa_out);
12635 if (rsal_out != NULL)
12636 PerlMem_free(rsal_out);
a480973c
JM
12637 set_errno(EVMSERR); set_vaxc_errno(sts);
12638 return 0;
12639 }
12640 }
12641
12642
12643 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12644 sys$close(&fab_in); sys$close(&fab_out);
12645 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
a480973c 12646
c5375c28
JM
12647 PerlMem_free(vmsin);
12648 PerlMem_free(vmsout);
c5375c28 12649 PerlMem_free(ubf);
d584a1c6
JM
12650 PerlMem_free(esa);
12651 if (esal != NULL)
12652 PerlMem_free(esal);
c5375c28 12653 PerlMem_free(rsa);
d584a1c6
JM
12654 if (rsal != NULL)
12655 PerlMem_free(rsal);
c5375c28 12656 PerlMem_free(esa_out);
d584a1c6
JM
12657 if (esal_out != NULL)
12658 PerlMem_free(esal_out);
12659 PerlMem_free(rsa_out);
12660 if (rsal_out != NULL)
12661 PerlMem_free(rsal_out);
12662
12663 if (!(sts & 1)) {
12664 set_errno(EVMSERR); set_vaxc_errno(sts);
12665 return 0;
12666 }
12667
a480973c
JM
12668 return 1;
12669
12670} /* end of rmscopy() */
a5f75d66
AD
12671/*}}}*/
12672
12673
748a9306
LW
12674/*** The following glue provides 'hooks' to make some of the routines
12675 * from this file available from Perl. These routines are sufficiently
12676 * basic, and are required sufficiently early in the build process,
12677 * that's it's nice to have them available to miniperl as well as the
12678 * full Perl, so they're set up here instead of in an extension. The
12679 * Perl code which handles importation of these names into a given
12680 * package lives in [.VMS]Filespec.pm in @INC.
12681 */
12682
12683void
5c84aa53 12684rmsexpand_fromperl(pTHX_ CV *cv)
01b8edb6 12685{
12686 dXSARGS;
bbce6d69 12687 char *fspec, *defspec = NULL, *rslt;
2d8e6c8d 12688 STRLEN n_a;
360732b5 12689 int fs_utf8, dfs_utf8;
01b8edb6 12690
360732b5
JM
12691 fs_utf8 = 0;
12692 dfs_utf8 = 0;
bbce6d69 12693 if (!items || items > 2)
5c84aa53 12694 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
2d8e6c8d 12695 fspec = SvPV(ST(0),n_a);
360732b5 12696 fs_utf8 = SvUTF8(ST(0));
bbce6d69 12697 if (!fspec || !*fspec) XSRETURN_UNDEF;
360732b5
JM
12698 if (items == 2) {
12699 defspec = SvPV(ST(1),n_a);
12700 dfs_utf8 = SvUTF8(ST(1));
12701 }
12702 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
bbce6d69 12703 ST(0) = sv_newmortal();
360732b5
JM
12704 if (rslt != NULL) {
12705 sv_usepvn(ST(0),rslt,strlen(rslt));
12706 if (fs_utf8) {
12707 SvUTF8_on(ST(0));
12708 }
12709 }
740ce14c 12710 XSRETURN(1);
01b8edb6 12711}
12712
12713void
5c84aa53 12714vmsify_fromperl(pTHX_ CV *cv)
748a9306
LW
12715{
12716 dXSARGS;
12717 char *vmsified;
2d8e6c8d 12718 STRLEN n_a;
360732b5 12719 int utf8_fl;
748a9306 12720
5c84aa53 12721 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
360732b5
JM
12722 utf8_fl = SvUTF8(ST(0));
12723 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 12724 ST(0) = sv_newmortal();
360732b5
JM
12725 if (vmsified != NULL) {
12726 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12727 if (utf8_fl) {
12728 SvUTF8_on(ST(0));
12729 }
12730 }
748a9306
LW
12731 XSRETURN(1);
12732}
12733
12734void
5c84aa53 12735unixify_fromperl(pTHX_ CV *cv)
748a9306
LW
12736{
12737 dXSARGS;
12738 char *unixified;
2d8e6c8d 12739 STRLEN n_a;
360732b5 12740 int utf8_fl;
748a9306 12741
5c84aa53 12742 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
360732b5
JM
12743 utf8_fl = SvUTF8(ST(0));
12744 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 12745 ST(0) = sv_newmortal();
360732b5
JM
12746 if (unixified != NULL) {
12747 sv_usepvn(ST(0),unixified,strlen(unixified));
12748 if (utf8_fl) {
12749 SvUTF8_on(ST(0));
12750 }
12751 }
748a9306
LW
12752 XSRETURN(1);
12753}
12754
12755void
5c84aa53 12756fileify_fromperl(pTHX_ CV *cv)
748a9306
LW
12757{
12758 dXSARGS;
12759 char *fileified;
2d8e6c8d 12760 STRLEN n_a;
360732b5 12761 int utf8_fl;
748a9306 12762
5c84aa53 12763 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
360732b5
JM
12764 utf8_fl = SvUTF8(ST(0));
12765 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 12766 ST(0) = sv_newmortal();
360732b5
JM
12767 if (fileified != NULL) {
12768 sv_usepvn(ST(0),fileified,strlen(fileified));
12769 if (utf8_fl) {
12770 SvUTF8_on(ST(0));
12771 }
12772 }
748a9306
LW
12773 XSRETURN(1);
12774}
12775
12776void
5c84aa53 12777pathify_fromperl(pTHX_ CV *cv)
748a9306
LW
12778{
12779 dXSARGS;
12780 char *pathified;
2d8e6c8d 12781 STRLEN n_a;
360732b5 12782 int utf8_fl;
748a9306 12783
5c84aa53 12784 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
360732b5
JM
12785 utf8_fl = SvUTF8(ST(0));
12786 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 12787 ST(0) = sv_newmortal();
360732b5
JM
12788 if (pathified != NULL) {
12789 sv_usepvn(ST(0),pathified,strlen(pathified));
12790 if (utf8_fl) {
12791 SvUTF8_on(ST(0));
12792 }
12793 }
748a9306
LW
12794 XSRETURN(1);
12795}
12796
12797void
5c84aa53 12798vmspath_fromperl(pTHX_ CV *cv)
748a9306
LW
12799{
12800 dXSARGS;
12801 char *vmspath;
2d8e6c8d 12802 STRLEN n_a;
360732b5 12803 int utf8_fl;
748a9306 12804
5c84aa53 12805 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
360732b5
JM
12806 utf8_fl = SvUTF8(ST(0));
12807 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 12808 ST(0) = sv_newmortal();
360732b5
JM
12809 if (vmspath != NULL) {
12810 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12811 if (utf8_fl) {
12812 SvUTF8_on(ST(0));
12813 }
12814 }
748a9306
LW
12815 XSRETURN(1);
12816}
12817
12818void
5c84aa53 12819unixpath_fromperl(pTHX_ CV *cv)
748a9306
LW
12820{
12821 dXSARGS;
12822 char *unixpath;
2d8e6c8d 12823 STRLEN n_a;
360732b5 12824 int utf8_fl;
748a9306 12825
5c84aa53 12826 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
360732b5
JM
12827 utf8_fl = SvUTF8(ST(0));
12828 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
748a9306 12829 ST(0) = sv_newmortal();
360732b5
JM
12830 if (unixpath != NULL) {
12831 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12832 if (utf8_fl) {
12833 SvUTF8_on(ST(0));
12834 }
12835 }
748a9306
LW
12836 XSRETURN(1);
12837}
12838
12839void
5c84aa53 12840candelete_fromperl(pTHX_ CV *cv)
748a9306
LW
12841{
12842 dXSARGS;
988c775c 12843 char *fspec, *fsp;
a5f75d66
AD
12844 SV *mysv;
12845 IO *io;
2d8e6c8d 12846 STRLEN n_a;
748a9306 12847
5c84aa53 12848 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
a5f75d66
AD
12849
12850 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
988c775c
JM
12851 Newx(fspec, VMS_MAXRSS, char);
12852 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
6d24fbd1 12853 if (isGV_with_GP(mysv)) {
a15cef0c 12854 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
a5f75d66 12855 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 12856 ST(0) = &PL_sv_no;
988c775c 12857 Safefree(fspec);
a5f75d66
AD
12858 XSRETURN(1);
12859 }
12860 fsp = fspec;
12861 }
12862 else {
2d8e6c8d 12863 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
a5f75d66 12864 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6b88bc9c 12865 ST(0) = &PL_sv_no;
988c775c 12866 Safefree(fspec);
a5f75d66
AD
12867 XSRETURN(1);
12868 }
12869 }
12870
54310121 12871 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
988c775c 12872 Safefree(fspec);
a5f75d66
AD
12873 XSRETURN(1);
12874}
12875
12876void
5c84aa53 12877rmscopy_fromperl(pTHX_ CV *cv)
a5f75d66
AD
12878{
12879 dXSARGS;
a480973c 12880 char *inspec, *outspec, *inp, *outp;
a3e9d8c9 12881 int date_flag;
a5f75d66
AD
12882 SV *mysv;
12883 IO *io;
2d8e6c8d 12884 STRLEN n_a;
a5f75d66 12885
a3e9d8c9 12886 if (items < 2 || items > 3)
5c84aa53 12887 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
a5f75d66
AD
12888
12889 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
a480973c 12890 Newx(inspec, VMS_MAXRSS, char);
6d24fbd1 12891 if (isGV_with_GP(mysv)) {
a15cef0c 12892 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
a5f75d66 12893 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 12894 ST(0) = sv_2mortal(newSViv(0));
a480973c 12895 Safefree(inspec);
a5f75d66
AD
12896 XSRETURN(1);
12897 }
12898 inp = inspec;
12899 }
12900 else {
2d8e6c8d 12901 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
a5f75d66 12902 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 12903 ST(0) = sv_2mortal(newSViv(0));
a480973c 12904 Safefree(inspec);
a5f75d66
AD
12905 XSRETURN(1);
12906 }
12907 }
12908 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
a480973c 12909 Newx(outspec, VMS_MAXRSS, char);
6d24fbd1 12910 if (isGV_with_GP(mysv)) {
a15cef0c 12911 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
a5f75d66 12912 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 12913 ST(0) = sv_2mortal(newSViv(0));
a480973c
JM
12914 Safefree(inspec);
12915 Safefree(outspec);
a5f75d66
AD
12916 XSRETURN(1);
12917 }
12918 outp = outspec;
12919 }
12920 else {
2d8e6c8d 12921 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
a5f75d66 12922 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
fd188159 12923 ST(0) = sv_2mortal(newSViv(0));
a480973c
JM
12924 Safefree(inspec);
12925 Safefree(outspec);
a5f75d66
AD
12926 XSRETURN(1);
12927 }
12928 }
a3e9d8c9 12929 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
a5f75d66 12930
fd188159 12931 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
a480973c
JM
12932 Safefree(inspec);
12933 Safefree(outspec);
748a9306
LW
12934 XSRETURN(1);
12935}
12936
a480973c
JM
12937/* The mod2fname is limited to shorter filenames by design, so it should
12938 * not be modified to support longer EFS pathnames
12939 */
4b19af01 12940void
fd8cd3a3 12941mod2fname(pTHX_ CV *cv)
4b19af01
CB
12942{
12943 dXSARGS;
12944 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12945 workbuff[NAM$C_MAXRSS*1 + 1];
4e0c9737 12946 int counter, num_entries;
4b19af01
CB
12947 /* ODS-5 ups this, but we want to be consistent, so... */
12948 int max_name_len = 39;
12949 AV *in_array = (AV *)SvRV(ST(0));
12950
12951 num_entries = av_len(in_array);
12952
12953 /* All the names start with PL_. */
12954 strcpy(ultimate_name, "PL_");
12955
12956 /* Clean up our working buffer */
12957 Zero(work_name, sizeof(work_name), char);
12958
12959 /* Run through the entries and build up a working name */
12960 for(counter = 0; counter <= num_entries; counter++) {
12961 /* If it's not the first name then tack on a __ */
12962 if (counter) {
a35dcc95 12963 my_strlcat(work_name, "__", sizeof(work_name));
4b19af01 12964 }
a35dcc95 12965 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
4b19af01
CB
12966 }
12967
12968 /* Check to see if we actually have to bother...*/
12969 if (strlen(work_name) + 3 <= max_name_len) {
a35dcc95 12970 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
4b19af01
CB
12971 } else {
12972 /* It's too darned big, so we need to go strip. We use the same */
12973 /* algorithm as xsubpp does. First, strip out doubled __ */
12974 char *source, *dest, last;
12975 dest = workbuff;
12976 last = 0;
12977 for (source = work_name; *source; source++) {
12978 if (last == *source && last == '_') {
12979 continue;
12980 }
12981 *dest++ = *source;
12982 last = *source;
12983 }
12984 /* Go put it back */
a35dcc95 12985 my_strlcpy(work_name, workbuff, sizeof(work_name));
4b19af01
CB
12986 /* Is it still too big? */
12987 if (strlen(work_name) + 3 > max_name_len) {
12988 /* Strip duplicate letters */
12989 last = 0;
12990 dest = workbuff;
12991 for (source = work_name; *source; source++) {
12992 if (last == toupper(*source)) {
12993 continue;
12994 }
12995 *dest++ = *source;
12996 last = toupper(*source);
12997 }
a35dcc95 12998 my_strlcpy(work_name, workbuff, sizeof(work_name));
4b19af01
CB
12999 }
13000
13001 /* Is it *still* too big? */
13002 if (strlen(work_name) + 3 > max_name_len) {
13003 /* Too bad, we truncate */
13004 work_name[max_name_len - 2] = 0;
13005 }
a35dcc95 13006 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
4b19af01
CB
13007 }
13008
13009 /* Okay, return it */
13010 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13011 XSRETURN(1);
13012}
13013
748a9306 13014void
96e176bf
CL
13015hushexit_fromperl(pTHX_ CV *cv)
13016{
13017 dXSARGS;
13018
13019 if (items > 0) {
13020 VMSISH_HUSHED = SvTRUE(ST(0));
13021 }
13022 ST(0) = boolSV(VMSISH_HUSHED);
13023 XSRETURN(1);
13024}
13025
dca5a913
JM
13026
13027PerlIO *
13028Perl_vms_start_glob
13029 (pTHX_ SV *tmpglob,
13030 IO *io)
13031{
13032 PerlIO *fp;
13033 struct vs_str_st *rslt;
13034 char *vmsspec;
13035 char *rstr;
13036 char *begin, *cp;
13037 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13038 PerlIO *tmpfp;
13039 STRLEN i;
13040 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13041 struct dsc$descriptor_vs rsdsc;
13042 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13043 unsigned long hasver = 0, isunix = 0;
13044 unsigned long int lff_flags = 0;
13045 int rms_sts;
85e7c9de 13046 int vms_old_glob = 1;
dca5a913 13047
83b907a4
CB
13048 if (!SvOK(tmpglob)) {
13049 SETERRNO(ENOENT,RMS$_FNF);
13050 return NULL;
13051 }
13052
85e7c9de
JM
13053 vms_old_glob = !decc_filename_unix_report;
13054
dca5a913
JM
13055#ifdef VMS_LONGNAME_SUPPORT
13056 lff_flags = LIB$M_FIL_LONG_NAMES;
13057#endif
13058 /* The Newx macro will not allow me to assign a smaller array
13059 * to the rslt pointer, so we will assign it to the begin char pointer
13060 * and then copy the value into the rslt pointer.
13061 */
13062 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13063 rslt = (struct vs_str_st *)begin;
13064 rslt->length = 0;
13065 rstr = &rslt->str[0];
13066 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13067 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13068 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13069 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13070
13071 Newx(vmsspec, VMS_MAXRSS, char);
13072
13073 /* We could find out if there's an explicit dev/dir or version
13074 by peeking into lib$find_file's internal context at
13075 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13076 but that's unsupported, so I don't want to do it now and
13077 have it bite someone in the future. */
13078 /* Fix-me: vms_split_path() is the only way to do this, the
13079 existing method will fail with many legal EFS or UNIX specifications
13080 */
13081
13082 cp = SvPV(tmpglob,i);
13083
13084 for (; i; i--) {
13085 if (cp[i] == ';') hasver = 1;
13086 if (cp[i] == '.') {
13087 if (sts) hasver = 1;
13088 else sts = 1;
13089 }
13090 if (cp[i] == '/') {
13091 hasdir = isunix = 1;
13092 break;
13093 }
13094 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13095 hasdir = 1;
13096 break;
13097 }
13098 }
85e7c9de
JM
13099
13100 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13101 if ((hasdir == 0) && decc_filename_unix_report) {
13102 isunix = 1;
13103 }
13104
dca5a913 13105 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
85e7c9de
JM
13106 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13107 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13108 int wildstar = 0;
13109 int wildquery = 0;
990cad08 13110 int found = 0;
dca5a913
JM
13111 Stat_t st;
13112 int stat_sts;
13113 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13114 if (!stat_sts && S_ISDIR(st.st_mode)) {
85e7c9de
JM
13115 char * vms_dir;
13116 const char * fname;
13117 STRLEN fname_len;
13118
13119 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13120 /* path delimiter of ':>]', if so, then the old behavior has */
94ae10c0 13121 /* obviously been specifically requested */
85e7c9de
JM
13122
13123 fname = SvPVX_const(tmpglob);
13124 fname_len = strlen(fname);
13125 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13126 if (vms_old_glob || (vms_dir != NULL)) {
13127 wilddsc.dsc$a_pointer = tovmspath_utf8(
13128 SvPVX(tmpglob),vmsspec,NULL);
13129 ok = (wilddsc.dsc$a_pointer != NULL);
13130 /* maybe passed 'foo' rather than '[.foo]', thus not
13131 detected above */
13132 hasdir = 1;
13133 } else {
13134 /* Operate just on the directory, the special stat/fstat for */
13135 /* leaves the fileified specification in the st_devnam */
13136 /* member. */
13137 wilddsc.dsc$a_pointer = st.st_devnam;
13138 ok = 1;
13139 }
dca5a913
JM
13140 }
13141 else {
360732b5 13142 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
dca5a913
JM
13143 ok = (wilddsc.dsc$a_pointer != NULL);
13144 }
13145 if (ok)
13146 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13147
13148 /* If not extended character set, replace ? with % */
13149 /* With extended character set, ? is a wildcard single character */
85e7c9de
JM
13150 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13151 if (*cp == '?') {
13152 wildquery = 1;
998e0439 13153 if (!decc_efs_charset)
85e7c9de
JM
13154 *cp = '%';
13155 } else if (*cp == '%') {
13156 wildquery = 1;
13157 } else if (*cp == '*') {
13158 wildstar = 1;
13159 }
dca5a913 13160 }
85e7c9de
JM
13161
13162 if (ok) {
13163 wv_sts = vms_split_path(
13164 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13165 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13166 &wvs_spec, &wvs_len);
13167 } else {
13168 wn_spec = NULL;
13169 wn_len = 0;
13170 we_spec = NULL;
13171 we_len = 0;
13172 }
13173
dca5a913
JM
13174 sts = SS$_NORMAL;
13175 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13176 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13177 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
85e7c9de 13178 int valid_find;
dca5a913 13179
85e7c9de 13180 valid_find = 0;
dca5a913
JM
13181 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13182 &dfltdsc,NULL,&rms_sts,&lff_flags);
13183 if (!$VMS_STATUS_SUCCESS(sts))
13184 break;
13185
13186 /* with varying string, 1st word of buffer contains result length */
13187 rstr[rslt->length] = '\0';
13188
13189 /* Find where all the components are */
13190 v_sts = vms_split_path
360732b5 13191 (rstr,
dca5a913
JM
13192 &v_spec,
13193 &v_len,
13194 &r_spec,
13195 &r_len,
13196 &d_spec,
13197 &d_len,
13198 &n_spec,
13199 &n_len,
13200 &e_spec,
13201 &e_len,
13202 &vs_spec,
13203 &vs_len);
13204
13205 /* If no version on input, truncate the version on output */
13206 if (!hasver && (vs_len > 0)) {
13207 *vs_spec = '\0';
13208 vs_len = 0;
85e7c9de
JM
13209 }
13210
13211 if (isunix) {
13212
13213 /* In Unix report mode, remove the ".dir;1" from the name */
13214 /* if it is a real directory */
13215 if (decc_filename_unix_report || decc_efs_charset) {
13216 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13217 Stat_t statbuf;
13218 int ret_sts;
13219
13220 ret_sts = flex_lstat(rstr, &statbuf);
13221 if ((ret_sts == 0) &&
13222 S_ISDIR(statbuf.st_mode)) {
13223 e_len = 0;
13224 e_spec[0] = 0;
13225 }
13226 }
13227 }
dca5a913
JM
13228
13229 /* No version & a null extension on UNIX handling */
85e7c9de 13230 if ((e_len == 1) && decc_readdir_dropdotnotype) {
dca5a913
JM
13231 e_len = 0;
13232 *e_spec = '\0';
13233 }
13234 }
13235
13236 if (!decc_efs_case_preserve) {
13237 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13238 }
13239
85e7c9de
JM
13240 /* Find File treats a Null extension as return all extensions */
13241 /* This is contrary to Perl expectations */
13242
13243 if (wildstar || wildquery || vms_old_glob) {
13244 /* really need to see if the returned file name matched */
13245 /* but for now will assume that it matches */
13246 valid_find = 1;
13247 } else {
13248 /* Exact Match requested */
13249 /* How are directories handled? - like a file */
13250 if ((e_len == we_len) && (n_len == wn_len)) {
13251 int t1;
13252 t1 = e_len;
13253 if (t1 > 0)
13254 t1 = strncmp(e_spec, we_spec, e_len);
13255 if (t1 == 0) {
13256 t1 = n_len;
13257 if (t1 > 0)
13258 t1 = strncmp(n_spec, we_spec, n_len);
13259 if (t1 == 0)
13260 valid_find = 1;
13261 }
13262 }
13263 }
13264
13265 if (valid_find) {
13266 found++;
13267
13268 if (hasdir) {
13269 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13270 begin = rstr;
13271 }
13272 else {
13273 /* Start with the name */
13274 begin = n_spec;
13275 }
13276 strcat(begin,"\n");
13277 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13278 }
dca5a913
JM
13279 }
13280 if (cxt) (void)lib$find_file_end(&cxt);
990cad08
CB
13281
13282 if (!found) {
13283 /* Be POSIXish: return the input pattern when no matches */
a35dcc95 13284 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
2da7a6b5
CB
13285 strcat(rstr,"\n");
13286 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
990cad08
CB
13287 }
13288
dca5a913
JM
13289 if (ok && sts != RMS$_NMF &&
13290 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13291 if (!ok) {
13292 if (!(sts & 1)) {
13293 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13294 }
13295 PerlIO_close(tmpfp);
13296 fp = NULL;
13297 }
13298 else {
13299 PerlIO_rewind(tmpfp);
13300 IoTYPE(io) = IoTYPE_RDONLY;
13301 IoIFP(io) = fp = tmpfp;
13302 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13303 }
13304 }
13305 Safefree(vmsspec);
13306 Safefree(rslt);
13307 return fp;
13308}
13309
cd1191f1 13310
2497a41f 13311static char *
5c4d031a 13312mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
d584a1c6 13313 int *utf8_fl);
2497a41f
JM
13314
13315void
4d8d3a9c 13316unixrealpath_fromperl(pTHX_ CV *cv)
2497a41f 13317{
d584a1c6
JM
13318 dXSARGS;
13319 char *fspec, *rslt_spec, *rslt;
13320 STRLEN n_a;
2497a41f 13321
d584a1c6 13322 if (!items || items != 1)
4d8d3a9c 13323 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
2497a41f 13324
d584a1c6
JM
13325 fspec = SvPV(ST(0),n_a);
13326 if (!fspec || !*fspec) XSRETURN_UNDEF;
2497a41f 13327
d584a1c6
JM
13328 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13329 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13330
13331 ST(0) = sv_newmortal();
13332 if (rslt != NULL)
13333 sv_usepvn(ST(0),rslt,strlen(rslt));
13334 else
13335 Safefree(rslt_spec);
13336 XSRETURN(1);
2497a41f 13337}
2ee6e19d 13338
b1a8dcd7
JM
13339static char *
13340mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13341 int *utf8_fl);
13342
13343void
4d8d3a9c 13344vmsrealpath_fromperl(pTHX_ CV *cv)
b1a8dcd7
JM
13345{
13346 dXSARGS;
13347 char *fspec, *rslt_spec, *rslt;
13348 STRLEN n_a;
13349
13350 if (!items || items != 1)
4d8d3a9c 13351 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
b1a8dcd7
JM
13352
13353 fspec = SvPV(ST(0),n_a);
13354 if (!fspec || !*fspec) XSRETURN_UNDEF;
13355
13356 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13357 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13358
13359 ST(0) = sv_newmortal();
13360 if (rslt != NULL)
13361 sv_usepvn(ST(0),rslt,strlen(rslt));
13362 else
13363 Safefree(rslt_spec);
13364 XSRETURN(1);
13365}
13366
13367#ifdef HAS_SYMLINK
2ee6e19d
CB
13368/*
13369 * A thin wrapper around decc$symlink to make sure we follow the
cc9aafbd
CB
13370 * standard and do not create a symlink with a zero-length name,
13371 * and convert the target to Unix format, as the CRTL can't handle
13372 * targets in VMS format.
2ee6e19d 13373 */
4148925f 13374/*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
cc9aafbd
CB
13375int
13376Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13377{
13378 int sts;
13379 char * utarget;
4148925f 13380
cc9aafbd
CB
13381 if (!link_name || !*link_name) {
13382 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13383 return -1;
13384 }
4148925f 13385
c11536f5 13386 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
cc9aafbd
CB
13387 /* An untranslatable filename should be passed through. */
13388 (void) int_tounixspec(contents, utarget, NULL);
13389 sts = symlink(utarget, link_name);
13390 PerlMem_free(utarget);
13391 return sts;
2ee6e19d
CB
13392}
13393/*}}}*/
13394
13395#endif /* HAS_SYMLINK */
2497a41f 13396
2497a41f
JM
13397int do_vms_case_tolerant(void);
13398
13399void
4d8d3a9c 13400case_tolerant_process_fromperl(pTHX_ CV *cv)
2497a41f
JM
13401{
13402 dXSARGS;
13403 ST(0) = boolSV(do_vms_case_tolerant());
13404 XSRETURN(1);
13405}
2497a41f 13406
9ec7171b
CB
13407#ifdef USE_ITHREADS
13408
96e176bf
CL
13409void
13410Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13411 struct interp_intern *dst)
13412{
7918f24d
NC
13413 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13414
96e176bf
CL
13415 memcpy(dst,src,sizeof(struct interp_intern));
13416}
13417
9ec7171b
CB
13418#endif
13419
96e176bf
CL
13420void
13421Perl_sys_intern_clear(pTHX)
13422{
13423}
13424
13425void
13426Perl_sys_intern_init(pTHX)
13427{
3ff49832
CL
13428 unsigned int ix = RAND_MAX;
13429 double x;
96e176bf
CL
13430
13431 VMSISH_HUSHED = 0;
13432
1a3aec58 13433 MY_POSIX_EXIT = vms_posix_exit;
7a7fd8e0 13434
96e176bf
CL
13435 x = (float)ix;
13436 MY_INV_RAND_MAX = 1./x;
ff7adb52 13437}
96e176bf
CL
13438
13439void
f7ddb74a 13440init_os_extras(void)
748a9306 13441{
a69a6dba 13442 dTHX;
748a9306 13443 char* file = __FILE__;
988c775c 13444 if (decc_disable_to_vms_logname_translation) {
93948341
CB
13445 no_translate_barewords = TRUE;
13446 } else {
13447 no_translate_barewords = FALSE;
13448 }
748a9306 13449
740ce14c 13450 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
a5f75d66
AD
13451 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13452 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13453 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13454 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13455 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13456 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13457 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
4b19af01 13458 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
a5f75d66 13459 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
96e176bf 13460 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
4d8d3a9c
CB
13461 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13462 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13463 newXSproto("VMS::Filespec::case_tolerant_process",
13464 case_tolerant_process_fromperl,file,"");
17f28c40 13465
afd8f436 13466 store_pipelocs(aTHX); /* will redo any earlier attempts */
22d4bb9c 13467
748a9306
LW
13468 return;
13469}
13470
f7ddb74a
JM
13471#if __CRTL_VER == 80200000
13472/* This missed getting in to the DECC SDK for 8.2 */
13473char *realpath(const char *file_name, char * resolved_name, ...);
13474#endif
13475
13476/*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13477/* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13478 * The perl fallback routine to provide realpath() is not as efficient
13479 * on OpenVMS.
13480 */
d584a1c6 13481
c11536f5
CB
13482#ifdef __cplusplus
13483extern "C" {
13484#endif
13485
d584a1c6
JM
13486/* Hack, use old stat() as fastest way of getting ino_t and device */
13487int decc$stat(const char *name, void * statbuf);
312ac60b
JM
13488#if !defined(__VAX) && __CRTL_VER >= 80200000
13489int decc$lstat(const char *name, void * statbuf);
13490#else
13491#define decc$lstat decc$stat
13492#endif
d584a1c6 13493
c11536f5
CB
13494#ifdef __cplusplus
13495}
13496#endif
13497
d584a1c6
JM
13498
13499/* Realpath is fragile. In 8.3 it does not work if the feature
13500 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13501 * links are implemented in RMS, not the CRTL. It also can fail if the
13502 * user does not have read/execute access to some of the directories.
13503 * So in order for Do What I Mean mode to work, if realpath() fails,
13504 * fall back to looking up the filename by the device name and FID.
13505 */
13506
312ac60b
JM
13507int vms_fid_to_name(char * outname, int outlen,
13508 const char * name, int lstat_flag, mode_t * mode)
d584a1c6 13509{
312ac60b
JM
13510#pragma message save
13511#pragma message disable MISALGNDSTRCT
13512#pragma message disable MISALGNDMEM
13513#pragma member_alignment save
13514#pragma nomember_alignment
d584a1c6
JM
13515struct statbuf_t {
13516 char * st_dev;
b1a8dcd7 13517 unsigned short st_ino[3];
312ac60b 13518 unsigned short old_st_mode;
d584a1c6
JM
13519 unsigned long padl[30]; /* plenty of room */
13520} statbuf;
312ac60b
JM
13521#pragma message restore
13522#pragma member_alignment restore
13523
13524 int sts;
13525 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13526 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13527 char *fileified;
13528 char *temp_fspec;
13529 char *ret_spec;
13530
13531 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13532 * unexpected answers
13533 */
13534
c11536f5 13535 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
312ac60b
JM
13536 if (fileified == NULL)
13537 _ckvmssts_noperl(SS$_INSFMEM);
13538
c11536f5 13539 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
312ac60b
JM
13540 if (temp_fspec == NULL)
13541 _ckvmssts_noperl(SS$_INSFMEM);
13542
13543 sts = -1;
13544 /* First need to try as a directory */
13545 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13546 if (ret_spec != NULL) {
13547 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13548 if (ret_spec != NULL) {
13549 if (lstat_flag == 0)
13550 sts = decc$stat(fileified, &statbuf);
13551 else
13552 sts = decc$lstat(fileified, &statbuf);
13553 }
13554 }
13555
13556 /* Then as a VMS file spec */
13557 if (sts != 0) {
13558 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13559 if (ret_spec != NULL) {
13560 if (lstat_flag == 0) {
13561 sts = decc$stat(temp_fspec, &statbuf);
13562 } else {
13563 sts = decc$lstat(temp_fspec, &statbuf);
13564 }
13565 }
13566 }
13567
13568 if (sts) {
13569 /* Next try - allow multiple dots with out EFS CHARSET */
13570 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13571 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13572 * enable it if it isn't already.
13573 */
13574#if __CRTL_VER >= 70300000 && !defined(__VAX)
13575 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13576 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13577#endif
13578 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13579 if (lstat_flag == 0) {
13580 sts = decc$stat(name, &statbuf);
13581 } else {
13582 sts = decc$lstat(name, &statbuf);
13583 }
13584#if __CRTL_VER >= 70300000 && !defined(__VAX)
13585 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13586 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13587#endif
13588 }
13589
13590
13591 /* and then because the Perl Unix to VMS conversion is not perfect */
13592 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13593 /* characters from filenames so we need to try it as-is */
13594 if (sts) {
13595 if (lstat_flag == 0) {
13596 sts = decc$stat(name, &statbuf);
13597 } else {
13598 sts = decc$lstat(name, &statbuf);
13599 }
13600 }
d584a1c6 13601
d584a1c6 13602 if (sts == 0) {
312ac60b 13603 int vms_sts;
d584a1c6
JM
13604
13605 dvidsc.dsc$a_pointer=statbuf.st_dev;
d94c5a78 13606 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
d584a1c6
JM
13607
13608 specdsc.dsc$a_pointer = outname;
13609 specdsc.dsc$w_length = outlen-1;
13610
d94c5a78 13611 vms_sts = lib$fid_to_name
d584a1c6 13612 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
d94c5a78 13613 if ($VMS_STATUS_SUCCESS(vms_sts)) {
d584a1c6 13614 outname[specdsc.dsc$w_length] = 0;
312ac60b
JM
13615
13616 /* Return the mode */
13617 if (mode) {
13618 *mode = statbuf.old_st_mode;
13619 }
d584a1c6
JM
13620 }
13621 }
9e2bec02
CB
13622 PerlMem_free(temp_fspec);
13623 PerlMem_free(fileified);
d584a1c6
JM
13624 return sts;
13625}
13626
13627
13628
f7ddb74a 13629static char *
5c4d031a 13630mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
d584a1c6 13631 int *utf8_fl)
f7ddb74a 13632{
d584a1c6
JM
13633 char * rslt = NULL;
13634
b1a8dcd7
JM
13635#ifdef HAS_SYMLINK
13636 if (decc_posix_compliant_pathnames > 0 ) {
13637 /* realpath currently only works if posix compliant pathnames are
13638 * enabled. It may start working when they are not, but in that
13639 * case we still want the fallback behavior for backwards compatibility
13640 */
d584a1c6 13641 rslt = realpath(filespec, outbuf);
b1a8dcd7
JM
13642 }
13643#endif
d584a1c6
JM
13644
13645 if (rslt == NULL) {
13646 char * vms_spec;
13647 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13648 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
312ac60b 13649 mode_t my_mode;
d584a1c6
JM
13650
13651 /* Fall back to fid_to_name */
13652
13653 Newx(vms_spec, VMS_MAXRSS + 1, char);
13654
312ac60b 13655 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
4d8d3a9c 13656 if (sts == 0) {
d584a1c6
JM
13657
13658
13659 /* Now need to trim the version off */
13660 sts = vms_split_path
13661 (vms_spec,
13662 &v_spec,
13663 &v_len,
13664 &r_spec,
13665 &r_len,
13666 &d_spec,
13667 &d_len,
13668 &n_spec,
13669 &n_len,
13670 &e_spec,
13671 &e_len,
13672 &vs_spec,
13673 &vs_len);
13674
13675
4d8d3a9c
CB
13676 if (sts == 0) {
13677 int haslower = 0;
13678 const char *cp;
d584a1c6 13679
4d8d3a9c
CB
13680 /* Trim off the version */
13681 int file_len = v_len + r_len + d_len + n_len + e_len;
13682 vms_spec[file_len] = 0;
d584a1c6 13683
f785e3a1
JM
13684 /* Trim off the .DIR if this is a directory */
13685 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13686 if (S_ISDIR(my_mode)) {
13687 e_len = 0;
13688 e_spec[0] = 0;
13689 }
13690 }
13691
13692 /* Drop NULL extensions on UNIX file specification */
13693 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13694 e_len = 0;
13695 e_spec[0] = '\0';
13696 }
13697
4d8d3a9c 13698 /* The result is expected to be in UNIX format */
0e5ce2c7 13699 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
4d8d3a9c
CB
13700
13701 /* Downcase if input had any lower case letters and
13702 * case preservation is not in effect.
13703 */
13704 if (!decc_efs_case_preserve) {
13705 for (cp = filespec; *cp; cp++)
13706 if (islower(*cp)) { haslower = 1; break; }
13707
13708 if (haslower) __mystrtolower(rslt);
13709 }
13710 }
643f470b
CB
13711 } else {
13712
13713 /* Now for some hacks to deal with backwards and forward */
94ae10c0 13714 /* compatibility */
643f470b
CB
13715 if (!decc_efs_charset) {
13716
13717 /* 1. ODS-2 mode wants to do a syntax only translation */
6fb6c614
JM
13718 rslt = int_rmsexpand(filespec, outbuf,
13719 NULL, 0, NULL, utf8_fl);
643f470b
CB
13720
13721 } else {
13722 if (decc_filename_unix_report) {
13723 char * dir_name;
13724 char * vms_dir_name;
13725 char * file_name;
13726
13727 /* 2. ODS-5 / UNIX report mode should return a failure */
13728 /* if the parent directory also does not exist */
13729 /* Otherwise, get the real path for the parent */
29475144 13730 /* and add the child to it. */
643f470b
CB
13731
13732 /* basename / dirname only available for VMS 7.0+ */
13733 /* So we may need to implement them as common routines */
13734
13735 Newx(dir_name, VMS_MAXRSS + 1, char);
13736 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13737 dir_name[0] = '\0';
13738 file_name = NULL;
13739
13740 /* First try a VMS parse */
13741 sts = vms_split_path
13742 (filespec,
13743 &v_spec,
13744 &v_len,
13745 &r_spec,
13746 &r_len,
13747 &d_spec,
13748 &d_len,
13749 &n_spec,
13750 &n_len,
13751 &e_spec,
13752 &e_len,
13753 &vs_spec,
13754 &vs_len);
13755
13756 if (sts == 0) {
13757 /* This is VMS */
13758
13759 int dir_len = v_len + r_len + d_len + n_len;
13760 if (dir_len > 0) {
a35dcc95 13761 memcpy(dir_name, filespec, dir_len);
643f470b
CB
13762 dir_name[dir_len] = '\0';
13763 file_name = (char *)&filespec[dir_len + 1];
13764 }
13765 } else {
13766 /* This must be UNIX */
13767 char * tchar;
13768
13769 tchar = strrchr(filespec, '/');
13770
4148925f
JM
13771 if (tchar != NULL) {
13772 int dir_len = tchar - filespec;
a35dcc95 13773 memcpy(dir_name, filespec, dir_len);
4148925f
JM
13774 dir_name[dir_len] = '\0';
13775 file_name = (char *) &filespec[dir_len + 1];
13776 }
13777 }
13778
13779 /* Dir name is defaulted */
13780 if (dir_name[0] == 0) {
13781 dir_name[0] = '.';
13782 dir_name[1] = '\0';
13783 }
13784
13785 /* Need realpath for the directory */
13786 sts = vms_fid_to_name(vms_dir_name,
13787 VMS_MAXRSS + 1,
312ac60b 13788 dir_name, 0, NULL);
4148925f
JM
13789
13790 if (sts == 0) {
29475144 13791 /* Now need to pathify it. */
1fe570cc
JM
13792 char *tdir = int_pathify_dirspec(vms_dir_name,
13793 outbuf);
4148925f
JM
13794
13795 /* And now add the original filespec to it */
13796 if (file_name != NULL) {
a35dcc95 13797 my_strlcat(outbuf, file_name, VMS_MAXRSS);
4148925f
JM
13798 }
13799 return outbuf;
13800 }
13801 Safefree(vms_dir_name);
13802 Safefree(dir_name);
13803 }
13804 }
643f470b 13805 }
d584a1c6
JM
13806 Safefree(vms_spec);
13807 }
13808 return rslt;
f7ddb74a
JM
13809}
13810
b1a8dcd7
JM
13811static char *
13812mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13813 int *utf8_fl)
13814{
13815 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13816 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
b1a8dcd7
JM
13817
13818 /* Fall back to fid_to_name */
13819
312ac60b 13820 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
cd43acd7
CB
13821 if (sts != 0) {
13822 return NULL;
13823 }
13824 else {
b1a8dcd7
JM
13825
13826
13827 /* Now need to trim the version off */
13828 sts = vms_split_path
13829 (outbuf,
13830 &v_spec,
13831 &v_len,
13832 &r_spec,
13833 &r_len,
13834 &d_spec,
13835 &d_len,
13836 &n_spec,
13837 &n_len,
13838 &e_spec,
13839 &e_len,
13840 &vs_spec,
13841 &vs_len);
13842
13843
13844 if (sts == 0) {
4d8d3a9c
CB
13845 int haslower = 0;
13846 const char *cp;
13847
13848 /* Trim off the version */
13849 int file_len = v_len + r_len + d_len + n_len + e_len;
13850 outbuf[file_len] = 0;
b1a8dcd7 13851
4d8d3a9c
CB
13852 /* Downcase if input had any lower case letters and
13853 * case preservation is not in effect.
13854 */
13855 if (!decc_efs_case_preserve) {
13856 for (cp = filespec; *cp; cp++)
13857 if (islower(*cp)) { haslower = 1; break; }
13858
13859 if (haslower) __mystrtolower(outbuf);
13860 }
b1a8dcd7
JM
13861 }
13862 }
13863 return outbuf;
13864}
13865
13866
f7ddb74a
JM
13867/*}}}*/
13868/* External entry points */
360732b5
JM
13869char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13870{ return do_vms_realpath(filespec, outbuf, utf8_fl); }
f7ddb74a 13871
b1a8dcd7
JM
13872char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13873{ return do_vms_realname(filespec, outbuf, utf8_fl); }
f7ddb74a 13874
f7ddb74a
JM
13875/* case_tolerant */
13876
13877/*{{{int do_vms_case_tolerant(void)*/
13878/* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13879 * controlled by a process setting.
13880 */
13881int do_vms_case_tolerant(void)
13882{
13883 return vms_process_case_tolerant;
13884}
13885/*}}}*/
13886/* External entry points */
b1a8dcd7 13887#if __CRTL_VER >= 70301000 && !defined(__VAX)
f7ddb74a
JM
13888int Perl_vms_case_tolerant(void)
13889{ return do_vms_case_tolerant(); }
13890#else
13891int Perl_vms_case_tolerant(void)
13892{ return vms_process_case_tolerant; }
13893#endif
13894
13895
13896 /* Start of DECC RTL Feature handling */
13897
f7ddb74a 13898
f7ddb74a
JM
13899/* C RTL Feature settings */
13900
e2367aa8
CB
13901#if defined(__DECC) || defined(__DECCXX)
13902
13903#ifdef __cplusplus
13904extern "C" {
13905#endif
13906
13907extern void
13908vmsperl_set_features(void)
f7ddb74a
JM
13909{
13910 int status;
13911 int s;
f7ddb74a 13912 char val_str[10];
3c841f20 13913#if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
f7ddb74a
JM
13914 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13915 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13916 unsigned long case_perm;
13917 unsigned long case_image;
3c841f20 13918#endif
f7ddb74a 13919
9c1171d1
JM
13920 /* Allow an exception to bring Perl into the VMS debugger */
13921 vms_debug_on_exception = 0;
8dc9d339 13922 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
9c1171d1 13923 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 13924 val_str[0] = _toupper(val_str[0]);
9c1171d1
JM
13925 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13926 vms_debug_on_exception = 1;
13927 else
13928 vms_debug_on_exception = 0;
13929 }
13930
b53f3677
JM
13931 /* Debug unix/vms file translation routines */
13932 vms_debug_fileify = 0;
8dc9d339 13933 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
b53f3677
JM
13934 if ($VMS_STATUS_SUCCESS(status)) {
13935 val_str[0] = _toupper(val_str[0]);
13936 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13937 vms_debug_fileify = 1;
13938 else
13939 vms_debug_fileify = 0;
13940 }
13941
13942
13943 /* Historically PERL has been doing vmsify / stat differently than */
13944 /* the CRTL. In particular, under some conditions the CRTL will */
13945 /* remove some illegal characters like spaces from filenames */
13946 /* resulting in some differences. The stat()/lstat() wrapper has */
13947 /* been reporting such file names as invalid and fails to stat them */
13948 /* fixing this bug so that stat()/lstat() accept these like the */
13949 /* CRTL does will result in several tests failing. */
13950 /* This should really be fixed, but for now, set up a feature to */
13951 /* enable it so that the impact can be studied. */
13952 vms_bug_stat_filename = 0;
8dc9d339 13953 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
b53f3677
JM
13954 if ($VMS_STATUS_SUCCESS(status)) {
13955 val_str[0] = _toupper(val_str[0]);
13956 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13957 vms_bug_stat_filename = 1;
13958 else
13959 vms_bug_stat_filename = 0;
13960 }
13961
13962
38a44b82 13963 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
360732b5 13964 vms_vtf7_filenames = 0;
8dc9d339 13965 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
360732b5 13966 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 13967 val_str[0] = _toupper(val_str[0]);
360732b5
JM
13968 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13969 vms_vtf7_filenames = 1;
13970 else
13971 vms_vtf7_filenames = 0;
13972 }
13973
e0e5e8d6 13974 /* unlink all versions on unlink() or rename() */
d584a1c6 13975 vms_unlink_all_versions = 0;
8dc9d339 13976 status = simple_trnlnm
e0e5e8d6
JM
13977 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13978 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 13979 val_str[0] = _toupper(val_str[0]);
e0e5e8d6
JM
13980 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13981 vms_unlink_all_versions = 1;
13982 else
13983 vms_unlink_all_versions = 0;
13984 }
13985
360732b5
JM
13986 /* Dectect running under GNV Bash or other UNIX like shell */
13987#if __CRTL_VER >= 70300000 && !defined(__VAX)
13988 gnv_unix_shell = 0;
8dc9d339 13989 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
360732b5 13990 if ($VMS_STATUS_SUCCESS(status)) {
360732b5
JM
13991 gnv_unix_shell = 1;
13992 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
13993 set_feature_default("DECC$EFS_CHARSET", 1);
13994 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13995 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13996 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13997 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
e0e5e8d6 13998 vms_unlink_all_versions = 1;
1a3aec58 13999 vms_posix_exit = 1;
360732b5
JM
14000 }
14001#endif
9c1171d1 14002
2497a41f
JM
14003 /* hacks to see if known bugs are still present for testing */
14004
2497a41f 14005 /* PCP mode requires creating /dev/null special device file */
2623a4a6 14006 decc_bug_devnull = 0;
8dc9d339 14007 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
2497a41f 14008 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14009 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14010 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14011 decc_bug_devnull = 1;
682e4b71
JM
14012 else
14013 decc_bug_devnull = 0;
2497a41f
JM
14014 }
14015
2497a41f
JM
14016 /* UNIX directory names with no paths are broken in a lot of places */
14017 decc_dir_barename = 1;
8dc9d339 14018 status = simple_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
2497a41f 14019 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14020 val_str[0] = _toupper(val_str[0]);
2497a41f
JM
14021 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14022 decc_dir_barename = 1;
14023 else
14024 decc_dir_barename = 0;
14025 }
14026
f7ddb74a
JM
14027#if __CRTL_VER >= 70300000 && !defined(__VAX)
14028 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14029 if (s >= 0) {
14030 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14031 if (decc_disable_to_vms_logname_translation < 0)
14032 decc_disable_to_vms_logname_translation = 0;
14033 }
14034
14035 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14036 if (s >= 0) {
14037 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14038 if (decc_efs_case_preserve < 0)
14039 decc_efs_case_preserve = 0;
14040 }
14041
14042 s = decc$feature_get_index("DECC$EFS_CHARSET");
b53f3677 14043 decc_efs_charset_index = s;
f7ddb74a
JM
14044 if (s >= 0) {
14045 decc_efs_charset = decc$feature_get_value(s, 1);
14046 if (decc_efs_charset < 0)
14047 decc_efs_charset = 0;
14048 }
14049
14050 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14051 if (s >= 0) {
14052 decc_filename_unix_report = decc$feature_get_value(s, 1);
1a3aec58 14053 if (decc_filename_unix_report > 0) {
f7ddb74a 14054 decc_filename_unix_report = 1;
1a3aec58
JM
14055 vms_posix_exit = 1;
14056 }
f7ddb74a
JM
14057 else
14058 decc_filename_unix_report = 0;
14059 }
14060
14061 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14062 if (s >= 0) {
14063 decc_filename_unix_only = decc$feature_get_value(s, 1);
14064 if (decc_filename_unix_only > 0) {
14065 decc_filename_unix_only = 1;
14066 }
14067 else {
14068 decc_filename_unix_only = 0;
14069 }
14070 }
14071
14072 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14073 if (s >= 0) {
14074 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14075 if (decc_filename_unix_no_version < 0)
14076 decc_filename_unix_no_version = 0;
14077 }
14078
14079 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14080 if (s >= 0) {
14081 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14082 if (decc_readdir_dropdotnotype < 0)
14083 decc_readdir_dropdotnotype = 0;
14084 }
14085
f7ddb74a
JM
14086#if __CRTL_VER >= 80200000
14087 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14088 if (s >= 0) {
14089 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14090 if (decc_posix_compliant_pathnames < 0)
14091 decc_posix_compliant_pathnames = 0;
14092 if (decc_posix_compliant_pathnames > 4)
14093 decc_posix_compliant_pathnames = 0;
14094 }
14095
14096#endif
14097#else
8dc9d339 14098 status = simple_trnlnm
f7ddb74a
JM
14099 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14100 if ($VMS_STATUS_SUCCESS(status)) {
14101 val_str[0] = _toupper(val_str[0]);
14102 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14103 decc_disable_to_vms_logname_translation = 1;
14104 }
14105 }
14106
14107#ifndef __VAX
8dc9d339 14108 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
f7ddb74a
JM
14109 if ($VMS_STATUS_SUCCESS(status)) {
14110 val_str[0] = _toupper(val_str[0]);
14111 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14112 decc_efs_case_preserve = 1;
14113 }
14114 }
14115#endif
14116
8dc9d339 14117 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
f7ddb74a
JM
14118 if ($VMS_STATUS_SUCCESS(status)) {
14119 val_str[0] = _toupper(val_str[0]);
14120 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14121 decc_filename_unix_report = 1;
14122 }
14123 }
8dc9d339 14124 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
f7ddb74a
JM
14125 if ($VMS_STATUS_SUCCESS(status)) {
14126 val_str[0] = _toupper(val_str[0]);
14127 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14128 decc_filename_unix_only = 1;
14129 decc_filename_unix_report = 1;
14130 }
14131 }
8dc9d339 14132 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
f7ddb74a
JM
14133 if ($VMS_STATUS_SUCCESS(status)) {
14134 val_str[0] = _toupper(val_str[0]);
14135 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14136 decc_filename_unix_no_version = 1;
14137 }
14138 }
8dc9d339 14139 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
f7ddb74a
JM
14140 if ($VMS_STATUS_SUCCESS(status)) {
14141 val_str[0] = _toupper(val_str[0]);
14142 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14143 decc_readdir_dropdotnotype = 1;
14144 }
14145 }
14146#endif
14147
28ff9735 14148#if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
f7ddb74a
JM
14149
14150 /* Report true case tolerance */
14151 /*----------------------------*/
14152 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14153 if (!$VMS_STATUS_SUCCESS(status))
14154 case_perm = PPROP$K_CASE_BLIND;
14155 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14156 if (!$VMS_STATUS_SUCCESS(status))
14157 case_image = PPROP$K_CASE_BLIND;
14158 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14159 (case_image == PPROP$K_CASE_SENSITIVE))
14160 vms_process_case_tolerant = 0;
14161
14162#endif
14163
1a3aec58 14164 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
94ae10c0 14165 /* for strict backward compatibility */
8dc9d339 14166 status = simple_trnlnm
1a3aec58
JM
14167 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14168 if ($VMS_STATUS_SUCCESS(status)) {
b53f3677 14169 val_str[0] = _toupper(val_str[0]);
1a3aec58
JM
14170 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14171 vms_posix_exit = 1;
14172 else
14173 vms_posix_exit = 0;
14174 }
c11536f5 14175}
f7ddb74a 14176
e2367aa8
CB
14177/* Use 32-bit pointers because that's what the image activator
14178 * assumes for the LIB$INITIALZE psect.
14179 */
14180#if __INITIAL_POINTER_SIZE
14181#pragma pointer_size save
14182#pragma pointer_size 32
14183#endif
14184
14185/* Create a reference to the LIB$INITIALIZE function. */
14186extern void LIB$INITIALIZE(void);
14187extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14188
14189/* Create an array of pointers to the init functions in the special
14190 * LIB$INITIALIZE section. In our case, the array only has one entry.
14191 */
14192#pragma extern_model save
14193#pragma extern_model strict_refdef "LIB$INITIALIZE" gbl,noexe,nowrt,noshr,long
14194extern void (* const vmsperl_unused_global_2[])() =
14195{
14196 vmsperl_set_features,
14197};
14198#pragma extern_model restore
14199
14200#if __INITIAL_POINTER_SIZE
14201#pragma pointer_size restore
14202#endif
14203
14204#ifdef __cplusplus
14205}
f7ddb74a
JM
14206#endif
14207
e2367aa8 14208#endif /* defined(__DECC) || defined(__DECCXX) */
748a9306 14209/* End of vms.c */